微信小程序疫苗预约系统实战:从设计到上线全复盘 简介预约系统是连接服务提供方与用户的数字化桥梁其核心在于将线下排队转化为线上数字化流程。在技术实现上需要处理高并发场景下的资源竞争例如疫苗库存的扣减必须依赖原子操作以保证数据一致性。这类系统的技术价值不仅在于提升预约效率更在于通过微信生态的订阅消息、登录授权等能力构建完整的用户触达链路。在社区卫生服务中心等场景中一套轻量级全栈预约系统能显著降低管理成本。本文完整复盘了基于微信小程序与Spring Boot的疫苗预约系统涵盖数据库设计、前端交互、后端接口以及Redis并发扣减库存等核心环节并分享了上线过程中的实战踩坑经验为类似业务提供了可参考的工程实践路径。 去年帮一个社区卫生服务中心落地了一套疫苗预约接种系统最近终于把源码整理打包了。这篇文章我打算把整个项目从设计到上线的过程完整复盘一遍包括微信小程序端的页面实现、后端接口设计、疫苗库存扣减的并发处理还有上线审核时踩过的坑。如果你正准备接手类似的项目或者想找一份完整的小程序全栈项目练手这里面的设计思路和解法应该能帮你省不少事。这个系统的核心价值很简单把原来电话预约、现场排队的流程搬到微信里居民扫码就能看到附近接种点的库存和可约时段选好时间提交预约到点直接去接种。对接种点来说排班、核销、统计都能在后台完成不用再拿纸质登记本翻来翻去。1. 项目整体设计先想清楚再动手写代码1.1 为什么选择微信小程序作为预约入口这个选择其实没有太多犹豫。社区卫生服务中心面对的是本地居民微信的渗透率摆在那里小程序扫码即用不用下载App也不用注册新账号。相比做一个独立的App小程序的开发成本低更重要的是微信自带订阅消息能力预约成功提醒、接种前一天提醒都能直接触达用户这是传统网页预约做不到的。还有一个现实原因维护成本。这类系统一般没有专职的运维团队小程序前后端一体后端接口固定微信的审核体系虽然严但一旦上线后几乎是稳定运行。我见过很多单位用问卷星做报名到后面数据乱得没法看疫苗库存和实际预约对不上。小程序至少能把预约、库存、核销这条链路串起来。技术选型上小程序端用原生开发没有引入uni-app或者Taro。为什么因为这个项目的页面不算复杂原生足够而且原生小程序对微信新特性的跟进最快比如订阅消息、手机号快速验证这些能力原生改起来最顺手。后端用Spring Boot 2.7MyBatis-Plus做数据访问MySQL 8.0存储Redis处理库存扣减的并发。这套组合很成熟招人好招出了问题网上一搜一大把解决方案。1.2 系统角色与功能模块划分整个系统分三个角色普通用户、接种点管理员、系统管理员。用户端就是微信小程序管理端是Web后台。普通用户需要的能力是查看疫苗列表、查看接种点位置、选择可约时段、填写受种者信息、确认预约、取消预约、查看历史记录。接种点管理员需要的是维护本接种点的疫苗库存、设置每日放号时段、核销到店用户的预约单、查看当日预约列表。系统管理员则负责搭建整个框架管理接种点账户、维护疫苗目录、查看全局统计报表。功能模块用表格列出来就是下面这样模块用户端接种点管理员系统管理员疫苗信息浏览支持支持维护本点库存维护全局疫苗目录接种点查询支持支持维护本点信息管理接种点账户时段预约支持设置排班和放号量查看全局排班预约核销不支持支持扫码/手动核销支持数据统计查看个人记录查看本点统计查看全局统计这样拆完前后端接口的边界就清晰了。用户端只调用户相关的接口管理端只调管理相关的接口权限通过token里的角色字段控制后端用拦截器统一校验。1.3 核心预约流程梳理这个流程我画了不少版本最后定下来的核心链路是用户选择疫苗然后选择接种点查看该接种点剩余号源选择一个时段填写受种者信息提交预约后端扣减库存预约成功微信订阅消息触达到店后接种点管理员核销状态变成已完成。这里有一个很重要的业务点接种的是受种者不一定等于登录微信的那个人。比如父母帮孩子预约登录的是父母的微信实际接种的是孩子。所以预约单里必须有受种者姓名、身份证号、出生日期这些字段而不是直接拿微信用户的实名信息。这个细节在数据库设计的时候就要考虑进去否则后面改表很痛苦。另外一个关键点是时段和库存的关系。疫苗预约和普通商品预约不一样疫苗有批次、有剂次比如乙肝疫苗要打三针第二针和第一针间隔一个月。所以设计时疫苗表里要有剂次字段预约时用户要选择当前是第几剂系统根据间隔天数做校验防止用户在第一针还没打的情况下直接约第二针。2. 数据库设计预约系统的地基怎么打2.1 用户、疫苗、接种点三类基础表数据库是整个系统的地基我先把基础表的设计讲清楚。用户表的核心字段是openid、姓名、手机号、身份证号、出生日期、地址。openid是微信唯一标识必须加唯一索引。这里补充说明一下openid是用户在当前小程序下的唯一标识同一个用户在不同小程序里openid不同这个字段是登录态的核心。疫苗表的核心字段是疫苗名称、生产厂家、剂次、间隔天数、适用年龄段、疫苗类别一类免费还是二类自费、库存数量、状态。这里要特别注意间隔天数这个字段它是业务校验的关键。比如某疫苗第二剂需要间隔28天用户在预约第二剂时后端要查这个用户同种疫苗第一剂的接种时间如果没到28天就直接拒绝。接种点表相对简单主要字段是名称、地址、经纬度、联系电话、工作时间。经纬度字段要特别注意使用DECIMAL类型不要用DOUBLE不然计算距离时会有精度问题。我在项目里用的是DECIMAL(10,6)精度足够。2.2 预约单表与时段设计预约单表是整个系统逻辑最复杂的表。核心字段包括预约单号、用户ID、受种者姓名、受种者身份证号、疫苗ID、接种点ID、时段ID、预约日期、剂次、状态。预约单号我建议用业务号而不是自增ID方便后续对账和用户咨询时查询。我的规则是YYMMDD接种点ID四位随机数生成后做唯一索引。时段表的设计要单独说。最开始我设计的是给每个接种点每天生成一组可用时段比如上午8:00-9:30放20个号9:30-11:00放20个号下午14:00-16:00放30个号。时段表的核心字段是接种点ID、日期、开始时间、结束时间、总配额、剩余配额、版本号。剩余配额这个字段是关键。你可能会想为什么不直接数预约表里的记录数来判断还有没有号原因很简单疫苗库存是有限的而预约记录是无限的如果删掉一条取消的记录count就变了。而且用Redis原子扣减剩余配额效率比count判断高得多。这里我先不做具体代码展示后面专门讲并发处理时再展开。2.3 状态管理与索引优化预约单的状态是典型的有限状态机。我定义了五个状态待确认0、已预约1、已完成2、已取消3、已过期4。待确认是用户提交后、管理员确认前的中间态实际上如果系统不做人工审核待确认可以直接跳过用已预约就行。但考虑到某些特殊疫苗需要人工审核资质我把待确认状态保留下来没有审核流程的接种点可以设置成提交后直接转已预约。索引优化方面有几个查询频率非常高的组合一定要建索引user_id 预约日期这是用户查自己预约记录的site_id 预约日期这是接种点管理员查看当日预约列表的user_id 疫苗ID 剂次这是校验间隔天数时用的。我的经验是凡是出现在WHERE条件里且区分度高的字段都要考虑建复合索引否则数据量到了几万条之后慢查询会非常明显。3. 微信小程序端核心页面实现3.1 登录授权与手机号快速获取小程序端的登录流程现在和早年不一样了。现在推荐的方式是wx.login拿到code传给后端后端再调用微信的jscode2session接口换openid。注意这个code有效期只有五分钟而且只能用一次所以前端拿到code后要立刻传给后端。用户手机号获取现在也不能用getUserInfo那一套了微信把手机号接口收紧了必须用button组件的open-typegetPhoneNumber才能触发获取。获取到的是加密数据后端要用session_key解密才能拿到真实手机号。这里有个细节button组件获取手机号时用户如果拒绝授权bindgetphonenumber回调里的detail.encryptedData是空的一定要做空值判断否则前端解析undefined会直接报错。还有个体验上的优化点不需要强制用户一进页面就授权登录。我的做法是让用户先浏览疫苗列表和接种点信息等到真正提交预约的时候才引导登录。这样能明显降低用户的流失率尤其是那些只是随便看看的人。3.2 疫苗列表与接种点地图展示疫苗列表页面用卡片式设计每张卡片展示疫苗名称、剂次、剩余库存、适用年龄段。这里要说明一下剩余库存这个数据不需要实时刷新每次进入页面请求一次就够了没必要用WebSocket推。倒是库存数量不多的时候比如低于10前端可以在卡片右上角加一个余量少的角标引导用户尽快预约。接种点地图展示这块微信小程序原生提供了一个map组件可以直接用来标注接种点位置。你只需要给后端传一个接种点列表每个接种点包含经纬度map组件的markers属性就能渲染出地图标记。有用户在微信开发者社区问能不能用天地图我的实践是map组件的底层地图服务是微信封装的你不能直接换天地图但可以通过设置markers的经纬度来展示任意坐标的点位。如果需要天地图的服务只能用web-view嵌天地图的网页但体验不如原生map组件流畅建议普通展示场景直接用map组件就够了。3.3 预约表单联动单选框与时段选择预约表单是前端交互最密集的页面。第一个联动是疫苗选择不同疫苗对应不同的可预约剂次。这里我用了radio-group组件注意radio的value属性是字符串类型的如果你在后端约定的是数字1、2、3前端绑定的时候一定要做类型转换否则提交到后端会出问题。这个坑我印象很深某次联调时后端一直报参数类型错误查了半天发现是radio的value是1而不是1。第二个联动是接种点和时段的关系。用户先选接种点再查这个接种点的可约时段。时段按钮我用一个grid布局展示每行四个每个时段显示08:00-09:30 余20。点击时段按钮后会有一个选中态再次点击可以取消。前端逻辑用data-current-slot记录当前选中的时段ID切换时段时清空已选状态这样即使本地上次选择错了重新选择也不会把脏数据带到后端。这里特别说一下预约表单的校验。受种者姓名、身份证号、出生日期这三项一定要做前端校验和后端校验双重校验。身份证号用正则校验18位格式出生日期根据身份证号反推不要拿过去的日期当出生日期处理否则婴儿预约疫苗时年龄会算错。3.4 订阅消息实现预约状态提醒订阅消息是整个预约流程体验的重要环节。预约成功后用户最关心的就是我约上了没有和我什么时候去打。微信的订阅消息恰好能做这个事。关键点在于订阅消息是一次性订阅用户点一次授权只能收到一条消息。所以要在用户最可能同意的地方触发请求我的做法是在用户点击确认预约按钮、提交接口调用成功之后再调wx.requestSubscribeMessage。这样用户已经完成预约动作心理上更容易接受接收通知。需要用到的订阅消息模板有两类预约成功通知和接种日提醒。预约成功通知是提交预约后立即发送接种日提醒是在预约日期的前一天晚上发送。注意一次性订阅消息不能在用户完全没有授权的情况下调用所以接种日提醒需要用户在预约时再点一次授权我会在预约成功页用一个明显的按钮引导用户开启到日提醒。4. 后端接口与预约并发处理细节4.1 接口清单与统一返回格式后端接口按照模块划分整体结构比较清晰。登录模块只提供一个POST /api/user/login入参是code返回token和用户基本信息。疫苗模块提供GET /api/vaccine/list返回疫苗列表GET /api/vaccine/detail返回某个疫苗的详细说明。接种点模块提供GET /api/site/list返回接种点列表。预约模块的接口是核心我列一下接口方法说明/api/slot/availableGET查询某接种点某日期的可约时段/api/appointment/createPOST创建预约单/api/appointment/cancelPOST取消预约/api/appointment/detailGET查询预约详情/api/appointment/mineGET查询我的预约列表所有接口统一返回结构code、message、data。code为0表示成功非0表示失败。这样前端只需要在request封装里统一对code做判断不用每个页面各自处理错误逻辑。后端还有一个全局异常处理器把参数校验异常、业务异常、系统异常分别映射到不同的code前端可以根据code做不同提示。4.2 疫苗库存原子扣减与防重复提交这是整个系统技术含量最高的部分。先说说我的方案为什么从数据库锁升级到Redis。最开始我直接在预约创建接口里用select ... for update锁住时段记录先检查剩余配额再判断能否预约。这个方案在小流量下没问题但问题在于如果同一时段的并发请求量大会出现比较多锁等待而且死锁的排查很麻烦。后来我改用Redis做库存预扣预约成功后再异步同步到MySQL。核心代码是用Lua脚本保证原子性如下-- KEYS[1]: 时段库存key例如 slot:stock:1001 -- ARGV[1]: 扣减数量 local stock tonumber(redis.call(get, KEYS[1])) if stock false then return -1 -- 库存key不存在说明时段已过期或未初始化 end if stock tonumber(ARGV[1]) then return 0 -- 库存不足 end redis.call(decrby, KEYS[1], ARGV[1]) return 1Lua脚本在Redis中是原子执行的不会出现多个请求同时读到剩余库存为1然后都扣成功的情况。执行完Lua脚本后如果返回1业务层继续创建预约单如果返回0直接抛号源不足异常。光有库存扣减还不够还要防同一个用户对同一个时段重复提交。我用的是数据库唯一索引预约表上加了一个联合唯一索引字段是user_id slot_id。这样即使前端快速点了两次提交后端第二次插入时也会触发唯一约束异常业务层捕获后返回请勿重复提交。加上Redis里用用户ID做幂等键双保险。4.3 预约状态流转与过期处理预约状态流转要做到可追踪。用户提交预约后状态是已预约到店接种后管理员核销状态变成已完成用户在预约日期之前可以取消状态变成已取消。过期的处理容易遗漏。如果用户约了某天的号但当天没来接种点管理员也没操作这条预约单会一直停在已预约状态而时段库存已经扣掉了造成浪费。我的解法是写一个定时任务每天凌晨校验所有预约日期小于当天且状态仍为已预约的预约单把状态批量更新为已过期同时回补时段库存。回补库存时要加个判断防止手动核销之后又被定时任务重复回补。定时任务我用了Spring自带的Scheduled注解配置成每天凌晨两点执行。因为这种任务不需要实时性没必要引入XXL-Job之类的分布式调度框架。注意如果系统部署了多个实例一定要给定时任务加个执行锁比如用Redis的setnx做一个分布式锁防止多个实例同时扫描同一批数据导致重复回补。5. 管理后台与上线部署实操记录5.1 管理端功能与接口设计管理端我用的是Vue3 Element Plus前后端分离。管理端的原则是和用户端的接口分开单独走一套admin路径并通过拦截器做角色权限校验。比如管理端的预约列表接口是POST /api/admin/appointment/list普通用户的token调用这个接口会被拦截器直接拒绝。管理端最核心的功能是排班。接种点每天放多少号什么时间段都要提前设置。我给管理员做了一个模板机制先设置一周的默认排班规则比如周一上午放30个号下午放40个号周二上午放20个号下午放50个号。然后系统根据模板自动生成未来两周的时段数据管理员可以针对某一天做微调比如国庆放假就把那几天的时段状态置为停用。核销功能是另一个重点。到店用户出示预约码管理员在管理端输入预约单号或扫描二维码系统展示预约详情管理员确认后点击核销。为了省事我直接在管理端加了一个根据手机号后四位查询今日预约的功能这样即使预约码加载不出来的用户也能快速找到记录。5.2 小程序域名、HTTPS与审核上线小程序上线有几个硬性要求提前不准备好审核的时候会卡很久。第一request合法域名必须是HTTPS而且域名要ICP备案。这里说的ICP备案是针对域名本身的服务器在国内就必须备案这是绕不过去的。第二小程序类目选择。疫苗预约属于医疗服务类微信把这类内容管得很严。个人主体根本没法通过审核必须用企业或事业单位主体而且需要提供相应的资质文件比如医疗机构执业许可证。每个地区的具体审核口径会有差异但提前把资质材料准备好是必须的。部署方面我是用Nginx做反向代理后端跑在Spring Boot内嵌的Tomcat上。SSL证书用的免费DV证书配置好Nginx后别忘了把443端口在安全组里打开。还要配置HTTP到HTTPS的301跳转否则用户用http访问会报错。我踩过这个坑小程序开发工具里请求http地址能通但真机调试时被微信拦截报url not in domain list调试了很久才发现是证书问题。5.3 服务器容器化部署记录我习惯用Docker Compose做部署这样MySQL、Redis、后端服务都能一键启动。以下是docker-compose.yml的关键配置version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: your_password MYSQL_DATABASE: vaccine_appointment volumes: - ./mysql-data:/var/lib/mysql ports: - 3306:3306 redis: image: redis:7.0 ports: - 6379:6379 backend: build: ./backend depends_on: - mysql - redis ports: - 8080:8080 environment: SPRING_PROFILES_ACTIVE: prod这里介绍一下我的经验MySQL和Redis的数据目录都挂载到了宿主机上否则容器一旦重建数据就全没了。数据库备份用crontab每天凌晨备份一次保留最近七天的备份文件。生产环境不要用默认密码这个应该是常识了但我见过太多上线一个月数据库被扫的案例这里多提醒一句。6. 实战中踩过的坑问题与排查速查6.1 微信小程序端典型问题真机正常但开发者工具白屏。这个问题的根源是基础库版本和ES6转ES5的设置不一致。开发者工具的ES6转ES5选项如果关闭某些低版本基础库的手机上会直接白屏。建议开启ES6转ES5同时把调试基础库版本调到和线上版本一致。radio组件的value类型问题。radio的value在WXML里绑定后事件回调里拿到的是字符串而后端接口如果用的是JSON格式数字和字符串混淆会导致后端反序列化失败。解决办法是在提交前统一用Number()转换或者后端用String接收后在业务层转换。推荐在后端转这样即使前端漏了也还能兜住。顶部导航栏高度适配。不同机型的导航栏高度不一样iPhone X以上的刘海屏和普通安卓机差异很大。我封装了一个获取胶囊按钮位置的方法根据胶囊按钮的top和系统状态栏高度计算自定义导航栏的高度。这个在自定义导航栏的场景下是必做的不做的话页面布局会错乱。6.2 后端与数据库典型问题并发扣库存导致的超卖。这个问题在初期用纯MySQL方案时遇到过后来换Redis Lua脚本解决了。排查思路是压测时发现库存扣到负数看日志发现多个请求同时通过了库存大于0的校验。所以最终方案一定是原子操作不能在Java代码里先查再写必须把这个过程放到数据库或Redis的原子操作里。时区问题导致预约日期偏移。这个比较隐蔽。服务器时区如果是UTC而MySQL连接的serverTimezone没配对存进去的日期会比预期早八个小时。用户在首页看到某天有号提交后生成的预约日期却变成了前一天。解决方法是统一时区JVM启动参数加-Duser.timezoneAsia/ShanghaiMySQL连接串加serverTimezoneAsia/Shanghai并在代码里不要依赖数据库的now()统一用Java的LocalDateTime生成。同一用户重复提交预约单。除了数据库唯一索引兜底前端也要做好按钮防重复。我的做法是提交后立即把按钮置为disabled状态等接口返回后再恢复同时加载一个loading动画。这样既照顾了用户体验又减轻了后端压力。6.3 源码目录结构说明与二次开发建议源码压缩包解开后主要分两个目录miniprogram是微信小程序前端server是Spring Boot后端。前端目录按页面分模块每个页面一个文件夹公共组件放components目录公共工具方法放utils目录。后端按业务分包controller、service、mapper、entity各一层业务包裹清晰找代码不会迷路。如果你准备在这个源码基础上做二次开发我有几个建议。第一把硬编码的配置项抽取到配置文件里比如接种点的工作时间、某个疫苗的间隔天数这些写成常量后维护成本低很多。第二考虑加入微信支付能力现在的版本预约是免费的如果要做二类疫苗的在线缴费需要在小程序里开通微信支付并在预约创建后增加支付回调的处理逻辑。第三建议把admin管理端也整合到同一套用户体系里不要单独维护一套管理员账号用RBAC模型做权限控制后面扩展多角色会方便很多。我在实际开发中还有一个体会这类预约系统业务逻辑本身并不复杂难的是把每一个边界情况都想清楚。比如用户取消预约后库存怎么回补、预约过期后消息怎么通知、一个微信号能不能给多个受种者预约、同一受种者在同一天能不能预约两个接种点这些问题在写代码之前就应该在文档里定好规则不然等代码写完了再补逻辑改起来非常痛苦。最后再分享一个小技巧上线后一定要有日志监控。我在关键业务节点打了操作日志包括用户提交预约、取消预约、管理员核销、库存回补每条日志都带上时间和操作人ID。一旦用户投诉或者数据对不上顺着日志能很快定位到问题发生的时间和源头比自己猜要快得多。这套系统的源码我已经整理成zip压缩包了里面的代码可以正常编译运行接口文档和数据库初始化脚本也一并放在里面了需要的朋友拿到手直接导入就能跑起来本地调试完再根据实际情况做定制修改能省下从零开始写的两到三周时间。本文还有配套的精品资源点击获取

