import json import os import time from threading import Lock import atexit # --- 配置 --- TIMEOUT_SECONDS = 300 # 5分钟无活动视为离线 STATS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data_usage") STATS_FILE = os.path.join(STATS_DIR, "usage_stats.json") SAVE_INTERVAL_SECONDS = int(os.environ.get("USAGE_SAVE_INTERVAL", "60")) # --- 全局变量 --- active_users = {} # {session_id: last_active_timestamp} users_lock = Lock() stats_lock = Lock() total_users = 0 seen_sessions = set() _LAST_SAVE_TS = 0.0 # --- 内部函数 --- def _ensure_stats_dir(): try: os.makedirs(STATS_DIR, exist_ok=True) except OSError: pass def _load_usage_stats(): global total_users, _LAST_SAVE_TS try: if os.path.exists(STATS_FILE): with open(STATS_FILE, "r", encoding="utf-8") as f: data = json.load(f) total_users = int(data.get("total_users", 0)) _LAST_SAVE_TS = float(data.get("last_saved_at", time.time())) except (IOError, json.JSONDecodeError): total_users = 0 _LAST_SAVE_TS = time.time() def _save_usage_stats(): global _LAST_SAVE_TS try: with stats_lock: tmp_path = STATS_FILE + ".tmp" with open(tmp_path, "w", encoding="utf-8") as f: json.dump({"total_users": total_users, "last_saved_at": time.time()}, f, ensure_ascii=False, indent=2) os.replace(tmp_path, STATS_FILE) _LAST_SAVE_TS = time.time() except (IOError, OSError): pass def _maybe_save_usage_stats(): if time.time() - _LAST_SAVE_TS >= SAVE_INTERVAL_SECONDS: _save_usage_stats() # --- 外部接口 --- def update_user_activity(session_id: str): """更新用户活跃时间并在首次出现时累计总人数。""" current_time = time.time() with users_lock: active_users[session_id] = current_time with stats_lock: global total_users if session_id not in seen_sessions: seen_sessions.add(session_id) total_users += 1 def get_active_users_count(): """获取当前活跃用户数量""" current_time = time.time() with users_lock: expired_users = [uid for uid, last_time in active_users.items() if current_time - last_time > TIMEOUT_SECONDS] for uid in expired_users: del active_users[uid] return len(active_users) def get_online_status_html(): """生成在线人数与总人数的显示HTML,并按需触发持久化。""" count = get_active_users_count() with stats_lock: total = total_users _maybe_save_usage_stats() return f"""