Spring Boot社区诊所挂号排队系统:数据库设计、状态机与并发控制

发布时间:2026/10/1 18:27:47
Spring Boot社区诊所挂号排队系统:数据库设计、状态机与并发控制 挂号就诊管理系统在毕业设计里算是一个“万年青”选题——它不冷门、不花哨但业务链条完整涉及到用户角色、预约时序、排队状态流转、医生排班、病历记录和统计报表。走完一个这样的项目springboot后端、数据库设计、前后端联调、事务和并发的几个坑基本都能踩一遍。这篇文章从项目拆解、数据库设计、核心流程实现、到部署演示和答辩包装完整走一遍基于springboot的社区诊所挂号排队系统适合正在选毕设题目、或者已经把程序跑起来但要写文档、准备答辩的读者。1. 项目整体设计与技术选型1.1 为什么“社区诊所”是一个恰到好处的业务场景很多人第一反应是“挂号就诊系统医院不是有现成的吗”但实际上去做一个三甲医院级别的挂号系统光医院分科、医生排班、医保对接、检查检验流程就能把你拖进无底洞。毕设的核心诉求是在有限周期内展示一个完整的业务闭环同时又要有现实意义。社区诊所刚好卡在合适的复杂度科室有限内科、外科、儿科、口腔等、医生数量可控、没有复杂的住院流程但预约、排队、过号、统计这些核心痛点一个不少。社区诊所的真实痛点还挺接地气的患者到店后在前台排队人多的时候全靠喊医生不知道下一个患者是谁管理者盯不住高峰时段的就诊量。所以这套系统把线上预约、线下取号排队、叫号通知、就诊记录串起来既有线上的预约能力又有线下排队场景的现场感。答辩的时候老师问“这个项目解决了什么问题”可以从这里切入。1.2 技术栈选择的三个关键考量这一套系统我给出的推荐技术栈如下后端Spring Boot 2.7.x MyBatis-Plus Spring MVC前端Thymeleaf 服务端渲染为主搭配 Bootstrap 做管理后台界面数据库MySQL 8.0权限Spring Security 或 拦截器 会话管理构建工具Maven部署演示内嵌 Tomcat打包成 jar 直接运行Spring Boot 在这个项目里的优势在于自动配置减少了大量 XML 配置内嵌 Tomcat 让部署变成“双击 jar 包”Spring Data 生态让数据库操作门槛降低。MyBatis-Plus 提供了分页插件、条件构造器写查询非常省时间特别适合毕设这种开发周期紧的场景。前端用 Thymeleaf 而不是前后端分离是有意的取舍。前后端分离比如 Spring Boot Vue看起来技术上更“新”但会引入跨域问题、双项目部署、联调成本。毕设评审更看重业务完整性服务端渲染一套代码跑起来省掉的调试时间可以全部投入到业务逻辑和文档编写上。1.3 三种角色与功能模块拆解整个系统的用户侧分为三类角色权限边界清晰分明角色核心权限典型操作管理员基础数据维护、排班管理管理科室、医生账号、排班规则、查看统计报表医生看诊、病历录入查看今日排队列表、呼叫下一位患者、填写诊断信息患者预约、查询在线挂号、取消预约、查看排队实时状态功能模块上至少有这几块系统管理管理员登录、医生账号管理、科室管理排班管理按星期和时段生成医生出诊计划排班决定号源数量在线预约患者选择科室→选择医生→选择时间槽→确认挂号排队叫号预约患者到院后取号或者自动入队医生端叫号屏幕上同步显示病历管理医生基于挂号和排队记录填写病历患者可查看历史就诊记录统计报表按医生、科室统计问诊量按时间段统计预约人数模块之间不是独立的而是主流程串行排班表产生号源 → 预约占用号源 → 挂号记录生成排队条目 → 叫号更新状态 → 病历伴随整个就诊过程。这也是答辩时可以画业务流转图的核心逻辑。2. 核心功能实现拆解从预约到叫号的全链路2.1 在线挂号号源模型与时间槽设计在线挂号模块里最容易犯的错误是把“预约”简单理解为“插入一条记录”。实际运营中号源是有限的一个医生上午最多看 20 个号每个时间段满了就必须提示患者选择其他时段。我的号源模型设计思路如下排班表schedule按医生星期时段生成比如周一上午、周二下午号源表schedule_slot在排班基础上把时间切成时间槽比如上午分成 9:00、9:30、10:00……每槽一个号源预约表appointment记录患者 ID、排班 ID、时间槽 ID、状态患者在预约界面做三连操作选科室 → 选医生和日期 → 选时间槽。后端接口接收后执行两步逻辑检查时间槽是否已被占用如果未被占用则创建预约记录并把槽位标记为不可用。这里有个全局性技巧时间槽的状态不能只靠查询判断必须在事务里完成“查询 更新”的原子操作。SQL 层面可以使用类似UPDATE slot SET status 1 WHERE id ? AND status 0通过受影响行数判断是否抢锁成功。如果先把号取出来了再“检查后插入”并发场景下大概率会出超卖问题。毕设演示可能测不出并发但文档里这么写评委印象分会高很多。2.2 排队系统的状态机设计排队叫号是这个项目最有特色的部分也是区别于普通 CRUD 系统的亮点。它本质上是一个状态机待就诊WAITING → 已叫号CALLED → 就诊中IN_CONSULT → 已完成FINISHED ↓ 超时未到PASSED状态机设计清晰之后代码就变成了状态迁移的控制器。每次叫号操作只允许从 WAITING 和 CALLED 状态进入下一步非法迁移直接返回错误提示。具体到排队列表的展示逻辑医生端首页加载“今天的全部待就诊患者”按预约时间排序点击“呼叫下一位”系统自动把最早到诊的 WAITING 状态改为 CALLED返回当前叫号号码诊室门口的电视大屏前端页面通过轮询接口获取当前 CALLED 和 IN_CONSULT 状态的号码实现叫号展示需要特别注意的是“过号处理”。现实中患者可能离开诊室叫号没人应答。我的处理方案是当医生点击呼叫后设置一个 5 分钟倒计时超时未确认则状态自动变为 PASSED并且允许患者重新签到从 PASSED 恢复到 WAITING 队尾。这个小逻辑成本很低但它体现出了系统的真实可用性论文里也能多写一段。2.3 医生排班用模板生成号源排班功能很多人会做成“管理员手动添加每天的出诊记录”但这样维护成本太高。合理的方案是排班模板 自动生成管理员为医生配置一周七天的出诊时段比如张三周一上午、周三全天系统根据模板自动为指定日期范围生成排班记录每一条排班记录再根据预设的时段粒度比如每 30 分钟一个号自动生成时间槽这样做的好处是预约模块的查询只需要关注时间槽表不需要每条预约都去解析排班规则。生成号源的过程可以写成一个 Service 层方法在管理员点击“生成排班”时调用也可以在启动时自动校验并补齐。我实操中的一个心得排班表里的星期建议用 1-7 的整数不要存“周一”这种中文文本。转换逻辑放在前端展示层否则你写“查询今天有哪个医生出诊”的时候要处理编码堆叠问题白白浪费时间。2.4 数据可视化让管理员一眼看懂运营情况统计报表模块在毕设里属于“锦上添花但最好别缺”的部分。大多数毕设管理系统只有增删改查加一个图表统计立刻显得完整不少。我用 ECharts 的柱状图和折线图做了两块统计门诊量统计按科室、按医生统计近 30 天问诊人次时段分布统计每周一到周日各时段的预约量辅助诊所决策哪天需要增加医生后端对应的接口写 SQL 聚合即可例如SELECT doctor_id, COUNT(*) AS cnt FROM appointment WHERE create_time BETWEEN ? AND ? GROUP BY doctor_id ORDER BY cnt DESC前端拿到 JSON 数据后填充到 ECharts 里。这部分不复杂但答辩时“统计分析辅助诊所管理决策”这个点非常加分。3. 数据库设计与核心流程落地3.1 核心表结构一览数据库设计我建议至少包含以下 7 张表它们覆盖了从基础数据到业务数据的全部链路表名用途关键字段user用户表患者/医生/管理员id, username, password, role, real_namedepartment科室表id, name, descriptiondoctor医生信息表id, user_id, department_id, title, introductionschedule排班表id, doctor_id, weekday, period, date_rangeschedule_slot号源时间槽表id, schedule_id, slot_time, statusappointment预约记录表id, patient_id, slot_id, status, create_timequeue_record排队记录表id, appointment_id, queue_no, status, called_timemedical_record病历记录表id, appointment_id, diagnosis, prescription, create_time用户表这里要注意医生和患者我统一放在 user 表里通过 role 字段区分而不是单独建两张表。原因有两点登录认证只查一张表更简单毕设要展示的是业务系统设计能力单表角色设计完全可以讲清楚。当然如果导师偏好强调数据库范式也可以拆成两张表但代码复杂度会上升需要自己权衡。3.2 工作流一次完整就诊串起五张表把整个流程从用户视角过一遍患者注册登录后选择科室、医生、日期系统展示该医生当天的剩余号源时间槽。患者点击某个时间槽预约写入 appointment 表同时把 schedule_slot 表对应记录的 status 更新为 1已占用。患者到社区诊所后在前台或自助机上确认签到。系统根据 appointment 记录生成 queue_record分配今天的排队序号。医生点击“呼叫下一位”queue_record 的状态从 WAITING 变为 CALLED诊室外面板展示当前号码和患者姓名。患者进入诊室医生点击“开始就诊”状态变为 IN_CONSULT然后填写 medical_record就诊结束后状态变为 FINISHED。这个流程下来业务数据完整落在 5 张表里没有任何信息断裂。写论文时这个“一次完整业务流转 状态变化”的时序说明就是系统核心价值的最好证明。3.3 事务边界与并发请求处理事务在这个系统的几个关键点位必须用上预约挂号检查槽位状态 写入预约表 更新槽位三步必须在一个事务里取消预约删除预约记录或改为取消状态 释放槽位生成排班插入多条 schedule 和 schedule_slot要么全部成功要么全部回滚Spring 里给 Service 方法加Transactional是最直接的方案。但要切记事务开在 Service 层不是 Controller 层而且事务方法内部尽量只做业务操作不要混入外部接口调用否则长事务会锁住数据库连接。关于并发毕设阶段不用引入 Redis 分布式锁MyBatis-Plus 的乐观锁插件Version字段加上 SQL 条件更新足够。把并发控制的方案写清楚答辩被问到“多用户同时挂号会不会出问题”时就有底气了。3.4 缓存用的时机和技巧Spring Boot 整合 Redis 在热词里出现频率很高但这个项目不是所有模块都适合加缓存。我的建议是科室列表、医生列表这种低频变更数据可以用Cacheable缓存今日排队列表这种实时数据不要缓存或者只做 3-5 秒的短缓存避免医生和患者看到不一致的数据验证码、短信验证码如果有可以放 Redis 并设置过期时间如果时间紧不引入 Redis 也可以正常完成项目缓存部分但引入 Redis 能体现技术栈深度。条件允许的话用一个简单示例实现“科室列表缓存 手动更新”就够了。4. 从开发到答辩的完整落地流程4.1 30 分钟跑通本地环境很多同学拿到代码第一步就卡在环境配置上。这套项目的环境准备三步就能完成JDK 1.8 及以上版本推荐用 JDK 8 或 JDK 17避免版本太高时 Spring Boot 2.x 出现兼容问题安装 MySQL 8.0执行项目里自带的init.sql初始化数据库脚本用 IDEA 打开 Maven 项目等待依赖下载修改application.yml里的数据库用户名密码启动启动类启动成功后端口默认是 8080浏览器访问http://localhost:8080/就能跳转登录页。如果端口被占用在application.yml里临时换一个就好。4.2 演示数据的重要性毕设演示最怕现场数据是空的。我的建议是初始化脚本里就造一批演示数据3 个科室每个科室 2-3 位医生给每个医生生成未来 7 天的排班和号源造几个患者账号以及过去两周的历史预约、病历记录、排队记录最好让统计数据看起来有起伏别是均匀的“每条记录都相同”演示数据的质量直接决定答辩时的视觉效果。空荡荡的管理后台和满屏有业务含义的数据给人的专业感完全不同。4.3 答辩讲稿的包装思路答辩不是念代码而是讲“你发现了一个什么问题 → 你的方案是什么 → 你怎么验证方案有效”。这个项目推荐的讲稿脉络开场社区诊所挂号排队效率低、人工管理混乱我们做了在线挂号与排队系统设计三大角色、四个核心模块、完整业务闭环技术亮点事务保证号源不超卖、状态机管理排队流程、排班模板自动生成号源、可视化辅助运营决策总结个人的难点和解决过程比如调试排队状态机的边界情况、并发挂号问题怎么解决技术亮点在答辩时的优先级排序并发事务处理 状态机设计 可视化报表 技术栈宽度Redis、JWT 等扩展。4.4 定制化扩展的可行方向定制化是这个项目很看重的一点因为直接复制系统的人太多有点自己的东西答辩时讲起来更加分。可以考虑这些方向微信小程序端把患者端预约和排队查询做成小程序展示前后端分离能力消息通知接入阿里云短信或邮件服务预约成功和即将叫号时通知患者电子病历增强加入病历附件上传、检查报告图片上传顺带引入 MinIO 文件存储服务多诊所模式增加诊所维度一套系统服务多个诊所管理员按诊所隔离数据扩展方向的本质是在主流程上加“增值服务”每加一层都是一句话的亮点。5. 开发过程常见问题与排查经验5.1 前端资源加载不了的经典场景使用 Thymeleaf 时最常犯的错误是静态资源路径写错。Spring Boot 的静态资源默认放在src/main/resources/static/下模板放在templates/下。如果页面能打开但是 CSS、JS 加载不出来按这个顺序排查检查控制台请求路径是否为/css/style.css检查 Thymeleaf 引入时用的是不是th:href普通href无法处理上下文路径模板页面修改后不生效检查 application.yml 是否开启了spring.thymeleaf.cachefalse开发阶段可关闭模板缓存5.2 LocalDateTime 序列化问题Spring Boot 默认使用 Jackson 序列化时间类型LocalDateTime 可能会出现类似 “Invalid GMT format” 或者 JSON 里时间变成数组的报错。最省事的方案是在 application.yml 全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果前端表格展示时间出现 8 小时偏差基本都是时区没设置。确认数据库连接的 url 也加上serverTimezoneAsia/Shanghai前后端时区统一问题一次性解决。5.3 预约超卖的并发陷阱演示时不会遇到但压测或者老师追问时会暴露。超卖的本质是两个请求同时查到槽位状态为 0然后同时执行插入。解决办法在最前面提过使用条件更新保证原子性。代码层面可以这样写boolean success slotService.updateSlotStatus(slotId, 0, 1); if (!success) { throw new BusinessException(该时间段已被预约请重新选择); } // 继续创建预约记录这里updateSlotStatus的 SQL 是UPDATE schedule_slot SET status 1 WHERE id ? AND status 0通过影响行数来判断是否成功。只要这一步先执行成功再插预约表就不会出现双方都以为抢到号的情况。5.4 过号逻辑的边界测试排队状态机看着简单但边界条件很考验人。我测试时发现几个问题医生叫号时没有患者处于待就诊状态接口会报空指针同一患者重复签到生成了两条排队记录患者取消预约后排队记录里还停留着这条数据解决方案所有状态迁移操作前校验当前状态是否可迁移取消预约时同步把排队记录标记为已取消列表查询默认过滤掉已取消状态。给状态字段加索引查询性能也能好不少。5.5 构建部署阶段的环境坑构建打包最常遇到的三个问题Maven 依赖下载慢配置 Maven 镜像源推荐阿里云公共仓库JDK 版本不匹配Spring Boot 2.7 搭配 JDK 8 或 JDK 17 都行但 Spring Boot 3.x 必须配 JDK 17打包后运行报No main manifest attribute检查是否引入了 spring-boot-maven-plugin 插件部署阶段做一个小优化把配置文件放在 jar 包同级目录通过--spring.config.additional-location指定外部配置。这样改数据库地址或端口不用重新打包。5.6 常见问题速查表现象可能原因解决方式启动报端口占用8080 被其他进程占用换端口或关闭占用进程数据库连接失败账号密码或库名错误核对 application.yml 与 MySQL 实际配置预约后槽位没标记事务未提交或更新逻辑错误检查 Transactional 与条件 SQL 返回值排队列表重复签到未做幂等校验添加唯一约束appointment_id时间显示 8 小时偏差时区未统一全局设置 GMT8前端 CSS 无样式静态资源路径错误检查模板中 th:href 写法6. 这个项目还可以继续扩展的方向顶着“定制”的卖点最后聊一下如果你想让这套系统的完成度再上一个台阶可以从哪些地方下手。第一个方向把预约和排队从“后台系统”走向“终端化”。社区诊所的实际场景里前台签到可能不需要登录完整账号而是一个设备端页面只提供“输入手机号后四位 → 自动关联当日预约 → 取号”。这种轻量级交互很适合做成移动端自适应页面体验上的提升非常明显。第二个方向给排队模块加预计等待时间。有了平均就诊时长和当前排队人数系统可以估算一个新的排队序号大概还要等多久。核心算法不复杂前 N 个患者的平均就诊时长累加即可但这一条数据对诊所前台和患者都有很强的心理安抚作用。第三个方向离线容错机制。诊所偶尔断网时预约模块会失效但如果能将当日的预约数据缓存在本地恢复后自动上传整个系统的工业成熟度会高出一大截。当然这个方向更偏工程落地适合想以这套项目去找后端开发岗位的同学深挖。第四个方向权限控制的细化。目前是基于角色的简单区分可以继续扩展为 Spring Security JWT 的认证授权体系把接口权限细化到具体的操作方法上。这既是技术热词也是面试时很好的谈资。这几个方向我自己在实际接触这类带定制需求的毕设项目时验证过每加一个功能系统就有了额外的记忆点。最后说一点个人体会毕业设计项目没有绝对的好坏评委看重的永远是你对自己系统的理解深度——代码你可以参考别人的但业务逻辑、设计取舍、踩坑经历这些问题一定要自己把每一条都想透了答辩才有底气。希望这篇拆解能帮你少走一些弯路。