Files
FastapiAdmin/backend/app/core/router_class.py
T
zhangtao a86233d6c2 refactor: 完成项目整体架构升级与依赖优化
此提交包含多项核心优化:
1.  依赖库升级:更新FastAPI、替换fastapi-limiter为slowapi,更换MySQL驱动为aiomysql,重构缓存系统使用fastapi-cache2-fork
2.  代码重构:移除自定义缓存工具,统一使用fastapi-cache2;重构请求限流逻辑,修复操作日志记录开关逻辑
3.  接口规范:统一将Depends分页/查询参数改为Query注解,补全File上传依赖注解
4.  前端优化:调整登录滑块进度条颜色、移除冗余demo页面样式类
5.  日志优化:重构日志配置,使用time.perf_counter替代time.time统计耗时
6.  测试优化:移除冗余的限流mock配置,改用官方限流方案
7.  模板优化:更新vue代码生成模板,移除冗余导入并优化组件使用
8.  导入功能优化:移除pandas依赖,使用Excel工具类重构文件导入逻辑
2026-07-01 00:35:28 +08:00

86 lines
3.7 KiB
Python

import json
import time
from collections.abc import Callable, Coroutine
from typing import Any
from fastapi import Request, Response
from fastapi.routing import APIRoute
from starlette.background import BackgroundTask
from app.config.setting import settings
from app.core.base_schema import AuthSchema
from app.core.database import async_db_session
from app.core.logger import logger
async def _write_operation_log_async(log_data: dict) -> None:
from app.api.v1.module_system.log.schema import OperationLogCreateSchema
from app.api.v1.module_system.log.service import OperationLogService
try:
async with async_db_session() as _session:
async with _session.begin():
_auth = AuthSchema(db=_session)
await OperationLogService(_auth).create(data=OperationLogCreateSchema(**log_data))
except Exception:
logger.exception("操作日志写入失败: path={}", log_data.get("request_path"))
class OperationLogRoute(APIRoute):
"""操作日志路由 — 自动记录请求/响应并后台异步写入"""
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
start = time.time()
response: Response = await original_route_handler(request)
if request.method not in settings.OPERATION_RECORD_METHOD:
return response
route: APIRoute = request.scope.get("route", None)
try:
oper_param: dict[str, Any] = {}
content_type = request.headers.get("Content-Type", "")
if content_type.startswith(("multipart/form-data", "application/x-www-form-urlencoded")):
form_data = await request.form()
oper_param["form"] = dict(form_data.items())
else:
payload = await request.body()
if payload:
try:
oper_param["body"] = json.loads(payload.decode())
except (json.JSONDecodeError, UnicodeDecodeError):
oper_param["body"] = payload.decode("utf-8", errors="ignore")
if request.path_params:
oper_param["path_params"] = dict(request.path_params)
log_payload = json.dumps(oper_param, ensure_ascii=False)
if len(log_payload) > 2000:
log_payload = "请求参数过长"
is_json = "application/json" in response.headers.get("Content-Type", "")
response_data = response.body if is_json else b"{}"
ctx = getattr(request.state, "ctx", None)
current_user_id = ctx.user_id if ctx else None
log_data: dict[str, Any] = {
"request_path": request.url.path,
"request_method": request.method,
"request_payload": log_payload,
"response_code": response.status_code,
"response_json": response_data.decode(),
"process_time": f"{(time.time() - start):.2f}s",
"description": route.summary if route else "",
"created_id": current_user_id,
"updated_id": current_user_id,
}
response.background = BackgroundTask(_write_operation_log_async, log_data)
except Exception:
logger.warning("操作日志采集异常: {}", request.url.path, exc_info=True)
return response
return custom_route_handler