恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Spring Boot+Vue+MySQL打造线上医疗系统:预约挂号与防超卖实战

  • 首页
  • 资讯中心
  • /
  • Spring Boot+Vue+MySQL打造线上医疗系统:预约挂号与防超卖实战

相关资讯

Windows 7镜像安全下载与校验全指南:避免装机翻车 2026/10/10 3:29:59
时序数据库选型指南:从原理到实操,避开监控数据存储的坑 2026/10/10 3:29:59
OpenHarmony 上 Flutter 高对比度 UI 实践:从颜色模型到无障碍适配 2026/10/10 3:29:59

最新资讯

ChatGLM3-6B LoRA微调实战:轻量、稳定、可验证的工程化链路
Spring Boot体育场馆预约系统毕设全攻略:从数据库到并发控制
ChatGLM3-6B LoRA微调实战:中小团队低成本落地指南
Windows启动级权限控制:BCD配置与内核调试实战指南
C++模板参数包与void_t:彻底解放参数列表的复用革命
从排课冲突到状态流转:微信小程序私教预约系统开发记录

今日推荐

Codex 总用英文回答?从 AGENTS.md 到 config.toml 的中文输出调优指南
OpenClaw 自定义插件开发完整指南(2026最新版):从 TypeScript 到 npm 发布
基于Spark的电影推荐系统全链路实战:从爬虫到Web展示

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Spring Boot+Vue+MySQL打造线上医疗系统:预约挂号与防超卖实战

发布时间:2026/10/10 3:35:00
Spring Boot+Vue+MySQL打造线上医疗系统:预约挂号与防超卖实战 上个月我完整把一套线上医疗服务系统从零撸了一遍技术栈就是标题里的那套后端Java Spring Boot前端Vue数据库用的MySQL。整个系统做下来最大的感受是这类业务系统真正难的不是某个框架用法而是角色权限怎么分、业务流程怎么闭环、并发挂号怎么防超卖这几个设计点。所以这篇文章我不会只丢一堆代码而是从整体设计、表结构、后端核心逻辑、前端联调、上线部署一路讲下来把我踩过的坑和验证过能用的方案都写上。这套系统能做什么简单说就是把线下医院常见的预约挂号、科室导诊、在线问诊、病历管理、处方记录搬到浏览器里分患者端、医生端、管理后台三块。如果你正准备做毕业设计或者刚学完Spring Boot想找一个完整的前后端分离实战项目再或者打算往医疗信息化方向转这篇文章应该能让你少走不少弯路。1. 系统设计先理清楚“给谁用”和“怎么分工”1.1 角色划分与核心业务流程做业务系统第一件事不是写代码而是确定用户角色和他们各自的操作闭环。这套线上医疗系统我划分了三类角色患者注册登录、浏览科室和医生、查看排班、在线预约、查看病历和处方、取消预约。医生查看自己的排班处理待接诊患者在线填写病历、开处方查看历史问诊记录。管理员维护科室、医生信息、排班规则、号源数量、系统公告。角色定下来核心业务流程就清楚了患者先按科室找到医生再看医生的排班时间选一个时间段提交预约系统扣减号源到时间后在线上问诊医生结束问诊后补充病历和处方患者可以随时查看自己的历史记录。这个流程看着简单实际落地时我建议先把流程图手动画一遍再动手尤其是排队状态流转预约状态分为待就诊、已就诊、已取消问诊状态分为待接诊、问诊中、已完成。状态没有提前定好后面写接口会特别痛苦因为每个状态变更都要校验合法性。提示我在项目初期用一张状态机表把所有“状态从A到B是否允许”标出来这个动作帮我避免了至少十几个if判断上的逻辑漏洞。1.2 技术选型为什么是Spring Boot Vue前后端分离现在基本是标配选Spring Boot Vue并不是因为它时髦而是这套组合足够稳后端选Spring Boot核心原因是它把配置简化到了一个非常舒服的程度。内嵌Tomcat不再需要单独配置服务器自动装配机制让数据库连接、Web配置、JSON序列化这些琐碎事大部分都能零配置搞定和MyBatis-Plus配合之后基础CRUD连SQL都不用写。我自己做项目习惯用Spring Boot 2.7.x版本搭配JDK 8兼容性最好网上能查到的资料也最多比追新版本省心很多。前端选Vue看重的是组件化开发和数据双向绑定。同一个医生卡片组件首页展示用、搜索结果用、预约确认弹窗用复用起来极其方便。生态系统里Element UI组件库可以直接拿来搭后台表格、表单、弹窗开发效率非常高。为什么坚持前后端分离而不是用Thymeleaf做服务端渲染核心原因是部署和维护上的自由度前端打包成静态文件扔给Nginx后端打成一个jar包独立运行两边可以单独升级、分开扩容。对一个小型医疗系统来说这个架构的伸缩空间足够了。技术栈具体选型说明后端框架Spring Boot 2.7稳定资料多ORMMyBatis-Plus单表CRUD零SQL复杂查询用XML数据库MySQL 8.0主流事务支持成熟认证JWT无状态适合前后端分离前端框架Vue 2.x Vue Router Vuex组件化生态成熟UI库Element UI后台管理页面效率高构建工具Maven版本依赖管理严格1.3 权限模型后端拦截前端控菜单权限这块一定不能偷懒。我的方案是后端用RBAC模型控制接口访问前端只做路由和菜单的展示层控制。数据库里做用户表、角色表、权限表再加用户-角色和角色-权限两张关联表。后端拦截器解析出JWT里的用户身份再判断当前接口需要什么权限没权限直接返回401。前端用Vue Router的全局守卫根据登录状态下发的角色信息动态生成菜单和路由。要注意这是体验层面的控制真正的安全闸门永远在后端。有人只做前端隐藏按钮就以为权限安全了那只是自欺欺人——别人用Postman直接调接口照样能访问。2. 后端核心实现从表结构到业务逻辑2.1 数据库建模七张核心表怎么设计数据库设计是一整套系统的地基。这套系统核心表我归纳成七张用户表(user)、科室表(department)、医生表(doctor)、排班表(schedule)、预约挂号表(registration)、病历表(medical_record)、问诊记录表(consultation)。关键设计点我拆开说排班表是挂号系统的核心。它需要记录医生某一天在某个时间段出诊以及这个时间段的号源总量和已预约量。不能简单把出诊时间存成一个字符串我设计字段时包含了doctor_id关联医生schedule_date日期time_slot时间段比如 08:00-09:00total_count号源总量booked_count已预约数量用数据库约束保证booked_count total_count每次挂号都会在同一个事务里更新这个数字后面会详细讲怎么防止超卖。挂号表是业务流程的核心关键字段包括用户id、排班id、挂号单号、状态、创建时间。这里有两个容易被忽略的点一是给user_id schedule_id加唯一索引让数据库从底层挡住同一用户重复预约同一个时间段二是状态字段用tinyint数字字典比如0待就诊、1已就诊、2已取消比直接用字符串更节省空间索引效率也更高。病历表和挂号表是一对一关系通过registration_id关联记录主诉、查体、诊断、医生建议等。处方不单独建表的话可以直接在病历表里用JSON字符串存药品列表对早期版本来说这个方案最省事。建表SQL要提交到Git仓库里作为项目的一部分。有人习惯用MyBatis-Plus的代码生成器直接根据Java实体类反向建表我个人的建议是建表这件事一定要手动控制因为字段类型、长度、索引、默认值都需要经过设计自动生成的表往往后患无穷。Navicat里建完表导出SQL脚本放一份在项目里任何时候都能一键初始化数据库。2.2 JWT登录认证的实现方式登录认证我采用JWT因为前后端分离场景下Session方案要处理跨域CORS的Cookie携带问题麻烦还容易出bug。JWT的做法是登录成功后后端生成一个带过期时间的token返回给前端前端存到localStorage里后续每个请求在HTTP头里带上Authorization: Bearer token。token生成我用的是io.jsonwebtoken这个库核心代码也就几十行public String generateToken(Integer userId, String role) { Date now new Date(); Date expireDate new Date(now.getTime() 24 * 60 * 60 * 1000); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(now) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }服务端要做的就是写一个拦截器在请求进入Controller之前解析token。解析失败、过期、角色不匹配都直接返回错误响应码不放行。拦截器选HandlerInterceptor而不是Filter是因为它能拿到Spring MVC的路由匹配信息做细粒度的权限控制更方便。注意JWT一旦签发在过期前是无法主动作废的所以如果项目里需要“强制下线”这类功能建议额外引入Redis做token黑名单或者把token版本号存库里。我做第一个版本时忽略了这一点后来加退出登录功能时才发现不搞一个token失效机制根本没法彻底退出。2.3 预约挂号怎么防并发超卖这是整个系统业务逻辑里最有含金量的一段。挂号本质上是库存扣减和秒杀场景非常像。最常见的错误写法是先查排班表发现booked_count小于total_count然后执行update在并发情况下两个请求同时查到同一个数字同时通过检查双双执行update号源就被透支了。正确做法是把“校验扣减”合并成一条原子性的更新语句int updated scheduleMapper.updateCountIfAvailable(scheduleId); // SQL: // UPDATE schedule // SET booked_count booked_count 1 // WHERE id ? AND booked_count total_count这条SQL利用MySQL单条更新语句的行锁特性同一时刻只有一个请求能更新同一行数据。更新成功返回的行数是1就说明号源充足返回0就说明没号了直接提示用户。这一步是整个挂号系统性能和安全性的核心一定要用这种原子操作绝对不要拆成两步去写。扣减完号源再插入预约记录这两步必须放在同一个事务里。另外在挂号表加user_id schedule_id的唯一索引就能阻止同一个用户反复抢占同一个时间段。取消预约的逻辑刚好相反先校验状态是待就诊然后把状态改成已取消同时执行UPDATE schedule SET booked_count booked_count - 1 WHERE id ? AND booked_count 0把号源释放回去。有一个坑是用户反复点击取消按钮如果代码里没做状态校验会重复释放号源所以每次取消前必须先确认当前状态是待就诊更新时用WHERE status 0再更新。2.4 在线问诊与病历的业务闭环患者完成挂号后医生端工作台会列出待接诊的记录。医生点击“开始问诊”后问诊状态从待接诊变为问诊中结束问诊时医生需要同时填写病历和处方然后状态变为已完成。问诊记录和病历这两张表都在同一个事务里写入避免病历创建成功但问诊状态没更新的脏数据。这里有个设计心得不要把业务状态散落在各个if分支里最好用状态码数字集中管理写一个枚举类比如public enum RegistrationStatus { PENDING(0, 待就诊), FINISHED(1, 已就诊), CANCELED(2, 已取消); private final int code; private final String desc; }这样所有接口判断状态时都引用枚举不会出现魔法数字满天飞的情况。3. 前端Vue实现与前后端联调细节3.1 Vue工程初始化与环境配置前端部分我用Vue CLI初始化项目。环境上建议Node版本稳定用16.x或者18.x LTS版npm下载依赖慢的问题可以换成淘宝镜像源npm config set registry https://registry.npmmirror.com vue create medical-frontend cd medical-frontend npm install element-ui axios vuex vue-router -S工程结构我习惯这样组织src/api统一放接口请求封装src/router放路由配置src/store放全局状态src/views按角色分目录放页面。关键一步是封装axios实例把所有重复逻辑收敛在这里const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )这种封装的意义在于所有页面组件里调接口时只需要关心业务数据不需要重复写token注入和错误处理代码。3.2 核心页面实现预约挂号与医生工作台预约挂号的交互流程是进入医生列表页面按科室筛先筛选点击医生卡片进入详情看到最近7天的排班数据选择一个时间段确认弹窗提交预约。排班数据接口返回的是这样一个结构[ { scheduleId: 101, date: 2025-06-10, timeSlot: 08:00-09:00, total: 20, booked: 15, remain: 5 } ]前端拿到数据后就可以渲染出排班卡片并判断remain是否大于0来判断是否可点击。提交预约时把scheduleId传给后端后端返回成功或者说号已满。医生工作台是另一个重点页面。医生登录后看到自己的患者列表每一项显示患者基本信息、预约时间段、当前状态。点击接诊按钮后进入问诊页面左侧是患者信息右侧是问诊历史对话底部是病历填写表单。这个页面核心是把病历表单拆成独立组件因为诊断字段、处方字段、备注字段的校验逻辑比较复杂独立出来更好维护。组件化开发的价值在这里体现得很明显医生列表页用到的卡片组件在后台管理页面的医生管理表格里可以复用预约时间段的组件在手机端模拟视图里也可以复用。不要把每个页面都从零写一遍先看看哪些区块是可以抽出来的。3.3 跨域和联调中最容易踩的坑前后端分离开发最经典的坑就是跨域。开发环境我的处理方式是启用Vue CLI的proxy代理页面请求是/api开头的路径devServer把它转发到后端的8080端口。配置在vue.config.js里module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样做的好处是开发环境下前端代码里的请求路径不需要写完整域名和后端部署在Nginx后同域的效果一致。我强烈不建议在开发时直接在前端代码里写死http://localhost:8080这种绝对地址后面上线改起来极其痛苦。后端接口返回时间字段的问题也是联调高发区。Java 8的LocalDateTime默认被Jackson序列化成数组前端拿到的是[2025,6,10,10,30,0]这种结构根本没法直接用。我一早就做了全局配置Bean public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() { return builder - builder .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))) .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))); }统一全项目的时间格式联调效率提升巨大。提示IDE里开发前后端项目的时候建议把IDEA的Vue插件装上不然vue文件全是纯文本没有高亮特别别扭。前端起服务用npm run serve后端直接跑Spring Boot主类两边同时运行联调时前端页面改动会热更新后端改动通过devtools自动重启这套开发节奏非常顺。4. 常见问题与排查技巧实录4.1 前后端联调经典报错报错一接口调用跨域报错浏览器里提示CORS policy或者blocked by CORS。开发环境用上面的proxy解决如果后端也配置了CrossOrigin要留意两者叠加时可能出现的奇怪行为——我遇到过一次前端配了proxy后后端CORS配置反而导致多了一次OPTIONS预检把请求搞乱了。我的建议是开发环境用proxy生产环境用Nginx反向代理后端不单独配跨域分工明确。报错二返回数据是null或字段对不上这类问题九成是命名风格不一致。数据库字段是下划线风格schedule_dateJava实体类用的是驼峰scheduleDateJSON输出默认也是驼峰但前端如果按schedule_date取字段就会拿不到。排查思路是先在浏览器Network面板里看真实响应体确认字段名后再去改前端取值不要盲目猜。报错三前端一直弹401先看浏览器请求头里Authorization有没有带上token再看后端控制台有没有解析token的异常日志。如果刷新页面就丢登录状态几乎所有原因都是localStorage的key写错了或者拦截器读取的key和写入时不一致。4.2 业务逻辑上的隐藏坑并发挂号把号源扣成负数这个问题我前面讲过解决方案用原子的条件更新语句是唯一靠谱的做法。加锁也行但性能差而且分布式环境下JVM锁根本不管用。同一个用户重复预约只靠代码判断不可靠并发场景下两次请求可能都通过了业务校验正确方案是在数据库层做唯一索引兜底。取消预约后号源没有释放取消操作和号源释放必须在一个事务里。我写过一版把两步拆开的结果出现用户取消成功但管理员后台显示号源没回来排查了好久。时间校验缺失预约时前端只判断了这个时间段的号源余量没有判断这个时间段是否已经过去。后来加上后端校验挂号的排班日期必须大于等于当前日期且如果日期是当天时间段必须还没结束。这类校验不复杂但漏掉就会出现一堆脏数据。医生权限越级医生的工作台接口如果直接传一个registrationId就能查到任意患者的病历这就是越权漏洞。正确做法是从token里解析出当前医生的id然后在查询条件里强制拼上doctor_id 当前登录医生而不是只依赖前端传参数。4.3 部署上线的操作经验项目做完要上线我踩过的坑值得说一遍。后端打包用Maven打包成jarmvn clean package -DskipTests java -jar medical-server.jarjar包放服务器上启动命令用nohup java -jar medical-server.jar app.log 21 日志重定向到文件排查问题就不会两眼一抹黑。生产环境数据库密码不能用明文写在配置文件里用环境变量覆盖spring: datasource: username: ${DB_USERNAME} password: ${DB_PASSWORD}前端打包后生成dist目录把里面的静态文件放到Nginx的html目录下。Nginx配置里做两件事/路径指向前端静态文件/api路径反向代理到后端jar的8080端口server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files那行特别重要不加的话刷新页面到/patient/register这种路由时Nginx会直接404这是Vue Router使用history模式的标准踩坑点。部署验证清单分享一个我每次上线前都会过一遍的检查项启动日志有没有报错、数据库连接是否正常、登录接口是否通、预约流程是否完整走通、前端刷新不404、图片资源能正常加载、日志文件有持续输出。这七项全绿再宣布上线不然就是在给运维挖坑。4.4 一点实操体会整套项目做完我自己的体会是医疗类业务系统算法和酷炫技术不是重点数据一致性和流程严谨性才是根本。挂号超卖、病历错乱、越权查看这些问题的杀伤力远比页面不够好看严重得多。如果你准备拿这个题目做毕业设计或者项目练手建议按这个顺序推进先画清角色流程和状态机再把七张核心表建好然后后端按登录认证→科室医生查询→预约挂号→病历管理的顺序走通最后做前端页面和联调。前后端分离开发时接口路径和参数结构一定要先和后端同学统一好我自己习惯先写一份简单的API文档再动手编码能省掉后面大量无意义的联调返工。最后再分享一个小技巧开发时把后端接口的测试数据在Navicat里提前造好比如多名医生、多天排班、号源数量不同的时间段联调时直接能看到余量为0的班次怎么显示、满号后预约按钮怎么禁用。真实业务数据永远是检验代码逻辑的最好工具比你自己对着空表猜测应该没问题靠谱得多。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号