news 2026/9/25 4:18:13

微信小程序疫苗预约系统源码解析:高并发号源扣减与幂等下单实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序疫苗预约系统源码解析:高并发号源扣减与幂等下单实战

简介:这份资源是基于微信小程序的疫苗预约接种系统源码,面向具备Java与小程序开发基础、需要课程设计或二次开发参考的开发者。项目对原有疫苗预约系统进行重构后二次开发,管理员端覆盖数据分析图、接种点与医护人员管理、预约计划维护、接种者信息及支付、预约、签到、预检、接种、留观等历史记录查询,接种者端支持疫苗与接种点浏览、预约申请提交、模拟支付及接种二维码状态查看,功能链路完整。压缩包共644个文件,约3.56MB,以html、vue、xml、java、js等前后端源码为主,辅以scss、css样式与gif、jpg、png图片资源,另含sql脚本、properties配置和md说明,结构清晰便于按模块阅读。导入IDEA后修改pom中的MySQL驱动及数据库、图片路径配置即可启动。目前已有1054人学习,适合作为毕业设计、课程作业或小程序全栈练习的参考模板。

1. 疫苗预约接种小程序:从一份源码包到能跑通的业务闭环

社区接种点最头疼的不是没苗,而是放号那一刻的并发和现场秩序。一份「基于微信小程序的疫苗预约接种系统源码.zip」落到手里,真正要回答的是三件事:它能不能扛住放号瞬间的请求、接种记录和库存怎么对齐、以及我改完能不能直接上线。这套系统本质是「小程序端 + 后端服务 + 数据库」的三段式结构,小程序负责建档、选苗、选时段、看凭证,后端负责号源扣减、幂等下单、接种核销,数据库负责库存与记录的一致性。适合两类人:一类是课程设计或毕设需要完整业务链路的同学,另一类是社区/门诊想快速搭一套轻量预约工具的开发者。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲,源码只是起点,跑通业务闭环才是终点。

2. 拆解源码包结构:小程序端、服务端、数据库各管什么

拿到压缩包先别急着导入 IDE,先搞清楚目录分层,否则后面改一个字段要在三个地方来回找。常见做法是根目录下分miniprogram/、server/、sql/三块,有的还会带admin/管理后台。这一章先把「谁负责什么」讲清楚,再给出导入和初始化的具体命令。

2.1 目录分层与职责边界

典型结构长这样,不同作者命名略有差异,但职责划分基本一致:

目录职责关键文件
miniprogram/小程序页面、组件、请求封装app.js、pages/、utils/request.js
server/接口、库存扣减、核销逻辑app.js(入口)、routes/、service/
sql/建表语句与初始数据schema.sql、init_data.sql
admin/(可选)号源配置、接种记录导出视实现而定

小程序端只做展示和表单校验,真正的库存判断必须在服务端做,这是后面避坑章节要反复强调的点。请求封装一般在utils/request.js里统一处理 token 和错误码,改接口地址就改这一处。

2.2 导入与初始化:三条命令跑起来

先建库再导数据,顺序反了会报外键错误。以 MySQL 为例:

# 1. 创建数据库,字符集用 utf8mb4,疫苗名称可能有生僻字 mysql -u root -p -e "CREATE DATABASE vaccine_db DEFAULT CHARSET utf8mb4;" # 2. 导入表结构和初始号源数据 mysql -u root -p vaccine_db < sql/schema.sql mysql -u root -p vaccine_db < sql/init_data.sql # 3. 启动服务端,端口按源码里的配置来,常见是 3000 cd server && npm install && npm run dev

逻辑说明:第一步单独建库是为了控制字符集,很多源码包默认 latin1,中文疫苗名会变问号。第二步分两次导入,schema 建表、init_data 灌初始数据,方便你只重置数据不动结构。第三步npm run dev走的是 nodemon 热重载,改代码不用重启。

参数说明:数据库连接信息通常在server/config.js或.env里,重点看host、user、password、database四项;服务端口如果和本地其他项目冲突,改PORT即可,但小程序端request.js里的 baseUrl 要同步改,否则请求全 404。

2.3 小程序端配置:appid 与合法域名

用微信开发者工具打开miniprogram/目录,第一件事是填自己的 appid。测试阶段可以在「详情 → 本地设置」勾选「不校验合法域名」,但上线前必须把后端域名配到小程序后台的 request 合法域名里,且必须是 https。

