前阵子帮一个学生调试基于Spring Boot的社区诊所在线挂号与排队系统,标题一眼看过去就是典型的Java毕设选题:前端Vue加后端Spring Boot,配一套MySQL数据库脚本,再把排队叫号的业务逻辑跑通。这种项目在毕设里出现的频率非常高,但真正能把它从“会跑”做到“跑得明白”的人并不多。这周刚好把这套系统从头到尾捋了一遍,把源码结构、业务设计、调试过程、还有踩过的坑一并整理出来,给准备拿这个题目做毕设,或者想自己动手做一套挂号排队系统的同学做个参考。
这个系统的核心价值其实很聚焦:解决社区诊所线下排队混乱、医患信息不透明的问题。用户通过在线挂号和排队取号,能实时看到前面还有几个人、预计等待多久,诊所这边也能通过管理后台维护医生排班、科室信息和叫号状态。整条链路不复杂,但涉及的关键点很典型:权限角色划分、号源分配、队列状态机、定时任务刷新等待人数,这些都是面试和答辩时容易被追问的地方。
1. 项目整体拆解与业务设计思路
1.1 为什么“社区诊所”是一个最合适的毕设切入点
很多同学选题时容易走两个极端:要么选一个宏大平台,比如“智慧医疗云平台”“区域远程会诊系统”,一听就很虚,开发半个月连页面都拼不齐;要么选一个纯工具型管理系统,比如“诊所药品进销存”,虽然能做完,但技术点太薄,答辩时没什么可讲的。
社区诊所在线挂号与排队系统正好卡在中间:业务规模小,但流程完整。它既有患者端的预约、挂号、排队,又有医生端的接诊、叫号,还有管理端的排班、统计。角色一多,权限设计、状态流转、数据关联自然就出来了,技术难点它不是凭空增加的,而是被业务场景本身逼出来的。
从表结构设计上看,这套系统常用的核心表大概是下面这些:
user(用户表,区分患者、医生、管理员三种角色)department(科室表)doctor(医生表,包含所属科室、职称、排班信息)schedule(排班表,记录某个医生在某个时间段是否出诊)registration(挂号记录表,记录患者挂了哪个医生的号)queue(排队记录表,记录取号顺序和当前叫号状态)medical_record(就诊记录表,记录医生写的诊断和处方信息)
这几张表之间是典型的主外键关联关系,设计时要注意的细节是:挂号记录和排队记录其实可以合并,也可以拆开。我建议拆开,因为“挂号”表达的是预约行为和费用状态,“排队”表达的是到诊后的顺序流转,两者在业务流程上是有时间差的。把状态混在一张表里,后面做“过号重排”“退号释放号源”的时候就会非常别扭。
1.2 业务模块划分与角色权限的边界
整个系统的业务模块可以按角色拆成三条线:
第一条线是患者。患者登录后看到科室列表,选择科室后可以看到该科室下所有医生的排班信息,选择某个出诊时间段后提交挂号请求,系统生成挂号记录并分配一个排队号。患者可以在“我的挂号”里看到当前排队位置,如果前面只剩一两个号了,系统会在页面轮询刷新,提示患者去诊室门口等待。
第二条线是医生。医生登录后看到的是今天自己的出诊队列,队列根据取号时间排序,医生点“叫下一号”后,系统把当前队列的第一个患者状态改为“就诊中”,同时在大屏或患者端展示当前叫到的号码。看完一个患者后,医生录入简单诊断信息,点击“完成就诊”,患者状态变为“已完成”,并从当前队列移除。
第三条线是管理员。管理员负责维护科室信息、医生信息、排班计划,还可以查看某天的挂号统计和医生接诊量。统计这块建议用最简单的分组聚合查询来完成,不要引入独立报表框架,否则工作量会失控。
这里面有一个比较容易遗漏的点:挂号记录里的“号源”。如果排班表里没有做号源总数和剩余号源的字段,患者就可以无限挂同一个医生的号,系统就失去了合理分流的意义。设计时应该在排班表里加两个字段:total_count 和 left_count,每次挂号成功做一次 left_count - 1,退号或取消时再加回来,这样业务闭环才算完整。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心技术选型与关键配置细节
2.1 Spring Boot 版本与依赖组合的取舍
Spring Boot 版本这个问题,几乎所有做毕设的同学都要面对一次。我看到的很多项目代码都是用 Spring Boot 2.x 写的,但你如果现在去新建项目,Spring Initializr 默认生成的是 3.x 版本。3.x 带来的最大变化是 javax.* 包名换成了 jakarta.*,以及部分配置项调整,比如老项目里的 spring.factories 自动配置机制在 3.x 里有了变化。
如果你的毕设要求不苛刻,我建议直接用 Spring Boot 2.7.x 版本,配 JDK 1.8。原因很实在:网上能找到的参考代码、博客方案、报错解决方案绝大多数是基于这个组合的,遇到问题搜一下就有答案。而 Spring Boot 3.x 要求 JDK 17 起,对系统的硬件环境要求更高,部分旧版本依赖(比如某些老牌 MyBatis 插件)也没跟上适配,调试起来容易卡壳。
这套系统里常用的核心依赖组合建议如下:
| 依赖 | 版本建议 | 用途说明 |
|---|---|---|
| Spring Boot | 2.7.x | 基础框架 |
| MyBatis | 2.2.x(mybatis-spring-boot-starter) | 数据持久层 |
| MySQL 驱动 | 8.0.x | 数据库连接 |
| Lombok | 1.18.x | 简化实体代码 |
| Hutool | 5.8.x | 工具类集合(ID生成、日期处理) |
| Swagger / knife4j | 2.0.9 / 4.3.0 | 接口文档与调试 |
| Vue + Element UI | 2.x | 前端页面框架 |
其中 knife4j 这个组件非常好用,比原版 Swagger UI 简洁很多,启动后直接访问 doc.html 就能对每个接口做在线调试,对于毕设答辩时现场演示 API 功能很有帮助。
2.2 排队叫号的核心逻辑怎么落地
排队系统最核心的算法其实不复杂,复杂的是各种边界情况:过号、取消、退号、反复刷新排队状态。
我的建议是把排队状态设计成四个值:
0:待就诊(已取号,在队列中等待)1:就诊中(医生已叫号)2:已完成(看诊结束,离队)3:已过号(叫号三次未响应,标记为过号)
每一次叫号动作本质上就是一个“从待就诊队列里取出队头,并把状态改为就诊中”的操作。用 SQL 表达就是:
sql复制-- 查询指定医生当前队列中最早取号的患者
SELECT * FROM queue
WHERE doctor_id = #{doctorId}
AND status = 0
ORDER BY queue_no ASC
LIMIT 1;
拿到这条记录之后,再用 update 把它的 status 改为 1。这里有一个并发隐患:如果两个医生同时操作同一个队列,或者患者端刷新页面触发了重复请求,就会出现“同一时刻叫了同一个号”的问题。解决思路是在更新语句里加状态条件:
sql复制UPDATE queue
SET status = 1
WHERE id = #{id}
AND status = 0;
如果更新受影响的行数为 0,说明这个号已经被处理过,前端需要给出相应提示。这种乐观锁的思路在很多并发场景里都通用,写进项目里是加分项。
2.3 前后端交互与接口风格设计
系统接口建议遵循 RESTful 风格,统一返回一个 Result 包装类:包含 code(状态码)、message(提示信息)、data(数据体)三个字段。
json复制{
"code": 200,
"message": "操作成功",
"data": null
}
这个 Result 类通常写在 common 包里,每个 Controller 都返回它。这样前后端联调的时候,前端只需要统一处理 code 字段,不用照顾各种奇奇怪怪的返回格式。
接口按业务划分大致如下:
/api/auth/login:登录接口(区分角色)/api/department/list:科室列表/api/doctor/listByDept:按科室查医生/api/schedule/list:查询某医生的排班/api/registration/create:提交挂号/api/queue/status:查询当前排队位置和前面人数/api/queue/next:医生端叫下一号/api/medical/record/save:保存诊断记录
接口数量控制在 10 到 15 个之间比较合适,太少显得工作量不足,太多容易失控。每个接口对应一个 Service 方法,Service 里做核心业务判断,Controller 层保持薄的状态,这种分层结构在答辩时很容易解释清楚。
3. 从源码到跑通:完整调试运行过程
3.1 环境准备与项目初始化
拿到一套源码之后,第一步不是急着启动,而是先梳理环境依赖。这套系统需要准备的东西有:
- JDK 1.8(推荐使用 1.8.0_202 或更高版本)
- Maven 3.6.x 以上
- MySQL 5.7 或 8.0
- Node.js(前端构建用,建议 14 到 16 版本,版本太新容易出问题)
- IDEA 2021 版以上
把后端工程用 IDEA 打开后,Maven 会自动开始下载依赖。这里有一个很容易踩的坑:IDEA 默认的 Maven 仓库地址在国内访问非常慢,甚至会出现依赖下载到一半就失败的情况。建议在 settings.xml 里配置阿里云镜像,这个问题能省掉一大半的等待时间。
Maven 配置镜像的代码片段如下:
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
配置好之后,在 IDEA 的 Maven 面板里点一下 Reload All Maven Projects,耐心等依赖下完。如果依赖报红,多半是网络问题和版本冲突,优先检查右下角的报错提示,不要盲目删 pom.xml。
3.2 数据库初始化与数据脚本修正
后端工程里一般会附带一个 sql 目录,里面放 init.sql 或者 schema.sql。用 Navicat 或命令行执行脚本前,建议先扫一眼建表语句,确认这几类信息:
- 字符集是否为
utf8mb4(如果包含用户昵称等文本,utf8mb4才能存下特殊字符) - 主键策略是否为自增(有的脚本会写成手动插入,后续插入数据会麻烦)
- 外键约束是否严格(建议保持外键约束开启,否则数据一致性无法保证)
执行完建表脚本之后,还需要确认是否有种子数据。很多毕设项目只给了表结构,但没有任何初始数据,导致启动后登录页面都打不开。遇到这种情况,自己动手往 dept、doctor、user 表里插入几条测试数据就好:
sql复制-- 插入管理员账号
INSERT INTO user (username, password, role, name, phone)
VALUES ('admin', '123456', 'admin', '系统管理员', '13800000000');
-- 插入测试科室和医生
INSERT INTO department (dept_name) VALUES ('内科'), ('外科'), ('儿科');
INSERT INTO doctor (name, dept_id, title)
VALUES ('王医生', 1, '主治医师'), ('李医生', 2, '副主任医师');
测试密码建议用明文的简化处理方式,毕设项目通常不会做加密处理的强制要求,但如果你想在答辩时加分,把密码改成 MD5 加密存库,成本很低,效果却很好。
3.3 后端启动与前端联调的关键配置
前后端分离的架构里,最容易出问题的就是静态资源和跨域配置。这套项目如果用 Vue 写前端,开发模式下前端是独立运行的,默认占用 8080 端口,后端 Spring Boot 占用 8081 或 8082 端口,两者端口不同,必然产生跨域问题。
解决跨域有两种常见方式:
第一种是后端加全局跨域配置:
java复制@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:8080");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
第二种是开发环境下用前端代理转发,在 vue.config.js 中配置:
js复制devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081',
changeOrigin: true
}
}
}
我个人的建议是两种都配好:开发时用代理,部署演示时用后端跨域配置兜底。很多同学在这上面花了很长时间排查,最后发现只是前端请求路径和后端接口路径对不上,或者代理配置没生效。
3.4 核心业务功能的调试路径
系统启动后,建议按下面这条路径逐项验证,每验证一步就在笔记里记录状态,后面出问题才能快速定位:
- 用户登录:测试用管理员、医生、患者三种角色分别登录,确认权限拦截生效。
- 科室查询:前端打开首页,科室列表能正常从数据库加载。
- 排班查看:选择科室和医生,能看到对应日期的排班数据。
- 挂号提交:选择一个未满号源提交挂号,数据库
registration表新增记录,同时对应排班的剩余号源减一。 - 排队取号:挂号成功后,系统自动生成一个排队号,返回给前端。
- 医生叫号:切换到医生账号,看到当前队列,点击叫号后患者端状态变化。
- 完成就诊:医生录入诊断内容,点击完成,患者端状态变为已完成。
我当时调试这套系统时,最常用的工具是 knife4j 的在线接口测试。把每个接口按顺序调一遍,相当于把整套业务链路在浏览器里完整跑了一次。前端页面如果有报错,直接按 F12 看 Network 面板的请求状态码,200 说明接口通,4xx 多半是参数问题,5xx 才是后端逻辑问题。
4. 常见问题与排查技巧实录
4.1 Spring Boot 版本太高导致启动直接失败
这是我从多个学生项目里看到频率最高的问题。表现是:项目用自己的电脑新建时 Spring Boot 选了 3.2.x,然后引入了一个基于 javax.servlet 的老依赖,一启动就报 ClassNotFoundException: javax.servlet.Filter 或类似错误。
原因很直接:Spring Boot 3.x 全面切换到 jakarta.* 命名空间,老代码里所有 import javax.servlet 的地方都需要改成 import jakarta.servlet。一下子没法改完的话,最稳妥的方式是回到 Spring Boot 2.7.x 加 JDK 1.8 的组合。
如果你确实想用 3.x,那要检查的依赖包括:mybatis-spring-boot-starter 是否用了支持 jakarta 的版本、knife4j 是否升级到支持 Spring Boot 3 的版本、pagehelper 是否更换为新版。这些排查比较琐碎,答辩前没把握就别折腾,用成熟组合最安全。
4.2 端口被占用与配置修改
启动 Spring Boot 项目时如果报 Port 8080 was already in use,说明本机的 8080 端口被其他程序占用了。最快解决方式是换一个端口,在 application.yml 里改:
yaml复制server:
port: 8081
如果只想临时测试一下,也可以在启动命令里指定:
bash复制java -jar community-clinic.jar --server.port=8082
这里要同步检查前端配置里的请求地址是否也跟着改了。很多同学后端换了端口,前端 baseURL 里还写着 8080,接口自然就调不通。
4.3 数据库连接失败与账号权限问题
启动时报数据库连不上,常见的原因有三类:
第一类是密码错误。检查 application.yml 配置的 spring.datasource.password 是否和本地 MySQL 的密码一致。建议把数据库账号密码统一用 root/123456 这种简单组合,方便联调。
第二类是时区问题。MySQL 8.x 默认时区设置可能导致连接报错,在 JDBC URL 后面加上参数即可:
yaml复制url: jdbc:mysql://localhost:3306/clinic?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
第三类是驱动版本问题。MySQL 8 需要 com.mysql.cj.jdbc.Driver,有些老代码写的是 com.mysql.jdbc.Driver,这个是 5.x 用的旧驱动名,必须改过来,否则启动阶段就报获取连接失败。
4.4 二次开发与定制扩展的几个建议
如果标题里说的“定制”指的是拿到代码后需要加功能或者改业务流程,我建议从这几种最常见的定制方向入手:
- 增加短信或微信公众号通知:患者挂号成功后,通过接入第三方短信 API 或公众号模板消息发送排队通知。这个加在
RegistrationServiceImpl里即可,不影响原有业务逻辑。 - 引入 Redis 缓存排队号:如果并发量大,排队号用
Redis INCR生成比查数据库max(queue_no)更实时、更可靠。这个改造需要新增 Redis 配置,但业务逻辑改动不大。 - 增加系统管理员的首页统计图表:用 ECharts 展示每日挂号量趋势图、各科室接诊量饼图。前端引入 ECharts 组件,后端加一个聚合查询接口,开发量在两到三天左右。
做定制扩展时,核心原则是“不侵入已有稳定代码”。新增业务逻辑尽量以新增 Service 方法或新增 Controller 接口的方式实现,不要去改已经调通的底层 Mapper 方法,否则很容易把原本完好的功能搞坏。
5. 写在最后的一些体会
调试这套系统时我最大的感受是:毕设项目的难点真的不在技术上,而是在“把逻辑理顺”这件事上。挂号、排队、叫号这三个动作,看起来只是状态和记录的流转,但每一步都有对应的数据变动和边界情况。谁先把这张状态流转图在脑子里画清楚,谁写出来的代码就清晰。
对于准备拿这个项目做毕设的同学,我想再说几句实在话。前端不必做得太花哨,但核心页面要齐全;后端接口不用追求微服务那种复杂度,但分层要清楚,注释要到位;文档里要把业务流程图和数据模型说透,这些比多写十行炫技代码更有用。答辩时老师通常不会刁难你“用了什么高深框架”,而是会问“为什么这样设计”“并发情况下怎么处理”,这些内容提前准备好,答复起来自然从容。
最后分享一个小技巧:项目交出去之前,用一天时间把整个项目从零到一重新搭一遍。删掉本地数据库重新执行脚本,清掉 target 目录重新打包,换一台电脑或用一个干净虚拟机跑一次。这个过程能暴露出你平时完全注意不到的问题,也能让你对项目的每一个配置都心里有数。出问题不可怕,答辩前发现问题比答辩时被问住好一百倍。
