做企业绩效考核管理系统其实是很多JavaWeb开发者绕不开的一个练手项目也是我前后帮人做过多次的成熟方案。这套系统完整说就是“基于javaweb和mysql的ssmmaven企业绩效考核管理系统”技术栈非常典型后端是Spring SpringMVC MyBatis的SSM组合加上Maven做依赖管理前端用JSP Bootstrap jQuery数据库落在MySQL。它主要解决传统月度考核里“纸质打分表流转慢、Excel汇总容易错、指标权重分摊麻烦、结果难以查询复盘”这一堆痛点。适合正在做课程设计、毕业设计或者想通过一个完整项目把SSM整合、Maven构建、JSP页面渲染、MySQL建模全部串起来的人参考。我会把项目从设计、数据库、后端、前端到常见坑全部拆开讲按这套思路你完全可以自己复现一套同样的系统。1. 项目整体设计与技术选型1.1 需求定位绩效考核的典型流程到底长什么样先别急着写代码。做这种管理系统最关键的一步是先把业务场景里“考核”这件事的流程理清楚。我在企业里接手过的绩效考核流程大致是这么走的每个季度或者每个月HR或管理员先配置考核方案也就是“这个周期考哪些人、用哪些指标、每个指标权重多大”然后由部门经理对下属员工逐项打分员工自己也可以先自评最后系统按权重算出综合得分再生成排名和统计报表。整个过程看起来不复杂但真正落地时全是细节。比如指标怎么建不同岗位考核侧重点不同销售岗看业绩、研发岗看交付、职能岗看协作如果指标库不能灵活配置系统做完就是死板的一块。再比如打分权限怎么控制员工只能看自己的结果经理能给下属打分但不能给自己打管理员能做全局配置。这些就是“角色权限”的细化来源。还有考核状态一份评分记录要从“草稿”到“已提交”再到管理员“确认生效”中间任何一个环节出问题都会导致结果错乱。所以在一开始我把系统拆成五个功能域登录认证与权限控制、指标与方案管理、考核任务下发、评分与汇总、统计与系统管理。这样数据库建模和页面规划都清晰很多。这个设计逻辑适用于大多数考核系统你做别的版本也可以按这个骨架走。1.2 技术选型SSM Maven这套组合为什么还值得用可能很多人会问现在新项目不都直接上Spring Boot了为什么还做SSM JSP这套“旧组合”我的观点是这要看项目场景。毕设和课程设计里SSM依然是出现频率最高的技术栈因为学校课程普遍按传统JavaWeb路线教导师也最容易验收。而且SSM是Spring Boot的前身你把SSM的整合逻辑吃透了看Spring Boot的自动配置会通透得多。反过来直接上手Spring Boot很多XML配置被隐藏了出了问题反而不知道怎么排查。Maven在这里不是可有可无的。一个JavaWeb项目如果不用Maven光jar包依赖就能把人逼疯Spring要引十几个包MyBatis要引包数据库驱动要引包版本冲突还得手动调。Maven把依赖管理和构建流程统一了pom.xml里写了坐标就能拉包还能配合打包插件打出war包直接丢Tomcat。热搜词里“maven是干嘛的”“maven安装与配置”“maven仓库”被搜这么多就是因为这个工具看着不起眼但确实是绕不过去的基础。前端用JSP Bootstrap jQuery说实话也不是追求多高级而是这套组合对后端开发者最友好。JSP可以直接用EL表达式和JSTL渲染数据Bootstrap给了一套现成的栅格布局和表格样式没有前端工程化门槛一个人就能把页面写完。jQuery的Ajax足够应付评分表单提交、指标动态添加这些交互。如果你想在这套系统基础上加前端框架后面可以慢慢替换但第一版用这个组合开发效率是最高的。数据库用MySQL就不用多说了免费、易得、教程多又是国内用的最多的关系型数据库。这套系统里主要涉及用户表、部门表、指标表、考核方案表、评分记录表和结果汇总表MySQL的建表、索引、事务能力完全足够。1.3 功能模块划分与页面清单我习惯在开发前把模块和页面先列出来免得后面写到哪里算哪里。这版系统的模块划分是这样的模块主要功能对应页面登录认证账号密码登录、退出、角色识别登录页、主页系统管理用户增删改查、部门维护、管理员操作用户管理页、部门管理页指标管理考核指标库配置、指标启用停用指标列表页、指标编辑Modal方案管理创建考核方案、绑定指标与权重、设置考核周期方案列表页、方案配置页考核任务下发考核任务、生成评分记录任务列表页、任务下发页评分考核员工自评、经理打分、提交与撤回评分表页、待评列表页结果统计成绩汇总、排名、按部门筛选考核结果页、统计报表页页面数量看着多但很多是共用一套列表模板真正核心的业务页面就是评分表页、方案配置页、结果汇总页这三块。开发顺序上我建议先把登录和用户管理做通再磕指标和方案然后做评分流程最后做统计展示。数据库表和后端Service也跟着这个顺序走每一步都能独立测试不会憋到最后才联调那样排错成本太高。2. 数据库设计与核心表结构2.1 考核数据怎么建模才合理数据库设计是整个项目的地基。我第一次做这类系统时犯过一个大错误就是把“考核方案”和“考核任务”混成一张表结果一到月度考核要复制多份数据的时候彻底绷不住各种冗余。后来重新梳理才发现考核系统里所有实体之间其实是两个层次一层是“配置数据”包括用户、部门、指标、考核方案另一层是“流转数据”包括考核任务、评分记录、汇总结果。配置数据的特点是相对固定、可以复用。比如指标库里有“工作业绩”“团队协作”“创新能力”这些指标它们是公共的可以被多个方案引用。考核方案相当于“指标组合包”一个方案里包含若干指标以及每个指标的权重百分比。流转数据的特点是每个考核周期都会新增。比如三月份的考核任务下发之后会产生一批新的评分记录四月份再下发又会产生一批新的。如果建模的时候不分层后期做历史查询、对比分析会非常痛苦。我的设计原则就是一句话把“用什么考”和“考了谁、打了多少分”分开建模。前者是配置区后者是业务区。这样做的好处是数据冗余少查询逻辑简单而且当指标权重调整时不会影响已经在进行中的评分记录。2.2 核心表结构与字段说明这套系统我总共设计了6张核心表。为了控制复杂度后面的代码示例都围绕这6张表展开。具体的建表字段如下。用户表t_user主键id用户名username密码password姓名real_name角色role1管理员、2经理、3员工部门id dept_id创建时间create_time。部门表t_dept主键id部门名称dept_name部门负责人dept_leader。指标表t_indicator主键id指标名称indicator_name指标描述indicator_desc状态status1启用、0停用。考核方案表t_assessment主键id方案名称assessment_name考核周期cycle_type方案状态status。方案指标关联表t_assessment_item主键id方案id assessment_id指标id indicator_id权重weight指标在该方案中的上限分值max_score。这里weight是小数字段比如0.3表示30%。考核任务表t_task主键id方案id assessment_id被考核人id user_id考核人id assessor_id考核期次period比如“2024-03”评分状态score_status0未评分、1已自评、2已提交最终得分final_score备注remark。这样一张表就能串起整个考核流程。要强调的是t_task里同时存了user_id和assessor_id表示“谁被考”和“谁来考”一个员工可以被部门经理考也可以自评所以评分的状态分成了多个阶段。这里有一个容易忽略的点score_status为什么不用一个字段同时表达自评和经理评分因为实际业务里自评和经理评是两条并行线。我先用成一个字段后面发现如果要支持“自评未提交、经理已打分”这种中间态这个设计就不够用。更合理的方式是拆成self_status和assessor_status两个字段或者用一张task_record表分别记录评分角色和分值。如果你从零开始建议直接拆开避免后期加字段。2.3 表关系与指标权重的计算逻辑表关系和表结构同样重要。用户表关联部门表考核方案表通过方案指标关联表关联指标表考核任务表关联考核方案表、用户表。外键在MySQL里我是建议加上的虽然一些开发者为了性能喜欢去掉物理外键但对于这种课程设计和中小型系统外键能让数据一致性更好Navicat里可视化看关系图也方便。你担心性能那就给外键字段建立普通索引效果差别不大。关于权重计算这是最容易理解错的地方。某个员工在这个周期内参与了一次考核方案里配置了三个指标权重分别是0.4、0.3、0.3每个指标满分如果是100分那么最终得分就是指标A打分 * 0.4 指标B打分 * 0.3 指标C打分 * 0.3。注意权重和必须是1如果配置界面让管理员填整数后端在保存方案时要做一个校验。我在方案配置页直接要求填写0到1之间的数字并实时在页面上显示合计是否等于1前端拦截一部分错误后端Service里再校验一次双保险。3. 后端核心实现与SSM整合实战3.1 用Maven搭建项目骨架与关键依赖技术选型定了接下来就是搭项目。我用IDEA创建Maven项目使用war包方式因为最终要部署到Tomcat。项目结构上我习惯分成controller、service、mapper、entity四层各个包名按照业务划分好再动手写代码。标准的Maven结构大概是这样的assessment-system/ ├── pom.xml ├── src/main/java │ ├── com/company/assessment/controller │ ├── com/company/assessment/service │ │ ├── impl │ ├── com/company/assessment/dao │ ├── com/company/assessment/entity │ └── com/company/assessment/common ├── src/main/resources │ ├── jdbc.properties │ ├── spring-dao.xml │ ├── spring-mvc.xml │ └── mybatis-config.xml └── src/main/webapp ├── WEB-INF │ ├── web.xml │ └── jsp ├── static │ ├── css │ ├── js │ └── images └── index.jsppom.xml里依赖我建议这么配版本都经过我实测可以一起工作。Spring用5.3.29MyBatis用3.5.15mybatis-spring用2.0.7MySQL驱动用8.0.33这几个版本组合很稳。JSTL用1.2Servlet和JSP相关的API用provided范围不然会和Tomcat自带的冲突。properties spring.version5.3.29/spring.version /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.15/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency /dependencies如果你是第一次搭最稳妥的方式是先把pom写好再执行Maven的reload和clean install让依赖全都下载成功后再写代码。热搜词里“maven命令行 clean install”被频繁搜索就是因为很多人不知道IDEA里的那个Maven面板其实底层调用的就是mvn clean install。遇到依赖下载慢去配置一个阿里云公共仓库镜像这个后面常见问题里会专门说。3.2 SSM三层整合的关键配置解读SSM整合最核心的工作就是把Spring、SpringMVC、MyBatis三者的生命周期对接好。Spring管理Service层和Dao层SpringMVC管理Controller层MyBatis负责数据库访问SqlSessionFactory要交给Spring容器。我实际配置时分了两个Spring配置文件spring-dao.xml管数据源和MyBatisspring-mvc.xml管Controller和视图解析。之所以分开是为了保证容器层次清晰SpringMVC的子容器能继承父容器的Bean而不会重复扫描造成事务失效。web.xml里要配置三样东西编码过滤器、ContextLoaderListener、DispatcherServlet。编码过滤器必须放在最前面字符集统一成UTF-8否则JSP提交中文就是一堆问号。这一步可是JavaWeb项目乱码的头号来源。filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingspring-dao.xml里我会用Druid或普通DriverManagerDataSource来配数据源。如果是第一次跑通避免引入太多连接池配置项可以用Spring自带的DriverManagerDataSource等系统稳定后再换成Druid。数据源配置里jdbc.properties单独放方便切换数据库环境。MyBatis的SqlSessionFactoryBean要指定mapper.xml位置然后通过MapperScannerConfigurer自动扫描dao接口。事务管理用DataSourceTransactionManager开启注解式事务驱动在Service实现类上加Transactional。这个设计非常关键考核评分提交时涉及多张表的更新没有事务的话半路报错会造成评分记录和汇总结果对不上。spring-mvc.xml里开启注解驱动配置InternalResourceViewResolver把Controller返回的字符串映射到WEB-INF目录下的JSP页面。同时还要放行静态资源否则Bootstrap的CSS和jQuery的JS会全部404。静态资源配置是个大坑很多人页面样式死活不出来就是Controller把静态资源请求也拦截了。3.3 评分提交与成绩汇总的核心业务实现评分是系统里事务性最强的操作。员工提交评分时要更新t_task表的状态要写入评分明细记录还要实时计算总分。如果员工只是保存草稿那先只写明细、不改状态如果点了正式提交那就要把草稿数据盖章确认状态改成“已提交”这个阶段之后就不能再自己改了。我做的评分提交逻辑用Service方法实现这个方法标了Transactional。大致流程是先根据taskId把指标明细查出来逐条写入t_score_detail再根据权重公式算出总成绩更新t_task表。写成代码就是下面这样省略了参数校验和异常处理的逐步扩充核心骨架已经足够说明事务的必要性Transactional public void submitScore(Integer taskId, ListScoreItem items) { Task task taskMapper.selectById(taskId); if (task null) { throw new BusinessException(考核任务不存在); } if (!0.equals(task.getScoreStatus())) { throw new BusinessException(该任务已提交不能重复打分); } double total 0; for (ScoreItem item : items) { ScoreDetail detail new ScoreDetail(); detail.setTaskId(taskId); detail.setIndicatorId(item.getIndicatorId()); detail.setScore(item.getScore()); scoreDetailMapper.insert(detail); Indicator ind indicatorMapper.selectById(item.getIndicatorId()); total item.getScore() * ind.getWeight(); } BigDecimal finalScore BigDecimal.valueOf(total).setScale(2, RoundingMode.HALF_UP); task.setFinalScore(finalScore); task.setScoreStatus(2); taskMapper.updateById(task); }实际开发里我在循环里做了个优化把指标信息一次性查询出来放到Map里避免每条明细都触发一次数据库查询。这个细节看起来不起眼但当评分指标有几十个、并发提交的时候就很有意义了。课程设计阶段数据量不大追求代码清晰比过度优化更重要但写map缓存这件事本身也简单顺手就做了。3.4 用MyBatis动态SQL实现分页条件查询考核任务的查询是典型的“多条件动态组合”。管理员想按部门查按考核期次查按评分状态查每个条件都可能为空如果用JDBC拼接SQL串字符串处理繁琐且容易出错。MyBatis的动态SQL就是为这个场景设计的。我的做法是写一个查询方法传入的条件对象里各字段允许为null然后用 标签自动处理条件拼接。select idselectTaskListByCondition resultTypecom.assessment.entity.TaskVO SELECT t.id, t.period, t.score_status, t.final_score, u.real_name AS user_name, d.dept_name AS dept_name, a.assessment_name FROM t_task t JOIN t_user u ON t.user_id u.id JOIN t_dept d ON u.dept_id d.id JOIN t_assessment a ON t.assessment_id a.id where if testdeptId ! null AND u.dept_id #{deptId} /if if testperiod ! null and period ! AND t.period #{period} /if if testscoreStatus ! null and scoreStatus ! AND t.score_status #{scoreStatus} /if /where ORDER BY t.id DESC /select分页我用的是PageHelper插件物理分页页面上传pageNum和pageSize两个参数Service层直接调用PageHelper.startPage然后返回PageInfo。这个插件用法简单但有一个注意点PageHelper只能作用于紧随其后的第一条查询所以在代码里startPage和下面的mapper方法之间不要插入其他数据库操作否则分页会串到别的地方去。我实际踩过这个坑往中间加了一个日志写入方法结果查询结果被日志查询“吃”掉了分页信息页面上第一页还是对的第二页就空了。4. 前端页面实现与交互细节4.1 登录页与权限入口的简洁设计前端这套系统我整体用的是Bootstrap 3.3.7的UI配上AdminLTE的布局思路没有额外引入复杂框架。登录页是最简单的表单提交时先用前端校验非空再用ajax把数据发给后端login接口后端验证通过后把当前用户信息放进session前端根据用户角色跳转到不同的首页。权限这块我除了在Controller层用拦截器做统一拦截还在导航栏上做了角色判断用JSTL标签从session里读当前用户角色只有管理员才能看到“系统管理”“指标管理”这些菜单。这样前端展示和后端接口双重控制算是权限管理的最小可用方案。如果你是第一次做权限系统不用一上来就思考接入Shiro还是Spring Security先把拦截器和session判断做好整个系统的骨架就能立起来。登录后主页放几个统计卡片员工总数、当前考核期任务总数、已完成评分任务数、平均分。用Bootstrap栅格排四个卡片再加一张图表。图表我为了不引入复杂库最开始用纯CSS画了个简单的横向柱状条后来换成ECharts做了个部门绩效对比图效果确实好很多。ECharts是按需引入的页面也不卡。4.2 考核评分表单的动态布局与数据回显评分表是这套系统里交互最复杂的一个页面。进入评分页时前端要先获取当前任务绑定的考核方案指标列表然后为每个指标渲染一个打分输入框最后在底部展示实时计算的总分。指标数量是不固定的所以HTML不能写死必须用jQuery动态拼接。我初始化评分页时先用Ajax调一次后端接口拿到指标列表和当前已有的评分明细。如果发现已保存过草稿就要把这些旧数据回显到输入框里否则用户一刷新就丢了之前的填写内容。这个回显逻辑容易被忽略但实际使用中基本是用户必踩的点。动态生成行的事务性代码大致是这样function loadIndicators(taskId) { $.ajax({ url: contextPath /assessment/indicators, type: GET, data: {taskId: taskId}, dataType: json, success: function (data) { if (data.success) { var list data.data; var html ; for (var i 0; i list.length; i) { html tr; html td list[i].indicatorName /td; html tdinput typenumber min0 max100 classform-control score-input data-weight list[i].weight value (list[i].score || ) /td; html td classweight-text (list[i].weight * 100) %/td; html /tr; } $(#scoreTable tbody).html(html); } } }); }实时总分计算我是给每个输入框绑了input事件每次键盘输入都会重新遍历页面上的所有输入框把“分值 * 权重”累加起来显示在底部的总分栏。为了不让用户乱填还要限制分值不能超过方案里设定的maxScore超过时提示并恢复原值。这里面的逻辑属于典型的“细节决定体验”。4.3 使用Ajax与后端交互保存草稿和提交的区别评分表上我放了两个按钮一个“保存草稿”一个“提交评分”。保存草稿允许用户多次操作提交之后就锁定。前端通过一个state字段区分后端接口也对应两个不同的Service方法。保存草稿时事务里不更新任务状态的finalScore提交时才计算总分并关闭编辑权限。很多初学JavaWeb的人会纠结到底用form表单直接提交还是用Ajax我的建议是凡是涉及无刷新局部渲染或者需要根据后端返回结果做不同操作的都用Ajax。评分表单提交后通常要弹提示框提示“提交成功”并且把页面切到只读状态这种体验用form表单刷新是达不到的。而且Ajax提交的时候数据序列化为JSON传给后端后端用RequestBody接收代码结构更清晰。不过要注意JSP页面使用Ajax时URL必须写全应用上下文路径否则部署到Tomcat后项目URL会变成http://localhost:8080/assessment-system/xxx而你写死的路径不带项目前缀请求就会404。这个问题我在另一篇文章里也提过解决办法是提前在所有页面里注入一个全局JS变量contextPath动态从当前请求路径中获取var contextPath %request.getContextPath()%;然后在所有Ajax请求里都用contextPath拼接前缀。这个习惯建议一上来就养成不然越到后面改起来越麻烦。4.4 JSP页面与Bootstrap组件的几个易错点JSP页面在引入CSS和JS时路径问题我上面已经说了这是最常见的坑。第二个易错点是JSP的编码。页面顶部必须把charset设为UTF-8同时用JSTL的fmt标签来处理日期和数字格式否则日期和分数位数的显示会乱。我测试时发现页面头部的% page contentTypetext/html;charsetUTF-8 languagejava %不能省web.xml的过滤器也不能省两处都配置了才能保证整个请求链路都是UTF-8。第三个易错点是操作确认弹层。删除指标、取消考核这类危险操作必须弹“你确定吗”我直接用了一个简单封装的confirmModal基于Bootstrap的Modal组件。如果用浏览器原生confirm样式跟整个页面风格不搭而且主流浏览器对原生confirm的定制能力太弱。前端这一块如果你的Bootstrap表格数据比较多建议再配一个简单的分页条我直接在页脚用JSTL循环渲染了页码导航没有额外引入DataTables。数据量不大时这种方式更轻量代码也更容易理解。热搜词里“jsp个人信息展示页面”“jsp入门”这类问题本质上都是对JSP的渲染和路径不熟把上面这些问题搞明白了JSP基本就入门了。5. 常见问题与排查技巧实录5.1 数据库连接与MySQL使用中的高频故障这个项目里跟MySQL相关的问题几乎是每个新手必遇一次的那种。最典型的就是MySQL 8.0版本连接报SSL错误。我用mysql-connector-j 8.0.33连接时有时会报Communications link failure或者SSL握手失败。解决办法是在jdbc.url里加上两个参数jdbc.urljdbc:mysql://localhost:3306/assessment_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8serverTimezone这个参数特别重要因为MySQL 8.0默认时区和驱动不一致时操作时间字段会直接报错。另一个典型问题是ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock这个报错基本表示MySQL服务没启动或者你用命令行连接时指定了错误的socket路径。Linux环境下确认MySQL用什么方式安装的service or systemctl启动一下然后再用Navicat连接走TCP 3306端口一般不会出这个问题。Navicat连接MySQL 8.0时密码加密方式也要注意。MySQL 8.0默认用了caching_sha2_password插件旧版本Navicat可能连不上要么升级Navicat版本要么在MySQL里把用户的加密规则改回mysql_native_password。这个我在部署时就遇到过新装的MySQLNavicat怎么都认证不了后来改加密方式才解决。还有一点是在建表时状态字段默认值问题。很多字段如果允许为空会让后续逻辑很别扭比如score_status这列我想让它默认是“0”可以在建表语句里写score_status varchar(1) DEFAULT 0。MySQL设置默认值为0这个操作本身不难难的是建表时要养成习惯把默认值想清楚不要为了图省事都写成可空后面写Query时到处都是null判断。5.2 Maven依赖下载缓慢与构建命令最实用的处理方案Maven的问题大部分集中在两处依赖下载慢、依赖冲突。早期我直接连Maven中央仓库下载Spring的依赖能卡十几分钟后来稳定方案是使用阿里云公共仓库镜像。在Maven的settings.xml里配一个mirror就行把所有远程仓库请求都指向阿里云镜像mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror执行Maven命令行时最常用的组合是mvn clean install -DskipTests。为什么强调clean因为Maven项目改动代码后如果不clean只install偶尔会出现旧的class文件没清理干净导致的“改完代码还是没变化”。我自己Debug过很多次奇怪的问题最后都是clean一次就好了。搜索词里“maven命令行 clean install”被搜得多就是这个命令行太常用了。另外测试跳过参数-DskipTests能省去很多不必要的单元测试等待时间本地验证核心功能时基本都是这么用的。依赖冲突这块最常见的是Spring的版本被传递依赖覆盖掉了。排查方式也很简单在IDEA的Maven工具栏里点“Show Dependencies”查看依赖树找到重复的jar包然后在pom里用exclusions排除不需要的传递依赖。这属于Maven进阶操作但如果你的项目跑起来报NoClassDefFoundError大概率就是版本冲突导致class加载失败。5.3 JSP页面乱码、404与500问题的排查路径JSP项目里最让人抓狂的就是乱码。乱码出现的地方有三处页面显示乱码、后端接收参数乱码、数据库存储乱码。排查思路要一条线走先看页面文件编码是不是UTF-8再看web.xml的CharacterEncodingFilter有没有配置再看数据库连接URL里有没有characterEncodingutf8最后看数据库表是不是utf8mb4。这四个环节任何一个断了都会出现断断续续的乱码问题。热搜词里有“mysql设置默认值为0”“mysql的数据库连接池”从侧面能发现很多人都是在完成项目后部署时才会遇到这些问题。404问题大多是路径写错。JSP放在WEB-INF目录下时必须通过Controller返回视图名称来跳转直接访问URL会404而放在webapp根目录下的静态资源可以直接访问。404时先看控制台日志再看请求URL和实际路径不要一上来就怀疑端口占用。500错误通常就是后端代码运行异常看IDEA控制台的异常堆栈从第一行报错开始定位不要拖到最后才看。SSM整合时很常见的一个500是“Invalid bound statement (not found)”这是MyBatis的Mapper接口和XML文件没有绑定成功检查mapper.xml的namespace和接口全限定名是否一致文件位置是否正确。5.4 整合部署到Tomcat后的几个细节整套系统做完要部署到Tomcat这里有两个高频问题。第一个是war包爆炸式部署后项目名和访问路径不对。默认情况下Tomcat会把war包解压到webapps下的同名目录访问路径是http://localhost:8080/项目名/。如果你配置了项目上下文路径为ROOT那就是直接通过根路径访问上下文前缀丢失会导致页面所有静态资源路径出错所以我上面说全局contextPath变量的必要性在这一刻体现得特别明显。第二个问题是JSP预编译。Tomcat在第一次访问JSP页面时需要编译所以首次访问会比较慢这不是系统卡死。如果部署到生产环境可以使用JSP预编译技术也可以用Tomcat的预编译命令把JSP全部提前编译成class文件这样首次访问速度会快很多。但课程设计和内部项目一般不用这么讲究理解这个机制就够了。我见过很多人把项目跑起来后页面能看登录页但登录进去全是404和500其实大部分是Tomcat部署的上下文路径和数据库连接串这两个低级错误。先把Tomcat日志和MySQL状态确认好再谈其他问题排查方向不会跑偏。如果你也准备动手搭一份这个绩效考核管理系统我个人的建议是这样的先用两到三天时间把SSM整合跑通用最简单的“查询用户列表”做端到端验证确认Controller、Service、Mapper三层链路没问题再开始写业务表。业务上不要贪多先把核心闭环打通——创建方案、下发任务、提交评分、汇总结果——这四步能走通整个项目的主体就建立了。后面再慢慢补统计图表、导出报表这些增强功能。看着项目大但真正核心的代码量并没有那么多耐住性子一步步来两周做到能演示的程度是完全可以的。