Files
g0v0-server/app/service/interval_stats.py
2025-08-22 05:02:24 +08:00

149 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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