chore: 批量优化项目代码,修复多处细节问题

本次提交包含多项优化和修复:
1. 修复CRUD初始化参数传递、搜索参数处理逻辑
2. 更新环境配置中的大模型相关参数
3. 重构部分服务方法命名,统一代码风格
4. 新增多个枚举类型,补充模型关联关系和加载选项
5. 优化查询参数类实现,完善字段校验逻辑
6. 调整Pydantic模型字段注释和类型定义
7. 简化并移除冗余的CRUD方法实现
8. 新增超级管理员权限装饰器
9. 修复邮件日志模型的租户关联和字段定义
This commit is contained in:
zhangtao
2026-06-20 23:37:58 +08:00
parent bbe77930a8
commit 4e2b668d7b
96 changed files with 2914 additions and 1936 deletions
@@ -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),
+17 -5
View File
@@ -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")
+32 -39
View File
@@ -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:
"""
导出角色列表