ARTICLE DETAIL

资讯详情

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

Spring Boot医院就诊系统开发实战:从排班挂号到电子病历的完整实现

Spring Boot医院就诊系统开发实战:从排班挂号到电子病历的完整实现 做医院就诊系统这个Spring Boot项目我前前后后折腾了好几周。标题里“源码文档运行视频讲解视频”看起来是个很完整的毕业设计套餐但真正入手你会发现把医生排班、预约挂号、电子病历、药品管理串起来坑比想象中多得多。这篇文章就把我实际开发、调试、部署这套系统的完整经验整理出来包括技术选型思路、数据库设计、核心业务逻辑处理、运行环境搭建和排坑记录给正在做类似课题的同学一份能直接“抄作业”的参考。1. 项目整体设计与技术选型思路1.1 为什么选Spring Boot Java Web而不是别的方案医院就诊系统这种管理类项目业务核心是“数据管理和流程流转”不涉及高并发、海量数据、复杂算法Spring Boot是目前最合适的选择没有之一。原因有三点第一开发效率高。Spring Boot把Spring MVC、MyBatis、事务管理、参数校验这些基础配置全部自动化了你不需要写一堆XML配置文件一个SpringBootApplication注解就能起项目。做课程设计或者毕业设计时间宝贵省掉的配置时间可以用在业务逻辑上。第二生态成熟资料多。SSHStrutsSpringHibernate和SSMSpringSpringMVCMyBatis是前几年的主流现在新项目基本都用Spring Boot。遇到问题随便一搜就是答案不像冷门框架那样半天查不到资料。而且招人单位包括面试官对Spring Boot的认可度也高写在简历上比“SSM”有说服力。第三前后端分离更灵活。虽然很多课程设计还是用Thymeleaf模板渲染但这个项目我推荐用REST接口 前端静态页面或者Vue实现。Spring Boot天生就是为REST API设计的RestController直接返回JSON前端想怎么调就怎么调。就算你暂时不会Vue用Bootstrap AJAX也能做出不错的交互效果。1.2 功能模块拆解从标题看系统必须覆盖的五个核心域标题已经很直白了“医生排班、预约挂号、电子病历、药品”外加角色权限这就是整个系统的核心。我把需求拆成了五个模块系统管理登录认证、用户管理、角色权限管理员、医生、患者三种角色。排班管理医生按照科室、日期、时间段生成排班表支持查看、修改。预约挂号管理患者选择科室-医生-时间段进行预约支持取消预约、签到。电子病历管理医生在接诊时填写病历包括主诉、诊断、处方、检查建议等患者可查看历史病历。药品管理药品信息维护名称、规格、库存、价格医生开处方时扣减库存管理员可采购入库。没有做“收费结算”和“住院管理”是因为标题里没有这俩关键词。做项目一定要克制功能范围越聚焦越容易做深。如果你想加亮点可以在预约挂号的“号源锁定”和“超时释放”上做文章这个我们后面详细讲。2. 数据库设计与核心业务表2.1 表结构设计思路宁可多关联不要存冗余数据库是这类系统的地基。我第一版设计图省事把医生姓名直接存到挂号记录表里后来加排班功能的时候发现根本没法维护——医生改了科室、排班改了时间历史挂号记录全对不上。所以后来老老实实按范式设计核心表如下表名用途关键字段user用户表统一登录id, username, password, role, statuspatient_profile患者信息表id, user_id, real_name, id_card, phone, gender, birthdaydoctor_profile医生信息表id, user_id, dept_id, title, intro, register_feedepartment科室表id, dept_name, dept_descschedule排班表id, doctor_id, dept_id, work_date, period, max_count, remain_countappointment挂号预约表id, patient_id, schedule_id, appoint_no, status, create_timemedical_record电子病历表id, appointment_id, patient_id, doctor_id, diagnosis, prescription, create_timedrug药品表id, drug_name, spec, unit, price, stockprescription_item处方明细表id, medical_record_id, drug_id, quantity, dosage用户表统一了所有角色的登录账号再用profile表扩展不同角色的具体信息。好处是登录逻辑只要写一套密码加密校验可以统一处理。排班表和挂号表是核心关联一个排班schedule对应多个预约appointment挂号时要把schedule.remain_count减1这个字段就是防止超挂的“锁”。2.2 核心表字段设计踩坑金额用decimal时间用date类型有几个字段类型我踩过坑提醒一下金额、价格drug.price和register_fee务必用DECIMAL(10,2)不要用FLOAT或DOUBLE。浮点数在数据库里存的是近似值做结算累计时容易出现0.01的误差。我在药品管理里就遇到库存和销售额对不上的尴尬后来排查发现是price字段用了double。日期和时间排班的work_date用DATE时间段用VARCHAR比如“上午”“下午”反而最简单。如果用DATETIME跨天、节假日调整排班会非常痛苦。预约的create_time用DATETIME记录实际创建时间。状态字段appointment.status建议用TINYINT0-待就诊1-已就诊2-已取消3-已过期。不要用中文枚举程序里定义常量就行数据库里数字更快更规范。2.3 索引与唯一约束防止重复挂号和慢查询预约挂号表必须加联合唯一索引UNIQUE KEY uk_schedule_patient (schedule_id, patient_id)防止同一个患者对同一个排班重复挂号。没有这个约束并发操作时可能出现两条记录。另外schedule表的(doctor_id, work_date)上也建议加普通索引因为排班页面经常按医生和日期查询。我在实际测试时用JMeter模拟50个并发同时挂号不加索引时数据库CPU飙升加了索引后响应时间从2秒降到80毫秒。别小看索引这种小项目虽然数据量不大但面试时问起来你能答出“为什么加索引”会加分不少。3. 核心业务逻辑实现预约挂号的并发处理、排班生成与病历管理3.1 预约挂号的“库存扣减”乐观锁才是正解预约挂号的核心逻辑是用户选一个排班申请挂号系统检查余号0创建一个挂号单余号减一。这里最经典的错误写法是Schedule schedule scheduleMapper.selectById(scheduleId); if (schedule.getRemainCount() 0) { schedule.setRemainCount(schedule.getRemainCount() - 1); scheduleMapper.updateById(schedule); // 创建挂号记录 }这段代码在单用户下没问题但并发场景下两个请求同时读到remain_count为1都进入if分支都减一最后生成两张挂号单余号变成-1。这就是“超卖”问题。解决办法有两个我最终选了乐观锁UPDATE schedule SET remain_count remain_count - 1 WHERE id #{scheduleId} AND remain_count 0在Service层执行这条update如果影响行数为1说明扣减成功才去创建挂号记录。如果影响行数为0说明余号不足或已被抢走直接抛异常“号源已约满”。这种写法比在Java代码里先查再改简单得多也彻底避免了超挂。注意这种方式要求schedule表的数据操作必须在事务里同时要保证事务隔离级别至少为READ_COMMITTED。好消息是Spring的Transactional默认就是REQUIRED直接加在方法上即可。另外还要考虑“30分钟内未支付或者未确认自动释放号源”的需求。简单做法是定时任务扫描status0的预约记录超过30分钟将其置为“已过期”并回补schedule的remain_count。回补SQLUPDATE schedule SET remain_count remain_count 1 WHERE id #{scheduleId}这里不需要再判断余号上限因为本来就扣过一次回补是安全的。3.2 排班生成一张schedule表搞定别搞复杂的时间段表排班界面通常展示一周的排班表需求是管理员为医生选择星期几、上午/下午设置号源数。我最开始设计成了“排班明细表”每天每个时段一条记录结果发现生成一周排班时要循环7天×2时段写代码麻烦前端展示也绕。后来简化成schedule表就是“医生某日某时段”的排班记录生成排班时先删除该医生在目标日期范围内的旧数据再插入新数据。示例代码简化版Transactional public void generateSchedule(int doctorId, ListScheduleRequest requests) { // 先删除该医生在范围内的排班 scheduleMapper.deleteByDoctorIdAndDateRange(doctorId, startDate, endDate); for (ScheduleRequest req : requests) { Schedule s new Schedule(); s.setDoctorId(doctorId); s.setWorkDate(req.getDate()); s.setPeriod(req.getPeriod()); s.setMaxCount(req.getMaxCount()); s.setRemainCount(req.getMaxCount()); scheduleMapper.insert(s); } }前端用一个周视图点击“生成排班”按钮后端接收医生ID和一周的数据列表批量处理。这里不用考虑跨周复杂排班规则因为没那个必要。需求要克制功能完整即可。3.3 电子病历与药品扣减的联动电子病历模块医生的操作路径是选择预约记录 → 填写主诉、诊断 → 添加药品明细选药、数量、用法 → 提交。提交时要在同一个事务里保存病历主记录和处方明细同时扣减药品库存。核心代码思路Transactional public void addMedicalRecord(MedicalRecordDTO dto) { MedicalRecord record new MedicalRecord(); // 填充字段 medicalRecordMapper.insert(record); for (PrescriptionItemDTO item : dto.getItems()) { PrescriptionItem pi new PrescriptionItem(); pi.setMedicalRecordId(record.getId()); pi.setDrugId(item.getDrugId()); pi.setQuantity(item.getQuantity()); pi.setDosage(item.getDosage()); prescriptionItemMapper.insert(pi); // 扣库存 int updated drugMapper.reduceStock(item.getDrugId(), item.getQuantity()); if (updated 0) { throw new BusinessException(药品【 item.getDrugName() 】库存不足); } } }这里有一个小坑reduceStock的SQL要写成UPDATE drug SET stock stock - #{qty} WHERE id #{id} AND stock #{qty}条件带stock qty返回0就是库存不足。如果在Java里先查库存再判断同样会出并发超卖问题。药品扣减和号源扣减本质是一样的。病历保存和药品扣减必须在同一个事务里否则万一药品扣减失败病历已经保存了会出现“病例有处方但库存没扣”的数据不一致。Transactional就是干这个的spring通过AOP在异常时回滚全部操作。3.4 角色权限控制用一个Interceptor就够了这类系统不需要引入Spring Security这种重型框架虽然引入它能让简历好看一点但学习成本高而且课程设计/毕设的重点通常不在安全上。我用的方案是自定义HandlerInterceptor 登录用户存入ThreadLocal。具体做法登录成功后把用户信息存到Session或者用JWT存到前端但纯模板渲染用Session简单然后写一个LoginInterceptor在preHandle里判断Session中是否有用户没有就重定向到登录页。对于需要角色限制的接口用RequireRole(ADMIN)之类的注解匹配或者简单地在方法里用UserUtil.getCurrentUser().getRole()判断。注意静态资源css/js/images要排除拦截。我一开始没排除页面样式直接全挂了排查半天才发现拦截器把静态资源也给拦了哭笑不得。4. 实操过程从源码到运行逐个环节避坑4.1 环境准备工作清单标题里说“源码文档运行视频讲解视频”我拿到手的源码一般是完整的工程但是要正常运行还需要符合版本要求的本地环境。我的环境清单供参考组件版本推荐备注JDK1.8 或 11Spring Boot 2.x推荐1.8如果是Spring Boot 3.0以上必须用JDK17Maven3.6用IDEA内置也行MySQL5.7 或 8.08.0记得改驱动和时区配置IDEIntelliJ IDEA社区版够用前端静态资源Bootstrap jQuery一般包含在resources/static里如果你发现源码里的pom.xmlparent版本是2.7.x那JDK8没问题。如果是3.1.x开头别犹豫直接装JDK17。很多同学卡在启动报错十有八九是JDK版本不对。我一开始没看版本用JDK8跑Spring Boot 3.1启动直接报UnsupportedClassVersionError。4.2 配置文件修改别漏了数据库时区和表名大小写Spring Boot项目的核心配置在src/main/resources/application.yml或者properties。需要修改的地方spring: datasource: url: jdbc:mysql://localhost:3306/hospital?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456这里最坑的是serverTimezone。MySQL 8.0默认时区是UTC如果不设置数据库连接会报错The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。加上serverTimezoneAsia/Shanghai即可。第二个坑是MySQL 8.0的表名大小写敏感。Linux下MySQL默认lower_case_table_names0如果你在Window开发时表名是大写部署到Linux就报“表不存在”。统一小写表名或者在配置文件里加mybatis.configuration.map-underscore-to-camel-casetrue同时保证Java实体字段和下划线列名能映射上。数据库初始化脚本一般放在db/hospital.sql导入时注意选择合适的编码。用Navicat或命令行导入mysql -uroot -p123456 hospital.sql导入成功后先执行show tables;确认所有表都在。4.3 启动项目与运行视频中常见的“演示顺序”这里分享一个实操经验运行视频里演示的步骤通常很流畅但自己跑起来经常手忙脚乱。建议按照下面的顺序过一遍能少走很多弯路启动MySQL服务导入SQL脚本确认服务端口3306没问题。IDEA打开源码工程等待Maven下载依赖第一次很慢建议换阿里云镜像。修改application.yml里的数据库用户名密码。运行HospitalApplication.java主类观察控制台日志出现Tomcat started on port(s): 8080就成功了。浏览器访问http://localhost:8080先注册一个患者账号如果系统开放注册。用管理员账号登录最好在SQL里看下初始账号通常是admin/admin123之类进入后台管理系统先建科室、加医生、录药品。给医生排班生成一周排班。切换到患者端找到对应科室医生点击挂号。用医生账号登录点击“接诊”填写电子病历和处方。管理员查看挂号统计、药品库存变化。这个顺序覆盖了所有核心功能演示录项目演示视频时也建议按这个来逻辑连贯屏幕前的人不会看不懂。4.4 Maven依赖下载慢配置阿里云镜像如果你发现Maven下载依赖龟速在settings.xml里加阿里云镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror另外IDEA中设置Maven的Runner里勾选“Delegate IDE build/run actions to Maven”有时候能避免一些诡异的资源文件加载问题。5. 常见问题与排查技巧实录5.1 启动时端口被占用症状启动日志显示Port 8080 was already in use。 解决换端口在application.yml里加server.port: 8081或者杀掉占用进程。Windows下用netstat -ano | findstr 8080查看PID然后taskkill /F /PID 进程号。5.2 MyBatis的XML文件找不到绑定症状启动后调用Mapper方法报Invalid bound statement (not found)。这是非常典型的问题原因是MyBatis的mapper XML文件没有打进classes目录。检查pom.xml里是否有resource directorysrc/main/resources/directory includes include**/*.xml/include /includes /resource另外确认application.yml里配置了mybatis: mapper-locations: classpath:mapper/*.xml我在写这个系统时把mapper目录放在resources/mapper下但忘记在pom里配置resource结果测试时查了半小时最后发现编译后的target目录里根本没有XML文件。这个坑面试也常考值得记下来。5.3 时间字段查询不出来或者少8小时如果数据库能查出来但程序返回的时间比实际少8小时大概率是时区问题。在JDBC连接串上加上serverTimezoneAsia/Shanghai同时Jackson序列化设置也检查下。Spring Boot中JSON序列化LocalDateTime还需要特殊配置如果前端显示格式不对加spring.jackson.date-format或自定义JacksonConfig。5.4 前端AJAX访问接口遇到跨域前后端分离项目如果前端页面不是由Spring Boot直接托管而是单独开了一个端口比如用Vue的8081那AJAX请求会访问8080遇到跨域错误。在后端加一个配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*); } }注意allowedOriginPatterns(*)在Spring Boot 2.4才支持老版本写allowedOrigins(*)也可以。5.5 排班日期跨周显示不全SQL查询的日期边界排班表查询时经常用BETWEEN ? AND ?如果前端传的日期范围没有覆盖到周日会出现周日排班缺失。我当时在前端组件里用date-fns计算周一和周日作为默认查询范围后端再校验一下startDate endDate就返回参数异常。这个属于细节逻辑但演示时很能体现专业度。6. 额外加分项让这个项目的“技术深度”不落俗套6.1 引入Redis做号源缓存与分布式锁可选如果你的毕设答辩需要讲一点“性能优化”可以在预约挂号接口上引入Redis。思路是把每个排班的号源先缓存到Redis挂号的原子操作变成DECR命令然后异步写回数据库。在高并发下直接对DECR后的值判断是否小于0小于0说明号源没了。这里要注意Redis与数据库数据一致性问题需要定时同步余号。但如果是课程设计用乐观锁就够了Redis属于“锦上添花”讲得好是亮点讲不透反而被质疑。6.2 添加“叫号队列”功能系统还可以扩展一个“科室候诊队列”医生点击“开始接诊”时将当前患者从候诊列表移到接诊中。这个小功能逻辑简单但能让项目比普通的增删改查高一个档次。对应的表可以增加一个visit_queue或者直接在appointment表加一个queue_no字段按预约时间排序生成队列号前端显示候诊人数。6.3 数据可视化统计报表管理员首页通常要展示统计信息今日挂号量、各科室挂号占比、药品库存预警。用ECharts画饼图、柱状图数据从后端聚合查询返回。这样整个系统的“管理端”看起来更饱满也弥补了纯增删改查的单调感。7. 测试与演示的完整流程确保万无一失7.1 功能测试用例速查表编号测试项操作预期结果01登录输入错误密码提示密码错误停留在登录页02科室管理新增科室列表显示新科室可编辑/删除03医生排班选择医生与日期提交该医生的排班展示在周视图中04预约挂号患者选择号源并确认余号数减1挂号单状态为待就诊05重复预约同一患者再次预约同一排班提示“您已预约该时段”06挂号超时释放用定时任务触发过期过期号源回补余号07医生接诊填写病历开处方病历保存药品库存扣减08药品库存不足开处方时数量大于库存提示库存不足事务回滚09权限控制患者访问医生接口拦截并跳转到无权限页面10管理员统计查看统计图表图表数据和数据库一致7.2 演示视频录制的几个技巧标题里包含“运行视频讲解视频”说明交付物里需要录屏。我录视频时踩过几个坑说一下录屏前先清空浏览器缓存保证打开页面是干净的状态。录入库脚本时用命令行的source导入不要用Navicat图形化命令行看起来更专业。演示数据要预置好至少三个科室、五名医生、十种药品、几天的排班。当场现建虽然也行但会很啰嗦。讲解视频可以用OBS录屏 麦克风解说语速慢一点每个操作前先说“我要做什么预期看到什么”然后操作。视频分辨率至少1080p视频剪辑用剪映足够了记得给鼠标点击的位置加高亮观看体验会好很多。7.3 答辩/演示时的加分话术如果被问到“挂号并发怎么解决”可以这么说“我在设计时考虑到了秒杀场景下的超卖问题。在扣减号源时使用了数据库的原子更新操作将判断余号和扣减合并成一条带条件的SQL保证同一时刻只有一个线程能成功扣减。同时把创建挂号记录和扣减号源放在同一个事务里确保数据一致性。高并发场景下还可以引入Redis的预减库存方案做进一步的性能优化。”这段话既有理论又结合了实现是“能打”的回答。8. 写在最后的实操心得这个项目做完我最深刻的体会是课程设计/毕业设计的核心不是炫技而是闭环。从数据库设计到后端接口再到前端页面最后能完整跑通一条“患者挂号-医生接诊-开药-扣库存”的业务链路就是优秀的交付。很多同学卡在刚起步的阶段总想着把所有技术都塞进去结果数据库都没建明白。我建议你先从最小的闭环做起再逐步扩展功能。还有一个小技巧是给项目写一份“用户手册”哪怕只有两三页面试官会觉得你很有工程意识。手册里写清楚怎么导入数据库、怎么修改配置、怎么测试每个功能顺便也方便你自己录视频时照着操作。我这份手册最后变成了答辩PPT里的核心素材省了不少时间。如果你正在做类似的系统希望这篇博客能帮你少走一点弯路。项目源码里的注释和文档再全也没有自己亲手跑通一遍来得实在。按照我上面说的流程先数据库、再后端、再前端遇到问题对着“常见问题与排查”一节逐个对照基本能搞定。祝你的系统一次顺利跑通答辩时信心满满。
返回列表