本文目录导读:

基于实时的Java技术栈(如Spring Boot + MyBatis/MyBatis-Plus + Redis + 定时任务),针对“体能低谷期何时到来”这一命题,我为你设计了一套基于心率变异性(HRV)和静息心率(RHR)的疲劳度监测系统的核心算法与实现逻辑。
这个案例模拟了可穿戴设备(如智能手表)上报数据后,后端如何通过算法预测并通知用户“低谷期”的来临。
案例核心:基于“训练负荷”与“恢复状态”的预测模型
在运动科学中,体能低谷(疲劳累积)通常由急性训练负荷(ACWR)和恢复状态(HRV)共同决定,我们用Java来实现这个判断逻辑。
核心实体与数据模型 (POJO)
我们定义核心的数据结构,使用LocalDate来处理日期,避免时区问题。
import java.time.LocalDate;
import java.util.List;
import lombok.Data;
// 每日生理指标快照
@Data
public class DailyPhysioSnapshot {
private Long userId;
private LocalDate date;
/** 静息心率 (次/分钟) */
private Double restingHeartRate;
/** 心率变异性 (毫秒,通常取RMSSD) */
private Double hrv;
/** 当日训练负荷 (TSS分数或TRIMP) */
private Double trainingLoad;
/** 睡眠质量评分 (0-100) */
private Integer sleepScore;
}
核心算法服务 (Spring Service)
这里是关键,我们通过计算HRV的7天基线漂移和ACWR(急性/慢性负荷比)来判断是否处于“低谷临界点”。
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class FatigueAnalysisService {
// 配置参数:HRV基线下降阈值(如低于基线10%)
private static final double HRV_DROP_THRESHOLD = 0.10;
// 配置参数:ACWR警戒值(gt;1.5为过度训练风险)
private static final double ACWR_ALERT_THRESHOLD = 1.5;
/**
* 预测低谷期是否在“未来3天内”到来
*
* @param historicalData 过去28天的生理数据
* @return 风险评估结果
*/
public RiskAssessment predictLowPerformanceWindow(List<DailyPhysioSnapshot> historicalData) {
// 1. 数据预处理:按日期排序
List<DailyPhysioSnapshot> sortedData = historicalData.stream()
.sorted(Comparator.comparing(DailyPhysioSnapshot::getDate))
.collect(Collectors.toList());
// 2. 计算指标
double currentAcwr = calculateAcwr(sortedData);
double hrvBaseline = calculateHrvBaseline(sortedData);
double currentHrv = getCurrentHrv(sortedData);
// 3. 计算HRV下降幅度
double hrvDropRatio = (hrvBaseline - currentHrv) / hrvBaseline;
// 4. 决策判断
RiskAssessment risk = new RiskAssessment();
risk.setCurrentAcwr(currentAcwr);
risk.setHrvDropRatio(hrvDropRatio);
if (currentAcwr > ACWR_ALERT_THRESHOLD && hrvDropRatio > HRV_DROP_THRESHOLD) {
risk.setLevel(RiskLevel.HIGH);
risk.setPredictedLowDate(LocalDate.now().plusDays(1)); // 明天来
risk.setSuggestion("建议降低训练强度40%,并保证睡眠时间,低谷期即将在24-48小时内出现。");
} else if (currentAcwr > ACWR_ALERT_THRESHOLD || hrvDropRatio > HRV_DROP_THRESHOLD) {
risk.setLevel(RiskLevel.MEDIUM);
risk.setPredictedLowDate(LocalDate.now().plusDays(2)); // 后天
risk.setSuggestion("指标出现异常,正在接近低谷期,请做好恢复准备。");
} else {
risk.setLevel(RiskLevel.LOW);
risk.setPredictedLowDate(null); // 暂无风险
risk.setSuggestion("身体状态良好,体能储备充足。");
}
return risk;
}
/**
* 计算ACWR (急性/慢性负荷比)
* 急性负荷 = 最近7天平均负荷;慢性负荷 = 最近28天平均负荷
*/
private double calculateAcwr(List<DailyPhysioSnapshot> data) {
int size = data.size();
if (size < 28) return 0.0; // 数据不足
double acuteLoad = data.subList(size - 7, size).stream()
.mapToDouble(DailyPhysioSnapshot::getTrainingLoad)
.average().orElse(0.0);
double chronicLoad = data.stream()
.mapToDouble(DailyPhysioSnapshot::getTrainingLoad)
.average().orElse(0.0);
// 避免除零
return chronicLoad == 0 ? 0.0 : acuteLoad / chronicLoad;
}
/**
* 计算HRV基线(取最近14天非异常值的平均值)
*/
private double calculateHrvBaseline(List<DailyPhysioSnapshot> data) {
int size = data.size();
if (size < 14) return 0.0;
// 简单过滤:去掉最高和最低的2个值(去除噪声)
List<Double> hrvValues = data.subList(size - 14, size).stream()
.map(DailyPhysioSnapshot::getHrv)
.sorted()
.collect(Collectors.toList());
// 去掉头和尾各2个异常点
return hrvValues.subList(2, hrvValues.size() - 2).stream()
.mapToDouble(Double::doubleValue)
.average().orElse(0.0);
}
/**
* 获取最近一天的HRV(即今日状态)
*/
private double getCurrentHrv(List<DailyPhysioSnapshot> data) {
return data.get(data.size() - 1).getHrv();
}
}
定时任务 + 实时推送 (Scheduler + Redis)
当算法判定“低谷期”即将到来时,我们需要通过WebSocket(实时)或消息队列推送告警,下面是利用@Scheduled定时触发检测,并通过Redis发布订阅通知的简略示例。
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
@Component
public class FatigueMonitorScheduler {
private final FatigueAnalysisService analysisService;
private final StringRedisTemplate redisTemplate;
public FatigueMonitorScheduler(FatigueAnalysisService analysisService, StringRedisTemplate redisTemplate) {
this.analysisService = analysisService;
this.redisTemplate = redisTemplate;
}
// 每天早晨8点执行一次检测
@Scheduled(cron = "0 0 8 * * ?")
public void checkAndNotify() {
// 实际应用中这里会从数据库获取当前用户
Long userId = 12345L;
List<DailyPhysioSnapshot> userData = loadUserDataFromDB(userId);
RiskAssessment result = analysisService.predictLowPerformanceWindow(userData);
// 如果预测到低谷期
if (result.getPredictedLowDate() != null) {
// 构建推送消息
String message = String.format(
"{\"userId\":%d, \"level\":\"%s\", \"lowDate\":\"%s\"}",
userId,
result.getLevel(),
result.getPredictedLowDate().toString()
);
// 通过Redis发布消息,设备网关订阅后进行实时推送
redisTemplate.convertAndSend("channel:fatigue:alert", message);
// 同时写入数据库记录日志(这里省略)
}
}
private List<DailyPhysioSnapshot> loadUserDataFromDB(Long userId) {
// 此处模拟从数据库查询最近28天数据
// 实际使用 MyBatis-Plus:
// LambdaQueryWrapper<PhysioRecord> wrapper = new LambdaQueryWrapper<>();
// wrapper.eq(PhysioRecord::getUserId, userId)
// .ge(PhysioRecord::getDate, LocalDate.now().minusDays(28))
// .orderByAsc(PhysioRecord::getDate);
return null; // 实际返回查询结果
}
}
模拟实时数据流 (演示入口)
为了测试,我们可以模拟一个数据生成的入口(模拟传感器数据)。
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MockDataGenerator {
public static List<DailyPhysioSnapshot> generateData() {
Random random = new Random();
List<DailyPhysioSnapshot> list = new ArrayList<>();
LocalDate today = LocalDate.now();
// 生成过去28天的数据
for (int i = 27; i >= 0; i--) {
DailyPhysioSnapshot snapshot = new DailyPhysioSnapshot();
snapshot.setDate(today.minusDays(i));
snapshot.setRestingHeartRate(50 + random.nextInt(10));
// HRV 基线在 60-80 之间波动
snapshot.setHrv(60 + random.nextInt(20) * 1.0);
// 训练负荷逐渐增加(模拟过度训练)
snapshot.setTrainingLoad(100 + random.nextInt(50) + (20 - i) * 2.0);
list.add(snapshot);
}
return list;
}
}
这个案例如何回答“低谷期何时到来”?
- 数据基础:通过IoT设备上报的HRV(心率变异性)和静息心率来量化疲劳。
- 核心指标:引入运动科学中的 ACWR(急性/慢性负荷比),如果最近7天的训练负荷(急性)远高于过去28天的平均负荷(慢性),说明在“堆积”疲劳。
- 判断逻辑:当ACWR > 1.5(突然加练)且HRV下降超过10%(身体压力大)时,算法会预测低谷期将在未来1-2天内到来。
- 实时架构:通过定时任务扫描,结合Redis发布订阅实现毫秒级推送,确保用户能收到“明天你将状态不佳,建议今天调整训练计划”的实时Java指令。
这种设计模式在真实项目中,常用于“Keep”、“悦跑圈”等App的智能教练系统。