exec("CREATE TABLE IF NOT EXISTS online_snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, snapshot_time INTEGER NOT NULL, online_count INTEGER NOT NULL DEFAULT 0 )"); $db->exec("CREATE INDEX IF NOT EXISTS idx_snap_time ON online_snapshots(snapshot_time)"); // 清理24小时前的旧快照(保留24小时数据) $db->exec("DELETE FROM online_snapshots WHERE snapshot_time < " . (time() - 86400)); } catch (\Throwable $e) { debugLog('[online_curve] 建表异常: ' . $e->getMessage()); } } // ===== 记录快照 ===== function recordSnapshot() { ensureSnapshotTable(); $count = 0; try { $db = getDB(); $res = $db->query("SELECT COUNT(*) AS cnt FROM online_players"); $row = $res->fetchArray(SQLITE3_ASSOC); $count = (int)($row['cnt'] ?? 0); $now = time(); $stmt = $db->prepare("INSERT INTO online_snapshots (snapshot_time, online_count) VALUES (:t, :c)"); $stmt->bindValue(':t', $now, SQLITE3_INTEGER); $stmt->bindValue(':c', $count, SQLITE3_INTEGER); $stmt->execute(); } catch (\Throwable $e) { debugLog('[online_curve] 记录快照异常: ' . $e->getMessage()); } return $count; } // ===== 确保有最新快照(如果上次快照超过60秒则自动记录) ===== function ensureRecentSnapshot() { try { $db = getDB(); $res = $db->query("SELECT MAX(snapshot_time) AS last_time FROM online_snapshots"); $row = $res->fetchArray(SQLITE3_ASSOC); $lastTime = (int)($row['last_time'] ?? 0); $now = time(); // 没有快照,或上次快照超过 55 秒(留 5 秒余量防止频繁写) if ($lastTime === 0 || ($now - $lastTime) > 55) { return recordSnapshot(); } } catch (\Throwable $e) { debugLog('[online_curve] ensureRecentSnapshot 异常: ' . $e->getMessage()); } return null; } // ===== 获取24小时聚合数据(按小时) ===== function getHourlyData() { ensureSnapshotTable(); // 每次查询数据时自动确保有最新快照(如果超过60秒未记录) ensureRecentSnapshot(); // 24小时前的时间戳 $since = time() - 86400; // 按自然小时聚合:分桶到 h0~h23,取平均值 $buckets = []; for ($i = 0; $i < 24; $i++) $buckets[$i] = ['hour' => $i, 'count' => 0, 'samples' => 0]; try { $db = getDB(); $stmt = $db->prepare("SELECT snapshot_time, online_count FROM online_snapshots WHERE snapshot_time >= :since ORDER BY snapshot_time ASC"); $stmt->bindValue(':since', $since, SQLITE3_INTEGER); $res = $stmt->execute(); while ($row = $res->fetchArray(SQLITE3_ASSOC)) { $snapshotTime = (int)$row['snapshot_time']; // 使用本地时区获取小时(0-23) $h = (int)date('G', $snapshotTime); $buckets[$h]['count'] += (int)$row['online_count']; $buckets[$h]['samples']++; } } catch (\Throwable $e) { debugLog('[online_curve] 查询异常: ' . $e->getMessage()); } $result = []; foreach ($buckets as $b) { $avg = $b['samples'] > 0 ? round($b['count'] / $b['samples'], 1) : 0; $result[] = [ 'hour' => sprintf('%02d:00', $b['hour']), 'avg' => $avg, 'max' => $b['samples'] > 0 ? round($b['count'] / max(1,$b['samples']), 1) : 0, // same as avg for now 'samples' => $b['samples'], ]; } return ['buckets' => $result, 'total_snapshots' => array_sum(array_column($result, 'samples'))]; } // ===== 路由 ===== switch ($action) { case 'record': $c = recordSnapshot(); success(['recorded' => true, 'online_count' => $c]); exit; case 'data': success(getHourlyData()); exit; case 'page': default: // 页面加载时也记录一次快照 $currentOnline = recordSnapshot(); $initialState = json_encode(array_merge( ['current_online' => $currentOnline], getHourlyData() ), JSON_UNESCAPED_UNICODE); break; } ?>