ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java医院预约挂号系统源码实战:微信小程序开发与号源并发扣减避坑指南

Java医院预约挂号系统源码实战:微信小程序开发与号源并发扣减避坑指南 简介这是一套基于Java与微信小程序的医院预约挂号系统源码面向计算机专业学生、课程设计或毕业设计开发者以及需要快速搭建医疗预约类项目的技术人员。系统采用SpringBoot MyBatisPlus MySQL Redis架构前台使用uni-app Vue开发微信小程序后台分为管理员端与医生端涵盖用户管理、排班管理、预约记录、科室疾病管理、医生管理、公告管理以及医生设置排班、查看预约订单等模块前台支持注册登录、预约挂号、核酸检测、查看坐诊信息、管理就诊人、导航到院等功能。资源包共185个文件以113个vue组件、41个js脚本、10个scss样式为主另含json配置、md说明与字体图标等压缩包约517KB结构清晰便于二次开发。目前已有2377人学习下载适合作为完整项目参考帮助理解前后端分离与小程序端联调思路。1. 从一份 Java 医院预约挂号系统源码说起它到底能跑通什么一份名为「Java医院预约挂号系统 - 微信小程序.zip」的源码包通常包含两套东西一套跑在服务器上的 Java 后端一套跑在手机里的微信小程序前端。后端负责号源管理、排班、订单、用户和支付回调前端负责科室选择、医生列表、日期时段、确认下单。它解决的核心问题是把线下窗口排队变成线上分时段预约让医院放号、患者抢号、订单核销形成闭环。适合谁正在做 Java 课程设计的学生、想接私活练手的小团队、以及需要一套可二次开发的预约类业务底座的人。但源码包不等于能上线真正决定它能不能用的是号源并发扣减、微信登录态、订单超时释放这三件事。下面按「先跑起来、再改对、最后防坑」的顺序拆开讲。2. 环境搭建与最小可运行链路把 Java 后端和微信小程序同时拉起来2.1 后端技术栈的选型理由与依赖清单拿到源码先别急着改业务先看它用什么搭的。常见的 Java 医院预约挂号系统后端组合是 Spring Boot MyBatis-Plus MySQL Redis管理端页面可能是 Vue 或 Thymeleaf。选 Spring Boot 是因为预约系统接口多但逻辑不复杂起步快选 MyBatis-Plus 是因为号源、订单、排班这些表字段多手写 SQL 容易出错Redis 一般用来做号源缓存和分布式锁防止同一时段被超卖。先确认本机环境。JDK 用 8 或 11 都行但要看源码pom.xml里的java.version别用 17 去跑只写了 8 的项目容易在反射和日期类上翻车。MySQL 建议 5.7 或 8.0注意 8.0 的驱动类名是com.mysql.cj.jdbc.Driver老项目里写com.mysql.jdbc.Driver会报弃用警告。Redis 装本地或 Docker 都行预约系统对 Redis 的依赖通常只在扣号和缓存不是强绑定。# 检查本机 Java 版本必须和 pom.xml 里的 java.version 对齐 java -version # 建库字符集用 utf8mb4医院系统里患者姓名可能有生僻字 mysql -uroot -p -e CREATE DATABASE hospital_reg DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入源码里的 SQL 文件注意先看文件里有没有 DROP TABLE mysql -uroot -p hospital_reg doc/hospital_reg.sql上面三步做完数据库这层就通了。参数上重点看连接串useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue少一个serverTimezone就可能出现预约时间差 8 小时这是血泪经验。导入 SQL 前一定先打开文件搜DROP TABLE有些课程设计源码会直接删表重建别把已有数据冲掉。2.2 微信小程序的 AppID、合法域名与本地联调小程序端要跑起来先解决三件事AppID、请求域名、本地调试。没有企业主体就用测试号测试号也能调wx.login和大部分接口只是不能真机支付。请求域名在开发阶段可以勾「不校验合法域名」但上线前必须配 HTTPS 域名且域名要备案。// utils/request.js 里常见的请求封装重点看 baseUrl 和 token 注入 const BASE_URL http://localhost:8080/api; // 本地联调改成局域网 IP真机才能访问 function request(options) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, token: wx.getStorageSync(token) || // 登录后存的 token }, success: (res) { if (res.data.code 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); return; } resolve(res.data); }, fail: reject }); }); }这段封装里有两个参数决定联调成败BASE_URL在模拟器里写localhost可以但真机预览必须换成电脑的局域网 IP比如http://192.168.1.10:8080/api否则手机访问不到你的电脑。token从缓存取说明登录态是前端自己维护的后端一般用拦截器校验。如果登录后接口一直 401先看wx.login换取的 code 有没有发给后端、后端有没有正确调微信接口换 openid。2.3 从登录到选号一条最小预约链路的接口顺序把链路按顺序走一遍比漫无目的地点页面高效。典型顺序是wx.login拿 code → 后端换 openid 并返回 token → 拉科室列表 → 拉医生列表 → 拉排班和号源 → 提交预约 → 支付回调 → 查询订单。# 用 curl 模拟小程序登录验证后端登录接口是否通 curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {code:test_code_123} # 预期返回{code:200,data:{token:xxx,userId:1}}如果这一步返回 500先看后端日志里调微信jscode2session的返回常见是 AppID 和 Secret 填错或者测试号没有该接口权限。登录通了之后号源接口一般会带doctorId和date两个参数返回该医生当天各时段的剩余号数。这里要留意号源是实时变的前端展示的剩余数只是快照真正扣减在后端提交订单时完成。3. 号源并发扣减与订单状态机预约系统最容易写错的两块3.1 为什么「先查再减」一定会超卖很多课程设计源码的扣号逻辑是这样的先select查剩余号数判断大于 0再update减一。这个写法在单机低并发下没问题但只要有两个人同时点确认就可能都查到剩余 1然后都减成功号源变成 -1。这就是典型的超卖。医院放号往往集中在某个整点瞬时并发不低这个坑必须堵。常见做法有三种数据库乐观锁、Redis 原子扣减、分布式锁。乐观锁适合号源表按「医生日期时段」一行存储的场景SQL 写成update schedule set remain remain - 1 where id ? and remain 0看影响行数是否为 1。Redis 适合号源预热到缓存、扣减走decr的场景性能好但要处理缓存和数据库一致性。分布式锁适合逻辑复杂、需要跨多张表操作的场景但锁粒度要细别一把大锁锁住整个预约接口。-- 乐观锁扣减关键是 where 里带 remain 0且用影响行数判断 UPDATE doctor_schedule SET remain remain - 1, version version 1 WHERE id #{scheduleId} AND remain 0 AND version #{version}; -- 如果返回 0 行说明号已被抢完或版本冲突直接抛「号源不足」参数说明scheduleId是排班主键version是前端带过来的版本号也可以不用 version 只用remain 0但加上 version 能防止 ABA 问题。执行后必须判断affectedRows等于 0 就回滚并提示用户。这一步是预约系统的命门写错一次线上就可能出现负数号源。3.2 订单状态机待支付、已支付、已取消、已核销怎么流转预约订单不是简单的「创建即完成」它有一条状态链。常见状态是待支付 → 已支付 → 已核销以及待支付 → 已取消超时或用户主动取消。状态流转必须由后端控制前端传什么状态都不能信。状态值含义允许的下一步触发方0待支付1 或 2用户支付 / 超时任务1已支付3医院核销2已取消无用户或定时任务3已核销无医院端状态机落地时每次更新都要带where status 当前状态比如取消订单写update orders set status 2 where id ? and status 0防止已支付的订单被误取消。这个细节在源码里经常被忽略但它是订单数据一致性的最后一道防线。3.3 订单超时释放号源的定时任务怎么写用户下单后不支付号源不能一直占着。常见做法是下单时给一个过期时间比如 15 分钟然后用定时任务扫描超时未支付订单取消并回补号源。// 基于 Spring 的定时任务每 5 分钟扫一次超时订单 Scheduled(cron 0 0/5 * * * ?) public void cancelExpiredOrders() { // 查出创建超过 15 分钟且状态仍为待支付的订单 ListOrder expired orderMapper.selectList( new QueryWrapperOrder() .eq(status, 0) .lt(create_time, LocalDateTime.now().minusMinutes(15)) ); for (Order order : expired) { // 先改订单状态带 status 0 条件防止并发下重复取消 int updated orderMapper.update(null, new UpdateWrapperOrder() .set(status, 2) .eq(id, order.getId()) .eq(status, 0)); if (updated 1) { // 只有真正取消成功的订单才回补号源避免多回 scheduleMapper.addRemain(order.getScheduleId()); } } }这段代码的关键在updated 1这个判断。如果两个定时任务实例同时扫到同一订单只有一个能把状态从 0 改成 2另一个影响行数为 0就不会重复回补号源。参数上cron表达式按业务量调号源紧张就 1 分钟一次一般 5 分钟够用。回补号源的 SQL 用remain remain 1注意别超过总号数可以在 where 里加remain total。4. 微信登录态、支付回调与数据一致性联调阶段最容易卡住的地方4.1 wx.login 换 openid 的完整流程与 session 维护小程序的登录不是账号密码而是wx.login拿临时 code后端拿 code 加 AppID、Secret 去微信服务器换 openid 和 session_key。这个流程里code 只能用一次且五分钟过期。后端换到 openid 后一般生成自己的 token 返回给前端后续请求带 token 识别用户。// 后端登录接口的核心逻辑用 RestTemplate 调微信接口 String url https://api.weixin.qq.com/sns/jscode2session ?appid appId secret secret js_code code grant_typeauthorization_code; String resp restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(resp); String openid json.getString(openid); if (openid null) { // errcode 40029 表示 code 无效40163 表示 code 已被使用 throw new BizException(登录失败 json.getString(errmsg)); } // 用 openid 查用户没有就注册然后签发 token参数上appId和secret必须和当前小程序对应测试号和企业号的 secret 不通用。grant_type固定authorization_code。如果返回errcode 40029多半是 code 重复使用或已过期前端要重新调wx.login。这里有个常见误用把 session_key 返回给前端这是不安全的session_key 只应留在后端用于解密手机号等敏感数据。4.2 微信支付回调的验签与幂等处理预约系统如果涉及支付回调是必须处理的。微信支付回调会 POST 一段加密数据到你的 notify 接口你需要验签、解密、更新订单状态。这里最容易出两个问题一是没验签任何人都能伪造回调二是没做幂等微信重复通知导致订单被多次处理。// 支付回调的幂等处理核心是先查订单状态再更新 PostMapping(/pay/notify) public String payNotify(RequestBody String body) { // 1. 验签微信支付 SDK 提供 verify 方法失败直接返回失败 // 2. 解密拿到 out_trade_no 和 transaction_id String orderNo decrypt(body).getOutTradeNo(); // 3. 幂等只有待支付的订单才处理 int updated orderMapper.update(null, new UpdateWrapperOrder() .set(status, 1) .set(transaction_id, transactionId) .eq(order_no, orderNo) .eq(status, 0)); if (updated 0) { // 已经处理过直接返回成功避免微信重复通知 return SUCCESS; } // 4. 支付成功后的业务比如发短信、写核销码 return SUCCESS; }参数说明out_trade_no是你自己生成的订单号必须全局唯一transaction_id是微信侧流水号用于对账。返回给微信的必须是SUCCESS的 XML 或 JSON否则微信会按策略重复通知。幂等判断用status 0做条件更新和前面订单状态机的思路一致。4.3 数据库和缓存双写时怎么保证号源不脏如果号源同时存在 MySQL 和 Redis扣减时先动谁、后动谁是个经典问题。常见做法是扣减以数据库为准Redis 只做展示缓存扣减成功后删除缓存而不是更新缓存让下次查询回源重建。这样即使缓存删失败最坏情况是展示短暂偏多但不会出现数据库和缓存都不一致导致超卖。// 扣减号源先数据库乐观锁成功后删缓存 int updated scheduleMapper.decrRemain(scheduleId); if (updated 1) { redisTemplate.delete(schedule:remain: scheduleId); } else { throw new BizException(号源不足); }注意这里删的是缓存 key不是更新。更新缓存会引入并发写覆盖的问题删除则简单可靠。如果业务对展示实时性要求高可以在删除后主动查一次数据库回填但别在扣减事务里做太多事事务越长锁持有越久并发越差。5. 避坑与排查源码跑不起来、号源对不上、支付不回调时看哪里5.1 启动报错「Unknown database」或「Access denied」现象后端启动直接抛数据库连接异常。原因SQL 没导入、库名和配置不一致、或 MySQL 用户没有远程权限。解决先确认application.yml里的url库名和实际建库名一致再确认用户名密码。如果是 Docker 里的 MySQL注意端口映射和host别写localhost要写容器名或宿主 IP。5.2 小程序请求一直失败但浏览器能访问接口现象Postman 调接口正常小程序里报request:fail。原因小程序开发工具没勾「不校验合法域名」或者BASE_URL写的是localhost而真机访问不到。解决开发阶段在详情里勾选不校验真机联调把BASE_URL换成电脑局域网 IP并确保手机和电脑在同一网络。上线前再换回 HTTPS 备案域名。5.3 号源显示有但提交时提示不足现象列表页显示剩余 3 个号点确认却提示号源不足。原因列表数据是缓存或几秒前的快照真实号源已被别人扣走。解决这是正常现象前端要在提交失败后刷新号源列表并给用户明确提示。如果频繁出现检查缓存过期时间是不是太长一般号源缓存不超过 10 秒。5.4 支付成功后订单还是待支付现象用户付了钱订单状态没变。原因回调地址配错、验签失败、或回调处理抛异常。解决先看微信支付商户平台的回调地址是否公网可达再看后端日志有没有收到通知。如果收到但验签失败检查 API 密钥和证书。回调处理里任何异常都要记日志别吞掉。5.5 定时任务重复回补号源导致号数变多现象取消订单后号源数超过总号数。原因多个定时任务实例同时执行或回补逻辑没做幂等。解决给回补操作加条件比如remain total并确保订单状态更新和号源回补在同一个判断分支里只有状态真正从待支付改成已取消才回补。6. 把预约系统做扎实的一个进阶习惯用对账和压测代替感觉源码能跑通只是起点真正让这套 Java 医院预约挂号系统站得住的是两件事对账和压测。对账是每天定时比对订单表和支付流水找出状态不一致的单子压测是模拟放号瞬间的并发看扣减逻辑扛不扛得住。对账可以用一条 SQL 起步-- 找出已支付但支付流水缺失的订单这类单子要人工核 SELECT o.order_no, o.status, p.transaction_id FROM orders o LEFT JOIN pay_record p ON o.order_no p.order_no WHERE o.status 1 AND p.transaction_id IS NULL;压测不用复杂工具JMeter 或写个多线程脚本直接打扣减接口就行重点看两件事有没有出现负数号源以及响应时间在并发下是否可接受。我一般会先把号源改成 1然后开 50 个线程同时扣跑完查数据库remain是不是 0只要出现 -1 就说明扣减逻辑还有漏洞。还有一个习惯所有涉及状态变更的接口日志里必须打上订单号、用户 ID、变更前后状态。预约系统出问题时最怕的就是黑匣子有了这些日志排查时间能从半天缩到十分钟。别等线上出事了才后悔没加日志这是踩过坑之后最深的体会。希望帮到你。本文还有配套的精品资源点击获取
返回列表