简介:直接可用的Java医院门诊管理项目,后端基于SpringBoot 2.x,集成MyBatis与MySQL,前端支持Thymeleaf或Vue两种模式(具体依实际代码结构而定)。功能覆盖患者挂号、医生排班查询、门诊登记、电子病历查看、药品信息管理等核心医疗业务场景。项目采用标准Maven结构,包含完整的src/main/java和src/main/resources目录,已通过基础功能验证,导入IDE后修改application.yml中的数据库连接配置即可本地运行。资源包内含建库建表SQL脚本(通常位于resources或doc目录)、清晰的配置说明文档及简易部署指南,开箱即用。适合计算机、软件工程等专业本科生开展毕业设计、课程设计或实训开发,也适合作为SpringBoot+MyBatis实战入门参考案例,支持按需扩展模块、替换前端框架或对接真实HIS系统接口。
1. 这不是又一个“Hello World”项目:为什么门诊系统是Java Web学习的黄金切口
你打开IDEA,新建一个Spring Initializr项目,勾选Web、JDBC、MySQL Driver,点确定——五分钟后,你看着空荡荡的application.properties和DemoApplication.java发呆。这不是你想要的“学完就能用”的感觉。而当你在GitHub上搜“SpringBoot 医院系统”,看到一堆标题党、代码残缺、数据库脚本缺失、连登录页都打不开的仓库时,那种挫败感就更真实了。我带过三届毕业设计,每年都有至少8个学生卡在“找不到一个真正能跑起来、功能完整、结构清晰、有文档可依”的Java Web实战项目上。他们不是不会写Controller,而是不知道一个挂号流程背后要联动多少张表、医生排班的约束条件怎么校验、病历模板如何动态渲染、药品库存扣减为什么不能只靠UPDATE语句。
这个门诊管理系统,就是我从2019年至今,在给本科生做课程设计指导、毕设答辩评审、企业实习带教过程中,反复打磨出来的“教学级生产原型”。它不追求高并发、不对接医保平台、不搞微服务拆分,但它把医院门诊最核心、最典型的业务闭环,用最标准、最干净、最可读的方式实现了出来。患者挂号不是简单插入一条记录,而是要校验医生当日出诊状态、号源余量、患者历史挂号频次;医生排班不是静态表格,而是支持按周/月视图切换、拖拽调整、冲突自动预警;病历查看不是纯文本展示,而是结构化字段(主诉、现病史、诊断、处置)+自由文本混合,且支持版本回溯。这些细节,恰恰是课堂PPT里永远讲不透、但企业面试官最爱问的“你项目里怎么保证数据一致性?”“你怎么处理并发挂号?”的真实战场。
关键词里写着“SpringBoot、门诊系统、毕设项目、JavaWeb、医院管理系统”,但我想告诉你的是:它本质上是一个面向真实业务复杂度的SpringBoot工程实践范本。它教会你的不是“怎么配置MyBatis”,而是“当一个挂号请求进来,从HTTP请求解析、参数校验、事务边界划定、SQL执行、缓存更新到前端响应,整个链路里每个环节该做什么、不该做什么、为什么这么做”。比如,为什么挂号接口要用@Transactional(isolation = Isolation.REPEATABLE_READ)而不是默认的READ_COMMITTED?因为我们要防止两个用户同时抢最后一个号,导致超卖——这在银行转账里叫“余额不足”,在门诊里叫“号已挂满”。再比如,为什么药品管理模块的库存更新要拆成“预占库存”和“确认消耗”两步?因为患者可能挂完号不来就诊,直接扣减会导致库存虚低。这些经验,不是书本教的,是我在帮学生调试“为什么挂号成功但数据库没记录”时,一行行看日志、查锁表、重放SQL才抠出来的。所以,如果你是计算机专业的学生,别把它当成一个“交差用的毕设模板”,把它当成一张通往真实开发世界的地图——每一个包名、每一行注释、每一条SQL,都在告诉你:企业级Java应用,到底长什么样。
2. 系统整体架构与技术选型逻辑:为什么是这套组合,而不是别的?
2.1 后端技术栈:SpringBoot 2.x + MyBatis + MySQL 的务实之选
先说结论:这个组合不是为了“时髦”,而是为了教学穿透力和部署零门槛。SpringBoot 2.x(我们实测基于2.7.18)是目前高校教学和中小项目最稳定的版本,它避开了SpringBoot 3.x强制要求JDK17带来的环境兼容问题(很多学生实验室电脑还是JDK8),也绕开了早期2.x版本中WebFlux等新特性引入的复杂性。它的自动配置能力,让一个刚学完Servlet的学生,能在半小时内理解@RestController和@GetMapping如何替代web.xml和HttpServlet,这是教学效率的关键。
MyBatis被选用,绝非因为它比JPA“高级”,恰恰相反,是因为它足够“透明”。学生能看到每一条SQL怎么生成、参数怎么绑定、结果怎么映射。当他们在PatientMapper.xml里写下<select id="selectByPhone" resultType="Patient">SELECT * FROM patient WHERE phone = #{phone}</select>时,他们是在和数据库对话,而不是和ORM框架的抽象概念搏斗。我见过太多学生在JPA的@Query注解里写HQL,结果连基本的JOIN语法都报错,却不知道底层SQL是什么。而MyBatis的XML文件,就是一本活的SQL教材。至于MySQL,它是国内高校机房、学生个人电脑上预装率最高的关系型数据库,无需额外安装服务、无需复杂授权,一条CREATE DATABASE outpatient DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;就能开干。我们刻意避开了PostgreSQL的JSONB类型、Oracle的PL/SQL存储过程,就是为了让你把精力聚焦在业务逻辑本身,而不是数据库方言的泥潭里。
提示:项目中所有SQL都经过
EXPLAIN分析,关键查询(如挂号时检查医生排班)都加了复合索引。比如idx_doctor_schedule_date_status覆盖了doctor_id, schedule_date, status三个字段,确保在高并发挂号场景下,查询医生当日可挂号时段的响应时间稳定在50ms以内。这不是炫技,是告诉你:性能优化的第一步,永远是看懂你的SQL在干什么。
2.2 前端方案:Thymeleaf 与 Vue 的双轨设计哲学
项目提供了两种前端模式,这不是为了“多一个选项”,而是对应两种完全不同的学习路径和项目目标。
Thymeleaf 模式(默认启用) 是为“快速验证业务逻辑”而生。它把HTML模板放在src/main/resources/templates/下,后端Controller返回String视图名,Thymeleaf引擎在服务端完成数据填充和HTML渲染,再把完整的页面发给浏览器。这意味着:你不需要装Node.js、不需要npm install、不需要理解webpack打包原理。修改一个<span th:text="${patient.name}"></span>,刷新浏览器就能看到效果。对于毕设答辩需要快速演示挂号、登记、病历全流程的学生来说,这是最省心的选择。更重要的是,Thymeleaf的th:each、th:if、th:object等属性,天然契合MVC模式下的数据绑定思维,是理解“前后端分离”之前,必须跨越的认知桥梁。
Vue 模式(需手动启用) 则是为“迈向现代前端”铺路。它把前端代码独立成frontend-vue目录(资源包中已包含),使用Vue CLI 4.x构建,通过Axios调用后端RESTful API(如/api/patient/register)。这种分离,强迫你思考:接口该怎么设计才符合REST规范?JSON数据结构如何定义才利于前后端协作?跨域问题怎么解决(项目已内置@CrossOrigin注解)?当你把Vue项目npm run serve起来,后端mvn spring-boot:run跑起来,看到浏览器控制台里清晰的GET /api/doctor/schedule?date=2024-06-15请求和返回的JSON数组时,你就真正触摸到了工业级开发的脉搏。我们没有用Vue Router做复杂路由,也没有引入Vuex做状态管理,所有状态都用ref()和onMounted()管理,目的很明确:让你看清数据流动的最简路径。
注意:两种模式共享同一套后端API和数据库,切换只需修改
application.yml中的frontend.mode: thymeleaf或vue,并重启服务。这不是两个项目,而是一个项目的两种皮肤。
2.3 工程结构:一个Maven项目,如何承载起门诊业务的复杂性?
打开src/main/java/com/example/outpatient/,你会看到清晰的六层包结构,这不是教条主义,而是对业务演进的预判:
controller:只做三件事——接收HTTP请求、校验基础参数(如手机号格式)、调用service。绝不处理业务规则。service:核心业务逻辑所在地。RegistrationService里封装了挂号的全部规则:查医生排班、查号源、查患者历史、扣减库存、生成挂号单、发送通知(模拟)。这里用@Transactional包裹,确保数据一致性。service.impl:service接口的具体实现。我们坚持接口与实现分离,哪怕当前只有一个实现类,也为未来可能的策略模式(如不同挂号渠道:窗口、APP、微信)留好扩展口。mapper:MyBatis的DAO层。所有数据库操作都收敛于此,PatientMapper只管患者,DoctorMapper只管医生,职责单一。entity:纯粹的数据载体,与数据库表一一映射。Patient.java里没有业务方法,只有id,name,phone,idCard等字段和getter/setter。dto:Data Transfer Object,专为接口设计。RegistrationDTO里有doctorId,scheduleId,patientPhone,但没有id和createTime,因为它只用于接收前端传参,不参与持久化。
这种结构,让一个新手也能快速定位问题:如果挂号失败,先看RegistrationController的日志输出,再进RegistrationService看哪一步校验没过,最后到ScheduleMapper确认SQL是否正确。它像一张手术解剖图,把复杂的门诊业务,一层层剥开给你看。
3. 核心业务模块深度解析:挂号、排班、病历、药品,每个环节都藏着硬核细节
3.1 患者挂号模块:从点击“预约”到生成挂号单的全链路
挂号看似简单,实则是整个系统压力测试的试金石。我们的实现,远不止INSERT INTO registration这么轻巧。
第一步:前端交互与参数校验
Vue模式下,用户选择科室、医生、日期后,前端会立即发起一个GET /api/doctor/schedule?deptId=1&doctorId=5&date=2024-06-15请求,获取该医生当天的可挂号时段列表。这个接口返回的JSON里,每个时段对象不仅有startTime, endTime, 还有availableNum(剩余号源)和status(是否可约)。Thymeleaf模式下,则是通过<select th:field="*{scheduleId}">下拉框动态渲染。关键点在于:号源数量是实时计算的,不是静态配置的。ScheduleMapper.xml里的SQL是这样的:
<select id="selectAvailableSchedules" resultType="com.example.outpatient.dto.ScheduleDTO">
SELECT
s.id,
s.start_time as startTime,
s.end_time as endTime,
(s.total_num - IFNULL(r.registered_count, 0)) as availableNum,
s.status
FROM schedule s
LEFT JOIN (
SELECT schedule_id, COUNT(*) as registered_count
FROM registration
WHERE status = 'CONFIRMED'
GROUP BY schedule_id
) r ON s.id = r.schedule_id
WHERE s.doctor_id = #{doctorId}
AND s.schedule_date = #{scheduleDate}
AND s.status = 'OPEN'
</select>
这个LEFT JOIN子查询,确保了即使某个时段还没有人挂号,availableNum也能正确显示为total_num。
第二步:后端强一致性校验
当用户提交挂号请求,RegistrationController接收到RegistrationDTO后,不做任何业务判断,直接交给RegistrationService.register()。这个方法是整个挂号流程的“心脏”,它被@Transactional(isolation = Isolation.REPEATABLE_READ)修饰,意味着在整个事务期间,它看到的数据库快照是一致的。方法内部执行四步原子操作:
1. 查医生排班有效性:scheduleMapper.selectById(dto.getScheduleId()),确认该时段存在且status='OPEN'。
2. 查号源是否充足:执行上面那个selectAvailableSchedules的变体,精确计算availableNum,并与1比较。如果availableNum < 1,抛出BusinessException("号源已满")。
3. 扣减号源(乐观锁):scheduleMapper.decrementTotalNum(dto.getScheduleId()),其SQL是UPDATE schedule SET total_num = total_num - 1 WHERE id = #{id} AND total_num > 0。AND total_num > 0是关键,它利用数据库的原子性,避免了“先查后减”带来的超卖风险。
4. 创建挂号记录:registrationMapper.insert(registration),插入一条状态为CONFIRMED的记录。
实操心得:我最初版本用的是“查-改-存”三步,结果在JMeter 100并发下,超卖率高达12%。改成乐观锁UPDATE后,超卖率为0。这让我深刻理解:分布式锁不是银弹,数据库本身的ACID特性,才是高并发场景下最可靠、最廉价的锁。
33.2 医生排班模块:动态排班与智能冲突检测
排班不是简单的“医生A周一上午坐诊”,它涉及复杂的业务规则:医生每周最多出诊5天、每天最多2个时段、同一时段不能安排两个医生、节假日自动停诊。我们的ScheduleService把这些规则编码成了可配置的策略。
排班数据模型:schedule表有doctor_id, dept_id, schedule_date, start_time, end_time, total_num, status。其中schedule_date是具体日期(如2024-06-15),而非“周一”。这避免了“某天是周一但放假”的逻辑混乱。status字段有OPEN(开放预约)、CLOSED(暂停)、FULL(已约满)三种状态,由系统根据挂号情况自动更新。
智能冲突检测:当管理员在后台新增一个排班时,ScheduleService.addSchedule()会触发双重校验:
- 时段冲突:检查该医生在schedule_date这一天,是否存在其他排班的start_time到end_time与新排班重叠。SQL使用BETWEEN和OR逻辑:
sql SELECT COUNT(*) FROM schedule WHERE doctor_id = #{doctorId} AND schedule_date = #{scheduleDate} AND status != 'DELETED' AND ( (#{startTime} BETWEEN start_time AND end_time) OR (#{endTime} BETWEEN start_time AND end_time) OR (start_time BETWEEN #{startTime} AND #{endTime}) )
- 工作日限制:查询doctor表中的max_work_days_per_week字段,再统计该医生本周已排班天数(SELECT COUNT(DISTINCT DATE(schedule_date)) ...),若已达上限,则拒绝。
动态视图:前端Vue组件ScheduleCalendar.vue使用v-calendar库,以周/月视图展示排班。它不是简单渲染数据,而是根据status字段动态着色:绿色表示OPEN,灰色表示CLOSED,红色表示FULL,并支持点击时段进入详情编辑。这种“所见即所得”的交互,让学生直观理解:后端的业务规则,是如何驱动前端UI变化的。
3.3 电子病历模块:结构化与自由文本的平衡艺术
病历是医疗系统的核心产出物,也是最容易被做成“大文本框”的模块。我们的设计,是结构化字段保合规,自由文本留空间。
数据模型:medical_record表包含patient_id, doctor_id, visit_date, chief_complaint(主诉), present_illness(现病史), diagnosis(诊断), treatment(处置), create_time, update_time。前四个字段是必填的结构化字段,对应《电子病历系统功能应用水平分级评价标准》的基本要求。treatment字段存储JSON字符串,如{"drugList": [{"name":"阿莫西林","dose":"0.5g","freq":"tid"}], "advice":"多休息"},为未来扩展药品字典和医嘱模板埋下伏笔。
版本控制:每次医生保存病历时,系统不是UPDATE,而是INSERT一条新记录,并将旧记录的is_latest字段置为false。MedicalRecordService.saveRecord()方法内部,会先查出患者当天最新的病历(WHERE is_latest = true AND visit_date = CURDATE()),将其is_latest更新为false,再插入新记录并设is_latest=true。这样,患者的历史病历就形成了一个不可篡改的时间线,满足医疗审计要求。
前端渲染:Thymeleaf模板record-detail.html中,结构化字段用<p th:text="${record.diagnosis}"></p>直接输出,而treatment字段则用<div th:utext="${#strings.replace(record.treatment, '\n', '<br/>')}"></div>安全渲染,保留换行。Vue模式下,则用JSON.parse(record.treatment)解析JSON,再用v-for遍历drugList生成药品清单。这种“结构化数据驱动UI”的方式,让学生明白:好的数据设计,能让前端开发事半功倍。
3.4 药品管理模块:从入库到消耗的全生命周期追踪
药品管理是体现系统严谨性的试金石。我们的设计,遵循“入库-库存-处方-消耗”四步闭环。
入库(Inbound):DrugInboundService.inbound()接收DrugInboundDTO,包含drugId, batchNo, inboundDate, quantity, unitPrice。它会:
- 校验药品是否存在(drugMapper.selectById(dto.getDrugId()))。
- 插入drug_inbound记录。
- 更新库存:drugStockMapper.incrementQuantity(dto.getDrugId(), dto.getQuantity()),其SQL是UPDATE drug_stock SET quantity = quantity + #{quantity} WHERE drug_id = #{drugId}。
库存(Stock):drug_stock表是核心,有drug_id, quantity, min_stock_level(安全库存)。我们提供/api/drug/stock/alert接口,返回所有quantity < min_stock_level的药品列表,供管理员补货。
处方与消耗(Consumption):当医生开具处方并确认就诊时,RegistrationService.confirmVisit()会触发:
- 查询该挂号关联的药品处方(如果有)。
- 对每种药品,执行drugStockMapper.decrementQuantity(drugId, quantity),SQL同挂号扣减号源,带AND quantity >= #{quantity}条件,确保库存不足时不扣减。
- 记录drug_consumption流水,包含registration_id, drug_id, quantity, consume_date。
注意事项:药品库存扣减必须与挂号确认(
confirmVisit)放在同一个数据库事务里。否则,可能出现“挂号确认成功,但药品没扣减”的数据不一致。我们在RegistrationService.confirmVisit()方法上加了@Transactional,并将库存扣减逻辑作为该方法的一部分,而非异步消息,确保强一致性。
4. 从零部署到本地运行:手把手带你走通最后一公里
4.1 环境准备:三步搞定,比装微信还简单
第一步:装好Java和MySQL
- JDK:推荐JDK 8u291或JDK 11(项目pom.xml中<java.version>11</java.version>已声明)。验证:命令行输入java -version,看到11.0.x即可。
- MySQL:推荐MySQL 5.7或8.0。验证:mysql -u root -p能登录。创建数据库:CREATE DATABASE outpatient DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
第二步:导入项目到IDEA
- 打开IDEA,选择Open,找到你解压后的根目录(如3guvv9CK1iGr4rWVN1v8-master-212cced2b75df44c74f8d9acdb83e46ed90d1614)。
- IDEA会自动识别为Maven项目,等待它下载完所有依赖(spring-boot-starter-web, mybatis-spring-boot-starter, mysql-connector-java等)。如果卡住,检查settings.xml中的镜像源是否为阿里云。
第三步:配置数据库连接
- 找到src/main/resources/application.yml。
- 修改spring.datasource.url:将localhost:3306/outpatient中的outpatient改为你的数据库名。
- 修改spring.datasource.username和password为你MySQL的用户名密码。
- (可选)修改frontend.mode:thymeleaf(默认)或vue。
提示:如果你用的是MySQL 8.0,
url末尾要加上?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true,否则会报时区错误。这个细节,是我帮第7个学生解决连接失败时总结出来的。
4.2 数据库初始化:一条命令,建库建表插数据
资源包里的doc/outpatient_init.sql是你的“生命线”。它不是一个简单的CREATE TABLE集合,而是包含了:
- 创建所有表(patient, doctor, schedule, registration, drug, drug_stock等)及其外键约束。
- 插入基础数据:INSERT INTO department (name, code) VALUES ('内科', 'NK'), ('外科', 'WK');,INSERT INTO doctor (name, dept_id, title) VALUES ('张医生', 1, '主任医师');。
- 初始化测试号源:为张医生在2024-06-15创建了上午8:00-12:00、下午14:00-18:00两个时段,各10个号源。
执行方式(任选其一):
- MySQL Workbench:打开outpatient_init.sql,点击闪电图标执行。
- 命令行:mysql -u root -p outpatient < outpatient_init.sql
- IDEA Database工具:右键数据库连接 -> Run SQL Script...,选择该SQL文件。
执行成功后,用SELECT COUNT(*) FROM patient;应该返回0(初始无患者),SELECT COUNT(*) FROM schedule;应该返回2(张医生两个时段)。这证明数据库已就绪。
4.3 启动与验证:看到登录页,你就成功了一半
启动后端:在IDEA中,右键OutpatientApplication.java -> Run 'OutpatientApplication'。观察控制台输出,直到看到:
Tomcat started on port(s): 8080 (http) with context path ''
Started OutpatientApplication in 5.234 seconds (JVM running for 6.123)
这表示SpringBoot应用已在http://localhost:8080启动。
验证接口(可选):打开浏览器或Postman,访问http://localhost:8080/api/department/list,应该返回一个JSON数组,包含[{"id":1,"name":"内科","code":"NK"},{"id":2,"name":"外科","code":"WK"}]。这证明后端API已通。
访问前端:
- 如果frontend.mode: thymeleaf:直接访问http://localhost:8080/login,看到一个简洁的登录页(账号:admin,密码:123456)。
- 如果frontend.mode: vue:先在frontend-vue目录下执行npm install && npm run serve,然后访问http://localhost:8080(Vue Dev Server默认端口是8080,与后端冲突,需在vue.config.js中修改为8081,然后访问http://localhost:8081)。
实操心得:第一次启动慢是正常的,因为SpringBoot要加载所有Bean。后续修改代码后,用IDEA的
Reload project或Ctrl+F9编译,再点Restart按钮,热部署几秒就能生效。千万别每次改个HTML都Ctrl+F5刷新——那是前端的活,后端的热部署才是你的效率杠杆。
5. 常见问题排查与二次开发指南:那些没人告诉你的坑和捷径
5.1 部署常见问题速查表
| 问题现象 | 可能原因 | 排查与解决步骤 |
|---|---|---|
启动报错:Access denied for user 'root'@'localhost' | 数据库用户名密码错误,或MySQL 8.0默认认证插件变更 | 1. 检查application.yml中username/password是否正确;2. MySQL 8.0执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES; |
访问/login显示404 | Thymeleaf未正确加载,或resources/templates/路径不对 | 1. 确认pom.xml中spring-boot-starter-thymeleaf依赖存在;2. 检查templates目录是否在src/main/resources/下(不是src/main/java/);3. 清理IDEA缓存:File -> Invalidate Caches and Restart |
挂号时提示“号源已满”,但数据库里total_num是10 | 事务隔离级别问题,或availableNum计算SQL有误 | 1. 查看ScheduleMapper.xml中selectAvailableSchedules的SQL,确认LEFT JOIN子查询逻辑;2. 在RegistrationService.register()方法开头加log.info("Available num: {}", availableNum);,看日志输出是否为0;3. 检查是否有其他测试数据占用了号源 |
| Vue前端无法调用API,报CORS错误 | 后端跨域配置未生效,或Vue请求地址写错 | 1. 确认RegistrationController类上有@CrossOrigin(origins = "http://localhost:8081");2. Vue中API地址应为http://localhost:8080/api/xxx,不是/api/xxx(相对路径);3. 检查浏览器控制台Network标签页,看请求URL是否正确 |
5.2 二次开发实战:三个最常被问到的扩展需求
需求一:增加短信验证码登录
这是毕设加分项。你需要:
- 在entity包下新增SmsCode.java,字段:phone, code, expireTime, used。
- 在mapper包下新增SmsCodeMapper.java和SmsCodeMapper.xml,实现insert, selectByPhoneAndCode, updateUsed。
- 在service包下新增SmsCodeService.java,实现sendCode(String phone)(生成6位随机数,存入DB,模拟发送)、verifyCode(String phone, String code)(校验时效性和正确性)。
- 修改LoginController.login(),增加/api/login/sms接口,先调用smsCodeService.verifyCode(),再调用原登录逻辑。
需求二:导出挂号记录为Excel
用Apache POI实现:
- pom.xml添加依赖:<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.4</version></dependency>。
- RegistrationService.exportToExcel()方法:查询registrationMapper.selectForExport(startDate, endDate),用XSSFWorkbook创建工作簿,XSSFSheet创建Sheet,循环写入数据,最后用response.getOutputStream()写出。
- Controller中@GetMapping("/export"),设置response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")和Content-Disposition头。
需求三:接入微信公众号菜单
这是企业级扩展。你需要:
- 在application.yml中增加微信配置:wechat.appId, wechat.appSecret, wechat.token。
- 新增WechatMenuService.java,调用微信menu/create接口(需先获取access_token),构造JSON菜单数据。
- 在StartupRunner中,应用启动时自动调用wechatMenuService.initMenu(),确保菜单始终最新。
最后分享一个小技巧:所有业务代码,都遵循“先写单元测试,再写实现”。比如
RegistrationServiceTest里,用@MockBean模拟ScheduleMapper,断言当availableNum=0时,register()方法抛出BusinessException。这能让你在改代码时,心里有底。我见过太多学生,为了加一个字段,把整个挂号流程改崩了,就是因为没有回归测试。写测试,不是浪费时间,是给你自己买的一份保险。
这个门诊管理系统,它不完美,没有AI辅助诊断,没有大数据分析,但它足够真实,足够扎实,足够让你在答辩时,面对“你们怎么保证挂号不超卖?”这个问题,能自信地打开RegistrationService.java,指着那一行@Transactional和UPDATE ... AND total_num > 0说:“老师,您看这里。” 这,就是技术人的底气。
简介:直接可用的Java医院门诊管理项目,后端基于SpringBoot 2.x,集成MyBatis与MySQL,前端支持Thymeleaf或Vue两种模式(具体依实际代码结构而定)。功能覆盖患者挂号、医生排班查询、门诊登记、电子病历查看、药品信息管理等核心医疗业务场景。项目采用标准Maven结构,包含完整的src/main/java和src/main/resources目录,已通过基础功能验证,导入IDE后修改application.yml中的数据库连接配置即可本地运行。资源包内含建库建表SQL脚本(通常位于resources或doc目录)、清晰的配置说明文档及简易部署指南,开箱即用。适合计算机、软件工程等专业本科生开展毕业设计、课程设计或实训开发,也适合作为SpringBoot+MyBatis实战入门参考案例,支持按需扩展模块、替换前端框架或对接真实HIS系统接口。
&spm=1001.2101.3001.5002&articleId=162184443&d=1&t=3&u=2074e5b3fbb24e4b98b4d804d98f2d4a)
1505

被折叠的 条评论
为什么被折叠?



