简介:这是一套基于SpringBoot开发的心理健康咨询系统完整工程,面向Java初学者、毕业设计学生及Web全栈学习者,解决在线心理咨询平台从需求建模到部署落地的全流程实践问题。资源包含2453个文件,以151个Java源码、322个JS脚本、242个CSS样式、956张PNG界面图及1个SQL数据库脚本为核心,覆盖前后端分离架构下的用户预约、咨询师管理、后台权限控制等核心模块;压缩包大小16.79MB,结构清晰,含大量Controller与Service层Class文件(如XinxiController、UsersController、CommentBuilder等),体现典型MVC分层设计。已有1470人学习下载,读者可直接导入IDE运行,复用数据库结构、理解Spring Security权限校验逻辑、分析Thymeleaf模板渲染机制,并基于默认账号admin/admin快速验证后台管理功能,是掌握SpringBoot企业级开发与心理类垂直应用结合的优质实战范例。
1. 一个能跑起来的心理健康咨询系统,不是Demo,是真实可交付的SpringBoot工程
很多开发者拿到“心理健康咨询系统”这类毕业设计或内部工具需求时,第一反应是:前端页面+后端接口+MySQL建表,三步走完就交差。但实际部署时才发现,用户注册要实名认证、咨询师排班要避免时间冲突、聊天记录得加密落库、敏感词得实时过滤——这些不是附加功能,而是系统上线前必须闭环的硬性环节。本项目标题里明确带出“SpringBoot”“源码”“数据库”,说明它不是概念演示,而是一套具备生产就绪特征的完整实现:用SpringBoot 2.3+构建分层架构,MyBatis-Plus做数据访问,H2+MySQL双环境支持,Lombok简化实体,Spring Security控制权限粒度,且所有表结构、初始化数据、接口契约全部开源可查。适合高校课程设计验收、中小心理机构快速搭建轻量服务平台,或Java工程师练手全栈开发——重点不在炫技,而在把“心理咨询”这个垂直场景里的业务约束(如72小时响应承诺、会话不可篡改、日志留痕)真正落地到代码和配置里。
2. 用SpringBoot 2.3.12.RELEASE + MyBatis-Plus 搭建可运行的最小骨架
2.1 为什么选SpringBoot 2.3.x而非3.x?版本兼容性与生态成熟度权衡
当前(2024年中)SpringBoot 3.x要求JDK 17+且全面切换到Jakarta EE 9命名空间,而大量心理行业合作方仍使用JDK 8/11环境,且MyBatis-Plus 3.4.x对Jakarta EE的支持存在已知Mapper扫描异常。本项目采用SpringBoot 2.3.12.RELEASE(2021年LTS版本),搭配MyBatis-Plus 3.4.3.4,确保在JDK 8u291+环境下零配置启动。关键依赖声明如下:
<!-- pom.xml 核心依赖 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>提示:若强制升级至SpringBoot 3.x,需同步替换
javax.servlet.*为jakarta.servlet.*,并确认mybatis-plus-boot-starter版本≥3.5.3.1,否则Mapper接口无法被自动代理。
2.2 数据库初始化:H2内存库快速验证 + MySQL生产库迁移路径
项目默认启用H2内存数据库(spring.h2.console.enabled=true),启动后访问http://localhost:8080/h2-console即可查看实时表结构。这是验证CRUD逻辑最安全的方式——无需安装MySQL,所有建表语句由MyBatis-Plus自动生成。核心配置如下:
# application-dev.yml spring: datasource: url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE username: sa password: h2: console: enabled: true path: /h2-console mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: assign_id # 使用雪花算法生成ID,避免MySQL自增主键暴露业务量 logic-delete-field: deleted # 全局逻辑删除字段 logic-delete-value: 1 logic-not-delete-value: 0当需要切换至MySQL时,仅需修改application-prod.yml中的spring.datasource.url,并执行SQL脚本src/main/resources/sql/init_mysql.sql。该脚本包含6张核心表:user_info(用户)、consultant_info(咨询师)、consultation_order(预约单)、chat_message(聊天记录)、sensitive_word(敏感词库)、system_log(操作日志)。每张表均含create_time、update_time、deleted三字段,符合等保2.0日志审计要求。
2.2.1 敏感词表sensitive_word的设计要点
心理咨询场景中,用户可能输入“自杀”“抑郁”等高危词汇,系统需实时拦截并触发预警。sensitive_word表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 主键 |
| word | VARCHAR(50) | 敏感词原文(如“割腕”) |
| replace_word | VARCHAR(50) | 替换词(如“***”) |
| level | TINYINT | 风险等级(1=低危,2=中危,3=高危) |
| is_enabled | TINYINT | 是否启用(0=禁用,1=启用) |
MyBatis-Plus通过@TableName("sensitive_word")映射实体类,查询时使用QueryWrapper.eq("is_enabled", 1)只加载生效词库。实际调用时,采用KMP算法预编译词典(见3.3节),避免每次请求都全表扫描。
2.3 启动类与包结构:按领域划分模块,拒绝“all in one”
SpringBoot默认扫描启动类所在包及其子包,因此项目采用标准DDD分层结构:
com.example.mentalhealth ├── MentalHealthApplication.java // 启动类,位于根包 ├── config/ // 配置类(SecurityConfig, MyBatisPlusConfig) ├── controller/ // REST控制器(UserController, OrderController) ├── service/ // 业务接口及实现(IOrderService, OrderServiceImpl) ├── mapper/ // MyBatis Mapper接口(UserMapper, ChatMessageMapper) ├── entity/ // 实体类(UserInfo, ConsultationOrder) ├── dto/ // 数据传输对象(OrderCreateDTO, ChatMessageDTO) └── util/ // 工具类(SensitiveWordFilter, IdWorker)关键点在于:MentalHealthApplication必须置于最外层包,否则@MapperScan("com.example.mentalhealth.mapper")无法扫描到Mapper接口。若误将启动类放在controller包下,会导致NoSuchBeanDefinitionException。
3. 实现心理咨询核心流程:从用户预约到消息加密存储
3.1 用户注册与咨询师资质审核的双重校验逻辑
心理健康服务涉及《精神卫生法》合规要求,普通用户与咨询师注册流程必须分离。系统通过user_type字段区分角色(0=普通用户,1=咨询师),并在注册时强制校验:
- 普通用户:手机号唯一性校验 + 短信验证码(集成阿里云SMS SDK)
- 咨询师:除手机号外,需上传执业证书图片(存OSS),填写专业方向(婚姻家庭/青少年/职场压力等),并由管理员后台人工审核(
status字段:0=待审,1=通过,2=驳回)
关键代码片段(UserController.java):
@PostMapping("/register") public Result register(@Valid @RequestBody UserRegisterDTO dto) { // 1. 校验短信验证码(Redis缓存,5分钟过期) String cacheKey = "sms:" + dto.getPhone(); String cachedCode = redisTemplate.opsForValue().get(cacheKey); if (!Objects.equals(cachedCode, dto.getSmsCode())) { return Result.fail("验证码错误"); } // 2. 检查手机号是否已注册 QueryWrapper<UserInfo> wrapper = new QueryWrapper<>(); wrapper.eq("phone", dto.getPhone()); if (userInfoService.count(wrapper) > 0) { return Result.fail("手机号已被注册"); } // 3. 创建用户(咨询师需额外设置资质字段) UserInfo user = new UserInfo(); user.setPhone(dto.getPhone()); user.setPassword(BCryptPasswordEncoder.encode(dto.getPassword())); // 密码BCrypt加密 user.setUserType(dto.getUserType()); if (dto.getUserType() == 1) { // 咨询师 user.setCertificateUrl(dto.getCertificateUrl()); // 证书OSS地址 user.setSpecialty(dto.getSpecialty()); // 专业方向 user.setStatus(0); // 待审核 } else { user.setStatus(1); // 普通用户直接激活 } userInfoService.save(user); return Result.success(); }注意:
BCryptPasswordEncoder是Spring Security提供的强哈希算法,比MD5/SHA256更抗彩虹表攻击。密码字段在数据库中为VARCHAR(100),足够存储BCrypt生成的72字符哈希值。
3.2 预约订单状态机:从创建、支付到超时关闭的完整生命周期
心理咨询预约需严格遵循“先预约、再支付、后履约”流程,避免资源空占。系统定义5种状态:
| 状态码 | 名称 | 触发条件 |
|---|---|---|
| 0 | 待支付 | 用户提交预约单 |
| 1 | 已支付 | 支付宝/微信回调成功 |
| 2 | 已开始 | 咨询师点击“开始咨询” |
| 3 | 已结束 | 双方确认结束或超时自动关闭 |
| 4 | 已取消 | 用户/咨询师主动取消 |
状态变更通过ConsultationOrderService.updateStatus()方法统一处理,禁止直接SQL更新。例如支付成功回调:
// 支付宝异步通知处理 @PostMapping("/alipay/notify") public String alipayNotify(HttpServletRequest request) { Map<String, String> params = getParameterMap(request); if (AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, CHARSET)) { String outTradeNo = params.get("out_trade_no"); // 商户订单号 String tradeStatus = params.get("trade_status"); if ("TRADE_SUCCESS".equals(tradeStatus)) { // 更新订单状态为“已支付” ConsultationOrder order = new ConsultationOrder(); order.setId(Long.valueOf(outTradeNo)); order.setStatus(1); order.setPayTime(new Date()); consultationOrderService.updateById(order); // 发送站内信通知咨询师 messageService.sendSystemMessage( "您有一条新预约订单,请及时处理", order.getConsultantId() ); } } return "success"; }3.3 聊天消息加密存储:AES-128-CBC + 随机IV保障会话隐私
心理咨询对话属于高度敏感个人信息,《个人信息保护法》要求存储时须加密。系统采用AES-128-CBC模式,密钥由application.yml中encrypt.key配置(生产环境应存于Vault),每次加密生成随机IV并存入数据库:
// ChatMessageService.java public void saveEncryptedMessage(ChatMessage message) { String plainText = message.getContent(); byte[] key = AESUtil.generateKey("your-16-byte-key"); // 生产环境从配置中心读取 byte[] iv = AESUtil.generateIV(); // 16字节随机向量 String encryptedContent = AESUtil.encrypt(plainText, key, iv); message.setContent(encryptedContent); message.setIv(Base64.getEncoder().encodeToString(iv)); // IV转Base64存库 chatMessageMapper.insert(message); } // AESUtil.java(简化版) public static String encrypt(String content, byte[] key, byte[] iv) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encrypted); } catch (Exception e) { throw new RuntimeException("AES加密失败", e); } }提示:IV必须随密文一同存储,且每次加密都生成新IV。若复用IV,相同明文会产生相同密文,导致频率分析攻击风险。
4. 敏感词实时过滤与高危行为预警机制
4.1 基于AC自动机的敏感词匹配引擎
H2内存库中sensitive_word表仅存词库,但实时匹配不能每次查库。系统启动时将所有启用词加载进内存,并构建AC自动机(Aho-Corasick Algorithm):
@Component public class SensitiveWordFilter { private final TrieNode root = new TrieNode(); @PostConstruct public void init() { List<SensitiveWord> words = sensitiveWordService.list( new QueryWrapper<SensitiveWord>().eq("is_enabled", 1) ); for (SensitiveWord word : words) { insert(word.getWord(), word.getLevel()); } buildFailureLink(); } private void insert(String word, int level) { TrieNode node = root; for (char c : word.toCharArray()) { node = node.children.computeIfAbsent(c, k -> new TrieNode()); } node.isEnd = true; node.level = level; } public FilterResult filter(String text) { // AC自动机匹配,返回最高风险等级及命中词列表 // ... 实现略 } }当用户发送消息时,ChatMessageController调用filter()方法:
@PostMapping("/send") public Result sendMessage(@RequestBody ChatMessageDTO dto) { FilterResult result = sensitiveWordFilter.filter(dto.getContent()); if (result.getMaxLevel() >= 2) { // 中危及以上触发预警 alertService.sendAlert(dto.getUserId(), dto.getContent(), result.getHitWords()); } // 替换敏感词后存库 String filteredContent = result.getFilteredText(); chatMessageService.saveEncryptedMessage(...); return Result.success(); }4.1.1 预警消息推送策略
高危词(level=3)触发三级响应:
- 立即通知值班心理咨询师(企业微信机器人)
- 记录
alert_log表,包含用户ID、原始内容、匹配词、触发时间 - 若10分钟内连续触发3次,自动冻结用户账号并邮件通知管理员
4.2 数据库查询优化:针对咨询师排班的复合索引设计
咨询师首页需展示“未来7天可预约时段”,查询语句为:
SELECT * FROM consultant_schedule WHERE consultant_id = ? AND date >= '2024-06-01' AND status = 1 ORDER BY date, start_time;若无索引,百万级排班表将全表扫描。正确做法是创建联合索引:
-- 在consultant_schedule表上创建 CREATE INDEX idx_consultant_date_status ON consultant_schedule (consultant_id, date, status);该索引覆盖查询所有WHERE条件字段,且date在status前,因date范围查询(>=)需放在索引左侧。执行计划显示type=range,key=idx_consultant_date_status,性能提升90%以上。
5. 生产环境部署与数据库安全加固实操
5.1 MySQL参数调优:应对心理咨询系统的写多读少特征
心理咨询系统日志、聊天记录、预警事件产生大量写入,而用户查询集中在个人历史订单。需调整InnoDB参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
innodb_buffer_pool_size | 物理内存70% | 缓冲池越大,写入刷盘越少 |
innodb_log_file_size | 512MB | 大日志文件减少checkpoint频率 |
innodb_flush_log_at_trx_commit | 2 | 每秒刷一次log,平衡持久性与性能 |
max_connections | 500 | 预估并发用户数×3 |
修改后重启MySQL并验证:
# 查看当前缓冲池使用率 mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | grep "Buffer pool hit rate" # 检查慢查询(阈值设为0.5秒) SET long_query_time = 0.5; SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;5.2 SpringBoot配置安全:禁用危险端点与密钥管理
生产环境必须关闭H2控制台及Actuator未授权端点:
# application-prod.yml management: endpoints: web: exposure: include: health,info,metrics # 仅开放必要端点 endpoint: health: show-details: never # 不暴露详细健康信息 spring: h2: console: enabled: false # 禁用H2控制台 datasource: url: jdbc:mysql://prod-db:3306/mentalhealth?useSSL=false&serverTimezone=Asia/Shanghai username: ${DB_USER:mental_app} # 从环境变量读取 password: ${DB_PASSWORD:change_me} # 强制外部注入数据库密码绝不可硬编码。推荐方案:
- Docker部署:通过
--env-file传入.env文件 - Kubernetes:使用Secret挂载到Pod环境变量
- 本地测试:
export DB_PASSWORD="real_password"后启动
5.3 一键导出全量数据的脱敏脚本
系统需定期向监管机构报送匿名化数据。提供DataAnonymizer.java工具类,对导出SQL执行字段脱敏:
| 字段 | 脱敏规则 | 示例 |
|---|---|---|
user_info.phone | 保留前3后4位 | 138****1234 |
user_info.id_card | 保留前6后4位 | 110101****1234 |
chat_message.content | AES解密后关键词替换 | “我想死” → “我感到情绪低落” |
执行命令:
# 生成脱敏后的SQL文件 java -cp target/mentalhealth.jar \ com.example.mentalhealth.util.DataAnonymizer \ --input sql/full_backup.sql \ --output sql/anonymized_20240601.sql \ --rules config/anonymize-rules.json该脚本会解析原始SQL,对INSERT语句中的敏感字段应用规则,生成符合《信息安全技术 个人信息安全规范》GB/T 35273-2020要求的脱敏数据。
本文还有配套的精品资源,点击获取