☰
微信小程序与Spring Boot构建儿童疫苗预约系统:库存、并发与闭环设计
2026/9/30 4:34:02 网站建设 项目流程

我最初想做这个项目,是因为一次陪家里人去社区接种门诊的经历。登记台前队伍排得很长,好几个家长抱着孩子挤在一起,护士一边翻纸质接种本,一边扯着嗓子确认疫苗库存。有个妈妈的接种本不知道什么时候弄丢了,当场急得满头汗。这种每天都在重复的混乱场面,完全可以通过一套系统提前化解。于是就有了这个基于微信小程序的儿童预防接种预约管理系统:家长在手机上查疫苗、选时段、提交预约,门诊端负责核销、管库存、看统计,后台统一维护儿童档案和疫苗批次信息。

这篇文章我会从业务痛点拆起,把技术选型、数据库设计、预约并发控制、管理端闭环、源码目录与论文组织方式一次讲清楚。适合正在做毕业设计的学生、想系统学小程序全栈开发的初学者,也想给基层门诊做信息化参考的从业者。源码和论文的整理方式我会放在最后一章,你可以直接对照着编排自己的项目文档,而不是拿过来就粘贴。

1. 儿童接种预约到底在解决什么痛点

1.1 门诊接种日的真实混乱

很多人把“预约系统”想得很简单,觉得就是做个日历选时间。但到了接种门诊,你会发现真正的麻烦根本不在“选时间”这一步。

先说排队。家长到了之后要先排队做登记,医护人员得核对儿童档案、接种本、本次要打的疫苗,确认没有发热、没有禁忌症,然后才能开单。登记完还要排队体检,量身高体重、听心肺,最后才是排队接种。每一个环节都在消耗门诊的人力,而每个环节的信息全靠纸质记录和口口相传。遇上接种日,门诊大厅基本就是三个队列交叉,护士喊号喊到嗓子哑。

再说信息管理。纸质接种本一旦丢失,既往接种记录就可能彻底断档。门诊电脑里虽然也有记录,但很多基层门诊的表格散落在Excel里,连“这个月该通知哪些孩子来打第二针乙肝”这种最基本的问题,都要靠人工翻记录。你做一个预约系统,表面上解决的是“排队”问题,实际上解决的是登记、库存、通知、溯源一整条链路的效率问题。

1.2 三类使用者和一份需求清单

做这类项目最忌讳一上来就写代码。先把用户分清楚,需求才不会跑偏。这套系统里有三类核心角色:

  • 家长端用户:给孩子建档案、查疫苗计划、约时间、接收提醒、看接种记录。
  • 门诊医护:维护疫苗批次和库存、核销预约、登记每一条实际接种记录、处理退约。
  • 系统管理员:维护基础数据,比如疫苗目录、门诊工作时间、号源参数、用户权限。

三类角色的需求其实很清楚。家长要的是“少排队、不错过”;医护要的是“信息准、操作快”;管理员要的是“数据完整、能统计”。后续所有功能点都应该围绕这三句话展开,而不是为了凑功能而加模块。

1.3 和普通挂号系统最大的区别:免疫程序硬约束

儿童接种预约和医院挂号有个本质区别:不是家长想约哪个科就约哪个科,而是儿童到了什么月龄,就应当接种对应的疫苗,这个顺序由国家的免疫规划程序决定。

比如乙肝疫苗要在出生后24小时内接种第一针,之后满月和半岁各一针;脊灰疫苗有4剂,百白破有4剂,每一剂都有对应的月龄窗口。这个约束意味着系统里必须有一个“月龄计算 + 可接种疫苗判断”的逻辑,而不是简单地让家长选疫苗。

我在项目里把这种规则做成了一个独立的服务端校验模块:根据儿童出生日期算出当前月龄,再对照疫苗目录里每一个疫苗的“推荐月龄下限/上限”,自动生成“该儿童当前可预约的疫苗列表”。家长端展示的永远只是这个结果,而不是全量疫苗目录。这个设计不仅让家长操作简单,也大大减少了医护核销时的纠错成本。

2. 系统架构与技术选型:微信小程序加Spring Boot的组合逻辑

2.1 小程序端用原生还是uni-app

这个决定影响整个项目的开发路径,我建议你先想清楚再做。

原生微信小程序的好处是:调试工具成熟,文档齐全,社区里什么问题都搜得到答案,项目结构对评审老师来说也最直观。对于以“源码+论文”为交付目标的场景,原生实现几乎不会被质疑。页面、组件、生命周期、wx.request这些概念本身就是微信官方体系的,论文里写起来也顺畅。

