""" 日志管理接口 """ from fastapi import APIRouter, Depends, Query from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session from sqlalchemy import or_, and_ from app.database import get_db from app.api.deps import get_current_admin from app.models.api_log import APILog from datetime import datetime, timedelta import csv import io router = APIRouter() @router.get("") async def get_logs( page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), keyword: str = Query(None, description="搜索关键词(接口路径)"), user_id: int = Query(None, description="用户ID筛选"), endpoint: str = Query(None, description="接口筛选"), method: str = Query(None, description="方法筛选"), response_code: int = Query(None, description="状态码筛选"), start_time: str = Query(None, description="开始时间"), end_time: str = Query(None, description="结束时间"), db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """获取日志列表""" offset = (page - 1) * page_size query = db.query(APILog) # 搜索 if keyword: query = query.filter(APILog.endpoint.like(f"%{keyword}%")) # 筛选 if user_id is not None: query = query.filter(APILog.user_id == user_id) if endpoint: query = query.filter(APILog.endpoint.like(f"%{endpoint}%")) if method: query = query.filter(APILog.method == method) if response_code is not None: query = query.filter(APILog.response_code == response_code) if start_time: try: start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00')) query = query.filter(APILog.created_at >= start_dt) except: pass if end_time: try: end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00')) query = query.filter(APILog.created_at <= end_dt) except: pass # 排序和分页 total = query.count() logs = query.order_by(APILog.created_at.desc()).offset(offset).limit(page_size).all() return { "code": 200, "msg": "成功", "data": [ { "id": l.id, "user_id": l.user_id, "endpoint": l.endpoint, "method": l.method, "ip_address": l.ip_address, "user_agent": l.user_agent, "response_code": l.response_code, "execution_time": l.execution_time, "created_at": l.created_at.isoformat() if l.created_at else None } for l in logs ], "total": total, "page": page, "page_size": page_size } @router.get("/{log_id}") async def get_log_detail( log_id: int, db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """获取日志详情""" log = db.query(APILog).filter(APILog.id == log_id).first() if not log: return {"code": 404, "msg": "日志不存在"} return { "code": 200, "msg": "成功", "data": { "id": log.id, "user_id": log.user_id, "endpoint": log.endpoint, "method": log.method, "request_data": log.request_data, "response_data": log.response_data, "ip_address": log.ip_address, "user_agent": log.user_agent, "response_code": log.response_code, "execution_time": log.execution_time, "created_at": log.created_at.isoformat() if log.created_at else None } } @router.delete("") async def clear_logs( db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """清空日志""" try: db.query(APILog).delete() db.commit() return {"code": 200, "msg": "清空成功"} except Exception as e: db.rollback() return {"code": 500, "msg": f"清空失败: {str(e)}"} @router.get("/export/csv") async def export_logs_csv( keyword: str = Query(None), user_id: int = Query(None), endpoint: str = Query(None), method: str = Query(None), response_code: int = Query(None), start_time: str = Query(None), end_time: str = Query(None), db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """导出日志为CSV""" query = db.query(APILog) # 应用相同的筛选条件 if keyword: query = query.filter(APILog.endpoint.like(f"%{keyword}%")) if user_id is not None: query = query.filter(APILog.user_id == user_id) if endpoint: query = query.filter(APILog.endpoint.like(f"%{endpoint}%")) if method: query = query.filter(APILog.method == method) if response_code is not None: query = query.filter(APILog.response_code == response_code) if start_time: try: start_dt = datetime.fromisoformat(start_time.replace('Z', '+00:00')) query = query.filter(APILog.created_at >= start_dt) except: pass if end_time: try: end_dt = datetime.fromisoformat(end_time.replace('Z', '+00:00')) query = query.filter(APILog.created_at <= end_dt) except: pass logs = query.order_by(APILog.created_at.desc()).all() # 生成CSV output = io.StringIO() writer = csv.writer(output) writer.writerow(['ID', '用户ID', '接口', '方法', 'IP地址', '用户代理', '状态码', '执行时间(ms)', '创建时间']) for log in logs: writer.writerow([ log.id, log.user_id or '', log.endpoint, log.method, log.ip_address or '', log.user_agent or '', log.response_code or '', log.execution_time or '', log.created_at.isoformat() if log.created_at else '' ]) output.seek(0) return StreamingResponse( iter([output.getvalue()]), media_type="text/csv", headers={"Content-Disposition": f"attachment; filename=logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"} ) @router.get("/export/excel") async def export_logs( keyword: str = Query(None), user_id: int = Query(None), endpoint: str = Query(None), method: str = Query(None), response_code: int = Query(None), start_time: str = Query(None), end_time: str = Query(None), db: Session = Depends(get_db), current_admin = Depends(get_current_admin) ): """导出日志为Excel""" # 暂时使用CSV格式,如果需要Excel格式可以安装openpyxl return await export_logs_csv( keyword=keyword, user_id=user_id, endpoint=endpoint, method=method, response_code=response_code, start_time=start_time, end_time=end_time, db=db, current_admin=current_admin )