refactor(test, config, router): 整理代码结构与配置,优化导入顺序

1. 调整多个测试文件中TestClient的导入顺序,统一放到conftest导入之后
2. 删除后端配置中无用的WebSocket限流配置,统一使用全局200次/10秒的限流策略
3. 精简平台、系统模块的路由初始化注释文档
4. 优化conftest.py中的数据库初始化导入顺序
5. 调整初始化日志输出格式,更清晰易懂
6. 前端开发环境配置切回本地后端地址,注释远程服务地址
7. 重构路由类的变量命名,提升可读性
8. 简化动态路由热重载逻辑,移除冗余缓存变量
9. 重构注册路由的代码,统一限流配置,调整导入顺序和挂载逻辑
10. 删除冗余的v1接口层统一导出文件
This commit is contained in:
zhangtao
2026-06-21 21:47:32 +08:00
parent 0b38612cb2
commit 8949f6dc46
18 changed files with 32 additions and 154 deletions
-75
View File
@@ -1,75 +0,0 @@
"""数据模型层 — 统一导入入口
本模块集中导出所有 ORM 模型,方便 Alembic 迁移脚本、
Service 层及其他模块引用。
模型自动发现机制(ImportUtil.find_models)已覆盖以下路径:
- app/api/v1/module_system/*/model.py
- app/api/v1/module_system/*/*/model.py
- app/api/v1/module_platform/*/model.py
- app/api/v1/module_platform/*/*/model.py
- app/plugin/*/model.py
- app/plugin/*/*/model.py
新增模型可直接参考已有文件完成定义。
"""
from typing import TYPE_CHECKING
# 使用 TYPE_CHECKING 避免循环导入
if TYPE_CHECKING:
from app.api.v1.module_platform.email.model import (
EmailConfigModel,
EmailLogModel,
EmailTemplateModel,
)
from app.api.v1.module_platform.invoice.model import InvoiceModel
from app.api.v1.module_platform.menu.model import MenuModel
from app.api.v1.module_platform.order.model import OrderModel, PaymentRecordModel, RefundModel
from app.api.v1.module_platform.package.model import PackageMenuModel, PackageModel
from app.api.v1.module_platform.plugin.model import PluginModel
from app.api.v1.module_platform.tenant.model import TenantModel, TenantUserModel
from app.api.v1.module_system.dept.model import DeptModel
from app.api.v1.module_system.dict.model import DictDataModel, DictTypeModel
from app.api.v1.module_system.log.model import LoginLogModel, OperationLogModel
from app.api.v1.module_system.notice.model import NoticeModel
from app.api.v1.module_system.params.model import ParamsModel
from app.api.v1.module_system.position.model import PositionModel
from app.api.v1.module_system.role.model import RoleDeptsModel, RoleMenusModel, RoleModel
from app.api.v1.module_system.ticket.model import TicketModel
from app.api.v1.module_system.user.model import (
UserModel,
UserPositionsModel,
UserRolesModel,
)
__all__ = [
"DeptModel",
"DictDataModel",
"DictTypeModel",
"EmailConfigModel",
"EmailLogModel",
"EmailTemplateModel",
"InvoiceModel",
"LoginLogModel",
"MenuModel",
"NoticeModel",
"OperationLogModel",
"OrderModel",
"PackageMenuModel",
"PackageModel",
"ParamsModel",
"PaymentRecordModel",
"PluginModel",
"PositionModel",
"RefundModel",
"RoleDeptsModel",
"RoleMenusModel",
"RoleModel",
"TenantModel",
"TenantUserModel",
"TicketModel",
"UserModel",
"UserPositionsModel",
"UserRolesModel",
]
@@ -1,17 +1,3 @@
"""
平台级模块 - module_platform
包含平台级管理功能,不受租户隔离限制:
- 租户管理 (tenant)
- 套餐管理 (package)
- 插件管理 (plugin)
- 邮件服务 (email)
- 订单与支付 (order)
- 发票管理 (invoice)
- 租户自助服务 (self_service)
- 菜单管理 (menu)
"""
from fastapi import APIRouter from fastapi import APIRouter
from app.api.v1.module_platform.email.controller import EmailRouter from app.api.v1.module_platform.email.controller import EmailRouter
@@ -1,19 +1,3 @@
"""
系统级模块 - module_system
租户内部管理功能,受租户隔离限制:
- 认证授权 (auth)
- 用户管理 (user)
- 角色管理 (role)
- 部门管理 (dept)
- 岗位管理 (position)
- 字典管理 (dict)
- 公告管理 (notice)
- 参数管理 (params)
- 日志管理 (log)
- 工单管理 (ticket)
"""
from fastapi import APIRouter from fastapi import APIRouter
from app.api.v1.module_system.auth.controller import AuthRouter from app.api.v1.module_system.auth.controller import AuthRouter
-2
View File
@@ -250,8 +250,6 @@ class Settings(BaseSettings):
REQUEST_LIMITER_REDIS_PREFIX: str = "fastapiadmin:request_limiter:" REQUEST_LIMITER_REDIS_PREFIX: str = "fastapiadmin:request_limiter:"
RATE_LIMITER_TIMES: int = 200 RATE_LIMITER_TIMES: int = 200
RATE_LIMITER_SECONDS: int = 10 RATE_LIMITER_SECONDS: int = 10
WS_RATE_LIMITER_TIMES: int = 1
WS_RATE_LIMITER_SECONDS: int = 5
# ================================================= # # ================================================= #
# ******************* 重构配置 ******************* # # ******************* 重构配置 ******************* #
+3 -11
View File
@@ -178,22 +178,14 @@ def reload_dynamic_router() -> APIRouter:
# ── 2. 清除插件模块缓存,迫使 importlib 重新执行模块代码 ── # ── 2. 清除插件模块缓存,迫使 importlib 重新执行模块代码 ──
_purge_plugin_modules() _purge_plugin_modules()
# ── 3. 重新扫描并构建 ── # ── 3. 将新路由挂载回 app ──
_dynamic_router_cache = _build_dynamic_router()
# ── 4. 将新路由挂载回 app ──
if app: if app:
# 构造与 init_app.py 中相同的依赖项 # 构造与 init_app.py 中相同的依赖项
try: app.include_router(_build_dynamic_router())
from fastapi_limiter.depends import RateLimiter
rate_deps = [Depends(RateLimiter(times=200, seconds=10))]
except Exception:
rate_deps = []
app.include_router(_dynamic_router_cache, dependencies=rate_deps)
logger.info("✅ 新插件路由已挂载到运行中的 app") logger.info("✅ 新插件路由已挂载到运行中的 app")
logger.info("✅ 插件动态路由热重载完成") logger.info("✅ 插件动态路由热重载完成")
return _dynamic_router_cache return _build_dynamic_router()
def _purge_plugin_modules() -> None: def _purge_plugin_modules() -> None:
+2 -2
View File
@@ -29,11 +29,11 @@ class OperationLogRoute(APIRoute):
"""操作日志路由 — 自动记录请求/响应并后台异步写入""" """操作日志路由 — 自动记录请求/响应并后台异步写入"""
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
original = super().get_route_handler() original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response: async def custom_route_handler(request: Request) -> Response:
start = time.time() start = time.time()
response: Response = await original(request) response: Response = await original_route_handler(request)
if not settings.OPERATION_LOG_RECORD or request.method not in settings.OPERATION_RECORD_METHOD: if not settings.OPERATION_LOG_RECORD or request.method not in settings.OPERATION_RECORD_METHOD:
return response return response
+9 -8
View File
@@ -97,18 +97,19 @@ def register_routers(app: FastAPI) -> None:
from app.api.v1.module_monitor import monitor_router from app.api.v1.module_monitor import monitor_router
from app.api.v1.module_platform import platform_router from app.api.v1.module_platform import platform_router
from app.api.v1.module_system import system_router from app.api.v1.module_system import system_router
from app.core.discover import get_dynamic_router, set_app_ref
app.include_router(common_router, dependencies=[Depends(RateLimiter(times=200, seconds=10))])
app.include_router(monitor_router, dependencies=[Depends(RateLimiter(times=200, seconds=10))])
app.include_router(platform_router, dependencies=[Depends(RateLimiter(times=200, seconds=10))])
app.include_router(system_router, dependencies=[Depends(RateLimiter(times=200, seconds=10))])
from app.plugin.module_ai.chat.ws import WS_AI from app.plugin.module_ai.chat.ws import WS_AI
app.include_router(router=WS_AI, dependencies=[Depends(WebSocketRateLimiter(times=200, seconds=10))])
for router in (common_router, system_router, platform_router, monitor_router): from app.core.discover import get_dynamic_router, set_app_ref
app.include_router(router, dependencies=[Depends(RateLimiter(times=settings.RATE_LIMITER_TIMES, seconds=settings.RATE_LIMITER_SECONDS))]) app.include_router(router=get_dynamic_router(), dependencies=[Depends(RateLimiter(times=200, seconds=10))])
app.include_router(router=get_dynamic_router(), dependencies=[Depends(RateLimiter(times=settings.RATE_LIMITER_TIMES, seconds=settings.RATE_LIMITER_SECONDS))])
set_app_ref(app) set_app_ref(app)
app.include_router(router=WS_AI, dependencies=[Depends(WebSocketRateLimiter(times=settings.WS_RATE_LIMITER_TIMES, seconds=settings.WS_RATE_LIMITER_SECONDS))])
def register_files(app: FastAPI) -> None: def register_files(app: FastAPI) -> None:
if settings.STATIC_ENABLE: if settings.STATIC_ENABLE:
settings.STATIC_ROOT.mkdir(parents=True, exist_ok=True) settings.STATIC_ROOT.mkdir(parents=True, exist_ok=True)
+1 -1
View File
@@ -164,7 +164,7 @@ class InitializeData:
if table_name in ("sys_login_log", "sys_operation_log"): if table_name in ("sys_login_log", "sys_operation_log"):
count = await db.execute(select(func.count()).select_from(model)) count = await db.execute(select(func.count()).select_from(model))
if count.scalar(): if count.scalar():
logger.info(f"⏭️ {table_name} 已有数据,跳过") logger.info(f"⏭️ 跳过 {table_name} 表数据初始化(表已有数据)")
continue continue
objs = [model(**item) for item in data] objs = [model(**item) for item in data]
db.add_all(objs) db.add_all(objs)
+4 -3
View File
@@ -186,11 +186,12 @@ async def _test_lifespan(app) -> AsyncGenerator[Any, None]:
app.state.redis = _mock_redis app.state.redis = _mock_redis
# 将 admin 密码重置为已知密码 "admin123" # 将 admin 密码重置为已知密码 "admin123"
from app.core.database import async_db_session
from app.api.v1.module_system.user.model import UserModel
from app.utils.hash_bcrpy_util import PwdUtil
from sqlalchemy import update from sqlalchemy import update
from app.api.v1.module_system.user.model import UserModel
from app.core.database import async_db_session
from app.utils.hash_bcrpy_util import PwdUtil
async with async_db_session() as db: async with async_db_session() as db:
await db.execute( await db.execute(
update(UserModel) update(UserModel)
+1 -2
View File
@@ -5,9 +5,8 @@
每个接口一个测试用例,覆盖会话 CRUD 与 AI 对话。 每个接口一个测试用例,覆盖会话 CRUD 与 AI 对话。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route from conftest import assert_route
from fastapi.testclient import TestClient
class TestAiChat: class TestAiChat:
+1 -2
View File
@@ -3,9 +3,8 @@
每个接口一个测试用例,验证路由存在且返回码正确。 每个接口一个测试用例,验证路由存在且返回码正确。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route from conftest import assert_route
from fastapi.testclient import TestClient
class TestHealth: class TestHealth:
+1 -2
View File
@@ -5,9 +5,8 @@
每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。 每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route from conftest import assert_route
from fastapi.testclient import TestClient
class TestExampleDemo: class TestExampleDemo:
+1 -2
View File
@@ -5,9 +5,8 @@
每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。 每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route from conftest import assert_route
from fastapi.testclient import TestClient
class TestGenerator: class TestGenerator:
+1 -2
View File
@@ -3,9 +3,8 @@
认证数据测试:admin 登录后验证监控接口数据。 认证数据测试:admin 登录后验证监控接口数据。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route # noqa: F401 from conftest import assert_route # noqa: F401
from fastapi.testclient import TestClient
class TestCache: class TestCache:
+1 -2
View File
@@ -3,9 +3,8 @@
认证数据测试:admin 登录后验证 CRUD 真实数据。 认证数据测试:admin 登录后验证 CRUD 真实数据。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route # noqa: F401 from conftest import assert_route # noqa: F401
from fastapi.testclient import TestClient
class TestTenant: class TestTenant:
+1 -2
View File
@@ -3,9 +3,8 @@
认证数据测试:admin 登录后验证 CRUD 真实数据。 认证数据测试:admin 登录后验证 CRUD 真实数据。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route # noqa: F401 from conftest import assert_route # noqa: F401
from fastapi.testclient import TestClient
class TestAuth: class TestAuth:
+1 -3
View File
@@ -7,10 +7,8 @@
每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。 每个接口一个测试用例,覆盖查询 / 新增 / 修改 / 删除 等操作。
""" """
from fastapi.testclient import TestClient
from conftest import assert_route from conftest import assert_route
from fastapi.testclient import TestClient
# ============================================================ # ============================================================
# /task/cronjob — 调度器与任务 # /task/cronjob — 调度器与任务
+4 -4
View File
@@ -10,12 +10,12 @@ VITE_APP_TITLE = FastapiAdmin
VITE_API_URL = / VITE_API_URL = /
# 代理目标:本机后端(若出现 ENOTFOUND,说明上面域名在当前网络/DNS 下不可解析,改用本地或可用地址) # 代理目标:本机后端(若出现 ENOTFOUND,说明上面域名在当前网络/DNS 下不可解析,改用本地或可用地址)
# VITE_API_BASE_URL = http://127.0.0.1:8001 VITE_API_BASE_URL = http://127.0.0.1:8001
VITE_API_BASE_URL = https://service.fastapiadmin.com # VITE_API_BASE_URL = https://service.fastapiadmin.com
# 是否删除控制台输出 # 是否删除控制台输出
VITE_DROP_CONSOLE = false VITE_DROP_CONSOLE = false
# WebSocket 端点(AI对话功能需要配置) # WebSocket 端点(AI对话功能需要配置)
# VITE_APP_WS_ENDPOINT = ws://localhost:8001 VITE_APP_WS_ENDPOINT = ws://localhost:8001
VITE_APP_WS_ENDPOINT = wss://service.fastapiadmin.com # VITE_APP_WS_ENDPOINT = wss://service.fastapiadmin.com