uni-app的优势是跨端,一套代码能同时出小程序、H5和App,以后想扩展安卓、iOS甚至鸿蒙端确实方便。但它会引入一层编译概念,遇到问题排查链路更长,部分原生能力还是要通过条件编译去处理。我在这个项目里最终选了原生微信小程序,核心原因就是“可维护性优先”:项目要给别人复现,越少黑盒越好。

不过我建议你在做技术选型的时候把uni-app当作备选答案记下来。如果毕设题目明确要求“多端适配”,那就上uni-app;如果题目就是微信小程序,原生更省事。

2.2 后端为什么是Spring Boot加MySQL

后端我选了Spring Boot,搭配MyBatis Plus和MySQL数据库。这个组合在Java生态里几乎是最常见的毕业设计配置,资料多、出问题容易查,而且面试和答辩的时候可聊的东西也足够。

Spring Boot的约定大于配置大幅降低了搭建成本,一个启动类加几个注解就能把项目跑起来。MyBatis Plus把单表CRUD的重复劳动省掉了很多,我可以把精力集中在预约事务、疫苗库存扣减这些真正的核心逻辑上。

MySQL这边,我用InnoDB引擎,事务隔离级别走默认的REPEATABLE READ。对单门诊场景来说,这个级别完全够用。有些人可能会问,为什么不直接上Redis做库存?我的观点是,接种门诊的并发量远没有到秒杀级别,数据库自身的事务和行锁已经能解决防超约问题,引入Redis反而会让前后端联调逻辑变复杂,论文里还得专门解释缓存一致性问题。技术栈不是越多越好,适合自己的场景才是对的。

2.3 管理端以及整体数据流

管理端我用的Vue3加Element Plus,独立成一个后台项目。这样做的最大好处是前后端职责清晰:小程序端只做家长侧操作,管理端只做医护和管理员操作,两者共享同一套后端API。

整体数据流可以这样理解:家长在小程序端提交预约请求,请求带着登录态token到后端,后端先做令牌鉴权,再校验儿童月龄、疫苗库存、时段余量,最后通过事务写入预约记录。接种当天,医护在管理端输入档案编号或扫描预约码完成核销,系统扣减疫苗库存并生成正式接种记录。所有的数据最终都落在MySQL的不同表里,统计模块直接基于这些表做聚合查询。

这套架构虽然简单,但每条链路都很完整,非常适合作为毕设的基线,后续加功能也不会推倒重来。

3. 数据库设计:从儿童档案到接种记录的一张张表

3.1 儿童档案表:出生日期、监护人、既往情况

儿童档案是整个系统的地基,字段设计直接影响后续所有功能。我挑几个关键字段说一下。

第一个是出生日期。这个字段必须精确到天,而且数据库里存日期类型,不能存年龄。原因很简单:免疫程序判断依赖的是“当前日期减去出生日期”的月龄,如果只存“3岁”,系统无法知道这个孩子具体是哪天满3岁,也无法判断是否到了某剂疫苗的接种窗口。

第二个是证件号。这里我建议加唯一索引,同一儿童只能存在一份档案,避免家长重复建档造成数据混乱。

第三个是既往病史和过敏史。接种前筛查必须看这个,比如对鸡蛋过敏的儿童要谨慎接种某些流感疫苗,既往有严重不良反应的儿童需要医生评估。这些信息在预约提交时就要登记,而不是等到门诊现场再问。

儿童档案表核心建表逻辑大致如下:

