修复数据统计问题

This commit is contained in:
咕谷酱
2025-08-22 05:02:24 +08:00
parent fabfbd9f7f
commit 56ae478264
4 changed files with 397 additions and 18 deletions

View File

@@ -0,0 +1,148 @@
from __future__ import annotations
import json
from datetime import datetime, timedelta
from typing import Dict, Optional
from app.dependencies.database import get_redis, get_redis_message
from app.log import logger
from app.router.v2.stats import (
REDIS_ONLINE_HISTORY_KEY,
_get_online_users_count,
_get_playing_users_count,
_redis_exec
)
# Redis key for current interval stats
CURRENT_INTERVAL_STATS_KEY = "server:current_interval_stats"
class IntervalStatsManager:
"""区间统计管理器 - 管理当前30分钟区间的实时统计"""
@staticmethod
def _get_current_interval_key() -> str:
"""获取当前30分钟区间的唯一标识"""
now = datetime.utcnow()
# 将时间对齐到30分钟区间
interval_start = now.replace(minute=(now.minute // 30) * 30, second=0, microsecond=0)
return f"{CURRENT_INTERVAL_STATS_KEY}:{interval_start.strftime('%Y%m%d_%H%M')}"
@staticmethod
async def update_current_interval() -> None:
"""更新当前区间的统计数据"""
redis_sync = get_redis_message()
redis_async = get_redis()
try:
# 获取当前在线和游玩用户数
online_count = await _get_online_users_count(redis_async)
playing_count = await _get_playing_users_count(redis_async)
current_time = datetime.utcnow()
interval_key = IntervalStatsManager._get_current_interval_key()
# 准备区间统计数据
interval_stats = {
"timestamp": current_time.isoformat(),
"online_count": online_count,
"playing_count": playing_count,
"last_updated": current_time.isoformat()
}
# 存储当前区间统计
await _redis_exec(redis_sync.set, interval_key, json.dumps(interval_stats))
await redis_async.expire(interval_key, 35 * 60) # 35分钟过期确保覆盖整个区间
logger.debug(f"Updated current interval stats: online={online_count}, playing={playing_count}")
except Exception as e:
logger.error(f"Error updating current interval stats: {e}")
@staticmethod
async def get_current_interval_stats() -> Optional[Dict]:
"""获取当前区间的统计数据"""
redis_sync = get_redis_message()
try:
interval_key = IntervalStatsManager._get_current_interval_key()
stats_data = await _redis_exec(redis_sync.get, interval_key)
if stats_data:
return json.loads(stats_data)
return None
except Exception as e:
logger.error(f"Error getting current interval stats: {e}")
return None
@staticmethod
async def finalize_current_interval() -> bool:
"""完成当前区间统计,将其添加到历史记录中"""
redis_sync = get_redis_message()
redis_async = get_redis()
try:
# 获取当前区间的统计
current_stats = await IntervalStatsManager.get_current_interval_stats()
if not current_stats:
# 如果没有当前区间数据,使用实时数据
online_count = await _get_online_users_count(redis_async)
playing_count = await _get_playing_users_count(redis_async)
current_time = datetime.utcnow()
current_stats = {
"timestamp": current_time.isoformat(),
"online_count": online_count,
"playing_count": playing_count
}
# 调整时间戳到区间结束时间
now = datetime.utcnow()
interval_end = now.replace(minute=(now.minute // 30) * 30, second=0, microsecond=0)
if now.minute % 30 != 0 or now.second != 0:
interval_end += timedelta(minutes=30)
history_point = {
"timestamp": interval_end.isoformat(),
"online_count": current_stats["online_count"],
"playing_count": current_stats["playing_count"]
}
# 添加到历史记录
await _redis_exec(redis_sync.lpush, REDIS_ONLINE_HISTORY_KEY, json.dumps(history_point))
# 只保留48个数据点24小时每30分钟一个点
await _redis_exec(redis_sync.ltrim, REDIS_ONLINE_HISTORY_KEY, 0, 47)
# 设置过期时间为26小时确保有足够缓冲
await redis_async.expire(REDIS_ONLINE_HISTORY_KEY, 26 * 3600)
logger.info(f"Finalized interval stats: online={current_stats['online_count']}, playing={current_stats['playing_count']} at {interval_end.strftime('%H:%M:%S')}")
return True
except Exception as e:
logger.error(f"Error finalizing current interval: {e}")
return False
# 便捷函数
async def update_user_activity_stats() -> None:
"""在用户活动时更新统计(登录、开始游玩等)"""
await IntervalStatsManager.update_current_interval()
async def get_enhanced_current_stats() -> Dict:
"""获取增强的当前统计,包含区间数据"""
from app.router.v2.stats import get_server_stats
# 获取基础统计
current_stats = await get_server_stats()
# 获取区间统计
interval_stats = await IntervalStatsManager.get_current_interval_stats()
result = {
"registered_users": current_stats.registered_users,
"online_users": current_stats.online_users,
"playing_users": current_stats.playing_users,
"timestamp": current_stats.timestamp.isoformat(),
"interval_data": interval_stats
}
return result