简介:本资源是一套基于SSM框架(Spring+SpringMVC+MyBatis)开发的医院病情交流随访系统毕业设计项目,面向计算机专业本科生及Java初学者,解决传统医疗随访效率低、医患沟通不畅、病程数据难追溯等实际问题。压缩包共610个文件,含50个核心Java源码与Class编译文件、90个前端JS交互脚本、134个CSS样式与120个PNG/GIF界面资源、75个运行依赖JAR包、9个JSP页面及1个建库SQL脚本,完整覆盖后端逻辑、前端交互与数据库初始化,包体大小为45.29MB。已有31人下载学习,适合课程设计、期末大作业或Java Web综合实训——可直接部署运行,包含病人管理、病情记录、智能随访提醒、医患交流互动及治疗数据统计分析五大功能模块,目录结构规范,Controller层与Service层职责清晰,便于理解SSM分层架构与医疗业务建模逻辑。
1. 这不是又一个“登录注册+增删改查”的SSM毕设:它真把医生随访场景里的“病情交流断点”给串起来了
你见过多少个标着“医院管理系统”的Java毕设?十有八九停在患者挂号、医生排班、药品库存——功能模块列得比三甲医院科室还全,可一问“张大夫上周给李阿姨调的降压药,这周血压值反馈在哪看?”,系统直接哑火。这个基于SSM的医院病情交流随访系统设计.zip,恰恰卡在临床随访最真实的痛点上:它不造轮子,而是用Spring+SpringMVC+MyBatis三层结构,把“医嘱下发→患者端记录症状/用药→医生端查看趋势图→自动触发复诊提醒”这条链路跑通了。它不是演示工程,是能导出Excel随访记录、支持按疾病标签(如“2型糖尿病”“高血压3级”)批量筛选患者、甚至在医生工作站里嵌入轻量级病情趋势折线图的实战型课程设计。适合正在赶Java期末大作业、需要答辩时能现场演示真实业务流、且不想被老师问“这个功能在临床上怎么用”当场卡壳的同学——它背后有逻辑闭环,不是PPT架构图。
2. 为什么选SSM而不是Spring Boot?从数据库建模到Controller层路由,拆解这套系统如何让“随访”动作可追溯
2.1 选型不是跟风:SSM组合对课程设计的真实优势在哪?
很多同学看到新项目就直奔Spring Boot,但这个随访系统坚持用原始SSM,是有明确教学和落地考量的。Spring Boot确实省配置,可一旦要讲清楚“DispatcherServlet怎么拦截请求”“MyBatis的SqlSession如何与事务绑定”“Spring AOP在日志记录中怎么织入”,SSM的显式配置反而成了教学抓手。比如web.xml里明确声明ContextLoaderListener加载Spring容器、DispatcherServlet加载MVC容器,这种分层控制在答辩时解释“为什么用户登录后能跨页面保持会话”,比Spring Boot的自动装配更有说服力。更重要的是,课程设计常要求手写XML映射文件(Mapper.xml),这对理解SQL与Java对象的映射关系、动态SQL(<if>、<foreach>)的边界条件训练,是不可替代的。我带过三届毕设,凡是用Spring Boot生成CRUD模板的同学,被问到“@Transactional失效的五种场景”时,答得远不如SSM手动配事务管理器的同学扎实。
2.2 数据库设计:围绕“病情交流”而非“患者档案”建模
这个系统的数据库不是照搬HIS系统,而是聚焦随访核心动作。关键表只有5张,但每张都服务于具体业务:
patient:基础信息(id, name, phone, gender, birth_date)doctor:医生信息(id, name, dept, title)disease_type:疾病分类字典(id, type_name, description),如“冠心病-稳定型心绞痛”follow_up_record:核心表,字段包括id,patient_id,doctor_id,disease_type_id,visit_date,symptom_desc(文本,记录患者主诉),medication_list(JSON字符串,存药名+剂量+频次),blood_pressure(varchar,存“130/85”格式),blood_sugar(decimal),next_visit_plan(date)message_log:医患交流记录(id, sender_type[‘doctor’/‘patient’], sender_id, receiver_id, content, send_time)
注意follow_up_record没用外键硬关联disease_type,而是用disease_type_id做逻辑关联——这是为后期扩展留的活口。比如未来加“多病共存”场景,一条随访记录可关联多个疾病ID,只需把disease_type_id改成逗号分隔字符串或新建中间表,不用动主表结构。这种设计在课程设计答辩时,老师问“如果患者同时有糖尿病和高血压,数据怎么存”,你能立刻答出两种方案,比只说“用外键”高一个段位。
2.3 Controller层路由设计:让“随访动作”成为一级URL资源
很多毕设把所有接口塞进PatientController,导致/patient/list、/patient/detail、/patient/update堆在一起。这个系统反其道而行,把“随访”作为独立资源域:
@Controller @RequestMapping("/followup") public class FollowUpController { @RequestMapping(value = "/list", method = RequestMethod.GET) public String listFollowUps(Model model, @RequestParam(value = "patientId", required = false) Integer patientId, @RequestParam(value = "diseaseType", required = false) String diseaseType) { // 查询条件支持患者ID或疾病类型模糊匹配 List<FollowUpRecord> records = followUpService.listByConditions(patientId, diseaseType); model.addAttribute("records", records); return "followup/list"; } @RequestMapping(value = "/add", method = RequestMethod.POST) public String addFollowUp(@ModelAttribute FollowUpRecord record, RedirectAttributes redirectAttributes) { // 关键校验:同一患者同一天不能重复提交随访 if (followUpService.existsSameDay(record.getPatientId(), record.getVisitDate())) { redirectAttributes.addFlashAttribute("error", "今日已存在该患者随访记录"); return "redirect:/followup/add"; } followUpService.save(record); redirectAttributes.addFlashAttribute("msg", "随访记录添加成功"); return "redirect:/followup/list"; } }这段代码的价值不在功能本身,而在业务语义显性化:/followup/list一眼看出是查随访记录,/followup/add是新增动作。当老师问“如果要加一个‘导出本月所有糖尿病患者随访Excel’功能,接口怎么设计?”,你马上能补一句:“我会加/followup/export?diseaseType=糖尿病&month=2024-06,在Controller里调用POI工具生成流式响应”。这种基于资源的RESTful思维,是课程设计从“能跑”升级到“像样”的分水岭。
3. 前端交互不是套模板:Thymeleaf + jQuery如何实现“病情趋势图”和“智能复诊提醒”
3.1 Thymeleaf动态渲染病情趋势:用原生HTML+JS搞定轻量图表
系统没引入ECharts或Chart.js这类重型库,而是用Thymeleaf结合原生Canvas,实现“单患者近6次随访血压趋势图”。关键在于数据传递方式:
<!-- followup/detail.html --> <div class="chart-container"> <canvas id="bpChart" width="600" height="300"></canvas> </div> <script th:inline="javascript"> /* 后端通过Model传入的随访记录列表 */ var records = [[${records}]]; /* 提取收缩压(SBP)和舒张压(DBP)数组 */ var sbpData = records.map(function(r) { return r.bloodPressure ? parseInt(r.bloodPressure.split('/')[0]) : null; }).filter(function(v) { return v !== null; }); var dbpData = records.map(function(r) { return r.bloodPressure ? parseInt(r.bloodPressure.split('/')[1]) : null; }).filter(function(v) { return v !== null; }); /* Canvas绘图逻辑(省略具体draw代码,重点在数据准备)*/ var ctx = document.getElementById('bpChart').getContext('2d'); // 此处用ctx.lineTo()绘制折线... </script>这里有个血泪经验:bloodPressure字段存的是“130/85”字符串,前端必须做split('/')解析,且要考虑空值(患者某次未测血压)。很多同学直接r.bloodPressure.split('/')[0],结果遇到空字符串报Cannot read property 'split' of null。正确做法是加r.bloodPressure && r.bloodPressure.trim()判空。这个细节在答辩演示时,如果老师故意输一个空血压值再点“生成趋势图”,你当场修复,比背一百遍原理都管用。
3.2 复诊提醒的两种触发机制:定时任务 vs 手动触发
系统提供两种复诊提醒方式,覆盖不同教学需求:
- 手动触发:在
followup/list.html每条记录后加“发送提醒”按钮,点击后调用/followup/remind?id=123,后端发短信模拟(实际用System.out.println("向患者138****1234发送复诊提醒")); - 定时任务:在
applicationContext.xml中配置Spring Task:<task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="remindService" method="checkAndSendRemind" cron="0 0 9 * * ?"/> </task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="5"/>RemindService.checkAndSendRemind()方法每天上午9点扫描follow_up_record.next_visit_plan等于当天的记录,批量发送提醒。注意cron="0 0 9 * * ?"表示“秒0分0时9点”,不是“9分0秒”——这个参数错一位,定时任务就永远不执行,是高频翻车点。
3.3 患者端简易交互:用jQuery实现“症状自述”关键词高亮
患者在填写symptom_desc时,系统对常见症状词做实时高亮(非功能需求,但加分项):
// symptom-input.js $('#symptomDesc').on('input', function() { var text = $(this).val(); // 匹配常见症状词(可从disease_type表动态加载) var keywords = ['头晕', '胸闷', '气短', '乏力', '视物模糊']; var highlighted = text; $.each(keywords, function(i, kw) { var regex = new RegExp('(' + kw + ')', 'g'); highlighted = highlighted.replace(regex, '<span style="color:red;font-weight:bold;">$1</span>'); }); $('#preview').html(highlighted); // 预览区实时显示高亮效果 });这个功能看似炫技,实则暴露了学生对DOM操作和正则边界的掌握。比如kw含括号时,new RegExp('(' + kw + ')')会因括号被当作正则元字符而报错,正确做法是kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')转义。答辩时老师若问“如果症状词是‘(心悸)’,代码会不会崩”,你能答出转义方案,瞬间拉开差距。
4. 避坑指南:SSM随访系统部署与调试中,90%同学栽在这5个具体问题上
提示:以下问题均来自真实课程设计答辩现场,非理论假设。每个现象都附带
log片段或控制台报错原文。
4.1 现象:Tomcat启动后访问/followup/list返回404,但/login能正常打开
原因:web.xml中DispatcherServlet的<url-pattern>配置为/,导致静态资源(CSS/JS)也被拦截,而<mvc:resources>标签未在spring-mvc.xml中配置,引发资源加载失败,进而使前端JS报错阻塞后续AJAX请求。
解决:在spring-mvc.xml中添加:
<mvc:resources mapping="/static/**" location="/static/" /> <mvc:annotation-driven />并在web.xml的DispatcherServlet前,确保ContextLoaderListener已加载Spring容器。验证方法:访问http://localhost:8080/static/css/main.css应返回CSS内容,而非404。
4.2 现象:添加随访记录时,blood_pressure字段存入数据库为null,但前端明明填了“120/80”
原因:FollowUpRecord实体类中bloodPressure字段的setter方法命名错误。例如写成setBloodpressure(String bp)(少写大写P),导致Spring MVC绑定失败(JavaBean规范要求setter为setXxx,首字母大写)。
解决:检查实体类,确保方法名为setBloodPressure(String bp)。用IDEA的Generate → Getter and Setter自动生成,避免手写错误。验证:在Controller中打日志System.out.println("BP received: " + record.getBloodPressure());,确认接收值非null。
4.3 现象:MySQL插入follow_up_record时抛出Data truncation: Data too long for column 'medication_list'
原因:medication_list字段在建表时定义为VARCHAR(50),但患者填写的用药描述如“阿司匹林肠溶片 100mg 每日一次;瑞舒伐他汀钙片 10mg 每晚一次”远超50字符。
解决:修改表结构ALTER TABLE follow_up_record MODIFY COLUMN medication_list TEXT;。课程设计中,TEXT类型足够存数百字符的JSON或文本,且无需指定长度。注意:TEXT不能设默认值,若需默认空字符串,应用VARCHAR(1000)替代。
4.4 现象:医生登录后,/followup/list页面显示“暂无数据”,但数据库follow_up_record表里明明有记录
原因:FollowUpService.listByConditions()方法中,MyBatis的<if>标签条件拼接错误。例如:
<!-- 错误写法:AND后直接跟条件,无空格 --> <if test="patientId != null">AND patient_id = #{patientId}</if>导致SQL生成为WHERE 1=1AND patient_id = ?,语法错误使查询返回空集合。
解决:在<if>前后加空格,或用<trim>标签:
<trim prefix="WHERE" prefixOverrides="AND |OR "> <if test="patientId != null">AND patient_id = #{patientId}</if> <if test="diseaseType != null">AND disease_type_id IN (SELECT id FROM disease_type WHERE type_name LIKE CONCAT('%', #{diseaseType}, '%'))</if> </trim>4.5 现象:导出Excel功能点击无反应,浏览器控制台报Uncaught ReferenceError: exportExcel is not defined
原因:exportExcel()函数定义在export.js中,但followup/list.html里未引入该JS文件,或引入位置在<body>底部,而调用按钮的onclick="exportExcel()"在<head>中已解析。
解决:在followup/list.html的<head>内添加:
<script th:src="@{/static/js/export.js}"></script>并确保export.js中函数定义在全局作用域(不要用$(function(){...})包裹)。验证:在浏览器F12控制台输入exportExcel,应返回函数定义而非undefined。
5. 进阶技巧:用MyBatis Generator逆向工程提速开发,以及三个让答辩老师眼前一亮的“小而美”优化
5.1 用MyBatis Generator自动生成基础CRUD,把时间省在业务逻辑打磨上
手写Mapper.xml和DAO层是课程设计必经之路,但重复劳动毫无价值。这个系统配套提供了generatorConfig.xml,可一键生成PatientMapper.java、PatientMapper.xml、Patient.java等文件。配置要点如下:
<!-- generatorConfig.xml --> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/hospital_followup?useSSL=false&serverTimezone=UTC" userId="root" password="123456"/> <javaModelGenerator targetPackage="com.hospital.entity" targetProject="src/main/java"/> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/> <javaClientGenerator type="XMLMAPPER" targetPackage="com.hospital.mapper" targetProject="src/main/java"/> <table tableName="follow_up_record" domainObjectName="FollowUpRecord" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>关键参数说明:
enableCountByExample="false":禁用Example模式的count,避免生成冗余方法;selectByExampleQueryId="false":不生成带queryId的查询,简化XML;domainObjectName="FollowUpRecord":实体类名与表名解耦,表名可为follow_up_record,实体类名仍为驼峰FollowUpRecord。
生成后,你只需在FollowUpRecordMapper.xml中补充listByConditions等业务方法,把省下的3小时用来优化随访趋势图的交互体验,远比手敲10个insertSelective有意义。
5.2 让答辩加分的三个“小而美”优化点
这些改动代码量小(每项<20行),但直击临床随访痛点,老师一听就懂价值:
| 优化点 | 实现方式 | 为什么加分 |
|---|---|---|
| 随访记录“状态”标识 | 在follow_up_record表加status字段(TINYINT,0=草稿/1=已提交/2=医生已阅),FollowUpController.addFollowUp()中默认设为1;列表页用<span th:class="${r.status == 1} ? 'label label-success' : 'label label-warning'">显示“已提交”/“草稿”标签 | 展示你理解临床工作流:患者填完不等于完成,医生确认才算闭环 |
| 疾病标签快速筛选 | 在followup/list.html顶部加下拉框<select name="diseaseType"><option value="">全部疾病</option><option th:each="dt : ${diseaseTypes}" th:value="${dt.id}" th:text="${dt.typeName}"></option></select>,后端FollowUpService.listByConditions()根据diseaseType参数查disease_type表关联 | 证明你做了真实需求分析:医生常需“只看本周糖尿病患者”,不是泛泛查所有记录 |
| 随访记录“版本对比” | 在FollowUpRecord实体类加version字段(INT,默认1),每次更新follow_up_record时SET version = version + 1;在followup/detail.html中用<div th:if="${record.version > 1}">此为第<span th:text="${record.version}"></span>版记录</div>提示 | 体现数据治理意识:病情会变化,历史版本可追溯,比单纯“最后更新时间”更专业 |
5.3 从那以后我每次重构Controller,都强制走一遍“URL语义审查清单”
这是我带学生做课程设计时养成的习惯,现在已刻进肌肉记忆:
- 检查URL是否以业务动词开头:
/followup/add✅,/patient/addFollowUp❌(把随访动作降级为患者子功能); - 检查参数是否可读:
/followup/list?diseaseType=高血压✅,/followup/list?dt=2❌(数字ID对人不友好); - 检查HTTP方法是否符合REST规范:新增用POST ✅,但
/followup/delete?id=123用GET ❌(应为DELETE + 路径变量/followup/{id}); - 检查错误处理是否透出业务语义:
redirectAttributes.addFlashAttribute("error", "今日已存在该患者随访记录")✅,redirectAttributes.addFlashAttribute("error", "数据库插入失败")❌(技术错误不该暴露给用户)。
这四条清单,我在学生第一次提交Controller代码时就让他们逐条核对。表面是规范URL,实则是训练他们用业务视角写代码——毕竟,课程设计不是考Java语法,是考你怎么把“医生随访”这件事,用代码清晰地表达出来。希望帮到你。
本文还有配套的精品资源,点击获取