CREATE TABLE child_profile ( id BIGINT PRIMARY KEY AUTO_INCREMENT, child_name VARCHAR(50) NOT NULL, gender TINYINT NOT NULL COMMENT '1男 2女', birth_date DATE NOT NULL, id_card_no VARCHAR(20) NULL, guardian_name VARCHAR(50) NOT NULL, guardian_phone VARCHAR(20) NOT NULL, allergy_history VARCHAR(255) DEFAULT NULL, past_history VARCHAR(255) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_id_card (id_card_no), KEY idx_birth_date (birth_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

出生日期上的索引很重要,因为“按年龄段筛选应种名单”是后台统计的高频操作。

3.2 疫苗与批次库存:为什么“数量”不能单独成表

绝大多数没有经验的人会把疫苗库存设计成“疫苗一张表,上面放一个库存数字”。这个设计在真实场景里马上会出问题。

同一支疫苗,比如乙肝疫苗,会有多个生产批号同时存在于门诊冰箱里。不同批号的有效期不同,进货时间不同,厂家也可能不同。如果只存一个总数,护士根本不知道当前要消耗的是哪一批,一旦某批疫苗出现质量问题需要召回,你也无法追溯谁接种了这一批。

所以我把疫苗和批次拆成了两张表。疫苗表维护疫苗名称、适用月龄、剂次说明;批次表维护批号、有效期、库存数量、当前状态。核销扣减库存时,遵循“先到期先出”的FEFO原则,优先扣减有效期最近的批次。

批次表的核心字段大致是这样的:

CREATE TABLE vaccine_batch ( id BIGINT PRIMARY KEY AUTO_INCREMENT, vaccine_id BIGINT NOT NULL, batch_no VARCHAR(50) NOT NULL, manufacturer VARCHAR(100), produce_date DATE, expire_date DATE NOT NULL, stock_quantity INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1 COMMENT '1正常 0停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_vaccine (vaccine_id), KEY idx_expire (expire_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

每次做入库操作时新增一条批次记录,库存数量增加;每次接种核销时按FEFO顺序选批次扣减。这样疫苗从入库到消耗的每一个环节都有据可查。

3.3 预约与接种记录:一条预约如何变成一次正式接种

预约表和接种记录表是这条业务链路的出口,也是最容易出设计问题的地方。

预约表的核心字段包括:儿童档案ID、预约日期、预约时段、疫苗ID、批次ID(可以为空,核销时再确定)、状态(待接种/已完成/已取消/爽约)、创建时间。这里有一个关键约束:同一儿童同一天只能存在一条有效预约,通过联合唯一索引来保证。

接种记录表则是一次接种行为的最终落库结果,字段包括:预约ID、儿童ID、疫苗ID、批次ID、接种部位、接种人员、接种时间、不良反应等。预约表的状态在核销后变为“已完成”,同时生成一条接种记录。接种记录不允许修改,只能查看,保证数据的严肃性。

这个设计让“预约”和“实际接种”解耦。你可以预约了不来,也可以来了但医生评估后暂缓接种,系统都能准确记录,不会出现预约成功就算接种完成的错误逻辑。

3.4 六张核心表职责一览

表名核心职责关键字段
child_profile儿童基础档案出生日期、证件号、监护人电话
sys_user系统用户与登录态账号、密码哈希、角色
vaccine疫苗目录疫苗名称、适用月龄、剂次说明
vaccine_batch库存批次批号、有效期、剩余数量
appointment预约单预约日期时间段、状态、儿童ID
vaccination_record接种记录疫苗ID、批次ID、接种时间、人员

这几张表之间通过外键逻辑关联,但没有全加物理外键,保持查询性能。事务一致性由代码层控制。

4. 预约链路里的并发控制与体验设计

4.1 号源容量:不是拍脑袋,是算出来的

很多初学项目的人会把“时段号源”设计成固定数字,比如每个15分钟段放3个号。这样简单,但脱离实际。

号源容量应该根据门诊的接待能力动态计算。假设门诊上午时段从8点到11点共180分钟,现有两个接种台,平均每个儿童完成登记到接种大约需要8分钟,那么理论容量就是180除以8乘以2,约45人。考虑医生可能临时处理其他事务,再打90%的折扣,上午实际放号40人左右。

更细一点,如果把上午拆成12个15分钟段,每段放多少人合适?每段约3到4人。但要注意接种高峰通常出现在9点到10点半之间,放量时可以前紧后松,把多数号源集中在中间段。

我建议你把“每时段容量”做成管理端可配置参数,而不是写死。门诊可以根据当天出诊医生人数、疫苗存量动态调整。这个细节在论文的功能设计里写出来,非常加分。

4.2 防超约的核心操作:乐观锁与唯一约束

预约系统最怕的一个问题是超约,也就是同一个时段被两个人同时抢到最后一个号。解决这个问题的关键不是前端按钮置灰,而是数据库层面的原子操作。

我的做法是在预约提交事务里,先检查是否已存在同一儿童同日预约,然后执行条件更新锁住号源记录。核心SQL大概是:

UPDATE appointment_slot SET booked = booked + 1 WHERE id = #{slotId} AND booked < total

这条语句本身就是原子的,数据库行锁保证同一时刻只有一个事务能成功把booked加1。如果更新影响行数为0,说明号源已满,事务回滚,返回“该时段已约满”。

你可以把它理解成电影院选座:座位是否被占,取决于数据库里那行的状态是否被成功修改,而不是取决于前端页面给人看的剩余票数。

MySQL默认的REPEATABLE READ隔离级别配合行锁,在这个并发量级下完全够用。我在压测环境里模拟过50个线程同时抢同一个时段,最终只有不超过总数的预约成功,没有出现超卖。这个测试结果直接写进了论文的测试章节。

4.3 预约冲突、退约与爽约释放

防超约只是其中一半,另一半是“同一儿童的预约冲突”。

按照常规接种流程,两种不同疫苗通常不建议同一天接种,除非医生有明确判断。所以系统里要做一层校验:同一儿童同一日只能有一条约好的预约。我采用联合唯一索引加应用层预检双重控制,防止并发情况下绕过索引约束导致重复预约。

退约功能也很有必要。家长临时有事要在接种日前一天之前取消,释放号源并更新状态。这里我做了个细节:退约释放的号源不会立即回到可约列表,而是进入“候补释放”池,下一个排队等这个时段的用户可以直接顶上。如果你不想做候补池,至少要在退约后更新号源余量,否则就会出现“系统显示已满但实际有人退了”的情况。

爽约则需要限制。我在档案维度记录爽约次数,连续爽约3次的账号限制预约30天。这个设计能有效减少号源浪费,也是答辩时老师比较喜欢听的一个业务闭环。

4.4 订阅消息:预约通知和接种提醒的触发方式

微信小程序的订阅消息是家长触达的核心渠道。它和公众号模板消息不同,必须用户明确授权后才能发送,而且一次性订阅默认只能发一条。

我的处理是为这个项目申请两个消息模板:一个是“预约成功通知”,在家长提交预约后立即发送;一个是“接种提醒”,在接种日前一天晚上通过后端定时任务批量发送。用户授权时机放在预约提交成功后,请求wx.requestSubscribeMessage时把两个模板ID都带进去,系统会弹出授权面板。

这里有个坑:很多用户会拒绝授权,导致后续提醒发不出去。我的补救办法是在“预约成功”页面加一个“开启接种提醒”按钮,如果检测到用户之前没授权,就再次唤起授权面板。实测下来,二次引导的打开率明显比首次好。另外要注意,真机调试时订阅消息必须在真机上测试,开发者工具里往往会出现权限状态不一致的问题。

5. 管理端闭环:从疫苗入库到核销统计

5.1 疫苗批次入库与有效期预警

管理端不是简单做几个CRUD页面就完事,它要真正帮医护减少出错率。我认为整个管理端里最有价值的功能是疫苗批次的库存管理。

入库时,医护录入疫苗名称、批号、厂家、生产日期、有效期、数量,系统自动校验批号是否重复。列表页会按有效期倒序排列,快要过期的批次用醒目颜色标记。我在项目里加了一条定时任务:每天扫描所有批次,有效期小于30天的自动标记“临期”,小于7天的标记“停用”。停用批次不会出现在可选库存里,直接从源头上避免护士误用。

这个功能我在实地调研时观察到的需求:冰箱最里面总是有几支被遗忘的疫苗,等发现的时候已经过期了。系统主动预警,比人眼检查冰箱可靠得多。

5.2 核销操作:怎么把库存、档案、预约全部联动起来

接种当天,家长到达门诊后,医护在管理端核销预约。我做的第一种方式是用档案编号查询,第二种是在小程序端展示预约详情页,管理端输入预约单号完成核销。

核销不是简单把状态改成“已完成”,它是一系列操作的事务入口。后端要同时完成三件事:

  • 校验该预约状态确实处于“待接种”。
  • 按FEFO原则选中一个有效批次,扣减库存数量。
  • 写入一条完整的接种记录,记录疫苗ID、批次ID、接种人员、接种时间。

这三个动作必须在一个事务里,任何一个失败都整体回滚。我建议核销页面加上二次确认提示:“确定核销该预约并扣减乙肝疫苗库存吗?”因为操作不可逆,误点会造成库存数据和实际不符。

核销完成之后,预约状态变为“已完成”,家长小程序端同步看到“接种完成”的状态和本次接种记录。

5.3 数据统计和应种未种清单

管理端的第三个重点是统计。我用ECharts做了两个核心看板:每日门诊量与疫苗消耗趋势。

每日门诊量很简单,按天聚合预约和核销数据,能直接看出高峰时段,辅助门诊调整放号策略。疫苗消耗趋势则按疫苗和批次统计出库数,和入库数对比,形成库存报表。

更有价值的是“应种未种清单”。这个功能逻辑是:根据免疫程序,筛选出“截至某个日期已经达到某疫苗推荐月龄,但最近一段时间没有对应接种记录”的儿童。生成名单后可以导出Excel,门诊用来做电话催种和通知。这套逻辑在基层公共卫生工作中是刚需,也是我这套系统里商业化潜力最高的模块。

实现上就是一次带月龄计算的多表关联查询,但写的时候要特别注意把月龄判断逻辑写成公共工具类,不要散落在各个查询里。

6. 源码结构、论文组织和答辩准备

6.1 代码仓库的目录长什么样,先跑哪部分

有一个好习惯是:项目根目录按“端”分文件夹,每个端自己能独立说明。我的项目结构大致是这样的:

prevent-vaccine-system/ ├── miniprogram/ # 微信小程序家长端 │ ├── pages/ │ ├── api/ │ └── app.json ├── admin-web/ # Vue3管理端 │ ├── src/ │ └── vite.config.js ├── server/ # Spring Boot后端 │ ├── controller/ │ ├── service/ │ ├── mapper/ │ └── resources/ ├── sql/ # 建表脚本和初始化数据 └── docs/ # 论文和设计文档

拿到项目源码,建议按这个顺序跑通:先导入sql目录的初始化脚本,再启动Spring Boot后端,然后启动Vue3管理端登录,最后用微信开发者工具导入miniprogram目录。前后端接口地址在各自配置文件中修改。

我只提醒一句:不要拿源码直接就当自己项目交。至少要在理解的基础上改一个功能点,比如给库存预警加一个自动通知,或者新增一个“疫苗价格与报销记录”模块。代码可以借鉴,但答辩时老师问两句你就露馅,那是很尴尬的事。

6.2 论文章节建议与配套图表清单

论文部分,我建议按软件工程的标准流程组织,但每一章都要紧扣这个项目:

第一章绪论写背景和意义,重点写儿童接种门诊的现实痛点,不要堆砌大而空的政策语言。第二章需求分析放用例图,把家长端、医护端、管理端三类角色的功能边界画清楚,配合用例描述表。第三章做系统设计,画系统架构图、功能模块图、ER图,时序图重点画预约和核销两条主链路。第四章是数据库设计,把核心表结构和字段含义逐个列清楚。第五章是系统实现,按模块贴核心代码并说明思路。第六章是测试,写功能测试用例和并发测试结果。第七章总结收尾。

图表方面,我认为最容易被忽略也最加分的是“预约时序图”和“核销状态流转图”。很多人的论文里只有ER图和架构图,缺少对主流程的时序描述。这两张图能直接向老师证明你真的理解业务闭环,而不是只会贴代码。

6.3 答辩前我建议你准备的几个问题

根据我这些年做项目和辅助评审的经验,答辩老师对这个题目的高频问题就那么几个,提前准备好答案是稳赚不赔的:

  • 为什么选微信小程序而不做App?回答方向:免安装、微信生态内触达方便、家长使用门槛低。
  • 预约并发怎么解决的?回答方向:数据库乐观锁条件更新加事务,带出压测结果。
  • 疫苗库存怎么防过期?回答方向:批次有效期预警定时任务,FEFO优先扣减。
  • 爽约怎么办?回答方向:状态机设计加连续爽约限制机制。
  • 数据安全性怎么考虑?回答方向:登录态token鉴权、敏感字段脱敏、HTTPS传输。
  • 你在这个项目里独立完成的部分是什么?这个问题必须说得非常具体,比如“月龄计算工具类是我自己推导实现的”,比笼统说“我做了全部功能”可信得多。

最后说点实际操作中的体会

项目做完之后我最大的感受是:儿童接种预约这个题目,看起来小,但业务约束非常多,做完整之后对事务、并发、权限、状态机这些后端基础概念的理解会上一个台阶。如果你正在做类似项目,我建议你优先把“库存批次管理”和“应种未种统计”这两个点做透,它们比单纯的预约页面更能体现你的系统设计能力。

另外一个很实用的小技巧:在小程序真机调试时,务必注意请求域名必须是HTTPS且已在微信后台配置到“request合法域名”白名单里。开发阶段可以勾选“不校验合法域名”先跑通,但上线前漏配这一项,就会出现安卓能请求、iOS大量请求失败这种奇怪现象。iOS对证书链的要求更严格,遇到问题先查证书是否完整,而不是怀疑代码。

如果你想让项目更进一步,可以考虑加一个“接种记录PDF导出”功能,家长可以一键生成并保存孩子的接种证明,这对以后入托入学查验非常实用。扩展方向就留给有需要的同学去实现了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询