如果让我用一个词来概括医院临床管理系统开发,我不会选“复杂”,而会选“流程”。很多同学拿到这个题目后,第一反应是“要把医院每个科室都做成页面”,于是菜单堆了一大堆,最后功能之间互不相干,演示时只能说“这是医生管理,这是患者管理”。这类系统在评审时非常吃亏。我做这个方向时始终会提醒一句:医院系统不是某个办公室的单独软件,它是一连串业务状态的接力。
SpringBoot医院临床管理系统,本质上是把患者从建档、挂号、接诊、开医嘱、缴费、药房执行到出院归档的整个临床链路搬进系统。它不是什么高并发项目,也不要求你做出媲美三甲医院HIS系统的能力,但它对业务建模、状态设计、事务边界和接口规范的要求,恰恰是计算机毕业设计里最该展示的东西。这篇文章我把完整的设计思路、表结构、实现片段和踩坑经验全部拆开,适合正在选题、已经选了这个题目、甚至快到答辩阶段的同学照着做。
1. 临床系统不是“医院生活照搬”:本质是两条主流程的状态机
很多人一开始就把系统想大了,门诊、住院、药房、检验、手术、病历、收费全都要,结果做了一个学期,发现每一个模块都只是增删改查。真正的问题不是页面不够多,而是没有把业务串起来。
1.1 为什么一写代码就迷茫:需求看着多,实际是流程没分清
医院临床管理系统在毕设语境下可以拆成两条主线:一条是门诊主线,一条是住院主线。这两条线都以“医嘱”为核心,又各自有独立的状态流转。你要是先把这两条线梳理清楚,功能模块根本不用硬想,按照线上每个节点的角色随便一列都是需求。
我通常会给第一次接触这个题目的同学画一张非常朴素的草图,不需要什么建模工具,白纸也行:
- 患者建档,拿到唯一患者编号;
- 挂号/预约,产生一次就诊记录;
- 医生接诊,在就诊记录上书写诊断;
- 医生下达医嘱,可以是用药、检查、检验或治疗;
- 患者去收费处缴费,医嘱状态从“待缴费”变成“已缴费”;
- 药房看到已缴费的药品处方后发药,发药后医嘱变成“已完成”;
- 检查科室看到已缴费的检查申请后安排执行,回填检查结果。
这段看起来像医院流程介绍,但它是整个系统的骨架。你说的“面向医院全流程的SpringBoot临床业务综合系统”,核心就是这个闭环。很多同学一开始做出来的东西没有状态,患者挂完号,就诊记录还停在待诊,医生看完了也没有任何变更,处方缴费前后数据库里根本看不出来差别,这就是流程没落成状态导致的。
1.2 门诊主流程:从建档到取药的六个关键节点
门诊那一段,我用六个节点去看:
- 建档完成。患者档案创建后,状态可用,之后每次来院都不需要重新录基础信息。
- 挂号成功。产生一条就诊记录,记录关联到患者、科室、医生、时间段,初始状态是“候诊”。
- 医生接诊开始。就诊记录状态切为“就诊中”,此时其他医生不能重复接诊。
- 医生提交诊断和医嘱。诊断写在病历上,医嘱单独建表,初始状态是“待缴费”。
- 患者缴费成功。医嘱状态变为“已缴费”,同时记录支付流水。
- 药房/医技科室执行完成。执行科室回写状态,医嘱变为“已完成”。
请注意,第5步和第6步之间隔着一次工作交接。如果没有状态字段,医院里药房根本不知道哪些处方能发药。系统演示的时候,你能不能当场展示这一步,决定了评审老师认为你在做“管理系统”还是在做“登记表集合”。
1.3 住院主流程:从入区到归档的状态约束
住院流程在门诊流程之上多了“床位”和“长期医嘱”这两个复杂点。毕设阶段不建议做太重,能完成“入院登记-分配床位-医嘱下达-护士执行-出院结算”这条基本链路就够了。
住院的核心表通常是一张住院记录,它关联患者、病区、床位、主管医生、责任护士。住院主流程节点有这么几项:
- 门诊医生在诊疗过程中判断患者需要住院,办理入院申请;
- 住院部或护士站确认入区,分配床位,住院记录状态变成“在院”;
- 医生在住院记录下开长期医嘱或临时医嘱,初始状态“待执行”;
- 护士执行医嘱,状态变更为“已执行”并记录执行时间;
- 患者达到出院条件,医生下达出院医嘱,住院记录状态变为“待结算”;
- 收费结算完成,床位释放,住院记录归档。
长期医嘱和临时医嘱怎么区分?我的经验是不要用“长期/临时”这种语义去约束字段,而是用接口和状态去隔离。长期医嘱每天重复执行,临时医嘱只执行一次。毕设里你可以简化成“医嘱表里有一个order_type字段,长期医嘱可以被护士标记今日已执行并生成执行记录,临时医嘱执行完直接把主状态置为已完成”。这套设计能让答辩老师看出你理解医院业务,而不是只会照着管理系统模板改名字。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从门诊到住院的功能盘面:按业务闭环切模块,不按菜单切
我发现不少人的数据库设计是按界面菜单建的,一个菜单一张表,一个页面一套增删改查。这种设计方式做出来的模块,彼此之间是不通信的。正确做法是先找业务闭环,再决定哪些表参与闭环,最后才规划接口和页面。
2.1 用“谁在什么节点操作什么数据”来拆模块
我给这个系统推荐的模块切分方式是这样的:
| 模块 | 核心角色 | 核心实体 | 这个模块存在的意义 |
|---|---|---|---|
| 患者管理 | 挂号员/护士 | patient | 所有业务流程的起点 |
| 门诊医生工作站 | 门诊医生 | visit_record / clinic_record / prescription | 接诊、诊断、开单 |
| 药房管理 | 药师 | drug_info / prescription_item | 负责药品处方执行与库存扣除 |
| 收费管理 | 收费员 | payment_record | 将未缴费的医嘱变为已缴费 |
| 护士工作站 | 护士 | inpatient_record / medical_order | 床位分配与住院医嘱执行 |
| 系统管理 | 管理员 | sys_user / sys_role / sys_menu | 登录、权限、基础数据维护 |
| 统计看板 | 管理员/医生 | 各业务表聚合 | 给答辩提供直观展示 |
这里每一条都是“角色+数据+动作”,没有一个模块是孤立的。比如药房管理如果离开处方,它就是一个药品库存管理系统,放进临床系统里就不伦不类;但药房管理挂到“已缴费处方”下,就是医嘱执行链路的最后一环,价值完全不一样。
2.2 门诊医生工作站是系统的心脏
如果时间只够做一个高质量业务模块,我强烈建议把资源砸在门诊医生工作站上。因为它是信息最密集的地方,也最能体现“临床”概念。
门诊医生工作站大体需要做这些事:
- 查看本人当前排班的候诊患者列表;
- 选一个患者开始接诊,拉出他的历史就诊记录和过敏史;
- 填写主诉、现病史、既往史,给出诊断结果;
- 开药品医嘱、检查医嘱、检验医嘱;
- 提交后自动生成待缴费单据,患者去收费处结算;
- 支持查看本人已接诊患者列表。
这里面有个很细微但容易被忽略的点:历史就诊记录的展示。纯粹做CRUD的系统只关心“当前这单”,而一个临床系统必须体现出时间连续性。患者三年前来医院看过病,三年后再来,医生打开系统能看到之前的诊断和用药。这块做得好,答辩时提到“为医生提供连续诊疗视图”,分数会明显不一样。
2.3 哪些模块可以做成“加分项”:排班、消息、统计
核心闭环做完后,还剩下的时间应该花在低成本高展示性的模块上。我建议按下面这个优先级参考:
- 排班管理。让医生有排班表,挂号时能选择某位医生某个时间段。这个模块实现成本不高,却让系统从“可运行”变成“符合真实业务”。
- 药品/检查项目字典管理。用于支撑处方开单时的下拉选择,也向评审展示系统有基础数据维护意识。
- 统计看板。统计门诊量、科室热度、药品消耗排行、每日收费金额。建议用ECharts画几个图表放首页,答辩开场非常醒目。
- 消息提醒。比如医生开完处方,收费处能看见待缴费列表;药房有新处方时待处理数量自动增加。这个用简单的定时刷新或WebSocket都行,属于锦上添花。
我见过有同学非要把在线支付、小程序挂号塞进毕设,结果把自己搞到焦头烂额。毕设评审看得是业务完整性、代码可读性、个人陈述可信度,不是功能数量。你把核心闭环跑通,再加两三个加分模块,已经是很高质量的作品。
3. 技术选型定稿:为什么Spring Boot单体在毕设场景里是“最优解”而不是妥协
技术选型这部分经常争论。有人劝你上Spring Cloud,有人说必须前后端分离,还有人说JSP直接一把梭。我的观点很直接:毕设场景下Spring Boot单体是最合理的选择,它不丢人,反而匹配这个规模系统的主流企业形态。
3.1 技术栈清单与选择理由
我推荐这套组合,也顺便说明它背后的理由:
| 技术/组件 | 推荐选型 | 使用位置 |
|---|---|---|
| 后端框架 | Spring Boot 2.7.x 或 3.x | 整个后端服务 |
| 权限与安全 | Spring Security + JWT 或 Sa-Token | 登录、接口鉴权 |
| ORM | MyBatis-Plus | 数据持久化和分页 |
| 数据库 | MySQL 8.0 | 存储业务数据 |
| 前端 | Vue 3 + Element Plus | 管理后台页面 |
| 接口文档 | SpringDoc OpenAPI 或 knife4j | 自测与答辩演示 |
| 文件存储 | 本地磁盘 + 虚拟路径映射 | 检查报告图片、患者头像 |
| 缓存(可选) | Redis | 验证码缓存、token黑名单、热点字典 |
Spring Boot在这个系统里确实不是性能最优解,但它是最合适的解。一方面,医院临床业务的特点是状态多、场景多,但对单接口吞吐量要求没有电商秒杀那么夸张;另一方面,Spring Boot的自动配置和starter机制能把大量重复配置收起来,让你把时间花在业务流程而不是xml配置上。
3.2 版本选择:到底用JDK8还是JDK17
很多人在网上看到Spring Boot 3要求JDK17,就开始纠结。我给你的建议取决于你剩余的时间和机器环境:
- 如果你对Java 8更熟、电脑上还是JDK8环境,选Spring Boot 2.7.x + JDK8最稳。这是一个非常成熟的稳定版本,网上资料也最多,MyBatis-Plus等框架几乎不会有坑。
- 如果你愿意花半天把环境切到JDK17,选Spring Boot 3.x也挺好,毕竟新版默认使用jakarta命名空间,部分老教程的javax导入会失效,需要自己改。
- 别盲目追Spring Boot 3.4之类的太新小版本。毕设不需要抢首发,组件兼容性更重要。你一个项目等着交付,没时间给框架踩坑。
还有一点要提醒:Java环境版本过高时,可能出现Lombok插件不兼容、Maven编译报错这类小问题。遇到这种就先确认IDE的Lombok版本、Maven编译器版本是否匹配。很多“SpringBoot版本太高”导致的报错,最后都不是框架问题,而是本机工具链新旧混搭。
3.3 什么时候才需要加Redis、MQ、Flowable这类重型组件
热词里出现了一批中间件,比如activemq、kafka、flowable,这些确实在企业系统里有应用场景,但放在这个毕设里要克制。我的判断标准很简单:业务场景是否真的需要。
- Redis适合做登录验证码、短期缓存,这个加进去不复杂,可以加。
- RabbitMQ/Kafka在这个系统里能做的场景是“开立医嘱后异步通知药房”。但毕设阶段用一张“待处理任务表”或者在查询时带出状态变更,是完全等价的效果,没必要为异步而异步。
- Flowable是工作流引擎,适合做审批类业务。如果你想做“医生提交入院申请-科室主任审批-护士站办理入区”这样的流程,用Flowable确实漂亮,但学习成本不低。建议只把这个作为论文里“系统扩展方向”提一句,别写进核心开发进度里。
技术选型的最终说服力,不是看你用了多少技术,而是你能否在答辩时讲清楚每一项技术的引入解决的是什么问题。哪怕你只用SpringBoot+MyBatis-Plus,只要把“医疗状态机如何用事务保证一致”讲透,一样是可以拿高分的作品。
4. 核心表结构、主键策略与状态机的细节设计
很多系统的失败不是代码写崩了,而是表结构从第一天就不支持业务流转。这里我给出一个可直接参考的建模思路,重点不是说把所有字段堆出来,而是把几个卡脖子的设计点讲清楚。
4.1 核心表设计概览
以门诊主流程为例,核心表应该长这样:
| 表名 | 主要职责 | 关键字段 |
|---|---|---|
| patient | 患者档案 | id, patient_no, name, gender, birthday, phone, allergy_history, status |
| visit_record | 就诊/挂号记录 | id, patient_id, doctor_id, dept_id, visit_status, create_time, end_time |
| clinic_record | 门诊病历记录 | id, visit_id, patient_id, doctor_id, symptom, diagnosis, advice |
| prescription | 医嘱/处方主表 | id, prescription_no, patient_id, doctor_id, visit_id, category, status, total_amount |
| prescription_item | 医嘱明细 | id, prescription_id, item_name, spec, dosage, quantity, unit_price, amount |
| payment_record | 支付流水 | id, payment_no, prescription_id, amount, pay_method, pay_status, pay_time |
| drug_info | 药品字典 | id, drug_code, drug_name, spec, unit, price, stock |
| sys_user | 系统用户 | id, username, password, real_name, role_code, dept_id |
这里我最想强调的是prescription这个表。它不只是“药品单”,而是广义的医嘱单。通过category字段区分西药、检查、检验,明细表里可以是药品,也可以是检查项目。这样整个系统不需要为“检查单”再造一套类似的Model,所有待缴费单据统一走处方主表,收费模块只需要面向一张表工作。
4.2 为什么自增主键在这个场景里不是最优解
我知道很多课设用自增主键用习惯了,但临床系统我更建议用MyBatis-Plus的ASSIGN_ID雪花ID策略。原因很现实:
- 患者档案、处方单、支付流水最终可能要导出体检报告、医保结算文件,分布式系统里自增ID容易冲突;
- 雪花ID是数字型长整型,适合做分库分表的key,也适合当前端拿到ID回传时避免暴露业务量(比如“用户第1000号”这种敏感信息);
- MyBatis-Plus的雪花ID生成的ID是Long类型,实体类直接配上@TableId(type = IdType.ASSIGN_ID),不需要额外写算法。
这里也回应一个老问题:BigDecimal还是Double。金额一律BigDecimal,数据库字段一律decimal(10,2)。费用汇总、单价乘以数量这类计算,Double会出现0.1+0.2不等于0.3的经典问题。医药费这种东西不允许差一分钱,面试被问到金额精度也能体现你踩过坑。
4.3 状态字段建议用status整数状态机,不要用“是否已缴费”这种布尔表达
我见过一个反面设计:prescription表里放一个is_paid、一个is_dispensed、一个is_completed,三个布尔字段。表面看很方便,实际维护时很容易出现“费没缴、药也发了”这种不可能状态。更合理的方式是保留一个status字段,用一组状态码表达整个生命周期:
| 状态码值 | 含义 | 对应操作 |
|---|---|---|
| 1 | 待缴费 | 医生提交医嘱后,等待患者缴费 |
| 2 | 已缴费待执行 | 收费完成,等待执行科室处理 |
| 3 | 执行中 | 检查项目已预约/药品正在配药,可选 |
| 4 | 已完成 | 药房已发药,或检查结果已填写 |
| 5 | 已作废 | 医生取消、或超时未缴费被取消 |
用status而不是多个布尔字段,最大的好处是状态转换可以通过SQL条件更新原子完成。比如缴费接口可以写成:
sql复制UPDATE prescription
SET status = 2, pay_time = NOW()
WHERE id = #{id} AND status = 1
这条SQL是原子性的,影响行数为1说明缴费成功;影响行数为0说明处方已经被别人缴过费,或者已经作废,业务上直接提示用户不能重复缴费。这种更新方式不需要悲观锁,也不容易出现并发重复支付问题。
4.4 药房库存扣减的时机:建议在发药时扣,而不是开单时扣
库存扣减是个业务决策问题。有些电商系统在下单时锁库存,但医疗场景里患者可能开完药不缴、缴完费不来取,如果开单就扣库存,会造成账面库存和实际库存长期不一致。我的建议是在药房执行发药操作时扣减库存,同时用一个小事务保证“更新处方状态 + 扣库存”的一致性。
一个可参考的发药接口逻辑是这样:
- 药师按处方号查到处于“已缴费待执行”状态的处方;
- 校验处方明细里的每种药品库存是否充足;
- 若库存足够,扣减库存;
- 把处方状态更新为“已完成”;
- 如果中间有任何一步失败,事务回滚,处方保持“已缴费待执行”,库存也不变。
这套流程在演示和答辩时都是完整的案例,因为它同时覆盖了事务、并发、一致性三个高频提问点。比起喋喋不休地背八股,能现场把这段逻辑讲清楚的人,才是真做过项目的人。
5. 用代码落地的关键三段:权限、开具医嘱、缴费防重
有些同学做到这一步会发现,业务表建完了,SpringBoot工程也启动了,但不知道先写哪段代码。我的建议是先写权限,再写核心业务事务,最后专门打磨那些最容易出bug的更新语句。
5.1 登录与权限:用RBAC模型落地,别把角色写在每个if里
临床管理系统千万不能用那种“用用户名判断是不是管理员”的方式。该系统天然有医生、护士、药师、收费员、管理员等角色,不同角色的菜单和操作权限不同,最合适的就是RBAC模型。资源有限的毕设可以不建五张表,用三张表也能表达清楚:sys_user、sys_role、sys_user_role,或者直接在用户表带一个role_code。
我推荐的最小权限实现:
- 登录成功后用JWT生成token,把用户ID和角色码放进去;
- 后端提供自定义注解@RequireRole("DOCTOR")切在接口上;
- 拦截器里解析token,校验角色是否匹配。
示例代码可以这样写:
java复制@PreAuthorize("hasRole('DOCTOR')")
@PostMapping("/prescription")
public Result<Long> createPrescription(@RequestBody @Valid PrescriptionCreateDTO dto) {
return Result.success(prescriptionService.createPrescription(dto));
}
只要你走的是Spring Security,这个注解就是现成的,不需要自己造轮子。我还见过一种更省方案的姿势:用Sa-Token,它的鉴权注解和登录API比Spring Security更容易上手。建议不懂Security原理的同学直接选Sa-Token,把时间留给业务表。
5.2 开医嘱必须是一个事务:一张处方背后是主表和明细表
医生点击“提交”时,后端需要同时插入prescription和prescription_item。如果明细插了一半失败,主单还在,那这条处方在收费处就会变成“能查到但没法缴费”的脏数据。解决方式就是使用Spring声明式事务:
java复制@Transactional(rollbackFor = Exception.class)
public Long createPrescription(PrescriptionCreateDTO dto) {
Prescription prescription = new Prescription();
prescription.setPatientId(dto.getPatientId());
prescription.setDoctorId(LoginUtil.getCurrentUserId());
prescription.setVisitId(dto.getVisitId());
prescription.setCategory(dto.getCategory());
prescription.setStatus(PrescriptionStatusEnum.WAIT_PAY.getCode());
prescriptionMapper.insert(prescription);
BigDecimal totalAmount = BigDecimal.ZERO;
for (PrescriptionItemDTO itemDTO : dto.getItems()) {
PrescriptionItem item = new PrescriptionItem();
item.setPrescriptionId(prescription.getId());
item.setItemName(itemDTO.getItemName());
item.setSpec(itemDTO.getSpec());
item.setDosage(itemDTO.getDosage());
item.setQuantity(itemDTO.getQuantity());
item.setUnitPrice(itemDTO.getUnitPrice());
item.setAmount(itemDTO.getUnitPrice().multiply(new BigDecimal(itemDTO.getQuantity())));
totalAmount = totalAmount.add(item.getAmount());
prescriptionItemMapper.insert(item);
}
Prescription update = new Prescription();
update.setId(prescription.getId());
update.setTotalAmount(totalAmount);
prescriptionMapper.updateById(update);
return prescription.getId();
}
这里有两个细节要注意。第一,@Transactional一定要加rollbackFor = Exception.class,默认情况下Spring只回滚RuntimeException,如果代码抛了受检异常而没指定rollbackFor,事务照样提交,数据就直接错乱了。第二,金额不要在数据库里反复算,而是Java这边按明细逐项计算后写入总金额,保证主表总金额和明细表总和一致。
5.3 收费防重:把状态判断放到UPDATE语句里,而不是先查再改
缴费接口是并发问题高发区。患者可能手滑点了两次缴费,或者在收费窗口和自助终端的业务里同时提交。如果代码先查处方状态,判断等于1再更新,两个请求同时查到状态都是1,最终可能执行两次更新并生成两笔流水。解决办法就是用上一章提到的“状态条件更新”。
我这里给出一个Service层参考:
java复制@Transactional(rollbackFor = Exception.class)
public PayResultVO pay(Long prescriptionId) {
Prescription prescription = prescriptionMapper.selectById(prescriptionId);
if (prescription == null) {
throw new BizException("处方不存在");
}
if (prescription.getStatus() == 5) {
throw new BizException("该处方已作废,不能缴费");
}
int rows = prescriptionMapper.updateStatusWithVersion(
prescriptionId,
PrescriptionStatusEnum.WAIT_PAY.getCode(), // 期待当前状态
PrescriptionStatusEnum.PAID.getCode() // 目标状态
);
if (rows == 0) {
throw new BizException("该处方已在其他窗口缴费,请勿重复支付");
}
PaymentRecord record = new PaymentRecord();
record.setPrescriptionId(prescriptionId);
record.setPatientId(prescription.getPatientId());
record.setAmount(prescription.getTotalAmount());
record.setPayStatus(1);
paymentRecordMapper.insert(record);
return PayResultVO.success(...);
}
里面那个updateStatusWithVersion,对应SQL就是前文那段状态原子更新。这招在毕业设计里非常讨巧,既不需要引入Redis分布式锁,又能让状态机转换变得可靠,面试时还可以顺势讲出一套“乐观锁在状态流转中的应用”来。
6. 容易被忽略、但在评审时特别加分的工程细节
表结构、状态机、核心接口都做对之后,项目已经能跑了。但很多人的系统在演示时一操作就报错,或者表格里出现乱码,毛病往往不是业务逻辑,而是几处常见工程细节。
6.1 统一返回体与全局异常处理会让代码干净很多
很多同学写的Controller每次返回HashMap,或者直接返回一个实体类。当前端需要判断业务失败时,很难拿到统一的结构。我建议第一个基础类就写一个Result对象,包含code、message、data三个字段,配合全局异常处理器把业务异常转换成统一响应。这样业务Controller里完全不用写try-catch,代码看起来会很清爽。
这是一个非常加分的工程习惯,因为答辩老师经常翻代码看Controller层是否规范。一个接口一份try-catch堆出来的Controller,和一份干干净净的Service+Controller分层代码,高下立判。
6.2 逻辑删除与唯一约束的冲突,要提前想好
医疗系统里患者档案、药品信息往往用逻辑删除,就是is_deleted字段,这样历史数据不丢。但数据库里如果name或者phone设了唯一索引,逻辑删除后就出问题了:第一位患者删除后,手机号仍然占着唯一索引位置,下一位同号码患者无法重新建档。解决办法有几个:
- 唯一索引改成(phone, is_deleted)联合索引;
- 或者删除时置空手机号再逻辑删除;
- 或者在删除前校验该手机号的档案是否已有业务单据,如果有就做封存而不仅做删除。
这种问题在很多业务系统里都是真实存在的,属于工作量不大但很体现数据库设计能力的点。
6.3 日期时间处理建议统一用LocalDateTime
很多老项目还在用java.util.Date,在这个毕设里我强烈建议统一LocalDateTime。它可以避免很多新手常见的“时间少了8小时”问题。如果你用了Jackson,需要在配置里注册JavaTimeModule并统一日期格式,否则LocalDateTime序列化出来是一段难看的数组。
如果是Spring Boot项目,日期格式化可以这样配:
yaml复制spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
但要注意,LocalDateTime默认不认date-format这种配置,需要在Java配置里额外加一个Bean或者在字段上加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")。不处理的话,前端显示时间大概率会变成“2025-03-20T10:30:00”这种带T的格式,很影响演示观感。
6.4 分页不要自己写LIMIT实现,直接用MyBatis-Plus分页插件
临床系统的列表页特别多,比如患者列表、待接诊列表、药房待发药列表,这些都要分页。MyBatis-Plus自带分页插件,配一个拦截器就行:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
然后在Service层使用Page对象,返回给前端时带上总条数和当前页数据。这样比手写LIMIT ? , ?要安全,也不会出现SQL注入风险。还有,如果你想做“条件分页查询”,建议把查询条件封装成DTO,用LambdaQueryWrapper去构建where条件,不要在前端拼SQL条件字符串传给后端。
6.5 大字段和文件上传:检查报告图片、患者附件
医生开完检查后,检查科室可能需要上传检查报告图片,这部分在临床系统里属于合理需求。我建议不要引入OSS这种重量级服务,本地存储就够:把文件存到服务器磁盘的固定目录,数据库中存文件的相对路径,再通过一个资源映射接口把图片暴露出来。
Spring Boot里的资源映射可以这样配:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceHandler("file:" + uploadDir + "/");
}
}
需要提醒的是,演示环境如果是Windows,文件路径要写对盘符;如果是Linux部署,要注意目录是否存在以及读写权限。很多同学本地一切正常,部署到云服务器后图片加载不出来,基本都是路径问题。
7. 从零到答辩的推进节奏与演示规划
已经做到这里,功能差不多完成了。最后聊一聊怎么安排时间、怎么准备答辩,这部分和写代码一样重要,因为毕设是一个“做出来+讲清楚”的组合任务。
7.1 建议的四周开发计划
如果现在离交稿还有一个月,我建议按这种节奏推进:
| 阶段 | 时间 | 交付内容 |
|---|---|---|
| 第一周 | 3-5天 | 项目工程搭建、数据库表建好、统一返回体/异常/JWT登录 |
| 第二周 | 5-7天 | 患者档案、门诊就诊、医生开立处方、药品字典管理 |
| 第三周 | 5-7天 | 收费闭环、药房发药扣库存、门诊流程串通 |
| 第四周 | 3-5天 | 统计看板、演示数据清理、本地录屏、部署、写答辩稿 |
很多同学倒在第一周,因为沉迷搭环境,结果业务还没开始就到时间了。我建议第一周就定死节点:周五之前必须跑通登录跳转主页,否则后面所有计划都会崩。登录跳转主页是SpringBoot+Vue的“Hello World”,过了这关后面都是增量开发。
7.2 演示时要按流程走,不要按菜单走
这一点我觉得值得反复强调。评审老师不可能对每个页面都细看,他关心的是系统能不能把医院业务串起来。所以我建议的演示顺序是:
- 先登录一个医生账号,展示工作台首页,看到今天若干候诊患者;
- 点开其中一名患者的就诊记录,简单说两句病历展示;
- 模拟诊断并开出几张处方,其中一张是药品处方、一张是检查申请;
- 退出医生账号,登录收费员账号,看到刚才生成的待缴费单据,完成收费;
- 登录药师账号,看到新处方待发药,点击发药;
- 回到医生端的已接诊列表,能看到状态已经完成。
这条演示链路非常短,大约3分钟,但足以完整告诉大家:你的系统在管理的是“状态流转”,不是“一堆表格”。如果还做了统计看板,放在最后收尾,展示一组可视化图表,整体效果会非常好。
7.3 答辩前要提前准备的几个回答方向
毕设答辩最常见的问题是“你这个系统有什么难点”。我建议你准备这几个方向的回答,每个都用自己项目里的具体场景举例:
- 事务一致性:可以说开处方要同时插入主表和明细表、发药要同时更新状态和扣库存,出了问题都要回滚;
- 并发防重:可以说缴费接口用状态条件更新防止重复支付;
- 权限控制:可以说不同角色使用不同菜单和接口权限,用JWT配合注解实现;
- 业务理解:可以说门诊和住院两条主流程的差异,长期医嘱与临时医嘱的执行区别;
- 扩展方向:可以说如果后续需要对接检查设备、医保系统,可在现有单体架构上拆分独立的医技服务、收费服务。
要是被问到“SpringBoot自动配置原理”“循环依赖”这类纯八股问题,也不用慌。面试和答辩考察的是你有没有真正理解自己的技术栈,建议把常用注解、启动流程、配置加载顺序准备到一个可以讲清楚的程度。
再分享一个小技巧。答辩演示时很容易出现网络中断、浏览器缓存导致页面样式错乱这种意外。无论系统多稳定,提前用录屏软件把刚才那条完整演示流程录一份,不需要长,3分钟就够,存在桌面备用。现场即使环境出问题,也可以播放录屏继续讲业务。这不算投机取巧,而是每个做过系统演示的人都会考虑的应急预案。
医院临床管理系统这个题目,看着像“管理系统里最难的一档”,踏踏实实把主流程状态机建好,把事务边界想清楚,把权限和异常边界收拾干净,它反而很容易做出亮点。做这个项目最忌讳贪大,最需要的是把一个闭环讲透,祝你顺利。
