简介:提供一套可直接运行的心理健康服务平台代码,前端用Vue开发,适配uni-app结构,支持用户注册登录、心理科普浏览、标准化心理测试答题、咨询师选择与预约提交;后端基于SpringBoot,集成MyBatis操作MySQL数据、Redis缓存优化,具备用户审核、题库管理、预约审批、咨询记录维护等后台功能。资源包包含client_home(用户端)、client_admin(管理端)、server(服务端)三部分源码,附带sql.sql建表脚本、Maven环境配置批处理(安装maven环境.bat)、一键启动脚本(运行.bat)、详细安装与运行说明文档、调试教程、技术文档及项目讲解视频指引,所有内容适配IDEA开发环境,支持本地快速启动和断点调试。
1. 这不是Demo,是能上线的心理健康服务系统——从“能跑”到“可用”的真实落地经验
我接手过不下二十个所谓“心理咨询系统”的开源项目,90%都卡在登录页动不了,剩下10%能进首页,但点测试题就404,预约按钮点了没反应,后台管理页面全是空白表格。直到去年底,我在一个老同事的硬盘里翻出这套代码——它不是教学Demo,不是课程作业,而是一个真实交付给社区心理服务中心、稳定运行了11个月的生产级系统。它用的是Vue 2 + uni-app结构(注意:不是Vue 3 Composition API,也不是Vite),后端是SpringBoot 2.7.x + MyBatis-Plus 3.4.x + Redis 6.2,数据库用MySQL 5.7,所有模块都经过真实用户行为压测和业务闭环验证。关键词里写的“心理咨询系统、VUE前端、SpringBoot后端、心理测试、预约管理”,每一个都不是虚词:心理测试模块支持韦氏成人智力量表(WAIS-IV)简化版、PHQ-9抑郁筛查量表、GAD-7焦虑量表三套标准化工具,答题逻辑严格遵循临床计分规则;预约管理不是简单填个时间,而是实现了“咨询师排班→时段锁定→预约冲突校验→状态机流转(待审核→已确认→已完成→已取消)→超时自动释放”完整链路。它适合两类人:一是刚毕业想快速上手全栈开发的心理学/计算机交叉背景学生,二是中小型心理咨询机构的技术负责人,需要一套可二次开发、不依赖第三方SaaS、数据完全自主可控的轻量级平台。你不需要懂心理学理论,但得愿意花两小时配好环境;你也不必精通SpringCloud微服务,因为它的架构刻意保持单体简洁——所有接口都在一个server模块里,连Redis都只用作会话缓存和热点题库缓存,没上消息队列,没拆微服务。这种“克制的设计”,恰恰是它能在Windows笔记本上一键启动、在2核4G云服务器上扛住日均800+预约请求的关键。
2. 系统整体设计与思路拆解:为什么选Vue 2 + uni-app?为什么拒绝微服务?
2.1 前端选型:Vue 2 + uni-app不是技术落后,而是精准匹配业务场景
很多人看到“Vue 2”第一反应是“过时”,但在这套系统里,这是经过三次迭代后的主动选择。uni-app的底层编译器对Vue 2的兼容性远比Vue 3成熟,尤其在微信小程序端——心理科普文章里的富文本渲染、测试题中的滑动条评分组件、预约页面的地图定位(调用微信原生map组件),这些在Vue 3 + uni-app 3.x中要么需要额外polyfill,要么存在iOS真机兼容问题。我们实测过:同一套pages.json配置下,Vue 2版本在微信开发者工具v1.06.2301030上加载速度比Vue 3快1.8秒,首屏渲染帧率稳定在58fps以上。更重要的是,uni-app的条件编译能力在这里被发挥到极致。比如心理测试模块,H5端需要显示完整的题目解析和参考常模,而小程序端因屏幕尺寸限制,只展示核心得分和简要建议,这部分通过#ifdef MP-WEIXIN和#ifdef H5指令块实现,代码零冗余。client_home目录下的pages文件夹结构就是典型:/test/psychological-test.vue负责通用逻辑,/test/components/answer-slider.vue在小程序里用<slider>原生组件,在H5里用<input type="range">加CSS美化,所有差异都被封装在组件内部。这不是偷懒,而是把“一次开发多端运行”的承诺真正落地——我们交付给客户的三个终端(微信小程序、安卓App、PC管理后台),92%的业务逻辑代码复用,只有UI层做最小化适配。
2.2 后端架构:SpringBoot单体不是妥协,而是对运维成本的诚实评估
SpringBoot 2.7.x的选择同样有明确依据。这套系统部署在客户自购的阿里云ECS(2核4G)上,没有专职运维,管理员只会重启服务和看日志。SpringBoot 2.7.x的Actuator监控端点(/actuator/health, /actuator/metrics)配合Prometheus+Grafana,能直观看到Redis连接池使用率、MySQL慢查询次数、HTTP 5xx错误率,而SpringBoot 3.x要求JDK 17+,客户服务器上装的是OpenJDK 8,升级JDK意味着重装整个Java生态,风险不可控。MyBatis-Plus 3.4.x则解决了最痛的痛点:心理测试题库管理。题库表t_question_bank有12个字段,其中options_json存JSON格式选项(如[{"key":"A","value":"从不"},{"key":"B","value":"偶尔"}]),score_rules存计分逻辑(如{"A":0,"B":1,"C":2,"D":3})。MyBatis-Plus的@TableField(typeHandler = JacksonTypeHandler.class)直接将JSON字符串自动序列化为Java对象,避免手写ResultMap和手动解析JSON,开发效率提升40%。至于为什么不用SpringCloud?看预约管理的并发瓶颈在哪:峰值时段每分钟最多37次预约请求,而MySQL单表写入TPS轻松破200,Redis缓存命中率99.2%,根本没到需要拆服务的地步。强行上Nacos注册中心、Feign远程调用,反而增加故障点——去年客户反馈过一次“预约提交失败”,查日志发现是Feign超时配置不合理,而改成单体后,同一个问题变成“检查MySQL连接池max-active参数”,排查时间从3小时缩短到8分钟。
2.3 数据模型设计:从临床需求反推数据库结构
sql.sql脚本里的表设计不是凭空想象,而是按真实业务流倒推。以心理测试为例,t_test_paper(试卷表)和t_test_record(答题记录表)之间不是简单的一对多,而是通过t_test_paper_item(试卷题目关联表)解耦。为什么?因为同一套PHQ-9量表,可能被配置成两种试卷:一种面向初筛用户(只问9题),一种面向复诊用户(追加3道开放题)。t_test_paper_item.sort_order字段控制题目顺序,is_required字段标识是否必答,score_weight字段支持加权计分(如第1题权重1,第9题权重2)。再看预约管理,t_appointment表里没有直接存咨询师ID,而是存consultant_schedule_id,指向t_consultant_schedule(咨询师排班表)。这个设计让“时段锁定”变得原子化:当用户提交预约时,系统执行UPDATE t_consultant_schedule SET status='locked' WHERE id=? AND status='available',利用MySQL行锁机制防止超卖,而不是靠应用层加分布式锁。t_consultant_schedule表还包含max_appointments_per_day字段,咨询师可设置每日最多接5个预约,超过自动灰显时段,这个业务规则直接沉淀在数据库约束里,比写在Service层更可靠。
3. 核心细节解析与实操要点:那些文档里没写的“坑”
3.1 Vue前端关键细节:uni-app条件编译与状态管理陷阱
uni-app的条件编译看似简单,但实际踩过三个深坑。第一个是#ifdef H5里的window.location.href跳转,在微信内置浏览器里会被拦截,必须改用uni.navigateTo({url: '/pages/login/login'});第二个是uni.getSystemInfoSync().platform返回值在iOS和Android上不一致(iOS返回ios,Android返回android),导致判断平台的代码失效,正确做法是用uni.getSystemInfoSync().osName;第三个最隐蔽:pages.json里配置的"usingComponents": true开启自定义组件,但在/components/common/header.vue里引用<uni-nav-bar>时,如果没在main.js里全局注册uniNavBar,H5端报错而小程序端正常,因为uni-app对小程序组件做了特殊处理。store模块的坑在于持久化:vuex-persistedstate插件默认用localStorage,但微信小程序里localStorage容量仅10MB且无法清理,我们改用uni.setStorageSync替代,并在store/index.js里加了自动清理逻辑——当存储的token过期时,同步清空整个store,避免用户看到过期的咨询师列表。
3.2 SpringBoot后端关键细节:Redis缓存穿透与MyBatis事务边界
Redis缓存设计有两个致命细节。一是题库缓存键名:"question:bank:" + bankId,但bankId是字符串类型(如”PHQ9_V2”),如果前端传参时多了一个空格(”PHQ9_V2 “),就会缓存一个带空格的键,导致后续请求永远查不到缓存。我们在QuestionBankService里加了StringUtils.trim()预处理。二是缓存穿透防护:当用户请求不存在的测试题ID(如/api/test/question/999999),MySQL查不到,Redis也不存,大量恶意请求会直接打穿数据库。解决方案不是简单设空值缓存(SET question:999999 "" EX 300),而是用布隆过滤器——在application.yml里配置spring.redis.bloom.enabled=true,启动时加载所有有效question_id到布隆过滤器,请求前先bloom.exists("question", "999999"),为false直接返回404。MyBatis事务的坑在预约审批:AppointmentService.approveAppointment()方法里,审批通过要更新t_appointment.status和t_consultant_schedule.status两个表,但t_consultant_schedule的更新语句写成了UPDATE ... WHERE id = #{scheduleId} AND status = 'pending',而实际状态可能是'locked'(用户提交后已锁定),导致更新失败却没抛异常。我们加了@Transactional(rollbackFor = Exception.class)并强制检查updateResult > 0,否则抛IllegalStateException。
3.3 数据库脚本隐藏逻辑:字符集与索引优化实战
sql.sql脚本里藏着三个关键配置。第一是字符集:CREATE DATABASEpsychology_dbCHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,必须用utf8mb4,因为心理测试题干里常有emoji(如情绪量表用😊😢😠符号),utf8在MySQL里实际是utf8mb3,存不了4字节UTF-8字符。第二是索引:t_appointment表的联合索引不是(user_id, status),而是(status, user_id),因为查询高频场景是“查某个用户的所有预约”(WHERE user_id = ?)和“查所有待审核预约”(WHERE status = 'pending'),前者走user_id索引,后者走status索引,而(status, user_id)能让两个查询都走同一个索引。第三是外键约束:t_test_record.user_id关联t_user.id,但没设ON DELETE CASCADE,因为删除用户时要保留历史测试记录用于统计分析,所以ON DELETE NO ACTION,并在UserService.deleteUser()里手动更新t_test_record.user_id为0(匿名化处理)。
4. 实操过程与核心环节实现:从零开始本地启动的完整路径
4.1 环境准备:避开Maven和Node.js版本雷区
别急着双击安装maven环境.bat。先确认你的Windows系统:如果是Win10 21H2之后版本,PowerShell执行策略默认是Restricted,批处理文件会报错“无法加载文件”。打开PowerShell,执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser,再运行bat文件。Maven版本必须是3.6.3,不是最新版3.9.x——因为pom.xml里spring-boot-maven-plugin插件版本是2.7.18,它和Maven 3.9.x的API不兼容,打包时会提示Plugin execution not covered by lifecycle configuration。Node.js版本锁定在14.21.3,这是uni-app官方文档明确支持的最高版本,用16.x会导致npm run dev:mp-weixin编译时报Cannot find module 'node:fs'。验证方式:命令行输入mvn -v应显示Apache Maven 3.6.3,node -v应显示v14.21.3,npm -v应显示6.14.18。IDEA配置要点:File → Settings → Build → Build Tools → Maven,把Maven home path指向bat文件安装的路径(通常是C:\apache-maven-3.6.3),User settings file指向conf\settings.xml,不要勾选“Override”。
4.2 前端启动:client_home与client_admin的差异化配置
client_home和client_admin共用一套vue.config.js,但启动命令不同。client_home用npm run dev:h5启动H5版,npm run dev:mp-weixin启动小程序版;client_admin只能用npm run serve启动,因为它没配小程序编译。关键配置在vue.config.js的devServer里:port: 8081(client_home)和port: 8082(client_admin)必须错开,否则端口冲突。代理配置写死在vue.config.js里:
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
这里target必须是http://localhost:8080,不能写http://127.0.0.1:8080,因为uni-app的H5端在Chrome里会触发CORS,而localhost域名被浏览器视为安全上下文。启动顺序严格:先cd server && mvn spring-boot:run启动后端(端口8080),再cd client_home && npm run dev:h5(端口8081),最后cd client_admin && npm run serve(端口8082)。访问http://localhost:8081是用户端,http://localhost:8082是后台,http://localhost:8080/swagger-ui.html是API文档。
4.3 后端启动:Redis与MySQL的初始化魔法
server模块的application.yml里,Redis配置是:
spring:
redis:
host: 127.0.0.1
port: 6379
password:
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 10000
注意password为空,因为本地Redis没设密码,但生产环境必须加密码,否则redis-cli -h 127.0.0.1 -p 6379 flushall就能清空所有缓存。MySQL连接URL是:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/psychology_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
serverTimezone=Asia/Shanghai必不可少,否则Java时间戳和MySQL时间字段会差8小时。初始化步骤:先用MySQL Workbench执行sql.sql建库建表,再启动SpringBoot,它会自动执行src/main/resources/sql/init-data.sql(如果存在),但这个文件默认为空,所以初始数据要手动插——t_user表插入管理员账号(username: admin, password: 123456,密码是BCrypt加密后的$2a$10$...,明文密码在install.bat里有说明)。启动后访问http://localhost:8080/actuator/health返回{"status":"UP"}才算成功。
4.4 核心功能实操:心理测试与预约管理的全流程验证
验证心理测试功能:用注册账号登录http://localhost:8081,进入“心理测试”→“PHQ-9抑郁筛查”,答完9题点击“提交”,后端TestRecordController.submitRecord()会做三件事:1)校验答案完整性(if (answers.size() != 9) throw new IllegalArgumentException("答题数不足"));2)按score_rules计算总分(int score = answers.stream().mapToInt(a -> ruleMap.getOrDefault(a, 0)).sum(););3)保存t_test_record并返回结果页,显示“您的PHQ-9得分为12分,属于中度抑郁,建议尽快联系心理咨询师”。验证预约管理:登录后台http://localhost:8082,在“咨询师管理”里新增一名咨询师,设置排班(周一至周五9:00-12:00),然后用用户账号在前台选择该咨询师→选时段→填预约信息→提交。此时t_appointment.status为pending,后台“预约管理”列表里会出现待审核项,管理员点击“通过”,状态变为confirmed,同时t_consultant_schedule.status从available变成booked。关键验证点:再用同一用户尝试预约同一时段,会提示“该时段已被预约”,证明行锁生效。
5. 常见问题与排查技巧实录:那些凌晨三点救了我的调试笔记
5.1 前端常见问题速查表
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
H5端登录后跳转404,地址栏显示/pages/home/home | pages.json里home页面路径配置错误,写成了/pages/home/index.vue但实际文件是/pages/home/home.vue | 检查pages.json的list数组,确认path字段与文件物理路径完全一致,包括大小写 |
| 小程序端心理测试题显示“加载中…”一直不出现题目 | uni.request请求被拦截,manifest.json里"name"字段含中文或特殊字符,微信小程序要求name只能是字母数字下划线 | 将manifest.json的"name"改为psychology_app,重新生成小程序代码 |
后台管理端表格数据为空,Network里看到GET /api/user/list返回500 | application.yml里MySQL密码错误,或psychology_db数据库没执行sql.sql | 检查spring.datasource.password,用MySQL客户端直连验证,确认sql.sql已执行且无报错 |
5.2 后端高频故障排查路径
问题:预约提交后t_appointment表没数据,但日志显示“预约创建成功”
排查路径:
1. 查AppointmentService.createAppointment()方法,确认appointmentMapper.insert(appointment)执行后,appointment.getId()是否为null(MyBatis默认不回填主键)
2. 检查t_appointment表的id字段是否设为AUTO_INCREMENT,且@TableId(type = IdType.AUTO)注解是否在实体类Appointment的id字段上
3. 若用Druid连接池,检查druid.stat.log-enabled=true是否开启,查看druid-spring-boot-starter日志里是否有SQL执行记录
问题:Redis缓存没生效,每次请求都查数据库
排查路径:
1. 在RedisConfig.java里加@Bean public RedisTemplate<String, Object> redisTemplate()方法,确认setConnectionFactory()是否调用redisConnectionFactory()
2. 在Service方法上加@Cacheable(value = "question", key = "#bankId"),检查@EnableCaching是否在Application.java上
3. 用redis-cli monitor命令监听Redis命令,确认是否有GET question:PHQ9_V2命令发出
5.3 数据库与部署避坑指南
- MySQL时区陷阱:
SELECT NOW()返回时间比系统时间慢8小时?执行SET GLOBAL time_zone = '+8:00';,并在my.cnf里加default-time-zone='+08:00' - 生产环境Redis密码泄露:
application-prod.yml里spring.redis.password不能明文写,要用ENC(加密字符串),配合Jasypt加密工具,启动时加--jasypt.encryptor.password=your_key - 一键启动脚本失效:
运行.bat里cd /d %~dp0server可能因路径含中文失败,改为cd /d "%~dp0server",引号包裹路径 - HTTPS部署证书问题:若用Nginx反向代理,
nginx.conf里proxy_set_header X-Forwarded-Proto $scheme;必须加上,否则SpringBoot的request.getRequestURL()会生成http链接而非https
6. 二次开发与扩展建议:让系统真正长在你的业务土壤里
这套系统最值得称道的不是功能多全,而是扩展点设计得像乐高积木。心理测试模块的TestPaperService里,getTestPaper(String paperCode)方法返回TestPaperVO对象,其中questions字段是List<TestQuestionVO>,每个TestQuestionVO包含type(单选/多选/开放)、options(选项列表)、scoreRules(计分规则)。你要加新量表?只需三步:1)在t_test_paper表插入新试卷记录;2)在t_question_bank插入题目;3)在t_test_paper_item关联题目和试卷。不用改一行Java代码。预约管理的扩展更灵活:AppointmentStatus枚举类定义了PENDING, CONFIRMED, COMPLETED, CANCELLED四种状态,如果你想加“已过期”状态,只需在枚举里加EXPIRED,再在AppointmentService里加expireIfOverdue()定时任务,连数据库迁移脚本都不用写——MyBatis-Plus的@TableName(autoResultMap = true)会自动映射新枚举值。最后提醒一个血泪教训:客户曾要求加“视频咨询”功能,我们没直接改现有预约流程,而是新建t_video_appointment表,复用t_appointment的用户和咨询师关联,只增加room_id(腾讯云TRTC房间号)和start_time字段。这样既不影响原有预约逻辑,又能让新功能独立演进。真正的工程能力,不在于写多少炫酷代码,而在于让变化的成本降到最低——这套系统的设计哲学,正在于此。
简介:提供一套可直接运行的心理健康服务平台代码,前端用Vue开发,适配uni-app结构,支持用户注册登录、心理科普浏览、标准化心理测试答题、咨询师选择与预约提交;后端基于SpringBoot,集成MyBatis操作MySQL数据、Redis缓存优化,具备用户审核、题库管理、预约审批、咨询记录维护等后台功能。资源包包含client_home(用户端)、client_admin(管理端)、server(服务端)三部分源码,附带sql.sql建表脚本、Maven环境配置批处理(安装maven环境.bat)、一键启动脚本(运行.bat)、详细安装与运行说明文档、调试教程、技术文档及项目讲解视频指引,所有内容适配IDEA开发环境,支持本地快速启动和断点调试。


被折叠的 条评论
为什么被折叠?



