news 2026/9/12 16:13:17

SpringBoot+Vue医院挂号系统:事务一致性与并发控制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue医院挂号系统:事务一致性与并发控制实战

简介:这是一套基于SpringBoot开发的医院预约挂号系统完整源码,面向计算机、电子信息工程等专业学生,适用于高分毕业设计、课程设计及期末大作业。系统采用B/S架构与MVC模式,整合Java、MySQL、MyBatis、Vue、Ajax等主流技术,支持Windows/Mac环境,兼容IDEA/Eclipse开发,配套JDK1.8、Maven3.6、MySQL5.7及Tomcat8.0/9.0,开箱即用。资源包共753个文件,涵盖101个Java后端逻辑类、56个Vue前端组件、157个JS交互脚本、34个HTML页面及多种静态资源(CSS、SVG、图片、字体等),结构清晰,前后端分离明确,含build/run/install三类批处理脚本,便于一键部署与调试。目前已有75人学习下载,所有代码均经严格测试,可直接运行并二次开发,附带完整数据库初始化脚本与基础UI样式,助力快速掌握医疗信息系统开发全流程。

1. 这不是又一个“SpringBoot + Vue”模板项目:它用真实挂号流程倒逼你理解 MVC 分层边界与事务一致性

医院预约挂号系统常被当成 Java 初学者练手的「标准 CRUD」项目,但真正跑通一个能上线的挂号流程,远不止增删改查。比如:用户选号成功后,号源库存必须原子性扣减;医生排班变更时,已预约记录要自动失效并通知;退号操作需回滚库存、释放号段、更新患者就诊状态三者强一致。本套代码不是 Demo,而是基于 SpringBoot 2.7.x(兼容 JDK8)构建的完整 B/S 架构系统,前端用 Vue 2 + Element UI 封装挂号页、医生排班页、个人中心页,后端严格按 Controller → Service → Mapper 三层拆分,Service 层全部标注@Transactional,MyBatis XML 显式控制 SQL 执行顺序。它适合计算机/电子信息工程专业学生做毕业设计或课程设计——不是因为“能跑”,而是因为它的每一处@SelectProvider、每一个@Transactional(timeout = 30)、每一条INSERT INTO t_order ... ON DUPLICATE KEY UPDATE都直指真实业务中的并发控制、幂等设计和数据一致性痛点。如果你正卡在“为什么我写的挂号接口在高并发下会超号?”或“退号后库存没恢复,日志里却显示 success”,这套代码就是你的调试镜。

2. 环境搭建与数据库初始化:从 bat 脚本切入理解部署链路

这套代码的启动方式非常“接地气”:三个.bat文件直接暴露了 Windows 下的典型部署路径。这不是偷懒,而是把环境依赖显性化——每个脚本都对应一个关键阶段,理解它们才能避开 80% 的启动失败。

2.1 三个 bat 文件的真实作用与执行顺序

1-install.bat并非安装 JDK 或 MySQL,而是执行数据库初始化。它调用mysql -u root -p < init.sql,其中init.sql包含建库、建表、插入初始医生/科室/号源数据。注意:该脚本默认连接本地 MySQL 5.7,若密码非空,需手动修改脚本中-p后的密码字段。
3-build.bat是 Maven 构建入口,执行mvn clean package -Dmaven.test.skip=true。它跳过测试用例(因部分测试依赖未提供的 mock 数据),生成target/hospital-booking-1.0.jar关键点:该 JAR 内嵌 Tomcat 8.5,无需额外部署 WAR 包。
2-run.bat是最终启动命令:java -jar hospital-booking-1.0.jar --spring.profiles.active=dev--spring.profiles.active=dev指向application-dev.yml,其中配置了本地 MySQL 地址、账号密码及 MyBatis 日志级别(设为DEBUG可查看 SQL 执行细节)。

提示:Mac/Linux 用户需将.bat改为.sh,并替换mysqljava命令路径。例如1-install.sh中应写mysql -u root -p < init.sql,而非mysql -uroot -proot < init.sql(空格位置影响解析)。

2.2 数据库结构解析:号源表(t_schedule)与预约表(t_order)的设计逻辑

系统核心是号源与预约的强绑定关系。t_schedule表存储医生每日可放号信息:

CREATE TABLE `t_schedule` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `doctor_id` bigint(20) NOT NULL COMMENT '医生ID', `dept_id` bigint(20) NOT NULL COMMENT '科室ID', `date` date NOT NULL COMMENT '出诊日期', `session` tinyint(4) NOT NULL COMMENT '上午/下午:1=上午,2=下午', `total_num` int(11) NOT NULL DEFAULT '0' COMMENT '总号源数', `used_num` int(11) NOT NULL DEFAULT '0' COMMENT '已用号源数', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0=停诊,1=正常', PRIMARY KEY (`id`), UNIQUE KEY `uk_doctor_date_session` (`doctor_id`,`date`,`session`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

关键设计点在于used_num字段——它不通过SELECT COUNT(*)实时计算,而是在每次预约成功后UPDATE t_schedule SET used_num = used_num + 1 WHERE id = ? AND used_num < total_num。这种“乐观锁式”更新避免了并发超号:AND used_num < total_num是硬性校验条件,若此时号源已满,SQL 影响行数为 0,事务回滚。

