Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions backend/app/api/v1/dept.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@
from backend.app.common.rbac import DependsRBAC
from backend.app.common.jwt import DependsJwtAuth
from backend.app.common.response.response_schema import response_base
from backend.app.schemas.dept import CreateDept, GetAllDept, UpdateDept
from backend.app.schemas.dept import GetAllDept, CreateDept, UpdateDept
from backend.app.services.dept_service import DeptService
from backend.app.utils.serializers import select_to_json
from backend.app.utils.serializers import select_as_dict

router = APIRouter()


@router.get('/{pk}', summary='获取部门详情', dependencies=[DependsJwtAuth])
async def get_dept(pk: int):
dept = await DeptService.get(pk=pk)
data = GetAllDept(**select_to_json(dept))
data = GetAllDept(**await select_as_dict(dept))
return await response_base.success(data=data)


@router.get('', summary='获取所有部门展示树', dependencies=[DependsJwtAuth])
async def get_all_depts(
level: Annotated[int | None, Query()] = None,
name: Annotated[str | None, Query()] = None,
leader: Annotated[str | None, Query()] = None,
phone: Annotated[str | None, Query()] = None,
Expand Down
4 changes: 2 additions & 2 deletions backend/app/api/v1/dict_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
from backend.app.database.db_mysql import CurrentSession
from backend.app.schemas.dict_data import GetAllDictData, CreateDictData, UpdateDictData
from backend.app.services.dict_data_service import DictDataService
from backend.app.utils.serializers import select_to_json
from backend.app.utils.serializers import select_as_dict

router = APIRouter()


@router.get('/{pk}', summary='获取字典详情', dependencies=[DependsJwtAuth])
async def get_dict_data(pk: int):
dict_data = await DictDataService.get(pk=pk)
data = GetAllDictData(**select_to_json(dict_data))
data = GetAllDictData(**await select_as_dict(dict_data))
return await response_base.success(data=data)


Expand Down
4 changes: 2 additions & 2 deletions backend/app/api/v1/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from backend.app.common.response.response_schema import response_base
from backend.app.schemas.menu import GetAllMenu, CreateMenu, UpdateMenu
from backend.app.services.menu_service import MenuService
from backend.app.utils.serializers import select_to_json
from backend.app.utils.serializers import select_as_dict

router = APIRouter()

Expand All @@ -23,7 +23,7 @@ async def get_user_menus(request: Request):
@router.get('/{pk}', summary='获取菜单详情', dependencies=[DependsJwtAuth])
async def get_menu(pk: int):
menu = await MenuService.get(pk=pk)
data = GetAllMenu(**select_to_json(menu))
data = GetAllMenu(**await select_as_dict(menu))
return await response_base.success(data=data)


Expand Down
8 changes: 4 additions & 4 deletions backend/app/api/v1/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
from backend.app.schemas.role import GetAllRole, CreateRole, UpdateRole, UpdateRoleMenu
from backend.app.services.menu_service import MenuService
from backend.app.services.role_service import RoleService
from backend.app.utils.serializers import select_to_json, select_to_list
from backend.app.utils.serializers import select_list_serialize, select_as_dict

router = APIRouter()


@router.get('/all', summary='获取所有角色', dependencies=[DependsJwtAuth])
async def get_all_roles():
roles = await RoleService.get_all()
data = select_to_list(roles)
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):
roles = await RoleService.get_user_all(pk=pk)
data = select_to_list(roles)
data = await select_list_serialize(roles)
return await response_base.success(data=data)


Expand All @@ -40,7 +40,7 @@ async def get_role_all_menus(pk: int):
@router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth])
async def get_role(pk: int):
role = await RoleService.get(pk=pk)
data = GetAllRole(**select_to_json(role))
data = GetAllRole(**await select_as_dict(role))
return await response_base.success(data=data)


Expand Down
10 changes: 5 additions & 5 deletions backend/app/api/v1/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
ResetPassword,
UpdateUser,
Avatar,
GetCurrentUserInfo,
UpdateUserRole,
AddUser,
GetCurrentUserInfo,
)
from backend.app.services.user_service import UserService
from backend.app.utils.serializers import select_to_json
from backend.app.utils.serializers import select_as_dict

router = APIRouter()

Expand All @@ -35,7 +35,7 @@ async def user_register(obj: RegisterUser):
async def add_user(obj: AddUser):
await UserService.add(obj=obj)
current_user = await UserService.get_userinfo(username=obj.username)
data = GetAllUserInfo(**select_to_json(current_user))
data = GetAllUserInfo(**await select_as_dict(current_user))
return await response_base.success(data=data)


Expand All @@ -49,14 +49,14 @@ async def password_reset(request: Request, obj: ResetPassword):

@router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth])
async def get_current_userinfo(request: Request):
data = GetCurrentUserInfo(**select_to_json(request.user))
data = GetCurrentUserInfo(**await select_as_dict(request.user))
return await response_base.success(data=data, exclude={'password'})


@router.get('/{username}', summary='查看用户信息', dependencies=[DependsJwtAuth])
async def get_user(username: str):
current_user = await UserService.get_userinfo(username=username)
data = GetAllUserInfo(**select_to_json(current_user))
data = GetAllUserInfo(**await select_as_dict(current_user))
return await response_base.success(data=data)


Expand Down
52 changes: 20 additions & 32 deletions backend/app/common/response/response_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
from datetime import datetime
from typing import Any

from asgiref.sync import sync_to_async
from pydantic import validate_arguments, BaseModel
from pydantic import BaseModel

