refactor: 重构项目模块结构与代码细节优化

1.  调整后端模块路由与插件文件结构,迁移部分模块代码至api/v1目录
2.  优化代码注释格式与文档字符串,简化冗余代码
3.  添加监控仪表盘、工单评论等新模块功能
4.  修复前端部分组件交互逻辑与类型定义
5.  清理冗余的初始化数据与废弃配置文件
6.  新增租户注册表单字段与国际化支持
7.  优化HTTP请求拦截器与快捷键功能
This commit is contained in:
zhangtao
2026-07-10 00:18:11 +08:00
parent 349ab7b525
commit 5ff72b086f
323 changed files with 7633 additions and 15306 deletions
@@ -2,7 +2,7 @@ import json
import secrets
from typing import Annotated
from fastapi import APIRouter, BackgroundTasks, Body, Depends, Path, Query, Request
from fastapi import APIRouter, BackgroundTasks, Body, Depends, Path, Query, Request, status
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi_cache import FastAPICache
from fastapi_cache.decorator import cache
@@ -14,8 +14,6 @@ from app.config.setting import settings
from app.core.base_schema import (
AuthSchema,
JWTOutSchema,
LogoutPayloadSchema,
RefreshTokenPayloadSchema,
)
from app.core.dependencies import db_getter, get_current_user, redis_getter
from app.core.exceptions import CustomException
@@ -26,6 +24,7 @@ from app.core.security import CustomOAuth2PasswordRequestForm
from .oauth_service import (
STATE_PREFIX,
OAuthProvider,
_callback_url,
build_authorize_url,
complete_oauth_login,
@@ -34,20 +33,22 @@ from .oauth_service import (
save_oauth_state,
)
from .schema import (
AutoLoginTokenSchema,
AutoLoginUserSchema,
CaptchaOutSchema,
EnterPlatformOutSchema,
ImpersonateOutSchema,
ImpersonateSchema,
LoginWithTenantsSchema,
SelectTenantOutSchema,
SelectTenantSchema,
TenantLookupOutSchema,
TenantOptionSchema,
TenantRegisterOutSchema,
TenantRegisterSchema,
)
from .service import (
AutoLoginService,
CaptchaService,
LoginService,
TenantLookupService,
TenantRegisterService,
)
@@ -56,6 +57,26 @@ AuthRouter = APIRouter(route_class=OperationLogRoute, prefix="/auth", tags=["认
_AUTH_TENANTS_NS = "auth_tenants"
@AuthRouter.get("/tenant/{code}", summary="通过编码查询租户", response_model=ResponseSchema[TenantLookupOutSchema])
async def lookup_tenant_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
code: Annotated[str, Path(description="租户编码")],
) -> JSONResponse:
"""根据租户编码查询租户信息(用于登录页自动加载租户品牌配置)"""
data = await TenantLookupService.lookup_by_code(db=db, code=code)
return SuccessResponse(data=data, msg="查询成功")
@AuthRouter.get("/tenant-by-domain", summary="通过域名查询租户", response_model=ResponseSchema[TenantLookupOutSchema])
async def lookup_tenant_by_domain_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
domain: Annotated[str, Query(description="域名(如 tenant.example.com")],
) -> JSONResponse:
"""根据域名查询租户信息(用于登录页通过访问域名自动识别租户品牌)"""
data = await TenantLookupService.lookup_by_domain(db=db, domain=domain)
return SuccessResponse(data=data, msg="查询成功")
@AuthRouter.post("/login", summary="登录", response_model=LoginWithTenantsSchema)
async def login_for_access_token_controller(
request: Request,
@@ -63,10 +84,8 @@ async def login_for_access_token_controller(
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
login_form: Annotated[CustomOAuth2PasswordRequestForm, Depends()],
) -> JSONResponse | dict:
login_result = await LoginService.authenticate_user(
request=request, redis=redis, login_form=login_form, db=db, background_tasks=background_tasks
)
) -> JSONResponse | LoginWithTenantsSchema:
login_result = await LoginService.authenticate_user(request=request, redis=redis, login_form=login_form, db=db, background_tasks=background_tasks)
logger.info(f"用户{login_form.username}登录成功")
@@ -79,7 +98,7 @@ async def login_for_access_token_controller(
async def get_new_token_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
redis: Annotated[Redis, Depends(redis_getter)],
payload: Annotated[RefreshTokenPayloadSchema, Body(description="刷新token参数")],
payload: Annotated[str, Body(description="刷新token参数")],
) -> JSONResponse:
new_token = await LoginService.refresh_token(db=db, redis=redis, refresh_token=payload)
return SuccessResponse(data=new_token, msg="刷新成功")
@@ -96,7 +115,7 @@ async def get_captcha_for_login_controller(
@AuthRouter.post("/logout", summary="退出登录", response_model=ResponseSchema[None], dependencies=[Depends(get_current_user)])
async def logout_controller(
redis: Annotated[Redis, Depends(redis_getter)],
payload: Annotated[LogoutPayloadSchema, Body(description="退出登录参数")],
payload: Annotated[str, Body(description="退出登录参数")],
) -> JSONResponse:
if await LoginService.logout(redis=redis, token=payload):
logger.info("退出成功")
@@ -104,40 +123,6 @@ async def logout_controller(
return ErrorResponse(msg="退出失败")
@AuthRouter.get("/auto-login/users", summary="获取免登录用户列表", response_model=ResponseSchema[list[AutoLoginUserSchema]])
async def get_auto_login_users_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
db: Annotated[AsyncSession, Depends(db_getter)],
) -> JSONResponse:
tenant_id = None if auth.user.is_superuser else auth.user.tenant_id
users = await AutoLoginService.get_auto_login_users(db=db, tenant_id=tenant_id)
return SuccessResponse(data=users, msg="获取成功")
@AuthRouter.post("/auto-login/token", summary="获取免登录Token", response_model=ResponseSchema[AutoLoginTokenSchema])
async def get_auto_login_token_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
user_id: Annotated[int, Body(description="用户ID")],
) -> JSONResponse:
tenant_id = None if auth.user.is_superuser else auth.user.tenant_id
result = await AutoLoginService.create_auto_login_token(redis=redis, db=db, user_id=user_id, tenant_id=tenant_id)
return SuccessResponse(data=result, msg="获取成功")
@AuthRouter.post("/auto-login", summary="免登录", response_model=ResponseSchema[JWTOutSchema])
async def auto_login_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
token: Annotated[str, Body(description="免登录Token")],
) -> JSONResponse:
login_token = await AutoLoginService.auto_login(request=request, redis=redis, db=db, token=token)
logger.info("用户免登录成功")
return SuccessResponse(data=login_token, msg="登录成功")
@AuthRouter.post("/select-tenant", summary="选择租户", response_model=ResponseSchema[SelectTenantOutSchema], dependencies=[Depends(get_current_user)])
async def select_tenant_controller(
request: Request,
@@ -150,6 +135,17 @@ async def select_tenant_controller(
return SuccessResponse(data=result, msg="租户切换成功")
@AuthRouter.post("/enter-platform", summary="进入平台管理模式", response_model=ResponseSchema[EnterPlatformOutSchema], dependencies=[Depends(get_current_user)])
async def enter_platform_controller(
request: Request,
auth: Annotated[AuthSchema, Depends(get_current_user)],
redis: Annotated[Redis, Depends(redis_getter)],
) -> JSONResponse:
result = await LoginService(auth).enter_platform(request=request, redis=redis)
await FastAPICache.clear(namespace=_AUTH_TENANTS_NS)
return SuccessResponse(data=result, msg="已返回平台管理模式")
@AuthRouter.get("/tenants", summary="获取可选租户列表", response_model=ResponseSchema[list[TenantOptionSchema]], dependencies=[Depends(get_current_user)])
@cache(expire=120, namespace=_AUTH_TENANTS_NS)
async def get_user_tenants_controller(
@@ -160,11 +156,23 @@ async def get_user_tenants_controller(
return SuccessResponse(data=tenants, msg="获取租户列表成功")
@AuthRouter.get("/oauth/{provider}/login", summary="第三方OAuth跳转", response_model=RedirectContentResponse[None])
@AuthRouter.post("/impersonate", summary="平台管理员代签入", response_model=ResponseSchema[ImpersonateOutSchema], dependencies=[Depends(get_current_user)])
async def impersonate_controller(
request: Request,
auth: Annotated[AuthSchema, Depends(get_current_user)],
redis: Annotated[Redis, Depends(redis_getter)],
data: Annotated[ImpersonateSchema, Body(description="代签入参数")],
) -> JSONResponse:
result = await LoginService(auth).impersonate(request=request, redis=redis, tenant_id=data.tenant_id)
await FastAPICache.clear(namespace=_AUTH_TENANTS_NS)
return SuccessResponse(data=result, msg="代签入成功")
@AuthRouter.get("/oauth/{provider}/login", summary="第三方OAuth跳转")
async def oauth_login_redirect_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
provider: Annotated[str, Path(description="wechat | qq | github | gitee")],
provider: Annotated[OAuthProvider, Path(description="wechat | qq | github | gitee")],
redirect_uri: Annotated[str | None, Query(description="OAuth 完成后浏览器回到的前端登录页完整 URL")] = None,
) -> RedirectResponse:
allowed = {"wechat", "qq", "github", "gitee"}
@@ -197,12 +205,12 @@ async def oauth_login_redirect_controller(
)
@AuthRouter.get("/oauth/{provider}/callback", summary="第三方OAuth回调", include_in_schema=False, response_model=RedirectContentResponse[None])
@AuthRouter.get("/oauth/{provider}/callback", summary="第三方OAuth回调", include_in_schema=False)
async def oauth_callback_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
provider: Annotated[str, Path(description="wechat | qq | github | gitee")],
provider: Annotated[OAuthProvider, Path(description="wechat | qq | github | gitee")],
code: Annotated[str | None, Query(description="OAuth 授权码")] = None,
state: Annotated[str | None, Query(description="OAuth 状态参数")] = None,
) -> RedirectResponse:
@@ -244,7 +252,7 @@ async def oauth_callback_controller(
return RedirectContentResponse(url=oauth_service_error_redirect(fe, e.msg), status_code=302)
@AuthRouter.post("/tenant/register", summary="租户自助注册", response_model=ResponseSchema[TenantRegisterOutSchema])
@AuthRouter.post("/tenant/register", status_code=status.HTTP_201_CREATED, summary="租户自助注册", response_model=ResponseSchema[TenantRegisterOutSchema])
async def tenant_register_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
data: Annotated[TenantRegisterSchema, Body(description="租户注册参数")],