尧图精选

Spring Boot居家康复管理系统毕设实战:从需求设计到部署全流程

🕒 发布时间:2026/9/21 5:05:12 📁 来源:尧图网络
做毕业设计的时候很多人一上来就问我居家康复管理系统和医院HIS系统到底有什么区别老实说这个事想清楚比多写几百行代码重要得多。Spring Boot Java做网站后端这套玩法在毕设里不算新鲜但“居家康复”这个场景里真正难住人的往往不是技术而是业务逻辑怎么落地——康复计划怎么分配、打卡记录怎么统计、复诊提醒怎么触发。这篇文章就拿我这边搭建的这套基于Spring Boot的社区慢病康复与健康管理服务平台为例把需求设计、表结构、核心接口和部署细节一步步拆开讲透适合正在做Spring Boot毕设、或者想了解Java Web项目完整流程的同学参考。1. 居家康复系统与普通医疗系统的本质区别需求拆解先行1.1 业务痛点出院后康复过程为什么无人跟踪很多慢性病和术后患者在医院里恢复得不错一出院就断了线。患者回到家没人指导训练家属也不知道哪个动作该做、哪个动作不能做复诊时间一拖再拖最后病情反复又得住进去。这就是居家康复管理系统要解决的核心痛点把康复过程从医院内延伸到院外用系统化的手段跟踪患者每天的康复状况。所以这个系统不是简单做一个“病历管理系统”它关注的是持续性的过程管理。我在需求分析阶段专门跟康复科医生聊过医生反复提到一个需求患者每天做了什么训练、身体指标有什么变化、有没有异常这些数据如果能够在复诊前就提前汇总到医生手上医生看诊的效率会高很多。这就决定了系统的业务主链路制定计划 → 执行打卡 → 异常预警 → 复诊评估。1.2 系统角色拆分患者、家属、康复医师、管理员各要什么我在做角色设计时没有照搬常规后台管理系统的“用户管理员”两角色模型而是按实际使用场景拆成了四类患者移动端/网页端完成每日康复打卡查看自己的康复计划、健康宣教文章、复诊提醒。家属帮助不熟悉手机操作的老人查看进度接收异常提醒辅助督促训练。康复医师创建患者的康复方案查看打卡数据与健康指标趋势给出阶段评估结论。系统管理员维护用户账号、科室信息、公告内容、数据统计查看。这四类角色分开建模很关键。如果你直接把所有功能堆在一个用户模型里后面做权限控制时会非常痛苦——因为患者想看的是“今天练什么”医生想看的是“这个月所有人的完成率”管理员想看的是“整个平台的使用量”三者数据范围完全不同。所以我在数据库设计阶段就把角色字段和菜单权限绑定在一起后续所有查询都带着角色维度去过滤数据。1.3 用问题清单替代原型图先缕清核心流程老实说很多毕设项目垮掉不是因为代码写不出来而是需求想得太少。我建议在动手前先列几个问题患者提交的打卡数据由谁审核还是只记录不审核康复计划是一对一制定还是可以批量套用模板复诊提醒是在计划结束时自动生成还是医生手动创建异常指标的预警阈值是固定值还是每个患者单独设置这几个问题想清楚以后系统的边界就出来了。以我做的这个项目为例打卡数据不需要医生逐条审核但系统会根据指标阈值自动标记异常记录医生只关注异常即可康复计划支持从模板复制再按患者情况调整复诊提醒由系统根据计划周期自动生成医生可以修改提醒时间。这些决策直接影响后面的表结构和接口数量。2. 功能模块与后端接口设计用一张清单理清系统全貌2.1 患者端与家属端核心功能围绕“高频打卡”展开患者端的核心不是信息展示而是打卡这个高频动作要足够顺。每次打卡需要填写的内容包括康复项目对应计划中的某个训练、训练时长、难度感受、心率/血压等可选项指标、备注。考虑到患者群体普遍不是年轻人打卡页面的字段必须精简后端接口也要做好参数校验。患者端的主要模块首页看板今日待办训练、连续打卡天数、最近一次评估结果计划详情当前康复阶段、每个阶段包含的训练项目打卡记录日历视图展示打卡历史标记异常记录复诊提醒按时间线展示预约记录支持提醒开启/关闭健康宣教文章列表与详情2.2 康复医师端数据看板与批量操作是重点医生的核心诉求是“少点几次、一次看够”。所以医生端的页面要以数据维度来组织计划模板管理、患者康复进度列表、异常指标患者列表、阶段评估结果。这里我特意做了一个批量操作医生可以按诊断类型筛选患者然后统一调整康复计划模板不用一张张卡去修改。2.3 管理员端系统配置不追求复杂管理员端相对简单账号管理、角色权限配置、系统公告、基础数据统计。统计页面用简单的柱状图就够了例如近7天新增患者数、各病种分布、打卡完成率。注意管理端不要去接复杂报表毕设项目里报表复杂了只会给自己挖坑。2.4 RESTful接口规范统一返回体和状态码设计后端所有接口我都采用统一返回结构避免手机端和Web端各写一套解析逻辑{ code: 200, message: success, data: { } }状态码我自定义了几个常用值200成功、400参数错误、401未登录或Token失效、403无权限、500服务器异常。实际开发中我遇到很多同学喜欢直接用HTTP状态码去表达业务错误比如登录失败就返回401参数不对就返回400。这样做的结果是前端根本分不清“参数错误”和“未登录”的区别因为浏览器会直接拦截401响应干扰正常的前端逻辑。所以业务层面的状态码和HTTP状态码一定要分开设计HTTP层只用200表示请求已到达服务器具体业务成功与否看响应体里的code。3. 数据库设计6张核心表怎么撑起整套系统3.1 核心表清单与字段设计数据库是毕设项目里最能体现基本功的部分。我这边按照业务主链路设计了6张核心表表名说明关键字段user用户表包含四类角色username, password, role_type, statuspatient_profile患者档案表与user一对一user_id, diagnosis, illness_history, allergy, emergency_contactrehab_plan康复计划表一患者多计划patient_id, plan_name, start_date, end_date, statusrehab_task计划子项表一计划多训练任务plan_id, task_name, task_type, target_duration, frequencycheck_record打卡记录表task_id, patient_id, check_date, duration, difficulty, metrics, statusappointment复诊预约表patient_id, doctor_id, appoint_date, remind_status, conclusion这6张表基本构成了“计划—任务—打卡—复诊”的闭环。第7张表是系统公告表和消息通知表属于辅助功能按需添加就行。3.2 关键表结构设计思路以打卡记录表为例我会做这样的设计CREATE TABLE check_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, task_id BIGINT NOT NULL COMMENT 关联康复任务, patient_id BIGINT NOT NULL COMMENT 患者ID, check_date DATE NOT NULL COMMENT 打卡日期, duration_minutes INT DEFAULT 0 COMMENT 实际训练时长, difficulty_level TINYINT COMMENT 难度感受1-5分, metrics_json VARCHAR(500) COMMENT 指标数据如血压心率, status TINYINT DEFAULT 0 COMMENT 状态0正常 1异常标记, remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_patient_date (patient_id, check_date), KEY idx_task (task_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个设计细节需要注意。第一metrics_json字段用JSON格式存储血压、心率等动态指标而不是单独建一张指标表。因为患者每次打卡记录的指标数量不固定有的测血压有的测血糖关系型表结构会很死板。第二索引一定要建组合索引patient_id, check_date因为“某个患者某段时间内的打卡记录”是最常见的查询模式单查task_id的频率反而不高。3.3 为什么这套表结构不适合做分库分表很多同学看完技术文章以后总想给毕设上微服务、分库分表、消息队列这是典型的“为了技术而技术”。按这个系统的数据量来看——就算每天有1000个患者打卡一个月也才3万条记录单表300万以内MySQL都扛得住。分库分表不仅不会带来性能提升反而会让join查询变得极其复杂、开发周期翻倍。项目选型的标准应该是当前业务量下最简单的方案能满足需求就是最好的方案。3.4 慢查询隐患在这张表上特别容易出现打卡记录表最容易出现的慢查询场景是“统计一段时间内所有患者的打卡完成率”。如果没有索引MySQL就得全表扫描。解决方法是把统计类的查询拆成两步先按patient_id和时间范围查打卡次数再在Java代码里做完成率计算。实在需要复杂统计可以建一张汇总表用定时任务每天凌晨汇总前一天的数据查询时直接读汇总结果。4. Spring Boot工程搭建与核心编码实现从登录到健康监测全链路4.1 工程结构与依赖选型我的工程结构如下用的是标准的Controller-Service-Mapper分层com.rehab ├── controller # 接口层 ├── service # 业务逻辑层 ├── mapper # MyBatis Plus的Mapper接口 ├── entity # 数据库实体类 ├── dto # 前端交互对象 ├── vo # 视图返回对象 ├── config # 配置类拦截器、跨域等 ├── common # 统一返回体、异常处理器、工具类 └── task # 定时任务依赖选型我用了Spring Boot 2.7.x MyBatis Plus 3.5.x Redis MySQL JWT。之所以选MyBatis Plus而不是原生MyBatis是因为它对单表CRUD的封装足够好用selectById、insert这些都是现成的省下大量重复的XML编写时间我只需要在真正涉及多表关联的查询里手写SQL开发效率高很多。多说一句版本的问题。Spring Boot版本太高容易遇到依赖兼容性问题比如Spring Boot 3.x要求JDK 17以上部分MyBatis Plus版本没跟上会导致启动报错。我当时直接用2.7.18JDK 8稳定教程多遇到问题随便一搜就有答案。4.2 登录认证与JWT工具类的实现逻辑登录接口的核心逻辑根据用户名查出用户 → 比对密码摘要 → 生成Token → 返回用户基本信息。密码存储用的是BCrypt加密这是Spring Security自带的一个加密工具同一个密码每次生成的哈希值都不同比MD5加盐更安全。我这里的项目没有全套引入Spring Security因为毕设项目只需要登录认证和权限校验两层用拦截器就能实现没必要引入那么重的安全框架。Token的生成我用了现成的io.jsonwebtoken库核心方法public String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 3600 * 1000L)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }Token有效期设成7天患者端体验比较好不用天天重新登录。医生端因为涉及敏感数据我建议把有效期缩短到2天根据角色来定过期时间。这里有个细节secretKey一定不能写在代码里硬编码应该放到application.yml配置文件中打包部署的时候再通过环境变量注入。4.3 康复打卡接口的完整实现打卡是整个系统最关键的一个接口。患者点击打卡时系统要做的不是简单insert一条记录而是要经过四步校验校验该任务是否属于当前登录患者的康复计划校验当前时间是否在计划的起止日期内校验当天是否已经打过卡防止重复提交校验核心指标是否在合理范围内比如心率在30—200之间校验通过后执行插入同时更新缓存中的连续打卡天数并判断是否存在异常指标需要标记。Override public ResultString submitCheck(CheckDTO dto, Long patientId) { RehabTask task rehabTaskMapper.selectById(dto.getTaskId()); if (task null || !task.getPatientId().equals(patientId)) { return Result.error(任务不存在或无权操作); } Date today new Date(); if (today.before(task.getStartDate()) || today.after(task.getEndDate())) { return Result.error(当前不在计划执行期内); } Integer count checkRecordMapper.selectCount( new LambdaQueryWrapperCheckRecord() .eq(CheckRecord::getTaskId, dto.getTaskId()) .eq(CheckRecord::getCheckDate, DateUtil.today())); if (count 0) { return Result.error(今日已经打过卡了); } // 指标合理性校验 if (dto.getHeartRate() ! null (dto.getHeartRate() 30 || dto.getHeartRate() 200)) { return Result.error(心率数值异常请复核后填写); } CheckRecord record new CheckRecord(); // ... 属性赋值 checkRecordMapper.insert(record); // 判断是否需要标记异常 boolean abnormal checkAbnormal(record); if (abnormal) { pushDoctorAlert(patientId, record); } return Result.success(打卡成功); }这段代码的逻辑已经能覆盖大部分业务场景也体现了三层安全意识归属校验、防重复、数值边界。很多同学写接口只管“能插进去就行”没有做前置校验这在答辩时很容易被问到“如果患者把心率填成500呢如果患者重复提交呢”提前把这些防御逻辑写完整比临时想答案要靠谱得多。4.4 复诊提醒的定时任务实现复诊提醒功能用的是Spring Boot自带的Scheduled定时任务。每天上午10点扫描一次找出所有计划到期时间在未来3天内、且未创建复诊预约记录的患者自动生成一条预约记录并推送通知。Component public class AppointmentRemindTask { Scheduled(cron 0 0 10 * * ?) public void generateRemind() { ListPatientProfile patients patientProfileMapper.selectExpiringPatients(3); for (PatientProfile patient : patients) { appointmentService.createAutoAppointment(patient); messageService.push(patient.getId(), 您的康复计划即将结束请及时预约复诊。); } } }这里有一个教训添加Scheduled注解后必须在启动类上加EnableScheduling不然定时任务不会生效。我曾经排查了一个下午才发现任务没触发的原因就是这个注解漏了这个坑在后面的踩坑章节会详细展开。5. 权限控制与操作安全不能只有登录没有校验5.1 拦截器 角色注解实现接口级权限很多毕设项目只做了登录校验没有做接口级权限控制导致患者登录后可以直接调用医生管理接口、上传公告这是一个非常严重的漏洞。我是这样设计的写一个AuthInterceptor拦截所有请求通过JWT解析出当前用户的角色再配合自定义注解RequireRole来控制接口访问权限。public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String path request.getRequestURI(); if (OPTIONS.equals(request.getMethod())) { return true; } if (path.startsWith(/api/auth/login)) { return true; } String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.setStatus(401); return false; } Long userId JwtUtil.getUserId(token); String role JwtUtil.getRole(token); request.setAttribute(userId, userId); request.setAttribute(role, role); // 检查注解 if (handler instanceof HandlerMethod) { RequireRole requireRole ((HandlerMethod) handler).getMethodAnnotation(RequireRole.class); if (requireRole ! null !ArrayUtils.contains(requireRole.value(), role)) { response.setStatus(403); return false; } } return true; } }拦截器里有一个必须处理的点跨域请求的OPTIONS预检请求一定要直接放行否则前端通过浏览器访问时会发现请求被拦截实际上后端根本没进Controller。这个坑我当时花了不少时间排查。5.2 敏感操作二次校验与操作日志除了登录拦截我在修改患者档案、删除康复计划这类敏感操作上还加了一层“本人确认”逻辑前端弹窗二次确认后后端会再校验当前登录用户是否为该患者的绑定家属或负责医师。另外设计了一张operation_log表记录谁在什么时间操作了什么内容这在答辩时是一个亮点也会在实际项目中被追问到。5.3 数据脱敏与隐私保护居家康复数据属于医疗健康数据在展示时需要注意脱敏。比如患者列表页展示的手机号中间四位用*替代家属端只能看到患者的康复数据不能看到诊断详情里的过敏史等信息。这个逻辑我是在VO层做的脱敏也就是数据库查出来的是完整数据在返回给前端之前做字段处理避免把脱敏逻辑写到SQL里搞乱查询。6. 开发过程中真实踩过的四个坑排查链路全记录6.1 坑一LocalDateTime返回给前端变成“yyyy-MM-dd‘T’HH:mm:ss”这是Spring Boot开发里出了名的序列化问题。Java 8的LocalDateTime默认序列化成ISO格式中间带一个T。患者的打卡记录页需要展示“2024-06-01 10:30”结果前端拿到的是“2024-06-01T10:30:00”直接显示会把T也渲染出来非常难看。排查链路先在前端Network面板看接口返回确实有T→ 确定是后端序列化问题 → 检查是否配置了jackson的日期格式 → 发现只在application.yml里写了spring.jackson.date-format但这个配置只对java.util.Date生效对LocalDateTime无效。最终解决方案是引入统一配置类Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder - builder.simpleDateFormat(yyyy-MM-dd HH:mm:ss) .serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))) .deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))); }6.2 坑二前端传日期字符串时后端接收报400打卡接口传check_date参数时前端用的是2024-06-01字符串后端Controller定义的是LocalDate类型结果一直报400错误。排查链路先把报错堆栈拉出来看到JSON parse error→ 用Postman直接调接口还是400 → 确认反序列化配置缺失 → 添加LocalDateDeserializer。这个问题的根因和坑一类似都是Java 8时间类型与JSON格式的适配问题。所以在一开始我建议就把全局的Jackson时间格式化配置类写好不要等前端同事来反馈。6.3 坑三JWT拦截器导致登录后仍然访问受限做了一个较长时间的联调后突然发现部分接口有时能用、有时不能用而且401错误不固定。排查半天才发现是拦截器的白名单路径里写了/api/auth/**按理说登录接口和验证码接口都能放行但是Controller里有一个/api/auth/user/register是通过通配符/**拦下来的吗不是问题出在放行规则里我没有处理验证码接口的路径。这个坑给我们的教训是保持路径前缀规划的一致性。所有白名单接口都放在/api/auth/前缀下业务接口不要和认证接口混在一起用同一个前缀这样放行规则写起来就会非常清晰。6.4 坑四Scheduled定时任务没有生效复诊提醒功能上线后一直没有收到推送提醒。排查链路如下先检查数据库里是否有符合条件的患者记录——有再手动调generateRemind()方法——执行正常能生成提醒那问题就出在定时任务没被调度。检查启动类发现确实忘了加EnableScheduling。补上后第二天上午10点任务准点执行。这个坑看起来很低级但它很有代表性。Spring Boot的很多能力不是引入依赖就能生效而是必须通过启动类上的注解显式开启包括EnableScheduling、EnableCaching、EnableAsync这三个我建议在动手之前就加到启动类上避免后续排错浪费时间。7. 打包部署与答辩准备从本地环境到服务器上线7.1 打包前的配置文件处理本地开发和线上环境至少有两处配置不一样数据库连接和JWT密钥。我的做法是维护两份配置文件application.yml # 公共配置 application-dev.yml # 本地开发环境 application-prod.yml # 线上生产环境打包时指定profilemvn clean package -DskipTests -Dprod或者启动时指定java -jar rehab-server.jar --spring.profiles.activeprod这里有一个实际工作中常见的惨痛教训有人把生产数据库的密码直接写死在application.yml里然后传到公共代码仓库导致泄露。正确的做法是使用环境变量占位符spring: datasource: password: ${DB_PASSWORD}部署时先export DB_PASSWORDxxx再启动代码仓库里的配置就没有敏感信息了。7.2 服务器环境准备与Systemd守护我用的部署方案是阿里云轻量服务器2核4G MySQL 8.0 JDK 1.8 Maven打包。上线部署的关键步骤安装JDK和MySQL初始化数据库表结构把打包好的jar上传到服务器用Systemd配置服务守护保证进程意外退出后自动重启Systemd配置文件参考[Unit] DescriptionRehab Server Afternetwork.target [Service] Userroot EnvironmentFile/etc/rehab.env ExecStart/usr/local/java/bin/java -Xms256m -Xmx512m -jar /opt/rehab/rehab-server.jar Restartalways RestartSec5 [Install] WantedBymulti-user.target用Systemd的好处是开机自启、崩溃自动重启、日志统一用journalctl -u rehab查看比自己在终端里跑nohup java -jar要规范很多。7.3 验收测试清单答辩前自测一遍很多同学答辩前只测了“能登录、能注册、能录入数据”就上场了一旦评委随机点一个按钮就露馅。我总结了这份验收清单建议答辩前完整过一遍测试场景操作步骤预期结果未登录访问业务接口删除Token直接请求返回401患者访问医生接口患者角色Token请求医生列表返回403重复打卡同一任务当天二次提交提示“今日已打卡”修复数据权限医生A查看医生B的患者查询结果为空定时任务触发把系统时间调后到计划结束时自动生成复诊预约部署环境切换启动时指定prod环境连接线上数据库正常7.4 答辩演示时的演示顺序建议答辩演示的路线要体现业务闭环不要按前后端模块逐页演示。我的建议是先演示患者端打卡 → 再登录医生端查看打卡数据与异常指标 → 然后演示计划到期后系统自动生成复诊提醒 → 最后切到管理端展示用户数据和统计图表。这条路线能让评委完整看到“计划—执行—监控—复诊”的闭环比零散地展示每个页面有说服力得多。我个人在实际开发中的体会是居家康复管理系统最大的难点不在某个单独的技术点而是整个链条上的业务逻辑自洽。只要把扣住“康复计划—每日打卡—异常预警—复诊闭环”这条主线Spring Boot的代码写起来其实非常顺。最后再分享一个小建议如果你也是拿这个题目做毕设建议在前期多花时间梳理表结构和接口清单这一步稳了后面编码你几乎不会遇到大改整个项目的推进速度会比预期快很多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →