ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot疫情病人治疗跟踪管理系统:从需求到答辩的全流程实战

SpringBoot疫情病人治疗跟踪管理系统:从需求到答辩的全流程实战 这里是博文内容直接开始不输出任何前置说明我去年做毕业设计时选的就是“新冠肺炎病人治疗跟踪管理系统”。当时身边同学第一批劝退的都是同一个理由这个题目听着带疫情背景答辩时容易被老师穷追猛打。但我做下来反而觉得这题目是最适合拿来练手的选题之一——业务闭环完整数据敏感度高能同时把权限设计、状态流转、统计报表、操作日志这些毕业设计里最爱的考察点全部串起来。如果你现在正卡在选题阶段或者已经定下这个题目却不知道从哪下手这篇文章就是按我当时的完整思路整理的从需求拆解、数据库设计到核心功能实现全程带坑复现到可以直接答辩。1. 项目整体设计与选题解构1.1 这题目到底在考什么大多数毕业生看到“管理系统”三个字第一反应就是增删改查然后就开始焦虑怎么讲出工作量。但你先把题目拆开看新冠肺炎病人治疗跟踪管理系统重点不在“系统”而在“跟踪”。跟踪意味着什么病人从入院、确诊、治疗、转归到出院随访是一条完整的生命周期数据链。普通的企业人员管理系统今天录一条、明天改一条数据之间没有强关联。但这里不一样——一个病人的状态变化是连续的确诊时间必须早于治疗记录治疗记录必须关联到某个住院阶段出院后的随访又依赖治疗结论。这些数据之间的约束关系才是老师想看到的设计能力。所以我的思路是把系统的核心业务定义成一条时间线驱动的状态流。入院登记建立基础档案确诊信息补充医学属性每次治疗记录在时间线上追加最终通过转归操作把病人置为治愈、转重症或死亡等状态出院后还能继续挂接随访记录。整个系统不是简单的CRUD而是围绕病人的全生命周期做管理。1.2 为什么选Java SpringBoot这套组合题目已经限定了JavaSpringBoot就是最合理的选择没有之一。我说几个理由都是实际做毕业设计时能感受到的。第一SpringBoot内置Tomcat一个java -jar就能跑起来不需要像老式SSM项目那样配置一堆XML、还要单独装Tomcat。毕设答辩时最尴尬的场面就是环境起不来——SpringBoot能把这个风险降到最低。第二Spring Boot的生态对毕业生太友好。Spring Data JPA或者MyBatis都有非常成熟的中文文档遇到问题一搜一大把基本不会卡死你超过半天。第三项目的可扩展性好。你如果想给系统加分SpringBoot整合WebSocket做消息推送、整合定时任务做随访提醒、整合MinIO做文件存储都是相对顺畅的方案。但也有个提醒SpringBoot版本别选太新的。我当时用的2.7.x稳定、资料多。有人图新鲜用SpringBoot 3.x结果JDK版本、Jakarta命名空间的变化把自己折腾得欲仙欲死答辩前一周还在调类路径问题完全不值得。1.3 系统整体架构与角色边界系统采用经典的B/S架构前后端我这里区分得很清楚。后端用SpringBoot提供RESTful API前端用Vue Element UI做单页应用最后把前端打包后的静态资源直接放进SpringBoot的static目录里一个jar包就能跑整个系统。这样部署简单答辩演示也方便——不用现场再起一个Nginx。如果你前端基础比较薄弱我也给个备选方案后端用SpringBoot Thymeleaf模板引擎页面直接由后端渲染。这套路更传统、更稳但页面的交互体验和“现代感”会弱一些。我建议只要时间允许尽量用Vue做前端。哪怕只是简单调几个接口答辩时被问“怎么理解前后端分离”你都能有一段具体的话可讲。角色的边界是这样划分的超级管理员负责系统基础配置、账号分配、查看全量统计报表。医护人员负责病人入院登记、治疗记录录入、转归操作、随访记录维护。患者/家属可选角色只允许查看自己相关的治疗与随访信息这是给系统增加权限层次的一个加分项。访客不登录仅能看到公开的统计数据页比如某地区每日治愈人数的趋势图。这个角色划分看起来简单但实际对应到后端就是Spring Security或拦截器里的三套权限逻辑。很多同学做毕设只管登录不管角色老师一问“谁能改数据、谁能看什么”答不上来就是明显的失分点。2. 需求分析与功能模块拆解2.1 核心业务闭环从入院到随访的状态流转需求分析阶段别一上来就想着建表先画出业务闭环。我当时在白板上画了这样一条线入院登记 → 确诊信息补录 → 治疗过程记录 → 阶段性评估 → 转归判定 → 出院 → 随访跟踪转归判定分支会走向四个结局治愈出院、转重症监护、死亡、仍在治疗中。其中“转重症监护”和“仍在治疗中”都会停留在闭环里继续循环只有“治愈出院”和“死亡”才会走出主流程。为什么要专门设计“转重症”这个状态这就是这个题目的一个加分点。如果系统只记录“治愈/未治愈”两种状态那统计口径就太粗糙了。有了转重症记录后期就可以统计“转重症率”“平均治疗周期”“不同症状类型的治愈周期对比”这些颗粒度的数据才支撑得起一个毕业设计的分析深度。2.2 六大功能模块的具体拆解需求画完闭环落到模块上我当时拆成六个患者档案管理基本信息、身份证号、联系方式、常住地址、入院日期、症状类型、基础疾病高血压、糖尿病等。这里必须加一个“来源地区”字段因为后续统计要按地区下钻。确诊信息管理确诊日期、确诊医院、病毒分型、密接来源、初筛方式核酸检测/抗原/CT。确诊日期和入院日期之间的时间差是一个可以计算的指标。治疗记录管理住院科室、主治医生、用药方案口服/静脉/吸氧、用药明细、生命体征数据体温、血氧饱和度、呼吸频率、病情评估等级。每次记录对应一个时间点形成个人治疗时间线。转归管理判定时间、判定结果、判定医生、转归备注。追踪随访管理随访日期、随访方式电话/上门/线上填报、当前症状、复诊结果、是否解除隔离。随访数据是独立表和主治疗流程分离但外键关联到患者ID。统计分析模块地区确诊趋势、每日新增/治愈/死亡人数、治愈率、转重症率、医院收治负荷、治疗周期分布。每个模块拆完我就评估了一下工作量是足够撑起一篇毕设论文的。说句实在话如果只做前三个模块答辩时论文都容易写成流水账但加上随访和统计分析整个论文的结构就可以是“主治疗链延伸管理链数据决策链”三层层次感立刻出来了。2.3 非功能性需求别忽略一般毕设需求文档里都会写“系统安全可靠、操作便捷”但要落到实处至少要考虑三件事密码不能明文存储至少用BCrypt哈希。关键操作删除病人档案、修改治疗记录、转归判定要有日志留痕。列表查询必须分页不能一次把全表数据怼到前端。我当时选用了MyBatis-Plus自带的分页插件省了不少事。3. 数据库设计与核心表结构3.1 实体关系梳理数据库设计是整个系统最见功底的部分。实体数量不用多但实体之间有清晰的继承和引用关系。我当时设计了6张核心表加上2张字典表总共8张。6张核心表是user系统用户patient患者基本信息diagnosis确诊信息treatment_record治疗记录outcome转归记录follow_up随访记录2张字典表是hospital医院信息region地区编码实体关系一句话概括user与patient是操作关系patient与diagnosis是1对1patient与treatment_record是1对多patient与outcome是1对多因为可以多次转归patient与follow_up是1对多。这里有个设计细节需要想清楚患者主表到底存不存当前状态我的方案是患者主表里冗余一个current_status字段类型为int对应业务枚举值1住院中、2已治愈、3转重症监护、4死亡、5失访。这样列表页可以直接根据状态过滤不用每次去关联子表聚合计算。这个设计叫做“冗余状态字段”是典型的以空间换时间的思路。当然代价是状态更新时要保证和应用逻辑同步这个我们在编码阶段通过service层统一处理。3.2 核心表字段逐一对照为了让你能直接抄作业我把几个关键表的字段设计列出来列名基本是我反复调过的直接建表就能用。patient患者基本信息表字段名类型说明idbigint主键自增namevarchar(50)患者姓名id_cardvarchar(18)身份证号做唯一索引gendertinyint1男 0女ageint年龄phonevarchar(20)联系电话region_codevarchar(12)所属地区编码关联region表addressvarchar(255)常住详细地址admission_datedatetime入院日期symptom_typevarchar(50)主要症状类型如发热、咳嗽underlying_diseasevarchar(255)基础疾病多个用逗号分隔current_statustinyint1住院中 2已治愈 3转重症 4死亡 5失访created_timedatetime创建时间updated_timedatetime更新时间treatment_record治疗记录表字段名类型说明idbigint主键patient_idbigint患者ID外键索引record_datedatetime记录时间departmentvarchar(50)科室doctor_namevarchar(50)主治医生medication_planvarchar(255)用药方案描述oxygen_saturationint血氧饱和度百分比body_temperaturedecimal(4,1)体温evaluate_leveltinyint病情评估等级 1轻 2中 3重 4危重remarkvarchar(500)备注follow_up随访记录表字段名类型说明idbigint主键patient_idbigint患者IDfollow_up_datedate随访日期follow_up_methodtinyint1电话 2上门 3线上填报current_symptomvarchar(255)当前症状描述recheck_resultvarchar(255)复诊结果is_releasedtinyint是否解除隔离 1是 0否follow_up_doctorvarchar(50)随访医生created_timedatetime创建时间3.3 三个容易踩坑的库表设计细节踩坑一身份证号要不要加密存储我建议不加毕业设计阶段用明文即可但数据库层面必须建唯一索引。老师如果问安全性你回答“生产环境会用AES对敏感字段加密演示环境为了展示效果没启用”这个回答是有层次感的。踩坑二字典表到底要不要建必须建。医院表和地区表就是孪生兄弟没有这两张表所有下拉框选项都得硬编码在前端答辩演示时改数据特别麻烦。地区表我是用编码父子级设计的比如110000代表一个省级单位110100代表市级统计时用LIKE前缀匹配实现下钻。踩坑三时间字段一律用datetime不要用timestamp。MySQL的timestamp有2038年问题虽然现在还没到但这是规范性问题写成datetime省心而且datetime不会受数据库时区影响。4. 核心功能实现与关键代码4.1 登录鉴权JWT 拦截器把权限捏在手里后端权限这块我没有引入厚重的Spring Security因为毕设只是演示和学习Spring Security的配置链会把大量时间耗在环境调通上。我当时的方案是JWT HandlerInterceptor轻量且能讲清楚原理。JWT实现就三步用户登录成功后用jjwt库生成token把用户ID和角色塞进token的claims里。写一个拦截器拦截所有/api/**请求从请求头Authorization里读token解析并校验签名。校验通过后把用户信息放到ThreadLocal里供Service层随时取用。核心代码大致长这样public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || token.isEmpty()) { throw new BusinessException(401, 未登录或token已过期); } // 解析token这里用的是hmac密钥生产环境换成RSA Claims claims Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token.replace(Bearer , )) .getBody(); Long userId claims.get(userId, Long.class); Integer role claims.get(role, Integer.class); UserContext.set(new UserContext(userId, role)); return true; } }角色校验我写在自定义注解里比如RequireRole(Role.ADMIN)。拦截器通过反射读取handler方法上的注解再做权限判断。这样比在每个Controller里写if判断清爽得多也方便答辩时解释AOP思想。4.2 患者批量导入EasyExcel比POI省一个量级的精力患者数据在实际场景里必然来自Excel表格所以批量导入是刚需。我一开始用的Apache POI写了一大堆CellType判断、日期格式转换的代码又臭又长。后来换成阿里EasyExcel整个导入代码缩减到不到40行而且性能更好。导入的逻辑是前端传一个MultipartFile后端用EasyExcel监听器逐行读取转成PatientImportVO做基础校验身份证号格式、必填项是否为空然后分批插入数据库。这里要注意一定要实现AnalysisEventListener在doAfterAllAnalysed方法里处理批量插入不要每读一行就insert一次数据量大时会慢到怀疑人生。PostMapping(/api/patient/import) public Result importPatients(RequestParam(file) MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream()) .head(PatientImportVO.class) .registerReadListener(new PatientImportListener(patientService)) .sheet() .doRead(); return Result.success(导入完成); }4.3 治疗记录时间线一次查询纵向展示病情变化治疗记录这个模块核心体验在一个页面上纵向展示该患者的全部治疗时间线。前端用时间线组件后端只需要一个接口GET /api/patient/{id}/treatment/timeline。后端SQL很简单但有一个细节值得拎出来说按record_date倒序排列再用一个DATE()函数按天去重。有些患者一天可能会有多次生命体征采集记录前端时间线上一天显示一条即可点进去看详情再展开当天的多条记录。这样时间线既清爽又能体现出你考虑了用户体验。4.4 统计报表用SQL讲出业务故事统计报表是最容易出彩也最容易翻车的模块。我当时做了三张图治愈率趋势折线图、每日新增/治愈人数柱状图、各地区收治人数Top10横向条形图。图表库用的ECharts前端数据来自后端三个接口。治愈率怎么算关键是要定义清楚分母。治愈率 本期治愈人数 / 本期收治人数不是累计治愈/累计收治否则数据一直趋近于一个固定值毫无趋势可看。为了让答辩时有话可讲我在后端把统计口径写成注释接口返回时就附上statUnit字段。核心SQL长这样SELECT DATE_FORMAT(admission_date, %Y-%m) AS month, COUNT(*) AS total_admitted, SUM(CASE WHEN current_status 2 THEN 1 ELSE 0 END) AS cured, ROUND(SUM(CASE WHEN current_status 2 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS cure_rate FROM patient WHERE admission_date #{startDate} GROUP BY DATE_FORMAT(admission_date, %Y-%m) ORDER BY month;这个SQL不是特别复杂但包含了一个关键的CASE WHEN用法以及按月分组聚合的思想。答辩时把这段SQL的逻辑讲清楚基本能证明你具备真实的数据处理能力。4.5 操作日志AOP实现三分钟讲清记录留痕操作日志我用的是Spring AOP 自定义注解也是毕设里很容易写的一个模块。先定一个LogAnnotation注解标在需要记录的方法上。然后写一个切面类在Around里把方法名、参数、操作人、操作时间、IP地址捞出来包装成日志对象异步写入数据库。Aspect Component public class LogAspect { Around(annotation(logAnnotation)) public Object around(ProceedingJoinPoint pjp, LogAnnotation logAnnotation) throws Throwable { long start System.currentTimeMillis(); Object result pjp.proceed(); long cost System.currentTimeMillis() - start; // 组装日志信息异步保存 LogRecord logRecord new LogRecord(); logRecord.setUserId(UserContext.get().getUserId()); logRecord.setOperation(logAnnotation.operation()); logRecord.setMethod(pjp.getSignature().toString()); logRecord.setCostTime(cost); logRecord.setCreateTime(new Date()); logService.saveAsync(logRecord); return result; } }这个切面代码量不大但把“AOP思想”这个抽象概念落到了具体业务里答辩时讲“切点、通知、切入点表达式”都有实物可指比背概念强得多。而且系统管理页面直接就有“操作日志列表”功能展示界面一演示工作量就直观可见了。5. 开发环境搭建与部署5.1 本地开发环境组合我的开发环境是JDK 8 Maven 3.8 IDEA 2024 MySQL 5.7。有些同学喜欢用JDK 11或17也能兼容但JDK 8还是兼容性最稳的选择。MySQL我选5.7而不是8.0倒不是8.0不好而是5.7的配置信息在网上一搜全是答案8.0会偶尔遇到驱动类名和时区配置的坑没必要给自己加码。IDEA里创建项目时选择Spring InitializrGroup填com.exampleArtifact填covid-track-system。依赖选Spring Web、MyBatis-Plus、MySQL Driver、Lombok、Validation就够了。注意如果你用IDEA 2024版本创建项目时Spring Boot版本默认可能是3.x一定要手动改成2.7.x。这个坑在快捷键上就能省你半天时间。5.2 数据库初始化一把梭项目跑起来前数据库必须先建好。我建议把所有建表和初始化数据都写成一个init.sql比如CREATE DATABASE covid_tracker DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE TABLE patient ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, id_card VARCHAR(18) NOT NULL UNIQUE, gender TINYINT, age INT, phone VARCHAR(20), region_code VARCHAR(12), address VARCHAR(255), admission_date DATETIME, symptom_type VARCHAR(50), underlying_disease VARCHAR(255), current_status TINYINT DEFAULT 1, created_time DATETIME DEFAULT CURRENT_TIMESTAMP, updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );初始化数据我专门写了一个脚本生成200条模拟数据覆盖不同地区、不同症状、不同状态。这样启动项目后列表页、统计页都有数据可以看——答辩演示最忌讳空页面。如果你不想手搓数据可以直接用Navicat的“数据生成”功能20秒就生成200行。5.3 前后端合并打包如果你用了Vue前端命令是npm run build生成的是dist目录。把dist里所有的文件复制到SpringBoot项目的src/main/resources/static下重新打包。这样SpringBoot会把前端静态资源托管在默认路径下相当于前端和后端在一个服务里。有同学会问为什么不分开部署分开部署一个跑前端、一个跑后端确实更主流但毕业设计强调“答辩现场能演示成功”合包部署是把复杂度降到最低的选择。答辩前你在其他电脑上拿到jar包双击一跑浏览器一开系统就能用这才是毕业设计的正确打开方式。5.4 接口文档别靠猜我当时用SpringDoc整合了Swagger UI。每个接口都写了简要说明和参数示例。这个动作有两个收益第一前端对接接口时不用反复问你字段含义第二答辩时老师想“看看你有什么API”你直接把/swagger-ui.html打开一屏列得清清楚楚这是一个非常加分的演示动作。注意SpringBoot 2.x对应的是springdoc-openapi-ui1.x版本别下错。6. 常见问题与排查技巧实录下面这几个坑是我在开发过程中真实遇到的也是我在毕设群里帮同学看代码时反复见到的列出来供你对照排查。6.1 数据库连接不上控制台报Access denied多半是用户名密码不对或者MySQL服务没启动。你按这个顺序排查第一步在终端执行mysql -uroot -p确认密码能不能登进MySQL第二步如果本地能登检查SpringBoot的application.yml里用户名密码是否和本地一致第三步检查连接URL里的serverTimezoneAsia/Shanghai是否加了。第三个参数是MySQL 8.0驱动的硬性要求不加必报错。6.2 端口被占用Tomcat起不来默认端口8080被占报错信息通常是Port 8080 was already in use。解决方式是直接改了端口我当时用的9090。改端口的同时前端所有调用接口的baseURL都要跟着改不然全部404。这个坑不大但前后端不在一台机器上时容易漏。6.3 前端请求跨域浏览器控制台红色一片如果你前端独立运行在localhost:5173后端在9090跨域请求会被拦截。解决方案在SpringBoot里加一个WebMvcConfigurer配置CORSConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:5173) .allowCredentials(true) .allowedMethods(GET, POST, PUT, DELETE); } }把前后端合包部署之后这个跨域配置就不需要了因为同源。6.4 导入Excel时日期解析失败EasyExcel读日期默认是按yyyy-MM-dd HH:mm:ss格式如果你的Excel模板里日期是2023/1/5这种斜杠格式会直接ParseException。解决方式是给实体类的日期字段加DateTimeFormat(yyyy/MM/dd)注解并且在导入校验时先做两次格式兼容判断。6.5 统计报表数据为0SQL直接执行没问题但接口返回空这种多半是前端把字段名写驼峰了而后端返回的JSON是下划线字段。比如后端返回total_admitted前端取值totalAdmitted结果永远是undefined。我在实际项目里就在后端统一做了JsonProperty(totalAdmitted)映射或者直接用MyBatis-Plus的驼峰自动转换配置map-underscore-to-camel-case: true一劳永逸。6.6 患者状态更新后列表页没刷新加了Redis缓存才会遇到这个问题。没加缓存的场景下多半是前端调用了列表接口但没重新拉取数据。我当时踩过这个坑列表页在mounted钩子里拉数据但状态修改弹窗关闭后没有触发重新查询。这就需要在关闭回调里重新调用fetchData()或者在Vue的watch里监听路由变化。7. 答辩与论文的一点额外心得这里讲点论文写作和答辩准备的额外内容希望能帮你把最后几关也走稳。论文结构我建议分六章绪论、需求分析、系统设计、系统实现、系统测试、总结。重点是需求分析和系统实现这两章不要急着写代码先把用例图、数据流图、ER图画清楚。我那时候就是先画图再写代码论文框架基本等于我的开发计划。老师看论文最在意的是需求分析有没有讲清楚业务场景以及系统实现章节里的代码逻辑能不能和前面的设计对上。答辩演示我准备了“一条主线”和“三个支线”。主线是演示一个病人从入院到治愈出院的完整链路支线一演示权限控制普通账号访问管理接口被拦截、支线二演示统计报表动态刷新、支线三演示操作日志的留痕。这样讲即使你代码里有很多展示性质的页面整个过程也会显得逻辑严密。最后说一个别人不太会提的技术细节用Mock数据填充统计趋势时注意数据生成的时间跨度。比如你希望图表展示“近30天治愈率趋势”你就得保证模拟数据里有足够分布在不同日期的患者记录别把200条数据全集中在同一天。用SQL按DATE_ADD随机生成日期比手动改数据高效得多。我做完这个项目最强烈的体会是毕业设计不是比谁用了更酷的新技术而是比谁能把一套业务逻辑完整地讲清楚、做出来、演示好。这个新冠病人治疗跟踪管理系统恰好就是这样一个既不偏门又能把基本功全练到的题目。技术上不炫技但每一步都踩在产品逻辑和工程规范上这就足够让你在答辩时有底气了。如果你在开发中卡在某个具体环节可以按我文章的排查顺序走一遍如果还有更细的问题也欢迎在评论区聊聊你项目的具体实现方案。
返回列表