// utils/request.js 里通常长这样,改 baseUrl 指向你的服务 const BASE_URL = 'https://your-domain.com/api'; function request(options) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Authorization': wx.getStorageSync('token') || '' }, success: res => res.statusCode === 200 ? resolve(res.data) : reject(res.data), fail: reject }); }); }

逻辑说明:统一封装的好处是 token 注入和错误处理只写一次。Authorization从本地缓存取,登录后写入。参数说明:BASE_URL上线必须是 https 且已备案域名;method默认 GET,提交预约要用 POST;statusCode非 200 统一走 reject,页面里 catch 后弹提示。

3. 预约核心链路:号源扣减、幂等下单与接种记录落库

这一章是整套系统的命门。放号瞬间几十上百人同时点,如果扣库存写成「先查再减」,超卖是必然的。下面把下单链路拆成三步:校验资格、原子扣减、写预约单,再讲幂等和接种记录怎么落。

3.1 号源扣减为什么必须用原子操作

新手最容易翻车的地方:先SELECT stock判断大于 0,再UPDATE stock = stock - 1。两个请求同时查到 stock=1,都判断通过,结果扣成 -1。正确做法是把判断和扣减合并成一条 SQL,靠数据库行锁保证原子性:

-- 原子扣减:只有库存大于 0 才会更新成功 UPDATE vaccine_slot SET stock = stock - 1, updated_at = NOW() WHERE slot_id = ? AND stock > 0; -- 检查影响行数,为 0 说明没抢到 -- affectedRows === 1 才算预约成功

逻辑说明:WHERE stock > 0是条件,SET stock = stock - 1是动作,数据库在执行时对目标行加锁,两个并发请求会串行执行,第二个请求看到 stock 已经是 0,affectedRows 为 0,直接返回「已约满」。参数说明:slot_id是时段主键,建议加唯一索引配合;updated_at便于排查。这一步必须在事务里,扣减成功后紧接着写预约单,失败一起回滚。

3.2 幂等下单:防止用户连点两次生成两条记录

用户手抖连点、或者网络超时重试,都会造成重复预约。常见做法是前端按钮置灰 + 后端幂等键双保险。幂等键可以用「用户ID + 时段ID」拼一个唯一索引:

-- 预约单表加唯一索引,同一用户同一时段只能有一条有效记录 ALTER TABLE appointment ADD UNIQUE KEY uk_user_slot (user_id, slot_id, status);

逻辑说明:status参与唯一索引是为了允许「取消后重新预约」——取消时 status 改成 canceled,新记录 status 是 booked,两者不冲突。参数说明:如果业务允许同一时段约多种疫苗,唯一索引要加上vaccine_id。插入时捕获唯一键冲突异常,直接返回「请勿重复提交」,比先查后插更可靠。

3.3 接种记录落库与状态流转

预约单和接种记录建议分两张表。预约单管「约没约上」,接种记录管「打没打」。状态流转:booked(已预约)→ checked_in(已核销)→ completed(已接种),取消走 canceled。核销时用扫码或输入预约码,服务端校验状态必须是 booked 才能流转:

// 核销接口核心逻辑 async function checkIn(appointmentId) { const rows = await db.query( 'UPDATE appointment SET status = "checked_in", checkin_time = NOW() WHERE id = ? AND status = "booked"', [appointmentId] ); if (rows.affectedRows === 0) { throw new Error('预约状态异常,无法核销'); } // 核销成功后写接种记录 await db.query('INSERT INTO vaccination_record (appointment_id, vaccine_id, user_id, vaccinate_time) VALUES (?, ?, ?, NOW())', [appointmentId, vaccineId, userId]); }

逻辑说明:WHERE status = "booked"保证只有有效预约能核销,重复扫码第二次 affectedRows 为 0,直接报错。参数说明:checkin_time记录核销时刻,vaccinate_time记录实际接种时刻,两者可能不同,报表统计用哪个要提前和业务确认。

4. 避坑与排查:源码跑不起来时先看这几条

源码包质量参差不齐,下面五条是我踩过或见别人踩过的,按「现象 → 原因 → 解决」写,照着排查能省大半天。

4.1 小程序请求全部失败,控制台报「不在以下 request 合法域名列表中」

现象:开发者工具里接口全红,真机预览也请求不到。原因:小程序默认校验请求域名,本地 http 或未备案域名会被拦。解决:开发阶段在「详情 → 本地设置」勾选「不校验合法域名」;上线前把后端域名配到小程序后台,必须 https 且已备案,配置后重新编译。