相关新闻

最新新闻

Python安装教程:从官方下载到环境配置与常见问题排查

Python安装教程:从官方下载到环境配置与常见问题排查

最近不少读者在后台问我:Python 到底怎么装?为什么装完之后在命令行输入 python 没有反应?还有人在评论区问,网上那些“一键激活、永久使用、附激活码”的 Python 安装包能不能用? 先说结论:Python 本身就…

2026/8/29 8:41:30
Java从入门到实战:历史演进、核心概念与基础语法全解析

Java从入门到实战:历史演进、核心概念与基础语法全解析

如果你正打算学 Java,或者已经在简历上写过“熟练掌握 Java”,但心里偶尔会犯嘀咕:Java 到底是怎么来的?它凭什么这么多年还是招聘市场上的主力?为什么学了语法之后,一到面试题、一到项目实战还是会卡壳&am…

2026/8/29 8:41:30
Ubuntu20.04下Amber22 GNU版编译安装全流程详解

Ubuntu20.04下Amber22 GNU版编译安装全流程详解

之前给不少课题组配置过分子动力学环境,发现 Amber 的编译安装一直是很多同学头疼的环节。尤其是新版本 Amber22 发布后,依赖关系、编译器选择、CUDA 开关都比老版本复杂了不少。网上资料虽然多,但大多要么只讲 Intel 编译器,要么…