t_order表则记录预约详情,并通过外键关联t_schedule

CREATE TABLE `t_order` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `patient_id` bigint(20) NOT NULL COMMENT '患者ID', `schedule_id` bigint(20) NOT NULL COMMENT '号源ID', `order_no` varchar(32) NOT NULL COMMENT '订单号', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1=待就诊,2=已完成,3=已取消', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_schedule_id` (`schedule_id`), CONSTRAINT `fk_order_schedule` FOREIGN KEY (`schedule_id`) REFERENCES `t_schedule` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ON DELETE CASCADE确保当某条号源被删除(如医生临时停诊),其所有关联预约自动清除,避免脏数据。

2.3 启动前必须验证的四个配置项

打开src/main/resources/application-dev.yml,检查以下参数是否匹配你的环境:

配置项示例值说明
spring.datasource.urljdbc:mysql://localhost:3306/hospital?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai必须包含serverTimezone=Asia/Shanghai,否则时间字段存入 MySQL 会偏移8小时
spring.datasource.usernameroot若 MySQL 修改了 root 密码,此处必须同步
mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl设为StdOutImpl可在控制台打印完整 SQL,调试必备
server.port8080若 8080 被占用,需改为其他端口(如 8081),并同步修改前端axios请求地址

注意:application-dev.ymlspring.profiles.active已设为dev,若误删此行,SpringBoot 会加载application.yml(默认 profile),导致数据库配置失效。

3. 核心业务代码剖析:挂号、退号、号源锁定的事务实现

挂号功能看似简单,实则涉及多表联动与异常兜底。本系统将核心逻辑封装在OrderService.java中,其方法签名与实现细节直指生产级要求。

3.1 挂号接口(placeOrder)的事务边界与幂等控制

OrderController.placeOrder()接收 JSON 参数{ "scheduleId": 1001, "patientId": 2001 },调用orderService.placeOrder(scheduleId, patientId)。该 Service 方法定义如下:

@Transactional(rollbackFor = Exception.class, timeout = 30) public Result placeOrder(Long scheduleId, Long patientId) { // 1. 查询号源有效性(状态+库存) Schedule schedule = scheduleMapper.selectById(scheduleId); if (schedule == null || schedule.getStatus() != 1 || schedule.getUsedNum() >= schedule.getTotalNum()) { return Result.fail("号源不可用,请刷新后重试"); } // 2. 生成唯一订单号(防重复提交) String orderNo = "ORD" + System.currentTimeMillis() + RandomUtil.randomNumbers(6); // 3. 扣减号源库存(乐观锁) int updateCount = scheduleMapper.updateUsedNum(scheduleId); if (updateCount == 0) { return Result.fail("号源已被抢完,请稍后再试"); } // 4. 创建预约记录 Order order = new Order(); order.setPatientId(patientId); order.setScheduleId(scheduleId); order.setOrderNo(orderNo); order.setStatus(1); // 待就诊 orderMapper.insert(order); return Result.success("预约成功", orderNo); }

关键点解析:

  • @Transactional(timeout = 30):事务超时设为 30 秒,防止数据库锁等待过长;
  • scheduleMapper.updateUsedNum()对应 XML 中的<update>语句,执行UPDATE t_schedule SET used_num = used_num + 1 WHERE id = #{id} AND used_num < total_num
  • 订单号ORD + 时间戳 + 6位随机数保证全局唯一,避免重复提交导致多次扣库存;
  • if (updateCount == 0)判断是业务层面的“失败即回滚”,比数据库唯一索引更早拦截。

3.2 退号接口(cancelOrder)的级联回滚与状态同步

退号需同时更新t_order.statust_schedule.used_num,且必须保证二者原子性。OrderService.cancelOrder()实现如下:

@Transactional(rollbackFor = Exception.class) public Result cancelOrder(String orderNo) { // 1. 查询订单(带号源ID) Order order = orderMapper.selectByOrderNo(orderNo); if (order == null || order.getStatus() != 1) { // 仅允许取消“待就诊”状态 return Result.fail("订单状态异常,无法取消"); } // 2. 更新订单状态为“已取消” order.setStatus(3); orderMapper.updateById(order); // 3. 回滚号源库存(注意:此处用 UPDATE ... SET used_num = used_num - 1) int rollbackCount = scheduleMapper.rollbackUsedNum(order.getScheduleId()); if (rollbackCount == 0) { throw new RuntimeException("号源回滚失败,可能已被其他操作修改"); } return Result.success("退号成功"); }

对应的rollbackUsedNumXML 语句为:

<update id="rollbackUsedNum"> UPDATE t_schedule SET used_num = used_num - 1 WHERE id = #{id} AND used_num > 0 </update>

AND used_num > 0是安全阀,防止used_num被减至负数。

3.3 前端挂号页的关键交互逻辑(Vue + Element UI)

前端src/views/booking/BookingPage.vue中,挂号按钮绑定handlePlaceOrder方法:

handlePlaceOrder() { this.$confirm('确认预约该号源?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 1. 禁用按钮防重复点击 this.isSubmitting = true; // 2. 调用 API api.placeOrder({ scheduleId: this.selectedSchedule.id, patientId: this.patientId }) .then(res => { this.$message.success(`预约成功!订单号:${res.data}`); this.$router.push('/user/my-orders'); // 跳转至我的预约页 }) .catch(err => { this.$message.error(err.message || '预约失败'); }) .finally(() => { this.isSubmitting = false; // 恢复按钮状态 }); }); }

这里体现两个工程实践:

  • this.isSubmitting = true在请求发起时禁用按钮,从 UI 层杜绝重复提交;
  • finally块确保无论成功失败都恢复按钮状态,避免用户误以为操作卡死。

4. 常见启动失败排查与性能优化技巧

即使严格按照文档操作,仍可能遇到启动报错。以下是高频问题的定位路径与修复方案,附带可直接执行的验证命令。

4.1 启动报错“Failed to configure a DataSource”:四步定位法

该错误表明 SpringBoot 无法连接 MySQL。按顺序执行以下检查:

  1. 验证 MySQL 服务是否运行
    Windows 下执行services.msc,确认MySQL80服务状态为“正在运行”;
    Mac/Linux 执行brew services list | grep mysqlsystemctl status mysqld

  2. 验证数据库是否存在且可登录

    mysql -u root -p -e "SHOW DATABASES LIKE 'hospital';"

    若返回空,则说明1-install.bat未成功执行,需手动运行init.sql

  3. 验证 application-dev.yml 中 URL 是否可达

    telnet localhost 3306

    若连接超时,检查 MySQL 是否监听localhost:3306(而非127.0.0.1:3306),或防火墙是否拦截。

  4. 验证驱动版本兼容性
    pom.xml<mysql-connector-java.version>应为8.0.28(适配 MySQL 5.7)。若使用 MySQL 8.0+,需升级驱动并修改 URL 添加allowPublicKeyRetrieval=true&useSSL=false

4.2 号源页面加载慢:MyBatis 二级缓存启用指南

挂号页需实时展示各医生号源余量,频繁查询t_schedule表易成瓶颈。系统已预留二级缓存开关,只需两步激活:

  1. application-dev.yml中添加:

    mybatis: configuration: cache-enabled: true
  2. ScheduleMapper.java接口上添加注解:

    @CacheNamespace( eviction = org.apache.ibatis.cache.decorators.LruCache.class, flushInterval = 60000, // 60秒自动刷新 size = 1000, readWrite = true ) public interface ScheduleMapper { // ... }

启用后,selectById查询将缓存结果,相同scheduleId的后续请求直接从内存读取,QPS 提升 3~5 倍。注意:updateUsedNumrollbackUsedNum方法会自动清空缓存,保证数据一致性。

4.3 前端样式错乱:CSS 文件加载路径修正表

项目中 CSS 文件名含哈希(如app.0de1b4f2.css),这是 Webpack 打包产物。若前端页面样式异常,大概率是静态资源路径未正确映射。检查src/main/resources/static/index.html中的 link 标签:

错误写法正确写法说明
<link href="front-kaoshi-style.css" rel="stylesheet"><link href="/css/front-kaoshi-style.css" rel="stylesheet">SpringBoot 默认静态资源路径为/static,CSS 应放在/static/css/目录下
<link href="app.0de1b4f2.css" rel="stylesheet"><link href="/css/app.0de1b4f2.css" rel="stylesheet">所有哈希文件需统一加/css/前缀

修正后重启应用,浏览器访问http://localhost:8080/css/app.0de1b4f2.css应返回 CSS 内容,HTTP 状态码为 200。

提示:若使用 IDEA 启动,确保Run ConfigurationWorking directory设置为项目根目录(含pom.xml),否则1-install.bat可能找不到init.sql

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:11:27

机器人坐标变换系统的革新:从tf到tf2的深度解析与应用实践

在当今机器人技术飞速发展的时代,机器人软件的开发已成为推动自动化、智能导航和人机协作的核心支柱。特别是在复杂环境中的自主导航与精确定位任务上,坐标变换(transformation)作为基础但关键的系统组件,直接影响机器人的决策精度与操作效率。例如,在一个多传感器融合的…

作者头像 李华
网站建设 2026/9/12 16:11:11

OpenCLIP 快速上手:零样本分类、图文检索与微调实战指南

OpenCLIP 快速上手&#xff1a;零样本分类、图文检索与微调实战指南 【免费下载链接】open_clip An open source implementation of CLIP. 项目地址: https://gitcode.com/GitHub_Trending/op/open_clip OpenCLIP 是 CLIP 的开源实现&#xff0c;核心能力是把图片和文本…

作者头像 李华
网站建设 2026/9/12 16:09:47

如何把微信聊天记录导出成 Word 和 HTML?3步上手,免费开源

如何把微信聊天记录导出成 Word 和 HTML&#xff1f;3步上手&#xff0c;免费开源 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Tr…

作者头像 李华