#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ PerToolBox Server - 热度统计路由 Copyright (C) 2024 Sea Network Technology Studio Author: Canglan License: AGPL v3 """ from datetime import datetime from fastapi import APIRouter from ...utils.redis_client import redis_client from ...models import ToolStatsTotal from ...dependencies import DbDependency from ...middleware.rate_limit import rate_limit router = APIRouter(prefix="/api/v1", tags=["stats"]) # 预定义工具名称(对应前端页面) TOOL_NAMES = [ "todos", "notes", "password", "qrcode", "crypto_hash", "crypto_base64", "crypto_url", "crypto_aes", "json" ] @router.post("/tool/usage") @rate_limit(requests=20, period=60) async def record_usage(tool_name: str, db: DbDependency): """记录页面访问次数(热度)""" if tool_name not in TOOL_NAMES: raise HTTPException(status_code=400, detail="无效的工具名") today = datetime.now().strftime("%Y-%m-%d") today_key = f"tool:stats:today:{tool_name}:{today}" total_key = f"tool:stats:total:{tool_name}" # 增加今日计数(设置48小时过期) today_count = redis_client.incr(today_key) redis_client.expire(today_key, 48 * 3600) # 增加总计数 total_count = redis_client.incr(total_key) # 异步更新 MySQL(可选,这里简单处理) # 实际可改为定时任务同步,此处为简化,直接更新 stats = db.query(ToolStatsTotal).filter(ToolStatsTotal.tool_name == tool_name).first() if stats: stats.total_count = total_count else: stats = ToolStatsTotal(tool_name=tool_name, total_count=total_count) db.add(stats) db.commit() return {"success": True} @router.get("/tool/stats") @rate_limit(requests=100, period=60) async def get_stats(db: DbDependency): """获取所有工具的今日/总访问次数""" today = datetime.now().strftime("%Y-%m-%d") result = {} for tool_name in TOOL_NAMES: today_key = f"tool:stats:today:{tool_name}:{today}" total_key = f"tool:stats:total:{tool_name}" today_count = redis_client.get(today_key) total_count = redis_client.get(total_key) if total_count is None: # 从 MySQL 读取 stats = db.query(ToolStatsTotal).filter(ToolStatsTotal.tool_name == tool_name).first() total_count = stats.total_count if stats else 0 else: total_count = int(total_count) result[tool_name] = { "today": int(today_count) if today_count else 0, "total": total_count } return result