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

wen PHP项目 3

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

慢跑恢复时间(通常指运动后心率恢复到正常区间所需时间)的统计在PHP项目中实现,需要从数据建模、采集、计算、统计展示几个层面来设计,下面给你一套完整可落地的方案。

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


数据建模

核心表设计

-- 用户表(已有)
-- users(id, name, ...)
-- 慢跑记录表
CREATE TABLE run_records (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    start_time DATETIME NOT NULL,          -- 开始跑步时间
    end_time DATETIME NOT NULL,            -- 结束跑步时间
    duration INT NOT NULL,                 -- 持续秒数
    distance DECIMAL(8,2),                 -- 距离(km)
    avg_pace INT,                          -- 平均配速(秒/km)
    avg_heart_rate TINYINT,                -- 平均心率
    max_heart_rate TINYINT,                -- 最大心率
    end_heart_rate TINYINT,                -- 结束时心率
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_time (user_id, start_time)
);
-- 心率采样表(可选,用于精确计算恢复时间)
CREATE TABLE heart_rate_samples (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    run_record_id BIGINT UNSIGNED NOT NULL,
    user_id BIGINT UNSIGNED NOT NULL,
    heart_rate TINYINT NOT NULL,
    recorded_at DATETIME NOT NULL,
    phase ENUM('running','recovery') DEFAULT 'running',  -- 跑步中/恢复中
    INDEX idx_run (run_record_id, recorded_at)
);
-- 恢复记录表(关键)
CREATE TABLE recovery_records (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    run_record_id BIGINT UNSIGNED NOT NULL,
    user_id BIGINT UNSIGNED NOT NULL,
    peak_heart_rate TINYINT NOT NULL,      -- 结束瞬间心率
    target_heart_rate TINYINT NOT NULL,    -- 目标恢复心率(如静息+10)
    recovery_seconds INT NOT NULL,         -- 恢复耗时
    recovery_1min TINYINT,                 -- 1分钟后心率
    recovery_2min TINYINT,                 -- 2分钟后心率
    hrr_1min TINYINT,                      -- 1分钟心率恢复值(HRR1)
    hrr_2min TINYINT,                      -- 2分钟心率恢复值(HRR2)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user (user_id, created_at)
);

关键指标说明:

  • HRR1(1分钟心率恢复):运动停止后1分钟内心率下降值,是心血管健康的经典指标
  • 恢复时间:从结束心率降到目标心率(通常静息心率+10~20)所需时间

恢复时间计算逻辑(PHP)

class RecoveryCalculator
{
    /**
     * 根据心率采样计算恢复数据
     * @param array $samples 按时间排序的恢复期心率采样
     */
    public function calc(array $samples, int $restingHr): array
    {
        if (empty($samples)) return [];
        $peak = $samples[0]['heart_rate'];
        $target = $restingHr + 10;   // 目标心率
        $startTs = strtotime($samples[0]['recorded_at']);
        $recoverySeconds = null;
        $hr1min = $hr2min = null;
        foreach ($samples as $s) {
            $t = strtotime($s['recorded_at']) - $startTs;
            if ($t >= 60 && $hr1min === null) $hr1min = $s['heart_rate'];
            if ($t >= 120 && $hr2min === null) $hr2min = $s['heart_rate'];
            if ($recoverySeconds === null && $s['heart_rate'] <= $target) {
                $recoverySeconds = $t;
            }
        }
        // 未达标时,用线性回归外推(或标记为未恢复)
        if ($recoverySeconds === null) {
            $recoverySeconds = $this->extrapolate($samples, $target);
        }
        return [
            'peak_heart_rate'  => $peak,
            'target_heart_rate'=> $target,
            'recovery_seconds' => $recoverySeconds,
            'recovery_1min'    => $hr1min,
            'recovery_2min'    => $hr2min,
            'hrr_1min'         => $hr1min ? $peak - $hr1min : null,
            'hrr_2min'         => $hr2min ? $peak - $hr2min : null,
        ];
    }
    private function extrapolate(array $samples, int $target): ?int
    {
        // 简单对数拟合:HR(t) = a + b*ln(t+1)
        // 生产环境建议用最小二乘法,这里省略具体实现
        return null; // 或返回估算值
    }
}

慢跑恢复统计查询(SQL + PHP)

平均恢复时间趋势(按月)

SELECT
    DATE_FORMAT(r.start_time, '%Y-%m') AS month,
    COUNT(*) AS run_count,
    ROUND(AVG(rec.recovery_seconds), 0) AS avg_recovery_sec,
    ROUND(AVG(rec.hrr_1min), 1) AS avg_hrr1,
    MIN(rec.recovery_seconds) AS best_recovery,
    MAX(rec.recovery_seconds) AS worst_recovery
FROM run_records r
JOIN recovery_records rec ON rec.run_record_id = r.id
WHERE r.user_id = :user_id
  AND r.start_time >= :start
GROUP BY month
ORDER BY month;

用户恢复能力画像

SELECT
    u.id,
    u.name,
    COUNT(*) AS total_runs,
    ROUND(AVG(rec.recovery_seconds), 0) AS avg_recovery,
    ROUND(AVG(rec.hrr_1min), 1) AS avg_hrr1,
    -- 恢复能力评级
    CASE
        WHEN AVG(rec.recovery_seconds) < 180 THEN '优秀'
        WHEN AVG(rec.recovery_seconds) < 300 THEN '良好'
        WHEN AVG(rec.recovery_seconds) < 480 THEN '一般'
        ELSE '需改善'
    END AS level
FROM users u
JOIN run_records r ON r.user_id = u.id
JOIN recovery_records rec ON rec.run_record_id = r.id
WHERE r.start_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, u.name
HAVING total_runs >= 3
ORDER BY avg_recovery ASC;

性能优化要点

问题 优化手段
心率采样数据量大 冷热分离:近期数据 MySQL,历史归档到 ClickHouse/TDengine
统计查询慢 预聚合日/周/月汇总表 recovery_stats_daily,定时任务刷新
实时计算卡顿 恢复数据在跑步结束时异步(队列)计算并落库
高频查询用户画像 Redis 缓存,TTL 1小时,写入时失效
大数据量分页 用 WHERE id < last_id LIMIT 20 替代 OFFSET

预聚合表示例

CREATE TABLE recovery_stats_daily (
    user_id BIGINT UNSIGNED,
    stat_date DATE,
    run_count INT,
    avg_recovery_seconds INT,
    avg_hrr1 DECIMAL(4,1),
    best_recovery_seconds INT,
    PRIMARY KEY (user_id, stat_date)
);

业务建议

  1. 目标心率个性化:不要用固定值,用 静息心率 + 10~20,或按年龄 (220-年龄)*0.5。
  2. HRR1 是核心指标:研究表明运动后1分钟心率下降 <12 次提示心血管风险,可作为健康预警。
  3. 异常过滤:剔除采样间隔 >10s、心率突变 >30bpm 的脏数据。
  4. 可视化:前端展示恢复曲线(时间-心率),用折线图 + 目标线更直观。
  5. 对比维度:支持同一用户纵向对比(周/月/季)和同龄人群横向对比。

如果你能说明一下数据来源(手环/APP/手动录入)和规模量级(日增多少条),我可以给出更针对性的存储和计算架构方案。

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