1. 项目背景与核心需求
HPV疫苗预约系统是当前医疗信息化领域的热门应用方向。随着全民健康意识提升和HPV疫苗接种普及,传统线下预约方式暴露出诸多痛点:排队时间长、信息不透明、资源分配不均等。我们团队基于Spring Boot+Vue技术栈开发的这套系统,正是为了解决这些实际问题。
从技术选型角度看,Spring Boot作为Java生态中最成熟的微服务框架,提供了快速构建后端API的能力;而Vue.js的渐进式特性则完美适配前端页面的灵活需求。两者通过RESTful API进行数据交互,形成前后端分离的现代化架构。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术栈组成
后端技术栈:
- Spring Boot 2.7.x(稳定版)
- Spring Security(认证授权)
- MyBatis-Plus(数据持久化)
- Redis(缓存与秒杀控制)
- Quartz(定时任务调度)
前端技术栈:
- Vue 3.x(Composition API)
- Element Plus(UI组件库)
- Axios(HTTP客户端)
- Vue Router(路由管理)
- Vuex/Pinia(状态管理)
数据库:
- MySQL 8.0(关系型数据库主库)
- MongoDB(日志存储)
2.2 核心模块划分
-
用户服务模块
- 患者注册/登录
- 接种人信息管理
- 接种记录查询
-
疫苗库存模块
- 疫苗批次管理
- 库存实时监控
- 效期预警
-
预约服务模块
- 可预约时段管理
- 预约规则配置
- 号源动态分配
-
支付对账模块
- 多渠道支付对接
- 财务对账报表
- 退款处理流程
-
消息通知模块
- 短信提醒
- 微信模板消息
- 站内信
3. 关键技术实现
3.1 高并发预约处理
疫苗预约具有明显的"秒杀"特征,我们采用多级缓存策略:
java复制// Redis分布式锁实现
public boolean tryLock(String key, long expireTime) {
String value = UUID.randomUUID().toString();
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
库存扣减采用Redis原子操作:
java复制// Lua脚本保证原子性
String script = "if redis.call('get', KEYS[1]) >= ARGV[1] then " +
"return redis.call('decrby', KEYS[1], ARGV[1]) " +
"else return -1 end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(stockKey),
String.valueOf(quantity));
3.2 动态规则引擎
为适应各地不同的预约政策,我们设计了规则引擎:
java复制// 规则配置示例
@Rule(name = "ageLimit", description = "年龄限制规则")
public class AgeLimitRule implements BaseRule {
@Override
public boolean evaluate(Fact fact) {
Integer age = fact.get("age");
return age >= 9 && age <= 45;
}
}
3.3 实时数据可视化
使用ECharts实现接种数据看板:
vue复制<template>
<div ref="chart" style="width:100%;height:400px"></div>
</template>
<script setup>
import * as echarts from 'echarts'
import { onMounted, ref } from 'vue'
const chart = ref(null)
onMounted(() => {
const myChart = echarts.init(chart.value)
myChart.setOption({
tooltip: {...},
xAxis: {...},
series: [...]
})
})
</script>
4. 数据库设计要点
4.1 核心表结构
sql复制CREATE TABLE `vaccine_stock` (
`id` bigint NOT NULL AUTO_INCREMENT,
`batch_no` varchar(32) NOT NULL COMMENT '疫苗批次号',
`vaccine_type` tinyint NOT NULL COMMENT '疫苗类型',
`quantity` int NOT NULL DEFAULT '0' COMMENT '库存数量',
`manufacture_date` date NOT NULL COMMENT '生产日期',
`expiry_date` date NOT NULL COMMENT '有效期',
PRIMARY KEY (`id`),
KEY `idx_expiry` (`expiry_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 分库分表策略
预约记录表按月份分表:
java复制// 动态表名拦截器
public class DateTableInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
// 根据当前日期动态修改表名
String newTableName = "appointment_" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
ReflectionUtils.setFieldValue(invocation.getArgs()[0], "tableName", newTableName);
return invocation.proceed();
}
}
5. 安全防护措施
5.1 防刷单机制
- 设备指纹识别
- 行为轨迹分析
- 预约频率限制
java复制@RateLimiter(value = 5, key = "#userId")
public AppointmentResult createAppointment(Long userId) {
// 业务逻辑
}
5.2 敏感数据加密
采用国密SM4算法加密身份证号:
java复制public class IdCardEncryptor {
private static final String KEY = "secure_key_123";
public static String encrypt(String idCard) {
// SM4加密实现
}
public static String decrypt(String cipherText) {
// SM4解密实现
}
}
6. 部署架构
6.1 生产环境配置
yaml复制# application-prod.yml
spring:
datasource:
url: jdbc:mysql://cluster-mysql:3306/hpv?useSSL=false
hikari:
maximum-pool-size: 20
redis:
cluster:
nodes: redis-node1:6379,redis-node2:6379,redis-node3:6379
6.2 性能优化方案
- Nginx负载均衡
- 服务实例水平扩展
- 静态资源CDN加速
- 数据库读写分离
7. 典型问题解决方案
7.1 预约冲突处理
采用乐观锁机制:
java复制@Transactional
public boolean updateAppointment(AppointmentDTO dto) {
Appointment entity = mapper.selectById(dto.getId());
if (entity.getVersion() != dto.getVersion()) {
throw new OptimisticLockException("数据已被修改");
}
// 更新操作
return mapper.updateById(entity) > 0;
}
7.2 定时任务补偿
使用Quartz实现预约超时取消:
java复制public class AppointmentTimeoutJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 查询待支付超时的预约记录
List<Appointment> list = appointmentMapper.selectTimeoutRecords();
list.forEach(record -> {
// 释放库存
vaccineStockService.releaseStock(record.getVaccineId());
// 更新状态
appointmentMapper.cancelAppointment(record.getId());
});
}
}
8. 前端关键实现
8.1 预约日历组件
vue复制<template>
<el-calendar v-model="selectedDate">
<template #dateCell="{date, data}">
<div @click="handleDateClick(date)">
<div>{{ data.day.split('-')[2] }}</div>
<div v-if="timeSlots[date]">
<el-tag v-for="slot in timeSlots[date]"
:type="slot.available ? 'success' : 'info'">
{{ slot.time }}
</el-tag>
</div>
</div>
</template>
</el-calendar>
</template>
8.2 表单验证逻辑
javascript复制const rules = {
idCard: [
{ required: true, message: '请输入身份证号' },
{ pattern: /^\d{17}[\dXx]$/, message: '身份证格式错误' }
],
phone: [
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式错误' }
]
}
9. 测试策略
9.1 压力测试指标
- 单节点QPS ≥ 800
- 平均响应时间 < 200ms
- 错误率 < 0.1%
- 99线 < 1s
9.2 自动化测试方案
java复制@SpringBootTest
class AppointmentServiceTest {
@Autowired
private AppointmentService service;
@Test
@DisplayName("并发预约测试")
void testConcurrentAppointment() throws InterruptedException {
int threadCount = 100;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
service.createAppointment(testData);
} finally {
latch.countDown();
}
}).start();
}
latch.await();
// 验证库存扣减准确性
}
}
10. 项目演进方向
- 智能推荐接种点
- 疫苗接种电子凭证
- 不良反应上报系统
- 与区域医疗平台对接
- 移动端小程序支持
在实际开发过程中,我们发现疫苗批号管理需要特别注意效期追踪,建议采用双人复核机制。对于高并发场景,除了技术方案外,还需要设计完善的熔断降级策略。前端体验方面,添加加载状态提示能显著提升用户满意度。
