mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
feat: 新增刷新缓存功能并优化系统初始化
refactor: 重构模型加载选项和CRUD预加载逻辑 fix: 修复日期范围查询格式问题 style: 优化前端组件样式和布局 perf: 提升字典数据更新性能 docs: 更新注释和文档说明 chore: 清理无用文件和代码 test: 更新测试用例 build: 添加vue-json-pretty依赖 ci: 优化初始化脚本和事务处理
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
@@ -21,42 +21,45 @@ class McpCRUD(CRUDBase[McpModel, McpCreateSchema, McpUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=McpModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[McpModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[McpModel]:
|
||||
"""
|
||||
获取MCP服务器详情
|
||||
|
||||
参数:
|
||||
- id (int): MCP服务器ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[McpModel]: MCP服务器模型实例(如果存在)
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_by_name_crud(self, name: str) -> Optional[McpModel]:
|
||||
async def get_by_name_crud(self, name: str, preload: Optional[List[Union[str, Any]]] = None) -> Optional[McpModel]:
|
||||
"""
|
||||
通过名称获取MCP服务器
|
||||
|
||||
参数:
|
||||
- name (str): MCP服务器名称
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[McpModel]: MCP服务器模型实例(如果存在)
|
||||
"""
|
||||
return await self.get(name=name)
|
||||
return await self.get(name=name, preload=preload)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[McpModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[McpModel]:
|
||||
"""
|
||||
列表查询MCP服务器
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数字典
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数列表
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[McpModel]: MCP服务器模型实例序列
|
||||
"""
|
||||
return await self.list(search=search or {}, order_by=order_by or [{'id': 'asc'}])
|
||||
return await self.list(search=search or {}, order_by=order_by or [{'id': 'asc'}], preload=preload)
|
||||
|
||||
async def create_crud(self, data: McpCreateSchema) -> Optional[McpModel]:
|
||||
"""
|
||||
@@ -93,4 +96,4 @@ class McpCRUD(CRUDBase[McpModel, McpCreateSchema, McpUpdateSchema]):
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
return await self.delete(ids=ids)
|
||||
@@ -14,6 +14,7 @@ class McpModel(CreatorMixin):
|
||||
|
||||
__tablename__ = 'app_ai_mcp'
|
||||
__table_args__ = ({'comment': 'MCP 服务器表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), unique=True, comment='MCP 名称')
|
||||
type: Mapped[int] = mapped_column(Integer, default=0, comment='MCP 类型(0:stdio 1:sse)')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
@@ -21,30 +21,32 @@ class JobCRUD(CRUDBase[JobModel, JobCreateSchema, JobUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=JobModel, auth=auth)
|
||||
|
||||
async def get_obj_by_id_crud(self, id: int) -> Optional[JobModel]:
|
||||
async def get_obj_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[JobModel]:
|
||||
"""
|
||||
获取定时任务详情
|
||||
|
||||
参数:
|
||||
- id (int): 定时任务ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[JobModel]: 定时任务模型,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[JobModel]:
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[JobModel]:
|
||||
"""
|
||||
获取定时任务列表
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数字典
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数列表
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[JobModel]: 定时任务模型序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_obj_crud(self, data: JobCreateSchema) -> Optional[JobModel]:
|
||||
"""
|
||||
@@ -113,30 +115,32 @@ class JobLogCRUD(CRUDBase[JobLogModel, JobLogCreateSchema, JobLogUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=JobLogModel, auth=auth)
|
||||
|
||||
async def get_obj_log_by_id_crud(self, id: int) -> Optional[JobLogModel]:
|
||||
async def get_obj_log_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[JobLogModel]:
|
||||
"""
|
||||
获取定时任务日志详情
|
||||
|
||||
参数:
|
||||
- id (int): 定时任务日志ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[JobLogModel]: 定时任务日志模型,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_obj_log_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[JobLogModel]:
|
||||
async def get_obj_log_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[JobLogModel]:
|
||||
"""
|
||||
获取定时任务日志列表
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数字典
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数列表
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[JobLogModel]: 定时任务日志模型序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def delete_obj_log_crud(self, ids: List[int]) -> None:
|
||||
"""
|
||||
|
||||
@@ -14,6 +14,7 @@ class JobModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = 'app_job'
|
||||
__table_args__ = ({'comment': '定时任务调度表'})
|
||||
__loader_options__ = ["job_logs", "creator"]
|
||||
|
||||
name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, default='', comment='任务名称')
|
||||
jobstore: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, default='default', comment='存储器')
|
||||
@@ -37,6 +38,7 @@ class JobLogModel(MappedBase):
|
||||
"""
|
||||
__tablename__ = 'app_job_log'
|
||||
__table_args__ = ({'comment': '定时任务调度日志表'})
|
||||
__loader_options__ = ["job"]
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
||||
job_name: Mapped[str] = mapped_column(String(64),nullable=False,comment='任务名称')
|
||||
@@ -52,4 +54,4 @@ class JobLogModel(MappedBase):
|
||||
status: Mapped[bool] = mapped_column(Boolean(), default=True, nullable=False, comment="是否启用(True:启用 False:禁用)")
|
||||
create_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, comment='创建时间')
|
||||
# 任务关联关系
|
||||
job: Mapped[Optional["JobModel"]] = relationship(back_populates="job_logs", lazy="selectin")
|
||||
job: Mapped[Optional["JobModel"]] = relationship(back_populates="job_logs", lazy="selectin")
|
||||
@@ -60,10 +60,10 @@ class JobLogCreateSchema(BaseModel):
|
||||
class JobLogUpdateSchema(JobLogCreateSchema):
|
||||
"""定时任务调度日志表更新模型"""
|
||||
...
|
||||
job_log_id: Optional[int] = Field(default=None, description='任务日志ID')
|
||||
id: Optional[int] = Field(default=None, description='任务日志ID')
|
||||
|
||||
|
||||
class JobLogOutSchema(JobLogCreateSchema):
|
||||
class JobLogOutSchema(JobLogUpdateSchema):
|
||||
"""定时任务调度日志表响应模型"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
...
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
@@ -21,30 +21,32 @@ class ApplicationCRUD(CRUDBase[ApplicationModel, ApplicationCreateSchema, Applic
|
||||
self.auth = auth
|
||||
super().__init__(model=ApplicationModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[ApplicationModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[ApplicationModel]:
|
||||
"""
|
||||
根据id获取应用详情
|
||||
|
||||
参数:
|
||||
- id (int): 应用ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[ApplicationModel]: 应用详情,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[ApplicationModel]:
|
||||
async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[ApplicationModel]:
|
||||
"""
|
||||
列表查询应用
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数,默认None
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数,默认None
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[ApplicationModel]: 应用列表
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_crud(self, data: ApplicationCreateSchema) -> Optional[ApplicationModel]:
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ class ApplicationModel(CreatorMixin):
|
||||
|
||||
__tablename__ = 'app_myapp'
|
||||
__table_args__ = ({'comment': '应用系统表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
# 基本信息(必备字段)
|
||||
name: Mapped[str] = mapped_column(String(64), nullable=False, comment='应用名称', unique=True)
|
||||
@@ -24,5 +25,4 @@ class ApplicationModel(CreatorMixin):
|
||||
access_url: Mapped[str] = mapped_column(String(500), nullable=False, comment='访问地址')
|
||||
|
||||
# 外观展示
|
||||
icon_url: Mapped[str] = mapped_column(String(300), nullable=True, comment='应用图标URL')
|
||||
|
||||
icon_url: Mapped[str] = mapped_column(String(300), nullable=True, comment='应用图标URL')
|
||||
@@ -5,7 +5,6 @@ from fastapi.responses import JSONResponse, StreamingResponse
|
||||
import urllib.parse
|
||||
|
||||
from app.common.response import StreamResponse, SuccessResponse
|
||||
from app.common.request import PaginationService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission
|
||||
@@ -59,8 +58,14 @@ async def get_obj_list_controller(
|
||||
返回:
|
||||
- JSONResponse: 包含示例列表分页信息的JSON响应
|
||||
"""
|
||||
result_dict_list = await DemoService.list_service(auth=auth, search=search, order_by=page.order_by)
|
||||
result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size)
|
||||
# 使用数据库分页而不是应用层分页
|
||||
result_dict = await DemoService.page_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no if page.page_no is not None else 1,
|
||||
page_size=page.page_size if page.page_size is not None else 10,
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
logger.info("查询示例列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询示例列表成功")
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .model import DemoModel
|
||||
from .schema import DemoCreateSchema, DemoUpdateSchema
|
||||
from .schema import DemoCreateSchema, DemoUpdateSchema, DemoOutSchema
|
||||
|
||||
|
||||
class DemoCRUD(CRUDBase[DemoModel, DemoCreateSchema, DemoUpdateSchema]):
|
||||
@@ -20,30 +20,32 @@ class DemoCRUD(CRUDBase[DemoModel, DemoCreateSchema, DemoUpdateSchema]):
|
||||
"""
|
||||
super().__init__(model=DemoModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[DemoModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[DemoModel]:
|
||||
"""
|
||||
详情
|
||||
|
||||
参数:
|
||||
- id (int): 示例ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[DemoModel]: 示例模型实例或None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DemoModel]:
|
||||
async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[DemoModel]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[DemoModel]: 示例模型实例序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_crud(self, data: DemoCreateSchema) -> Optional[DemoModel]:
|
||||
"""
|
||||
@@ -93,4 +95,30 @@ class DemoCRUD(CRUDBase[DemoModel, DemoCreateSchema, DemoUpdateSchema]):
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.set(ids=ids, status=status)
|
||||
return await self.set(ids=ids, status=status)
|
||||
|
||||
async def page_crud(self, offset: int, limit: int, order_by: Optional[List[Dict[str, str]]] = None, search: Optional[Dict] = None, preload: Optional[List[Union[str, Any]]] = None) -> Dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- offset (int): 偏移量
|
||||
- limit (int): 每页数量
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Dict: 分页数据
|
||||
"""
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
search_dict = search or {}
|
||||
|
||||
return await self.page(
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
order_by=order_by_list,
|
||||
search=search_dict,
|
||||
out_schema=DemoOutSchema,
|
||||
preload=preload
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ class DemoModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = 'gen_demo'
|
||||
__table_args__ = ({'comment': '示例表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, default='', comment='名称')
|
||||
status: Mapped[bool] = mapped_column(Boolean(), default=True, nullable=False, comment="是否启用(True:启用 False:禁用)")
|
||||
status: Mapped[bool] = mapped_column(Boolean(), default=True, nullable=False, comment="是否启用(True:启用 False:禁用)")
|
||||
@@ -54,6 +54,33 @@ class DemoService:
|
||||
obj_list = await DemoCRUD(auth).list_crud(search=search_dict, order_by=order_by)
|
||||
return [DemoOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def page_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: Optional[DemoQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- auth (AuthSchema): 认证信息模型
|
||||
- page_no (int): 页码
|
||||
- page_size (int): 每页数量
|
||||
- search (Optional[DemoQueryParam]): 查询参数
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
|
||||
返回:
|
||||
- Dict: 分页数据
|
||||
"""
|
||||
search_dict = search.__dict__ if search else {}
|
||||
order_by_list = order_by or [{'id': 'asc'}]
|
||||
offset = (page_no - 1) * page_size
|
||||
|
||||
result = await DemoCRUD(auth).page_crud(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
order_by=order_by_list,
|
||||
search=search_dict
|
||||
)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def create_service(cls, auth: AuthSchema, data: DemoCreateSchema) -> Dict:
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy.engine.row import Row
|
||||
from sqlalchemy import and_, delete, select, text, update
|
||||
from sqlalchemy.orm import selectinload
|
||||
from sqlglot.expressions import Expression
|
||||
from typing import List, Optional, Sequence, Dict
|
||||
from typing import List, Optional, Sequence, Dict, Union, Any
|
||||
|
||||
from app.core.logger import logger
|
||||
from app.config.setting import settings
|
||||
@@ -34,12 +34,13 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""
|
||||
super().__init__(model=GenTableModel, auth=auth)
|
||||
|
||||
async def get_gen_table_by_id(self, table_id: int) -> Optional[GenTableModel]:
|
||||
async def get_gen_table_by_id(self, table_id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[GenTableModel]:
|
||||
"""
|
||||
根据业务表ID获取需要生成的业务表信息。
|
||||
|
||||
参数:
|
||||
- table_id (int): 业务表ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- GenTableModel | None: 业务表信息对象。
|
||||
@@ -58,12 +59,13 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
|
||||
return gen_table
|
||||
|
||||
async def get_gen_table_by_name(self, table_name: str) -> Optional[GenTableModel]:
|
||||
async def get_gen_table_by_name(self, table_name: str, preload: Optional[List[Union[str, Any]]] = None) -> Optional[GenTableModel]:
|
||||
"""
|
||||
根据业务表名称获取需要生成的业务表信息。
|
||||
|
||||
参数:
|
||||
- table_name (str): 业务表名称。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- GenTableModel | None: 业务表信息对象。
|
||||
@@ -82,10 +84,13 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
|
||||
return gen_table
|
||||
|
||||
async def get_gen_table_all(self) -> Sequence[GenTableModel]:
|
||||
async def get_gen_table_all(self, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenTableModel]:
|
||||
"""
|
||||
获取所有业务表信息。
|
||||
|
||||
参数:
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[GenTableModel]: 所有业务表信息列表。
|
||||
"""
|
||||
@@ -98,12 +103,13 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
|
||||
return gen_table_all
|
||||
|
||||
async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None) -> Sequence[GenTableModel]:
|
||||
async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenTableModel]:
|
||||
"""
|
||||
根据查询参数获取代码生成业务表列表信息。
|
||||
|
||||
参数:
|
||||
- search (GenTableQueryParam | None): 查询参数对象。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[GenTableModel]: 业务表列表信息。
|
||||
@@ -400,39 +406,42 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
"""
|
||||
super().__init__(model=GenTableColumnModel, auth=auth)
|
||||
|
||||
async def get_gen_table_column_by_id(self, id: int) -> Optional[GenTableColumnModel]:
|
||||
async def get_gen_table_column_by_id(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[GenTableColumnModel]:
|
||||
"""根据业务表字段ID获取业务表字段信息。
|
||||
|
||||
参数:
|
||||
- id (int): 业务表字段ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[GenTableColumnModel]: 业务表字段信息对象。
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_gen_table_column_list_by_table_id(self, table_id: int) -> Optional[GenTableColumnModel]:
|
||||
async def get_gen_table_column_list_by_table_id(self, table_id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[GenTableColumnModel]:
|
||||
"""根据业务表ID获取业务表字段列表信息。
|
||||
|
||||
参数:
|
||||
- table_id (int): 业务表ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[GenTableColumnModel]: 业务表字段列表信息对象。
|
||||
"""
|
||||
return await self.get(table_id=table_id)
|
||||
return await self.get(table_id=table_id, preload=preload)
|
||||
|
||||
async def list_gen_table_column_crud_by_table_id(self, table_id: int, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]:
|
||||
async def list_gen_table_column_crud_by_table_id(self, table_id: int, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenTableColumnModel]:
|
||||
"""根据业务表ID查询业务表字段列表。
|
||||
|
||||
参数:
|
||||
- table_id (int): 业务表ID。
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序字段列表,每个元素为{"field": "字段名", "order": "asc" | "desc"}。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[GenTableColumnModel]: 业务表字段列表信息对象序列。
|
||||
"""
|
||||
return await self.list(search={"table_id": table_id}, order_by=order_by)
|
||||
return await self.list(search={"table_id": table_id}, order_by=order_by, preload=preload)
|
||||
|
||||
async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> List[GenTableColumnOutSchema]:
|
||||
"""
|
||||
@@ -516,17 +525,18 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
|
||||
return result
|
||||
|
||||
async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]:
|
||||
async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenTableColumnModel]:
|
||||
"""根据业务表字段查询业务表字段列表。
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数,例如{"table_id": 1}。
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序字段列表,每个元素为{"field": "字段名", "order": "asc" | "desc"}。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[GenTableColumnModel]: 业务表字段列表信息对象序列。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_gen_table_column_crud(self, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]:
|
||||
"""创建业务表字段。
|
||||
|
||||
@@ -13,6 +13,7 @@ class GenTableModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = 'gen_table'
|
||||
__table_args__ = ({'comment': '代码生成表'})
|
||||
__loader_options__ = ["columns", "creator"]
|
||||
|
||||
table_name: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, default='', comment='表名称')
|
||||
table_comment: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, default='', comment='表描述')
|
||||
@@ -45,6 +46,7 @@ class GenTableColumnModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = 'gen_table_column'
|
||||
__table_args__ = ({'comment': '代码生成表字段'})
|
||||
__loader_options__ = ["tables", "creator"]
|
||||
|
||||
column_name: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='列名称')
|
||||
column_comment: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, comment='列描述')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -16,46 +16,49 @@ class DeptCRUD(CRUDBase[DeptModel, DeptCreateSchema, DeptUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=DeptModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[DeptModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[DeptModel]:
|
||||
"""
|
||||
根据 id 获取部门信息。
|
||||
|
||||
参数:
|
||||
- id (int): 部门 ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- DeptModel | None: 部门信息,未找到返回 None。
|
||||
"""
|
||||
obj = await self.get(id=id)
|
||||
obj = await self.get(id=id, preload=preload)
|
||||
if not obj:
|
||||
return None
|
||||
return obj
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DeptModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[DeptModel]:
|
||||
"""
|
||||
获取部门列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[DeptModel]: 部门列表。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DeptModel]:
|
||||
async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[DeptModel]:
|
||||
"""
|
||||
获取部门树形列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[DeptModel]: 部门树形列表。
|
||||
"""
|
||||
return await self.tree_list(search=search, order_by=order_by, children_attr='children')
|
||||
return await self.tree_list(search=search, order_by=order_by, children_attr='children', preload=preload)
|
||||
|
||||
async def set_available_crud(self, ids: List[int], status: bool) -> None:
|
||||
"""
|
||||
@@ -81,4 +84,4 @@ class DeptCRUD(CRUDBase[DeptModel, DeptCreateSchema, DeptUpdateSchema]):
|
||||
- str | None: 部门名称,未找到返回 None。
|
||||
"""
|
||||
obj = await self.get(id=id)
|
||||
return obj.name if obj else None
|
||||
return obj.name if obj else None
|
||||
@@ -136,6 +136,13 @@ class DeptService:
|
||||
dept = await DeptCRUD(auth).get_by_id_crud(id=id)
|
||||
if not dept:
|
||||
raise CustomException(msg='删除失败,该部门不存在')
|
||||
# 校验是否存在子级部门,存在则禁止删除
|
||||
dept_list = await DeptCRUD(auth).get_list_crud()
|
||||
id_map = get_child_id_map(model_list=dept_list)
|
||||
for id in ids:
|
||||
descendants = get_child_recursion(id=id, id_map=id_map)
|
||||
if len(descendants) > 1:
|
||||
raise CustomException(msg='删除失败,存在子级部门,请先删除子级部门')
|
||||
await DeptCRUD(auth).delete(ids=ids)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.dict.model import DictDataModel, DictTypeModel
|
||||
@@ -21,30 +21,32 @@ class DictTypeCRUD(CRUDBase[DictTypeModel, DictTypeCreateSchema, DictTypeUpdateS
|
||||
self.auth = auth
|
||||
super().__init__(model=DictTypeModel, auth=auth)
|
||||
|
||||
async def get_obj_by_id_crud(self, id: int) -> Optional[DictTypeModel]:
|
||||
async def get_obj_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[DictTypeModel]:
|
||||
"""
|
||||
获取数据字典类型详情
|
||||
|
||||
参数:
|
||||
- id (int): 数据字典类型ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[DictTypeModel]: 数据字典类型模型,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DictTypeModel]:
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[DictTypeModel]:
|
||||
"""
|
||||
获取数据字典类型列表
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数,默认值为None
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数,默认值为None
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[DictTypeModel]: 数据字典类型模型序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_obj_crud(self, data: DictTypeCreateSchema) -> Optional[DictTypeModel]:
|
||||
"""
|
||||
@@ -110,30 +112,32 @@ class DictDataCRUD(CRUDBase[DictDataModel, DictDataCreateSchema, DictDataUpdateS
|
||||
self.auth = auth
|
||||
super().__init__(model=DictDataModel, auth=auth)
|
||||
|
||||
async def get_obj_by_id_crud(self, id: int) -> Optional[DictDataModel]:
|
||||
async def get_obj_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[DictDataModel]:
|
||||
"""
|
||||
获取数据字典数据详情
|
||||
|
||||
参数:
|
||||
- id (int): 数据字典数据ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[DictDataModel]: 数据字典数据模型,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DictDataModel]:
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[DictDataModel]:
|
||||
"""
|
||||
获取数据字典数据列表
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数,默认值为None
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数,默认值为None
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[DictDataModel]: 数据字典数据模型序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_obj_crud(self, data: DictDataCreateSchema) -> Optional[DictDataModel]:
|
||||
"""
|
||||
|
||||
@@ -14,6 +14,7 @@ class DictTypeModel(CreatorMixin):
|
||||
|
||||
__tablename__ = "system_dict_type"
|
||||
__table_args__ = ({'comment': '字典类型表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
dict_name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment='字典名称')
|
||||
dict_type: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment='字典类型')
|
||||
@@ -28,6 +29,7 @@ class DictDataModel(CreatorMixin):
|
||||
|
||||
__tablename__ = 'system_dict_data'
|
||||
__table_args__ = ({'comment': '字典数据表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
dict_sort: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment='字典排序')
|
||||
dict_label: Mapped[str] = mapped_column(String(100), nullable=False, comment='字典标签')
|
||||
|
||||
@@ -378,27 +378,16 @@ class DictDataService:
|
||||
- Dict: 数据字典数据详情字典
|
||||
"""
|
||||
exist_obj = await DictDataCRUD(auth).get_obj_by_id_crud(id=id)
|
||||
if not exist_obj:
|
||||
raise CustomException(msg='更新失败,该字典数据不存在')
|
||||
exist_obj = await DictDataCRUD(auth).get(dict_label=data.dict_label)
|
||||
if not exist_obj:
|
||||
raise CustomException(msg='更新失败,该字典数据不存在')
|
||||
|
||||
if exist_obj.id != id:
|
||||
raise CustomException(msg='更新失败,数据字典数据重复')
|
||||
|
||||
# 如果状态变更,需要同步更新字典类型状态并刷新缓存
|
||||
if exist_obj.status != data.status or exist_obj.dict_type != data.dict_type:
|
||||
# 如果字典类型变更,仅刷新旧类型缓存,不联动字典类型状态
|
||||
if exist_obj.dict_type != data.dict_type:
|
||||
dict_type = await DictTypeCRUD(auth).get(dict_type=exist_obj.dict_type)
|
||||
if dict_type:
|
||||
update_data = DictTypeUpdateSchema(
|
||||
dict_name=dict_type.dict_name,
|
||||
dict_type=dict_type.dict_type,
|
||||
status=data.status,
|
||||
description=dict_type.description
|
||||
)
|
||||
await DictTypeCRUD(auth).update_obj_crud(id=dict_type.id, data=update_data)
|
||||
# 刷新Redis缓存
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{dict_type.dict_type}"
|
||||
try:
|
||||
dict_data_list = await DictDataCRUD(auth).get_obj_list_crud(search={'dict_type': dict_type.dict_type})
|
||||
@@ -409,7 +398,7 @@ class DictDataService:
|
||||
value=value,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新字典数据状态时刷新缓存失败: {e}")
|
||||
logger.error(f"更新字典数据类型变更时刷新旧缓存失败: {e}")
|
||||
|
||||
obj = await DictDataCRUD(auth).update_obj_crud(id=id, data=data)
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{data.dict_type}"
|
||||
@@ -451,6 +440,9 @@ class DictDataService:
|
||||
exist_obj = await DictDataCRUD(auth).get_obj_by_id_crud(id=id)
|
||||
if not exist_obj:
|
||||
raise CustomException(msg=f'{id} 删除失败,该字典数据不存在')
|
||||
# 新增:系统默认字典数据不允许删除(通过 is_default 判断)
|
||||
if exist_obj.is_default:
|
||||
raise CustomException(msg='删除失败,系统默认字典数据不允许删除')
|
||||
# 删除Redis缓存
|
||||
redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{exist_obj.dict_type}"
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -32,28 +32,29 @@ class OperationLogCRUD(CRUDBase[OperationLogModel, OperationLogCreateSchema, Ope
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[OperationLogModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[OperationLogModel]:
|
||||
"""
|
||||
根据ID获取操作日志详情。
|
||||
|
||||
参数:
|
||||
- id (int): 操作日志ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- OperationLogModel | None: 操作日志记录。
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[OperationLogModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[OperationLogModel]:
|
||||
"""
|
||||
获取操作日志列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件字典。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[OperationLogModel]: 操作日志列表。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
@@ -13,6 +13,7 @@ class OperationLogModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = "system_log"
|
||||
__table_args__ = ({'comment': '系统日志表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
type: Mapped[int] = mapped_column(Integer, comment="日志类型(1登录日志 2操作日志)")
|
||||
request_path: Mapped[str] = mapped_column(String(255), comment="请求路径")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -16,46 +16,49 @@ class MenuCRUD(CRUDBase[MenuModel, MenuCreateSchema, MenuUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=MenuModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[MenuModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[MenuModel]:
|
||||
"""
|
||||
根据 id 获取菜单信息。
|
||||
|
||||
参数:
|
||||
- id (int): 菜单 ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- MenuModel | None: 菜单信息,未找到返回 None。
|
||||
"""
|
||||
obj = await self.get(id=id)
|
||||
obj = await self.get(id=id, preload=preload)
|
||||
if not obj:
|
||||
return None
|
||||
return obj
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[MenuModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[MenuModel]:
|
||||
"""
|
||||
获取菜单列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[MenuModel]: 菜单列表。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[MenuModel]:
|
||||
async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[MenuModel]:
|
||||
"""
|
||||
获取菜单树形列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[MenuModel]: 菜单树形列表。
|
||||
"""
|
||||
return await self.tree_list(search=search, order_by=order_by, children_attr='children')
|
||||
return await self.tree_list(search=search, order_by=order_by, children_attr='children', preload=preload)
|
||||
|
||||
async def set_available_crud(self, ids: List[int], status: bool) -> None:
|
||||
"""
|
||||
@@ -68,4 +71,4 @@ class MenuCRUD(CRUDBase[MenuModel, MenuCreateSchema, MenuUpdateSchema]):
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
await self.set(ids=ids, status=status)
|
||||
await self.set(ids=ids, status=status)
|
||||
@@ -4,13 +4,16 @@
|
||||
定义系统菜单相关数据模型
|
||||
"""
|
||||
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import Boolean, String, Integer, JSON, ForeignKey
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import ModelMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.api.v1.module_system.role.model import RoleModel
|
||||
|
||||
|
||||
class MenuModel(ModelMixin):
|
||||
"""
|
||||
@@ -24,6 +27,8 @@ class MenuModel(ModelMixin):
|
||||
"""
|
||||
__tablename__ = "system_menu"
|
||||
__table_args__ = ({'comment': '菜单表'})
|
||||
__loader_options__ = ["roles"]
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
||||
name: Mapped[str] = mapped_column(String(50), nullable=False, comment='菜单名称', unique=True)
|
||||
type: Mapped[int] = mapped_column(Integer, nullable=False, default=2, comment='菜单类型(1:目录 2:菜单 3:按钮/权限 4:链接)')
|
||||
@@ -44,7 +49,7 @@ class MenuModel(ModelMixin):
|
||||
|
||||
parent_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('system_menu.id', ondelete='SET NULL'), default=None, index=True, comment='父菜单ID')
|
||||
parent: Mapped[Optional['MenuModel']] = relationship(back_populates='children', remote_side=[id], uselist=False)
|
||||
children: Mapped[Optional[List['MenuModel']]] = relationship(back_populates='parent')
|
||||
children: Mapped[Optional[List['MenuModel']]] = relationship(back_populates='parent', order_by="MenuModel.order")
|
||||
|
||||
# 角色关联关系
|
||||
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_role_menus", back_populates="menus", lazy="selectin")
|
||||
|
||||
@@ -137,6 +137,13 @@ class MenuService:
|
||||
menu = await MenuCRUD(auth).get_by_id_crud(id=id)
|
||||
if not menu:
|
||||
raise CustomException(msg='删除失败,该菜单不存在')
|
||||
# 校验是否存在子级菜单,存在则禁止删除
|
||||
menu_list = await MenuCRUD(auth).get_list_crud()
|
||||
id_map = get_child_id_map(model_list=menu_list)
|
||||
for id in ids:
|
||||
descendants = get_child_recursion(id=id, id_map=id_map)
|
||||
if len(descendants) > 1:
|
||||
raise CustomException(msg='删除失败,存在子级菜单,请先删除子级菜单')
|
||||
await MenuCRUD(auth).delete(ids=ids)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -21,30 +21,32 @@ class NoticeCRUD(CRUDBase[NoticeModel, NoticeCreateSchema, NoticeUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=NoticeModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[NoticeModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[NoticeModel]:
|
||||
"""
|
||||
根据ID获取公告详情。
|
||||
|
||||
参数:
|
||||
- id (int): 公告ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[NoticeModel]: 公告模型实例。
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[NoticeModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[NoticeModel]:
|
||||
"""
|
||||
获取公告列表。
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数。
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[NoticeModel]: 公告模型实例列表。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_crud(self, data: NoticeCreateSchema) -> Optional[NoticeModel]:
|
||||
"""
|
||||
|
||||
@@ -22,6 +22,7 @@ class NoticeModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = "system_notice"
|
||||
__table_args__ = ({'comment': '通知公告表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
notice_title: Mapped[str] = mapped_column(String(50), nullable=False, comment='公告标题')
|
||||
notice_type: Mapped[str] = mapped_column(String(50), nullable=False, comment='公告类型(1通知 2公告)')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -21,42 +21,45 @@ class ParamsCRUD(CRUDBase[ParamsModel, ParamsCreateSchema, ParamsUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=ParamsModel, auth=auth)
|
||||
|
||||
async def get_obj_by_id_crud(self, id: int) -> Optional[ParamsModel]:
|
||||
async def get_obj_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[ParamsModel]:
|
||||
"""
|
||||
获取配置管理型详情
|
||||
|
||||
参数:
|
||||
- id (int): 配置管理型ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[ParamsModel]: 配置管理型模型实例
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_obj_by_key_crud(self, key: str) -> Optional[ParamsModel]:
|
||||
async def get_obj_by_key_crud(self, key: str, preload: Optional[List[Union[str, Any]]] = None) -> Optional[ParamsModel]:
|
||||
"""
|
||||
根据key获取配置管理型详情
|
||||
|
||||
参数:
|
||||
- key (str): 配置管理型key
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[ParamsModel]: 配置管理型模型实例
|
||||
"""
|
||||
return await self.get(config_key=key)
|
||||
return await self.get(config_key=key, preload=preload)
|
||||
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[ParamsModel]:
|
||||
async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[ParamsModel]:
|
||||
"""
|
||||
获取配置管理型列表
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 查询参数对象。
|
||||
- order_by (List[Dict[str, str]] | None): 排序参数列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[ParamsModel]: 配置管理型模型实例列表
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def create_obj_crud(self, data: ParamsCreateSchema) -> Optional[ParamsModel]:
|
||||
"""
|
||||
@@ -93,4 +96,4 @@ class ParamsCRUD(CRUDBase[ParamsModel, ParamsCreateSchema, ParamsUpdateSchema]):
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
return await self.delete(ids=ids)
|
||||
return await self.delete(ids=ids)
|
||||
@@ -13,6 +13,7 @@ class ParamsModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = "system_param"
|
||||
__table_args__ = ({'comment': '系统参数表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
# 基础字段
|
||||
config_name: Mapped[str] = mapped_column(String(500), nullable=False, unique=True, comment='参数名称')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Optional, Sequence
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from ..auth.schema import AuthSchema
|
||||
@@ -21,30 +21,32 @@ class PositionCRUD(CRUDBase[PositionModel, PositionCreateSchema, PositionUpdateS
|
||||
self.auth = auth
|
||||
super().__init__(model=PositionModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[PositionModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[PositionModel]:
|
||||
"""
|
||||
根据 id 获取岗位信息。
|
||||
|
||||
参数:
|
||||
- id (int): 岗位 ID。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- PositionModel | None: 岗位信息,未找到返回 None。
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[PositionModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[PositionModel]:
|
||||
"""
|
||||
获取岗位列表。
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 搜索条件。
|
||||
- order_by (List[Dict[str, str]] | None): 排序字段列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[PositionModel]: 岗位列表。
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def set_available_crud(self, ids: List[int], status: bool) -> None:
|
||||
"""
|
||||
@@ -74,4 +76,4 @@ class PositionCRUD(CRUDBase[PositionModel, PositionCreateSchema, PositionUpdateS
|
||||
obj = await self.get(id=id)
|
||||
if obj:
|
||||
position_names.append(obj.name)
|
||||
return position_names
|
||||
return position_names
|
||||
@@ -18,6 +18,7 @@ class PositionModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = "system_position"
|
||||
__table_args__ = ({'comment': '岗位表'})
|
||||
__loader_options__ = ["creator"]
|
||||
|
||||
name: Mapped[str] = mapped_column(String(40), nullable=False, unique=True, comment="岗位名称")
|
||||
order: Mapped[int] = mapped_column(Integer, nullable=False, default=1, comment="显示排序")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Sequence, Optional
|
||||
from typing import Dict, List, Sequence, Optional, Union, Any
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from .model import RoleModel
|
||||
@@ -23,30 +23,32 @@ class RoleCRUD(CRUDBase[RoleModel, RoleCreateSchema, RoleUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=RoleModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[RoleModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[RoleModel]:
|
||||
"""
|
||||
根据id获取角色信息
|
||||
|
||||
参数:
|
||||
- id (int): 角色ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载选项
|
||||
|
||||
返回:
|
||||
- Optional[RoleModel]: 角色模型对象
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(id=id, preload=preload)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[RoleModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[RoleModel]:
|
||||
"""
|
||||
获取角色列表
|
||||
|
||||
参数:
|
||||
- search (Optional[Dict]): 查询参数
|
||||
- order_by (Optional[List[Dict[str, str]]]): 排序参数
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载选项
|
||||
|
||||
返回:
|
||||
- Sequence[RoleModel]: 角色模型对象列表
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def set_role_menus_crud(self, role_ids: List[int], menu_ids: List[int]) -> None:
|
||||
"""
|
||||
@@ -112,4 +114,4 @@ class RoleCRUD(CRUDBase[RoleModel, RoleCreateSchema, RoleUpdateSchema]):
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
await self.set(ids=ids, status=status)
|
||||
await self.set(ids=ids, status=status)
|
||||
@@ -4,13 +4,18 @@
|
||||
定义角色相关数据模型和关联表
|
||||
"""
|
||||
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import Boolean, String, Integer, ForeignKey
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import MappedBase, CreatorMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.api.v1.module_system.menu.model import MenuModel
|
||||
from app.api.v1.module_system.dept.model import DeptModel
|
||||
from app.api.v1.module_system.user.model import UserModel
|
||||
|
||||
|
||||
class RoleMenusModel(MappedBase):
|
||||
"""
|
||||
@@ -71,6 +76,7 @@ class RoleModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = "system_role"
|
||||
__table_args__ = ({'comment': '角色表'})
|
||||
__loader_options__ = ["menus", "depts", "creator"]
|
||||
|
||||
name: Mapped[str] = mapped_column(String(40), nullable=False, unique=True, comment="角色名称")
|
||||
code: Mapped[Optional[str]] = mapped_column(String(20), nullable=True, unique=True, comment="角色编码")
|
||||
@@ -78,7 +84,7 @@ class RoleModel(CreatorMixin):
|
||||
status: Mapped[bool] = mapped_column(Boolean(), default=True, nullable=False, comment="是否启用(True:启用 False:禁用)")
|
||||
data_scope: Mapped[int] = mapped_column(Integer, nullable=False, default=1, comment="数据权限范围")
|
||||
|
||||
menus: Mapped[List["MenuModel"]] = relationship(secondary="system_role_menus", back_populates="roles", lazy="selectin")
|
||||
menus: Mapped[List["MenuModel"]] = relationship(secondary="system_role_menus", back_populates="roles", lazy="selectin", order_by="MenuModel.order")
|
||||
depts: Mapped[List["DeptModel"]] = relationship(secondary="system_role_depts", back_populates="roles", lazy="selectin")
|
||||
users: Mapped[List["UserModel"]] = relationship(secondary="system_user_roles", back_populates="roles", lazy="selectin")
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Dict, List, Sequence, Optional
|
||||
from typing import Dict, List, Optional, Sequence, Union, Any
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from .model import UserModel
|
||||
from .schema import UserCreateSchema, UserForgetPasswordSchema, UserUpdateSchema
|
||||
@@ -26,54 +25,73 @@ class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]):
|
||||
self.auth = auth
|
||||
super().__init__(model=UserModel, auth=auth)
|
||||
|
||||
async def get_by_id_crud(self, id: int) -> Optional[UserModel]:
|
||||
async def get_by_id_crud(self, id: int, preload: Optional[List[Union[str, Any]]] = None) -> Optional[UserModel]:
|
||||
"""
|
||||
根据id获取用户信息
|
||||
|
||||
参数:
|
||||
- id (int): 用户ID
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[UserModel]: 用户信息,如果不存在则为None
|
||||
"""
|
||||
return await self.get(id=id)
|
||||
return await self.get(
|
||||
preload=preload,
|
||||
id=id,
|
||||
)
|
||||
|
||||
async def get_by_username_crud(self, username: str) -> Optional[UserModel]:
|
||||
async def get_by_username_crud(self, username: str, preload: Optional[List[Union[str, Any]]] = None) -> Optional[UserModel]:
|
||||
"""
|
||||
根据用户名获取用户信息
|
||||
|
||||
参数:
|
||||
- username (str): 用户名
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[UserModel]: 用户信息,如果不存在则为None
|
||||
"""
|
||||
return await self.get(username=username)
|
||||
return await self.get(
|
||||
preload=preload,
|
||||
username=username,
|
||||
)
|
||||
|
||||
async def get_by_mobile_crud(self, mobile: str) -> Optional[UserModel]:
|
||||
|
||||
|
||||
async def get_by_mobile_crud(self, mobile: str, preload: Optional[List[Union[str, Any]]] = None) -> Optional[UserModel]:
|
||||
"""
|
||||
根据手机号获取用户信息
|
||||
|
||||
参数:
|
||||
- mobile (str): 手机号
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Optional[UserModel]: 用户信息,如果不存在则为None
|
||||
"""
|
||||
return await self.get(mobile=mobile)
|
||||
return await self.get(
|
||||
preload=preload,
|
||||
mobile=mobile,
|
||||
)
|
||||
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[UserModel]:
|
||||
async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[UserModel]:
|
||||
"""
|
||||
获取用户列表
|
||||
|
||||
参数:
|
||||
- search (Dict | None): 查询参数对象。
|
||||
- order_by (List[Dict[str, str]] | None): 排序参数列表。
|
||||
- preload (Optional[List[Union[str, Any]]]): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
Sequence[UserModel]: 用户列表
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by)
|
||||
return await self.list(
|
||||
search=search,
|
||||
order_by=order_by,
|
||||
preload=preload,
|
||||
)
|
||||
|
||||
async def update_last_login_crud(self, id: int) -> Optional[UserModel]:
|
||||
"""
|
||||
@@ -187,5 +205,4 @@ class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]):
|
||||
"""
|
||||
if await self.get_by_username_crud(username=data.username):
|
||||
return None
|
||||
|
||||
return await self.create(data=UserCreateSchema(**data.model_dump()))
|
||||
return await self.create(data=UserCreateSchema(**data.model_dump()))
|
||||
@@ -13,7 +13,7 @@ from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from app.api.v1.module_system.dept.model import DeptModel
|
||||
from app.api.v1.module_system.position.model import PositionModel
|
||||
from app.api.v1.module_system.role.model import RoleModel
|
||||
from app.core.base_model import MappedBase, CreatorMixin
|
||||
from app.core.base_model import MappedBase
|
||||
|
||||
|
||||
class UserRolesModel(MappedBase):
|
||||
@@ -68,6 +68,7 @@ class UserModel(MappedBase):
|
||||
"""
|
||||
__tablename__ = "system_users"
|
||||
__table_args__ = ({'comment': '用户表'})
|
||||
__loader_options__ = ["dept", "roles", "positions", "creator"]
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class UserRegisterSchema(BaseModel):
|
||||
mobile: Optional[str] = Field(default=None, description="手机号")
|
||||
username: str = Field(..., max_length=32, description="账号")
|
||||
password: str = Field(..., max_length=128, description="密码哈希值")
|
||||
role_ids: Optional[List[int]] = Field(default=[2], description='角色ID')
|
||||
role_ids: Optional[List[int]] = Field(default=[1], description='角色ID')
|
||||
creator_id: Optional[int] = Field(default=1, description='创建人ID')
|
||||
description: Optional[str] = Field(default=None, max_length=255, description="备注")
|
||||
|
||||
|
||||
@@ -77,11 +77,6 @@ class UserService:
|
||||
user_list = await UserCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by)
|
||||
user_dict_list = []
|
||||
for user in user_list:
|
||||
if user.dept_id:
|
||||
dept = await DeptCRUD(auth).get_by_id_crud(id=user.dept_id)
|
||||
UserOutSchema.dept_name = dept.name if dept else None
|
||||
else:
|
||||
UserOutSchema.dept_name = None
|
||||
user_dict = UserOutSchema.model_validate(user).model_dump()
|
||||
user_dict_list.append(user_dict)
|
||||
|
||||
@@ -101,6 +96,9 @@ class UserService:
|
||||
"""
|
||||
if not data.username:
|
||||
raise CustomException(msg="用户名不能为空")
|
||||
# 检查是否试图创建超级管理员
|
||||
if data.is_superuser:
|
||||
raise CustomException(msg='不允许创建超级管理员')
|
||||
# 检查用户名是否存在
|
||||
user = await UserCRUD(auth).get_by_username_crud(username=data.username)
|
||||
if user:
|
||||
@@ -142,17 +140,34 @@ class UserService:
|
||||
"""
|
||||
if not data.username:
|
||||
raise CustomException(msg="用户名不能为空")
|
||||
# 检查是否是超级管理员
|
||||
if data.is_superuser:
|
||||
raise CustomException(msg='超级管理员系统唯一')
|
||||
# 检查用户是否存在
|
||||
user = await UserCRUD(auth).get_by_id_crud(id=id)
|
||||
if not user:
|
||||
raise CustomException(msg='用户不存在')
|
||||
|
||||
# 检查是否尝试修改超级管理员
|
||||
if user.is_superuser:
|
||||
raise CustomException(msg='超级管理员不允许修改')
|
||||
|
||||
# 检查用户名是否重复
|
||||
exist_user = await UserCRUD(auth).get_by_username_crud(username=data.username)
|
||||
if exist_user and exist_user.id != id:
|
||||
raise CustomException(msg='已存在相同的用户名')
|
||||
|
||||
# 新增:检查手机号是否重复
|
||||
if data.mobile:
|
||||
exist_mobile_user = await UserCRUD(auth).get_by_mobile_crud(mobile=data.mobile)
|
||||
if exist_mobile_user and exist_mobile_user.id != id:
|
||||
raise CustomException(msg='更新失败,手机号已存在')
|
||||
# 新增:检查邮箱是否重复
|
||||
if data.email:
|
||||
exist_email_user = await UserCRUD(auth).get(email=data.email)
|
||||
if exist_email_user and exist_email_user.id != id:
|
||||
raise CustomException(msg='更新失败,邮箱已存在')
|
||||
# 检查部门是否存在且可用
|
||||
|
||||
if data.dept_id:
|
||||
dept = await DeptCRUD(auth).get_by_id_crud(id=data.dept_id)
|
||||
if not dept:
|
||||
@@ -167,7 +182,8 @@ class UserService:
|
||||
# 更新用户
|
||||
# user_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids"})
|
||||
# new_user = await UserCRUD(auth).update(id=id, data=user_dict)
|
||||
new_user = await UserCRUD(auth).update(id=id, data=data)
|
||||
user_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids", "last_login", "password"})
|
||||
new_user = await UserCRUD(auth).update(id=id, data=user_dict)
|
||||
|
||||
# 更新角色和岗位
|
||||
if data.role_ids and len(data.role_ids) > 0:
|
||||
@@ -248,7 +264,7 @@ class UserService:
|
||||
# 获取菜单权限
|
||||
if auth.user and auth.user.is_superuser:
|
||||
# 使用树形结构查询,预加载children关系
|
||||
menu_all = await MenuCRUD(auth).get_tree_list_crud(search={'type': ('in', [1, 2, 4]), 'status': True})
|
||||
menu_all = await MenuCRUD(auth).get_tree_list_crud(search={'type': ('in', [1, 2, 4]), 'status': True}, order_by=[{"order": "asc"}])
|
||||
menus = [MenuOutSchema.model_validate(menu).model_dump() for menu in menu_all]
|
||||
|
||||
else:
|
||||
@@ -263,7 +279,7 @@ class UserService:
|
||||
# 使用树形结构查询,预加载children关系
|
||||
menus = [
|
||||
MenuOutSchema.model_validate(menu).model_dump()
|
||||
for menu in await MenuCRUD(auth).get_tree_list_crud(search={'id': ('in', list(menu_ids))})
|
||||
for menu in await MenuCRUD(auth).get_tree_list_crud(search={'id': ('in', list(menu_ids))}, order_by=[{"order": "asc"}])
|
||||
] if menu_ids else []
|
||||
user_dict["menus"] = traversal_to_tree(menus)
|
||||
return user_dict
|
||||
@@ -285,6 +301,18 @@ class UserService:
|
||||
user = await UserCRUD(auth).get_by_id_crud(id=auth.user.id)
|
||||
if not user:
|
||||
raise CustomException(msg="用户不存在")
|
||||
if user.is_superuser:
|
||||
raise CustomException(msg="超级管理员不能修改个人信息")
|
||||
# 新增:检查手机号是否重复
|
||||
if data.mobile:
|
||||
exist_mobile_user = await UserCRUD(auth).get_by_mobile_crud(mobile=data.mobile)
|
||||
if exist_mobile_user and exist_mobile_user.id != auth.user.id:
|
||||
raise CustomException(msg='更新失败,手机号已存在')
|
||||
# 新增:检查邮箱是否重复
|
||||
if data.email:
|
||||
exist_email_user = await UserCRUD(auth).get(email=data.email)
|
||||
if exist_email_user and exist_email_user.id != auth.user.id:
|
||||
raise CustomException(msg='更新失败,邮箱已存在')
|
||||
user_update_data = UserUpdateSchema(**data.model_dump())
|
||||
new_user = await UserCRUD(auth).update(id=auth.user.id, data=user_update_data)
|
||||
return UserOutSchema.model_validate(new_user).model_dump()
|
||||
@@ -380,6 +408,10 @@ class UserService:
|
||||
user = await UserCRUD(auth).get_by_id_crud(id=data.id)
|
||||
if not user:
|
||||
raise CustomException(msg="用户不存在")
|
||||
|
||||
# 检查是否是超级管理员
|
||||
if user.is_superuser:
|
||||
raise CustomException(msg="超级管理员密码不能重置")
|
||||
|
||||
# 更新密码
|
||||
new_password_hash = PwdUtil.set_password_hash(password=data.password)
|
||||
@@ -405,14 +437,10 @@ class UserService:
|
||||
|
||||
data.password = PwdUtil.set_password_hash(password=data.password)
|
||||
data.name = data.username
|
||||
data.creator_id = 1
|
||||
# dict_data = data.model_dump(exclude_unset=True)
|
||||
# result = await UserCRUD(auth).create(data=dict_data)
|
||||
user_create_data = UserCreateSchema(**data.model_dump())
|
||||
result = await UserCRUD(auth).create(data=user_create_data)
|
||||
create_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids"})
|
||||
result = await UserCRUD(auth).create(data=create_dict)
|
||||
if data.role_ids:
|
||||
await UserCRUD(auth).set_user_roles_crud(user_ids=[result.id], role_ids=data.role_ids)
|
||||
# await UserCRUD(auth).set_user_positions_crud(user_ids=[result.id], position_ids=data.position_ids)
|
||||
return UserOutSchema.model_validate(result).model_dump()
|
||||
|
||||
@classmethod
|
||||
@@ -432,6 +460,11 @@ class UserService:
|
||||
raise CustomException(msg="用户不存在")
|
||||
if not user.status:
|
||||
raise CustomException(msg="用户已停用")
|
||||
|
||||
# 检查是否是超级管理员
|
||||
if user.is_superuser:
|
||||
raise CustomException(msg="超级管理员密码不能重置")
|
||||
|
||||
new_password_hash = PwdUtil.set_password_hash(password=data.new_password)
|
||||
new_user = await UserCRUD(auth).forget_password_crud(id=user.id, password_hash=new_password_hash)
|
||||
return UserOutSchema.model_validate(new_user).model_dump()
|
||||
@@ -510,6 +543,10 @@ class UserService:
|
||||
# 处理用户导入
|
||||
exists_user = await UserCRUD(auth).get_by_username_crud(username=user_data["username"])
|
||||
if exists_user:
|
||||
# 检查是否是超级管理员
|
||||
if exists_user.is_superuser:
|
||||
error_msgs.append(f"第{count}行: 超级管理员不允许修改")
|
||||
continue
|
||||
if update_support:
|
||||
user_update_data = UserUpdateSchema(**user_data)
|
||||
await UserCRUD(auth).update(id=exists_user.id, data=user_update_data)
|
||||
|
||||
Reference in New Issue
Block a user