import logging import time from typing import Any, Dict, Tuple from fastapi import APIRouter, Query, Body, Path, BackgroundTasks from fastapi.background import P from tortoise.expressions import Q from app.controllers.token import token_controller from app.schemas.base import Fail, Success, SuccessExtra from app.schemas.token import BossTokenUpdate,BossTokenCreate logger = logging.getLogger(__name__) token_router = APIRouter() # 简单内存缓存:key 为查询参数组合,value 为 (缓存时间戳, 响应数据) _TOKENS_CACHE: Dict[Tuple[Any, Any, int, int], Tuple[float, Dict[str, Any]]] = {} _CACHE_TTL_SECONDS: int =60 @token_router.get("/tokens", summary="获取Boss Token列表") async def list_boss_tokens( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), status: int = Query(None, description="状态筛选"), ): """获取Boss Token列表""" from tortoise.expressions import Q q = Q() if status is not None: q &= Q(status=status) total, token_objs = await token_controller.get_tokens(page=page, page_size=page_size, search=q) data = [await obj.to_dict() for obj in token_objs] return SuccessExtra(data=data, total=total, page=page, page_size=page_size) @token_router.get("/tokens/{token_id}", summary="获取Boss Token详情") async def get_boss_token( token_id: int = Path(..., description="Token ID"), ): """获取Boss Token详情""" token_obj = await token_controller.get_token(token_id) token_dict = await token_obj.to_dict() return Success(data=token_dict) @token_router.post("/tokens", summary="创建Boss Token") async def create_boss_token( token_data: BossTokenCreate = Body(..., description="Token数据"), ): """创建Boss Token""" await token_controller.create_token(token_data) # 清空缓存,确保新数据立即生效 _TOKENS_CACHE.clear() return Success(msg="创建成功") @token_router.put("/tokens/{token_id}", summary="更新Boss Token") async def update_boss_token( token_id: int = Path(..., description="Token ID"), token_data: BossTokenUpdate = Body(..., description="Token数据"), ): """更新Boss Token""" await token_controller.update_token(token_id, token_data) # 清空缓存,确保更新立即生效 _TOKENS_CACHE.clear() return Success(msg="更新成功") @token_router.delete("/tokens/{token_id}", summary="删除Boss Token") async def delete_boss_token( token_id: int = Path(..., description="Token ID"), ): """删除Boss Token""" await token_controller.delete_token(token_id) # 清空缓存,确保删除立即生效 _TOKENS_CACHE.clear() return Success(msg="删除成功") @token_router.post("/tokens/cache/clear", summary="强制清除Token缓存") async def clear_token_cache(): """强制清除Token列表缓存""" global _TOKENS_CACHE cache_size = len(_TOKENS_CACHE) _TOKENS_CACHE.clear() logger.info(f"手动清除Token缓存,清除了 {cache_size} 条缓存数据") return Success(msg=f"成功清除 {cache_size} 条Token缓存") from typing import Optional, Dict, Any from fastapi import APIRouter, Query, HTTPException from tortoise.transactions import in_transaction from app.models.token import BossToken from app.schemas.base import Success token_router = APIRouter() @token_router.get("/tokens") async def list_tokens( wt2: Optional[str] = Query(None), mpt: Optional[str] = Query(None), page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=200), ): """获取 BossToken 列表,带两小时内存缓存。 Args: wt2 (Optional[str]): 按 `wt2` 模糊匹配。 mpt (Optional[str]): 按 `mpt` 模糊匹配。 page (int): 页码。 page_size (int): 每页数量。 Returns: Dict[str, Any]: 响应字典,包含 `code`、`data`、`total`。 """ cache_key: Tuple[Any, Any, int, int] = (wt2, mpt, page, page_size) now = time.monotonic() cached = _TOKENS_CACHE.get(cache_key) if cached and (now - cached[0] < _CACHE_TTL_SECONDS): return cached[1] qs = BossToken.all() if wt2: qs = qs.filter(wt2__icontains=wt2) if mpt: qs = qs.filter(mpt__icontains=mpt) total = await qs.count() items = await qs.order_by("-id").offset((page - 1) * page_size).limit(page_size) data = [ { "id": item.id, "wt2": item.wt2, "mpt": item.mpt, "is_active": item.is_active, "failed_count": item.failed_count, "last_used_time": item.last_used_time, "created_at": item.created_at, } for item in items ] resp: Dict[str, Any] = {"code": 200, "data": data, "total": total} _TOKENS_CACHE[cache_key] = (now, resp) return resp @token_router.post("/tokens") async def create_token(payload: Dict[str, Any]): try: async with in_transaction(): item = await BossToken.create( wt2=payload.get("wt2"), mpt=payload.get("mpt"), is_active=bool(payload.get("is_active", True)), failed_count=int(payload.get("failed_count", 0)), last_used_time=payload.get("last_used_time"), ) _TOKENS_CACHE.clear() return Success(data={"id": item.id}) except Exception as e: raise HTTPException(status_code=400, detail=str(e)) @token_router.put("/tokens/{id}") async def update_token(id: int, payload: Dict[str, Any]): token_id = id item = await BossToken.get_or_none(id=token_id) if not item: raise HTTPException(status_code=404, detail="Token not found") for field in ["wt2", "mpt", "is_active", "failed_count", "last_used_time"]: if field in payload: setattr(item, field, payload[field]) await item.save() _TOKENS_CACHE.clear() return Success(data={"id": item.id}) @token_router.delete("/tokens/{token_id}") async def delete_token(token_id: int): item = await BossToken.get_or_none(id=token_id) if not item: raise HTTPException(status_code=404, detail="Token not found") await item.delete() _TOKENS_CACHE.clear() return Success(data={"id": token_id})