php项目统计慢跑恢复时间数据如何?

wen PHP项目 2

PHP项目统计慢跑恢复时间数据方案

"慢跑恢复时间"通常指跑步后心率恢复到静息水平所需的时间,是评估心肺功能的重要指标,下面从数据模型、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
";

需要注意的坑

  1. 恢复时间定义要统一:是"心率降到静息+10"还是"降到静息的120%",全项目必须一致,否则趋势无意义。
  2. 静息心率动态变化:不要用固定值,建议取跑前 5 分钟或最近 7 天晨脉均值。
  3. 采样频率:心率带通常 1s/次,光电手表可能 5s/次,插值补齐或按最近点取值。
  4. 异常值过滤:恢复时间 > 30 分钟或 < 30 秒的记录应标记为可疑,排除统计。
  5. HRR1 比总恢复时间更可靠:医学上常用运动后 1 分钟心率下降值(HRR1),抗噪且对比性强,建议作为核心指标。

如果你能说明数据来源(心率带 / 手环 / 手动录入)和精度,我可以给出更贴合的实现细节或 ECharts 展示代码。

抱歉,评论功能暂时关闭!