免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

学生心理咨询评估系统实战:SpringBoot+Vue+MySQL+MyBatis

学生心理咨询评估系统实战:SpringBoot+Vue+MySQL+MyBatis 把“学生心理咨询评估系统”和SpringBoot、Vue、MySQL、MyBatis这几个词放在一起很多人第一反应是这不就是一套后台管理系统吗真动手做过才会发现这个项目最磨人的地方根本不在增删改查而在量表怎么动态化配置、一次测评提交之后结果怎么算、学生和咨询师之间的流程怎么闭环。这篇就以SpringBootVueMySQLMyBatis这套组合为基底把心理咨询评估系统从业务拆解、表结构设计、后端接口实现到Vue前端交互再到打包部署和版本踩坑完整过一遍。适合正在做毕设、课设的同学也适合想找一个贴近真实业务的前后端分离练手项目的人——理解业务闭合之后代码怎么展开都不会跑偏。文中的工程思路和关键代码都是可直接复现的本地有JDK和Maven就能跑。1. 学生心理咨询评估系统不是普通CRUD先想清楚业务再动手1.1 一条完整的学生测评与咨询业务链这套系统的用户有三类学生、咨询师、管理员。学生登录后可以看到咨询师发布的量表选择量表作答提交后系统自动计算得分和对应等级学生可以查看历史测评记录也可以预约咨询师做面谈。咨询师负责维护量表和题库查看学生的测评结果对有预警的分值重点关注处理预约并在咨询结束后写咨询记录。管理员则是管理账号、分配角色、看整体数据比如全校测评完成率、各等级占比之类的统计。一个标准的业务闭环是这样的管理员初始化账号或学生自助注册——咨询师发布量表——学生选量表答题提交——后端计算评估结果——咨询师查看评估报告并决定是否需要干预——学生预约线下咨询——咨询师填写咨询记录存档。这个闭环里涉及四个核心模块用户权限、量表题库、测评记录、咨询预约。很多同学做这类题目容易犯一个错误一上来就建表、写接口结果做到一半发现量表题目不知道存在哪里、评估结果不知道在哪一步算、预约冲突没有字段支撑。所以先把这个链条想清楚后面所有设计都是围绕它展开的。1.2 技术栈为什么这么选版本怎么定技术选型是答辩时被问概率最高的问题你得有话说清楚。SpringBoot解决的是后端基础设施问题。内嵌Tomcat、自动装配、起步依赖让Java后端开发不再需要一堆web.xml配置一个主类就能起服务。Vue解决的则是页面交互复杂度量表的单选、多选、评分题本身就是典型的动态渲染场景Vue组件化正好合适。MyBatis和MySQL的组合SQL完全在自己手里出问题容易排查也方便在面试时讲动态SQL、缓存、结果映射这些点。如果追求快速出活JPA写起来更省事但面试官问MyBatis的频率远高于JPA而且很多老项目都是MyBatis体系练这个不亏。版本上给一个明确建议Java 8 SpringBoot 2.7.x mybatis-spring-boot-starter 2.x MySQL 8.0 Vue 2或Vue 3。SpringBoot 3.x不是不能用但它要求JDK 17javax命名空间改成了jakarta跟着老教程写很多地方对不上。毕设和练手场景求稳2.7.x是最优解。Vue这边如果你熟Vue 3就用Vue 3熟Vue 2就用Vue 2本文示例按通用写法来差异只在语法层。这套技术选型不是最潮的但一定是最好找答案、最好复现、也最好在答辩和面试时讲出东西的。2. 核心表结构设计把量表、评估记录、用户角色一次理清表结构是这个项目的地基也是最能在答辩时展示业务理解的部分。我按业务模块把表拆开讲每一张表都对应前面说的业务闭环不搞冗余设计。2.1 用户表与角色设计用户表最简设计是在user表里加一个role字段1代表学生2代表咨询师3代表管理员。这样查用户信息不用join逻辑简单对毕设体量完全够用。如果你想把权限做得更“正规”可以拆user、role、user_role三张表但那样会增加很多维护成本收益在这个项目里不明显。核心字段可以这样设计字段类型说明idbigint主键usernamevarchar(50)登录名唯一passwordvarchar(100)BCrypt加密后的密码real_namevarchar(50)姓名roletinyint1学生 2咨询师 3管理员gendertinyint1男 2女student_novarchar(30)学号/工号phonevarchar(20)手机号statustinyint1正常 0停用create_timedatetime创建时间update_timedatetime更新时间deletedtinyint软删除标记0未删 1已删这里有一个实操中很容易踩的坑密码一定不能用明文。用Spring Security自带的BCryptPasswordEncoder或者jbcrypt库把密码哈希之后再入库。答辩时如果面试官问“用户密码你怎么存的”你说明文会被减分的。为什么加deleted软删除而不是物理删因为学生信息关联了很多测评历史硬删会破坏数据链路软删除只用把deleted置为1查询时统一加where deleted 0历史数据还在报表统计也不受影响。2.2 量表、题目、选项三张表动态化的关键心理咨询评估系统和普通问卷系统最核心的区别在“量表”这个概念。量表本身有分类、有评分方式题目按维度组织选项带有分值。很多同学把题目写死在代码里咨询师想改一个选项或者新增一套量表就必须找开发改代码发版这在真实场景里完全不可接受。我设计了三张独立的表scale表量表字段类型说明idbigint主键namevarchar(100)量表名称descriptiontext量表说明categoryvarchar(50)分类焦虑、抑郁、一般心理等scoring_methodtinyint1总分制 2维度制statustinyint1启用 0停用sort_orderint排序create_timedatetime创建时间question表题目字段类型说明idbigint主键scale_idbigint所属量表contentvarchar(500)题目内容question_typetinyint1单选 2多选 3评分题dimensionvarchar(50)所属维度如“精神性焦虑”sort_orderint题目顺序create_timedatetime创建时间scale_option表选项字段类型说明idbigint主键question_idbigint所属题目option_labelvarchar(10)选项标识如A/B/Coption_contentvarchar(200)选项内容score_valueint选中后的分值把题目和选项拆出来咨询师在后台维护量表时前端就能动态展示最新题目不用改一行Java代码。这才是“评估系统”区别于普通“留言板”的价值点。2.3 评估记录与评估明细一次测评的存档一次测评会保存两条数据主表记录一次测评的整体信息明细表记录每一道题的作答。两者是1对N的关系。assessment_record表字段类型说明idbigint主键student_idbigint学生用户idscale_idbigint量表idstatustinyint0草稿 1进行中 2已完成total_scoreint总分levelvarchar(20)等级正常/轻度/中度/重度conclusiontext自动生成的评估结论文本create_timedatetime创建时间submit_timedatetime提交时间assessment_detail表字段类型说明idbigint主键record_idbigint评估记录idquestion_idbigint题目idoption_idbigint选项idanswer_valueint本题得分这里的status状态机要提前想清楚。学生答题过程中如果中途退出下一次进入还能从保存的草稿/进行中状态继续一旦提交完成明细不能再改。后端在提交接口里根据status做幂等判断防止重复提交产生脏数据。另外把total_score、level、conclusion冗余在主表是一个典型的“空间换时间”设计。学生查看历史记录列表时只需要查record表就能展示分数和等级不用每次去聚合明细表性能好很多。下面给assessment_record的一段建表SQL参考其他表按同样的思路补全就行CREATE TABLE assessment_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_id BIGINT NOT NULL, scale_id BIGINT NOT NULL, status TINYINT DEFAULT 0 COMMENT 0草稿 1进行中 2已完成, total_score INT DEFAULT NULL, level VARCHAR(20) DEFAULT NULL, conclusion TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, submit_time DATETIME DEFAULT NULL, deleted TINYINT DEFAULT 0, KEY idx_student (student_id), KEY idx_scale (scale_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT测评记录主表;2.4 预约与咨询记录咨询预约表解决“学生约咨询师”这个动作。核心字段是学生id、咨询师id、预约时间、状态。consultation_appointment表字段类型说明idbigint主键student_idbigint学生idcounselor_idbigint咨询师idappointment_timedatetime预约时间段statustinyint0待确认 1已确认 2已完成 3已取消remarkvarchar(500)学生填写的备注create_timedatetime创建时间consultation_record表用于咨询结束后的存档字段类型说明idbigint主键appointment_idbigint关联预约student_idbigint学生idcounselor_idbigint咨询师idcontenttext咨询内容conclusiontext咨询结论与建议create_timedatetime创建时间注意status字段必须用0、1、2、3这种状态码而不是布尔值因为预约从申请到完成有完整的生命周期后面在“时间冲突处理”一节会专门讲状态判断。3. 后端核心模块拆解量表明细、测评提交、预约冲突与评估结果计算表设计定下来后端写起来就有抓手了。我按业务模块把接口和实现逻辑过一遍重点讲几个最容易写崩的地方。3.1 工程结构与接口概览我习惯按功能分包而不是按技术类型堆目录com.example.counseling ├── controller # REST接口 ├── service # 业务逻辑 ├── mapper # MyBatis Mapper接口 ├── entity # 数据库实体 ├── common # 统一返回体、异常、常量 ├── config # 配置类 └── interceptor # 登录与权限拦截器统一的返回体Result包含code、message、data三个字段。统一的异常处理器用RestControllerAdvice业务异常抛出BizException统一转成code500的JSON返回。这样做的好处是前端axios拦截器只判断一个code字段就行不需要为每个接口写try-catch。核心接口大致是这些方法路径说明POST/api/auth/login登录GET/api/scale/list量表列表GET/api/scale/{id}量表详情含题目和选项POST/api/assessment/submit提交测评GET/api/assessment/history历史测评列表GET/api/assessment/{id}测评详情报告POST/api/appointment创建预约PUT/api/appointment/{id}/status确认/取消预约POST/api/consultation/record填写咨询记录3.2 量表明细接口千万别在循环里查数据库量表详情是学生答题前的核心接口一个量表包含若干题目每题包含若干选项。最差的做法是在Java里遍历题目列表再调一次mapper查选项题目有30道就会执行31条SQL这是典型的N1问题。更合理的方式是用一条SQL把量表、题目、选项一次性查出来利用resultMap的collection嵌套映射组装对象。MyBatis XML里可以这样写resultMap idScaleDetailMap typeScale id columnscale_id propertyid/ result columnscale_name propertyname/ collection propertyquestions ofTypeQuestion id columnq_id propertyid/ result columnq_content propertycontent/ collection propertyoptions ofTypeScaleOption id columno_id propertyid/ result columno_content propertyoptionContent/ result columno_score propertyscoreValue/ /collection /collection /resultMapSQL里给字段起别名把scale_id、q_id、o_id区分开MyBatis会自动完成嵌套赋值。量表信息基本不变这个接口还可以加一层缓存用Caffeine或者最简单的ConcurrentHashMap都行咨询师在后台更新量表时再刷新缓存。这个优化点写进简历或答辩PPT里比“我封装了统一返回体”有含金量多了。3.3 测评提交事务、幂等与维度分计算测评提交是整个系统最核心的业务接口代码要串联“建主记录、批量存明细、计算分数、生成结论、更新状态”五件事必须放在一个事务里任何一步失败都要回滚。核心流程可以写成这样Transactional(rollbackFor Exception.class) public AssessmentResultVO submit(SubmitRequest req) { // 1. 防重复提交检查该学生该量表是否已有进行中/已完成记录 int cnt assessmentRecordMapper.countRunning(req.getStudentId(), req.getScaleId()); if (cnt 0) { throw new BizException(该量表已有测评记录请勿重复提交); } // 2. 创建主记录status进行中 AssessmentRecord record buildRecord(req); assessmentRecordMapper.insert(record); // 3. 批量插入明细避免循环insert ListAssessmentDetail details buildDetails(req, record.getId()); assessmentDetailMapper.batchInsert(details); // 4. 按维度聚合得分套用等级规则 MapString, Integer dimensionScores calcDimensionScores(details, req.getScaleId()); AssessmentResult result calcResult(dimensionScores, req.getScoringMethod()); // 5. 更新主记录status已完成 assessmentRecordMapper.updateResult(record.getId(), result); return result; }这里每步都有讲究。幂等检查放在最前面。学生答题页面如果网络卡顿连点两次提交没有这个检查就会生成两条测评记录。批量插入明细时MyBatis用foreach写批量insertinsert idbatchInsert INSERT INTO assessment_detail (record_id, question_id, option_id, answer_value) VALUES foreach collectionlist itemitem separator, (#{item.recordId}, #{item.questionId}, #{item.optionId}, #{item.answerValue}) /foreach /insert计算维度分时先把question表里每个题目对应的dimension查出来然后在Java里按dimension分组累加answer_value。总分制量表直接用totalScore维度制量表要在结果里给出每个维度分数方便前端画雷达图。等级规则用一张配置表或者枚举存区间比如0-40分“正常”、41-60分“轻度”、61-80分“中度”、81分以上“重度”具体区间按量表说明定。有一个行业细节必须在展示时注意系统自动生成的结论只能叫“评估参考”不能写成医疗诊断页面要展示“结果仅供参考如需专业帮助请联系校心理咨询中心”之类的话。这个细节写在设计说明书里很加分体现你懂业务边界。3.4 预约冲突处理与轻量权限校验预约时间冲突的本质是“同一个咨询师在同一时间不能被预约两次”。查询逻辑很简单SELECT COUNT(*) FROM consultation_appointment WHERE counselor_id #{counselorId} AND appointment_time #{time} AND status IN (0, 1)status IN (0, 1)的意思是待确认和已确认的预约都占住时间段已完成和已取消的不占。创建预约前执行这条查询count大于0就抛异常提示换时间。为什么强调带status的判断如果不加学生取消后的时间段永远约不了这就是隐藏的业务逻辑坑。权限校验我用轻量方案登录时用JWT生成token自定义RequireRole注解标注在Controller方法上HandlerInterceptor里解析token并校验角色。不引入Spring Security的原因很简单学起来重配置复杂对这个项目来说是杀鸡用牛刀。拦截器加注解的方式代码量少、逻辑透明答辩时你还能讲清楚每一步在做什么。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { int[] value(); }不过要留个扩展口如果后面要加复杂的细粒度权限再平滑迁移到Spring Security这也是可讲的演进思路。4. Vue前端分层测评动态渲染、路由守卫和axios封装怎么配合后端后端接口就位后前端要做的是把能力组织成可用的界面。很多人的前端代码就是一个超大组件加一堆复制粘贴用户看没问题但代码维护和答辩展示都不行。4.1 前端工程如何组织用Vue CLI或者Vite创建工程后src下面建议拆这么几块src ├── api # 接口请求按模块拆文件 ├── router # 路由配置 ├── store # Vuex/Pinia状态 ├── views # 页面级组件 ├── components # 通用组件 └── utils # axios实例、工具函数api目录独立出来的价值很明显所有后端接口都在这里维护组件中只调用方法。比如登录接口就是api/user.js里的login方法后端改了URL只改这一个文件全局生效。4.2 axios封装与路由守卫axios封装核心做三件事请求拦截器从本地存储取token塞到Authorization头响应拦截器统一判断业务code非0提示错误信息遇到401统一跳转登录页。import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization token } return config }) service.interceptors.response.use( res { if (res.data.code ! 0) { Message.error(res.data.message) return Promise.reject(new Error(res.data.message)) } return res.data.data }, error { if (error.response error.response.status 401) { router.push(/login) } return Promise.reject(error) } )路由守卫配合角色控制是另一个关键点。Vue Router的beforeEach里做两件判断未登录只能访问登录注册页已登录用户再根据meta里的roles判断是否能进入当前路由无权限就跳到403或首页。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) return } if (token to.meta.roles !to.meta.roles.includes(store.state.user.role)) { next(/403) return } next() })4.3 问卷答题页的动态渲染这是学生端体验的核心。量表题目类型有单选、多选、评分题组件上要用动态渲染而不是把三种题型各写一坨if。思路是从题库接口拿到questionType用Vue的component组件根据类型动态匹配组件名比如单选对应SingleChoice、多选对应MultiChoice、评分对应ScoreChoice。所有作答数据统一用一个对象answerMap保存每切换一道题就写入 answerMap[questionId] answerValue同时把这个对象同步到localStorage。为什么一定要同步到localStorage因为量表通常有几十道题学生填到一半误刷新不做本地暂存就全白填了体验极差。这个细节看着小实际使用中很影响口碑。页面底部用进度条展示已完成题数题量不大就没有拆分页面的必要滚动式答完最顺手提交时一次性把整个answerMap发给后端。4.4 多角色菜单与结果可视化登录后根据user.role动态渲染菜单可以用v-if控制菜单项也可以用动态路由注册。学生端看到的是测评中心、测评记录、咨询预约咨询师端看到的是量表管理、测评数据、预约管理管理员端是用户管理、数据统计。测评结果页值得多说一句总分和等级用大字展示在顶部维度分用ECharts的雷达图或者柱状图画出来比纯表格直观得多。ECharts做这个很成熟按官方示例改一下option就能用。这样一个页面放出去整个项目的完成度立刻上一个档次。5. MyBatis在这个项目里的实战用法动态SQL、日志定位与几个经典坑MyBatis是这个项目里“看起来简单、细究很深”的部分。Mapper接口加XML会的人半小时写完不会的人到处报错。这一章把我实际踩过的坑和推荐的姿势集中说一下。5.1 动态SQL条件查询与批量插入用户管理列表往往需要按用户名、角色、状态组合筛选MyBatis的 加 是标准写法select idselectUserList resultTypeUser SELECT * FROM user where if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testrole ! null AND role #{role} /if if teststatus ! null AND status #{status} /if AND deleted 0 /where ORDER BY create_time DESC /select标签会自动处理掉第一个AND比手工拼“WHERE 11”干净得多。这里建议无论如何都要用CONCAT而不是直接拼%一是可读性好二是避免参数边界问题。批量插入评估明细的foreach写法在第3章已经给过这里补充一个性能点MySQL对单条INSERT的VALUES数量有限制虽然默认max_allowed_packet足够大几百道题完全没压力但如果你今后做数据导入功能几千上万条批量插入要分批执行否则会报packet too large。5.2 N1问题与resultMap嵌套的取舍N1问题在前端展示列表时最容易暴露。比如查询“测评数据列表”需要展示每个学生的最新测评等级如果在循环里查每次测评结果接口就会很慢。我的建议是列表页用SQL联表一次性查出概要信息详情页再用resultMap嵌套查询。SELECT r.id, r.total_score, r.level, r.submit_time, u.real_name, u.student_no, s.name AS scale_name FROM assessment_record r JOIN user u ON r.student_id u.id JOIN scale s ON r.scale_id s.id WHERE r.status 2 AND r.deleted 0 ORDER BY r.submit_time DESC这样一条SQL搞定列表然后在Java里用PageHelper做分页即可。联表时注意字段名重复的问题比如两张表都有create_time必须用别名区分开不然MyBatis会自动映射到同名属性产生诡异的数据错乱。5.3 日志配置与慢SQL定位开发阶段一定要把SQL日志打印出来不然MyBatis像个黑盒报错了完全不知道发的什么SQL。在application.yml里加一行mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl启动后在控制台就能看到每条SQL的完整参数和影响行数。也可以把日志级别调成debuglogging: level: com.example.counseling.mapper: debug定位慢SQL有两个层面业务层面自己在Service打点计时数据库层面打开MySQL慢查询日志。对毕设来说通常控制台看到某个接口执行了多条SQL或者单条SQL扫描行数很大就能判断出问题在哪。5.4 缓存这个项目该不该开二级缓存MyBatis一级缓存默认开启作用范围是同一个SqlSession。在Spring里SqlSession是和事务绑定的意味着同一个事务内重复查询相同的SQL会走缓存事务结束缓存就没了。这个机制足够应付大多数情况。二级缓存我建议别开。原因有三个量表这种低频变更数据用本地缓存更好测评明细和学生数据实时性强开二级缓存容易出脏读更关键的是面试官问到缓存时你说“我知道二级缓存有什么问题所以我们用Caffeine替代”比“我开了二级缓存”更有含金量。这个项目里真正值得缓存的就是量表详情用一个带过期时间的本地缓存就够了。5.5 常见真坑汇总问题现象根因解决办法XML里写小于号报错XML解析失败使用lt;代替接口方法多个参数XML取不到值没加Param每个参数加Param注解批量插入报PacketTooBigException单次插入数据量超过MySQL限制分批插入查询结果字段全为null联表字段重名映射错位用别名区分同名字段MySQL 8连接报Public Key Retrieval错误驱动版本或allowPublicKeyRetrieval未开URL加allowPublicKeyRetrievaltrue动态SQL条件不生效test里字符串判断写成! 未用and用! null and ! 组合判断最后一个坑特别典型有人写if teststatus ! status是Integer类型实际不传时是nullnull不等于空字符串这个if会直接跳过条件查全表。这种问题没有统一改法只能靠日志确认SQL对不对。6. 从本地启动到打包部署版本兼容与常见环境错误汇总项目写完最激动的时刻就是把它跑起来给别人看。这一章我直接给一套从零启动的流程和一部错误排查清单照着走能少熬两个通宵。6.1 本地环境怎么准备需要装的东西是这些JDK 8、Maven 3.6、MySQL 8.0、Node.js 14Vue 2一般要求Node 14到16Vue 3用Node 16以上。很多人的MySQL安装配置卡在密码策略上装完初始化时如果选的是强密码策略本地开发建议设一个符合规则的密码并拿笔记下来如果连接时报caching_sha2_password相关错误可以在MySQL命令行里执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password;不过新版mysql-connector-java是支持caching_sha2_password的更推荐直接使用8.0.33以上的驱动别老停在5.1.x老版本老驱动才是各种连接报错的源头。6.2 SpringBoot配置与启动application.yml最小配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/counseling?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.counseling.entity启动报错高频原因有三种。第一种是端口被占用8080起不来改server.port或者杀掉占用进程。第二种是数据库连接失败先确认MySQL服务有没有启动再确认URL里的库名是否已创建。第三种是Mapper找不到检查启动类有没有MapperScanXML文件路径和mapper-locations是否匹配。额外一提如果想让项目启动的时候有点仪式感可以去banner生成器生成一个ASCII艺术字的启动横幅放到resources/banner.txt里纯属好看但是答辩演示时能活跃气氛。6.3 Vue构建与nginx部署前端开发跑npm run serve部署时跑npm run build产物在dist目录。本地联调时可以用nginx把前后端代理到一起避免跨域问题nginx配置参考server { listen 80; server_name localhost; root /usr/share/nginx/html/dist; index index.html; location /api/ { proxy_pass http://localhost:8080/api/; } location / { try_files $uri $uri/ /index.html; } }这里有两个必须提的坑。第一个是history路由刷新404问题必须加try_files $uri $uri/ /index.html否则路由切到/dashboard后刷新页面nginx找不到对应的物理文件直接404。第二个是“Vue打包后布局异常”的经典问题通常是publicPath配置错了默认/部署到子路径后css和js资源全部404样式全丢。解决办法是把vue.config.js里的publicPath改成./或者部署到nginx根目录时保持绝对路径不变。如果你图省事不想额外起nginx也可以把前端dist整个放到SpringBoot的static目录下打成jar一起跑适合纯演示场景。但正规部署还是建议nginxjar分离也方便以后扩展。6.4 SpringBoot版本太高的坑如果你用的SpringBoot 3.x会踩连环坑JDK必须17以上javax换成jakarta所有import语句要改对应MyBatis starter要用3.x版本。这些改动对老教程是灾难性的。如果已经建了项目想降级改pom里parent版本为2.7.18Java版本调回8然后把Java代码里的jakarta.改回javax.MyBatis starter换成2.3.x基本就能回来。降级过程虽然麻烦但比跟一堆兼容性问题搏斗省心。6.5 部署时的常见错误对照表现象原因解决前端页面正常但接口请求404nginx没配/api代理或接口路径不对检查location /api配置登录接口返回500数据库表没导入或密码加密方式不一致执行初始化SQL检查数据源打包到服务器后样式错乱publicPath用了绝对路径资源加载失败vue.config.js里publicPath改为相对路径系统部署后时间差8小时JDBC连接未指定serverTimezoneURL加serverTimezoneAsia/Shanghai启动报Invalid bound statementMapper XML和接口方法不对应检查namespace、方法id我个人在实际操作中的体会是部署排错有一半问题出在环境另一半出在路径。路径问题只要记住一个原则就行浏览器F12打开控制台看资源请求到底是什么路径缺什么补什么比瞎猜快得多。关于这个项目最后想多说两句如果你拿到的是一份完整源码建议按“数据库→后端→前端”的顺序跑通然后优先啃测评提交和量表动态化这两个模块。答辩和面试时最能拿分的绝对不在登录注册和用户管理而在评估结果的计算逻辑、量表的动态配置、以及角色间的工作流协同——这才是这个项目和普通CRUD的区别。心理评估类项目还有一个不能省的细节在测评结果页、报告页都加上“本评估结果仅供参考不构成医疗诊断如有需要请前往学校心理咨询中心或专业机构”的提示。这既是行业伦理要求也体现了你对项目背景的理解深度。最后给一个小方向如果做完这套还有余力可以把测评报告的导出功能做成PDF正反两页排版好看一点也可以把咨询师的“预警名单”做成一个独立页面用分值阈值自动筛选出需要重点关注的学生。这两个点投入不大但会让人觉得你做的不是一个课程作业而是一个真正在设计的产品。
返回列表