from backend.app.core.conf import settings
from backend.app.utils.encoders import jsonable_encoder
Expand All @@ -20,7 +19,8 @@ class ResponseModel(BaseModel):

.. tip::

如果你不想使用 ResponseBase 中的自定义编码器,可以使用此模型,返回数据将通过 fastapi 内部的编码器直接自动解析并返回
如果你不想使用 ResponseBase 中的自定义编码器,可以使用此模型,返回数据将通过 fastapi 内部的编码器自动解析并返回;
此返回模型会生成 openapi schema 文档

E.g. ::

Expand All @@ -47,7 +47,8 @@ class ResponseBase:

.. tip::

此类中的返回方法将通过自定义编码器预解析,然后由 fastapi 内部的编码器再次处理并返回,可能存在性能损耗,取决于个人喜好
此类中的返回方法将通过自定义编码器预解析,然后由 fastapi 内部的编码器再次处理并返回,可能存在性能损耗,取决于个人喜好;
此返回模型不会生成 openapi schema 文档

E.g. ::

Expand All @@ -57,47 +58,34 @@ def test():
""" # noqa: E501

@staticmethod
@sync_to_async
def __json_encoder(data: Any, exclude: _ExcludeData | None = None, **kwargs):
custom_encoder = {datetime: lambda x: x.strftime(settings.DATETIME_FORMAT)}
kwargs.update({'custom_encoder': custom_encoder})
result = jsonable_encoder(data, exclude=exclude, **kwargs)
return result

@validate_arguments
async def success(
self,
*,
code: int = 200,
msg: str = 'Success',
data: Any | None = None,
exclude: _ExcludeData | None = None,
**kwargs
async def __response(
*, code: int = None, msg: str = None, data: Any | None = None, exclude: _ExcludeData | None = None, **kwargs
) -> dict:
"""
请求成功返回通用方法

:param code: 返回状态码
:param msg: 返回信息
:param data: 返回数据
:param exclude: 排除返回数据(data)字段
:param exclude: 返回数据字段排除
:param kwargs: jsonable_encoder 配置项
:return:
"""
data = data if data is None else await self.__json_encoder(data, exclude, **kwargs)
if data is not None:
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': code, 'msg': msg, 'data': data}

@validate_arguments
async def success(
self, *, code=200, msg='Success', data: Any | None = None, exclude: _ExcludeData | None = None, **kwargs
) -> dict:
return await self.__response(code=code, msg=msg, data=data, exclude=exclude, **kwargs)

async def fail(
self,
*,
code: int = 400,
msg: str = 'Bad Request',
data: Any = None,
exclude: _ExcludeData | None = None,
**kwargs
self, *, code=400, msg='Bad Request', data: Any = None, exclude: _ExcludeData | None = None, **kwargs
) -> dict:
data = data if data is None else await self.__json_encoder(data, exclude, **kwargs)
return {'code': code, 'msg': msg, 'data': data}
return await self.__response(code=code, msg=msg, data=data, exclude=exclude, **kwargs)


response_base = ResponseBase()
5 changes: 1 addition & 4 deletions backend/app/schemas/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,7 @@ class Config:
orm_mode = True


class GetCurrentUserInfo(GetUserInfoNoRelation):
dept: GetAllDept | None = None
roles: list[GetAllRole]

class GetCurrentUserInfo(GetAllUserInfo):
@root_validator
def handel(cls, values):
"""处理部门和角色"""
Expand Down
1 change: 1 addition & 0 deletions backend/app/services/task_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def get_task(pk: str):
'next_run_time': job.next_run_time,
}
)

return task

async def run(self, pk: str):
Expand Down
7 changes: 3 additions & 4 deletions backend/app/utils/build_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
from asgiref.sync import sync_to_async

from backend.app.common.enums import BuildTreeType
from backend.app.utils.serializers import RowData, select_to_list
from backend.app.utils.serializers import RowData, select_list_serialize


@sync_to_async
def get_tree_nodes(row: Sequence[RowData]) -> list[dict[str, Any]]:
async def get_tree_nodes(row: Sequence[RowData]) -> list[dict[str, Any]]:
"""获取所有树形结构节点"""
tree_nodes = select_to_list(row)
tree_nodes = await select_list_serialize(row)
tree_nodes.sort(key=lambda x: x['sort'])
return tree_nodes

Expand Down
17 changes: 10 additions & 7 deletions backend/app/utils/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
from decimal import Decimal
from typing import Any, TypeVar, Sequence

from asgiref.sync import sync_to_async
from sqlalchemy import Row, RowMapping

RowData = Row | RowMapping | Any

R = TypeVar('R', bound=RowData)


def select_to_dict(row: R) -> dict:
@sync_to_async
def select_columns_serialize(row: R) -> dict:
"""
Serialize SQLAlchemy Select to dict
Serialize SQLAlchemy select table columns, does not contain relational columns

:param row:
:return:
Expand All @@ -28,20 +30,21 @@ def select_to_dict(row: R) -> dict:
return obj_dict


def select_to_list(row: Sequence[R]) -> list:
async def select_list_serialize(row: Sequence[R]) -> list:
"""
Serialize SQLAlchemy Select to list
Serialize SQLAlchemy select list

:param row:
:return:
"""
ret_list = [select_to_dict(_) for _ in row]
ret_list = [await select_columns_serialize(_) for _ in row]
return ret_list


def select_to_json(row: R) -> dict:
@sync_to_async
def select_as_dict(row: R) -> dict:
"""
Serialize SQLAlchemy Select to json
Converting select to dict, which can contain relational data, depends on the properties of the select object itself

:param row:
:return:
Expand Down