diff --git a/backend/app/api/v1/api.py b/backend/app/api/v1/api.py index 4b1a1e27..eab59e00 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/api/v1/api.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.api import CreateApi, GetAllApi, UpdateApi from backend.app.services.api_service import ApiService @@ -17,13 +17,13 @@ router = APIRouter() @router.get('/all', summary='获取所有接口', dependencies=[DependsJwtAuth]) -async def get_all_apis(): +async def get_all_apis() -> ResponseModel: data = await ApiService.get_all() return await response_base.success(data=data) @router.get('/{pk}', summary='获取接口详情', dependencies=[DependsJwtAuth]) -async def get_api(pk: int): +async def get_api(pk: int) -> ResponseModel: api = await ApiService.get(pk=pk) return await response_base.success(data=api) @@ -41,7 +41,7 @@ async def get_api_list( name: Annotated[str | None, Query()] = None, method: Annotated[str | None, Query()] = None, path: Annotated[str | None, Query()] = None, -): +) -> ResponseModel: api_select = await ApiService.get_select(name=name, method=method, path=path) page_data = await paging_data(db, api_select, GetAllApi) return await response_base.success(data=page_data) @@ -55,7 +55,7 @@ async def get_api_list( DependsRBAC, ], ) -async def create_api(obj: CreateApi): +async def create_api(obj: CreateApi) -> ResponseModel: await ApiService.create(obj=obj) return await response_base.success() @@ -68,7 +68,7 @@ async def create_api(obj: CreateApi): DependsRBAC, ], ) -async def update_api(pk: int, obj: UpdateApi): +async def update_api(pk: int, obj: UpdateApi) -> ResponseModel: count = await ApiService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -83,7 +83,7 @@ async def update_api(pk: int, obj: UpdateApi): DependsRBAC, ], ) -async def delete_api(pk: Annotated[list[int], Query(...)]): +async def delete_api(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await ApiService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/auth/auth.py b/backend/app/api/v1/auth/auth.py index f7d53e23..97716113 100644 --- a/backend/app/api/v1/auth/auth.py +++ b/backend/app/api/v1/auth/auth.py @@ -8,7 +8,7 @@ from fastapi_limiter.depends import RateLimiter from starlette.background import BackgroundTasks from backend.app.common.jwt import DependsJwtAuth -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.schemas.token import GetLoginToken, GetNewToken, GetSwaggerToken from backend.app.schemas.user import AuthLogin from backend.app.services.auth_service import AuthService @@ -33,7 +33,7 @@ async def swagger_user_login(form_data: OAuth2PasswordRequestForm = Depends()) - description='json 格式登录, 仅支持在第三方api工具调试, 例如: postman', dependencies=[Depends(RateLimiter(times=5, minutes=1))], ) -async def user_login(request: Request, obj: AuthLogin, background_tasks: BackgroundTasks): +async def user_login(request: Request, obj: AuthLogin, background_tasks: BackgroundTasks) -> ResponseModel: access_token, refresh_token, access_expire, refresh_expire, user = await AuthService().login( request=request, obj=obj, background_tasks=background_tasks ) @@ -48,7 +48,7 @@ async def user_login(request: Request, obj: AuthLogin, background_tasks: Backgro @router.post('/new_token', summary='创建新 token', dependencies=[DependsJwtAuth]) -async def create_new_token(request: Request, refresh_token: Annotated[str, Query(...)]): +async def create_new_token(request: Request, refresh_token: Annotated[str, Query(...)]) -> ResponseModel: ( new_access_token, new_refresh_token, @@ -65,6 +65,6 @@ async def create_new_token(request: Request, refresh_token: Annotated[str, Query @router.post('/logout', summary='用户登出', dependencies=[DependsJwtAuth]) -async def user_logout(request: Request): +async def user_logout(request: Request) -> ResponseModel: await AuthService.logout(request=request) return await response_base.success() diff --git a/backend/app/api/v1/auth/captcha.py b/backend/app/api/v1/auth/captcha.py index 0ae885bb..267518e7 100644 --- a/backend/app/api/v1/auth/captcha.py +++ b/backend/app/api/v1/auth/captcha.py @@ -6,7 +6,7 @@ from fastapi_limiter.depends import RateLimiter from starlette.concurrency import run_in_threadpool from backend.app.common.redis import redis_client -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.core.conf import settings router = APIRouter() @@ -17,7 +17,7 @@ router = APIRouter() summary='获取登录验证码', dependencies=[Depends(RateLimiter(times=5, seconds=10))], ) -async def get_captcha(request: Request): +async def get_captcha(request: Request) -> ResponseModel: """ 此接口可能存在性能损耗,尽管是异步接口,但是验证码生成是IO密集型任务,使用线程池尽量减少性能损耗 """ diff --git a/backend/app/api/v1/casbin.py b/backend/app/api/v1/casbin.py index 75672b0a..02c412b6 100644 --- a/backend/app/api/v1/casbin.py +++ b/backend/app/api/v1/casbin.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.casbin_rule import ( CreatePolicy, @@ -36,20 +36,20 @@ async def get_all_casbin( db: CurrentSession, ptype: Annotated[str | None, Query(description='规则类型, p / g')] = None, sub: Annotated[str | None, Query(description='用户 uuid / 角色')] = None, -): +) -> ResponseModel: casbin_select = await CasbinService.get_casbin_list(ptype=ptype, sub=sub) page_data = await paging_data(db, casbin_select, GetAllPolicy) return await response_base.success(data=page_data) @router.get('/policy', summary='获取所有P权限规则', dependencies=[DependsJwtAuth]) -async def get_all_policies(): +async def get_all_policies() -> ResponseModel: policies = await CasbinService.get_policy_list() return await response_base.success(data=policies) @router.get('/policy/{role}/all', summary='获取指定角色的所有P权限规则', dependencies=[DependsJwtAuth]) -async def get_role_policies(role: Annotated[str, Path(description='角色ID')]): +async def get_role_policies(role: Annotated[str, Path(description='角色ID')]) -> ResponseModel: policies = await CasbinService.get_policy_list_by_role(role=role) return await response_base.success(data=policies) @@ -62,7 +62,7 @@ async def get_role_policies(role: Annotated[str, Path(description='角色ID')]): DependsRBAC, ], ) -async def create_policy(p: CreatePolicy): +async def create_policy(p: CreatePolicy) -> ResponseModel: """ p 规则: @@ -84,7 +84,7 @@ async def create_policy(p: CreatePolicy): DependsRBAC, ], ) -async def create_policies(ps: list[CreatePolicy]): +async def create_policies(ps: list[CreatePolicy]) -> ResponseModel: data = await CasbinService.create_policies(ps=ps) return await response_base.success(data=data) @@ -97,7 +97,7 @@ async def create_policies(ps: list[CreatePolicy]): DependsRBAC, ], ) -async def update_policy(old: UpdatePolicy, new: UpdatePolicy): +async def update_policy(old: UpdatePolicy, new: UpdatePolicy) -> ResponseModel: data = await CasbinService.update_policy(old=old, new=new) return await response_base.success(data=data) @@ -110,7 +110,7 @@ async def update_policy(old: UpdatePolicy, new: UpdatePolicy): DependsRBAC, ], ) -async def update_policies(old: list[UpdatePolicy], new: list[UpdatePolicy]): +async def update_policies(old: list[UpdatePolicy], new: list[UpdatePolicy]) -> ResponseModel: data = await CasbinService.update_policies(old=old, new=new) return await response_base.success(data=data) @@ -123,7 +123,7 @@ async def update_policies(old: list[UpdatePolicy], new: list[UpdatePolicy]): DependsRBAC, ], ) -async def delete_policy(p: DeletePolicy): +async def delete_policy(p: DeletePolicy) -> ResponseModel: data = await CasbinService.delete_policy(p=p) return await response_base.success(data=data) @@ -136,7 +136,7 @@ async def delete_policy(p: DeletePolicy): DependsRBAC, ], ) -async def delete_policies(ps: list[DeletePolicy]): +async def delete_policies(ps: list[DeletePolicy]) -> ResponseModel: data = await CasbinService.delete_policies(ps=ps) return await response_base.success(data=data) @@ -149,7 +149,7 @@ async def delete_policies(ps: list[DeletePolicy]): DependsRBAC, ], ) -async def delete_all_policies(sub: DeleteAllPolicies): +async def delete_all_policies(sub: DeleteAllPolicies) -> ResponseModel: count = await CasbinService.delete_all_policies(sub=sub) if count > 0: return await response_base.success() @@ -157,7 +157,7 @@ async def delete_all_policies(sub: DeleteAllPolicies): @router.get('/group', summary='获取所有G权限规则', dependencies=[DependsJwtAuth]) -async def get_all_groups(): +async def get_all_groups() -> ResponseModel: data = await CasbinService.get_group_list() return await response_base.success(data=data) @@ -170,7 +170,7 @@ async def get_all_groups(): DependsRBAC, ], ) -async def create_group(g: CreateUserRole): +async def create_group(g: CreateUserRole) -> ResponseModel: """ g 规则 (**依赖 p 规则**): @@ -192,7 +192,7 @@ async def create_group(g: CreateUserRole): DependsRBAC, ], ) -async def create_groups(gs: list[CreateUserRole]): +async def create_groups(gs: list[CreateUserRole]) -> ResponseModel: data = await CasbinService.create_groups(gs=gs) return await response_base.success(data=data) @@ -205,7 +205,7 @@ async def create_groups(gs: list[CreateUserRole]): DependsRBAC, ], ) -async def delete_group(g: DeleteUserRole): +async def delete_group(g: DeleteUserRole) -> ResponseModel: data = await CasbinService.delete_group(g=g) return await response_base.success(data=data) @@ -218,7 +218,7 @@ async def delete_group(g: DeleteUserRole): DependsRBAC, ], ) -async def delete_groups(gs: list[DeleteUserRole]): +async def delete_groups(gs: list[DeleteUserRole]) -> ResponseModel: data = await CasbinService.delete_groups(gs=gs) return await response_base.success(data=data) @@ -231,7 +231,7 @@ async def delete_groups(gs: list[DeleteUserRole]): DependsRBAC, ], ) -async def delete_all_groups(uuid: str): +async def delete_all_groups(uuid: str) -> ResponseModel: count = await CasbinService.delete_all_groups(uuid=uuid) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/dept.py b/backend/app/api/v1/dept.py index f3ba41e8..a81bd779 100644 --- a/backend/app/api/v1/dept.py +++ b/backend/app/api/v1/dept.py @@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, Query from backend.app.common.jwt import DependsJwtAuth from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.schemas.dept import CreateDept, GetAllDept, UpdateDept from backend.app.services.dept_service import DeptService from backend.app.utils.serializers import select_as_dict @@ -16,7 +16,7 @@ router = APIRouter() @router.get('/{pk}', summary='获取部门详情', dependencies=[DependsJwtAuth]) -async def get_dept(pk: int): +async def get_dept(pk: int) -> ResponseModel: dept = await DeptService.get(pk=pk) data = GetAllDept(**await select_as_dict(dept)) return await response_base.success(data=data) @@ -28,7 +28,7 @@ async def get_all_depts( leader: Annotated[str | None, Query()] = None, phone: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, -): +) -> ResponseModel: dept = await DeptService.get_dept_tree(name=name, leader=leader, phone=phone, status=status) return await response_base.success(data=dept) @@ -41,7 +41,7 @@ async def get_all_depts( DependsRBAC, ], ) -async def create_dept(obj: CreateDept): +async def create_dept(obj: CreateDept) -> ResponseModel: await DeptService.create(obj=obj) return await response_base.success() @@ -54,7 +54,7 @@ async def create_dept(obj: CreateDept): DependsRBAC, ], ) -async def update_dept(pk: int, obj: UpdateDept): +async def update_dept(pk: int, obj: UpdateDept) -> ResponseModel: count = await DeptService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -69,7 +69,7 @@ async def update_dept(pk: int, obj: UpdateDept): DependsRBAC, ], ) -async def delete_dept(pk: int): +async def delete_dept(pk: int) -> ResponseModel: count = await DeptService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/dict_data.py b/backend/app/api/v1/dict_data.py index af27b2a9..f45da66b 100644 --- a/backend/app/api/v1/dict_data.py +++ b/backend/app/api/v1/dict_data.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.dict_data import CreateDictData, GetAllDictData, UpdateDictData from backend.app.services.dict_data_service import DictDataService @@ -18,7 +18,7 @@ router = APIRouter() @router.get('/{pk}', summary='获取字典详情', dependencies=[DependsJwtAuth]) -async def get_dict_data(pk: int): +async def get_dict_data(pk: int) -> ResponseModel: dict_data = await DictDataService.get(pk=pk) data = GetAllDictData(**await select_as_dict(dict_data)) return await response_base.success(data=data) @@ -37,7 +37,7 @@ async def get_all_dict_datas( label: Annotated[str | None, Query()] = None, value: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, -): +) -> ResponseModel: dict_data_select = await DictDataService.get_select(label=label, value=value, status=status) page_data = await paging_data(db, dict_data_select, GetAllDictData) return await response_base.success(data=page_data) @@ -51,7 +51,7 @@ async def get_all_dict_datas( DependsRBAC, ], ) -async def create_dict_data(obj: CreateDictData): +async def create_dict_data(obj: CreateDictData) -> ResponseModel: await DictDataService.create(obj=obj) return await response_base.success() @@ -64,7 +64,7 @@ async def create_dict_data(obj: CreateDictData): DependsRBAC, ], ) -async def update_dict_data(pk: int, obj: UpdateDictData): +async def update_dict_data(pk: int, obj: UpdateDictData) -> ResponseModel: count = await DictDataService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -79,7 +79,7 @@ async def update_dict_data(pk: int, obj: UpdateDictData): DependsRBAC, ], ) -async def delete_dict_data(pk: Annotated[list[int], Query(...)]): +async def delete_dict_data(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await DictDataService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/dict_type.py b/backend/app/api/v1/dict_type.py index e3469949..4a2b5d19 100644 --- a/backend/app/api/v1/dict_type.py +++ b/backend/app/api/v1/dict_type.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.dict_type import CreateDictType, GetAllDictType, UpdateDictType from backend.app.services.dict_type_service import DictTypeService @@ -29,7 +29,7 @@ async def get_all_dict_types( name: Annotated[str | None, Query()] = None, code: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, -): +) -> ResponseModel: dict_type_select = await DictTypeService.get_select(name=name, code=code, status=status) page_data = await paging_data(db, dict_type_select, GetAllDictType) return await response_base.success(data=page_data) @@ -43,7 +43,7 @@ async def get_all_dict_types( DependsRBAC, ], ) -async def create_dict_type(obj: CreateDictType): +async def create_dict_type(obj: CreateDictType) -> ResponseModel: await DictTypeService.create(obj=obj) return await response_base.success() @@ -56,7 +56,7 @@ async def create_dict_type(obj: CreateDictType): DependsRBAC, ], ) -async def update_dict_type(pk: int, obj: UpdateDictType): +async def update_dict_type(pk: int, obj: UpdateDictType) -> ResponseModel: count = await DictTypeService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -71,7 +71,7 @@ async def update_dict_type(pk: int, obj: UpdateDictType): DependsRBAC, ], ) -async def delete_dict_type(pk: Annotated[list[int], Query(...)]): +async def delete_dict_type(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await DictTypeService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/log/login_log.py b/backend/app/api/v1/log/login_log.py index 70cd3a5c..67d5e575 100644 --- a/backend/app/api/v1/log/login_log.py +++ b/backend/app/api/v1/log/login_log.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.login_log import GetAllLoginLog from backend.app.services.login_log_service import LoginLogService @@ -29,7 +29,7 @@ async def get_all_login_logs( username: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, ip: Annotated[str | None, Query()] = None, -): +) -> ResponseModel: log_select = await LoginLogService.get_select(username=username, status=status, ip=ip) page_data = await paging_data(db, log_select, GetAllLoginLog) return await response_base.success(data=page_data) @@ -43,7 +43,7 @@ async def get_all_login_logs( DependsRBAC, ], ) -async def delete_login_log(pk: Annotated[list[int], Query(...)]): +async def delete_login_log(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await LoginLogService.delete(pk=pk) if count > 0: return await response_base.success() @@ -58,7 +58,7 @@ async def delete_login_log(pk: Annotated[list[int], Query(...)]): DependsRBAC, ], ) -async def delete_all_login_logs(): +async def delete_all_login_logs() -> ResponseModel: count = await LoginLogService.delete_all() if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/log/opera_log.py b/backend/app/api/v1/log/opera_log.py index 922ec43d..9a264c38 100644 --- a/backend/app/api/v1/log/opera_log.py +++ b/backend/app/api/v1/log/opera_log.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.opera_log import GetAllOperaLog from backend.app.services.opera_log_service import OperaLogService @@ -29,7 +29,7 @@ async def get_all_opera_logs( username: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, ip: Annotated[str | None, Query()] = None, -): +) -> ResponseModel: log_select = await OperaLogService.get_select(username=username, status=status, ip=ip) page_data = await paging_data(db, log_select, GetAllOperaLog) return await response_base.success(data=page_data) @@ -43,7 +43,7 @@ async def get_all_opera_logs( DependsRBAC, ], ) -async def delete_opera_log(pk: Annotated[list[int], Query(...)]): +async def delete_opera_log(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await OperaLogService.delete(pk=pk) if count > 0: return await response_base.success() @@ -58,7 +58,7 @@ async def delete_opera_log(pk: Annotated[list[int], Query(...)]): DependsRBAC, ], ) -async def delete_all_opera_logs(): +async def delete_all_opera_logs() -> ResponseModel: count = await OperaLogService.delete_all() if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/menu.py b/backend/app/api/v1/menu.py index 04dd9191..331d245e 100644 --- a/backend/app/api/v1/menu.py +++ b/backend/app/api/v1/menu.py @@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, Query, Request from backend.app.common.jwt import DependsJwtAuth from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.schemas.menu import CreateMenu, GetAllMenu, UpdateMenu from backend.app.services.menu_service import MenuService from backend.app.utils.serializers import select_as_dict @@ -16,13 +16,13 @@ router = APIRouter() @router.get('/sidebar', summary='获取用户菜单展示树', dependencies=[DependsJwtAuth]) -async def get_user_menus(request: Request): +async def get_user_menus(request: Request) -> ResponseModel: menu = await MenuService.get_user_menu_tree(request=request) return await response_base.success(data=menu) @router.get('/{pk}', summary='获取菜单详情', dependencies=[DependsJwtAuth]) -async def get_menu(pk: int): +async def get_menu(pk: int) -> ResponseModel: menu = await MenuService.get(pk=pk) data = GetAllMenu(**await select_as_dict(menu)) return await response_base.success(data=data) @@ -32,7 +32,7 @@ async def get_menu(pk: int): async def get_all_menus( title: Annotated[str | None, Query()] = None, status: Annotated[int | None, Query()] = None, -): +) -> ResponseModel: menu = await MenuService.get_menu_tree(title=title, status=status) return await response_base.success(data=menu) @@ -45,7 +45,7 @@ async def get_all_menus( DependsRBAC, ], ) -async def create_menu(obj: CreateMenu): +async def create_menu(obj: CreateMenu) -> ResponseModel: await MenuService.create(obj=obj) return await response_base.success() @@ -58,7 +58,7 @@ async def create_menu(obj: CreateMenu): DependsRBAC, ], ) -async def update_menu(pk: int, obj: UpdateMenu): +async def update_menu(pk: int, obj: UpdateMenu) -> ResponseModel: count = await MenuService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -73,7 +73,7 @@ async def update_menu(pk: int, obj: UpdateMenu): DependsRBAC, ], ) -async def delete_menu(pk: int): +async def delete_menu(pk: int) -> ResponseModel: count = await MenuService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/mixed/config.py b/backend/app/api/v1/mixed/config.py index 4e117680..9941a786 100644 --- a/backend/app/api/v1/mixed/config.py +++ b/backend/app/api/v1/mixed/config.py @@ -5,7 +5,7 @@ from fastapi.routing import APIRoute from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base router = APIRouter() @@ -18,7 +18,7 @@ router = APIRouter() DependsRBAC, ], ) -async def get_all_route(request: Request): +async def get_all_route(request: Request) -> ResponseModel: data = [] for route in request.app.routes: if isinstance(route, APIRoute): diff --git a/backend/app/api/v1/mixed/tests.py b/backend/app/api/v1/mixed/tests.py index 9db11a61..ccf037ff 100644 --- a/backend/app/api/v1/mixed/tests.py +++ b/backend/app/api/v1/mixed/tests.py @@ -4,14 +4,14 @@ from typing import Annotated from fastapi import APIRouter, File, Form, UploadFile -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.tasks import task_demo_async router = APIRouter(prefix='/tests') @router.post('/send', summary='异步任务演示') -async def send_task(): +async def send_task() -> ResponseModel: result = task_demo_async.delay() return await response_base.success(data=result.id) @@ -21,9 +21,11 @@ async def create_file( file: Annotated[bytes, File()], fileb: Annotated[UploadFile, File()], token: Annotated[str, Form()], -): - return { - 'file_size': len(file), - 'token': token, - 'fileb_content_type': fileb.content_type, - } +) -> ResponseModel: + return ResponseModel( + data={ + 'file_size': len(file), + 'token': token, + 'fileb_content_type': fileb.content_type, + } + ) diff --git a/backend/app/api/v1/monitor/redis.py b/backend/app/api/v1/monitor/redis.py index 8f6ff6cc..6826a6e2 100644 --- a/backend/app/api/v1/monitor/redis.py +++ b/backend/app/api/v1/monitor/redis.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends from backend.app.common.jwt import DependsJwtAuth from backend.app.common.permission import RequestPermission -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.utils.redis_info import redis_info router = APIRouter() @@ -18,6 +18,6 @@ router = APIRouter() DependsJwtAuth, ], ) -async def get_redis_info(): +async def get_redis_info() -> ResponseModel: data = {'info': await redis_info.get_info(), 'stats': await redis_info.get_stats()} return await response_base.success(data=data) diff --git a/backend/app/api/v1/monitor/server.py b/backend/app/api/v1/monitor/server.py index 47aa4599..8b4c16bb 100644 --- a/backend/app/api/v1/monitor/server.py +++ b/backend/app/api/v1/monitor/server.py @@ -5,7 +5,7 @@ from starlette.concurrency import run_in_threadpool from backend.app.common.jwt import DependsJwtAuth from backend.app.common.permission import RequestPermission -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.utils.server_info import server_info router = APIRouter() @@ -19,7 +19,7 @@ router = APIRouter() DependsJwtAuth, ], ) -async def get_server_info(): +async def get_server_info() -> ResponseModel: """IO密集型任务,使用线程池尽量减少性能损耗""" data = { 'cpu': await run_in_threadpool(server_info.get_cpu_info), diff --git a/backend/app/api/v1/role.py b/backend/app/api/v1/role.py index cdb7dc58..91e25e61 100644 --- a/backend/app/api/v1/role.py +++ b/backend/app/api/v1/role.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.role import CreateRole, GetAllRole, UpdateRole, UpdateRoleMenu from backend.app.services.menu_service import MenuService @@ -19,27 +19,27 @@ router = APIRouter() @router.get('/all', summary='获取所有角色', dependencies=[DependsJwtAuth]) -async def get_all_roles(): +async def get_all_roles() -> ResponseModel: roles = await RoleService.get_all() data = await select_list_serialize(roles) return await response_base.success(data=data) @router.get('/{pk}/all', summary='获取用户所有角色', dependencies=[DependsJwtAuth]) -async def get_user_all_roles(pk: int): +async def get_user_all_roles(pk: int) -> ResponseModel: roles = await RoleService.get_user_all(pk=pk) data = await select_list_serialize(roles) return await response_base.success(data=data) @router.get('/{pk}/menus', summary='获取角色所有菜单', dependencies=[DependsJwtAuth]) -async def get_role_all_menus(pk: int): +async def get_role_all_menus(pk: int) -> ResponseModel: menu = await MenuService.get_role_menu_tree(pk=pk) return await response_base.success(data=menu) @router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth]) -async def get_role(pk: int): +async def get_role(pk: int) -> ResponseModel: role = await RoleService.get(pk=pk) data = GetAllRole(**await select_as_dict(role)) return await response_base.success(data=data) @@ -58,7 +58,7 @@ async def get_all_role_list( name: Annotated[str | None, Query()] = None, data_scope: Annotated[int | None, Query()] = None, status: Annotated[int | None, Query()] = None, -): +) -> ResponseModel: role_select = await RoleService.get_select(name=name, data_scope=data_scope, status=status) page_data = await paging_data(db, role_select, GetAllRole) return await response_base.success(data=page_data) @@ -72,7 +72,7 @@ async def get_all_role_list( DependsRBAC, ], ) -async def create_role(obj: CreateRole): +async def create_role(obj: CreateRole) -> ResponseModel: await RoleService.create(obj=obj) return await response_base.success() @@ -85,7 +85,7 @@ async def create_role(obj: CreateRole): DependsRBAC, ], ) -async def update_role(pk: int, obj: UpdateRole): +async def update_role(pk: int, obj: UpdateRole) -> ResponseModel: count = await RoleService.update(pk=pk, obj=obj) if count > 0: return await response_base.success() @@ -100,7 +100,7 @@ async def update_role(pk: int, obj: UpdateRole): DependsRBAC, ], ) -async def update_role_menu(request: Request, pk: int, menu_ids: UpdateRoleMenu): +async def update_role_menu(request: Request, pk: int, menu_ids: UpdateRoleMenu) -> ResponseModel: count = await RoleService.update_menus(request=request, pk=pk, menu_ids=menu_ids) if count > 0: return await response_base.success() @@ -115,7 +115,7 @@ async def update_role_menu(request: Request, pk: int, menu_ids: UpdateRoleMenu): DependsRBAC, ], ) -async def delete_role(pk: Annotated[list[int], Query(...)]): +async def delete_role(pk: Annotated[list[int], Query(...)]) -> ResponseModel: count = await RoleService.delete(pk=pk) if count > 0: return await response_base.success() diff --git a/backend/app/api/v1/task.py b/backend/app/api/v1/task.py index 2745a65f..872fca23 100644 --- a/backend/app/api/v1/task.py +++ b/backend/app/api/v1/task.py @@ -8,20 +8,20 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC from backend.app.common.response.response_code import CustomResponseCode -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.services.task_service import TaskService router = APIRouter() @router.get('', summary='获取所有可执行任务模块', dependencies=[DependsJwtAuth]) -async def get_all_tasks(): +async def get_all_tasks() -> ResponseModel: tasks = TaskService.gets() return await response_base.success(data=tasks) @router.get('/{pk}', summary='获取任务结果', dependencies=[DependsJwtAuth]) -async def get_task_result(pk: str = Path(description='任务ID')): +async def get_task_result(pk: str = Path(description='任务ID')) -> ResponseModel: task = TaskService.get(pk) if not task: return await response_base.fail(res=CustomResponseCode.HTTP_204, data=pk) @@ -40,6 +40,6 @@ async def run_task( module: Annotated[str, Path(description='任务模块')], args: Annotated[list | None, Body()] = None, kwargs: Annotated[dict | None, Body()] = None, -): +) -> ResponseModel: task = TaskService.run(module=module, args=args, kwargs=kwargs) return await response_base.success(data=task.result) diff --git a/backend/app/api/v1/user.py b/backend/app/api/v1/user.py index 2cfb8eb1..d19a31ea 100644 --- a/backend/app/api/v1/user.py +++ b/backend/app/api/v1/user.py @@ -8,7 +8,7 @@ from backend.app.common.jwt import DependsJwtAuth from backend.app.common.pagination import DependsPagination, paging_data from backend.app.common.permission import RequestPermission from backend.app.common.rbac import DependsRBAC -from backend.app.common.response.response_schema import response_base +from backend.app.common.response.response_schema import ResponseModel, response_base from backend.app.database.db_mysql import CurrentSession from backend.app.schemas.user import ( AddUser, @@ -27,7 +27,7 @@ router = APIRouter() @router.post('/register', summary='用户注册') -async def user_register(obj: RegisterUser): +async def user_register(obj: RegisterUser) -> ResponseModel: await UserService.register(obj=obj) return await response_base.success() @@ -48,10 +48,10 @@ async def password_reset(request: Request, obj: ResetPassword): return await response_base.fail() -@router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth]) +@router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth], response_model_exclude={'password'}) async def get_current_userinfo(request: Request): data = GetCurrentUserInfo(**await select_as_dict(request.user)) - return await response_base.success(data=data, exclude={'password'}) + return await response_base.success(data=data) @router.get('/{username}', summary='查看用户信息', dependencies=[DependsJwtAuth]) diff --git a/backend/app/common/exception/exception_handler.py b/backend/app/common/exception/exception_handler.py index d7aeaa19..c702ecae 100644 --- a/backend/app/common/exception/exception_handler.py +++ b/backend/app/common/exception/exception_handler.py @@ -6,17 +6,17 @@ from pydantic import ValidationError from pydantic.errors import PydanticUserError from starlette.exceptions import HTTPException from starlette.middleware.cors import CORSMiddleware -from starlette.responses import JSONResponse from backend.app.common.exception.errors import BaseExceptionMixin from backend.app.common.log import log -from backend.app.common.response.response_code import CustomResponse, CustomResponseCode, StandardResponseCode -from backend.app.common.response.response_schema import ResponseModel, response_base +from backend.app.common.response.response_code import CustomResponseCode, StandardResponseCode +from backend.app.common.response.response_schema import response_base from backend.app.core.conf import settings from backend.app.schemas.base import ( CUSTOM_USAGE_ERROR_MESSAGES, CUSTOM_VALIDATION_ERROR_MESSAGES, ) +from backend.app.utils.serializers import MsgSpecJSONResponse async def _validation_exception_handler(request: Request, e: RequestValidationError | ValidationError): @@ -43,18 +43,13 @@ async def _validation_exception_handler(request: Request, e: RequestValidationEr message = f'{field} {error_msg},输入:{error_input}' msg = f'请求参数非法: {message}' data = {'errors': errors} if settings.ENVIRONMENT == 'dev' else None - content = ResponseModel( - code=StandardResponseCode.HTTP_422, - msg=msg, - ).model_dump() + content = { + 'code': StandardResponseCode.HTTP_422, + 'msg': msg, + 'data': data, + } request.state.__request_validation_exception__ = content # 用于在中间件中获取异常信息 - return JSONResponse( - status_code=422, - content=await response_base.fail( - res=CustomResponse(code=StandardResponseCode.HTTP_422, msg=msg), - data=data, - ), - ) + return MsgSpecJSONResponse(status_code=422, content=content) def register_exception(app: FastAPI): @@ -67,13 +62,19 @@ def register_exception(app: FastAPI): :param exc: :return: """ - content = ResponseModel(code=exc.status_code, msg=exc.detail).model_dump() + if settings.ENVIRONMENT == 'dev': + content = { + 'code': exc.status_code, + 'msg': exc.detail, + 'data': None, + } + else: + res = await response_base.fail(res=CustomResponseCode.HTTP_400) + content = res.model_dump() request.state.__request_http_exception__ = content # 用于在中间件中获取异常信息 - return JSONResponse( + return MsgSpecJSONResponse( status_code=StandardResponseCode.HTTP_400, - content=content - if settings.ENVIRONMENT == 'dev' - else await response_base.fail(res=CustomResponseCode.HTTP_400), + content=content, headers=exc.headers, ) @@ -108,11 +109,13 @@ def register_exception(app: FastAPI): :param exc: :return: """ - return JSONResponse( + return MsgSpecJSONResponse( status_code=StandardResponseCode.HTTP_500, - content=await response_base.fail( - res=CustomResponse(code=StandardResponseCode.HTTP_500, msg=CUSTOM_USAGE_ERROR_MESSAGES.get(exc.code)) - ), + content={ + 'code': StandardResponseCode.HTTP_500, + 'msg': CUSTOM_USAGE_ERROR_MESSAGES.get(exc.code), + 'data': None, + }, ) @app.exception_handler(AssertionError) @@ -124,14 +127,18 @@ def register_exception(app: FastAPI): :param exc: :return: """ - return JSONResponse( + if settings.ENVIRONMENT == 'dev': + content = { + 'code': StandardResponseCode.HTTP_500, + 'msg': str(''.join(exc.args) if exc.args else exc.__doc__), + 'data': None, + } + else: + res = await response_base.fail(res=CustomResponseCode.HTTP_500) + content = res.model_dump() + return MsgSpecJSONResponse( status_code=StandardResponseCode.HTTP_500, - content=ResponseModel( - code=StandardResponseCode.HTTP_500, - msg=str(''.join(exc.args) if exc.args else exc.__doc__), - ).model_dump() - if settings.ENVIRONMENT == 'dev' - else await response_base.fail(CustomResponseCode.HTTP_500), + content=content, ) @app.exception_handler(Exception) @@ -144,13 +151,13 @@ def register_exception(app: FastAPI): :return: """ if isinstance(exc, BaseExceptionMixin): - return JSONResponse( + return MsgSpecJSONResponse( status_code=StandardResponseCode.HTTP_400, - content=ResponseModel( - code=exc.code, - msg=str(exc.msg), - data=exc.data if exc.data else None, - ).model_dump(), + content={ + 'code': exc.code, + 'msg': str(exc.msg), + 'data': exc.data if exc.data else None, + }, background=exc.background, ) else: @@ -158,12 +165,16 @@ def register_exception(app: FastAPI): log.error(f'未知异常: {exc}') log.error(traceback.format_exc()) - return JSONResponse( - status_code=StandardResponseCode.HTTP_500, - content=ResponseModel(code=500, msg=str(exc)).model_dump() - if settings.ENVIRONMENT == 'dev' - else await response_base.fail(CustomResponseCode.HTTP_500), - ) + if settings.ENVIRONMENT == 'dev': + content = { + 'code': 500, + 'msg': str(exc), + 'data': None, + } + else: + res = await response_base.fail(res=CustomResponseCode.HTTP_500) + content = res.model_dump() + return MsgSpecJSONResponse(status_code=StandardResponseCode.HTTP_500, content=content) if settings.MIDDLEWARE_CORS: @@ -179,14 +190,22 @@ def register_exception(app: FastAPI): :return: """ if isinstance(exc, BaseExceptionMixin): - content = ResponseModel(code=exc.code, msg=exc.msg, data=exc.data).model_dump() + content = { + 'code': exc.code, + 'msg': exc.msg, + 'data': exc.data, + } else: - content = ( - ResponseModel(code=StandardResponseCode.HTTP_500, msg=str(exc)).model_dump() - if settings.ENVIRONMENT == 'dev' - else await response_base.fail(CustomResponseCode.HTTP_500) - ) - response = JSONResponse( + if settings.ENVIRONMENT == 'dev': + content = { + 'code': StandardResponseCode.HTTP_500, + 'msg': str(exc), + 'data': None, + } + else: + res = await response_base.fail(res=CustomResponseCode.HTTP_500) + content = res.model_dump() + response = MsgSpecJSONResponse( status_code=exc.code if isinstance(exc, BaseExceptionMixin) else StandardResponseCode.HTTP_500, content=content, background=exc.background if isinstance(exc, BaseExceptionMixin) else None, diff --git a/backend/app/common/rbac.py b/backend/app/common/rbac.py index b43c643b..a4b3b700 100644 --- a/backend/app/common/rbac.py +++ b/backend/app/common/rbac.py @@ -5,7 +5,7 @@ import casbin_async_sqlalchemy_adapter from fastapi import Depends, Request -from backend.app.common.enums import StatusType, MethodType +from backend.app.common.enums import MethodType, StatusType from backend.app.common.exception.errors import AuthorizationError, TokenError from backend.app.common.jwt import DependsJwtAuth from backend.app.common.redis import redis_client diff --git a/backend/app/common/response/response_schema.py b/backend/app/common/response/response_schema.py index 983f5280..bb517656 100644 --- a/backend/app/common/response/response_schema.py +++ b/backend/app/common/response/response_schema.py @@ -3,7 +3,6 @@ from datetime import datetime from typing import Any -from fastapi.encoders import jsonable_encoder from pydantic import BaseModel, ConfigDict from backend.app.common.response.response_code import CustomResponse, CustomResponseCode @@ -53,60 +52,41 @@ class ResponseBase: .. tip:: - 此类中的返回方法将通过自定义编码器预解析,然后由 fastapi 内部的编码器再次处理并返回,可能存在性能损耗,取决于个人喜好; - 此返回模型不会生成 openapi schema 文档 + 此类中的返回方法将返回 ResponseModel 模型,作为一种编码风格而存在; E.g. :: @router.get('/test') - def test(): + def test() -> ResponseModel: return await response_base.success(data={'test': 'test'}) - """ # noqa: E501 + """ @staticmethod - async def __response( - *, - res: CustomResponseCode | CustomResponse = None, - data: Any | None = None, - exclude: _ExcludeData | None = None, - **kwargs, - ) -> dict: + async def __response(*, res: CustomResponseCode | CustomResponse = None, data: Any | None = None) -> ResponseModel: """ 请求成功返回通用方法 - :param code: 返回状态码 - :param msg: 返回信息 + :param res: 返回信息 :param data: 返回数据 - :param exclude: 返回数据字段排除 - :param kwargs: jsonable_encoder 配置项 :return: """ - if data is not None: - # TODO: custom_encoder 配置失效: https://github.com/tiangolo/fastapi/discussions/10252 - custom_encoder = {datetime: lambda x: x.strftime(settings.DATETIME_FORMAT)} - kwargs.update({'custom_encoder': custom_encoder}) - data = jsonable_encoder(data, exclude=exclude, **kwargs) - return {'code': res.code, 'msg': res.msg, 'data': data} + return ResponseModel(code=res.code, msg=res.msg, data=data) async def success( - self, - *, - res: CustomResponseCode | CustomResponse = CustomResponseCode.HTTP_200, - data: Any | None = None, - exclude: _ExcludeData | None = None, - **kwargs, - ) -> dict: - return await self.__response(res=res, data=data, exclude=exclude, **kwargs) + self, + *, + res: CustomResponseCode | CustomResponse = CustomResponseCode.HTTP_200, + data: Any | None = None, + ) -> ResponseModel: + return await self.__response(res=res, data=data) async def fail( - self, - *, - res: CustomResponseCode | CustomResponse = CustomResponseCode.HTTP_400, - data: Any = None, - exclude: _ExcludeData | None = None, - **kwargs, - ) -> dict: - return await self.__response(res=res, data=data, exclude=exclude, **kwargs) + self, + *, + res: CustomResponseCode | CustomResponse = CustomResponseCode.HTTP_400, + data: Any = None, + ) -> ResponseModel: + return await self.__response(res=res, data=data) response_base = ResponseBase() diff --git a/backend/app/core/registrar.py b/backend/app/core/registrar.py index 792525bc..39365ef3 100644 --- a/backend/app/core/registrar.py +++ b/backend/app/core/registrar.py @@ -17,6 +17,7 @@ from backend.app.middleware.opera_log_middleware import OperaLogMiddleware from backend.app.utils.demo_site import demo_site from backend.app.utils.health_check import ensure_unique_route_names, http_limit_callback from backend.app.utils.openapi import simplify_operation_ids +from backend.app.utils.serializers import MsgSpecJSONResponse @asynccontextmanager @@ -50,6 +51,7 @@ def register_app(): docs_url=settings.DOCS_URL, redoc_url=settings.REDOCS_URL, openapi_url=settings.OPENAPI_URL, + default_response_class=MsgSpecJSONResponse, lifespan=register_init, ) diff --git a/backend/app/middleware/jwt_auth_middleware.py b/backend/app/middleware/jwt_auth_middleware.py index 577a28c2..84c26d86 100644 --- a/backend/app/middleware/jwt_auth_middleware.py +++ b/backend/app/middleware/jwt_auth_middleware.py @@ -5,13 +5,13 @@ from typing import Any from fastapi import Request, Response from starlette.authentication import AuthCredentials, AuthenticationBackend, AuthenticationError from starlette.requests import HTTPConnection -from starlette.responses import JSONResponse from backend.app.common import jwt from backend.app.common.exception.errors import TokenError from backend.app.common.log import log from backend.app.core.conf import settings from backend.app.database.db_mysql import async_db_session +from backend.app.utils.serializers import MsgSpecJSONResponse class _AuthenticationError(AuthenticationError): @@ -29,7 +29,7 @@ class JwtAuthMiddleware(AuthenticationBackend): @staticmethod def auth_exception_handler(conn: HTTPConnection, exc: _AuthenticationError) -> Response: """覆盖内部认证错误处理""" - return JSONResponse(content={'code': exc.code, 'msg': exc.msg, 'data': None}, status_code=exc.code) + return MsgSpecJSONResponse(content={'code': exc.code, 'msg': exc.msg, 'data': None}, status_code=exc.code) async def authenticate(self, request: Request): auth = request.headers.get('Authorization') diff --git a/backend/app/services/casbin_service.py b/backend/app/services/casbin_service.py index e4c63673..6ea232da 100644 --- a/backend/app/services/casbin_service.py +++ b/backend/app/services/casbin_service.py @@ -22,19 +22,19 @@ class CasbinService: return await CasbinDao.get_all_policy(ptype, sub) @staticmethod - async def get_policy_list(): + async def get_policy_list() -> list: enforcer = await RBAC.enforcer() data = enforcer.get_policy() return data @staticmethod - async def get_policy_list_by_role(*, role: str): + async def get_policy_list_by_role(*, role: str) -> list: enforcer = await RBAC.enforcer() data = enforcer.get_filtered_named_policy('p', 0, role) return data @staticmethod - async def create_policy(*, p: CreatePolicy): + async def create_policy(*, p: CreatePolicy) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.add_policy(p.sub, p.path, p.method) if not data: @@ -42,7 +42,7 @@ class CasbinService: return data @staticmethod - async def create_policies(*, ps: list[CreatePolicy]): + async def create_policies(*, ps: list[CreatePolicy]) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.add_policies([list(p.model_dump().values()) for p in ps]) if not data: @@ -50,7 +50,7 @@ class CasbinService: return data @staticmethod - async def update_policy(*, old: UpdatePolicy, new: UpdatePolicy): + async def update_policy(*, old: UpdatePolicy, new: UpdatePolicy) -> bool: enforcer = await RBAC.enforcer() _p = enforcer.has_policy(old.sub, old.path, old.method) if not _p: @@ -59,7 +59,7 @@ class CasbinService: return data @staticmethod - async def update_policies(*, old: list[UpdatePolicy], new: list[UpdatePolicy]): + async def update_policies(*, old: list[UpdatePolicy], new: list[UpdatePolicy]) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.update_policies( [list(o.model_dump().values()) for o in old], [list(n.model_dump().values()) for n in new] @@ -67,7 +67,7 @@ class CasbinService: return data @staticmethod - async def delete_policy(*, p: DeletePolicy): + async def delete_policy(*, p: DeletePolicy) -> bool: enforcer = await RBAC.enforcer() _p = enforcer.has_policy(p.sub, p.path, p.method) if not _p: @@ -76,7 +76,7 @@ class CasbinService: return data @staticmethod - async def delete_policies(*, ps: list[DeletePolicy]): + async def delete_policies(*, ps: list[DeletePolicy]) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.remove_policies([list(p.model_dump().values()) for p in ps]) if not data: @@ -90,13 +90,13 @@ class CasbinService: return count @staticmethod - async def get_group_list(): + async def get_group_list() -> list: enforcer = await RBAC.enforcer() data = enforcer.get_grouping_policy() return data @staticmethod - async def create_group(*, g: CreateUserRole): + async def create_group(*, g: CreateUserRole) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.add_grouping_policy(g.uuid, g.role) if not data: @@ -104,7 +104,7 @@ class CasbinService: return data @staticmethod - async def create_groups(*, gs: list[CreateUserRole]): + async def create_groups(*, gs: list[CreateUserRole]) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.add_grouping_policies([list(g.model_dump().values()) for g in gs]) if not data: @@ -112,7 +112,7 @@ class CasbinService: return data @staticmethod - async def delete_group(*, g: DeleteUserRole): + async def delete_group(*, g: DeleteUserRole) -> bool: enforcer = await RBAC.enforcer() _g = enforcer.has_grouping_policy(g.uuid, g.role) if not _g: @@ -121,7 +121,7 @@ class CasbinService: return data @staticmethod - async def delete_groups(*, gs: list[DeleteUserRole]): + async def delete_groups(*, gs: list[DeleteUserRole]) -> bool: enforcer = await RBAC.enforcer() data = await enforcer.remove_grouping_policies([list(g.model_dump().values()) for g in gs]) if not data: diff --git a/backend/app/utils/serializers.py b/backend/app/utils/serializers.py index 86aae026..1527cf5b 100644 --- a/backend/app/utils/serializers.py +++ b/backend/app/utils/serializers.py @@ -1,10 +1,14 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +from datetime import datetime from decimal import Decimal from typing import Any, Sequence, TypeVar +import msgspec + from asgiref.sync import sync_to_async from sqlalchemy import Row, RowMapping +from starlette.responses import JSONResponse RowData = Row | RowMapping | Any @@ -44,7 +48,8 @@ async def select_list_serialize(row: Sequence[R]) -> list: @sync_to_async def select_as_dict(row: R) -> dict: """ - Converting select to dict, which can contain relational data, depends on the properties of the select object itself + Converting SQLAlchemy select to dict, which can contain relational data, + depends on the properties of the select object itself :param row: :return: @@ -53,3 +58,12 @@ def select_as_dict(row: R) -> dict: if '_sa_instance_state' in obj_dict: del obj_dict['_sa_instance_state'] return obj_dict + + +class MsgSpecJSONResponse(JSONResponse): + """ + JSON response using the high-performance msgspec library to serialize data to JSON. + """ + + def render(self, content: Any) -> bytes: + return msgspec.json.encode(content) diff --git a/requirements.txt b/requirements.txt index 33b0e02e..f8187703 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ gunicorn==21.2.0 httpx==0.25.2 itsdangerous==2.1.2 loguru==0.7.2 +msgspec==0.18.5 passlib==1.7.4 path==15.1.2 phonenumbers==8.13.27