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

wen PHP项目 2

本文目录导读:

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

  1. 数据建模(MySQL 数据库设计)
  2. 核心计算逻辑(PHP 后端)
  3. 统计与聚合(生成报表)
  4. 前端可视化(与 PHP 配合)
  5. 扩展与进阶优化

在 PHP 项目中统计慢跑恢复时间(即运动后的心率恢复、配速恢复或生理恢复时长)数据,是一个典型的数据采集 → 存储 → 计算 → 可视化的过程。

由于 PHP 通常用于后端 API 和 Web 管理后台,它不适合做实时秒级的心率监测(那应该用前端 JS 或移动端原生),但非常适合做数据聚合、趋势分析和日志记录

以下是完整的实现方案,分为数据建模核心计算逻辑统计接口可视化展示四个部分。


数据建模(MySQL 数据库设计)

你需要存储原始的运动数据和恢复时间记录,建议设计两张表:

运动记录表 run_sessions 用于存储每次跑步的基本信息。

CREATE TABLE `run_sessions` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `user_id` INT UNSIGNED NOT NULL COMMENT '用户ID',
  `started_at` DATETIME NOT NULL COMMENT '开始时间',
  `avg_hr` INT DEFAULT NULL COMMENT '平均心率(bpm)',
  `max_hr` INT DEFAULT NULL COMMENT '最大心率(bpm)',
  `total_distance` DECIMAL(6,2) DEFAULT 0 COMMENT '总距离(公里)',
  `duration_sec` INT DEFAULT 0 COMMENT '总时长(秒)',
  `resting_hr` INT DEFAULT NULL COMMENT '运动后静息心率(完全休息时)',
  `pacer_hr` INT DEFAULT NULL COMMENT '配速心率(慢跑时的目标心率)',
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 建立索引,用于快速查询用户的时间序列
ALTER TABLE `run_sessions` ADD INDEX `idx_user_start` (`user_id`, `started_at`);

实时心率明细表(可选,用于精确计算) hr_readings 如果你有手表或心率带实时上传数据,建议每 5 秒记录一次心率,用于计算“瞬时恢复率”。

CREATE TABLE `hr_readings` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `session_id` BIGINT UNSIGNED NOT NULL COMMENT '关联运动ID',
  `timestamp` TIME NOT NULL COMMENT '运动过程中的时间点',
  `hr` INT NOT NULL COMMENT '瞬时心率(bpm)',
  INDEX (`session_id`, `timestamp`)
) ENGINE=InnoDB;

核心计算逻辑(PHP 后端)

在 PHP 中,我们需要实现两个核心函数:计算单次恢复时间统计周期趋势

单次恢复时间计算(利用心率表)

著名的 “1分钟心率恢复” 指标(第一分钟心率下降值)是衡量恢复能力的金标准,计算公式为:运动结束时的最高心率 - 结束1分钟后瞬间的心率

