1. 项目概述:校园疫情防控系统的核心价值
去年帮某高校部署这套系统时,教务主任握着我的手说:"终于不用每天人工统计3000多人的健康信息了"。这个场景完美诠释了SpringBoot校园疫情防控系统的核心价值——通过技术手段解决传统人工防疫的痛点。
典型的校园疫情防控系统需要处理学生健康打卡、出入审批、风险区域预警、数据可视化等核心功能。采用B/S架构的SpringBoot应用天然适合这种场景:管理员通过浏览器即可完成所有操作,学生用手机就能提交健康信息,后端用Java处理高并发数据。我经手的项目中,日均处理5万+条健康记录是常态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择SpringBoot
在技术选型阶段,我们对比过三种方案:
- 传统Servlet开发(维护成本高)
- PHP快速开发(性能瓶颈明显)
- SpringBoot(最终选择)
SpringBoot的自动配置特性让疫情系统可以快速迭代。比如当学校突然要求增加核酸检测模块时,用@EnableScheduling注解就能快速实现定时任务:
java复制@Scheduled(cron = "0 0 8 * * ?") // 每天8点执行
public void sendTestReminder() {
// 发送检测提醒逻辑
}
2.2 前后端分离实践
采用Vue+SpringBoot的分离架构时,要注意跨域问题。这是我的通用配置方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowCredentials(true)
.maxAge(3600);
}
}
特别注意:生产环境一定要替换
allowedOrigins("*")为具体域名,我吃过安全漏洞的亏
3. 核心功能实现细节
3.1 健康打卡模块设计
健康打卡的表结构设计很有讲究,这是我的推荐方案:
sql复制CREATE TABLE `health_report` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL COMMENT '学工号',
`temperature` DECIMAL(3,1) NOT NULL COMMENT '体温',
`symptoms` VARCHAR(255) COMMENT '症状JSON数组',
`location` POINT COMMENT '打卡位置',
`is_risk_area` TINYINT DEFAULT 0 COMMENT '是否风险地区',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_user` (`user_id`),
INDEX `idx_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
使用MySQL的POINT类型存储地理位置,方便后续做轨迹分析。症状字段用JSON格式存储,比关联表更灵活。
3.2 出入校审批流程
审批流程的状态机设计是关键,推荐使用枚举实现:
java复制public enum ApprovalStatus {
PENDING(0, "待审核"),
APPROVED(1, "已通过"),
REJECTED(2, "已拒绝"),
CANCELLED(3, "已取消");
private final int code;
private final String desc;
// 构造方法、getter省略
}
审批逻辑要注意幂等性处理,这是我的模板代码:
java复制@Transactional
public ApprovalResult handleApproval(Long applicationId, Integer operatorId, boolean isApprove) {
// 1. 乐观锁校验
Approval application = approvalMapper.selectForUpdate(applicationId);
if (application.getStatus() != PENDING) {
throw new BusinessException("申请状态已变更");
}
// 2. 更新状态
ApprovalStatus newStatus = isApprove ? APPROVED : REJECTED;
approvalMapper.updateStatus(applicationId, newStatus, operatorId);
// 3. 记录操作日志
logService.addApprovalLog(applicationId, operatorId, newStatus);
return new ApprovalResult(newStatus);
}
4. 性能优化实战经验
4.1 高并发写入优化
健康打卡高峰期(早8-9点)的QPS可能突破1000+,我总结的优化方案:
- 批量插入:用MyBatis的
<foreach>标签实现
xml复制<insert id="batchInsert">
INSERT INTO health_report (...)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.userId}, #{item.temperature}, ...)
</foreach>
</insert>
- 异步处理:非核心逻辑用
@Async处理
java复制@Async
public void asyncProcessHealthData(HealthData data) {
// 数据分析等耗时操作
}
- 缓存热点数据:用Redis缓存学校最新防疫政策
java复制public String getLatestPolicy() {
String cacheKey = "policy:latest";
String policy = redisTemplate.opsForValue().get(cacheKey);
if (policy == null) {
policy = policyMapper.selectLatest();
redisTemplate.opsForValue().set(cacheKey, policy, 1, TimeUnit.HOURS);
}
return policy;
}
4.2 查询性能优化
数据看板页面需要聚合查询,我的优化方案:
- 预聚合:定时任务提前计算统计结果
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void preAggregateStats() {
// 计算各学院健康数据
List<CollegeStats> stats = reportMapper.aggregateByCollege();
stats.forEach(stat -> {
String key = "stats:college:" + stat.getCollegeId();
redisTemplate.opsForValue().set(key, stat);
});
}
- 读写分离:SpringBoot配置多数据源
properties复制# 主库
spring.datasource.master.url=jdbc:mysql://master:3306/epidemic
spring.datasource.master.username=root
# 从库
spring.datasource.slave.url=jdbc:mysql://slave:3306/epidemic
spring.datasource.slave.username=readonly
5. 安全防护方案
5.1 敏感数据保护
学生健康数据属于敏感信息,我的加密方案:
- 字段级加密:使用Jasypt加密
java复制@Bean
public StringEncryptor encryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
encryptor.setPassword(System.getenv("JASYPT_PASSWORD"));
return encryptor;
}
- 接口权限控制:Spring Security配置
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/health/**").hasRole("STUDENT")
.antMatchers("/api/approval/**").hasRole("TEACHER")
.anyRequest().authenticated();
}
5.2 防刷策略
防止恶意刷接口的三种实践:
- 限流:使用Guava RateLimiter
java复制private final RateLimiter limiter = RateLimiter.create(10.0); // 每秒10次
@PostMapping("/report")
public Result reportHealth(@RequestBody HealthReport report) {
if (!limiter.tryAcquire()) {
throw new BusinessException("操作过于频繁");
}
// 正常逻辑
}
- 验证码:集成Google Kaptcha
java复制@GetMapping("/captcha")
public void getCaptcha(HttpServletResponse response) {
String text = producer.createText();
BufferedImage image = producer.createImage(text);
// 将text存入redis,image写入response
}
- 设备指纹:通过请求特征识别设备
java复制public String generateDeviceFingerprint(HttpServletRequest request) {
String ip = request.getRemoteAddr();
String userAgent = request.getHeader("User-Agent");
return DigestUtils.md5DigestAsHex((ip + userAgent).getBytes());
}
6. 部署与监控
6.1 Docker化部署
我的标准Dockerfile模板:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/epidemic-system.jar .
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "epidemic-system.jar"]
配合docker-compose实现MySQL+Redis+应用的一键部署:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
app:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
volumes:
mysql_data:
6.2 监控方案
- 健康检查:SpringBoot Actuator配置
properties复制management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.show-details=always
- 日志收集:ELK方案
java复制@Bean
public LogstashTcpSocketAppender logstashAppender() {
LogstashTcpSocketAppender appender = new LogstashTcpSocketAppender();
appender.setDestination("logstash:5044");
return appender;
}
- 告警规则:Prometheus示例配置
yaml复制groups:
- name: epidemic.rules
rules:
- alert: HighErrorRate
expr: rate(http_server_requests_errors_total[1m]) > 0.1
for: 5m
7. 踩坑实录与解决方案
7.1 时区问题
MySQL时区导致的时间显示错误解决方案:
properties复制spring.datasource.url=jdbc:mysql://localhost:3306/epidemic?serverTimezone=Asia/Shanghai
7.2 JSON序列化循环引用
在实体类中使用@JsonIgnore解决:
java复制@ManyToOne
@JoinColumn(name = "class_id")
@JsonIgnore // 避免循环引用
private SchoolClass schoolClass;
7.3 大文件上传
使用分段上传解决大文件问题:
java复制@PostMapping("/upload")
public Result chunkUpload(
@RequestParam MultipartFile file,
@RequestParam Integer chunkIndex,
@RequestParam String fileMd5) {
String chunkKey = "upload:" + fileMd5 + ":" + chunkIndex;
if (redisTemplate.hasKey(chunkKey)) {
return Result.success("分片已存在");
}
// 保存分片文件
file.transferTo(new File("/tmp/" + fileMd5 + "_" + chunkIndex));
redisTemplate.opsForValue().set(chunkKey, "1", 2, TimeUnit.HOURS);
return Result.success();
}
8. 项目扩展方向
- 微信小程序集成:通过
WxJavaSDK实现消息推送
java复制@Autowired
private WxMaService wxMaService;
public void pushHealthReminder(String openId) {
WxMaTemplateMessage message = WxMaTemplateMessage.builder()
.toUser(openId)
.templateId("HEALTH_REMINDER_TEMPLATE")
.build();
wxMaService.getMsgService().sendTemplateMsg(message);
}
- 大数据分析:集成Flink做实时分析
java复制StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<HealthEvent> events = env
.addSource(new KafkaSource<>("health-events"));
events.keyBy("schoolId")
.window(TumblingProcessingTimeWindows.of(Time.hours(1)))
.aggregate(new HealthStatsAggregator())
.addSink(new RedisSink<>());
- 物联网集成:对接体温检测设备
java复制@MessageMapping("/device/{deviceId}")
public void handleDeviceMessage(
@DestinationVariable String deviceId,
@Payload TemperatureReading reading) {
if (reading.getValue() > 37.3) {
alertService.sendHighTempAlert(deviceId, reading);
}
}
这套系统在实际运行中,平均帮助学校减少防疫工作量70%以上。有个细节让我印象深刻:通过分析打卡数据,我们成功预警了3起潜在聚集性疫情风险。技术人最欣慰的时刻,莫过于看到自己的代码真实地解决问题、创造价值。
