1. 项目背景与核心价值
疫情常态化管理背景下,企事业单位、学校等集体单位对成员健康状态的动态监测需求激增。这个基于SpringBoot的健康打卡系统正是为解决以下痛点而生:
- 传统纸质登记弊端:手工记录效率低、易丢失、难追溯,无法实时掌握群体健康趋势
- 分散数据难以整合:微信接龙、Excel表格等零散方式导致数据孤岛,缺乏统一分析视图
- 应急响应延迟:异常情况无法自动预警,管理人员难以及时采取干预措施
我在2022年为某高校开发的这套系统,上线后实现每日3000+师生的无接触打卡,体温异常自动触发校医室告警,使疫情响应时间缩短80%。系统核心价值体现在三个维度:
- 用户侧:5秒完成每日健康上报,支持历史记录追溯
- 管理侧:实时可视化看板,支持按部门/班级多维筛选
- 决策侧:生成周/月感染趋势分析报告,辅助防疫资源调配
2. 技术架构设计解析
2.1 整体技术栈选型
mermaid复制graph TD
A[前端] -->|Vue.js| B[SpringBoot]
B -->|MyBatis-Plus| C[MySQL]
B -->|Redis| D[缓存层]
C -->|ECharts| E[数据可视化]
D -->|Quartz| F[定时任务]
(注:实际开发中采用以下技术组合)
- 前端:Vue2 + ElementUI(兼顾开发效率与兼容性)
- 后端:SpringBoot 2.7.3(稳定版)+ MyBatis-Plus 3.5.1(简化CRUD)
- 数据库:MySQL 8.0(JSON字段存储动态问卷)
- 中间件:Redis 6(缓存热点数据)+ RabbitMQ(异步处理报表生成)
- 安全层:Sa-Token(轻量级权限控制)
技术选型心得:
放弃SpringCloud微服务架构,采用单体应用设计。实测证明在200QPS以下的场景中,单体架构的运维复杂度更低,且疫情打卡场景具有明显的潮汐特征(早晚高峰访问)
2.2 核心业务模块拆解
2.2.1 动态表单引擎
java复制// 问卷问题定义JSON结构
{
"questionId": "Q001",
"type": "RADIO", // 支持RADIO/CHECKBOX/TEXTAREA
"title": "今日体温范围",
"options": ["36-37℃", "37-38℃", "38℃以上"],
"required": true,
"riskFlag": {"optionIndex": 2, "alertMsg": "体温过高请立即就医"}
}
通过动态JSON配置实现问卷灵活调整,管理员后台可随时新增防疫问题(如增加抗原结果上报)
2.2.2 分级预警机制
- 一级预警(黄色):体温≥37.3℃
- 触发动作:短信通知本人+班主任
- 二级预警(红色):体温≥38℃+有咳嗽症状
- 触发动作:自动锁定校园卡进出权限,校医端弹窗提醒
sql复制CREATE TABLE `health_alert_rule` (
`id` INT NOT NULL AUTO_INCREMENT,
`condition_json` JSON NOT NULL COMMENT '预警条件逻辑表达式',
`action_type` ENUM('SMS','WECHAT','SYSTEM_LOCK') NOT NULL,
`receiver_roles` VARCHAR(255) NOT NULL COMMENT '接收人角色,多个用逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 关键实现细节
3.1 高并发提交优化
实测早8:00-9:00会出现每分钟150+的提交峰值,采用三级缓冲策略:
- 前端防抖:提交按钮300ms冷却时间
- Redis计数器:用户ID为key,设置10秒过期防止重复提交
- 数据库批量插入:通过MyBatis-Plus的
insertBatchSomeColumn方法,每30条执行一次批量插入
java复制@Transactional
public void batchSubmit(List<HealthReport> reports) {
// 校验逻辑...
SqlHelper.executeBatch(HealthReport.class,
log -> sqlSessionFactory.openSession(ExecutorType.BATCH),
reports,
(sqlSession, entity) -> sqlSession.insert("insertReport", entity));
}
3.2 可视化看板实现
使用ECharts + WebSocket实现实时数据更新:
- 数据聚合:每小时跑一次定时任务,预计算各部门健康数据
- 推送策略:管理端连接建立时全量推送,后续仅推送差异数据
javascript复制// 前端订阅代码示例
const socket = new SockJS('/health-ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
stompClient.subscribe('/topic/dashboard', (data) => {
updateChart(JSON.parse(data.body));
});
});
4. 典型问题排查实录
4.1 缓存穿透问题
现象:凌晨定时缓存重建时,管理端频繁报错
根因:缓存过期时间全量设置为02:00,导致瞬时大量请求穿透到DB
解决方案:
- 错峰过期:基础数据缓存过期时间=02:00 + 随机0~600秒
- 永不过期:核心配置采用
redis.set(key, value, 'NX'),仅程序更新时删除
4.2 地理位置校验失效
现象:部分学生校外打卡仍显示校内定位
排查过程:
- 检查高德API返回的GPS坐标与地址是否匹配
- 发现前端未校验
location_type字段(可能返回"模拟位置") - 增加服务端校验逻辑:
java复制public boolean isValidLocation(LocationDTO dto) {
return "ROOFTOP".equals(dto.getLocationType())
&& SCHOOL_GPS_RANGE.contains(dto.getLat(), dto.getLng());
}
5. 扩展实践建议
5.1 离线打卡方案
针对网络条件差的山区学校,实现PWA离线应用:
- 前端通过Service Worker缓存关键静态资源
- 使用IndexedDB暂存未提交的打卡记录
- 网络恢复后通过
navigator.onLine事件触发同步
5.2 智能预警升级
结合历史数据实现预测性预警:
python复制# 使用Prophet库分析体温异常规律
from prophet import Prophet
model = Prophet(seasonality_mode='multiplicative')
model.fit(df) # df包含历史体温数据
future = model.make_future_dataframe(periods=7)
forecast = model.predict(future)
这套系统最让我意外的价值,是后来被复用于日常晨午检管理。通过将"疫情"模块改造成通用健康监测平台,使系统生命周期显著延长。建议开发时预留足够的字段扩展性,比如在数据库设计中加入extend_json字段存储动态属性