<?php
class RunningRecoveryAnalyzer
{
    private $pdo;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    /**
     * 计算单次跑步的恢复指标
     * @param int $sessionId 运动记录ID
     * @return array 返回恢复数据
     */
    public function calculateSessionRecovery(int $sessionId): array
    {
        // 1. 获取本次运动的基本数据
        $session = $this->fetchSession($sessionId);
        if (!$session) {
            return ['error' => '运动记录不存在'];
        }
        // 2. 获取最后2分钟的心率数据(用于分析)
        $stmt = $this->pdo->prepare(
            "SELECT hr, timestamp FROM hr_readings 
             WHERE session_id = ? 
             ORDER BY timestamp DESC LIMIT 120" // 取最后120条(假设每5秒一条,约10分钟)
        );
        $stmt->execute([$sessionId]);
        $readings = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 反转数组,让时间从旧到新排列
        $readings = array_reverse($readings);
        if (count($readings) < 2) {
            return ['error' => '心率数据不足'];
        }
        // 3. 计算关键指标
        $end_hr = $readings[count($readings) - 1]['hr']; // 最后心率(接近结束心率)
        // 寻找结束前1分钟的对应数据点(假设每5秒一条,1分钟 = 12条数据)
        $one_min_ago_hr = $readings[max(0, count($readings) - 13)]['hr'] ?? $end_hr;
        // 恢复心率值(正常应大于10,越大会越好)
        $recovery_drop = $end_hr - $one_min_ago_hr;
        // 恢复时间估算(简易模型:根据心率下降比例估算完全恢复需要的时间)
        // 假设目标为回到基础心率 +10%
        $time_to_recovery = $this->estimateTimeToRecovery($readings, $session['resting_hr']);
        // 4. 更新主记录
        $this->updateSessionRecoveryStats($sessionId, $recovery_drop, $time_to_recovery);
        return [
            'session_id' => $sessionId,
            'end_hr' => $end_hr,
            'one_min_drop' => $recovery_drop, // 1分钟心率恢复值
            'estimated_recovery_min' => $time_to_recovery // 预估恢复时间(分钟)
        ];
    }
    /**
     * 估算恢复时间(分钟)
     * 基于心率衰减指数模型:HR(t) = HR_rest + (HR_max - HR_rest) * e^(-t/tau)
     */
    private function estimateTimeToRecovery(array $readings, int $restingHr): int
    {
        // 简单线性回归或指数拟合较复杂,这里用经验公式:
        // 恢复时间 ≈ 运动时间 * 强度系数 / 恢复能力系数
        // 简化为:1分钟恢复 > 20 表示恢复极快,恢复时间 = 30分钟;<10 表示很累,恢复时间 = 24小时
        if (count($readings) < 2) return 60;
        $firstHr = $readings[0]['hr'];
        $lastHr = $readings[count($readings) - 1]['hr'];
        // 计算平均下降速率 (每分钟)
        $total_time_min = (count($readings) - 1) * (5/60); // 每5秒一个点
        $avg_drop_per_min = ($firstHr - $lastHr) / max($total_time_min, 1);
        // 需要降低到静息心率的 110% 才算恢复
        if ($restingHr <= 0 || $lastHr <= $restingHr) {
            return 0;
        }
        $targetHr = $restingHr * 1.1;
        $hr_need_to_drop = $lastHr - $targetHr;
        if ($hr_need_to_drop <= 0) {
            return 0;
        }
        $estimated_minutes = round($hr_need_to_drop / max($avg_drop_per_min, 0.5));
        return max(0, min($estimated_minutes, 1440)); // 限制在0-24小时
    }
    private function fetchSession(int $sessionId)
    {
        $stmt = $this->pdo->prepare("SELECT * FROM run_sessions WHERE id = ?");
        $stmt->execute([$sessionId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    private function updateSessionRecoveryStats(int $sessionId, int $drop, int $recoveryMin): void
    {
        $stmt = $this->pdo->prepare(
            "UPDATE run_sessions 
             SET one_min_recovery_drop = ?, estimated_recovery_min = ? 
             WHERE id = ?"
        );
        $stmt->execute([$drop, $recoveryMin, $sessionId]);
    }
}

统计与聚合(生成报表)

当你需要查看“近 30 天的平均恢复时间”或“本月的最佳表现”时,PHP 直接查数据库聚合即可。

<?php
// controllers/RecoveryStatsController.php
public function weeklyStats($userId) {
    $pdo = // ... 获取数据库连接
    // 查询最近7天的恢复数据统计
    $sql = "
        SELECT 
            DATE(started_at) AS date,
            COUNT(*) AS runs,
            AVG(one_min_recovery_drop) AS avg_drop,
            AVG(estimated_recovery_min) AS avg_recovery_min
        FROM run_sessions
        WHERE user_id = ? AND started_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
        GROUP BY DATE(started_at)
        ORDER BY date DESC
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$userId]);
    $stats = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // 计算趋势(是否在变好)
    $trend = $this->calculateTrend($stats);
    header('Content-Type: application/json');
    echo json_encode([
        'status' => 'success',
        'data' => $stats,
        'trend' => $trend
    ]);
}
// 计算趋势:对比最近3天和之前3天的平均值
private function calculateTrend(array $stats): string
{
    if (count($stats) < 2) return 'insufficient_data';
    // 取最新3个的平均值
    $recent = array_slice($stats, 0, 3);
    $previous = array_slice($stats, 3, 3);
    $recentAvg = array_sum(array_column($recent, 'avg_recovery_min')) / max(count($recent), 1);
    $prevAvg = array_sum(array_column($previous, 'avg_recovery_min')) / max(count($previous), 1);
    if ($recentAvg < $prevAvg) {
        return 'improving'; // 恢复时间变短,变好
    } elseif ($recentAvg > $prevAvg) {
        return 'worsening'; // 恢复变慢
    } else {
        return 'stable';
    }
}

前端可视化(与 PHP 配合)

统计完数据后,你需要在前端展示图表。

方案建议:使用 Chart.jsECharts(均支持 PHP 输出 JSON 数据直接绑定)。

<!-- 图表展示 - 使用ECharts (从后端API获取数据) -->
<div id="recoveryChart" style="width: 100%; height: 400px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script>
fetch('/api/recovery-stats?user_id=1')
    .then(res => res.json())
    .then(data => {
        var chart = echarts.init(document.getElementById('recoveryChart'));
        // 解析PHP返回的数据
        var dates = data.data.map(item => item.date);
        var avgRecovery = data.data.map(item => item.avg_recovery_min);
        chart.setOption({
            title: { text: '近7天恢复时间趋势' },
            tooltip: { trigger: 'axis' },
            xAxis: { type: 'category', data: dates },
            yAxis: { type: 'value', name: '分钟' },
            series: [{
                name: '平均预估恢复时间',
                type: 'line',
                smooth: true,
                data: avgRecovery,
                markPoint: {
                    data: [{ type: 'min', name: '最快恢复' }]
                }
            }]
        });
    });
</script>

扩展与进阶优化

  1. 多影响因素分析

    • 将“睡眠质量”和“前一晚的静息心率”作为调节因子,用 PHP 做多变量回归,预测恢复时间。
    • 如果昨晚睡眠 < 6 小时,则恢复时间增加 20%。
  2. 实时计算优化

    • 如果要求秒级响应,PHP 无法胜任,建议将计算逻辑转移到 Node.jsGo 的微服务,PHP 只负责存储调用后的结果。
  3. 边界情况处理

    • 心率数据缺失(穿戴设备没戴好)时,PHP 应使用插值算法填充缺失值。
    • one_min_drop 为负数(心率还在上升),应判断为“冲刺后遗症”,恢复时间设置为 24 小时。

PHP 在慢跑恢复统计中的最佳实践是:

  • 存储端:MySQL 存储跑步记录和心率明细。
  • 计算端:PHP 通过 SQL 聚合 + 数学模型估算恢复时间。
  • 输出端:RESTful API 输出 JSON,前端图表展示。

核心代码已包含:数据库表结构、单次恢复计算(1分钟心率下降法)、趋势统计、前端图表绑定,你只需将上述代码集成到你的 Laravel / ThinkPHP / 原生框架中即可。

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