1. 抗疫资源管理系统全栈开发实战
去年参与某市卫健委应急项目时,凌晨三点还在调试物资分配算法的场景让我深刻意识到:一套可靠的抗疫资源管理系统,必须在架构设计阶段就考虑极端场景下的稳定性。本文将基于SpringBoot+Vue全栈技术,拆解具备高并发处理能力的抗疫系统开发全流程,重点分享三个核心经验:①物资动态分配算法实现 ②健康打卡数据压缩策略 ③基于OAuth2的安全防护方案。
这个系统面向三类用户:社区居民(健康申报/物资申请)、志愿者(任务接收/打卡)、管理员(资源调配/数据分析),采用微服务架构应对突发流量。在2022年某地实际部署中,系统峰值QPS达到1200+,平均响应时间控制在300ms以内。
2. 系统架构设计
2.1 技术选型决策
后端选择SpringBoot 2.7.x而非最新3.x版本,主要考虑三点:
- 企业现有运维体系对JDK17的支持尚不完善
- 疫情系统需要快速接入大量遗留系统(如医院HIS)
- 2.7.x的Spring Security OAuth2扩展更成熟
数据库采用混合方案:
sql复制# MySQL核心表结构示例
CREATE TABLE `resource_stock` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'N95口罩/防护服',
`spec` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '型号规格',
`current_stock` int NOT NULL DEFAULT '0',
`safety_stock` int NOT NULL COMMENT '安全库存',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_name_spec` (`name`,`spec`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
2.2 高并发架构设计
通过压力测试发现的两个性能瓶颈及解决方案:
-
物资库存超卖问题:
- 初始方案:直接更新库存SQL
java复制// 错误示范! @Transactional public void allocateResource(Long resourceId, int amount) { ResourceStock stock = stockMapper.selectById(resourceId); if(stock.getCurrentStock() >= amount) { stockMapper.updateStock(resourceId, amount); } }- 优化方案:Redis分布式锁+乐观锁
java复制// 正确实现 public boolean safeAllocate(Long resourceId, int amount) { String lockKey = "res_lock:" + resourceId; try { // 获取分布式锁(Redisson实现) RLock lock = redissonClient.getLock(lockKey); if(lock.tryLock(3, 10, TimeUnit.SECONDS)) { ResourceStock stock = stockMapper.selectById(resourceId); if(stock.getCurrentStock() >= amount) { int rows = stockMapper.updateStockWithVersion( resourceId, amount, stock.getVersion()); return rows > 0; } return false; } } finally { lock.unlock(); } } -
健康打卡数据洪峰:
- 采用时间分片存储策略,按用户ID尾号分表
- 字段压缩:将症状描述文本转为数字编码
java复制// 症状编码示例 public enum SymptomCode { FEVER(1), // 发热 COUGH(2), // 咳嗽 FATIGUE(4); // 乏力 private final int mask; // 位运算存储多个症状 public static int encode(Symptom... symptoms) { return Arrays.stream(symptoms) .mapToInt(s -> s.mask) .reduce(0, (a,b) -> a | b); } }
3. 核心功能实现
3.1 智能物资分配算法
基于优先级的动态分配模型:
python复制# 伪代码实现(实际用Java实现)
def allocate_resources(request_list):
# 1. 计算优先级得分
for req in request_list:
req.score = 0.4*req.medical_urgency \
+ 0.3*req.population_density \
+ 0.2*req.isolation_status \
+ 0.1*req.history_compliance
# 2. 按优先级排序
sorted_requests = sorted(request_list, key=lambda x: -x.score)
# 3. 贪心算法分配
allocations = []
remaining_stock = get_total_stock()
for req in sorted_requests:
if remaining_stock >= req.amount:
allocations.append(req)
remaining_stock -= req.amount
elif remaining_stock > 0:
# 按比例分配剩余物资
partial_amount = remaining_stock
req.amount = partial_amount
allocations.append(req)
break
return allocations
3.2 志愿者任务调度
采用基于地理位置的最优匹配策略:
- 使用GeoHash算法建立区域网格
- Redis存储志愿者实时位置(经纬度)
- 任务分配时计算网格距离权重
java复制// 志愿者匹配核心逻辑
public List<Volunteer> matchVolunteers(Task task, int requiredCount) {
String taskGeoHash = GeoHash.encode(task.getLat(), task.getLng());
return volunteerRepository.findNearby(
task.getLat(),
task.getLng(),
5.0 /* 5公里范围 */)
.stream()
.sorted(Comparator.comparing(v ->
calculateMatchScore(v, taskGeoHash, task.getSkills())))
.limit(requiredCount)
.collect(Collectors.toList());
}
private double calculateMatchScore(Volunteer v, String taskGeoHash,
Set<Skill> requiredSkills) {
// 距离分(0-100分)
double distanceScore = 100 - GeoHash.distance(v.getGeoHash(), taskGeoHash)*10;
// 技能匹配度
long matchedSkills = v.getSkills().stream()
.filter(requiredSkills::contains)
.count();
double skillScore = (double)matchedSkills / requiredSkills.size() * 100;
return 0.6*distanceScore + 0.4*skillScore;
}
4. 安全防护方案
4.1 认证授权体系
采用OAuth2密码模式改造方案:
yaml复制# application.yml关键配置
security:
oauth2:
client:
client-id: pandemic-web
client-secret: ${RANDOM_UUID}
scope: read,write
access-token-uri: /oauth/token
resource:
user-info-uri: /api/user/me
前端Token自动刷新机制:
javascript复制// axios响应拦截器
instance.interceptors.response.use(response => {
return response
}, error => {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
return refreshToken().then(res => {
store.commit('updateToken', res.data.access_token)
originalRequest.headers.Authorization = 'Bearer ' + res.data.access_token
return instance(originalRequest)
})
}
return Promise.reject(error)
})
4.2 敏感数据保护
健康数据加密方案:
- 数据库层面:采用MySQL AES_ENCRYPT函数
sql复制INSERT INTO health_report (user_id, symptoms, encrypted_phone) VALUES (123, '1011', AES_ENCRYPT('13800138000', '${SECRET_KEY}')) - 日志脱敏:自定义Logback转换器
xml复制<conversionRule conversionWord="mask" converterClass="com.util.SensitiveDataConverter"/> <logger name="com.service.HealthService"> <appender-ref ref="FILE"/> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> </logger>
5. 性能优化实战
5.1 缓存策略设计
采用多级缓存架构:
- 本地缓存(Caffeine):高频访问的静态数据
java复制@Bean public CacheManager cacheManager() { CaffeineCacheManager manager = new CaffeineCacheManager(); manager.registerCustomCache("resourceTypes", Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(30, TimeUnit.MINUTES) .build()); return manager; } - Redis缓存:分布式共享数据
java复制@Cacheable(value = "resourceStock", key = "#id") public ResourceStock getStock(Long id) { return stockMapper.selectById(id); }
5.2 数据库优化
慢查询优化案例:
sql复制-- 优化前(执行时间1.8s)
EXPLAIN SELECT * FROM health_report
WHERE create_time > '2023-01-01'
AND temperature > 37.3
ORDER BY user_id;
-- 优化后(添加复合索引)
ALTER TABLE health_report ADD INDEX idx_temp_time (temperature, create_time);
批量插入优化对比:
java复制// 错误方式:循环单条插入
for(HealthReport report : reports) {
reportMapper.insert(report); // 500条耗时12s
}
// 正确方式:批量插入
@Insert("<script>" +
"INSERT INTO health_report (user_id, symptoms) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.userId}, #{item.symptoms})" +
"</foreach>" +
"</script>")
void batchInsert(List<HealthReport> reports); // 500条耗时0.8s
6. 部署与监控
6.1 Docker化部署
容器编排关键配置:
dockerfile复制# Dockerfile示例
FROM openjdk:11-jre
WORKDIR /app
COPY target/pandemic-system.jar .
EXPOSE 8080
ENTRYPOINT ["java","-jar",
"-Dspring.profiles.active=prod",
"-XX:+UseG1GC",
"-Xms512m",
"-Xmx2048m",
"pandemic-system.jar"]
健康检查端点配置:
yaml复制management:
endpoint:
health:
show-details: always
probes:
enabled: true
endpoints:
web:
exposure:
include: health,info,metrics
6.2 监控告警方案
Prometheus关键指标采集:
yaml复制# application.yml配置
metrics:
export:
prometheus:
enabled: true
web:
server:
auto-time-requests: true
Grafana监控看板核心指标:
- JVM内存使用率(堆/非堆)
- 接口P99响应时间
- 数据库连接池活跃数
- Redis缓存命中率
7. 开发经验总结
在三个城市的实际部署中,我们收获了这些关键经验:
-
物资分配算法需要具备可解释性,向管理员展示分配依据(如:A社区因有确诊病例获得60%权重)
-
健康打卡表单字段顺序影响填报效率,实测将"体温"字段置顶可减少20%填写时间
-
移动端适配必须考虑极端场景:
- 低版本WebView兼容(某地20%用户使用Android 5.1)
- 弱网环境下表单自动暂存(localStorage+定期同步)
-
压力测试要模拟真实场景:
- 早8-9点集中打卡时段
- 物资发放前30分钟的突发访问
这个项目的完整源码包含23个核心模块、142个Java类和58个Vue组件,采用Git子模块管理前后端代码。在资源有限的情况下,通过合理的技术选型和架构设计,系统最终支持了日均50万次健康申报和3万次物资调度请求。