2026/8/29 8:41:30
知识库问答Agent工程实践:从RAG检索到部署排错全链路

知识库问答Agent工程实践:从RAG检索到部署排错全链路

在大型云厂商的 AI 项目里做过一轮端到端开发之后,通常会对一个问题印象深刻:真正让 AI 应用难以落地的,往往不是模型选型,而是把模型接进业务过程里的工程链路。需求拆解、数据清洗、文档分块、检索召回、提示词构造、结果评估、…

2026/8/29 8:41:30
大数高次同余方程求解:从GMP、CRT到BSGS的算法实战

大数高次同余方程求解:从GMP、CRT到BSGS的算法实战

1. 项目概述:当“大数”遇上“高次同余” 最近在折腾一个跟密码学和数论相关的项目,核心就是处理“大数计算”下的“高次同余方程”。这听起来有点学术,但说白了,就是当数字大到计算机常规数据类型(比如64位整数&#…

2026/8/29 8:41:30
微信小程序疫苗预约系统实战:从设计到上线全复盘

微信小程序疫苗预约系统实战:从设计到上线全复盘

简介:预约系统是连接服务提供方与用户的数字化桥梁,其核心在于将线下排队转化为线上数字化流程。在技术实现上,需要处理高并发场景下的资源竞争,例如疫苗库存的扣减必须依赖原子操作,以保证数据一致性。这类系统的技术…

2026/8/29 8:36:30