基于SpringBoot的中西诊所管理系统:从数据库到并发控制的毕设全攻略
又到了一年一度挣扎毕业设计的季节。前阵子群里又有人问什么题目好做我给出的答案一直很明确找个业务边界清晰、需求味很足、技术栈主流的方向下手。中西诊所管理系统就是很典型的这种题目它基于SpringBoot做后端覆盖挂号、门诊、划价、收费、中西药房、库存、会员、统计报表这些闭环业务需求一听就懂代码量够论文故事也讲得圆评审老师几乎不会问出超出项目本身的问题。这篇文章把我从选题、架构、表设计、核心编码到论文答辩的完整过程摊开讲一遍既讲SpringBoot实现这类系统时的关键取舍也说清楚哪些地方容易翻车。如果你正在纠结毕设、想拿现成源码做二次修改或者单纯想找一份能练手的全栈项目这篇都能给你实际可用的参考。1. 中西诊所管理系统选题为什么这个题目值得做先说选题逻辑。很多同学选系统类毕设的时候有个通病要么选通用进销存要么选校园二手交易业务逻辑跟网上已有项目高度重复答辩时根本没得讲。而中西诊所管理系统的妙处在于它有一个很独特的业务线——中医和西医业务要同时在系统里跑。西医部分挂的是普通门诊、检查检验、处方划价中医部分则涉及辨证分型、中药方剂、煎药登记、理疗预约、针灸推拿项目计费。两类流程在挂号、收费、库存环节又必须统一。这种同系统内两条业务线并行、又要在核心节点合并的特点恰恰是毕业设计论文里最有话可说的点需求分析、功能模块、数据库设计、业务难点这些章节全都有真实的素材可以展开。从工作量看这个题目也很合适。后端涉及用户/权限、患者档案、医生排班、挂号、门诊接诊、电子处方、收费划价、中西药房库存、药品入库、供应商、报表统计前端的页面量大约15-18个再加小程序患者端和后台管理端的话工作量足够撑起一篇毕业论文又不至于失控做不完。从就业角度说SpringBoot技术栈依旧是绝大多数中小型企业内部系统的主力。答辩的时候你讲用了SpringBootMyBatis PlusJWTRBAC权限模型面试官看了也会觉得你做的不是一个玩具项目。Java方向的岗位微服务问得狠但单体项目的分层设计、事务处理、权限控制反而是入职后立刻要用的东西。2. 技术选型分析SpringBoot搭配Vue和小程序的方案为什么稳2.1 SpringBoot核心优势哪里最实用SpringBoot解决了传统SSH或SSM集成配置繁琐、依赖版本难控的问题。我搭这个项目的时候直接在Spring Initializr里生成骨架选Spring Web、MyBatis Plus、MySQL Driver、Lombok、Validation两分钟就把基础环境拉起。内嵌Tomcat这一点尤其爽打包成jar后在北京010、MySQL、Redis这些必备中间件之外不用在服务器上单独装容器就能跑实训演示和答辩演示都省了很多环境问题。选SpringBoot还有一个现实原因出问题好排查。诊所管理系统这种业务密集型的单体应用遇到最多的就是SQL不对、事务没生效、跨域没配好这类问题。SpringBoot配合Actuator可以看运行时状态配合全局异常处理可以让错误信息统一返回前端等比网上那些SSH项目老代码好调试太多。2.2 前端方案后台管理用Vue还是JSP标题里同时列了Java、PHP、Python、C#、小程序这些方向其实它们对应了不同的毕业设计实现路线。如果走纯Java路线我建议前端尽量选Vue3Element Plus后台管理界面做出来美观度高表格表单、弹窗、树形控件这些组件在中西诊所系统里几乎全部用得到。Element Plus的表格自带筛选、排序、分页写药品库存列表和挂号记录列表时省掉大量原生JS的活。小程序端可以单独选微信小程序。患者端的功能不需要太重主要是注册登录、查看医生排班、在线预约挂号、查看处方历史、在线缴费这几个页面在小程序里实现并不复杂。前端复试的时候小程序端算是一个加分亮点因为评委能看到你除了Web端还做了移动端适配。2.3 为什么加上单片机不是必需的标题里出现了单片机但这个方向和中西诊所管理系统的主线其实是分开的。如果你的毕设题目挂了物联网、智慧医疗可以在系统之外做一个硬件终端比如用51单片机采集环境温湿度DHT11或者通过LCD1602显示候诊排队号再通过串口或Wi-Fi模块把数据传给后端SpringBoot服务。这是一个扩展方向不是核心。如果你纯粹选软件方向不用为了显得高级硬加单片机模块评审老师会追问硬件和业务之间的数据链路说不清楚容易减分。3. 需求分析与功能架构把诊所的一整天装进系统我把一条完整的就医动线梳理了一遍这是做需求分析最有效的方式患者到诊所 - 建档或调取档案 - 挂号分中西医科- 医生接诊写病历开处方 - 划价收费 - 西药房取药/中药房抓药或煎药登记 - 下次复诊。这么一串走下来系统需要支持的模块其实就明确了。下面是我最终确定的功能模块表系统管理用户管理、角色权限、菜单管理、操作日志、数据字典患者管理档案建档、档案查询、历史就诊记录、过敏史/既往病史登记医生排班科室管理、排班计划、出诊状态、号源配置挂号模块窗口挂号、小程序预约挂号、退号、当天号源查询、挂号记录门诊接诊病历书写、诊断记录、辨证分型中医、西医诊断、医嘱电子处方中西药处方分开、模板处方、处方审核、处方历史查询收费划价门诊收费、中西药费合并计算、微信/支付宝/现金支付、退款药房库存西药库存、中药库存、采购入库、库存预警、效期管理、盘点统计报表日营收报表、科室工作量、药品消耗排行、患者流量分析小程序端患者注册、浏览排班、预约挂号、查看报告/处方这个列表不是拍脑袋想的。每个模块都在对应业务流程上有明确输入和输出。划价收费模块的数据一会儿要流向收入统计药房发药后库存要联动减扣患者档案要覆盖全生命周期这些功能间的依赖关系在后边的数据库设计里全部要落成外键和关联表。4. 数据库设计从患者档案到中药库存的表结构拆解4.1 核心表及字段梳理诊所管理系统的数据量不大但是表数量多关系繁杂。我一共设计了20张核心表这里挑几个关键的讲。用户与权限相关三张表sys_user、sys_role、sys_menu再加一张user_role关联表。权限模型我用的是经典的RBAC后端按角色注入菜单权限和操作权限前端用路由守卫控制页面访问。这几张表是几乎所有管理系统的地基字段上重点关注status启用禁用、del_flag逻辑删除。患者档案表patientid、patient_no、name、gender、birth_date、phone、id_card、address、allergy_history、medical_history、create_time、create_by。患者档案是一切的起点所以patient_no建议用规则生成比如P年月日四位流水保证在分页查询和关联查询中好辨识。医生排班表doctor_scheduleid、doctor_id、department_id、schedule_date、period_type上午/下午/晚班、total_num、used_num、status。total_num和used_num这两个字段一排出来就是挂号模块的号源控制核心。挂号表registrationid、patient_id、doctor_id、schedule_id、visit_type西医/中医、fee、status待就诊/已完成/已退号、register_time。这张表关联了患者、医生、排班是中西两条业务线的第一个汇聚点。处方表prescriptionid、registration_id、patient_id、doctor_id、prescription_type中药/西药、diagnosis、syndrome辨证分型、notes、total_amount、status。中药处方和西药处方共用这张表通过prescription_type区分。处方明细表prescription_itemid、prescription_id、drug_id、drug_name、specification、quantity、price、amount、usage_info。usage_info对中药特别重要要记录每日一剂水煎服早晚各一次这类煎服法信息。中药库存表的特殊性中药库存表tcm_stock和西药库存表western_stock可以分开设计。西药按通用名/规格/批次管理中药按药材名称、产地、单位g/kg、库存量管理。中药的计价单位和开方单位得单独处理比如医生开具时按克写库存单位可能是kg或袋处方划价时需要换算。4.2 字段类型的几个细节设计表的时候有几个细节吃过亏。金额字段一律用decimal(10,2)别为省事用double不然累计统计会出现0.10.2不等于0.3的问题。时间字段统一用datetime创建时间用create_time更新时间用update_time配合MyBatis Plus的自动填充功能。逻辑删除字段用tinyint0正常1删除。另外id_card和phone这些字段长度留足。身份证号现在18位加上将来的可能性varchar(32)稳妥。text类型的字段比如病历内容、处方说明不要放主表查询里拆出来或者只在详情页加载否则列表查询会变慢。5. 核心业务实现挂号、划价收费、中药库存这三个最难的点5.1 挂号并发控制挂号看起来简单实际上最容易出并发问题。想象一个场景某个热门医生的号源只剩最后一个两个患者同时提交挂号请求数据库如果没做控制used_num可能加两次变成超过total_num或者同一个号被两个患者抢到。我用的是乐观锁方案。doctor_schedule表里加一个version整数字段更新号源时执行如下逻辑int count doctorScheduleMapper.updateUsedNumAndVersion(scheduleId, usedNum 1, version); if (count 0) { throw new BusinessException(号源已被抢完请选择其他时段); }对应的SQL片段UPDATE doctor_schedule SET used_num used_num 1, version version 1 WHERE id #{scheduleId} AND used_num total_num AND status 1把判断和扣减放到一条SQL里通过影响行数判断是否成功这样既避免了超卖也避免引入分布式锁这种牛刀杀鸡的方案。挂号成功后同步往registration表插入记录整个过程用Transactional包住保证事务一致性。5.2 划价收费的金额计算划价收费模块要处理的最大陷阱是一张处方里既有西药又在中西药房取药收费时要一次性划价但费用明细要能区分中西药。所以在prescription表中total_amount存的是整单合计收费记录顶层按整单收不拆多条药房发药时按明细项目核销。前端展示处方明细时通过prescription_type和item明细分tab展示中、西药区块。收费记录表payment需要留支付方式和支付流水号字段。实际开发中用支付宝或微信官方接口需要商户号、证书等资质学生毕设阶段通常不具备。我用了一个更务实的方案模拟支付。后端生成订单后前端调用一个mock支付接口sleep几秒后回调成功同时生成payment记录。论文里可以写清楚本系统实现了支付流程闭环实际生产环境可无缝对接微信支付V3接口评委完全能理解也避免了资质问题卡住项目。5.3 中药库存扣减与发药联动中西诊所和普通药店最大的区别就是中药库存的特殊性。医生开方时按克、按剂量开药房抓药时要换算总量并扣减库存。下面是我在处方审核通过后进行库存扣减的完整逻辑遍历处方明细按药材ID汇总总用量将总用量换算为库存单位比如开方总量为150g库存单位是kg则换算为0.15kg执行库存扣减SQL携带库存校验条件// 按药品id汇总处方用量 MapLong, BigDecimal totalUsageMap itemList.stream() .collect(Collectors.groupingBy( PrescriptionItem::getDrugId, Collectors.reducing(BigDecimal.ZERO, PrescriptionItem::getQuantity, BigDecimal::add) )); for (Map.EntryLong, BigDecimal entry : totalUsageMap.entrySet()) { int rows tcmStockMapper.reduceStock(entry.getKey(), entry.getValue()); if (rows 0) { throw new BusinessException(药品库存不足请先采购入库); } }在真实抓药场景中药师抓完还要确认发药库存扣减放在发药确认动作里而不是处方开立时。这样设计更符合业务习惯医生开方只是预占药房实发才扣库存。理疗、针灸这类服务项目不涉及库存只走服务收费所以在收费明细里增加了item_type字段区分药品和服务。6. 开发中容易翻车的七个坑和排查链路这些坑都是我实际开发中踩过的如果你也做同类系统照着这个思路能省很多时间。6.1 MyBatis Plus分页插件失效症状Page对象传进去查出来还是全量数据。原因没有配置MyBatis Plus的分页拦截器。解决Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }另外分页查询多表关联的时候Page要作为第一个参数传给自定义Mapper方法别放在中间。6.2 LocalDateTime前后端序列化不一致实体类用了LocalDateTime前端拿到的时间格式是一串数组或者带了字母T的ISO格式。解决办法在application.yml里全局统一格式也可以在字段上配合JsonFormat注解。spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8要注意的是如果用了JsonFormat优先级会覆盖全局配置字段和全局配置最好保持一致。6.3 事务不生效的根本原因Spring的事务是AOP代理同类内部方法之间调用时事务会失效。我犯过的错是在Service层一个方法里直接调用本类的另一个Transactional方法第二个方法即使抛了异常外层也没上报。解决方式是把需要事务的入口方法加注解或者拆到不同Service互相调用。Transactional默认只回滚RuntimeException检查性异常不会自动回滚必要的时候用rollbackFor Exception.class。6.4 挂号扣库存时版本字段没生效如果忘记在实体类上给version字段加Version注解乐观锁等于没配。还有更新操作的实体对象要带上当次查询的version值不要只在SQL里写了version条件但传入的是null。6.5 跨域问题导致小程序调接口失败前端页面在8080端口后端在8081端口存在跨域。一次开发的时候忘记了全局跨域配置导致Vue联调时所有的POST请求全部报错。配置一个WebMvcConfigurer全局处理Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); }注意allowedOriginPatterns比allowedOrigins(*)更宽松允许带凭证的跨域请求。6.6 逻辑删除和唯一索引的冲突患者表的phone字段做了唯一索引如果逻辑删除了一位患者再新增同号患者会报唯一键冲突。这种情况可以加一个deleted_version字段或者唯一索引改成(phone, del_flag)。简单点处理是把del_flag从0/1改成记录删除时间的bigint0代表未删除删除时写入时间戳这样唯一键天然不冲突。6.7 原生SQL里传List参数没写ParamMapper接口方法中传List时如果SQL的foreach需要变量名必须在参数上加Param(list)否则MyBatis会报参数找不到或者只能取第0个元素。这个问题几乎每个做分页筛选、批量删除的同学都会遇到一次花五分钟记住就行。7. 论文写作与演示答辩把项目讲成高分毕设系统做完只是第一步毕业设计的分数很大比重在论文和答辩上。论文的结构我用了选题背景-需求分析-概要设计-详细设计-系统实现-测试这个标准六章法但在内容上做了两个强化。第一个强化是需求分析章节画了一个完整的业务时序图把患者从注册到取药离开的全流程标清楚。时序图画清楚之后后面的表结构设计、功能模块划分都是顺着时序往下拆的导师看到了会认为你有全局观。第二个强化是测试章节不只列了下拉框点选、必填项校验还专门设计了一个并发挂号测试用JMeter模拟50个用户同时抢一个号源验证乐观锁生效。论文里有这个性能/并发测试和那些只测增删改查的同学之间差距一下就拉开了。答辩环节我准备的几个高频问题是为什么选SpringBoot不用SSH答简化配置、内嵌容器、生态系统成熟、就业需求大。中西药两条业务线怎么在一个系统里共存答通过type字段区分核心流程挂号、收费、处方共用专业流程辨证分型、亚单位换算、煎药登记扩展。库存不足你是怎么处理的答前端预检查、后端扣减SQL条件校验、事务回滚三重保障。如果并发量再大怎么办答先做读写分离再上Redis缓存号源最后才是集群部署。按这个顺序答基本挑不出错。演示环节有个实操要点提前把演示数据准备好。常用药品、医生排班、患者档案每类数据准备8到10条演示的时候不要现场录数据直接展示列表、详情、统计报表节奏快且不翻车。这个项目整体做下来我对SpringBoot单体应用的理解提升了不少特别是事务边界、并发控制和数据库字段设计这些平时写练习Demo根本碰不到的东西。如果你也是拿现成源码起步建议不要直接跑起来就交差按这篇文章的思路把核心几个业务模块抠一遍改一遍答辩的时候才讲得出东西。有具体不懂的模块欢迎在评论区留言我看到会回复。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →