""" 管理员仪表盘 API """ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from sqlalchemy import func from ...database import get_db, SessionLocal from ...models import ( User, AIModel, Order, TokenUsageLog, OperationLog, AuditLog ) from ...auth import get_current_admin from ...middleware.log_service import LogService from datetime import datetime, date, timezone, timedelta router = APIRouter() # CST timezone (UTC+8) CST_TZ = timezone(timedelta(hours=8)) @router.get("/stats") async def get_dashboard_stats( admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """获取管理员仪表盘统计数据""" # Use CST timezone for "today" since container uses UTC but users are in CST now_cst = datetime.now(CST_TZ) today_cst = now_cst.date() # Convert CST today start to UTC for DB comparison today_start_utc = datetime(now_cst.year, now_cst.month, now_cst.day, 0, 0, 0, tzinfo=CST_TZ).astimezone(timezone.utc).replace(tzinfo=None) active_users = db.query(func.count(User.id)).filter( User.status == "active" ).scalar() or 0 enabled_models = db.query(func.count(AIModel.id)).filter( AIModel.is_enabled == True ).scalar() or 0 # Use range comparison instead of func.date() for timezone-correct results today_orders = db.query(func.count(Order.id)).filter( Order.created_at >= today_start_utc ).scalar() or 0 today_tokens = db.query(func.sum(TokenUsageLog.total_tokens)).filter( TokenUsageLog.created_at >= today_start_utc ).scalar() or 0 total_tokens = db.query(func.sum(TokenUsageLog.total_tokens)).scalar() or 0 return { "active_users": active_users, "enabled_models": enabled_models, "today_orders": today_orders, "today_tokens": int(today_tokens), "total_tokens": int(total_tokens), "server_time": now_cst.isoformat(), } @router.get("/recent-logs") async def get_recent_logs( admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """获取最近10条操作日志""" logs = db.query(OperationLog).order_by( OperationLog.created_at.desc() ).limit(10).all() return [ { "id": log.id, "user_id": log.user_id, "username": log.username, "action": log.action, "target": log.target, "target_id": log.target_id, "detail": log.detail, "ip": log.ip, "created_at": _format_cst(log.created_at), } for log in logs ] def _format_cst(dt_value): """Format UTC naive datetime to CST ISO string""" if dt_value is None: return None utc_dt = dt_value.replace(tzinfo=timezone.utc) cst_dt = utc_dt.astimezone(CST_TZ) return cst_dt.isoformat() @router.get("/recent-audit") async def get_recent_audit( admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """获取最近10条审计日志""" logs = db.query(AuditLog).order_by( AuditLog.created_at.desc() ).limit(10).all() return [ { "id": log.id, "admin_id": log.admin_id, "admin_name": log.admin_name, "action": log.action, "target_type": log.target_type, "target_id": log.target_id, "detail": log.detail, "created_at": _format_cst(log.created_at), } for log in logs ] def _format_cst(dt_value): """Format UTC naive datetime to CST ISO string""" if dt_value is None: return None utc_dt = dt_value.replace(tzinfo=timezone.utc) cst_dt = utc_dt.astimezone(CST_TZ) return cst_dt.isoformat()