"""操作审计日志 API。""" from typing import Optional from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, desc from app.core.database import get_db from app.core.security import require_role from app.models.user import User from app.models.operation_log import OperationLog router = APIRouter(prefix="/audit-logs", tags=["audit-logs"]) @router.get("") async def list_audit_logs( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), action: Optional[str] = Query(None), resource_type: Optional[str] = Query(None), username: Optional[str] = Query(None), db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """查询操作审计日志(仅管理员)。""" query = select(OperationLog) count_query = select(func.count(OperationLog.id)) if action: query = query.where(OperationLog.action == action) count_query = count_query.where(OperationLog.action == action) if resource_type: query = query.where(OperationLog.resource_type == resource_type) count_query = count_query.where(OperationLog.resource_type == resource_type) if username: query = query.where(OperationLog.username.contains(username)) count_query = count_query.where(OperationLog.username.contains(username)) total = (await db.execute(count_query)).scalar() or 0 query = query.order_by(desc(OperationLog.created_at)) query = query.offset((page - 1) * page_size).limit(page_size) result = await db.execute(query) logs = result.scalars().all() return { "total": total, "page": page, "page_size": page_size, "items": [ { "id": l.id, "user_id": l.user_id, "username": l.username, "action": l.action, "resource_type": l.resource_type, "resource_id": l.resource_id, "detail": l.detail, "ip_address": l.ip_address, "created_at": l.created_at.isoformat() if l.created_at else None, } for l in logs ], } @router.get("/stats") async def audit_log_stats( db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """审计日志统计概览。""" # 最近7天操作分布 from datetime import datetime, timedelta, timezone since = datetime.now(timezone.utc) - timedelta(days=7) result = await db.execute( select(OperationLog.action, func.count(OperationLog.id)) .where(OperationLog.created_at >= since) .group_by(OperationLog.action) ) action_stats = {row[0]: row[1] for row in result.all()} # 操作最多的用户 result = await db.execute( select(OperationLog.username, func.count(OperationLog.id)) .where(OperationLog.created_at >= since) .group_by(OperationLog.username) .order_by(desc(func.count(OperationLog.id))) .limit(5) ) top_users = [{"username": row[0], "count": row[1]} for row in result.all()] return {"action_stats": action_stats, "top_users": top_users}