1. 项目概述
去年参与了一个市级疫苗接种管理平台的开发,让我深刻体会到传统预约方式的痛点。社区卫生服务中心每天要处理上百个电话预约,工作人员手忙脚乱记录信息,经常出现重复预约、时间冲突的情况。我们团队用SpringBoot+Vue重构的这套系统,上线后预约效率提升了300%,今天就和大家分享这个实战项目的完整开发思路。
这个系统核心解决三个问题:一是将线下排队转为线上自助预约,二是实现疫苗库存与预约数据的实时联动,三是建立完整的接种者健康档案。采用前后端分离架构,后端用SpringBoot快速搭建RESTful API,前端用Vue构建响应式管理后台和移动端H5页面,数据库选用MySQL 5.7保证事务一致性。
2. 技术选型解析
2.1 后端技术栈
选择SpringBoot 2.3.4主要考虑三点:一是内嵌Tomcat简化部署,二是Starter依赖自动配置省去大量XML配置,三是与MyBatis的完美整合。实际开发中我们用到的关键依赖包括:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
特别提醒:SpringBoot默认使用Jackson,但国内项目普遍习惯Fastjson,需要手动配置HttpMessageConverters。我们遇到过日期序列化格式不统一的问题,最终在WebMvcConfigurer中统一配置了日期格式。
2.2 前端技术方案
采用Vue 2.6 + ElementUI的组合主要基于以下考量:
- 组件化开发适合多端适配(管理后台+H5)
- Axios拦截器方便统一处理HTTP异常
- Vuex管理全局状态如用户登录信息
一个踩坑经验:ElementUI的日期时间选择器在移动端表现不佳,我们最终用vant的DatetimePicker组件替代了H5端的日期选择。
2.3 数据库设计要点
MySQL表设计遵循几个原则:
- 预约表与用户表分离,通过user_id关联
- 疫苗库存单独建表,每日凌晨跑批更新
- 接种记录包含批次号等追溯信息
核心的预约表结构如下:
sql复制CREATE TABLE `vaccine_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`vaccine_id` int(11) NOT NULL,
`site_id` int(11) NOT NULL COMMENT '接种点ID',
`appoint_date` date NOT NULL,
`time_slot` varchar(20) NOT NULL COMMENT '时间段',
`status` tinyint(4) DEFAULT '0' COMMENT '0-待确认 1-已预约 2-已完成 3-已取消',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_date_site` (`appoint_date`,`site_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 预约业务逻辑
预约流程的核心是并发控制,我们采用MySQL乐观锁解决超订问题。关键代码片段:
java复制@Transactional
public ApiResult makeAppointment(Long userId, AppointmentDTO dto) {
// 检查库存
VaccineStock stock = stockMapper.selectForUpdate(dto.getVaccineId());
if (stock.getAvailable() <= 0) {
return ApiResult.fail("该疫苗已约满");
}
// 扣减库存
int update = stockMapper.reduceStock(dto.getVaccineId(), stock.getVersion());
if (update == 0) {
throw new ConcurrentBookingException("预约冲突请重试");
}
// 创建预约记录
VaccineOrder order = new VaccineOrder();
BeanUtils.copyProperties(dto, order);
order.setUserId(userId);
order.setStatus(OrderStatus.BOOKED.getCode());
orderMapper.insert(order);
// 发送短信通知
smsService.sendAppointmentSuccess(user.getPhone(), order);
return ApiResult.success(order);
}
重要提示:在高并发场景下,单纯靠数据库锁性能会成为瓶颈。我们最终引入Redis分布式锁+本地缓存的二级校验机制,将峰值QPS从200提升到2000+。
3.2 动态时间分段算法
接种点的工作时间不固定,我们设计了可配置的时间段生成策略:
java复制public List<TimeSlot> generateTimeSlots(LocalDate date, int siteId) {
// 获取接种点当日配置
SiteSchedule schedule = scheduleService.getSchedule(siteId, date);
List<TimeSlot> slots = new ArrayList<>();
LocalTime start = schedule.getStartTime();
LocalTime end = schedule.getEndTime();
// 每30分钟一个时段
while (start.isBefore(end)) {
LocalTime slotEnd = start.plusMinutes(30);
int booked = orderMapper.countByTime(siteId, date, start, slotEnd);
slots.add(new TimeSlot(
start.format(DateTimeFormatter.ISO_TIME),
slotEnd.format(DateTimeFormatter.ISO_TIME),
schedule.getMaxPerSlot() - booked
));
start = slotEnd;
}
return slots;
}
4. 安全与性能优化
4.1 安全防护措施
- 接口防刷:基于Guava RateLimiter实现方法级限流
- 数据加密:敏感字段如身份证号使用AES加密存储
- 权限控制:基于Spring Security的RBAC模型
- XSS防护:自定义HttpServletRequestWrapper过滤危险字符
4.2 性能调优实战
通过Arthas诊断发现两个性能瓶颈:
- 疫苗列表查询N+1问题:改用MyBatis的
标签优化 - 接种点详情频繁查询:添加Caffeine本地缓存
最终压测指标:
- 预约接口平均响应时间:78ms
- 系统最大并发用户数:5000+
- 数据库QPS:1200+
5. 异常处理与监控
5.1 事务一致性保障
在分布式环境下,我们采用最终一致性方案处理预约超时:
- 引入RocketMQ事务消息
- 建立预约状态检查任务
- 设计补偿机制处理异常订单
java复制@Scheduled(cron = "0 0/5 * * * ?")
public void checkExpiredOrders() {
List<VaccineOrder> orders = orderMapper.selectExpiredOrders();
orders.forEach(order -> {
// 释放库存
stockMapper.returnStock(order.getVaccineId());
// 更新状态
order.setStatus(OrderStatus.EXPIRED.getCode());
orderMapper.updateById(order);
// 发送通知
smsService.sendAppointmentCancel(order.getUserPhone());
});
}
5.2 监控体系建设
- 使用Prometheus+Grafana监控JVM指标
- 通过SkyWalking追踪分布式链路
- 关键业务指标埋点:
- 每日预约成功率
- 各接种点负载情况
- 疫苗库存预警
6. 部署与运维
6.1 容器化部署方案
采用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- ./mysql/data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
6.2 灰度发布策略
通过Nginx实现AB测试:
nginx复制upstream backend {
server 172.18.0.2:8080 weight=9;
server 172.18.0.3:8080 weight=1;
}
server {
listen 80;
location /api {
proxy_pass http://backend;
}
}
7. 项目演进方向
- 智能推荐:根据用户历史记录推荐接种点和时间
- 接种反应跟踪:添加不良反应上报功能
- 开放平台:提供API对接区域医疗系统
- 大屏展示:实时监控区域接种情况
这个项目让我深刻体会到,好的系统设计必须兼顾技术先进性和业务实用性。特别是在医疗健康领域,每个功能点都可能关系到用户体验甚至公共安全。建议后续开发者重点关注预约系统的公平性和容灾能力,我们曾因服务器宕机导致当天所有预约失效,后来通过多活架构解决了这个问题。