| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- """
- 日志管理接口
- """
- 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
- )
|