4.2 导入 SQL 报「Cannot add foreign key constraint」

现象:执行 schema.sql 中途报外键错误。原因:建表顺序不对,子表先于父表创建,或字段类型/字符集不一致。解决:按依赖顺序导入,先父表后子表;检查外键字段和主表主键的类型、字符集是否完全一致,utf8mb4 对 utf8 也会报错。

4.3 放号后库存变成负数

现象:后台看库存 -3,超卖了。原因:扣减逻辑写成了先查后减,并发下判断失效。解决:改成UPDATE ... WHERE stock > 0的原子写法,检查 affectedRows;如果用了 Redis 预扣,要保证 Redis 和数据库最终一致,加对账补偿。

4.4 用户反馈「约上了但查不到记录」

现象:用户截图显示预约成功,但我的预约列表为空。原因:扣库存和写预约单不在同一事务,扣减成功但插入失败,或者插入时用户ID取错。解决:把两步包进事务,任一失败回滚;检查写单时 user_id 是否来自 token 解析而非前端传参,前端传参可被篡改。

4.5 核销时提示「预约状态异常」但用户确实约了

现象:现场核销失败,用户着急。原因:用户之前取消过又重新预约,旧记录 status 是 canceled,扫码扫到旧码;或者唯一索引设计导致新记录没插进去。解决:核销时按「用户ID + 时段ID」查最新有效记录,而不是只按预约码;检查唯一索引是否把 canceled 也算进去了。

5. 上线前压测与库存对账:两个能救命的技巧

源码跑通只是及格线,放号场景必须压测。我一般用autocannon或wrk对扣减接口打并发,重点看两件事:库存有没有超卖、响应时间有没有雪崩。命令很简单:

# 对预约接口打 200 并发,持续 30 秒 npx autocannon -c 200 -d 30 -m POST -H "Content-Type: application/json" \ -b '{"slotId":1,"userId":1001}' http://localhost:3000/api/appointment

逻辑说明:-c 200是 200 个并发连接,-d 30持续 30 秒,-b是请求体。跑完看Non-2xx responses和数据库最终库存。参数说明:userId 要每次不同,否则会被幂等键挡住,测不出真实并发;slotId 选一个库存充足的时段,先测扣减逻辑再测超卖边界。

压测后必做库存对账:初始库存 - 成功预约数 = 剩余库存,差一条都要查。我习惯在服务端加一个对账脚本,每天凌晨跑一次,把vaccine_slot.stock和appointment表里 booked 状态的数量比对,不一致就告警。这个习惯救过我一次——某次 Redis 预扣和数据库不同步,对账脚本提前发现,没等到用户投诉。

最后一个技巧:把「放号时间」做成配置项而不是硬编码。社区接种点经常临时调整放号时间,硬编码意味着改代码重新发版。我在config表里加一行release_time,管理后台可改,服务端定时任务读配置触发放号。这个改动不大,但上线后运维省心很多。做这类系统,业务灵活性往往比技术炫技更重要,希望帮到你。

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

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

国奖AI竞赛源码拆解:从工程骨架到答辩闭环的通用模板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:17:10

GPS Android底层驱动全链路解析:从NMEA数据到HAL/JNI回调

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:17:01

Context Mode 实战:用 SQLite FTS5 与 MCP 构建上下文窗口管理骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:15:37

醋理大师糟粕醋口碑好吗,规模怎么样

一碗酸辣鲜香的糟粕醋火锅&#xff0c;正在从海南的街头巷尾走向全国餐桌。社交平台上&#xff0c;关于这道风味的话题热度持续攀升&#xff0c;越来越多的餐饮门店把它写进菜单&#xff0c;越来越多的外地食客开始好奇这口令人念念不忘的酸。然而热潮之下&#xff0c;真实的困…

作者头像 李华
网站建设 2026/9/25 4:13:05

应用类加载器全解析:从双亲委派到依赖冲突排查

先从一个很常见的现象说起。不知道你有没有遇到过这种情况&#xff1a;一个依赖明明已经放进去了&#xff0c;ClassNotFoundException却还是无情地砸下来&#xff1b;或者两个同名的类在项目里都存在&#xff0c;程序却“诡异地”加载了其中某一个&#xff0c;你翻遍代码也找不…

作者头像 李华