refactor: 大规模代码重构与项目配置优化

- 调整前端路由前缀为 /web 并新增配置项
- 重构环境变量配置,替换旧环境文件为 dev/prod/test 版本
- 优化后端接口路由与参数校验,替换 dataclass 查询参数为 Pydantic 模型
- 整理后端导入顺序,延迟加载循环依赖模块
- 统一前端 API 调用命名,重构模块命名
- 新增多种响应类型支持,优化代码模板
- 简化后端路由装饰器写法,合并配置参数
This commit is contained in:
zhangtao
2026-07-04 01:23:43 +08:00
parent 845f412e61
commit 349ab7b525
80 changed files with 814 additions and 2004 deletions
@@ -2,14 +2,14 @@ import json
import secrets
from typing import Annotated
from fastapi import APIRouter, BackgroundTasks, Body, Depends, Form, Path, Query, Request
from fastapi import APIRouter, BackgroundTasks, Body, Depends, Path, Query, Request
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi_cache import FastAPICache
from fastapi_cache.decorator import cache
from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from app.common.response import ErrorResponse, ResponseSchema, SuccessResponse
from app.common.response import ErrorResponse, RedirectContentResponse, ResponseSchema, SuccessResponse
from app.config.setting import settings
from app.core.base_schema import (
AuthSchema,
@@ -56,17 +56,13 @@ AuthRouter = APIRouter(route_class=OperationLogRoute, prefix="/auth", tags=["认
_AUTH_TENANTS_NS = "auth_tenants"
@AuthRouter.post(
"/login",
summary="登录",
response_model=LoginWithTenantsSchema,
)
@AuthRouter.post("/login", summary="登录", response_model=LoginWithTenantsSchema)
async def login_for_access_token_controller(
request: Request,
background_tasks: BackgroundTasks,
redis: Annotated[Redis, Depends(redis_getter)],
db: Annotated[AsyncSession, Depends(db_getter)],
login_form: Annotated[CustomOAuth2PasswordRequestForm, Form(description="登录表单")],
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
@@ -79,11 +75,7 @@ async def login_for_access_token_controller(
return SuccessResponse(data=login_result, msg="登录成功")
@AuthRouter.post(
"/token/refresh",
summary="刷新token",
response_model=ResponseSchema[JWTOutSchema],
)
@AuthRouter.post("/token/refresh", summary="刷新token", response_model=ResponseSchema[JWTOutSchema])
async def get_new_token_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
redis: Annotated[Redis, Depends(redis_getter)],
@@ -93,11 +85,7 @@ async def get_new_token_controller(
return SuccessResponse(data=new_token, msg="刷新成功")
@AuthRouter.get(
"/captcha/get",
summary="获取验证码",
response_model=ResponseSchema[CaptchaOutSchema],
)
@AuthRouter.get("/captcha/get", summary="获取验证码", response_model=ResponseSchema[CaptchaOutSchema])
async def get_captcha_for_login_controller(
redis: Annotated[Redis, Depends(redis_getter)],
) -> JSONResponse:
@@ -105,12 +93,7 @@ async def get_captcha_for_login_controller(
return SuccessResponse(data=captcha, msg="获取验证码成功")
@AuthRouter.post(
"/logout",
summary="退出登录",
dependencies=[Depends(get_current_user)],
response_model=ResponseSchema[None],
)
@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="退出登录参数")],
@@ -121,11 +104,7 @@ async def logout_controller(
return ErrorResponse(msg="退出失败")
@AuthRouter.get(
"/auto-login/users",
summary="获取免登录用户列表",
response_model=ResponseSchema[list[AutoLoginUserSchema]],
)
@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)],
@@ -135,11 +114,7 @@ async def get_auto_login_users_controller(
return SuccessResponse(data=users, msg="获取成功")
@AuthRouter.post(
"/auto-login/token",
summary="获取免登录Token",
response_model=ResponseSchema[AutoLoginTokenSchema],
)
@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)],
@@ -151,11 +126,7 @@ async def get_auto_login_token_controller(
return SuccessResponse(data=result, msg="获取成功")
@AuthRouter.post(
"/auto-login",
summary="免登录",
response_model=ResponseSchema[JWTOutSchema],
)
@AuthRouter.post("/auto-login", summary="免登录", response_model=ResponseSchema[JWTOutSchema])
async def auto_login_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
@@ -167,12 +138,7 @@ async def auto_login_controller(
return SuccessResponse(data=login_token, msg="登录成功")
@AuthRouter.post(
"/select-tenant",
summary="选择租户",
response_model=ResponseSchema[SelectTenantOutSchema],
dependencies=[Depends(get_current_user)],
)
@AuthRouter.post("/select-tenant", summary="选择租户", response_model=ResponseSchema[SelectTenantOutSchema], dependencies=[Depends(get_current_user)])
async def select_tenant_controller(
request: Request,
auth: Annotated[AuthSchema, Depends(get_current_user)],
@@ -184,12 +150,7 @@ async def select_tenant_controller(
return SuccessResponse(data=result, msg="租户切换成功")
@AuthRouter.get(
"/tenants",
summary="获取可选租户列表",
response_model=ResponseSchema[list[TenantOptionSchema]],
dependencies=[Depends(get_current_user)],
)
@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(
auth: Annotated[AuthSchema, Depends(get_current_user)],
@@ -199,10 +160,7 @@ async def get_user_tenants_controller(
return SuccessResponse(data=tenants, msg="获取租户列表成功")
@AuthRouter.get(
"/oauth/{provider}/login",
summary="第三方OAuth跳转",
)
@AuthRouter.get("/oauth/{provider}/login", summary="第三方OAuth跳转", response_model=RedirectContentResponse[None])
async def oauth_login_redirect_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
@@ -212,12 +170,12 @@ async def oauth_login_redirect_controller(
allowed = {"wechat", "qq", "github", "gitee"}
fe = redirect_uri or settings.OAUTH_FRONTEND_FALLBACK
if provider not in allowed:
return RedirectResponse(
return RedirectContentResponse(
url=oauth_service_error_redirect(fe, "不支持的 OAuth 渠道"),
status_code=302,
)
if not redirect_uri:
return RedirectResponse(
return RedirectContentResponse(
url=oauth_service_error_redirect(fe, "缺少 redirect_uri 参数"),
status_code=302,
)
@@ -231,19 +189,15 @@ async def oauth_login_redirect_controller(
)
cb = _callback_url(request, provider)
url = build_authorize_url(provider=provider, callback_url=cb, state=state)
return RedirectResponse(url=url, status_code=302)
return RedirectContentResponse(url=url, status_code=302)
except CustomException as e:
return RedirectResponse(
return RedirectContentResponse(
url=oauth_service_error_redirect(redirect_uri, e.msg),
status_code=302,
)
@AuthRouter.get(
"/oauth/{provider}/callback",
summary="第三方OAuth回调",
include_in_schema=False,
)
@AuthRouter.get("/oauth/{provider}/callback", summary="第三方OAuth回调", include_in_schema=False, response_model=RedirectContentResponse[None])
async def oauth_callback_controller(
request: Request,
redis: Annotated[Redis, Depends(redis_getter)],
@@ -270,10 +224,10 @@ async def oauth_callback_controller(
if provider not in {"wechat", "qq", "github", "gitee"}:
url = oauth_service_error_redirect(await resolve_frontend(), "不支持的 OAuth 渠道")
return RedirectResponse(url=url, status_code=302)
return RedirectContentResponse(url=url, status_code=302)
if not code or not state:
url = oauth_service_error_redirect(await resolve_frontend(), "授权被取消或参数不完整")
return RedirectResponse(url=url, status_code=302)
return RedirectContentResponse(url=url, status_code=302)
try:
token, fe = await complete_oauth_login(
request=request,
@@ -284,17 +238,13 @@ async def oauth_callback_controller(
state=state,
)
success_url = oauth_service_frontend_redirect_from_token(fe, token)
return RedirectResponse(url=success_url, status_code=302)
return RedirectContentResponse(url=success_url, status_code=302)
except CustomException as e:
fe = await resolve_frontend()
return RedirectResponse(url=oauth_service_error_redirect(fe, e.msg), status_code=302)
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", summary="租户自助注册", response_model=ResponseSchema[TenantRegisterOutSchema])
async def tenant_register_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
data: Annotated[TenantRegisterSchema, Body(description="租户注册参数")],
@@ -308,6 +258,3 @@ async def tenant_register_controller(
)
logger.info(f"新租户注册: username={data.username} tenant={result.tenant_name}")
return SuccessResponse(data=result, msg=result.message)
@@ -17,11 +17,7 @@ DeptRouter = APIRouter(route_class=OperationLogRoute, prefix="/dept", tags=["部
_DEPT_NS = "dept"
@DeptRouter.get(
"/tree",
summary="查询部门树",
response_model=ResponseSchema[list[DeptOutSchema]],
)
@DeptRouter.get("/tree", summary="查询部门树", response_model=ResponseSchema[list[DeptOutSchema]])
@cache(expire=300, namespace=_DEPT_NS)
async def get_dept_tree_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:query"]))],
@@ -31,11 +27,7 @@ async def get_dept_tree_controller(
result_dict_tree = await DeptService(auth).tree(search=search, order_by=order_by)
return SuccessResponse(data=result_dict_tree, msg="查询部门树成功")
@DeptRouter.get(
"/detail/{id}",
summary="查询部门详情",
response_model=ResponseSchema[DeptOutSchema],
)
@DeptRouter.get("/detail/{id}", summary="查询部门详情", response_model=ResponseSchema[DeptOutSchema])
async def get_obj_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:detail"]))],
id: Annotated[int, Path(description="部门ID")],
@@ -43,11 +35,7 @@ async def get_obj_detail_controller(
result_dict = await DeptService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="查询部门详情成功")
@DeptRouter.post(
"/create",
summary="创建部门",
response_model=ResponseSchema[DeptOutSchema],
)
@DeptRouter.post("/create", summary="创建部门", response_model=ResponseSchema[DeptOutSchema])
async def create_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:create"]))],
data: Annotated[DeptCreateSchema, Body(description="部门创建参数")],
@@ -56,11 +44,7 @@ async def create_obj_controller(
await FastAPICache.clear(namespace=_DEPT_NS)
return SuccessResponse(data=result_dict, msg="创建部门成功")
@DeptRouter.put(
"/update/{id}",
summary="修改部门",
response_model=ResponseSchema[DeptOutSchema],
)
@DeptRouter.put("/update/{id}", summary="修改部门", response_model=ResponseSchema[DeptOutSchema])
async def update_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:update"]))],
id: Annotated[int, Path(description="部门ID")],
@@ -70,11 +54,7 @@ async def update_obj_controller(
await FastAPICache.clear(namespace=_DEPT_NS)
return SuccessResponse(data=result_dict, msg="修改部门成功")
@DeptRouter.delete(
"/delete",
summary="删除部门",
response_model=ResponseSchema[None],
)
@DeptRouter.delete("/delete", summary="删除部门", response_model=ResponseSchema[None])
async def delete_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -83,11 +63,7 @@ async def delete_obj_controller(
await FastAPICache.clear(namespace=_DEPT_NS)
return SuccessResponse(msg="删除部门成功")
@DeptRouter.patch(
"/status/batch",
summary="批量修改部门状态",
response_model=ResponseSchema[None],
)
@DeptRouter.patch("/status/batch", summary="批量修改部门状态", response_model=ResponseSchema[None])
async def batch_set_available_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dept:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -1,11 +1,7 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
from app.core.validator import validate_required_code
@@ -63,14 +59,16 @@ class DeptTreeOutSchema(DeptOutSchema):
children: list["DeptTreeOutSchema"] | None = Field(default=None, description="子部门列表")
@dataclass
class DeptQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""部门管理查询参数"""
name: str | None = Query(None, description="部门名称")
status: int | None = Query(None, ge=0, le=1, description="状态(0:启动 1:停用)")
name: str | None = Field(None, description="部门名称")
status: int | None = Field(None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
self.name = (QueueEnum.like.value, self.name)
@model_validator(mode="after")
def validate_query_params(self) -> "DeptQueryParam":
if self.name:
self.name = (QueueEnum.like.value, self.name)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -7,8 +7,7 @@ from fastapi_cache.decorator import cache
from redis.asyncio.client import Redis
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission, redis_getter
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
@@ -29,11 +28,7 @@ DictRouter = APIRouter(route_class=OperationLogRoute, prefix="/dict", tags=["字
_DICT_TYPE_NS = "dict_type"
@DictRouter.get(
"/type/detail/{id}",
summary="获取字典类型详情",
response_model=ResponseSchema[DictTypeOutSchema],
)
@DictRouter.get("/type/detail/{id}", summary="获取字典类型详情", response_model=ResponseSchema[DictTypeOutSchema])
async def get_type_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:detail"]))],
id: Annotated[int, Path(description="字典类型ID", ge=1)],
@@ -41,11 +36,7 @@ async def get_type_detail_controller(
result_dict = await DictTypeService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取字典类型详情成功")
@DictRouter.get(
"/type/list",
summary="查询字典类型",
response_model=ResponseSchema[PageResultSchema[DictTypeOutSchema]],
)
@DictRouter.get("/type/list", summary="查询字典类型", response_model=ResponseSchema[PageResultSchema[DictTypeOutSchema]])
async def get_type_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页查询参数")],
@@ -59,11 +50,7 @@ async def get_type_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询字典类型列表成功")
@DictRouter.get(
"/type/optionselect",
summary="获取全部字典类型",
response_model=ResponseSchema[list[DictTypeOutSchema]],
)
@DictRouter.get("/type/optionselect", summary="获取全部字典类型", response_model=ResponseSchema[list[DictTypeOutSchema]])
@cache(expire=300, namespace=_DICT_TYPE_NS)
async def get_type_optionselect_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:query"]))],
@@ -71,11 +58,7 @@ async def get_type_optionselect_controller(
result_dict_list = await DictTypeService(auth).get_list()
return SuccessResponse(data=result_dict_list, msg="获取字典类型列表成功")
@DictRouter.post(
"/type/create",
summary="创建字典类型",
response_model=ResponseSchema[DictTypeOutSchema],
)
@DictRouter.post("/type/create", summary="创建字典类型", response_model=ResponseSchema[DictTypeOutSchema])
async def create_type_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:create"]))],
@@ -85,11 +68,7 @@ async def create_type_controller(
await FastAPICache.clear(namespace=_DICT_TYPE_NS)
return SuccessResponse(data=result_dict, msg="创建字典类型成功")
@DictRouter.put(
"/type/update/{id}",
summary="修改字典类型",
response_model=ResponseSchema[DictTypeOutSchema],
)
@DictRouter.put("/type/update/{id}", summary="修改字典类型", response_model=ResponseSchema[DictTypeOutSchema])
async def update_type_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:update"]))],
@@ -100,11 +79,7 @@ async def update_type_controller(
await FastAPICache.clear(namespace=_DICT_TYPE_NS)
return SuccessResponse(data=result_dict, msg="修改字典类型成功")
@DictRouter.delete(
"/type/delete",
summary="删除字典类型",
response_model=ResponseSchema[None],
)
@DictRouter.delete("/type/delete", summary="删除字典类型", response_model=ResponseSchema[None])
async def delete_type_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:delete"]))],
@@ -114,11 +89,7 @@ async def delete_type_controller(
await FastAPICache.clear(namespace=_DICT_TYPE_NS)
return SuccessResponse(msg="删除字典类型成功")
@DictRouter.patch(
"/type/status/batch",
summary="批量修改字典类型状态",
response_model=ResponseSchema[None],
)
@DictRouter.patch("/type/status/batch", summary="批量修改字典类型状态", response_model=ResponseSchema[None])
async def batch_set_available_dict_type_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -127,15 +98,11 @@ async def batch_set_available_dict_type_controller(
await FastAPICache.clear(namespace=_DICT_TYPE_NS)
return SuccessResponse(msg="批量修改字典类型状态成功")
@DictRouter.post(
"/type/export",
summary="导出字典类型",
response_model=ResponseSchema[None],
)
@DictRouter.post("/type/export", summary="导出字典类型")
async def export_type_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_type:export"]))],
search: Annotated[DictTypeQueryParam, Query(description="字典类型查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
# 获取全量数据并转为dict列表
result_dict_list = await DictTypeService(auth).get_list(search=search)
export_data = [item.model_dump() for item in result_dict_list]
@@ -147,11 +114,7 @@ async def export_type_list_controller(
headers={"Content-Disposition": "attachment; filename=dict_type.xlsx"},
)
@DictRouter.get(
"/data/detail/{id}",
summary="获取字典数据详情",
response_model=ResponseSchema[DictDataOutSchema],
)
@DictRouter.get("/data/detail/{id}", summary="获取字典数据详情", response_model=ResponseSchema[DictDataOutSchema])
async def get_data_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:detail"]))],
id: Annotated[int, Path(description="字典数据ID", ge=1)],
@@ -159,11 +122,7 @@ async def get_data_detail_controller(
result_dict = await DictDataService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取字典数据详情成功")
@DictRouter.get(
"/data/list",
summary="查询字典数据",
response_model=ResponseSchema[PageResultSchema[DictDataOutSchema]],
)
@DictRouter.get("/data/list", summary="查询字典数据", response_model=ResponseSchema[PageResultSchema[DictDataOutSchema]])
async def get_data_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
@@ -180,11 +139,7 @@ async def get_data_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询字典数据列表成功")
@DictRouter.post(
"/data/create",
summary="创建字典数据",
response_model=ResponseSchema[DictDataOutSchema],
)
@DictRouter.post("/data/create", summary="创建字典数据", response_model=ResponseSchema[DictDataOutSchema])
async def create_data_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:create"]))],
@@ -193,11 +148,7 @@ async def create_data_controller(
result_dict = await DictDataService(auth).create(redis=redis, data=data)
return SuccessResponse(data=result_dict, msg="创建字典数据成功")
@DictRouter.put(
"/data/update/{id}",
summary="修改字典数据",
response_model=ResponseSchema[DictDataOutSchema],
)
@DictRouter.put("/data/update/{id}", summary="修改字典数据", response_model=ResponseSchema[DictDataOutSchema])
async def update_data_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:update"]))],
@@ -207,11 +158,7 @@ async def update_data_controller(
result_dict = await DictDataService(auth).update(redis=redis, id=id, data=data)
return SuccessResponse(data=result_dict, msg="修改字典数据成功")
@DictRouter.delete(
"/data/delete",
summary="删除字典数据",
response_model=ResponseSchema[None],
)
@DictRouter.delete("/data/delete", summary="删除字典数据", response_model=ResponseSchema[None])
async def delete_data_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:delete"]))],
@@ -220,11 +167,7 @@ async def delete_data_controller(
await DictDataService(auth).delete(redis=redis, ids=ids)
return SuccessResponse(msg="删除字典数据成功")
@DictRouter.patch(
"/data/status/batch",
summary="批量修改字典数据状态",
response_model=ResponseSchema[None],
)
@DictRouter.patch("/data/status/batch", summary="批量修改字典数据状态", response_model=ResponseSchema[None])
async def batch_set_available_dict_data_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -232,16 +175,12 @@ async def batch_set_available_dict_data_controller(
await DictDataService(auth).set_available(data=data)
return SuccessResponse(msg="批量修改字典数据状态成功")
@DictRouter.post(
"/data/export",
summary="导出字典数据",
response_model=ResponseSchema[None],
)
@DictRouter.post("/data/export", summary="导出字典数据", response_model=StreamResponse[bytes])
async def export_data_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:dict_data:export"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
search: Annotated[DictDataQueryParam, Query(description="字典数据查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
result_dict_list = await DictDataService(auth).get_list(search=search, order_by=page.order_by)
export_data = [item.model_dump() for item in result_dict_list]
export_result = DictDataService.export(data_list=export_data)
@@ -252,11 +191,7 @@ async def export_data_list_controller(
headers={"Content-Disposition": "attachment; filename=dice_data.xlsx"},
)
@DictRouter.get(
"/data/info/{dict_type}",
summary="根据字典类型获取数据",
response_model=ResponseSchema[list[DictDataOutSchema]],
)
@DictRouter.get("/data/info/{dict_type}", summary="根据字典类型获取数据", response_model=ResponseSchema[list[DictDataOutSchema]])
async def get_init_dict_data_controller(
redis: Annotated[Redis, Depends(redis_getter)],
dict_type: Annotated[str, Path(description="字典类型")],
+14 -15
View File
@@ -1,7 +1,5 @@
import re
from dataclasses import dataclass
from fastapi import Query
from pydantic import (
BaseModel,
ConfigDict,
@@ -11,8 +9,7 @@ from pydantic import (
)
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
class DictTypeCreateSchema(BaseModel):
@@ -84,21 +81,22 @@ class DictTypeOutSchema(DictTypeCreateSchema, BaseSchema, UserBySchema, TenantBy
model_config = ConfigDict(from_attributes=True)
@dataclass
class DictTypeQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""字典类型查询参数"""
dict_name: str | None = Query(default=None, description="字典名称", max_length=100)
dict_type: str | None = Query(default=None, description="字典类型", max_length=100)
status: int | None = Query(default=None, ge=0, le=1, description="状态(0:启动 1:停用)")
dict_name: str | None = Field(default=None, description="字典名称", max_length=100)
dict_type: str | None = Field(default=None, description="字典类型", max_length=100)
status: int | None = Field(default=None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "DictTypeQueryParam":
if self.dict_name:
self.dict_name = (QueueEnum.like.value, self.dict_name)
if self.dict_type:
self.dict_type = (QueueEnum.eq.value, self.dict_type)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
class DictDataCreateSchema(BaseModel):
@@ -162,16 +160,16 @@ class DictDataOutSchema(DictDataCreateSchema, BaseSchema, UserBySchema, TenantBy
model_config = ConfigDict(from_attributes=True)
@dataclass
class DictDataQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""字典数据查询参数"""
dict_label: str | None = Query(default=None, description="字典标签", max_length=100)
dict_type: str | None = Query(default=None, description="字典类型", max_length=100)
dict_type_id: int | None = Query(default=None, description="字典类型ID")
status: int | None = Query(default=None, ge=0, le=1, description="状态(0:启动 1:停用)")
dict_label: str | None = Field(default=None, description="字典标签", max_length=100)
dict_type: str | None = Field(default=None, description="字典类型", max_length=100)
dict_type_id: int | None = Field(default=None, description="字典类型ID")
status: int | None = Field(default=None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "DictDataQueryParam":
if self.dict_label:
self.dict_label = (QueueEnum.like.value, self.dict_label)
if self.dict_type:
@@ -180,3 +178,4 @@ class DictDataQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
self.status = (QueueEnum.eq.value, self.status)
if self.dict_type_id is not None:
self.dict_type_id = (QueueEnum.eq.value, self.dict_type_id)
return self
@@ -4,8 +4,7 @@ from fastapi import APIRouter, Body, Depends, Path, Query
from fastapi.responses import JSONResponse
from app.common.response import ResponseSchema, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, PageResultSchema
from app.core.base_schema import AuthSchema, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission, get_current_user
from app.core.router_class import OperationLogRoute
@@ -24,11 +23,7 @@ from .service import LoginLogService, OperationLogService
LogRouter = APIRouter(route_class=OperationLogRoute, prefix="/log", tags=["日志管理"])
@LogRouter.get(
"/login/detail/{id}",
summary="获取登录日志详情",
response_model=ResponseSchema[LoginLogDetailOutSchema],
)
@LogRouter.get("/login/detail/{id}", summary="获取登录日志详情", response_model=ResponseSchema[LoginLogDetailOutSchema])
async def get_log_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:login_log:query"]))],
id: Annotated[int, Path(description="登录日志ID")],
@@ -37,11 +32,7 @@ async def get_log_detail_controller(
return SuccessResponse(data=result_dict, msg="获取登录日志详情成功")
@LogRouter.get(
"/login/list",
summary="查询登录日志列表",
response_model=ResponseSchema[PageResultSchema[LoginLogOutSchema]],
)
@LogRouter.get("/login/list", summary="查询登录日志列表", response_model=ResponseSchema[PageResultSchema[LoginLogOutSchema]])
async def get_log_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:login_log:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
@@ -56,11 +47,7 @@ async def get_log_list_controller(
return SuccessResponse(data=result_dict, msg="查询登录日志列表成功")
@LogRouter.post(
"/login/create",
summary="创建登录日志",
response_model=ResponseSchema[LoginLogDetailOutSchema],
)
@LogRouter.post("/login/create", summary="创建登录日志", response_model=ResponseSchema[LoginLogDetailOutSchema])
async def create_log_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
data: Annotated[LoginLogCreateSchema, Body(description="登录日志创建参数")],
@@ -69,11 +56,7 @@ async def create_log_controller(
return SuccessResponse(data=result_dict, msg="创建登录日志成功")
@LogRouter.delete(
"/login/delete",
summary="删除登录日志",
response_model=ResponseSchema,
)
@LogRouter.delete("/login/delete", summary="删除登录日志", response_model=ResponseSchema)
async def delete_log_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:login_log:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -82,31 +65,21 @@ async def delete_log_controller(
return SuccessResponse(msg="删除登录日志成功")
@LogRouter.get(
"/operation/detail/{id}",
summary="获取操作日志详情",
response_model=ResponseSchema[OperationLogDetailOutSchema],
dependencies=[Depends(AuthPermission(["module_system:log:query"]))],
)
@LogRouter.get("/operation/detail/{id}", summary="获取操作日志详情", response_model=ResponseSchema[OperationLogDetailOutSchema], dependencies=[Depends(AuthPermission(["module_system:log:query"]))])
async def get_operation_log_detail_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
id: Annotated[int, Path(description="操作日志ID", gt=0)],
):
) -> JSONResponse:
result_dict = await OperationLogService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取操作日志详情成功")
@LogRouter.get(
"/operation/list",
summary="获取操作日志列表",
response_model=ResponseSchema[PageResultSchema[OperationLogOutSchema]],
dependencies=[Depends(AuthPermission(["module_system:log:query"]))],
)
@LogRouter.get("/operation/list", summary="获取操作日志列表", response_model=ResponseSchema[PageResultSchema[OperationLogOutSchema]], dependencies=[Depends(AuthPermission(["module_system:log:query"]))])
async def get_operation_log_list_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
search: Annotated[OperationLogQueryParam, Query(description="操作日志查询参数")],
):
) -> JSONResponse:
result_dict = await OperationLogService(auth).page(
page_no=page.page_no,
page_size=page.page_size,
@@ -116,28 +89,19 @@ async def get_operation_log_list_controller(
return SuccessResponse(data=result_dict, msg="查询操作日志列表成功")
@LogRouter.post(
"/operation/create",
summary="创建操作日志",
response_model=ResponseSchema[OperationLogDetailOutSchema],
)
@LogRouter.post("/operation/create", summary="创建操作日志", response_model=ResponseSchema[OperationLogDetailOutSchema])
async def create_operation_log_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
data: Annotated[OperationLogCreateSchema, Body(description="操作日志创建参数")],
):
) -> JSONResponse:
result_dict = await OperationLogService(auth).create(data=data)
return SuccessResponse(data=result_dict, msg="创建操作日志成功")
@LogRouter.delete(
"/operation/delete",
summary="删除操作日志",
response_model=ResponseSchema,
dependencies=[Depends(AuthPermission(["module_system:log:delete"]))],
)
async def delete(
@LogRouter.delete("/operation/delete", summary="删除操作日志", response_model=ResponseSchema, dependencies=[Depends(AuthPermission(["module_system:log:delete"]))])
async def delete_operation_log_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
ids: Annotated[list[int], Body(description="ID列表")],
):
) -> JSONResponse:
await OperationLogService(auth).delete(ids=ids)
return SuccessResponse(msg="删除操作日志成功")
+14 -16
View File
@@ -1,11 +1,7 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
ALLOWED_REQUEST_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
@@ -49,30 +45,31 @@ class LoginLogDetailOutSchema(LoginLogOutSchema):
"""登录日志详情响应"""
@dataclass
class LoginLogQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""登录日志查询参数"""
username: str | None = Query(None, max_length=64, description="用户名")
status: int | None = Query(None, description="登录状态(1:成功 2:失败)")
username: str | None = Field(None, max_length=64, description="用户名")
status: int | None = Field(None, description="登录状态(1:成功 2:失败)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "LoginLogQueryParam":
if self.username:
self.username = (QueueEnum.like.value, self.username)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
@dataclass
class OperationLogQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""操作日志查询参数"""
request_path: str | None = Query(None, description="请求路径")
request_method: str | None = Query(None, description="请求方式")
username: str | None = Query(None, description="用户名")
status: int | None = Query(None, ge=0, le=1, description="状态(0:成功 1:失败)")
request_path: str | None = Field(None, description="请求路径")
request_method: str | None = Field(None, description="请求方式")
username: str | None = Field(None, description="用户名")
status: int | None = Field(None, ge=0, le=1, description="状态(0:成功 1:失败)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "OperationLogQueryParam":
if self.request_path:
self.request_path = (QueueEnum.like.value, self.request_path)
if self.request_method:
@@ -81,6 +78,7 @@ class OperationLogQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryPara
self.username = (QueueEnum.like.value, self.username)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
class OperationLogOutSchema(BaseSchema, UserBySchema, TenantBySchema):
@@ -6,31 +6,20 @@ from fastapi_cache import FastAPICache
from fastapi_cache.decorator import cache
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission, get_current_user
from app.core.logger import logger
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
from .schema import (
NoticeCreateSchema,
NoticeOutSchema,
NoticeQueryParam,
NoticeUpdateSchema,
PanelDataOut,
)
from .schema import NoticeCreateSchema, NoticeOutSchema, NoticeQueryParam, NoticeUpdateSchema, PanelDataOut
from .service import NoticeService
NoticeRouter = APIRouter(route_class=OperationLogRoute, prefix="/notice", tags=["公告通知"])
_NOTICE_NS = "notice"
@NoticeRouter.get(
"/detail/{id}",
summary="获取公告详情",
response_model=ResponseSchema[NoticeOutSchema],
)
@NoticeRouter.get("/detail/{id}", summary="获取公告详情", response_model=ResponseSchema[NoticeOutSchema])
async def get_notice_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:detail"]))],
id: Annotated[int, Path(description="公告ID")],
@@ -38,11 +27,7 @@ async def get_notice_detail_controller(
result_dict = await NoticeService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取公告详情成功")
@NoticeRouter.get(
"/list",
summary="查询公告",
response_model=ResponseSchema[PageResultSchema[NoticeOutSchema]],
)
@NoticeRouter.get("/list", summary="查询公告", response_model=ResponseSchema[PageResultSchema[NoticeOutSchema]])
async def get_notice_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
@@ -56,11 +41,7 @@ async def get_notice_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询公告列表成功")
@NoticeRouter.post(
"/create",
summary="创建公告",
response_model=ResponseSchema[NoticeOutSchema],
)
@NoticeRouter.post("/create", summary="创建公告", response_model=ResponseSchema[NoticeOutSchema])
async def create_notice_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:create"]))],
data: Annotated[NoticeCreateSchema, Body(description="公告创建参数")],
@@ -69,11 +50,7 @@ async def create_notice_controller(
await FastAPICache.clear(namespace=_NOTICE_NS)
return SuccessResponse(data=result_dict, msg="创建公告成功")
@NoticeRouter.put(
"/update/{id}",
summary="修改公告",
response_model=ResponseSchema[NoticeOutSchema],
)
@NoticeRouter.put("/update/{id}", summary="修改公告", response_model=ResponseSchema[NoticeOutSchema])
async def update_notice_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:update"]))],
id: Annotated[int, Path(description="公告ID")],
@@ -83,11 +60,7 @@ async def update_notice_controller(
await FastAPICache.clear(namespace=_NOTICE_NS)
return SuccessResponse(data=result_dict, msg="修改公告成功")
@NoticeRouter.delete(
"/delete",
summary="删除公告",
response_model=ResponseSchema[None],
)
@NoticeRouter.delete("/delete", summary="删除公告", response_model=ResponseSchema[None])
async def delete_notice_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -96,11 +69,7 @@ async def delete_notice_controller(
await FastAPICache.clear(namespace=_NOTICE_NS)
return SuccessResponse(msg="删除公告成功")
@NoticeRouter.patch(
"/status/batch",
summary="批量修改公告状态",
response_model=ResponseSchema[None],
)
@NoticeRouter.patch("/status/batch", summary="批量修改公告状态", response_model=ResponseSchema[None])
async def batch_set_available_notice_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -109,14 +78,11 @@ async def batch_set_available_notice_controller(
await FastAPICache.clear(namespace=_NOTICE_NS)
return SuccessResponse(msg="批量修改公告状态成功")
@NoticeRouter.post(
"/export",
summary="导出公告",
)
@NoticeRouter.post("/export", summary="导出公告")
async def export_notice_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:notice:export"]))],
search: Annotated[NoticeQueryParam, Query(description="公告查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
result_dict_list = await NoticeService(auth).get_list(search=search)
export_data = [item.model_dump() for item in result_dict_list]
export_result = NoticeService.export(notice_list=export_data)
@@ -127,11 +93,7 @@ async def export_notice_list_controller(
headers={"Content-Disposition": "attachment; filename=notice.xlsx"},
)
@NoticeRouter.get(
"/available",
summary="获取全局启用公告",
response_model=ResponseSchema[list[NoticeOutSchema]],
)
@NoticeRouter.get("/available", summary="获取全局启用公告", response_model=ResponseSchema[list[NoticeOutSchema]])
@cache(expire=120, namespace=_NOTICE_NS)
async def get_notice_list_available_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
@@ -139,11 +101,7 @@ async def get_notice_list_available_controller(
result_dict = await NoticeService(auth).available_page()
return SuccessResponse(data=result_dict, msg="查询已启用公告列表成功")
@NoticeRouter.get(
"/panel",
summary="通知面板数据(铃铛)",
response_model=ResponseSchema[PanelDataOut],
)
@NoticeRouter.get("/panel", summary="通知面板数据(铃铛)", response_model=ResponseSchema[PanelDataOut])
@cache(expire=30, namespace=_NOTICE_NS)
async def get_notification_panel_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
@@ -152,11 +110,7 @@ async def get_notification_panel_controller(
result = await NoticeService(auth).panel_data()
return SuccessResponse(data=result, msg="获取面板数据成功")
@NoticeRouter.post(
"/read/{id}",
summary="标记已读",
response_model=ResponseSchema[None],
)
@NoticeRouter.post("/read/{id}", summary="标记已读", response_model=ResponseSchema[None])
async def mark_read_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
id: Annotated[int, Path(description="通知ID")],
@@ -167,11 +121,7 @@ async def mark_read_controller(
logger.info(f"用户[{auth.user.id}]标记通知[{id}]已读")
return SuccessResponse(msg="标记已读成功")
@NoticeRouter.post(
"/read-all",
summary="全部已读",
response_model=ResponseSchema[int],
)
@NoticeRouter.post("/read-all", summary="全部已读", response_model=ResponseSchema[int])
async def mark_all_read_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
) -> JSONResponse:
@@ -181,11 +131,7 @@ async def mark_all_read_controller(
logger.info(f"用户[{auth.user.id}]全部已读, 数量={count}")
return SuccessResponse(data=count, msg=f"全部标记已读成功,共标记 {count}")
@NoticeRouter.get(
"/unread-count",
summary="获取未读数量",
response_model=ResponseSchema[int],
)
@NoticeRouter.get("/unread-count", summary="获取未读数量", response_model=ResponseSchema[int])
@cache(expire=15, namespace=_NOTICE_NS)
async def get_unread_count_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
@@ -1,6 +1,3 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import (
BaseModel,
ConfigDict,
@@ -10,8 +7,7 @@ from pydantic import (
)
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
from app.utils.xss_util import sanitize_html
@@ -64,21 +60,22 @@ class NoticeOutSchema(NoticeCreateSchema, BaseSchema, UserBySchema, TenantBySche
model_config = ConfigDict(from_attributes=True)
@dataclass
class NoticeQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""公告通知查询参数"""
notice_title: str | None = Query(None, description="公告标题")
notice_type: str | None = Query(None, description="公告类型")
status: int | None = Query(None, ge=0, le=1, description="状态(0:启动 1:停用)")
notice_title: str | None = Field(None, description="公告标题")
notice_type: str | None = Field(None, description="公告类型")
status: int | None = Field(None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "NoticeQueryParam":
if self.notice_title:
self.notice_title = (QueueEnum.like.value, self.notice_title)
if self.notice_type:
self.notice_type = (QueueEnum.eq.value, self.notice_type)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
class PanelMessageItem(BaseModel):
@@ -5,8 +5,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
from redis.asyncio.client import Redis
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission, redis_getter
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
@@ -16,11 +15,8 @@ from .service import ParamsService
ParamsRouter = APIRouter(route_class=OperationLogRoute, prefix="/param", tags=["参数管理"])
@ParamsRouter.get(
"/detail/{id}",
summary="获取参数详情",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.get("/detail/{id}", summary="获取参数详情", response_model=ResponseSchema[ParamsOutSchema])
async def get_param_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:detail"]))],
id: Annotated[int, Path(description="参数ID")],
@@ -28,11 +24,7 @@ async def get_param_detail_controller(
result_dict = await ParamsService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取参数详情成功")
@ParamsRouter.get(
"/key/{config_key}",
summary="根据配置键获取参数详情",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.get("/key/{config_key}", summary="根据配置键获取参数详情", response_model=ResponseSchema[ParamsOutSchema])
async def get_param_by_key_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:query"]))],
config_key: Annotated[str, Path(description="配置键")],
@@ -40,11 +32,7 @@ async def get_param_by_key_controller(
result_dict = await ParamsService(auth).get_by_key(config_key=config_key)
return SuccessResponse(data=result_dict, msg="根据配置键获取参数详情成功")
@ParamsRouter.get(
"/value/{config_key}",
summary="根据配置键获取参数值",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.get("/value/{config_key}", summary="根据配置键获取参数值", response_model=ResponseSchema[ParamsOutSchema])
async def get_config_value_by_key_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:query"]))],
config_key: Annotated[str, Path(description="配置键")],
@@ -52,11 +40,7 @@ async def get_config_value_by_key_controller(
result_value = await ParamsService(auth).get_config_value_by_key(config_key=config_key)
return SuccessResponse(data=result_value, msg="根据配置键获取参数值成功")
@ParamsRouter.get(
"/list",
summary="获取参数列表",
response_model=ResponseSchema[PageResultSchema[ParamsOutSchema]],
)
@ParamsRouter.get("/list", summary="获取参数列表", response_model=ResponseSchema[PageResultSchema[ParamsOutSchema]])
async def get_param_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
@@ -70,11 +54,7 @@ async def get_param_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询参数列表成功")
@ParamsRouter.post(
"/create",
summary="创建参数",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.post("/create", summary="创建参数", response_model=ResponseSchema[ParamsOutSchema])
async def create_param_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:create"]))],
@@ -83,11 +63,7 @@ async def create_param_controller(
result_dict = await ParamsService(auth).create(redis=redis, data=data)
return SuccessResponse(data=result_dict, msg="创建参数成功")
@ParamsRouter.put(
"/update/{id}",
summary="修改参数",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.put("/update/{id}", summary="修改参数", response_model=ResponseSchema[ParamsOutSchema])
async def update_param_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:update"]))],
@@ -97,11 +73,7 @@ async def update_param_controller(
result_dict = await ParamsService(auth).update(redis=redis, id=id, data=data)
return SuccessResponse(data=result_dict, msg="更新参数成功")
@ParamsRouter.delete(
"/delete",
summary="删除参数",
response_model=ResponseSchema[ParamsOutSchema],
)
@ParamsRouter.delete("/delete", summary="删除参数", response_model=ResponseSchema[ParamsOutSchema])
async def delete_param_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:delete"]))],
@@ -110,11 +82,7 @@ async def delete_param_controller(
await ParamsService(auth).delete(redis=redis, ids=ids)
return SuccessResponse(msg="删除参数成功")
@ParamsRouter.patch(
"/status/batch",
summary="批量设置参数状态",
response_model=ResponseSchema,
)
@ParamsRouter.patch("/status/batch", summary="批量设置参数状态", response_model=ResponseSchema)
async def batch_set_status_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -122,15 +90,11 @@ async def batch_set_status_controller(
await ParamsService(auth).batch_set_status(ids=data.ids, status=data.status)
return SuccessResponse(msg="批量设置参数状态成功")
@ParamsRouter.get(
"/export",
summary="导出参数",
response_model=ResponseSchema[None],
)
@ParamsRouter.get("/export", summary="导出参数")
async def export_param_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:param:export"]))],
search: Annotated[ParamsQueryParam, Query(description="参数查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
result_dict_list = await ParamsService(auth).get_list(search=search)
export_data = [item.model_dump() for item in result_dict_list]
export_result = ParamsService.export(data_list=export_data)
@@ -141,11 +105,7 @@ async def export_param_list_controller(
headers={"Content-Disposition": "attachment; filename=params.xlsx"},
)
@ParamsRouter.get(
"/info",
summary="获取初始化缓存参数",
response_model=ResponseSchema[list[ParamsOutSchema]],
)
@ParamsRouter.get( "/info", summary="获取初始化缓存参数", response_model=ResponseSchema[list[ParamsOutSchema]])
async def get_init_config_controller(
redis: Annotated[Redis, Depends(redis_getter)],
) -> JSONResponse:
@@ -1,12 +1,9 @@
import re
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
class ParamsCreateSchema(BaseModel):
@@ -53,7 +50,6 @@ class ParamsOutSchema(ParamsCreateSchema, BaseSchema, UserBySchema, TenantBySche
model_config = ConfigDict(from_attributes=True)
@dataclass
class ParamsQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""
参数管理查询参数
@@ -65,12 +61,13 @@ class ParamsQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
- 业务字段参数名称参数键名是否系统内置
"""
config_name: str | None = Query(None, description="参数名称")
config_key: str | None = Query(None, description="参数键名")
config_type: bool | None = Query(None, description="是否系统内置(True:是 False:否)")
status: int | None = Query(None, ge=0, le=1, description="状态(0:启动 1:停用)")
config_name: str | None = Field(None, description="参数名称")
config_key: str | None = Field(None, description="参数键名")
config_type: bool | None = Field(None, description="是否系统内置(True:是 False:否)")
status: int | None = Field(None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "ParamsQueryParam":
if self.config_name:
self.config_name = (QueueEnum.like.value, self.config_name)
if self.config_key:
@@ -79,3 +76,4 @@ class ParamsQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
self.config_type = (QueueEnum.eq.value, self.config_type)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -6,29 +6,19 @@ from fastapi_cache import FastAPICache
from fastapi_cache.decorator import cache
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
from .schema import (
PositionCreateSchema,
PositionOutSchema,
PositionQueryParam,
PositionUpdateSchema,
)
from .schema import PositionCreateSchema, PositionOutSchema, PositionQueryParam, PositionUpdateSchema
from .service import PositionService
PositionRouter = APIRouter(route_class=OperationLogRoute, prefix="/position", tags=["岗位管理"])
_POS_NS = "position"
@PositionRouter.get(
"/list",
summary="查询岗位",
response_model=ResponseSchema[PageResultSchema[PositionOutSchema]],
)
@PositionRouter.get("/list", summary="查询岗位", response_model=ResponseSchema[PageResultSchema[PositionOutSchema]])
@cache(expire=300, namespace=_POS_NS)
async def get_obj_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:query"]))],
@@ -46,11 +36,7 @@ async def get_obj_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询岗位列表成功")
@PositionRouter.get(
"/detail/{id}",
summary="查询岗位详情",
response_model=ResponseSchema[PositionOutSchema],
)
@PositionRouter.get("/detail/{id}", summary="查询岗位详情", response_model=ResponseSchema[PositionOutSchema])
async def get_obj_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:detail"]))],
id: Annotated[int, Path(description="岗位ID")],
@@ -58,11 +44,7 @@ async def get_obj_detail_controller(
result_dict = await PositionService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取岗位详情成功")
@PositionRouter.post(
"/create",
summary="创建岗位",
response_model=ResponseSchema[PositionOutSchema],
)
@PositionRouter.post("/create", summary="创建岗位", response_model=ResponseSchema[PositionOutSchema])
async def create_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:create"]))],
data: Annotated[PositionCreateSchema, Body(description="岗位创建参数")],
@@ -71,11 +53,7 @@ async def create_obj_controller(
await FastAPICache.clear(namespace=_POS_NS)
return SuccessResponse(data=result_dict, msg="创建岗位成功")
@PositionRouter.put(
"/update/{id}",
summary="修改岗位",
response_model=ResponseSchema[PositionOutSchema],
)
@PositionRouter.put("/update/{id}", summary="修改岗位", response_model=ResponseSchema[PositionOutSchema])
async def update_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:update"]))],
id: Annotated[int, Path(description="岗位ID")],
@@ -85,11 +63,7 @@ async def update_obj_controller(
await FastAPICache.clear(namespace=_POS_NS)
return SuccessResponse(data=result_dict, msg="修改岗位成功")
@PositionRouter.delete(
"/delete",
summary="删除岗位",
response_model=ResponseSchema[None],
)
@PositionRouter.delete("/delete", summary="删除岗位", response_model=ResponseSchema[None])
async def delete_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -98,11 +72,7 @@ async def delete_obj_controller(
await FastAPICache.clear(namespace=_POS_NS)
return SuccessResponse(msg="删除岗位成功")
@PositionRouter.patch(
"/status/batch",
summary="批量修改岗位状态",
response_model=ResponseSchema[None],
)
@PositionRouter.patch("/status/batch", summary="批量修改岗位状态", response_model=ResponseSchema[None])
async def batch_set_available_obj_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -111,15 +81,11 @@ async def batch_set_available_obj_controller(
await FastAPICache.clear(namespace=_POS_NS)
return SuccessResponse(msg="批量修改岗位状态成功")
@PositionRouter.get(
"/export",
summary="导出岗位",
response_model=ResponseSchema[None],
)
@PositionRouter.get("/export", summary="导出岗位")
async def export_obj_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:position:export"]))],
search: Annotated[PositionQueryParam, Query(description="岗位查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
position_query_result = await PositionService(auth).get_list(search=search)
position_export_result = PositionService.export_list(position_list=position_query_result)
@@ -1,11 +1,7 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
class PositionCreateSchema(BaseModel):
@@ -51,14 +47,16 @@ class PositionOutSchema(PositionCreateSchema, BaseSchema, UserBySchema, TenantBy
model_config = ConfigDict(from_attributes=True)
@dataclass
class PositionQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""岗位管理查询参数"""
name: str | None = Query(None, description="岗位名称")
status: int | None = Query(None, ge=0, le=1, description="状态(0:启动 1:停用)")
name: str | None = Field(None, description="岗位名称")
status: int | None = Field(None, ge=0, le=1, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
self.name = (QueueEnum.like.value, self.name)
@model_validator(mode="after")
def validate_query_params(self) -> "PositionQueryParam":
if self.name:
self.name = (QueueEnum.like.value, self.name)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -6,30 +6,19 @@ from fastapi_cache import FastAPICache
from fastapi_cache.decorator import cache
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission
from app.core.router_class import OperationLogRoute
from app.utils.common_util import bytes2file_response
from .schema import (
RoleCreateSchema,
RoleOutSchema,
RolePermissionSettingSchema,
RoleQueryParam,
RoleUpdateSchema,
)
from .schema import RoleCreateSchema, RoleOutSchema, RolePermissionSettingSchema, RoleQueryParam, RoleUpdateSchema
from .service import RoleService
RoleRouter = APIRouter(route_class=OperationLogRoute, prefix="/role", tags=["角色管理"])
_ROLE_NS = "role"
@RoleRouter.get(
"/list",
summary="查询角色",
response_model=ResponseSchema[PageResultSchema[RoleOutSchema]],
)
@RoleRouter.get("/list", summary="查询角色", response_model=ResponseSchema[PageResultSchema[RoleOutSchema]])
@cache(expire=300, namespace=_ROLE_NS)
async def get_role_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:query"]))],
@@ -47,11 +36,7 @@ async def get_role_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询角色成功")
@RoleRouter.get(
"/detail/{id}",
summary="查询角色详情",
response_model=ResponseSchema[RoleOutSchema],
)
@RoleRouter.get("/detail/{id}", summary="查询角色详情", response_model=ResponseSchema[RoleOutSchema])
async def get_role_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:detail"]))],
id: Annotated[int, Path(description="角色ID")],
@@ -59,11 +44,7 @@ async def get_role_detail_controller(
result_dict = await RoleService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取角色详情成功")
@RoleRouter.post(
"/create",
summary="创建角色",
response_model=ResponseSchema[RoleOutSchema],
)
@RoleRouter.post("/create", summary="创建角色", response_model=ResponseSchema[RoleOutSchema])
async def create_role_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:create"]))],
data: Annotated[RoleCreateSchema, Body(description="角色创建参数")],
@@ -72,11 +53,7 @@ async def create_role_controller(
await FastAPICache.clear(namespace=_ROLE_NS)
return SuccessResponse(data=result_dict, msg="创建角色成功")
@RoleRouter.put(
"/update/{id}",
summary="修改角色",
response_model=ResponseSchema[RoleOutSchema],
)
@RoleRouter.put("/update/{id}", summary="修改角色", response_model=ResponseSchema[RoleOutSchema])
async def update_role_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:update"]))],
id: Annotated[int, Path(description="角色ID")],
@@ -86,11 +63,7 @@ async def update_role_controller(
await FastAPICache.clear(namespace=_ROLE_NS)
return SuccessResponse(data=result_dict, msg="修改角色成功")
@RoleRouter.delete(
"/delete",
summary="删除角色",
response_model=ResponseSchema[None],
)
@RoleRouter.delete("/delete", summary="删除角色", response_model=ResponseSchema[None])
async def delete_role_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -99,11 +72,7 @@ async def delete_role_controller(
await FastAPICache.clear(namespace=_ROLE_NS)
return SuccessResponse(msg="删除角色成功")
@RoleRouter.patch(
"/status/batch",
summary="批量修改角色状态",
response_model=ResponseSchema[None],
)
@RoleRouter.patch("/status/batch", summary="批量修改角色状态", response_model=ResponseSchema[None])
async def batch_set_available_role_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -112,11 +81,7 @@ async def batch_set_available_role_controller(
await FastAPICache.clear(namespace=_ROLE_NS)
return SuccessResponse(msg="批量修改角色状态成功")
@RoleRouter.put(
"/permission",
summary="角色授权",
response_model=ResponseSchema[None],
)
@RoleRouter.put("/permission", summary="角色授权", response_model=ResponseSchema[None])
async def set_role_permission_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:permission"]))],
data: Annotated[RolePermissionSettingSchema, Body(description="角色授权参数")],
@@ -125,15 +90,11 @@ async def set_role_permission_controller(
await FastAPICache.clear(namespace=_ROLE_NS)
return SuccessResponse(msg="授权角色成功")
@RoleRouter.get(
"/export",
summary="导出角色",
response_model=ResponseSchema[None],
)
@RoleRouter.get("/export", summary="导出角色")
async def export_role_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:export"]))],
search: Annotated[RoleQueryParam, Query(description="角色查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
role_query_result = await RoleService(auth).get_list(search=search)
role_export_result = RoleService.export_list(role_list=role_query_result)
@@ -1,6 +1,3 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import (
BaseModel,
ConfigDict,
@@ -12,8 +9,7 @@ from pydantic import (
from app.api.v1.module_platform.menu.schema import MenuOutSchema
from app.api.v1.module_system.dept.schema import DeptOutSchema
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
from app.core.validator import (
role_permission_request_validator,
validate_required_code,
@@ -104,19 +100,21 @@ class RoleOutSchema(RoleCreateSchema, BaseSchema, UserBySchema, TenantBySchema):
depts: list[DeptOutSchema] = Field(default_factory=list, description="角色部门列表")
@dataclass
class RoleQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""
角色管理查询参数
"""
name: str | None = Query(None, description="角色名称")
code: str | None = Query(None, description="角色编码")
status: int | None = Query(None, description="状态(0:启动 1:停用)")
name: str | None = Field(None, description="角色名称")
code: str | None = Field(None, description="角色编码")
status: int | None = Field(None, description="状态(0:启动 1:停用)")
def __post_init__(self) -> None:
self.name = (QueueEnum.like.value, self.name)
@model_validator(mode="after")
def validate_query_params(self) -> "RoleQueryParam":
if self.name:
self.name = (QueueEnum.like.value, self.name)
if self.code:
self.code = (QueueEnum.like.value, self.code)
if self.status is not None:
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -1,6 +1,5 @@
from typing import Any
from app.api.v1.module_platform.tenant.service import TenantService
from app.core.base_schema import AuthSchema, BatchSetAvailable
from app.core.exceptions import CustomException
from app.utils.excel_util import ExcelUtil
@@ -84,6 +83,8 @@ class RoleService:
)
async def create(self, data: RoleCreateSchema) -> RoleOutSchema:
from app.api.v1.module_platform.tenant.service import TenantService
"""
创建角色
@@ -4,18 +4,11 @@ from fastapi import APIRouter, Body, Depends, Path, Query
from fastapi.responses import JSONResponse
from app.common.response import ResponseSchema, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, PageResultSchema
from app.core.base_schema import AuthSchema, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission
from app.core.router_class import OperationLogRoute
from .schema import (
TicketBatchSchema,
TicketCreateSchema,
TicketOutSchema,
TicketQueryParam,
TicketUpdateSchema,
)
from .schema import TicketBatchSchema, TicketCreateSchema, TicketOutSchema, TicketQueryParam, TicketUpdateSchema
from .service import TicketService
TicketRouter = APIRouter(route_class=OperationLogRoute, prefix="/ticket", tags=["工单管理"])
@@ -1,11 +1,7 @@
from dataclasses import dataclass
from fastapi import Query
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.common.enums import QueueEnum, TicketTypeEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, CommonSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, CommonSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
class TicketCreateSchema(BaseModel):
@@ -79,16 +75,16 @@ class TicketBatchSchema(BaseModel):
return v
@dataclass
class TicketQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""工单查询参数"""
title: str | None = None
ticket_type: str | None = None
assigned_id: int | None = None
status: int | None = Query(None, ge=0, le=3, description="状态(0:待处理 1:处理中 2:已完成 3:已关闭)")
title: str | None = Field(None, description="工单标题")
ticket_type: str | None = Field(None, description="工单类型")
assigned_id: int | None = Field(None, description="处理人ID")
status: int | None = Field(None, ge=0, le=3, description="状态(0:待处理 1:处理中 2:已完成 3:已关闭)")
def __post_init__(self) -> None:
@model_validator(mode="after")
def validate_query_params(self) -> "TicketQueryParam":
if self.title:
self.title = (QueueEnum.like.value, self.title)
if self.ticket_type:
@@ -97,3 +93,4 @@ class TicketQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
self.assigned_id = (QueueEnum.eq.value, self.assigned_id)
if isinstance(self.status, int):
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -6,8 +6,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse
from app.core.base_params import PaginationQueryParam
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema
from app.core.base_schema import AuthSchema, BatchSetAvailable, PageResultSchema, PaginationQueryParam
from app.core.dependencies import AuthPermission, db_getter, get_current_user
from app.core.logger import logger
from app.core.router_class import OperationLogRoute
@@ -28,22 +27,14 @@ from .service import UserService
UserRouter = APIRouter(route_class=OperationLogRoute, prefix="/user", tags=["用户管理"])
@UserRouter.get(
"/current/info",
summary="查询当前用户信息",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.get("/current/info", summary="查询当前用户信息", response_model=ResponseSchema[UserOutSchema])
async def get_current_user_info_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
) -> JSONResponse:
user_dict = await UserService(auth).current_info()
return SuccessResponse(data=user_dict, msg="获取当前用户信息成功")
@UserRouter.put(
"/current/info/update",
summary="更新当前用户基本信息",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.put("/current/info/update",summary="更新当前用户基本信息",response_model=ResponseSchema[UserOutSchema])
async def update_current_user_info_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
data: Annotated[CurrentUserUpdateSchema, Body(description="更新用户基本信息参数")],
@@ -51,11 +42,7 @@ async def update_current_user_info_controller(
result_dict = await UserService(auth).update_current_info(data=data)
return SuccessResponse(data=result_dict, msg="更新当前用户基本信息成功")
@UserRouter.put(
"/password/change",
summary="修改当前用户密码",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.put("/password/change", summary="修改当前用户密码", response_model=ResponseSchema[UserOutSchema])
async def change_current_user_password_controller(
auth: Annotated[AuthSchema, Depends(get_current_user)],
data: Annotated[UserChangePasswordSchema, Body(description="修改用户密码参数")],
@@ -63,11 +50,7 @@ async def change_current_user_password_controller(
result_dict = await UserService(auth).change_password(data=data)
return SuccessResponse(data=result_dict, msg="修改密码成功, 请重新登录")
@UserRouter.put(
"/password/reset/{id}",
summary="重置用户密码",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.put("/password/reset/{id}", summary="重置用户密码", response_model=ResponseSchema[UserOutSchema])
async def reset_password_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:update"]))],
id: Annotated[int, Path(description="用户ID")],
@@ -77,11 +60,7 @@ async def reset_password_controller(
result_dict = await UserService(auth).reset_password(data=data)
return SuccessResponse(data=result_dict, msg="重置密码成功")
@UserRouter.post(
"/register",
summary="注册用户",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.post("/register", summary="注册用户", response_model=ResponseSchema[UserOutSchema])
async def register_user_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
data: Annotated[UserRegisterSchema, Body(description="注册用户参数")],
@@ -91,11 +70,7 @@ async def register_user_controller(
logger.info(f"{data.username} 注册用户成功: {user_register_result}")
return SuccessResponse(data=user_register_result, msg="注册用户成功")
@UserRouter.post(
"/password/forget",
summary="忘记密码",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.post("/password/forget", summary="忘记密码", response_model=ResponseSchema[UserOutSchema])
async def forget_password_controller(
db: Annotated[AsyncSession, Depends(db_getter)],
data: Annotated[UserForgetPasswordSchema, Body(description="忘记密码参数")],
@@ -105,11 +80,7 @@ async def forget_password_controller(
logger.info(f"{data.username} 重置密码成功: {user_forget_password_result}")
return SuccessResponse(data=user_forget_password_result, msg="重置密码成功")
@UserRouter.get(
"/list",
summary="查询用户",
response_model=ResponseSchema[PageResultSchema[UserOutSchema]],
)
@UserRouter.get("/list", summary="查询用户", response_model=ResponseSchema[PageResultSchema[UserOutSchema]])
async def get_user_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:query"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
@@ -123,11 +94,7 @@ async def get_user_list_controller(
)
return SuccessResponse(data=result_dict, msg="查询用户成功")
@UserRouter.get(
"/detail/{id}",
summary="查询用户详情",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.get("/detail/{id}", summary="查询用户详情", response_model=ResponseSchema[UserOutSchema])
async def get_user_detail_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:detail"]))],
id: Annotated[int, Path(description="用户ID")],
@@ -135,11 +102,7 @@ async def get_user_detail_controller(
result_dict = await UserService(auth).detail(id=id)
return SuccessResponse(data=result_dict, msg="获取用户详情成功")
@UserRouter.post(
"/create",
summary="创建用户",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.post("/create", summary="创建用户", response_model=ResponseSchema[UserOutSchema])
async def create_user_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:create"]))],
data: Annotated[UserCreateSchema, Body(description="创建用户参数")],
@@ -147,11 +110,7 @@ async def create_user_controller(
result_dict = await UserService(auth).create(data=data)
return SuccessResponse(data=result_dict, msg="创建用户成功")
@UserRouter.put(
"/update/{id}",
summary="修改用户",
response_model=ResponseSchema[UserOutSchema],
)
@UserRouter.put("/update/{id}", summary="修改用户", response_model=ResponseSchema[UserOutSchema])
async def update_user_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:update"]))],
id: Annotated[int, Path(description="用户ID")],
@@ -160,11 +119,7 @@ async def update_user_controller(
result_dict = await UserService(auth).update(id=id, data=data)
return SuccessResponse(data=result_dict, msg="修改用户成功")
@UserRouter.delete(
"/delete",
summary="删除用户",
response_model=ResponseSchema[None],
)
@UserRouter.delete("/delete", summary="删除用户", response_model=ResponseSchema[None])
async def delete_user_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:delete"]))],
ids: Annotated[list[int], Body(description="ID列表")],
@@ -172,11 +127,7 @@ async def delete_user_controller(
await UserService(auth).delete(ids=ids)
return SuccessResponse(msg="删除用户成功")
@UserRouter.patch(
"/status/batch",
summary="批量修改用户状态",
response_model=ResponseSchema[None],
)
@UserRouter.patch("/status/batch", summary="批量修改用户状态", response_model=ResponseSchema[None])
async def batch_set_available_user_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:patch"]))],
data: Annotated[BatchSetAvailable, Body(description="状态设置")],
@@ -184,13 +135,8 @@ async def batch_set_available_user_controller(
await UserService(auth).set_available(data=data)
return SuccessResponse(msg="批量修改用户状态成功")
@UserRouter.get(
"/import/template",
summary="获取用户导入模板",
response_model=ResponseSchema[None],
dependencies=[Depends(AuthPermission(["module_system:user:download"]))],
)
async def export_user_import_template_controller() -> StreamingResponse:
@UserRouter.get("/import/template", summary="获取用户导入模板", dependencies=[Depends(AuthPermission(["module_system:user:download"]))])
async def export_user_import_template_controller() -> StreamingResponse[bytes]:
user_import_template_result = UserService.get_import_template()
return StreamResponse(
@@ -202,16 +148,12 @@ async def export_user_import_template_controller() -> StreamingResponse:
},
)
@UserRouter.get(
"/export",
summary="导出用户",
response_model=ResponseSchema[None],
)
@UserRouter.get("/export", summary="导出用户", response_model=StreamResponse[bytes])
async def export_user_list_controller(
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:export"]))],
page: Annotated[PaginationQueryParam, Query(description="分页参数")],
search: Annotated[UserQueryParam, Query(description="用户查询参数")],
) -> StreamingResponse:
) -> StreamingResponse[bytes]:
user_list = await UserService(auth).get_list(search=search, order_by=page.order_by)
user_export_result = UserService.export_list(user_list=user_list)
@@ -221,11 +163,7 @@ async def export_user_list_controller(
headers={"Content-Disposition": "attachment; filename=user.xlsx"},
)
@UserRouter.post(
"/import/data",
summary="导入用户",
response_model=ResponseSchema[None],
)
@UserRouter.post("/import/data", summary="导入用户", response_model=ResponseSchema[None])
async def import_user_list_controller(
file: Annotated[UploadFile, File(description="用户导入文件")],
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:user:import"]))],
+15 -15
View File
@@ -1,7 +1,5 @@
from dataclasses import dataclass
from urllib.parse import urlparse
from fastapi import Query
from pydantic import (
BaseModel,
ConfigDict,
@@ -14,8 +12,7 @@ from pydantic import (
from app.api.v1.module_platform.menu.schema import MenuOutSchema
from app.api.v1.module_system.role.schema import RoleOutSchema
from app.common.enums import QueueEnum
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
from app.core.base_schema import BaseSchema, CommonSchema, TenantBySchema, UserBySchema
from app.core.base_schema import BaseQueryParam, BaseSchema, CommonSchema, TenantByQueryParam, TenantBySchema, UserByQueryParam, UserBySchema
from app.core.validator import email_validator, mobile_validator
@@ -296,7 +293,6 @@ class UserOutSchema(UserUpdateSchema, BaseSchema, UserBySchema, TenantBySchema):
menus: list[MenuOutSchema] | None = Field(default=[], description="菜单")
@dataclass
class UserQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
"""
用户管理查询参数继承标准 Mixin
@@ -308,25 +304,29 @@ class UserQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
- 业务字段用户名名称手机号邮箱部门状态
"""
username: str | None = Query(None, description="用户名")
name: str | None = Query(None, description="名称")
mobile: str | None = Query(None, description="手机号", pattern=r"^1[3-9]\d{9}$")
email: str | None = Query(
username: str | None = Field(None, description="用户名")
name: str | None = Field(None, description="名称")
mobile: str | None = Field(None, description="手机号", pattern=r"^1[3-9]\d{9}$")
email: str | None = Field(
None,
description="邮箱",
pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
)
dept_id: int | None = Query(None, description="部门ID")
status: int | None = Query(None, description="是否可用")
dept_id: int | None = Field(None, description="部门ID")
status: int | None = Field(None, description="是否可用")
def __post_init__(self) -> None:
self.username = (QueueEnum.like.value, self.username)
self.name = (QueueEnum.like.value, self.name)
@model_validator(mode="after")
def validate_query_params(self) -> "UserQueryParam":
if self.username:
self.username = (QueueEnum.like.value, self.username)
if self.name:
self.name = (QueueEnum.like.value, self.name)
if self.mobile:
self.mobile = (QueueEnum.like.value, self.mobile)
if self.email:
self.email = (QueueEnum.like.value, self.email)
if self.dept_id:
self.dept_id = (QueueEnum.eq.value, self.dept_id)
if self.status:
if self.status is not None:
self.status = (QueueEnum.eq.value, self.status)
return self
@@ -2,10 +2,6 @@ from typing import Any
from fastapi import UploadFile
from app.api.v1.module_platform.menu.crud import MenuCRUD
from app.api.v1.module_platform.menu.schema import MenuOutSchema
from app.api.v1.module_platform.package.service import PackageService
from app.api.v1.module_platform.tenant.service import TenantService
from app.api.v1.module_system.dept.crud import DeptCRUD
from app.api.v1.module_system.position.crud import PositionCRUD
from app.api.v1.module_system.role.crud import RoleCRUD
@@ -69,6 +65,8 @@ class UserService:
)
async def create(self, data: UserCreateSchema) -> UserOutSchema:
from app.api.v1.module_platform.tenant.service import TenantService
if not data.username:
raise CustomException(msg="用户名不能为空")
if data.is_superuser:
@@ -161,6 +159,10 @@ class UserService:
await UserCRUD(self.auth).delete(ids=ids)
async def current_info(self) -> UserOutSchema:
from app.api.v1.module_platform.menu.crud import MenuCRUD
from app.api.v1.module_platform.menu.schema import MenuOutSchema
from app.api.v1.module_platform.package.service import PackageService
if not self.auth.user or not self.auth.user.id:
raise CustomException(msg="该数据不存在")
user = await UserCRUD(self.auth).get(id=self.auth.user.id)