mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-22 05:02:57 +00:00
chore: 批量优化项目代码,修复多处细节问题
本次提交包含多项优化和修复: 1. 修复CRUD初始化参数传递、搜索参数处理逻辑 2. 更新环境配置中的大模型相关参数 3. 重构部分服务方法命名,统一代码风格 4. 新增多个枚举类型,补充模型关联关系和加载选项 5. 优化查询参数类实现,完善字段校验逻辑 6. 调整Pydantic模型字段注释和类型定义 7. 简化并移除冗余的CRUD方法实现 8. 新增超级管理员权限装饰器 9. 修复邮件日志模型的租户关联和字段定义
This commit is contained in:
@@ -32,7 +32,7 @@ _ROLE_NS = "role"
|
||||
response_model=ResponseSchema[PageResultSchema[RoleOutSchema]],
|
||||
)
|
||||
@cache(expire=300, namespace=_ROLE_NS)
|
||||
async def get_obj_list_controller(
|
||||
async def get_role_list_controller(
|
||||
page: Annotated[PaginationQueryParam, Depends()],
|
||||
search: Annotated[RoleQueryParam, Depends()],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:query"]))],
|
||||
@@ -51,7 +51,7 @@ async def get_obj_list_controller(
|
||||
order_by = [{"order": "asc"}]
|
||||
if page.order_by:
|
||||
order_by = page.order_by
|
||||
result_dict = await RoleService.get_role_page_service(
|
||||
result_dict = await RoleService.page_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no,
|
||||
page_size=page.page_size,
|
||||
@@ -66,7 +66,7 @@ async def get_obj_list_controller(
|
||||
summary="查询角色详情",
|
||||
response_model=ResponseSchema[RoleOutSchema],
|
||||
)
|
||||
async def get_obj_detail_controller(
|
||||
async def get_role_detail_controller(
|
||||
id: Annotated[int, Path(description="角色ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:detail"]))],
|
||||
) -> JSONResponse:
|
||||
@@ -80,7 +80,7 @@ async def get_obj_detail_controller(
|
||||
返回:
|
||||
- JSONResponse: 角色详情JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.get_role_detail_service(id=id, auth=auth)
|
||||
result_dict = await RoleService.detail_service(id=id, auth=auth)
|
||||
return SuccessResponse(data=result_dict, msg="获取角色详情成功")
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ async def get_obj_detail_controller(
|
||||
summary="创建角色",
|
||||
response_model=ResponseSchema[RoleOutSchema],
|
||||
)
|
||||
async def create_obj_controller(
|
||||
async def create_role_controller(
|
||||
data: RoleCreateSchema,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:create"]))],
|
||||
) -> JSONResponse:
|
||||
@@ -103,7 +103,7 @@ async def create_obj_controller(
|
||||
返回:
|
||||
- JSONResponse: 创建角色JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.create_role_service(data=data, auth=auth)
|
||||
result_dict = await RoleService.create_service(data=data, auth=auth)
|
||||
await FastAPICache.clear(namespace=_ROLE_NS)
|
||||
return SuccessResponse(data=result_dict, msg="创建角色成功")
|
||||
|
||||
@@ -113,7 +113,7 @@ async def create_obj_controller(
|
||||
summary="修改角色",
|
||||
response_model=ResponseSchema[RoleOutSchema],
|
||||
)
|
||||
async def update_obj_controller(
|
||||
async def update_role_controller(
|
||||
data: RoleUpdateSchema,
|
||||
id: Annotated[int, Path(description="角色ID")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:update"]))],
|
||||
@@ -129,7 +129,7 @@ async def update_obj_controller(
|
||||
返回:
|
||||
- JSONResponse: 修改角色JSON响应
|
||||
"""
|
||||
result_dict = await RoleService.update_role_service(id=id, data=data, auth=auth)
|
||||
result_dict = await RoleService.update_service(id=id, data=data, auth=auth)
|
||||
await FastAPICache.clear(namespace=_ROLE_NS)
|
||||
return SuccessResponse(data=result_dict, msg="修改角色成功")
|
||||
|
||||
@@ -139,7 +139,7 @@ async def update_obj_controller(
|
||||
summary="删除角色",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def delete_obj_controller(
|
||||
async def delete_role_controller(
|
||||
ids: Annotated[list[int], Body(description="ID列表")],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:delete"]))],
|
||||
) -> JSONResponse:
|
||||
@@ -153,7 +153,7 @@ async def delete_obj_controller(
|
||||
返回:
|
||||
- JSONResponse: 删除角色JSON响应
|
||||
"""
|
||||
await RoleService.delete_role_service(ids=ids, auth=auth)
|
||||
await RoleService.delete_service(ids=ids, auth=auth)
|
||||
await FastAPICache.clear(namespace=_ROLE_NS)
|
||||
return SuccessResponse(msg="删除角色成功")
|
||||
|
||||
@@ -163,7 +163,7 @@ async def delete_obj_controller(
|
||||
summary="批量修改角色状态",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def batch_set_available_obj_controller(
|
||||
async def batch_set_available_role_controller(
|
||||
data: BatchSetAvailable,
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:patch"]))],
|
||||
) -> JSONResponse:
|
||||
@@ -177,7 +177,7 @@ async def batch_set_available_obj_controller(
|
||||
返回:
|
||||
- JSONResponse: 批量修改角色状态JSON响应
|
||||
"""
|
||||
await RoleService.set_role_available_service(data=data, auth=auth)
|
||||
await RoleService.set_available_service(data=data, auth=auth)
|
||||
await FastAPICache.clear(namespace=_ROLE_NS)
|
||||
return SuccessResponse(msg="批量修改角色状态成功")
|
||||
|
||||
@@ -201,7 +201,7 @@ async def set_role_permission_controller(
|
||||
返回:
|
||||
- JSONResponse: 角色授权JSON响应
|
||||
"""
|
||||
await RoleService.set_role_permission_service(data=data, auth=auth)
|
||||
await RoleService.set_permission_service(data=data, auth=auth)
|
||||
await FastAPICache.clear(namespace=_ROLE_NS)
|
||||
return SuccessResponse(msg="授权角色成功")
|
||||
|
||||
@@ -211,7 +211,7 @@ async def set_role_permission_controller(
|
||||
summary="导出角色",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def export_obj_list_controller(
|
||||
async def export_role_list_controller(
|
||||
search: Annotated[RoleQueryParam, Depends()],
|
||||
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_system:role:export"]))],
|
||||
) -> StreamingResponse:
|
||||
@@ -225,8 +225,8 @@ async def export_obj_list_controller(
|
||||
返回:
|
||||
- StreamingResponse: 导出角色流响应
|
||||
"""
|
||||
role_query_result = await RoleService.get_role_list_service(search=search, auth=auth)
|
||||
role_export_result = await RoleService.export_role_list_service(role_list=role_query_result)
|
||||
role_query_result = await RoleService.list_service(search=search, auth=auth)
|
||||
role_export_result = await RoleService.export_list_service(role_list=role_query_result)
|
||||
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(role_export_result),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ForeignKey, Integer, String, UniqueConstraint
|
||||
from sqlalchemy import ForeignKey, Integer, String, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.common.enums import PermissionFilterStrategy
|
||||
from app.core.base_model import MappedBase, ModelMixin, TenantMixin
|
||||
from app.core.base_model import MappedBase, ModelMixin, TenantMixin, UserMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.api.v1.module_platform.menu.model import MenuModel
|
||||
@@ -61,7 +61,7 @@ class RoleDeptsModel(MappedBase):
|
||||
)
|
||||
|
||||
|
||||
class RoleModel(ModelMixin, TenantMixin):
|
||||
class RoleModel(ModelMixin, TenantMixin, UserMixin):
|
||||
"""
|
||||
角色模型
|
||||
|
||||
@@ -70,7 +70,14 @@ class RoleModel(ModelMixin, TenantMixin):
|
||||
|
||||
__tablename__: str = "sys_role"
|
||||
__table_args__ = (UniqueConstraint("tenant_id", "code"), {"comment": "角色表"})
|
||||
__loader_options__: list[str] = ["menus", "depts"]
|
||||
__loader_options__: list[str] = [
|
||||
"menus",
|
||||
"depts",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"deleted_by",
|
||||
"tenant_by",
|
||||
]
|
||||
__permission_strategy__: PermissionFilterStrategy = PermissionFilterStrategy.USER_ROLE
|
||||
|
||||
name: Mapped[str] = mapped_column(String(64), nullable=False, comment="角色名称")
|
||||
@@ -80,6 +87,11 @@ class RoleModel(ModelMixin, TenantMixin):
|
||||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||||
data_scope: Mapped[int] = mapped_column(Integer, default=1, nullable=False, comment="数据权限范围(1:仅本人 2:本部门 3:本部门及以下 4:全部 5:自定义)")
|
||||
|
||||
menus: Mapped[list["MenuModel"]] = relationship(secondary="sys_role_menus", back_populates="roles", lazy="selectin", order_by="MenuModel.order",)
|
||||
menus: Mapped[list["MenuModel"]] = relationship(
|
||||
secondary="sys_role_menus",
|
||||
back_populates="roles",
|
||||
lazy="selectin",
|
||||
order_by="MenuModel.order",
|
||||
)
|
||||
depts: Mapped[list["DeptModel"]] = relationship(secondary="sys_role_depts", back_populates="roles", lazy="selectin")
|
||||
users: Mapped[list["UserModel"]] = relationship(secondary="sys_user_roles", back_populates="roles", lazy="selectin")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from fastapi import Query
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
@@ -10,16 +12,18 @@ 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_schema import BaseSchema
|
||||
from app.core.base_params import BaseQueryParam, TenantByQueryParam, UserByQueryParam
|
||||
from app.core.base_schema import BaseSchema, TenantBySchema, UserBySchema
|
||||
from app.core.validator import (
|
||||
DateTimeStr,
|
||||
role_permission_request_validator,
|
||||
validate_required_code,
|
||||
)
|
||||
|
||||
|
||||
class RoleCreateSchema(BaseModel):
|
||||
"""角色创建模型"""
|
||||
"""
|
||||
角色创建模型
|
||||
"""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=64, description="角色名称")
|
||||
code: str = Field(..., min_length=2, max_length=64, description="角色编码")
|
||||
@@ -58,10 +62,14 @@ class RoleCreateSchema(BaseModel):
|
||||
|
||||
|
||||
class RolePermissionSettingSchema(BaseModel):
|
||||
"""角色权限配置模型"""
|
||||
"""
|
||||
角色权限配置模型
|
||||
"""
|
||||
|
||||
data_scope: int = Field(
|
||||
default=1,
|
||||
ge=1,
|
||||
le=5,
|
||||
description="数据权限范围(1:仅本人 2:本部门 3:本部门及以下 4:全部 5:自定义)",
|
||||
)
|
||||
role_ids: list[int] = Field(default_factory=list, description="角色ID列表")
|
||||
@@ -73,24 +81,22 @@ class RolePermissionSettingSchema(BaseModel):
|
||||
"""
|
||||
校验角色权限配置字段(数据范围与关联 ID 等)。
|
||||
|
||||
参数:
|
||||
- self: 当前模型实例(校验后状态)。
|
||||
|
||||
返回:
|
||||
- RolePermissionSettingSchema: 通过 `role_permission_request_validator` 校验后的同一实例。
|
||||
|
||||
异常:
|
||||
- CustomException: 不满足权限配置约束时抛出。
|
||||
"""
|
||||
return role_permission_request_validator(self)
|
||||
|
||||
|
||||
class RoleUpdateSchema(RoleCreateSchema):
|
||||
"""角色更新模型"""
|
||||
"""
|
||||
角色更新模型
|
||||
"""
|
||||
|
||||
|
||||
class RoleOutSchema(RoleCreateSchema, BaseSchema):
|
||||
"""角色信息响应模型"""
|
||||
class RoleOutSchema(RoleCreateSchema, BaseSchema, UserBySchema, TenantBySchema):
|
||||
"""
|
||||
角色信息响应模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -98,36 +104,23 @@ class RoleOutSchema(RoleCreateSchema, BaseSchema):
|
||||
depts: list[DeptOutSchema] = Field(default_factory=list, description="角色部门列表")
|
||||
|
||||
|
||||
class RoleQueryParam:
|
||||
"""角色管理查询参数"""
|
||||
@dataclass
|
||||
class RoleQueryParam(BaseQueryParam, UserByQueryParam, TenantByQueryParam):
|
||||
"""
|
||||
角色管理查询参数
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str | None = Query(None, description="角色名称"),
|
||||
description: str | None = Query(None, description="描述"),
|
||||
status: str | None = Query(None, description="是否启用"),
|
||||
created_time: list[DateTimeStr] | None = Query(
|
||||
None,
|
||||
description="创建时间范围",
|
||||
examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"],
|
||||
),
|
||||
updated_time: list[DateTimeStr] | None = Query(
|
||||
None,
|
||||
description="更新时间范围",
|
||||
examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"],
|
||||
),
|
||||
code: str | None = Query(None, description="角色编码"),
|
||||
status: int | None = Query(None, description="状态(0:启动 1:停用)"),
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
# 模糊查询字段
|
||||
super().__init__(*args, **kwargs)
|
||||
self.name = (QueueEnum.like.value, name)
|
||||
if description:
|
||||
self.description = (QueueEnum.like.value, description)
|
||||
|
||||
# 精确查询字段
|
||||
if status:
|
||||
if code:
|
||||
self.code = (QueueEnum.like.value, code)
|
||||
if status is not None:
|
||||
self.status = (QueueEnum.eq.value, status)
|
||||
|
||||
# 时间范围查询
|
||||
if created_time and len(created_time) == 2:
|
||||
self.created_time = (QueueEnum.between.value, (created_time[0], created_time[1]))
|
||||
if updated_time and len(updated_time) == 2:
|
||||
self.updated_time = (QueueEnum.between.value, (updated_time[0], updated_time[1]))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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
|
||||
@@ -15,10 +16,14 @@ from .schema import (
|
||||
|
||||
|
||||
class RoleService:
|
||||
"""角色模块服务层"""
|
||||
"""
|
||||
角色管理服务
|
||||
|
||||
提供角色 CRUD、权限配置、数据权限范围设置、批量启/禁用、Excel 导出等业务能力。
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_role_detail_service(cls, auth: AuthSchema, id: int) -> RoleOutSchema:
|
||||
async def detail_service(cls, auth: AuthSchema, id: int) -> RoleOutSchema:
|
||||
"""
|
||||
获取角色详情
|
||||
|
||||
@@ -27,15 +32,12 @@ class RoleService:
|
||||
- id (int): 角色ID
|
||||
|
||||
返回:
|
||||
- dict: 角色详情字典
|
||||
- RoleOutSchema: 角色详情响应模型
|
||||
"""
|
||||
role = await RoleCRUD(auth).get(id=id)
|
||||
if not role:
|
||||
raise CustomException(msg="角色不存在")
|
||||
return RoleOutSchema.model_validate(role)
|
||||
return await RoleCRUD(auth).get_or_404(id=id, out_schema=RoleOutSchema)
|
||||
|
||||
@classmethod
|
||||
async def get_role_list_service(
|
||||
async def list_service(
|
||||
cls,
|
||||
auth: AuthSchema,
|
||||
search: RoleQueryParam | None = None,
|
||||
@@ -50,13 +52,13 @@ class RoleService:
|
||||
- order_by (list[dict[str, str]] | None): 排序参数列表
|
||||
|
||||
返回:
|
||||
- list[RoleOutSchema]: 角色详情字典列表
|
||||
- list[RoleOutSchema]: 角色响应模型列表
|
||||
"""
|
||||
role_list = await RoleCRUD(auth).list(search=search.__dict__ if search else {}, order_by=order_by)
|
||||
role_list = await RoleCRUD(auth).list(search=vars(search) if search else None, order_by=order_by)
|
||||
return [RoleOutSchema.model_validate(role) for role in role_list]
|
||||
|
||||
@classmethod
|
||||
async def get_role_page_service(
|
||||
async def page_service(
|
||||
cls,
|
||||
auth: AuthSchema,
|
||||
page_no: int,
|
||||
@@ -82,12 +84,12 @@ class RoleService:
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by or [{"id": "asc"}],
|
||||
search=search.__dict__ if search else {},
|
||||
search=vars(search) if search else None,
|
||||
out_schema=RoleOutSchema,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def create_role_service(cls, auth: AuthSchema, data: RoleCreateSchema) -> RoleOutSchema:
|
||||
async def create_service(cls, auth: AuthSchema, data: RoleCreateSchema) -> RoleOutSchema:
|
||||
"""
|
||||
创建角色
|
||||
|
||||
@@ -96,25 +98,23 @@ class RoleService:
|
||||
- data (RoleCreateSchema): 创建角色模型
|
||||
|
||||
返回:
|
||||
- dict: 新创建的角色详情字典
|
||||
- RoleOutSchema: 新创建的角色响应模型
|
||||
"""
|
||||
role = await RoleCRUD(auth).get(name=data.name)
|
||||
if role:
|
||||
raise CustomException(msg="创建失败,该角色已存在")
|
||||
raise CustomException(msg="创建失败,该数据已存在")
|
||||
obj = await RoleCRUD(auth).get(code=data.code)
|
||||
if obj:
|
||||
raise CustomException(msg="创建失败,编码已存在")
|
||||
|
||||
# 检查租户配额
|
||||
from app.api.v1.module_platform.tenant.service import TenantService
|
||||
|
||||
await TenantService.check_quota_service(auth, auth.tenant_id, "role")
|
||||
|
||||
new_role = await RoleCRUD(auth).create(data=data)
|
||||
return RoleOutSchema.model_validate(new_role)
|
||||
|
||||
@classmethod
|
||||
async def update_role_service(cls, auth: AuthSchema, id: int, data: RoleUpdateSchema) -> RoleOutSchema:
|
||||
async def update_service(cls, auth: AuthSchema, id: int, data: RoleUpdateSchema) -> RoleOutSchema:
|
||||
"""
|
||||
更新角色
|
||||
|
||||
@@ -124,14 +124,12 @@ class RoleService:
|
||||
- data (RoleUpdateSchema): 更新角色模型
|
||||
|
||||
返回:
|
||||
- dict: 更新后的角色详情字典
|
||||
- RoleOutSchema: 更新后的角色响应模型
|
||||
"""
|
||||
role = await RoleCRUD(auth).get(id=id)
|
||||
if not role:
|
||||
raise CustomException(msg="更新失败,该角色不存在")
|
||||
_ = await RoleCRUD(auth).get_or_404(id=id, msg="更新失败,该数据不存在")
|
||||
exist_role = await RoleCRUD(auth).get(name=data.name)
|
||||
if exist_role and exist_role.id != id:
|
||||
raise CustomException(msg="更新失败,角色名称重复")
|
||||
raise CustomException(msg="更新失败,名称已存在")
|
||||
exist_code = await RoleCRUD(auth).get(code=data.code)
|
||||
if exist_code and exist_code.id != id:
|
||||
raise CustomException(msg="更新失败,角色编码已存在")
|
||||
@@ -139,7 +137,7 @@ class RoleService:
|
||||
return RoleOutSchema.model_validate(updated_role)
|
||||
|
||||
@classmethod
|
||||
async def delete_role_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
||||
async def delete_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
||||
"""
|
||||
删除角色
|
||||
|
||||
@@ -156,14 +154,12 @@ class RoleService:
|
||||
# 批量校验角色存在性
|
||||
roles = await RoleCRUD(auth).list(search={"id": ("in", ids)})
|
||||
if len(roles) != len(ids):
|
||||
found = {r.id for r in roles}
|
||||
missing = [rid for rid in ids if rid not in found]
|
||||
raise CustomException(msg=f"角色 ID {missing} 不存在")
|
||||
raise CustomException(msg="删除失败,部分ID不存在")
|
||||
|
||||
await RoleCRUD(auth).delete(ids=ids)
|
||||
|
||||
@classmethod
|
||||
async def set_role_permission_service(cls, auth: AuthSchema, data: RolePermissionSettingSchema) -> None:
|
||||
async def set_permission_service(cls, auth: AuthSchema, data: RolePermissionSettingSchema) -> None:
|
||||
"""
|
||||
设置角色权限
|
||||
|
||||
@@ -187,7 +183,7 @@ class RoleService:
|
||||
await RoleCRUD(auth).set_role_depts_crud(role_ids=data.role_ids, dept_ids=[])
|
||||
|
||||
@classmethod
|
||||
async def set_role_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None:
|
||||
async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None:
|
||||
"""
|
||||
设置角色可用状态
|
||||
|
||||
@@ -202,11 +198,11 @@ class RoleService:
|
||||
role_map = {r.id: r for r in roles}
|
||||
for rid in data.ids:
|
||||
if rid not in role_map:
|
||||
raise CustomException(msg=f"角色ID {rid} 不存在")
|
||||
raise CustomException(msg="该数据不存在")
|
||||
await RoleCRUD(auth).set(ids=data.ids, status=data.status)
|
||||
|
||||
@classmethod
|
||||
async def export_role_list_service(cls, role_list: list[dict[str, Any]]) -> bytes:
|
||||
async def export_list_service(cls, role_list: list[dict[str, Any]]) -> bytes:
|
||||
"""
|
||||
导出角色列表
|
||||
|
||||
|
||||
Reference in New Issue
Block a user