import json from datetime import datetime from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from models import LoginLog, OperationLog def get_client_ip(request: Request) -> str: """从请求中提取客户端IP地址,支持代理转发""" xff = request.headers.get("X-Forwarded-For") if xff: return xff.split(",")[0].strip() xri = request.headers.get("X-Real-IP") if xri: return xri.strip() return request.client.host if request.client else "unknown" def get_user_agent(request: Request) -> str: """从请求中提取 User-Agent""" return request.headers.get("User-Agent", "") async def save_login_log( db: AsyncSession, username: str, ip_address: str, user_agent: str, status: str, failure_reason: str | None = None, ): """保存登录日志(独立提交,确保失败记录不丢失)""" log = LoginLog( username=username, login_time=datetime.utcnow(), ip_address=ip_address, user_agent=user_agent, status=status, failure_reason=failure_reason, ) db.add(log) await db.commit() async def create_operation_log( db: AsyncSession, user_id: int, username: str, action_type: str, target_type: str, target_id: int | None = None, target_name: str | None = None, detail: dict | None = None, ip_address: str = "", user_agent: str = "", ): """创建操作日志(不提交,由调用方事务统一提交)""" log = OperationLog( user_id=user_id, username=username, action_type=action_type, target_type=target_type, target_id=target_id, target_name=target_name, detail=json.dumps(detail, ensure_ascii=False) if detail else None, ip_address=ip_address, user_agent=user_agent, createtime=datetime.utcnow(), ) db.add(log)