1. 选题背景与整体思路:先把这个项目想清楚
每年到了毕设选题季,总有人跑来问:Java方向的题目到底怎么选才稳妥?我的回答一直挺统一——如果你想要一个技术覆盖面广、业务逻辑清楚、答辩好讲,又能真正跑出完整系统的题目,心理健康档案管理系统是非常值得做的一个方向。它表面上是“档案管理”,本质上却把用户角色、流程审批、在线测评、自动计分、数据统计、隐私保护这些常见功能都串到了一起,刚好能把Java技术栈从基础到框架完整过一遍。
这套系统要解决的事情很直接:把传统纸质心理测评和咨询记录搬到线上。管理员能维护来访者档案、发布量表、查看统计报表;咨询师能查看来访者的测评结果、填写咨询记录;来访者则可以在线注册、完善档案、做心理测评、预约咨询。基于Java来做,从Java基础语法、面向对象设计、集合、异常处理,到Servlet、JSP、Spring Boot、MyBatis、MySQL,能覆盖一个Java学习者大部分知识点。这也是我推荐它的核心原因:项目难度曲线平缓,但展示空间很大。
这篇内容我会按照一个完整项目的推进顺序来写,包括需求拆解、技术选型、数据库设计、核心模块实现、关键代码细节,以及实测环境里踩过的坑。不管你是拿它当毕业设计,还是想给学校心理咨询中心、企业EAP员工援助计划做一个轻量级信息化工具,都可以直接照这个思路落地。
1.1 这个题目到底好在哪:为什么值得推荐
先说实话,很多人对“心理档案管理系统”的第一反应是:这不就是个增删改查吗?确实,底层逻辑离不开增删改查,但它和普通的“图书管理系统”“学生管理系统”有一个关键区别:这个系统里面有“测评规则”和“角色权限”这两条业务主线。
测评规则意味着你不只是把数据存进去再查出来,还要处理题目选项分值、维度因子计算、总分和常模分对照,这已经是轻量级“规则引擎”的雏形。权限控制则要求你区分管理员、咨询师、来访者三类角色,不同的角色看到的菜单、按钮、数据范围都不一样,这会倒逼你把登录拦截、会话管理、权限校验这些Java Web核心知识点真正用起来。答辩的时候,这两块内容拿出来讲,深度一下就上去了。
另外,心理健康数据属于高度敏感数据,系统对隐私保护有天然需求。哪怕你只是在代码里做了个手机号脱敏、身份证号掩码,都能体现你对业务场景的思考。这种“非功能需求”在评阅老师眼里是非常加分的。
1.2 先别急着写代码:把三类角色和核心流程理清楚
任何管理系统的开发,第一步都是把“谁在用”和“怎么用”想明白。这套系统里最核心的角色有三类:
- 系统管理员:负责用户管理、量表管理、来访者档案审核、数据统计、系统公告维护。
- 心理咨询师:可以查看分配给自己的来访者列表、查看测评报告、填写咨询记录、管理预约日程。
- 来访者(学生/员工):注册登录后完善个人基础档案,参与心理测评,查看自己的测评报告(部分量表结果可能需要咨询师解读),在线预约咨询。
这三类角色对应着一条完整的业务链路:来访者注册并建立档案,管理员审核档案,来访者选择量表并在线答题,系统根据答题结果自动计算分值,咨询师查看测评报告并给出解读,来访者预约线下或线上咨询,咨询师填写咨询记录,管理员从后台看到汇总统计数据。把这条链路理清楚之后,系统的功能模块基本就浮出水面了。
1.3 系统边界与功能清单:哪些必做,哪些加分
我建议把系统拆成必做功能和扩展功能两层。必做功能是保证项目完整闭环的骨架,扩展功能则是在时间充裕时用来提升系统亮点的。这里给一份可以直接参考的清单:
| 模块 | 核心功能 | 建议等级 |
|---|---|---|
| 用户与权限 | 注册登录、角色区分、管理员审核、密码加密 | 必做 |
| 档案管理 | 来访者基础档案的增删改查、敏感信息脱敏 | 必做 |
| 测评量表管理 | 量表维护、题库维护、选项分值配置 | 必做 |
| 在线测评 | 来访者答题、自动计分、报告生成 | 必做 |
| 咨询预约 | 咨询师时间设置、来访者预约、状态变更 | 必做 |
| 咨询记录 | 咨询师填写记录、来访者历史记录查看 | 必做 |
| 数据统计 | 测评完成数、风险等级分布、咨询预约统计 | 加分 |
| 公告通知 | 管理员发布量表通知或心理科普内容 | 加分 |
| 消息提醒 | 预约成功、测评报告生成后的站内消息 | 加分 |
从毕设评分角度来说,把“必做”这七块全部跑通,系统已经是一个完整闭环。把“数据统计”和“公告管理”再加进去,整个项目的体量和展示效果就会明显超出平均水准。接下来在技术选型上,我给出一些相对稳妥的建议。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:Java技术栈到底怎么搭
很多人在做Java毕设的时候,纠结最多的就是框架选择:用传统的Servlet+JSP,还是用Spring Boot?用不用前后端分离?数据库用MySQL还是Oracle?这些问题没有标准答案,取决于你手头的精力、学校的评分偏好,以及你对自己代码能力的信心。但有一点是可以明确的:尽量选择自己真正能讲清楚的技术方案,答辩的时候最怕的就是用了自己不理解的框架,被老师一问就卡壳。
2.1 框架选型:Spring Boot 还是 SSM 还是 Servlet
如果放在五年前,SSM(Spring + SpringMVC + MyBatis)是绝对的主流。但现在新开的Java项目,我基本都推荐直接用Spring Boot。原因很现实:Spring Boot把大量繁琐的xml配置简化成了自动配置,内嵌Tomcat容器,一条命令就能启动项目,开发效率高很多。它本质还是Spring那一套东西,控制反转、依赖注入、面向切面这些核心思想一点都没少,答辩的时候照样能讲出深度。
如果你的学校课程体系中Servlet和JSP占的比重大,老师要求必须体现原生Java Web技术,那可以折中:用传统Servlet + JSP + JDBC做主体,或者用Spring Boot,但前端用JSP或Thymeleaf服务端渲染,这样既能体现Java Web基础,又不至于在配置上浪费大量时间。我个人更推荐第二种组合,因为Thymeleaf语法和HTML天然贴合,页面改起来比JSP顺手。
还有一点要注意:开发环境先把JDK、Maven、IDEA或Eclipse配置好,尤其是JDK环境变量。很多项目跑不起来,不是因为代码有问题,而是JAVA_HOME、PATH没有配置正确。JDK版本建议用8或11,这两个版本稳定,和大部分框架兼容性最好。如果你用的是JDK 17甚至更高版本,后面Lombok、MyBatis等依赖版本不匹配的情况会变多,这个问题我会在第五章详细说。
2.2 数据库设计:这几张核心表必须认真建
心理档案管理系统最核心的不是代码,是数据模型。数据模型设计得好,后面写代码会非常顺畅;设计得不好,业务越往后做越别扭。这里我列一下推荐的核心表:
- 用户表(sys_user):存储所有登录账号,字段包括用户ID、用户名、密码(加密存储)、真实姓名、角色类型、手机号、邮箱、状态、创建时间。
- 来访者档案表(visitor_profile):存储来访者的详细心理健康档案,字段包括档案ID、用户ID、性别、年龄、学号/工号、院系/部门、紧急联系人、既往咨询史、档案状态、审核状态。
- 量表表(scale):存储量表基本信息,比如量表名称、量表类型、题目数量、适用人群、是否启用。
- 量表题目表(scale_question):存储题目内容、题目顺序、所属量表ID、选项类型(单选/多选)。
- 量表维度表(scale_dimension):存储量表包含的维度因子,比如焦虑因子、抑郁因子、人际关系敏感因子,每个维度有对应的常模参考值。
- 测评记录表(assessment_record):存储来访者每一次测评的明细,包括用户ID、量表ID、各维度得分、总分、测评状态、测评时间。
- 测评答案表(assessment_answer):存储每一道题的具体选项和分值,便于回看明细。
- 咨询预约表(appointment):存储预约时间、咨询师ID、来访者ID、预约状态。
- 咨询记录表(counseling_record):存储每次咨询的实际记录,包括咨询师ID、来访者ID、主诉、评估要点、咨询建议、下次计划。
以测评记录为例,它需要关联用户、量表、量表维度等多张表,用外键或者逻辑关联字段建立关系。这里要注意一个设计原则:不要把每一次测评的所有结果都塞到一张宽表里,而是把“总分”和“维度分”合理拆分。比如总分和风险等级可以直接存在assessment_record里,各维度得分可以单独建一张维度得分表或者用JSON字段存储。这样后续做统计报表时查询效率更高。
2.3 项目分层与前后端交互方式
不管前端用什么,后端我都建议严格遵循三层架构:Controller层负责接收请求和参数校验,Service层负责业务逻辑处理,Mapper层(Dao层)负责数据库操作。这个分层的意义在于:业务逻辑和接口入口解耦,出了问题容易定位,模块之间可以并行开发。
服务端渲染方案下,后端返回ModelAndView,页面直接展示数据,适合不需要频繁交互的管理系统。前后端分离方案下,后端提供JSON接口,前端Vue或React调用接口渲染页面,适合展示效果要求更高、希望页面交互更流畅的场景。如果做前后端分离,需要额外处理跨域CORS、Token鉴权、接口统一返回格式等问题,开发周期会长一点。
如果你是第一次做完整项目,我建议先用服务端渲染把核心业务打通,如果时间允许再考虑把某个模块改成前后端分离。不要一上来就搭一个非常复杂的架构,最后写不完反而不划算。项目分层代码结构可以参考下面这个包结构:
text复制com.psyche
├── controller // 接口入口
├── service // 业务逻辑层,接口+实现
├── mapper // 数据访问层
├── entity // 数据库实体类
├── dto // 数据传输对象,比如前端传参封装
├── vo // 视图对象,比如返回给前端的结果封装
├── config // 配置类:拦截器、跨域、静态资源映射
├── utils // 工具类:脱敏、加密、Excel导出
└── common // 公共类:统一返回结果、状态码、常量
3. 核心功能模块的设计与实现
框架搭好之后,就可以开始往里面填业务模块了。下面我挑四个核心模块展开讲,这四个模块基本决定了你这套系统能不能算“成品”。如果你能把登录权限、测评计分、档案管理和统计报表都做扎实,这套系统拿到任何答辩现场都不会虚。
3.1 登录鉴权与三种角色权限控制
心理健康档案涉及大量个人隐私,权限控制做不好,系统做得再花哨也是白搭。最基础也是最稳妥的方式是使用Session + 拦截器:用户登录成功后,把用户ID、角色等信息写入Session;在Spring Boot中注册一个HandlerInterceptor,拦截所有需要权限的请求路径,判断当前Session是否有效、是否具有对应角色权限。
角色权限这里不一定要引入Spring Security或者Shiro,虽然它们是行业标准,但学习成本较高,对于大部分毕设项目来说属于“杀鸡用牛刀”。手写一个拦截器,简单直接,也方便你自己向评委解释。比如你可以把请求路径按模块划分:/admin/** 只允许管理员访问,/consultant/** 只允许咨询师访问,/visitor/** 只允许来访者访问。未登录用户访问受保护资源时,直接重定向到登录页面。
密码存储一定不能用明文。使用BCrypt算法对密码进行哈希加密,这个在Spring Security的crypto包里可以直接引入。BCrypt和普通MD5的区别在于它自带盐值,两个用户即使密码相同,加密结果也不一样,安全性高很多。MD5虽然简单,但在这个项目里我不推荐单独使用。
3.2 心理测评量表模块:题库管理与自动计分
这个模块是整个系统的灵魂。它的核心并不是“答题”这个动作,而是“计分规则”怎么设计。很多同学把计分规则写死在代码里,比如在Java代码里判断“如果这个题目ID是1、2、3,则维度A得分加多少”。这种做法短期内能跑通,但完全不具备可扩展性,换个量表就要改一次代码,非常痛苦。
我建议把规则配置化。也就是说,题目表里每个选项都有一个固定的分值,题目和维度之间建立关联表,系统通过量表ID加载完整配置之后,动态计算总分和维度分。比如某个焦虑自评量表包含10道题,每道题分成4个等级,分别计1到4分,其中第1、2、4题归属“焦虑情绪”维度,第3、5、8题归属“躯体反应”维度。测评结束后,程序把所有题的分值累加得到总分,再按维度分组求和得到维度分。最后对照预置的参考区间,把总分映射成“正常”“轻度”“中度”“重度”等风险等级。
计分规则配置化的好处在于:管理员在后台新增一个量表时,不需要动任何Java代码,只要配置好题目、选项分值和维度归属,系统就能自动支持新的测评。这个设计思路也符合软件工程里的开闭原则,对扩展开放、对修改关闭,答辩时讲出来会很有说头。
在线测评的交互流程建议这样做:来访者点开始测评,后端按量表ID返回题目列表,前端一题一题展示,用户全部答完以后统一提交。提交时后端再遍历一遍答案,做必答校验,防止漏题。提交成功之后生成一条测评记录,并把测评状态置为“已完成”。
3.3 档案管理与咨询记录维护
档案管理是系统的数据底座,来访者没有档案,测评和咨询都无从谈起。档案的创建建议和用户注册解耦:注册只是创建一个账号,档案需要来访者补齐信息后提交,管理员审核通过后档案才正式生效。这个设计的目的是避免虚假或重复档案进入系统,保证数据质量。
档案列表页需要支持多条件查询,比如按姓名、学号/工号、院系/部门、档案状态查询。查询结果里要对手机号、身份证号等敏感字段做脱敏显示,比如手机号显示成“138****5678”,身份证号只显示前六位和后四位。这块我把它归到隐私保护,后面代码部分单独讲。
咨询记录是咨询师填写的主观文本,属于更强隐私层级的数据。数据库设计上建议把咨询内容和基础档案分开存储,访问控制也要更严格:普通管理员可以查看档案,但不一定有权限查看完整咨询内容,只有该来访者的对接咨询师或授权人员才能查看。这种细节在答辩时可以重点提,它代表你对隐私合规确实有思考。
3.4 统计报表与数据导出
数据统计是这个系统最直观的展示窗口。比如管理员登录之后,首页可以展示几个核心指标:本月新增测评人数、高风险来访者人数、咨询预约完成率、各量表测评次数Top5。用折线图看每周测评量变化趋势,用饼图看来访者风险等级分布。前端可以用ECharts,后端只需要按时间段聚合查询后返回JSON数据就行。
报表导出的需求也要考虑到,比如咨询师需要把测评结果导出成Excel提交给上级或存档。行业里有Apache POI和EasyExcel两种常用方案,我更推荐EasyExcel,它对内存占用控制得更好,API也简单,一段代码就能把List数据写入Excel文件。导出时同样要注意隐私问题,最好增加“导出人”“导出时间”的记录,在系统日志里留痕。虽然这点对于毕设是加分项,放在真实项目中就是合规要求,提前养成习惯总没错。
4. 关键代码实现与避坑细节
前面讲的是模块设计和业务思路,这一部分我挑几个真正影响系统能不能跑稳的细节来拆解。这些细节写代码的时候很容易忽略,但恰恰是它们决定了项目是“能演示”还是“能上线”。每一个都是我在实际调试中踩过坑或者帮别人排查过问题的点。
4.1 登录拦截器与Session超时处理
登录拦截器的逻辑不复杂,但有两个问题必须处理到位。第一个是静态资源的放行,否则项目启动后CSS、JS、图片全部被拦截,页面直接变成“无样式版”。第二个是Ajax请求的兼容处理:Session超时之后,用户在前端某个页面点击操作,Ajax请求被拦截器重定向到登录页,前端拿到的是HTML而不是预期的JSON,控制台可能直接报解析错误。
拦截器核心代码大致是这样:
java复制@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null) {
String requestedWith = request.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(requestedWith)) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"登录已过期,请重新登录\"}");
} else {
response.sendRedirect("/login");
}
return false;
}
return true;
}
}
注册拦截器时,用addPathPatterns指定拦截路径,用excludePathPatterns放行登录页、静态资源等路径。Session超时时间可以统一在application.yml里配置,比如设置为30分钟。超时时间太短会影响使用体验,太长又会增加安全风险,30分钟是一个相对均衡的选择。
4.2 测评计分规则引擎的简单实现
测评计分这一块,我见过太多同学把分值的判断写在Controller里,结果一个接口几百行,改起来极其痛苦。正确的做法是把计分逻辑放到Service层,并且用“总分累加 + 维度分组”的方式来实现。下面给一个简化后的参考思路。
java复制public AssessmentReport calculateScore(Long scaleId, List<AnswerDTO> answers) {
// 1. 查出量表下的所有题目和维度关联关系
List<Question> questions = questionMapper.selectByScaleId(scaleId);
Map<Long, Long> questionDimensionMap = questionMapper.selectDimensionMapping(scaleId);
// 2. 遍历用户提交的答案,累加总分
int totalScore = 0;
Map<Long, Integer> dimensionScoreMap = new HashMap<>();
for (AnswerDTO answer : answers) {
Question question = findQuestion(questions, answer.getQuestionId());
if (question == null) {
continue;
}
int score = parseOptionScore(answer.getOptionValue());
totalScore += score;
Long dimensionId = questionDimensionMap.get(question.getId());
if (dimensionId != null) {
dimensionScoreMap.put(dimensionId,
dimensionScoreMap.getOrDefault(dimensionId, 0) + score);
}
}
// 3. 根据总分判断风险等级
RiskLevel riskLevel = RiskLevel.fromScore(totalScore);
// 4. 封装结果返回
return new AssessmentReport(totalScore, dimensionScoreMap, riskLevel);
}
写这段代码时有几个注意点:答案列表可能因为前端重复点击而重复提交,后端要做幂等处理,可以在测评记录表加一个用户ID和量表ID的唯一索引;题目集合建议转成Map按ID索引,不要每次都做O(n)的查找,数据量大了以后会明显变慢;分数计算时要注意空值判断,防止用户提交了未答题目,这个在业务上要提前拦截。规则配置化不仅让代码更干净,也为后续扩展新量表留好了路。
4.3 隐私保护:敏感字段脱敏与日志清理
心理档案系统的数据一旦泄露,后果比普通业务系统严重得多。所以我很建议在项目里专门做一个脱敏工具类,并在所有模型对象返回前端之前进行字段转换。下面这个例子可以作为一个通用脱敏工具雏形。
java复制public class DesensitizedUtil {
public static String maskPhone(String phone) {
if (phone == null || phone.length() != 11) {
return phone;
}
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() < 10) {
return idCard;
}
return idCard.replaceAll("(\\d{6})\\d*(\\d{4})", "$1********$2");
}
public static String maskName(String name) {
if (name == null || name.length() == 1) {
return "*";
}
if (name.length() == 2) {
return name.substring(0, 1) + "*";
}
return name.substring(0, 1) + "*" + name.substring(name.length() - 1);
}
}
除了显示端脱敏,还有一个很容易忽略的点:日志。很多人在排查问题时直接log.info打印整个用户对象,手机号、身份证号、咨询内容全部打到控制台,这在教学演示环境里没什么,但在真实部署环境里就是事故。建议统一封装一个日志工具,打印前先调用脱敏方法,或者直接约定日志内容不包含敏感字段。这个习惯可能会在未来的实习或工作中帮到你。
4.4 分页查询和条件检索的通用写法
档案列表、测评记录、预约记录这些页面都离不开分页查询。使用MyBatis-Plus时可以直接用内置的Page对象,配合LambdaQueryWrapper做条件构造,代码非常简洁。手写XML的话,动态SQL用<where>和<if>标签控制条件拼接,效果也一样。
一个统一返回结果的封装类很重要,它能让前端处理数据非常舒服。我常用的是这样一个结构:code表示状态码,message表示提示信息,data表示业务数据。接口正常时code为200,异常时code为500,未登录或权限不足时使用401或403。前后端都按照格式来,联调阶段能省掉大量口舌之争。
分页查询要注意一个细节:前端传过来的页码和每页大小要校验,避免传入负数或超大值。排序字段也不要直接拼接,防止SQL注入,尽量使用白名单方式校验。这些在答辩时如果主动提出来,能证明你有安全意识。
5. 实测中遇到的典型问题与排查方法
这部分我单独拿出来说,因为一套系统从写完到能稳定演示,中间一定会经历各种莫名其妙的报错。我在帮别人调试这类Java项目时,遇到的问题翻来覆去就那么几类,把下面这些问题提前搞清楚,你可以少踩至少一半的坑。
5.1 Lombok和编译器版本兼容问题
经常有同学在启动项目时看到这么一句话:java: you aren't using a compiler supported by lombok, so lombok will not work。这句话的意思是:你用的JDK编译器版本太新,当前Lombok版本不支持。遇到这个问题,优先检查三件事:第一,Lombok是否为最新版本,在pom.xml里把版本号升级,比如1.18.30或更高;第二,IDEA是否开启了注解处理器,在设置里搜索Annotation Processors,勾选Enable annotation processing;第三,确认编译环境用的确实是项目指定的JDK,而不是IDEA内置的某个过老版本。
还有一种情况是代码里明明用了@Data注解,但是实体类找不到getter和setter方法。除了确认上面的问题外,还可以执行一次mvn clean,删掉target目录重新编译。Lombok是在编译阶段生成方法的,clean之后再compile一般都能解决。别问我为什么知道,这种问题我至少帮人排了不下二十次。
5.2 中文乱码问题:从Tomcat到MySQL
中文乱码几乎是Java Web项目里的传统艺能,一层一层排查下去,大多数情况下都能解决。我习惯按来源分类处理:HTTP响应乱码,在Spring Boot里统一配置字符编码过滤器为UTF-8;JSP或Thymeleaf页面乱码,检查页面是否声明charset=UTF-8;控制台日志乱码,在IDEA的Help菜单里找到Edit Custom VM Options,加一行-Dfile.encoding=UTF-8,然后重启IDEA;数据库乱码,建库时使用utf8mb4字符集,JDBC连接串上加上characterEncoding=utf8和useSSL=false。
这里要特别提醒:Tomcat处理URL中的get参数时,默认解码字符集是ISO-8859-1,所以前端通过URL传中文参数出现乱码时,检查Tomcat的server.xml,在Connector节点上加URIEncoding="UTF-8"。Spring Boot内嵌Tomcat也一样,可以在配置文件里设置server.tomcat.uri-encoding=UTF-8。这条经验能解决很多让你怀疑人生的乱码问题。
5.3 内存溢出与启动缓慢问题
开发阶段最常见的报错是java.lang.OutOfMemoryError: Java heap space,或者IDEA提示insufficient memory。第一个场景是启动Spring Boot时直接内存不足,这是因为IDE的堆内存太小,在IDEA的VM Options里把-Xms和-Xmx调大,建议设置为512m和1024m,Maven的运行参数也可以同步调整。第二个场景是项目运行过程中内存逐渐涨满,通常和代码有关,比如一次查询把全表数据加载到内存、导出Excel时一次性创建大量对象、循环里不断new大对象没有释放。
如果项目已经启动起来,可以用jstat -gcutil <pid>查看JVM堆内存使用情况,用jmap -dump:format=b,file=heap.hprof <pid>导出堆转储文件。把dump文件用JVisualVM打开,能看到哪个对象占用了大量内存。毕设阶段一般不要求到这个深度,但知道这个排查流程,在老师问到性能优化时就有话说。
5.4 其他运行时异常的排查套路:空指针、数组越界、反射相关
开发这种业务系统,遇到最多的运行时异常是NullPointerException,其次就是ArrayIndexOutOfBoundsException,还有在Java面试中经常被问到的反射、序列化相关异常。我的排查思路很固定,可以总结为三步:
第一步,看异常堆栈第一行到最后一行Java文件,确定异常发生在哪个类的哪一行。第二步,跳转到对应代码,检查这一行里所有通过.取出来的对象或数组下标,哪个可能为null,哪个索引可能越界。第三步,回看接口入参和数据库数据,确认是不是前端传了空值,或者数据库里有脏数据触发了转发逻辑。
举个例子,我遇到过测评模块提交后报ArrayIndexOutOfBoundsException,查到最后发现是题目顺序问题:数据库里的题目顺序是1、2、3、4,但前端渲染时把题目重新排序后,后端还在用遍历位置去匹配题号,位置和题号一错位就越界了。这种问题写代码时就要注意:题目匹配一定要用题号ID,而不是依靠遍历顺序。这也说明一个道理——报错信息永远只是入口,真正的问题往往在业务语义里。
最后说一个我做这类系统时比较深的体会:技术方案不用追求最时髦,但业务需求一定要理解到位。很多同学把心理档案系统当成普通增删改查来做,测评计分写死、角色权限不区分、敏感信息明文展示,最后项目看起来功能很全,本质却经不起追问。反过来,如果能把角色权限、计分规则、隐私保护这些点做实,哪怕页面朴素一点,它也是一个思路清晰、逻辑自洽的完整系统。做Java项目最重要的不是跑通,而是跑通之后能说清楚每一步为什么这么设计。
