PHP项目统计慢跑恢复时间数据方案
"慢跑恢复时间"通常指跑步后心率恢复到静息水平所需的时间,是评估心肺功能的重要指标,下面从数据模型、PHP实现、统计分析三个层面给出完整方案。

数据表设计
CREATE TABLE `run_records` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `user_id` INT UNSIGNED NOT NULL, `run_date` DATE NOT NULL, `distance_km` DECIMAL(5,2) COMMENT '距离', `duration_sec` INT COMMENT '跑步时长(秒)', `avg_pace_sec` INT COMMENT '平均配速(秒/公里)', `avg_hr` TINYINT UNSIGNED COMMENT '平均心率', `max_hr` TINYINT UNSIGNED COMMENT '最大心率', `hr_1min` TINYINT UNSIGNED COMMENT '结束1分钟心率', `hr_2min` TINYINT UNSIGNED COMMENT '结束2分钟心率', `hr_5min` TINYINT UNSIGNED COMMENT '结束5分钟心率', `resting_hr` TINYINT UNSIGNED COMMENT '静息心率', `recovery_sec` INT COMMENT '恢复至静息+10的心率用时(秒)', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX `idx_user_date` (`user_id`, `run_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心PHP逻辑
计算恢复时间(心率衰减法)
class RecoveryCalculator
{
/**
* 根据心率采样点计算恢复时间
* @param array $samples [['t'=>0,'hr'=>160], ['t'=>60,'hr'=>130], ...]
* @param int $restingHr 静息心率
* @param int $buffer 允许超出静息心率的阈值,默认10
* @return array
*/
public function calculate(array $samples, int $restingHr, int $buffer = 10): array
{
$target = $restingHr + $buffer;
$recoverySec = null;
foreach ($samples as $s) {
if ($s['hr'] <= $target) {
$recoverySec = $s['t'];
break;
}
}
// 1分钟心率下降值(HRR1,心率恢复能力核心指标)
$hrAtEnd = $samples[0]['hr'] ?? 0;
$hr1min = $this->findHrAt($samples, 60);
$hrr1 = $hr1min !== null ? $hrAtEnd - $hr1min : null;
return [
'recovery_sec' => $recoverySec,
'resting_hr' => $restingHr,
'target_hr' => $target,
'hrr1' => $hrr1, // >12 为正常, <12 提示风险
];
}
private function findHrAt(array $samples, int $sec): ?int
{
foreach ($samples as $s) {
if ($s['t'] >= $sec) return $s['hr'];
}
return null;
}
}
保存记录
public function saveRecord(int $userId, array $data): int
{
$calc = (new RecoveryCalculator())->calculate(
$data['samples'],
$data['resting_hr']
);
$stmt = $this->pdo->prepare("
INSERT INTO run_records
(user_id, run_date, distance_km, duration_sec, avg_pace_sec,
avg_hr, max_hr, hr_1min, hr_2min, hr_5min, resting_hr, recovery_sec)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$userId, $data['run_date'], $data['distance_km'], $data['duration_sec'],
$data['avg_pace_sec'], $data['avg_hr'], $data['max_hr'],
$data['hr_1min'] ?? null, $data['hr_2min'] ?? null, $data['hr_5min'] ?? null,
$data['resting_hr'], $calc['recovery_sec'],
]);
return (int)$this->pdo->lastInsertId();
}
统计分析SQL
用户恢复能力概览
SELECT
COUNT(*) AS total_runs,
ROUND(AVG(recovery_sec), 0) AS avg_recovery,
MIN(recovery_sec) AS best_recovery,
MAX(recovery_sec) AS worst_recovery,
ROUND(AVG(hr_1min), 1) AS avg_hr1,
ROUND(AVG(resting_hr), 1) AS avg_resting_hr
FROM run_records
WHERE user_id = ? AND run_date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY);
按周趋势(用于折线图)
SELECT
YEARWEEK(run_date, 1) AS week,
MIN(run_date) AS week_start,
ROUND(AVG(recovery_sec), 0) AS avg_recovery,
ROUND(AVG(distance_km), 2) AS avg_distance,
COUNT(*) AS runs
FROM run_records
WHERE user_id = ? AND run_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
GROUP BY YEARWEEK(run_date, 1)
ORDER BY week;
恢复能力评级
function gradeRecovery(?int $sec, ?int $hrr1): string
{
// 优先用 HRR1 判断,更权威
if ($hrr1 !== null) {
if ($hrr1 >= 25) return '优秀';
if ($hrr1 >= 18) return '良好';
if ($hrr1 >= 12) return '一般';
return '偏弱(建议关注)';
}
if ($sec === null) return '数据不足';
if ($sec <= 120) return '优秀';
if ($sec <= 300) return '良好';
if ($sec <= 600) return '一般';
return '偏弱';
}
性能优化要点
| 场景 | 措施 |
|---|---|
| 数据量大 | (user_id, run_date) 联合索引;按月分表 |
| 频繁聚合 | 建立 user_recovery_stats 汇总表,每日定时任务重算 |
| 图表查询 | 加 Redis 缓存,key = recovery:trend:{user_id}:{period} |
| 实时计算 | 心率采样点存 JSON 字段,只算一次入库,避免每次重算 |
汇总表 + 定时任务
// crontab: 0 3 * * * php /path/cron/rebuild_stats.php
$sql = "
REPLACE INTO user_recovery_stats (user_id, stat_date, avg_recovery, avg_hrr1, run_count)
SELECT user_id, CURDATE(),
AVG(recovery_sec),
AVG(hr_1min - hr_at_start),
COUNT(*)
FROM run_records
WHERE run_date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
GROUP BY user_id
";
需要注意的坑
- 恢复时间定义要统一:是"心率降到静息+10"还是"降到静息的120%",全项目必须一致,否则趋势无意义。
- 静息心率动态变化:不要用固定值,建议取跑前 5 分钟或最近 7 天晨脉均值。
- 采样频率:心率带通常 1s/次,光电手表可能 5s/次,插值补齐或按最近点取值。
- 异常值过滤:恢复时间 > 30 分钟或 < 30 秒的记录应标记为可疑,排除统计。
- HRR1 比总恢复时间更可靠:医学上常用运动后 1 分钟心率下降值(HRR1),抗噪且对比性强,建议作为核心指标。
如果你能说明数据来源(心率带 / 手环 / 手动录入)和精度,我可以给出更贴合的实现细节或 ECharts 展示代码。