From c2ca6d19ac1ee7a727eb04ba7f6d01b002ba3f3d Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Sat, 18 Oct 2025 16:31:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=B3=A8=E9=87=8A=E5=92=8C=E6=96=87=E6=A1=A3=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit style: 统一代码风格和格式 docs: 完善函数和方法的文档字符串 refactor(base_model): 移除冗余的表名和表参数生成方法 refactor(constant): 更新返回码注释格式 refactor(router_class): 添加路由处理器的详细文档 refactor(database): 完善数据库连接函数的文档 refactor(security): 添加认证类和方法的详细文档 refactor(validator): 更新验证器函数的文档格式 refactor(serialize): 优化序列化工具类的文档 refactor(response): 完善响应类的文档字符串 refactor(dependencies): 添加依赖函数的详细文档 refactor(initialize): 完善初始化脚本的文档 refactor(plugin): 添加生命周期和中间件注册的文档 refactor(service): 完善服务层方法的文档 refactor(controller): 添加控制器方法的详细文档 refactor(crud): 完善CRUD操作的文档字符串 refactor(schema): 简化模型类并移除冗余字段 refactor(param): 更新查询参数类的注释格式 refactor(template): 优化代码生成模板的格式 refactor(console): 添加控制台输出功能的实现 refactor(util): 完善工具函数的文档字符串 --- .../v1/module_application/ai/controller.py | 64 +- .../app/api/v1/module_application/ai/crud.py | 69 +- .../api/v1/module_application/ai/service.py | 67 +- .../v1/module_application/job/controller.py | 140 ++- .../app/api/v1/module_application/job/crud.py | 106 +- .../api/v1/module_application/job/service.py | 123 ++- .../v1/module_application/myapp/controller.py | 62 ++ .../api/v1/module_application/myapp/crud.py | 64 +- .../v1/module_application/myapp/service.py | 68 +- .../api/v1/module_common/file/controller.py | 20 + .../app/api/v1/module_common/file/service.py | 30 +- .../v1/module_generator/demo/controller.py | 89 +- .../app/api/v1/module_generator/demo/crud.py | 70 +- .../api/v1/module_generator/demo/service.py | 126 ++- .../v1/module_generator/gencode/controller.py | 113 +++ .../api/v1/module_generator/gencode/crud.py | 180 +++- .../api/v1/module_generator/gencode/param.py | 2 - .../v1/module_generator/gencode/service.py | 341 +++++-- .../api/v1/module_monitor/cache/controller.py | 62 +- .../api/v1/module_monitor/cache/service.py | 83 +- .../v1/module_monitor/online/controller.py | 31 +- .../api/v1/module_monitor/online/service.py | 39 +- .../v1/module_monitor/resource/controller.py | 96 +- .../api/v1/module_monitor/resource/service.py | 198 +++- .../v1/module_monitor/server/controller.py | 7 +- .../api/v1/module_monitor/server/service.py | 52 +- .../api/v1/module_system/auth/controller.py | 52 + .../app/api/v1/module_system/auth/service.py | 87 +- .../api/v1/module_system/dept/controller.py | 79 ++ backend/app/api/v1/module_system/dept/crud.py | 49 +- .../app/api/v1/module_system/dept/service.py | 76 +- .../api/v1/module_system/dict/controller.py | 227 ++++- backend/app/api/v1/module_system/dict/crud.py | 140 ++- .../app/api/v1/module_system/dict/service.py | 171 +++- .../api/v1/module_system/log/controller.py | 45 +- backend/app/api/v1/module_system/log/crud.py | 37 +- .../app/api/v1/module_system/log/service.py | 53 +- .../api/v1/module_system/menu/controller.py | 63 +- backend/app/api/v1/module_system/menu/crud.py | 41 +- .../app/api/v1/module_system/menu/service.py | 63 +- .../api/v1/module_system/notice/controller.py | 82 +- .../app/api/v1/module_system/notice/crud.py | 70 +- .../api/v1/module_system/notice/service.py | 87 +- .../api/v1/module_system/params/controller.py | 105 +- .../app/api/v1/module_system/params/crud.py | 69 +- .../api/v1/module_system/params/service.py | 127 ++- .../v1/module_system/position/controller.py | 73 +- .../app/api/v1/module_system/position/crud.py | 45 +- .../api/v1/module_system/position/service.py | 78 +- .../api/v1/module_system/role/controller.py | 83 +- backend/app/api/v1/module_system/role/crud.py | 71 +- .../app/api/v1/module_system/role/service.py | 89 +- .../api/v1/module_system/user/controller.py | 163 +++- backend/app/api/v1/module_system/user/crud.py | 119 ++- .../app/api/v1/module_system/user/service.py | 196 +++- backend/app/common/constant.py | 10 +- backend/app/common/request.py | 22 +- backend/app/common/response.py | 55 +- backend/app/config/setting.py | 3 +- backend/app/core/ap_scheduler.py | 139 ++- backend/app/core/base_crud.py | 153 +-- backend/app/core/base_model.py | 10 - backend/app/core/base_params.py | 14 +- backend/app/core/base_schema.py | 8 +- backend/app/core/database.py | 31 +- backend/app/core/dependencies.py | 71 +- backend/app/core/exceptions.py | 109 ++- backend/app/core/logger.py | 7 +- backend/app/core/mongo_crud.py | 108 ++- backend/app/core/redis_crud.py | 126 ++- backend/app/core/router_class.py | 21 +- backend/app/core/security.py | 51 +- backend/app/core/serialize.py | 34 +- backend/app/core/validator.py | 69 +- backend/app/module_task/scheduler_test.py | 12 +- backend/app/plugin/init_app.py | 51 +- backend/app/scripts/initialize.py | 35 +- backend/app/utils/ai_util.py | 22 +- backend/app/utils/captcha_util.py | 12 +- backend/app/utils/common_util.py | 113 ++- backend/app/utils/console.py | 28 + backend/app/utils/cron_util.py | 108 ++- backend/app/utils/excel_util.py | 47 +- backend/app/utils/gen_util.py | 115 ++- backend/app/utils/hash_bcrpy_util.py | 95 +- backend/app/utils/ip_local_util.py | 89 +- backend/app/utils/jinja2_template_util.py | 285 ++++-- backend/app/utils/re_util.py | 35 +- backend/app/utils/string_util.py | 110 ++- backend/app/utils/time_util.py | 123 ++- backend/app/utils/upload_util.py | 142 ++- backend/templates/python/controller.py.j2 | 174 ++-- backend/templates/python/crud.py.j2 | 111 +-- backend/templates/python/model.py.j2 | 4 +- backend/templates/python/param.py.j2 | 20 +- backend/templates/python/schema.py.j2 | 16 +- backend/templates/python/service.py.j2 | 267 ++---- backend/templates/sql/sql.sql.j2 | 146 ++- backend/templates/vue/api.ts.j2 | 161 +++- backend/templates/vue/index.vue.j2 | 906 +++++++----------- frontend/src/views/gencode/backcode/index.vue | 1 - 101 files changed, 6915 insertions(+), 2296 deletions(-) diff --git a/backend/app/api/v1/module_application/ai/controller.py b/backend/app/api/v1/module_application/ai/controller.py index 49579367..6afccf69 100644 --- a/backend/app/api/v1/module_application/ai/controller.py +++ b/backend/app/api/v1/module_application/ai/controller.py @@ -23,7 +23,15 @@ async def chat_controller( query: ChatQuerySchema, auth: AuthSchema = Depends(AuthPermission(["app:ai:chat"])) ) -> StreamingResponse: - """智能对话接口""" + """ + 智能对话接口 + + 参数: + - query (ChatQuerySchema): 聊天查询模型 + + 返回: + - StreamingResponse: 流式响应,每次返回一个聊天响应 + """ user_name = auth.user.name if auth.user else "未知用户" logger.info(f"用户 {user_name} 发起智能对话: {query.message[:50]}...") @@ -45,6 +53,15 @@ async def detail_controller( id: int = Path(..., description="MCP ID"), auth: AuthSchema = Depends(AuthPermission(["app:ai:query"])) ) -> JSONResponse: + """ + 获取 MCP 服务器详情接口 + + 参数: + - id (int): MCP 服务器ID + + 返回: + - JSONResponse: 包含 MCP 服务器详情的 JSON 响应 + """ result_dict = await McpService.detail_service(auth=auth, id=id) logger.info(f"获取 MCP 服务器详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取 MCP 服务器详情成功") @@ -56,6 +73,17 @@ async def list_controller( search: McpQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:ai:query"])) ) -> JSONResponse: + """ + 查询 MCP 服务器列表接口 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (McpQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含 MCP 服务器列表的 JSON 响应 + """ result_dict_list = await McpService.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) logger.info(f"查询 MCP 服务器列表成功") @@ -67,6 +95,16 @@ async def create_controller( data: McpCreateSchema, auth: AuthSchema = Depends(AuthPermission(["app:ai:create"])) ) -> JSONResponse: + """ + 创建 MCP 服务器接口 + + 参数: + - data (McpCreateSchema): 创建 MCP 服务器模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建 MCP 服务器结果的 JSON 响应 + """ result_dict = await McpService.create_service(auth=auth, data=data) logger.info(f"创建 MCP 服务器成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建 MCP 服务器成功") @@ -78,6 +116,17 @@ async def update_controller( id: int = Path(..., description="MCP ID"), auth: AuthSchema = Depends(AuthPermission(["app:ai:update"])) ) -> JSONResponse: + """ + 修改 MCP 服务器接口 + + 参数: + - data (McpUpdateSchema): 修改 MCP 服务器模型 + - id (int): MCP 服务器ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改 MCP 服务器结果的 JSON 响应 + """ result_dict = await McpService.update_service(auth=auth, id=id, data=data) logger.info(f"修改 MCP 服务器成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改 MCP 服务器成功") @@ -88,6 +137,16 @@ async def delete_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["app:ai:delete"])) ) -> JSONResponse: + """ + 删除 MCP 服务器接口 + + 参数: + - ids (list[int]): MCP 服务器ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除 MCP 服务器结果的 JSON 响应 + """ await McpService.delete_service(auth=auth, ids=ids) logger.info(f"删除 MCP 服务器成功: {ids}") return SuccessResponse(msg="删除 MCP 服务器成功") @@ -97,7 +156,8 @@ async def delete_controller( async def websocket_chat_controller( websocket: WebSocket, ): - """WebSocket聊天接口 + """ + WebSocket聊天接口 ws://127.0.0.1:8001/api/v1/ai/mcp/ws/chat """ diff --git a/backend/app/api/v1/module_application/ai/crud.py b/backend/app/api/v1/module_application/ai/crud.py index f1ca8e69..3f9cf281 100644 --- a/backend/app/api/v1/module_application/ai/crud.py +++ b/backend/app/api/v1/module_application/ai/crud.py @@ -12,30 +12,85 @@ class McpCRUD(CRUDBase[McpModel, McpCreateSchema, McpUpdateSchema]): """MCP 服务器数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=McpModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[McpModel]: - """详情""" + """ + 获取MCP服务器详情 + + 参数: + - id (int): MCP服务器ID + + 返回: + - Optional[McpModel]: MCP服务器模型实例(如果存在) + """ return await self.get(id=id) async def get_by_name_crud(self, name: str) -> Optional[McpModel]: - """通过名称获取MCP服务器""" + """ + 通过名称获取MCP服务器 + + 参数: + - name (str): MCP服务器名称 + + 返回: + - Optional[McpModel]: MCP服务器模型实例(如果存在) + """ return await self.get(name=name) async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[McpModel]: - """列表查询""" + """ + 列表查询MCP服务器 + + 参数: + - search (Optional[Dict]): 查询参数字典 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - Sequence[McpModel]: MCP服务器模型实例序列 + """ return await self.list(search=search or {}, order_by=order_by or [{'id': 'asc'}]) async def create_crud(self, data: McpCreateSchema) -> Optional[McpModel]: - """创建""" + """ + 创建MCP服务器 + + 参数: + - data (McpCreateSchema): 创建MCP服务器模型 + + 返回: + - Optional[McpModel]: 创建的MCP服务器模型实例(如果成功) + """ return await self.create(data=data) async def update_crud(self, id: int, data: McpUpdateSchema) -> Optional[McpModel]: - """更新""" + """ + 更新MCP服务器 + + 参数: + - id (int): MCP服务器ID + - data (McpUpdateSchema): 更新MCP服务器模型 + + 返回: + - Optional[McpModel]: 更新的MCP服务器模型实例(如果成功) + """ return await self.update(id=id, data=data) async def delete_crud(self, ids: List[int]) -> None: - """批量删除""" + """ + 批量删除MCP服务器 + + 参数: + - ids (List[int]): MCP服务器ID列表 + + 返回: + - None + """ return await self.delete(ids=ids) diff --git a/backend/app/api/v1/module_application/ai/service.py b/backend/app/api/v1/module_application/ai/service.py index 0a0ef203..c5b87137 100644 --- a/backend/app/api/v1/module_application/ai/service.py +++ b/backend/app/api/v1/module_application/ai/service.py @@ -15,7 +15,16 @@ class McpService: @classmethod async def detail_service(cls, auth: AuthSchema, id: int) -> Dict[str, Any]: - """详情""" + """ + 获取MCP服务器详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): MCP服务器ID + + 返回: + - Dict[str, Any]: MCP服务器详情字典 + """ obj = await McpCRUD(auth).get_by_id_crud(id=id) if not obj: raise CustomException(msg='MCP 服务器不存在') @@ -23,7 +32,17 @@ async def detail_service(cls, auth: AuthSchema, id: int) -> Dict[str, Any]: @classmethod async def list_service(cls, auth: AuthSchema, search: Optional[McpQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict[str, Any]]: - """列表查询""" + """ + 列表查询MCP服务器 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[McpQueryParam]): 查询参数模型 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - List[Dict[str, Any]]: MCP服务器详情字典列表 + """ if order_by: order_by = eval(str(order_by)) obj_list = await McpCRUD(auth).get_list_crud(search=search.__dict__ if search else {}, order_by=order_by) @@ -31,7 +50,16 @@ async def list_service(cls, auth: AuthSchema, search: Optional[McpQueryParam] = @classmethod async def create_service(cls, auth: AuthSchema, data: McpCreateSchema) -> Dict[str, Any]: - """创建""" + """ + 创建MCP服务器 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (McpCreateSchema): 创建MCP服务器模型 + + 返回: + - Dict[str, Any]: 创建的MCP服务器详情字典 + """ obj = await McpCRUD(auth).get_by_name_crud(name=data.name) if obj: raise CustomException(msg='创建失败,MCP 服务器已存在') @@ -40,7 +68,17 @@ async def create_service(cls, auth: AuthSchema, data: McpCreateSchema) -> Dict[s @classmethod async def update_service(cls, auth: AuthSchema, id: int, data: McpUpdateSchema) -> Dict[str, Any]: - """更新""" + """ + 更新MCP服务器 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): MCP服务器ID + - data (McpUpdateSchema): 更新MCP服务器模型 + + 返回: + - Dict[str, Any]: 更新的MCP服务器详情字典 + """ obj = await McpCRUD(auth).get_by_id_crud(id=id) if not obj: raise CustomException(msg='更新失败,该数据不存在') @@ -52,7 +90,16 @@ async def update_service(cls, auth: AuthSchema, id: int, data: McpUpdateSchema) @classmethod async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: - """删除""" + """ + 批量删除MCP服务器 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (List[int]): MCP服务器ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -63,7 +110,15 @@ async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: @classmethod async def chat_query(cls, query: ChatQuerySchema): - """处理聊天查询""" + """ + 处理聊天查询 + + 参数: + - query (ChatQuerySchema): 聊天查询模型 + + 返回: + - AsyncGenerator[str, None]: 异步生成器,每次返回一个聊天响应 + """ # 创建MCP客户端实例 mcp_client = AIClient() try: diff --git a/backend/app/api/v1/module_application/job/controller.py b/backend/app/api/v1/module_application/job/controller.py index 6667ec16..005ea374 100644 --- a/backend/app/api/v1/module_application/job/controller.py +++ b/backend/app/api/v1/module_application/job/controller.py @@ -27,6 +27,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="定时任务ID"), auth: AuthSchema = Depends(AuthPermission(["app:job:query"])) ) -> JSONResponse: + """ + 获取定时任务详情 + + 参数: + - id (int): 定时任务ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含定时任务详情的JSON响应 + """ result_dict = await JobService.get_job_detail_service(id=id, auth=auth) logger.info(f"获取定时任务详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取定时任务详情成功") @@ -37,6 +47,17 @@ async def get_obj_list_controller( search: JobQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:job:query"])) ) -> JSONResponse: + """ + 查询定时任务 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (JobQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含分页后的定时任务列表的JSON响应 + """ result_dict_list = await JobService.get_job_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) logger.info(f"查询定时任务列表成功") @@ -47,6 +68,16 @@ async def create_obj_controller( data: JobCreateSchema, auth: AuthSchema = Depends(AuthPermission(["app:job:create"])) ) -> JSONResponse: + """ + 创建定时任务 + + 参数: + - data (JobCreateSchema): 创建参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建定时任务结果的JSON响应 + """ result_dict = await JobService.create_job_service(auth=auth, data=data) logger.info(f"创建定时任务成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建定时任务成功") @@ -57,6 +88,17 @@ async def update_obj_controller( id: int = Path(..., description="定时任务ID"), auth: AuthSchema = Depends(AuthPermission(["app:job:update"])) ) -> JSONResponse: + """ + 修改定时任务 + + 参数: + - data (JobUpdateSchema): 更新参数模型 + - id (int): 定时任务ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改定时任务结果的JSON响应 + """ result_dict = await JobService.update_job_service(auth=auth, id=id, data=data) logger.info(f"修改定时任务成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改定时任务成功") @@ -66,6 +108,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["app:job:delete"])) ) -> JSONResponse: + """ + 删除定时任务 + + 参数: + - ids (list[int]): ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除定时任务结果的JSON响应 + """ await JobService.delete_job_service(auth=auth, ids=ids) logger.info(f"删除定时任务成功: {ids}") return SuccessResponse(msg="删除定时任务成功") @@ -75,7 +127,16 @@ async def export_obj_list_controller( search: JobQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:job:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出定时任务 + + 参数: + - search (JobQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出定时任务结果的流式响应 + """ result_dict_list = await JobService.get_job_list_service(search=search, auth=auth) export_result = await JobService.export_job_service(data_list=result_dict_list) logger.info('导出定时任务成功') @@ -92,6 +153,15 @@ async def export_obj_list_controller( async def clear_obj_log_controller( auth: AuthSchema = Depends(AuthPermission(["app:job:delete"])) ) -> JSONResponse: + """ + 清空定时任务日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含清空定时任务结果的JSON响应 + """ await JobService.clear_job_service(auth=auth) logger.info(f"清空定时任务成功") return SuccessResponse(msg="清空定时任务成功") @@ -102,12 +172,29 @@ async def option_obj_controller( option: int = Body(..., description="操作类型 1: 暂停 2: 恢复 3: 重启"), auth: AuthSchema = Depends(AuthPermission(["app:job:update"])) ) -> JSONResponse: + """ + 暂停/恢复/重启定时任务 + + 参数: + - id (int): 定时任务ID + - option (int): 操作类型 1: 暂停 2: 恢复 3: 重启 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含操作定时任务结果的JSON响应 + """ await JobService.option_job_service(auth=auth, id=id, option=option) logger.info(f"操作定时任务成功: {id}") return SuccessResponse(msg="操作定时任务成功") @JobRouter.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(["app:job:query"]))]) async def get_job_log_controller(): + """ + 获取定时任务日志 + + 返回: + - JSONResponse: 获取定时任务日志的JSON响应 + """ data = [ { "id": i.id, @@ -136,6 +223,16 @@ async def get_job_log_detail_controller( id: int = Path(..., description="定时任务日志ID"), auth: AuthSchema = Depends(AuthPermission(["app:job:query"])) ) -> JSONResponse: + """ + 获取定时任务日志详情 + + 参数: + - id (int): 定时任务日志ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 获取定时任务日志详情的JSON响应 + """ result_dict = await JobLogService.get_job_log_detail_service(id=id, auth=auth) logger.info(f"获取定时任务日志详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取定时任务日志详情成功") @@ -147,6 +244,17 @@ async def get_job_log_list_controller( search: JobLogQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:job:query"])) ) -> JSONResponse: + """ + 查询定时任务日志 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (JobLogQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 查询定时任务日志列表的JSON响应 + """ order_by = [{"create_time": "desc"}] result_dict_list = await JobLogService.get_job_log_list_service(auth=auth, search=search, order_by=order_by) result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size) @@ -159,6 +267,16 @@ async def delete_job_log_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["app:job:delete"])) ) -> JSONResponse: + """ + 删除定时任务日志 + + 参数: + - ids (list[int]): ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除定时任务日志结果的JSON响应 + """ await JobLogService.delete_job_log_service(auth=auth, ids=ids) logger.info(f"删除定时任务日志成功: {ids}") return SuccessResponse(msg="删除定时任务日志成功") @@ -168,6 +286,15 @@ async def delete_job_log_controller( async def clear_job_log_controller( auth: AuthSchema = Depends(AuthPermission(["app:job:delete"])) ) -> JSONResponse: + """ + 清空定时任务日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含清空定时任务日志结果的JSON响应 + """ await JobLogService.clear_job_log_service(auth=auth) logger.info(f"清空定时任务日志成功") return SuccessResponse(msg="清空定时任务日志成功") @@ -178,7 +305,16 @@ async def export_job_log_list_controller( search: JobLogQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:job:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出定时任务日志 + + 参数: + - search (JobLogQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出定时任务日志结果的流式响应 + """ result_dict_list = await JobLogService.get_job_log_list_service(search=search, auth=auth) export_result = await JobLogService.export_job_log_service(data_list=result_dict_list) logger.info('导出定时任务日志成功') diff --git a/backend/app/api/v1/module_application/job/crud.py b/backend/app/api/v1/module_application/job/crud.py index 3f18a7d9..aa0aeda2 100644 --- a/backend/app/api/v1/module_application/job/crud.py +++ b/backend/app/api/v1/module_application/job/crud.py @@ -12,36 +12,91 @@ class JobCRUD(CRUDBase[JobModel, JobCreateSchema, JobUpdateSchema]): """定时任务数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化定时任务CRUD""" + """ + 初始化定时任务CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=JobModel, auth=auth) async def get_obj_by_id_crud(self, id: int) -> Optional[JobModel]: - """获取定时任务详情""" + """ + 获取定时任务详情 + + 参数: + - id (int): 定时任务ID + + 返回: + - Optional[JobModel]: 定时任务模型,如果不存在则为None + """ return await self.get(id=id) async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[JobModel]: - """获取定时任务列表""" + """ + 获取定时任务列表 + + 参数: + - search (Optional[Dict]): 查询参数字典 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - Sequence[JobModel]: 定时任务模型序列 + """ return await self.list(search=search, order_by=order_by) async def create_obj_crud(self, data: JobCreateSchema) -> Optional[JobModel]: - """创定时任务""" + """ + 创建定时任务 + + 参数: + - data (JobCreateSchema): 创建定时任务模型 + + 返回: + - Optional[JobModel]: 创建的定时任务模型,如果创建失败则为None + """ return await self.create(data=data) async def update_obj_crud(self, id: int, data: JobUpdateSchema) -> Optional[JobModel]: - """更新定时任务""" + """ + 更新定时任务 + + 参数: + - id (int): 定时任务ID + - data (JobUpdateSchema): 更新定时任务模型 + + 返回: + - Optional[JobModel]: 更新后的定时任务模型,如果更新失败则为None + """ return await self.update(id=id, data=data) async def delete_obj_crud(self, ids: List[int]) -> None: - """删除定时任务""" + """ + 删除定时任务 + + 参数: + - ids (List[int]): 定时任务ID列表 + """ return await self.delete(ids=ids) async def set_obj_field_crud(self, ids: List[int], **kwargs) -> None: - """设置定时任务的可用状态""" + """ + 设置定时任务的可用状态 + + 参数: + - ids (List[int]): 定时任务ID列表 + - kwargs: 其他要设置的字段,例如 available=True 或 available=False + """ return await self.set(ids=ids, **kwargs) async def clear_obj_crud(self) -> None: - """清除定时任务日志""" + """ + 清除定时任务日志 + + 注意: + - 此操作会删除所有定时任务日志,请谨慎操作 + """ return await self.clear() @@ -49,18 +104,45 @@ class JobLogCRUD(CRUDBase[JobLogModel, JobLogCreateSchema, JobLogUpdateSchema]): """定时任务日志数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化定时任务日志CRUD""" + """ + 初始化定时任务日志CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=JobLogModel, auth=auth) async def get_obj_log_by_id_crud(self, id: int) -> Optional[JobLogModel]: - """获取定时任务日志详情""" + """ + 获取定时任务日志详情 + + 参数: + - id (int): 定时任务日志ID + + 返回: + - Optional[JobLogModel]: 定时任务日志模型,如果不存在则为None + """ return await self.get(id=id) async def get_obj_log_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[JobLogModel]: - """获取定时任务日志列表""" + """ + 获取定时任务日志列表 + + 参数: + - search (Optional[Dict]): 查询参数字典 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - Sequence[JobLogModel]: 定时任务日志模型序列 + """ return await self.list(search=search, order_by=order_by) async def delete_obj_log_crud(self, ids: List[int]) -> None: - """删除定时任务日志""" + """ + 删除定时任务日志 + + 参数: + - ids (List[int]): 定时任务日志ID列表 + """ return await self.delete(ids=ids) \ No newline at end of file diff --git a/backend/app/api/v1/module_application/job/service.py b/backend/app/api/v1/module_application/job/service.py index a15be98c..59d13d08 100644 --- a/backend/app/api/v1/module_application/job/service.py +++ b/backend/app/api/v1/module_application/job/service.py @@ -19,16 +19,47 @@ class JobService: @classmethod async def get_job_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取定时任务详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 定时任务ID + + 返回: + - Dict: 定时任务详情字典 + """ obj = await JobCRUD(auth).get_obj_by_id_crud(id=id) return JobOutSchema.model_validate(obj).model_dump() @classmethod async def get_job_list_service(cls, auth: AuthSchema, search: Optional[JobQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """ + 获取定时任务列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[JobQueryParam]): 查询参数模型 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - List[Dict]: 定时任务详情字典列表 + """ obj_list = await JobCRUD(auth).get_obj_list_crud(search=search.__dict__, order_by=order_by) return [JobOutSchema.model_validate(obj).model_dump() for obj in obj_list] @classmethod async def create_job_service(cls, auth: AuthSchema, data: JobCreateSchema) -> Dict: + """ + 创建定时任务 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (JobCreateSchema): 定时任务创建模型 + + 返回: + - Dict: 定时任务详情字典 + """ exist_obj = await JobCRUD(auth).get(name=data.name) if exist_obj: raise CustomException(msg='创建失败,该定时任务已存在') @@ -41,6 +72,17 @@ async def create_job_service(cls, auth: AuthSchema, data: JobCreateSchema) -> Di @classmethod async def update_job_service(cls, auth: AuthSchema, id:int, data: JobUpdateSchema) -> Dict: + """ + 更新定时任务 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 定时任务ID + - data (JobUpdateSchema): 定时任务更新模型 + + 返回: + - Dict: 定时任务详情字典 + """ exist_obj = await JobCRUD(auth).get_obj_by_id_crud(id=id) if not exist_obj: raise CustomException(msg='更新失败,该定时任务不存在') @@ -54,6 +96,13 @@ async def update_job_service(cls, auth: AuthSchema, id:int, data: JobUpdateSchem @classmethod async def delete_job_service(cls, auth: AuthSchema, ids: list[int]) -> None: + """ + 删除定时任务 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 定时任务ID列表 + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -66,11 +115,25 @@ async def delete_job_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def clear_job_service(cls, auth: AuthSchema) -> None: + """ + 清空所有定时任务 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ SchedulerUtil().clear_jobs() await JobCRUD(auth).clear_obj_crud() @classmethod async def option_job_service(cls, auth: AuthSchema, id: int, option: int) -> None: + """ + 操作定时任务 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 定时任务ID + - option (int): 操作类型, 1: 暂停 2: 恢复 3: 重启 + """ # 1: 暂停 2: 恢复 3: 重启 obj = await JobCRUD(auth).get_obj_by_id_crud(id=id) if not obj: @@ -87,7 +150,15 @@ async def option_job_service(cls, auth: AuthSchema, id: int, option: int) -> Non @classmethod async def export_job_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出公告列表""" + """ + 导出定时任务列表 + + 参数: + - data_list (List[Dict[str, Any]]): 定时任务列表 + + 返回: + - bytes: Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'name': '任务名称', @@ -125,30 +196,60 @@ class JobLogService: @classmethod async def get_job_log_detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取定时任务日志详情""" + """ + 获取定时任务日志详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 定时任务日志ID + + 返回: + - Dict: 定时任务日志详情字典 + """ obj = await JobLogCRUD(auth).get_obj_log_by_id_crud(id=id) return JobLogOutSchema.model_validate(obj).model_dump() @classmethod async def get_job_log_list_service(cls, auth: AuthSchema, search: Optional[JobLogQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """获取定时任务日志列表""" + """ + 获取定时任务日志列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[JobLogQueryParam]): 查询参数模型, 包含分页信息和查询条件 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表, 每个元素为一个字典, 包含字段名和排序方向 + + 返回: + - List[Dict]: 定时任务日志详情字典列表 + """ obj_list = await JobLogCRUD(auth).get_obj_log_list_crud(search=search.__dict__, order_by=order_by) return [JobLogOutSchema.model_validate(obj).model_dump() for obj in obj_list] @classmethod async def delete_job_log_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除定时任务日志""" + """ + 删除定时任务日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 定时任务日志ID列表 + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: exist_obj = await JobLogCRUD(auth).get_obj_log_by_id_crud(id=id) if not exist_obj: - raise CustomException(msg='删除失败,该定时任务日志不存在') + raise CustomException(msg=f'删除失败,该定时任务日志ID为{id}的记录不存在') await JobLogCRUD(auth).delete_obj_log_crud(ids=ids) @classmethod async def clear_job_log_service(cls, auth: AuthSchema) -> None: - """清空定时任务日志""" + """ + 清空定时任务日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ # 获取所有日志ID并批量删除 all_logs = await JobLogCRUD(auth).get_obj_log_list_crud() if all_logs: @@ -157,7 +258,15 @@ async def clear_job_log_service(cls, auth: AuthSchema) -> None: @classmethod async def export_job_log_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出定时任务日志列表""" + """ + 导出定时任务日志列表 + + 参数: + - data_list (List[Dict[str, Any]]): 定时任务日志列表 + + 返回: + - bytes: Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'job_name': '任务名称', diff --git a/backend/app/api/v1/module_application/myapp/controller.py b/backend/app/api/v1/module_application/myapp/controller.py index d3acc958..af1a621f 100644 --- a/backend/app/api/v1/module_application/myapp/controller.py +++ b/backend/app/api/v1/module_application/myapp/controller.py @@ -26,6 +26,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="应用ID"), auth: AuthSchema = Depends(AuthPermission(["app:myapp:query"])) ) -> JSONResponse: + """ + 获取应用详情 + + 参数: + - id (int): 应用ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含应用详情的JSON响应 + """ result_dict = await ApplicationService.detail_service(id=id, auth=auth) logger.info(f"获取应用详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取应用详情成功") @@ -36,6 +46,17 @@ async def get_obj_list_controller( search: ApplicationQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["app:myapp:query"])) ) -> JSONResponse: + """ + 查询应用列表 + + 参数: + - page (PaginationQueryParam): 分页参数模型 + - search (ApplicationQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含应用列表的JSON响应 + """ result_dict_list = await ApplicationService.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) logger.info(f"查询应用列表成功") @@ -46,6 +67,16 @@ async def create_obj_controller( data: ApplicationCreateSchema, auth: AuthSchema = Depends(AuthPermission(["app:myapp:create"])) ) -> JSONResponse: + """ + 创建应用 + + 参数: + - data (ApplicationCreateSchema): 应用创建模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建应用详情的JSON响应 + """ result_dict = await ApplicationService.create_service(auth=auth, data=data) logger.info(f"创建应用成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建应用成功") @@ -56,6 +87,17 @@ async def update_obj_controller( id: int = Path(..., description="应用ID"), auth: AuthSchema = Depends(AuthPermission(["app:myapp:update"])) ) -> JSONResponse: + """ + 修改应用 + + 参数: + - data (ApplicationUpdateSchema): 应用更新模型 + - id (int): 应用ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改应用详情的JSON响应 + """ result_dict = await ApplicationService.update_service(auth=auth, id=id, data=data) logger.info(f"修改应用成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改应用成功") @@ -65,6 +107,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["app:myapp:delete"])) ) -> JSONResponse: + """ + 删除应用 + + 参数: + - ids (list[int]): 应用ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除应用详情的JSON响应 + """ await ApplicationService.delete_service(auth=auth, ids=ids) logger.info(f"删除应用成功: {ids}") return SuccessResponse(msg="删除应用成功") @@ -74,6 +126,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["app:myapp:patch"])) ) -> JSONResponse: + """ + 批量修改应用状态 + + 参数: + - data (BatchSetAvailable): 批量修改应用状态模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 批量修改应用状态成功 + """ await ApplicationService.set_available_service(auth=auth, data=data) logger.info(f"批量修改应用状态成功: {data.ids}") return SuccessResponse(msg="批量修改应用状态成功") \ No newline at end of file diff --git a/backend/app/api/v1/module_application/myapp/crud.py b/backend/app/api/v1/module_application/myapp/crud.py index d6432293..2e070c56 100644 --- a/backend/app/api/v1/module_application/myapp/crud.py +++ b/backend/app/api/v1/module_application/myapp/crud.py @@ -12,30 +12,80 @@ class ApplicationCRUD(CRUDBase[ApplicationModel, ApplicationCreateSchema, Applic """应用系统数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化应用CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=ApplicationModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[ApplicationModel]: - """获取应用详情""" + """ + 根据id获取应用详情 + + 参数: + - id (int): 应用ID + + 返回: + - Optional[ApplicationModel]: 应用详情,如果不存在则为None + """ return await self.get(id=id) async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[ApplicationModel]: - """列表查询""" + """ + 列表查询应用 + + 参数: + - search (Optional[Dict]): 查询参数,默认None + - order_by (Optional[List[Dict[str, str]]]): 排序参数,默认None + + 返回: + - Sequence[ApplicationModel]: 应用列表 + """ return await self.list(search=search, order_by=order_by) async def create_crud(self, data: ApplicationCreateSchema) -> Optional[ApplicationModel]: - """创建应用""" + """ + 创建应用 + + 参数: + - data (ApplicationCreateSchema): 应用创建模型 + + 返回: + - Optional[ApplicationModel]: 创建的应用详情,如果创建失败则为None + """ return await self.create(data=data) async def update_crud(self, id: int, data: ApplicationUpdateSchema) -> Optional[ApplicationModel]: - """更新应用""" + """ + 更新应用 + + 参数: + - id (int): 应用ID + - data (ApplicationUpdateSchema): 应用更新模型 + + 返回: + - Optional[ApplicationModel]: 更新后的应用详情,如果更新失败则为None + """ return await self.update(id=id, data=data) async def delete_crud(self, ids: List[int]) -> None: - """批量删除应用""" + """ + 批量删除应用 + + 参数: + - ids (List[int]): 应用ID列表 + """ return await self.delete(ids=ids) async def set_available_crud(self, ids: List[int], status: bool) -> None: - """批量设置可用状态""" + """ + 批量设置可用状态 + + 参数: + - ids (List[int]): 应用ID列表 + - status (bool): 可用状态,True为可用,False为不可用 + """ return await self.set(ids=ids, status=status) \ No newline at end of file diff --git a/backend/app/api/v1/module_application/myapp/service.py b/backend/app/api/v1/module_application/myapp/service.py index 6a1581d4..6f5a96be 100644 --- a/backend/app/api/v1/module_application/myapp/service.py +++ b/backend/app/api/v1/module_application/myapp/service.py @@ -18,7 +18,16 @@ class ApplicationService: @classmethod async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取应用详情""" + """ + 获取应用详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 应用ID + + 返回: + - Dict: 应用详情字典 + """ obj = await ApplicationCRUD(auth).get_by_id_crud(id=id) if not obj: raise CustomException(msg='应用不存在') @@ -26,7 +35,17 @@ async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: @classmethod async def list_service(cls, auth: AuthSchema, search: Optional[ApplicationQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """应用列表查询""" + """ + 获取应用列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[ApplicationQueryParam]): 查询参数模型 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - List[Dict]: 应用详情字典列表 + """ if order_by: order_by = eval(order_by) if isinstance(order_by, str) else order_by @@ -37,7 +56,16 @@ async def list_service(cls, auth: AuthSchema, search: Optional[ApplicationQueryP @classmethod async def create_service(cls, auth: AuthSchema, data: ApplicationCreateSchema) -> Dict: - """创建应用""" + """ + 创建应用 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (ApplicationCreateSchema): 应用创建模型 + + 返回: + - Dict: 应用详情字典 + """ # 检查名称是否重复 obj = await ApplicationCRUD(auth).get(name=data.name) if obj: @@ -48,7 +76,17 @@ async def create_service(cls, auth: AuthSchema, data: ApplicationCreateSchema) - @classmethod async def update_service(cls, auth: AuthSchema, id: int, data: ApplicationUpdateSchema) -> Dict: - """更新应用""" + """ + 更新应用 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 应用ID + - data (ApplicationUpdateSchema): 应用更新模型 + + 返回: + - Dict: 应用详情字典 + """ obj = await ApplicationCRUD(auth).get_by_id_crud(id=id) if not obj: raise CustomException(msg='更新失败,该应用不存在') @@ -63,7 +101,16 @@ async def update_service(cls, auth: AuthSchema, id: int, data: ApplicationUpdate @classmethod async def delete_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除应用""" + """ + 删除应用 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 应用ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -74,5 +121,14 @@ async def delete_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """批量设置应用状态""" + """ + 批量设置应用状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置应用状态模型 + + 返回: + - None + """ await ApplicationCRUD(auth).set_available_crud(ids=data.ids, status=data.status) \ No newline at end of file diff --git a/backend/app/api/v1/module_common/file/controller.py b/backend/app/api/v1/module_common/file/controller.py index 3f2a183c..4f42115b 100644 --- a/backend/app/api/v1/module_common/file/controller.py +++ b/backend/app/api/v1/module_common/file/controller.py @@ -18,6 +18,16 @@ async def upload_controller( file: UploadFile, request: Request, ) -> JSONResponse: + """ + 上传文件 + + 参数: + - file (UploadFile): 上传的文件 + - request (Request): 请求对象 + + 返回: + - JSONResponse: 包含上传文件详情的JSON响应 + """ result_dict = await FileService.upload_service(base_url=str(request.base_url), file=file) logger.info(f"上传文件成功 {result_dict}") return SuccessResponse(data=result_dict, msg="上传文件成功") @@ -28,7 +38,17 @@ async def download_controller( file_path: str = Body(..., description="文件路径"), delete: bool = Body(False, description="是否删除文件"), ) -> FileResponse: + """ + 下载文件 + + 参数: + - background_tasks (BackgroundTasks): 后台任务对象 + - file_path (str): 文件路径 + - delete (bool): 是否删除文件 + 返回: + - FileResponse: 包含下载文件的响应 + """ result = await FileService.download_service(file_path=file_path) if delete: background_tasks.add_task(UploadUtil.delete_file, Path(file_path)) diff --git a/backend/app/api/v1/module_common/file/service.py b/backend/app/api/v1/module_common/file/service.py index 89fa6b17..30c8ad94 100644 --- a/backend/app/api/v1/module_common/file/service.py +++ b/backend/app/api/v1/module_common/file/service.py @@ -15,13 +15,24 @@ class FileService: @classmethod async def upload_service(cls, base_url: str, file: UploadFile, upload_type: str = 'local') -> Dict: - """ 上传文件""" + """ + 上传文件。 + + 参数: + - base_url (str): 基础访问 URL。 + - file (UploadFile): 上传文件对象。 + - upload_type (str): 上传类型,'local' 或 'oss',默认 'local'。 + + 返回: + - Dict: 上传响应字典。 + + 异常: + - CustomException: 当未选择文件或上传类型错误时抛出。 + """ if not file: raise CustomException(msg="请选择要上传的文件") if upload_type == 'local': filename, filepath, file_url = await UploadUtil.upload_file(file=file, base_url=base_url) - elif upload_type == 'oss': - filename, filepath, file_url = await UploadUtil.upload_file_oss(file=file, oss_folder=file.filename) else: raise CustomException(msg="上传类型错误") @@ -36,9 +47,16 @@ async def upload_service(cls, base_url: str, file: UploadFile, upload_type: str @classmethod async def download_service(cls, file_path: str) -> DownloadFileSchema: """ - 下载文件 - :param file_path: 文件路径 - :return: 结果 + 下载文件。 + + 参数: + - file_path (str): 文件路径。 + + 返回: + - DownloadFileSchema: 下载文件响应对象。 + + 异常: + - CustomException: 当未选择文件或文件不存在时抛出。 """ if not file_path: raise CustomException(msg="请选择要下载的文件") diff --git a/backend/app/api/v1/module_generator/demo/controller.py b/backend/app/api/v1/module_generator/demo/controller.py index cf68bc87..e283d75a 100644 --- a/backend/app/api/v1/module_generator/demo/controller.py +++ b/backend/app/api/v1/module_generator/demo/controller.py @@ -28,6 +28,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="示例ID"), auth: AuthSchema = Depends(AuthPermission(["generator:demo:query"])) ) -> JSONResponse: + """ + 获取示例详情 + + 参数: + - id (int): 示例ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含示例详情的JSON响应 + """ result_dict = await DemoService.detail_service(id=id, auth=auth) logger.info(f"获取示例详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取示例详情成功") @@ -38,6 +48,17 @@ async def get_obj_list_controller( search: DemoQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["generator:demo:query"])) ) -> JSONResponse: + """ + 查询示例列表 + + 参数: + - page (PaginationQueryParam): 分页查询参数 + - search (DemoQueryParam): 查询参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - 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) logger.info("查询示例列表成功") @@ -48,6 +69,16 @@ async def create_obj_controller( data: DemoCreateSchema, auth: AuthSchema = Depends(AuthPermission(["generator:demo:create"])) ) -> JSONResponse: + """ + 创建示例 + + 参数: + - data (DemoCreateSchema): 示例创建模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建示例详情的JSON响应 + """ result_dict = await DemoService.create_service(auth=auth, data=data) logger.info(f"创建示例成功: {result_dict.get('name')}") return SuccessResponse(data=result_dict, msg="创建示例成功") @@ -58,6 +89,17 @@ async def update_obj_controller( id: int = Path(..., description="示例ID"), auth: AuthSchema = Depends(AuthPermission(["generator:demo:update"])) ) -> JSONResponse: + """ + 修改示例 + + 参数: + - data (DemoUpdateSchema): 示例更新模型 + - id (int): 示例ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改示例详情的JSON响应 + """ result_dict = await DemoService.update_service(auth=auth, id=id, data=data) logger.info(f"修改示例成功: {result_dict.get('name')}") return SuccessResponse(data=result_dict, msg="修改示例成功") @@ -67,6 +109,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["generator:demo:delete"])) ) -> JSONResponse: + """ + 删除示例 + + 参数: + - ids (list[int]): 示例ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除示例详情的JSON响应 + """ await DemoService.delete_service(auth=auth, ids=ids) logger.info(f"删除示例成功: {ids}") return SuccessResponse(msg="删除示例成功") @@ -76,6 +128,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["generator:demo:patch"])) ) -> JSONResponse: + """ + 批量修改示例状态 + + 参数: + - data (BatchSetAvailable): 批量修改示例状态模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含批量修改示例状态详情的JSON响应 + """ await DemoService.set_available_service(auth=auth, data=data) logger.info(f"批量修改示例状态成功: {data.ids}") return SuccessResponse(msg="批量修改示例状态成功") @@ -85,7 +147,16 @@ async def export_obj_list_controller( search: DemoQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["generator:demo:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出示例 + + 参数: + - search (DemoQueryParam): 查询参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含示例列表的Excel文件流响应 + """ result_dict_list = await DemoService.list_service(search=search, auth=auth) export_result = await DemoService.batch_export_service(obj_list=result_dict_list) logger.info('导出示例成功') @@ -103,12 +174,28 @@ async def import_obj_list_controller( file: UploadFile, auth: AuthSchema = Depends(AuthPermission(["generator:demo:import"])) ) -> JSONResponse: + """ + 导入示例 + + 参数: + - file (UploadFile): 导入的Excel文件 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含导入示例详情的JSON响应 + """ batch_import_result = await DemoService.batch_import_service(file=file, auth=auth, update_support=True) logger.info(f"导入示例成功: {batch_import_result}") return SuccessResponse(data=batch_import_result, msg="导入示例成功") @DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["generator:demo:download"]))]) async def export_obj_template_controller() -> StreamingResponse: + """ + 获取示例导入模板 + + 返回: + - StreamingResponse: 包含示例导入模板的Excel文件流响应 + """ example_import_template_result = await DemoService.import_template_download_service() logger.info('获取示例导入模板成功') diff --git a/backend/app/api/v1/module_generator/demo/crud.py b/backend/app/api/v1/module_generator/demo/crud.py index 1766e8b8..acb90812 100644 --- a/backend/app/api/v1/module_generator/demo/crud.py +++ b/backend/app/api/v1/module_generator/demo/crud.py @@ -12,29 +12,85 @@ class DemoCRUD(CRUDBase[DemoModel, DemoCreateSchema, DemoUpdateSchema]): """示例数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化CRUD数据层 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ super().__init__(model=DemoModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[DemoModel]: - """详情""" + """ + 详情 + + 参数: + - id (int): 示例ID + + 返回: + - Optional[DemoModel]: 示例模型实例或None + """ return await self.get(id=id) async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DemoModel]: - """列表查询""" + """ + 列表查询 + + 参数: + - search (Optional[Dict]): 查询参数 + - order_by (Optional[List[Dict[str, str]]]): 排序参数 + + 返回: + - Sequence[DemoModel]: 示例模型实例序列 + """ return await self.list(search=search, order_by=order_by) async def create_crud(self, data: DemoCreateSchema) -> Optional[DemoModel]: - """创建""" + """ + 创建 + + 参数: + - data (DemoCreateSchema): 示例创建模型 + + 返回: + - Optional[DemoModel]: 示例模型实例或None + """ return await self.create(data=data) async def update_crud(self, id: int, data: DemoUpdateSchema) -> Optional[DemoModel]: - """更新""" + """ + 更新 + + 参数: + - id (int): 示例ID + - data (DemoUpdateSchema): 示例更新模型 + + 返回: + - Optional[DemoModel]: 示例模型实例或None + """ return await self.update(id=id, data=data) async def delete_crud(self, ids: List[int]) -> None: - """批量删除""" + """ + 批量删除 + + 参数: + - ids (List[int]): 示例ID列表 + + 返回: + - None + """ return await self.delete(ids=ids) async def set_available_crud(self, ids: List[int], status: bool) -> None: - """批量设置可用状态""" + """ + 批量设置可用状态 + + 参数: + - ids (List[int]): 示例ID列表 + - status (bool): 可用状态 + + 返回: + - None + """ return await self.set(ids=ids, status=status) \ No newline at end of file diff --git a/backend/app/api/v1/module_generator/demo/service.py b/backend/app/api/v1/module_generator/demo/service.py index 9fa031a1..e7f9f234 100644 --- a/backend/app/api/v1/module_generator/demo/service.py +++ b/backend/app/api/v1/module_generator/demo/service.py @@ -8,7 +8,6 @@ from app.core.base_schema import BatchSetAvailable from app.core.exceptions import CustomException from app.utils.excel_util import ExcelUtil -from app.common.response import ErrorResponse from app.core.logger import logger from app.api.v1.module_system.auth.schema import AuthSchema from .schema import DemoCreateSchema, DemoUpdateSchema, DemoOutSchema @@ -23,7 +22,16 @@ class DemoService: @classmethod async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """详情""" + """ + 详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 示例ID + + 返回: + - Dict: 示例模型实例字典 + """ obj = await DemoCRUD(auth).get_by_id_crud(id=id) if not obj: raise CustomException(msg="该数据不存在") @@ -31,14 +39,33 @@ async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: @classmethod async def list_service(cls, auth: AuthSchema, search: Optional[DemoQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """列表查询""" + """ + 列表查询 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[DemoQueryParam]): 查询参数 + - order_by (Optional[List[Dict[str, str]]]): 排序参数 + + 返回: + - List[Dict]: 示例模型实例字典列表 + """ search_dict = search.__dict__ if search else None 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 create_service(cls, auth: AuthSchema, data: DemoCreateSchema) -> Dict: - """创建""" + """ + 创建 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (DemoCreateSchema): 示例创建模型 + + 返回: + - Dict: 示例模型实例字典 + """ obj = await DemoCRUD(auth).get(name=data.name) if obj: raise CustomException(msg='创建失败,名称已存在') @@ -47,7 +74,17 @@ async def create_service(cls, auth: AuthSchema, data: DemoCreateSchema) -> Dict: @classmethod async def update_service(cls, auth: AuthSchema, id: int, data: DemoUpdateSchema) -> Dict: - """更新""" + """ + 更新 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 示例ID + - data (DemoUpdateSchema): 示例更新模型 + + 返回: + - Dict: 示例模型实例字典 + """ # 检查数据是否存在 obj = await DemoCRUD(auth).get_by_id_crud(id=id) if not obj: @@ -63,7 +100,16 @@ async def update_service(cls, auth: AuthSchema, id: int, data: DemoUpdateSchema) @classmethod async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: - """删除""" + """ + 删除 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (List[int]): 示例ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') @@ -77,12 +123,29 @@ async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: @classmethod async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """批量设置状态""" + """ + 批量设置状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置状态模型 + + 返回: + - None + """ await DemoCRUD(auth).set_available_crud(ids=data.ids, status=data.status) @classmethod async def batch_export_service(cls, obj_list: List[Dict[str, Any]]) -> bytes: - """批量导出""" + """ + 批量导出 + + 参数: + - obj_list (List[Dict[str, Any]]): 示例模型实例字典列表 + + 返回: + - bytes: Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'name': '名称', @@ -109,7 +172,17 @@ async def batch_export_service(cls, obj_list: List[Dict[str, Any]]) -> bytes: @classmethod async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: - """批量导入""" + """ + 批量导入 + + 参数: + - auth (AuthSchema): 认证信息模型 + - file (UploadFile): 上传的Excel文件 + - update_support (bool): 是否支持更新存在数据 + + 返回: + - str: 导入结果信息 + """ header_dict = { '名称': 'name', @@ -137,49 +210,45 @@ async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_s # 验证必填字段 required_fields = ['name', 'status'] for field in required_fields: - if df[field].isnull().any(): - missing_rows = df[df[field].isnull()].index.tolist() - raise CustomException(msg=f"{[k for k,v in header_dict.items() if v == field][0]}不能为空,第{[i+1 for i in missing_rows]}行") + missing_rows = df[df[field].isnull()].index.tolist() + raise CustomException(msg=f"{[k for k,v in header_dict.items() if v == field][0]}不能为空,第{[i+1 for i in missing_rows]}行") error_msgs = [] success_count = 0 + count = 0 # 处理每一行数据 for index, row in df.iterrows(): + count += 1 try: # 数据转换前的类型检查 - try: - name = str(row['name']) - except ValueError: - error_msgs.append(f"第{index+1}行: 名称必须是字符串") - continue try: status = True if row['status'] == '正常' else False except ValueError: - error_msgs.append(f"第{index+1}行: 状态必须是'正常'或'停用'") + error_msgs.append(f"第{count}行: 状态必须是'正常'或'停用'") continue # 构建用户数据 data = { - "name": name, + "name": str(row['name']), "status": status, - "description": str(row['description']).strip() if not pd.isna(row['description']) else None, + "description": str(row['description']), } # 处理用户导入 - exists_user = await DemoCRUD(auth).get(name=data["name"]) - if exists_user: + exists_obj = await DemoCRUD(auth).get(name=data["name"]) + if exists_obj: if update_support: - await DemoCRUD(auth).update(id=exists_user.id, data=data) + await DemoCRUD(auth).update(id=exists_obj.id, data=data) success_count += 1 else: - error_msgs.append(f"第{index+1}行: 用户 {data['username']} 已存在") + error_msgs.append(f"第{count}行: 对象 {data['name']} 已存在") else: await DemoCRUD(auth).create(data=data) success_count += 1 except Exception as e: - error_msgs.append(f"第{index+1}行: {str(e)}") + error_msgs.append(f"第{count}行: {str(e)}") continue # 返回详细的导入结果 @@ -194,7 +263,12 @@ async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_s @classmethod async def import_template_download_service(cls) -> bytes: - """下载导入模板""" + """ + 下载导入模板 + + 返回: + - bytes: Excel文件字节流 + """ header_list = ['名称', '状态', '描述'] selector_header_list = ['状态'] option_list = [{'状态': ['正常', '停用']}] diff --git a/backend/app/api/v1/module_generator/gencode/controller.py b/backend/app/api/v1/module_generator/gencode/controller.py index 6eb2c69e..fa068790 100644 --- a/backend/app/api/v1/module_generator/gencode/controller.py +++ b/backend/app/api/v1/module_generator/gencode/controller.py @@ -27,6 +27,17 @@ async def gen_table_list_controller( search: GenTableQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) ) -> JSONResponse: + """ + 查询代码生成业务表列表 + + 参数: + - page (PaginationQueryParam): 分页查询参数 + - search (GenTableQueryParam): 搜索参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含查询结果和分页信息的JSON响应 + """ result_dict_list = await GenTableService.get_gen_table_list_service(auth=auth, search=search) result_dict = await PaginationService.paginate(data_list=result_dict_list, page_no=page.page_no, page_size=page.page_size) logger.info('获取代码生成业务表列表成功') @@ -39,6 +50,17 @@ async def get_gen_db_table_list_controller( search: GenTableQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["generator:dblist:query"])) ) -> JSONResponse: + """ + 查询数据库表列表 + + 参数: + - page (PaginationQueryParam): 分页查询参数 + - search (GenTableQueryParam): 搜索参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含查询结果和分页信息的JSON响应 + """ result_dict_list = await GenTableService.get_gen_db_table_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) logger.info('获取数据库表列表成功') @@ -50,6 +72,16 @@ async def import_gen_table_controller( table_names: List[str] = Body(..., description="表名列表"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:import"])), ) -> JSONResponse: + """ + 导入表结构 + + 参数: + - table_names (List[str]): 表名列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含导入结果和导入的表结构列表的JSON响应 + """ add_gen_table_list = await GenTableService.get_gen_db_table_list_by_name_service(auth, table_names) result = await GenTableService.import_gen_table_service(auth, add_gen_table_list) logger.info('导入表结构成功') @@ -61,6 +93,16 @@ async def gen_table_detail_controller( table_id: int = Path(..., description="业务表ID"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) ) -> JSONResponse: + """ + 获取业务表详细信息 + + 参数: + - table_id (int): 业务表ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含业务表详细信息的JSON响应 + """ gen_table = await GenTableService.get_gen_table_by_id_service(auth, table_id) gen_tables = await GenTableService.get_gen_table_all_service(auth) gen_table_detail_result = dict(info=gen_table.model_dump(), rows=gen_table.model_dump()['columns'], tables=[gen_table.model_dump() for gen_table in gen_tables]) @@ -73,6 +115,16 @@ async def create_table_controller( sql: str = Body(..., description="SQL语句:CREATE TABLE user_demo (\n id INTEGER NOT NULL PRIMARY KEY,\n username VARCHAR(64) NOT NULL UNIQUE,\n);"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:create"])), ) -> JSONResponse: + """ + 创建表结构 + + 参数: + - sql (str): SQL语句,用于创建表结构 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建结果的JSON响应 + """ result = await GenTableService.create_table_service(auth, sql) logger.info('创建表结构成功') return SuccessResponse(msg="创建表结构成功", data=result) @@ -84,6 +136,17 @@ async def update_gen_table_controller( data: GenTableSchema = Body(..., description="业务表信息"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:update"])), ) -> JSONResponse: + """ + 编辑业务表信息 + + 参数: + - table_id (int): 业务表ID + - data (GenTableSchema): 业务表信息模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含编辑结果的JSON响应 + """ await GenTableService.validate_edit(data) result_dict = await GenTableService.update_gen_table_service(auth, data, table_id) logger.info('编辑业务表信息成功') @@ -95,6 +158,16 @@ async def delete_gen_table_controller( ids: List[int] = Body(..., description="业务表ID列表"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:delete"])) ) -> JSONResponse: + """ + 删除业务表信息 + + 参数: + - ids (List[int]): 业务表ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除结果的JSON响应 + """ result = await GenTableService.delete_gen_table_service(auth, ids) logger.info('删除业务表信息成功') return SuccessResponse(msg="删除业务表信息成功", data=result) @@ -105,6 +178,16 @@ async def batch_gen_code_controller( table_names: List[str] = Body(..., description="表名列表"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:operate"])) ) -> StreamResponse: + """ + 批量生成代码 + + 参数: + - table_names (List[str]): 表名列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamResponse: 包含批量生成代码的ZIP文件流响应 + """ # 检查table_names是否为空 if not table_names: logger.error('表名列表不能为空') @@ -129,6 +212,16 @@ async def gen_code_local_controller( table_name: str = Path(..., description="表名"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:code"])) ) -> JSONResponse: + """ + 生成代码到指定路径 + + 参数: + - table_name (str): 表名 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含生成结果的JSON响应 + """ from app.config.setting import settings if not settings.allow_overwrite: logger.error('【系统预设】不允许生成文件覆盖到本地') @@ -143,6 +236,16 @@ async def preview_code_controller( table_id: int = Path(..., description="业务表ID"), auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) ) -> JSONResponse: + """ + 预览代码 + + 参数: + - table_id (int): 业务表ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含预览代码的JSON响应 + """ preview_code_result = await GenTableService.preview_code_service(auth, table_id) logger.info('预览代码成功') return SuccessResponse(data=preview_code_result, msg="预览代码成功") @@ -153,6 +256,16 @@ async def sync_db_controller( table_name: str = Path(..., description="表名"), auth: AuthSchema = Depends(AuthPermission(["generator:db:sync"])) ) -> JSONResponse: + """ + 同步数据库 + + 参数: + - table_name (str): 表名 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含同步数据库结果的JSON响应 + """ result = await GenTableService.sync_db_service(auth, table_name) logger.info('同步数据库成功') return SuccessResponse(msg="同步数据库成功", data=result) diff --git a/backend/app/api/v1/module_generator/gencode/crud.py b/backend/app/api/v1/module_generator/gencode/crud.py index 20b04387..1f047df7 100644 --- a/backend/app/api/v1/module_generator/gencode/crud.py +++ b/backend/app/api/v1/module_generator/gencode/crud.py @@ -26,15 +26,23 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): """代码生成业务表模块数据库操作层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化CRUD操作层 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ super().__init__(model=GenTableModel, auth=auth) async def get_gen_table_by_id(self, table_id: int) -> Optional[GenTableModel]: """ - 根据业务表id获取需要生成的业务表信息 + 根据业务表ID获取需要生成的业务表信息。 - :param table_id: 业务表id - :return: 需要生成的业务表信息对象 + 参数: + - table_id (int): 业务表ID。 + + 返回: + - GenTableModel | None: 业务表信息对象。 """ gen_table = ( ( @@ -52,10 +60,13 @@ async def get_gen_table_by_id(self, table_id: int) -> Optional[GenTableModel]: async def get_gen_table_by_name(self, table_name: str) -> Optional[GenTableModel]: """ - 根据业务表名称获取需要生成的业务表信息 + 根据业务表名称获取需要生成的业务表信息。 + + 参数: + - table_name (str): 业务表名称。 - :param table_name: 业务表名称 - :return: 需要生成的业务表信息对象 + 返回: + - GenTableModel | None: 业务表信息对象。 """ gen_table = ( ( @@ -73,9 +84,10 @@ async def get_gen_table_by_name(self, table_name: str) -> Optional[GenTableModel async def get_gen_table_all(self) -> Sequence[GenTableModel]: """ - 获取所有业务表信息 + 获取所有业务表信息。 - :return: 所有业务表信息列表 + 返回: + - Sequence[GenTableModel]: 所有业务表信息列表。 """ gen_table_all = ( await self.db.execute( @@ -88,10 +100,13 @@ async def get_gen_table_all(self) -> Sequence[GenTableModel]: async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None) -> Sequence[GenTableModel]: """ - 根据查询参数获取代码生成业务表列表信息 + 根据查询参数获取代码生成业务表列表信息。 + + 参数: + - search (GenTableQueryParam | None): 查询参数对象。 - :param search: 查询参数对象 - :return: 代码生成业务表列表信息对象 + 返回: + - Sequence[GenTableModel]: 业务表列表信息。 """ # 获取所有数据 result = await self.db.execute( @@ -110,7 +125,13 @@ async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None) async def add_gen_table(self, add_model: GenTableSchema) -> GenTableModel: """ - 增加 + 新增业务表信息。 + + 参数: + - add_model (GenTableSchema): 新增业务表信息模型。 + + 返回: + - GenTableModel: 新增的业务表信息对象。 """ gen_table = GenTableModel( **add_model.model_dump(exclude_unset=True, exclude={"sub", "tree", "crud"}) @@ -121,7 +142,14 @@ async def add_gen_table(self, add_model: GenTableSchema) -> GenTableModel: async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> GenTableSchema: """ - 修改 + 修改业务表信息。 + + 参数: + - table_id (int): 业务表ID。 + - edit_model (GenTableSchema): 修改业务表信息模型。 + + 返回: + - GenTableSchema: 修改后的业务表信息模型。 """ edit_dict_data = edit_model.model_dump(exclude_unset=True) await self.db.execute( @@ -135,7 +163,10 @@ async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> Gen async def delete_gen_table(self, ids: List[int]) -> None: """ - 删除 + 删除业务表信息。除了系统表。 + + 参数: + - ids (List[int]): 业务表ID列表。 """ await self.db.execute( delete(GenTableModel) @@ -145,10 +176,13 @@ async def delete_gen_table(self, ids: List[int]) -> None: async def get_db_table_list(self, search: Optional[GenTableQueryParam] = None) -> list[Dict]: """ - 根据查询参数获取数据库列表信息 + 根据查询参数获取数据库表列表信息。 - :param search: 查询参数对象 - :return: 数据库列表信息对象 + 参数: + - search (GenTableQueryParam | None): 查询参数对象。 + + 返回: + - list[Dict]: 数据库表列表信息(已转为可序列化字典)。 """ # 使用更健壮的方式检测数据库方言 @@ -241,10 +275,13 @@ async def get_db_table_list(self, search: Optional[GenTableQueryParam] = None) - async def get_db_table_list_by_names(self, table_names: List[str]) -> list[GenDBTableSchema]: """ - 根据业务表名称组获取数据库列表信息 + 根据业务表名称列表获取数据库表信息。 + + 参数: + - table_names (List[str]): 业务表名称列表。 - :param table_names: 业务表名称组 - :return: 数据库列表信息对象 + 返回: + - list[GenDBTableSchema]: 数据库表信息对象列表。 """ # 使用更健壮的方式检测数据库方言 if settings.DATABASE_TYPE == "postgresql": @@ -330,11 +367,13 @@ async def get_db_table_list_by_names(self, table_names: List[str]) -> list[GenDB async def create_table_by_sql(self, sql: str) -> bool: """ - 根据sql语句创建表结构 + 根据SQL语句创建表结构。 + + 参数: + - sql (str): 创建表的SQL语句。 - :param db: orm对象 - :param sql: sql语句 - :return: + 返回: + - bool: 是否创建成功。 """ try: await self.db.execute(text(sql)) @@ -353,27 +392,57 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen """代码生成业务表字段模块数据库操作层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化CRUD操作层 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ super().__init__(model=GenTableColumnModel, auth=auth) async def get_gen_table_column_by_id(self, id: int) -> Optional[GenTableColumnModel]: - """根据业务表字段ID获取业务表字段信息""" + """根据业务表字段ID获取业务表字段信息。 + + 参数: + - id (int): 业务表字段ID。 + + 返回: + - Optional[GenTableColumnModel]: 业务表字段信息对象。 + """ return await self.get(id=id) async def get_gen_table_column_list_by_table_id(self, table_id: int) -> Optional[GenTableColumnModel]: - """根据业务表ID获取业务表字段列表信息""" + """根据业务表ID获取业务表字段列表信息。 + + 参数: + - table_id (int): 业务表ID。 + + 返回: + - Optional[GenTableColumnModel]: 业务表字段列表信息对象。 + """ return await self.get(table_id=table_id) async def list_gen_table_column_crud_by_table_id(self, table_id: int, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]: - """根据业务表ID查询业务表字段列表""" + """根据业务表ID查询业务表字段列表。 + + 参数: + - table_id (int): 业务表ID。 + - order_by (Optional[List[Dict[str, str]]]): 排序字段列表,每个元素为{"field": "字段名", "order": "asc" | "desc"}。 + + 返回: + - Sequence[GenTableColumnModel]: 业务表字段列表信息对象序列。 + """ return await self.list(search={"table_id": table_id}, order_by=order_by) async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> List[GenTableColumnOutSchema]: """ - 根据业务表名称获取业务表字段列表信息 + 根据业务表名称获取业务表字段列表信息。 + + 参数: + - table_name (str | None): 业务表名称。 - :param table_name: 业务表名称 - :return: 业务表字段列表信息对象 + 返回: + - List[GenTableColumnOutSchema]: 业务表字段列表信息对象。 """ # 检查表名是否为空 if not table_name: @@ -448,19 +517,49 @@ async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> List return result async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]: - """根据业务表ID查询业务表字段列表""" + """根据业务表字段查询业务表字段列表。 + + 参数: + - search (Optional[Dict]): 查询参数,例如{"table_id": 1}。 + - order_by (Optional[List[Dict[str, str]]]): 排序字段列表,每个元素为{"field": "字段名", "order": "asc" | "desc"}。 + + 返回: + - Sequence[GenTableColumnModel]: 业务表字段列表信息对象序列。 + """ return await self.list(search=search, order_by=order_by) async def create_gen_table_column_crud(self, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]: - """创建业务表字段""" + """创建业务表字段。 + + 参数: + - data (GenTableColumnSchema): 业务表字段模型。 + + 返回: + - Optional[GenTableColumnModel]: 业务表字段列表信息对象。 + """ return await self.create(data=data) async def update_gen_table_column_crud(self, id: int, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]: - """更新业务表字段""" + """更新业务表字段。 + + 参数: + - id (int): 业务表字段ID。 + - data (GenTableColumnSchema): 业务表字段模型。 + + 返回: + - Optional[GenTableColumnModel]: 业务表字段列表信息对象。 + """ return await self.update(id=id, data=data) async def delete_gen_table_column_by_table_id_dao(self, table_ids: List[int]) -> None: - """根据业务表ID批量删除""" + """根据业务表ID批量删除业务表字段。 + + 参数: + - table_ids (List[int]): 业务表ID列表。 + + 返回: + - None + """ # 先查询出这些表ID对应的所有字段ID query = select(GenTableColumnModel.id).where(GenTableColumnModel.table_id.in_(table_ids)) result = await self.db.execute(query) @@ -471,5 +570,12 @@ async def delete_gen_table_column_by_table_id_dao(self, table_ids: List[int]) -> await self.delete(ids=column_ids) async def delete_gen_table_column_by_column_id_dao(self, data: GenTableColumnDeleteSchema) -> None: - """根据业务表字段ID批量删除""" + """根据业务表字段ID批量删除业务表字段。 + + 参数: + - data (GenTableColumnDeleteSchema): 业务表字段删除模型。 + + 返回: + - None + """ return await self.delete(ids=data.column_ids) diff --git a/backend/app/api/v1/module_generator/gencode/param.py b/backend/app/api/v1/module_generator/gencode/param.py index f38ddec7..6fc2490b 100644 --- a/backend/app/api/v1/module_generator/gencode/param.py +++ b/backend/app/api/v1/module_generator/gencode/param.py @@ -3,8 +3,6 @@ from typing import Optional from fastapi import Query -from app.core.validator import DateTimeStr - class GenTableQueryParam: """代码生成业务表查询参数""" diff --git a/backend/app/api/v1/module_generator/gencode/service.py b/backend/app/api/v1/module_generator/gencode/service.py index d21bc2cd..11b919a9 100644 --- a/backend/app/api/v1/module_generator/gencode/service.py +++ b/backend/app/api/v1/module_generator/gencode/service.py @@ -8,7 +8,6 @@ from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update from sqlglot import parse as sqlglot_parse -from app.api.v1.module_generator.gencode.model import GenTableModel from app.config.setting import settings from app.core.exceptions import CustomException from app.common.constant import GenConstant @@ -30,7 +29,15 @@ class GenTableService: @classmethod async def get_gen_table_detail_service(cls, auth: AuthSchema, table_id: int) -> Dict: - """获取业务表详细信息""" + """获取业务表详细信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - table_id (int): 业务表ID。 + + 返回: + - Dict: 包含业务表详细信息、字段列表和所有业务表的字典。 + """ gen_table = await cls.get_gen_table_by_id_service(auth, table_id) gen_tables = await cls.get_gen_table_all_service(auth) gen_columns = await GenTableColumnService.get_gen_table_column_list_by_table_id_service(auth, table_id) @@ -41,23 +48,47 @@ async def get_gen_table_detail_service(cls, auth: AuthSchema, table_id: int) -> return dict(info=gen_table, rows=gen_columns, tables=gen_tables) @classmethod - async def get_gen_table_list_service( - cls, auth: AuthSchema, search: GenTableQueryParam - ) -> List[Dict]: - """获取代码生成业务表列表信息""" + async def get_gen_table_list_service(cls, auth: AuthSchema, search: GenTableQueryParam) -> List[Dict]: + """ + 获取代码生成业务表列表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - search (GenTableQueryParam): 查询参数模型。 + + 返回: + - List[Dict]: 包含业务表列表信息的字典列表。 + """ gen_table_list_result = await GenTableCRUD(auth=auth).get_gen_table_list(search) return [GenTableOutSchema.model_validate(obj).model_dump() for obj in gen_table_list_result] @classmethod async def get_gen_db_table_list_service(cls, auth: AuthSchema, search: GenTableQueryParam, order_by: Optional[List[Dict[str, str]]] = None) -> list[Any]: - """获取数据库列表信息""" + """获取数据库列表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - search (GenTableQueryParam): 查询参数模型。 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表,默认值为None。 + + 返回: + - list[Any]: 包含数据库列表信息的任意类型列表。 + """ # 确保db是AsyncSession类型 gen_db_table_list_result = await GenTableCRUD(auth=auth).get_db_table_list(search) return gen_db_table_list_result @classmethod async def get_gen_db_table_list_by_name_service(cls, auth: AuthSchema, table_names: List[str]) -> List[GenTableOutSchema]: - """根据表名称组获取数据库列表信息""" + """根据表名称组获取数据库列表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - table_names (List[str]): 业务表名称列表。 + + 返回: + - List[GenTableOutSchema]: 包含业务表列表信息的模型列表。 + """ gen_db_table_list_result = await GenTableCRUD(auth=auth).get_db_table_list_by_names(table_names) # 检查是否有未找到的表 @@ -74,10 +105,19 @@ async def get_gen_db_table_list_by_name_service(cls, auth: AuthSchema, table_nam return result @classmethod - async def import_gen_table_service( - cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema] - ) -> Literal[True] | None: - """导入表结构""" + async def import_gen_table_service(cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema]) -> Literal[True] | None: + """导入表结构 + + 参数: + - auth (AuthSchema): 认证对象。 + - gen_table_list (List[GenTableOutSchema]): 要导入的业务表列表。 + + 返回: + - Literal[True] | None: 导入成功返回True,否则返回None。 + + 异常: + - CustomException: 当没有可导入的表结构、表已存在或导入过程中发生错误时抛出。 + """ # 检查是否有表需要导入 if not gen_table_list: raise CustomException(msg="没有可导入的表结构") @@ -131,7 +171,18 @@ async def import_gen_table_service( @classmethod async def create_table_service(cls, auth: AuthSchema, sql: str) -> Literal[True] | None: - """创建表结构""" + """创建表结构。 + + 参数: + - auth (AuthSchema): 认证信息。 + - sql (str): 包含建表SQL语句的字符串。 + + 返回: + - Literal[True] | None: 创建成功返回True,否则返回None。 + + 异常: + - CustomException: 当SQL语句不是合法的建表语句、创建表失败或导入表结构失败时抛出。 + """ try: sql_statements = sqlglot_parse(sql, dialect=settings.DATABASE_TYPE) # 校验sql语句是否为合法的建表语句 @@ -139,7 +190,9 @@ async def create_table_service(cls, auth: AuthSchema, sql: str) -> Literal[True] raise CustomException(msg='sql语句不是合法的建表语句') table_names = cls.__get_table_names(sql_statements) # 执行SQL语句创建表 - await GenTableCRUD(auth=auth).create_table_by_sql(sql) + result = await GenTableCRUD(auth=auth).create_table_by_sql(sql) + if not result: + raise CustomException(msg='创建表失败,请检查SQL语句,请确保语法是否符合标准,并检查后端日志') gen_table_list = await cls.get_gen_db_table_list_by_name_service(auth, table_names) import_result = await cls.import_gen_table_service(auth, gen_table_list) return import_result @@ -149,10 +202,13 @@ async def create_table_service(cls, auth: AuthSchema, sql: str) -> Literal[True] @classmethod def __is_valid_create_table(cls, sql_statements: List[Expression | None]) -> bool: """ - 校验sql语句是否为合法的建表语句 - - :param sql_statements: sql语句的ast列表 - :return: 校验结果 + 校验SQL语句是否为合法的建表语句。 + + 参数: + - sql_statements (List[Expression | None]): SQL的AST列表。 + + 返回: + - bool: 校验结果。 """ validate_create = [isinstance(sql_statement, Create) for sql_statement in sql_statements] validate_forbidden_keywords = [ @@ -169,10 +225,13 @@ def __is_valid_create_table(cls, sql_statements: List[Expression | None]) -> boo @classmethod def __get_table_names(cls, sql_statements: List[Expression | None]) -> List[str]: """ - 获取sql语句中所有的建表表名 - - :param sql_statements: sql语句的ast列表 - :return: 建表表名列表 + 获取SQL语句中所有的建表表名。 + + 参数: + - sql_statements (List[Expression | None]): SQL的AST列表。 + + 返回: + - List[str]: 建表表名列表。 """ table_names = [] for sql_statement in sql_statements: @@ -184,7 +243,19 @@ def __get_table_names(cls, sql_statements: List[Expression | None]) -> List[str] @classmethod async def update_gen_table_service(cls, auth: AuthSchema, data: GenTableSchema, table_id: int) -> Dict[str, Any]: - """编辑业务表信息""" + """编辑业务表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - data (GenTableSchema): 包含业务表更新信息的模型。 + - table_id (int): 业务表ID。 + + 返回: + - Dict[str, Any]: 更新后的业务表信息字典。 + + 异常: + - CustomException: 当业务表不存在、更新失败或处理字段参数时抛出。 + """ edit_gen_table = data.model_dump(exclude_unset=True, by_alias=True) gen_table_info = await cls.get_gen_table_by_id_service(auth, table_id) if gen_table_info.id: @@ -210,7 +281,18 @@ async def update_gen_table_service(cls, auth: AuthSchema, data: GenTableSchema, @classmethod async def delete_gen_table_service(cls, auth: AuthSchema, ids: List[int]) -> None: - """删除业务表信息""" + """删除业务表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - ids (List[int]): 业务表ID列表。 + + 返回: + - None + + 异常: + - CustomException: 当删除失败时抛出。 + """ try: # 先删除相关的字段信息 await GenTableColumnCRUD(auth=auth).delete_gen_table_column_by_table_id_dao(ids) @@ -221,7 +303,18 @@ async def delete_gen_table_service(cls, auth: AuthSchema, ids: List[int]) -> Non @classmethod async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> GenTableOutSchema: - """获取需要生成的业务表详细信息""" + """获取需要生成代码的业务表详细信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + - table_id (int): 业务表ID。 + + 返回: + - GenTableOutSchema: 包含业务表详细信息的模型。 + + 异常: + - CustomException: 当业务表不存在时抛出。 + """ gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_id(table_id) if gen_table: # 使用更直接的转换方式 @@ -257,7 +350,14 @@ async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> G @classmethod async def get_gen_table_all_service(cls, auth: AuthSchema) -> List[GenTableOutSchema]: - """获取所有业务表信息""" + """获取所有业务表信息。 + + 参数: + - auth (AuthSchema): 认证信息。 + + 返回: + - List[GenTableOutSchema]: 包含所有业务表详细信息的模型列表。 + """ gen_table_all = await GenTableCRUD(auth=auth).get_gen_table_all() gen_table_all_dict = [GenTableOutSchema.model_validate(gen_table).model_dump() for gen_table in gen_table_all] result = [GenTableOutSchema(**gen_table) for gen_table in gen_table_all_dict] @@ -266,11 +366,14 @@ async def get_gen_table_all_service(cls, auth: AuthSchema) -> List[GenTableOutSc @classmethod async def preview_code_service(cls, auth: AuthSchema, table_id: int) -> Dict[Any, Any]: """ - 预览代码service - - :param auth: 认证对象 - :param table_id: 业务表id - :return: 预览数据列表 + 预览代码。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_id (int): 业务表ID。 + + 返回: + - Dict[Any, Any]: 模版文件名到渲染内容的映射。 """ gen_table = GenTableOutSchema.model_validate( await GenTableCRUD(auth).get_gen_table_by_id(table_id) @@ -285,19 +388,29 @@ async def preview_code_service(cls, auth: AuthSchema, table_id: int) -> Dict[Any template_list = Jinja2TemplateUtil.get_template_list(tpl_category, tpl_web_type) preview_code_result = {} for template in template_list: - render_content = env.get_template(template).render(**context) + render_content = await env.get_template(template).render_async(**context) preview_code_result[template] = render_content return preview_code_result - @classmethod async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse: - """生成代码至指定路径""" + """生成代码至指定路径。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_name (str): 业务表名称。 + + 返回: + - SuccessResponse: 成功响应模型。 + + 异常: + - CustomException: 当渲染模板失败时抛出。 + """ env = Jinja2TemplateInitializerUtil.init_jinja2() render_info = await cls.__get_gen_render_info(auth, table_name) for template in render_info[0]: try: - render_content = env.get_template(template).render(**render_info[2]) + render_content = await env.get_template(template).render_async(**render_info[2]) gen_path = cls.__get_gen_path(render_info[3], template) if gen_path: os.makedirs(os.path.dirname(gen_path), exist_ok=True) @@ -311,11 +424,14 @@ async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> Succe @classmethod async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) -> bytes: """ - 批量生成代码service - - :param auth: 认证对象 - :param table_names: 业务表名称组 - :return: 下载代码结果 + 批量生成代码并打包为ZIP。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_names (List[str]): 业务表名称组。 + + 返回: + - bytes: 下载代码的ZIP二进制数据。 """ zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: @@ -323,7 +439,7 @@ async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) env = Jinja2TemplateInitializerUtil.init_jinja2() render_info = await cls.__get_gen_render_info(auth, table_name) for template_file, output_file in zip(render_info[0], render_info[1]): - render_content = env.get_template(template_file).render(**render_info[2]) + render_content = await env.get_template(template_file).render_async(**render_info[2]) zip_file.writestr(output_file, render_content) zip_data = zip_buffer.getvalue() @@ -332,67 +448,106 @@ async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) @classmethod async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> None: - """同步数据库""" + """同步数据库表结构。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_name (str): 业务表名称。 + + 返回: + - None + + 异常: + - CustomException: 当业务表不存在时抛出。 + """ gen_table = await GenTableCRUD(auth).get_gen_table_by_name(table_name) + if not gen_table: + raise CustomException(msg='业务表不存在') table = GenTableSchema.model_validate(gen_table) - # 处理table.columns为None的情况 + # 关键修复:确保 table.table_id 正确设置为持久化的表ID,否则列无法关联到该表 + if getattr(table, 'table_id', None) is None: + table.table_id = getattr(gen_table, 'id', None) table_columns = table.columns or [] table_column_map = {column.column_name: column for column in table_columns} - query_db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) - # 直接使用查询结果,因为get_gen_db_table_columns_by_name已经返回GenTableColumnOutSchema对象列表 - db_table_columns = query_db_table_columns - if not db_table_columns: - raise CustomException(msg='同步数据失败,原表结构不存在') + db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) db_table_column_names = [column.column_name for column in db_table_columns] try: for column in db_table_columns: + # 仅在缺省时初始化默认属性(包含 table_id 关联) GenUtils.init_column_field(column, table) if column.column_name in table_column_map: prev_column = table_column_map[column.column_name] - # 处理column.id为None的情况 + # 复用旧记录ID,确保执行更新 if hasattr(prev_column, 'id') and prev_column.id: column.id = prev_column.id - if column.list: + + # 保留用户配置的显示与查询属性 + if getattr(prev_column, 'dict_type', None): column.dict_type = prev_column.dict_type + if getattr(prev_column, 'query_type', None): column.query_type = prev_column.query_type - if ( - hasattr(prev_column, 'is_required') and prev_column.is_required != '' - and not column.pk - and (column.insert or column.edit) - and (column.usable_column or column.super_column) - ): - column.is_required = prev_column.is_required + if getattr(prev_column, 'html_type', None): column.html_type = prev_column.html_type - # 处理column.id为None的情况 + + # 保留 is_* 标志(旧值非空则保留),主键不设置必填 + def keep_str(orig, current): + return orig if (orig is not None and orig != '') else current + + is_pk_bool = bool(getattr(prev_column, 'pk', False)) or (prev_column.is_pk == '1') + if not is_pk_bool: + column.is_required = keep_str(prev_column.is_required, column.is_required) + column.is_unique = keep_str(prev_column.is_unique, column.is_unique) + column.is_insert = keep_str(prev_column.is_insert, column.is_insert) + column.is_edit = keep_str(prev_column.is_edit, column.is_edit) + column.is_list = keep_str(prev_column.is_list, column.is_list) + column.is_query = keep_str(prev_column.is_query, column.is_query) + if hasattr(column, 'id') and column.id: await GenTableColumnCRUD(auth).update_gen_table_column_crud(column.id, column) + else: + await GenTableColumnCRUD(auth).create_gen_table_column_crud(column) else: await GenTableColumnCRUD(auth).create_gen_table_column_crud(column) del_columns = [column for column in table_columns if column.column_name not in db_table_column_names] if del_columns: for column in del_columns: - # 处理column.id为None的情况 if hasattr(column, 'id') and column.id: await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_dao( GenTableColumnDeleteSchema(column_ids=[column.id]) ) except Exception as e: - raise e - + raise CustomException(msg=f'同步失败: {str(e)}') @classmethod async def set_sub_table(cls, auth: AuthSchema, gen_table: GenTableOutSchema) -> None: - """设置主子表信息""" + """设置主子表信息。 + + 参数: + - auth (AuthSchema): 认证对象。 + - gen_table (GenTableOutSchema): 业务表详细信息模型。 + + 返回: + - None + + 异常: + - CustomException: 当子表不存在时抛出。 + """ if gen_table.sub_table_name: gen_table_dao = GenTableCRUD(auth=auth) sub_table = await gen_table_dao.get_gen_table_by_name(gen_table.sub_table_name) if sub_table: gen_table.sub_table = GenTableOutSchema.model_validate(sub_table) - @classmethod async def set_pk_column(cls, gen_table: GenTableOutSchema) -> None: - """设置主键列信息""" + """设置主键列信息。 + + 参数: + - gen_table (GenTableOutSchema): 业务表详细信息模型。 + + 返回: + - None + """ if gen_table.columns: for column in gen_table.columns: if column.pk: @@ -411,7 +566,14 @@ async def set_pk_column(cls, gen_table: GenTableOutSchema) -> None: @classmethod async def set_table_from_options(cls, gen_table: GenTableOutSchema) -> GenTableOutSchema: - """设置代码生成其他选项值""" + """设置代码生成其他选项值。 + + 参数: + - gen_table (GenTableOutSchema): 业务表详细信息模型。 + + 返回: + - GenTableOutSchema: 更新后的业务表详细信息模型。 + """ # 处理gen_table.options为None的情况 if gen_table.options: try: @@ -432,7 +594,17 @@ async def set_table_from_options(cls, gen_table: GenTableOutSchema) -> GenTableO @classmethod async def validate_edit(cls, edit_gen_table: GenTableSchema) -> None: - """编辑保存参数校验""" + """编辑保存参数校验。 + + 参数: + - edit_gen_table (GenTableSchema): 编辑后的业务表模型。 + + 返回: + - None + + 异常: + - CustomException: 当参数校验失败时抛出。 + """ if edit_gen_table.tpl_category == GenConstant.TPL_TREE: # 从options字段获取参数,而不是params if not edit_gen_table.options: @@ -459,11 +631,17 @@ async def validate_edit(cls, edit_gen_table: GenTableSchema) -> None: @classmethod async def __get_gen_render_info(cls, auth: AuthSchema, table_name: str) -> List[Any]: """ - 获取生成代码渲染模板相关信息 - - :param auth: 认证对象 - :param table_name: 业务表名称 - :return: 生成代码渲染模板相关信息 + 获取生成代码渲染模板相关信息。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_name (str): 业务表名称。 + + 返回: + - List[Any]: [模板列表, 输出文件名列表, 渲染上下文, 业务表对象]。 + + 异常: + - CustomException: 当业务表不存在或数据转换失败时抛出。 """ gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_name(table_name) # 检查表是否存在 @@ -491,11 +669,18 @@ async def __get_gen_render_info(cls, auth: AuthSchema, table_name: str) -> List[ output_files.append(file_name) return [template_list, output_files, context, gen_table_schema] - @classmethod def __get_gen_path(cls, gen_table: GenTableOutSchema, template: str) -> Optional[str]: - """根据GenTableModel对象和模板名称生成路径""" + """根据GenTableOutSchema对象和模板名称生成路径。 + + 参数: + - gen_table (GenTableOutSchema): 业务表详细信息模型。 + - template (str): 模板名称。 + + 返回: + - Optional[str]: 生成的文件路径,若失败则返回None。 + """ try: gen_path = gen_table.gen_path or "" file_name = Jinja2TemplateUtil.get_file_name([template], gen_table) @@ -515,7 +700,15 @@ class GenTableColumnService: @classmethod async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[GenTableColumnOutSchema]: - """获取业务表字段列表信息""" + """获取业务表字段列表信息。 + + 参数: + - auth (AuthSchema): 认证对象。 + - table_id (int): 业务表ID。 + + 返回: + - List[GenTableColumnOutSchema]: 业务表字段详细信息模型列表。 + """ gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({"table_id": table_id}) return [ GenTableColumnOutSchema.model_validate(gen_table_column) diff --git a/backend/app/api/v1/module_monitor/cache/controller.py b/backend/app/api/v1/module_monitor/cache/controller.py index aa6c62dd..7fe1a4d1 100644 --- a/backend/app/api/v1/module_monitor/cache/controller.py +++ b/backend/app/api/v1/module_monitor/cache/controller.py @@ -24,7 +24,12 @@ async def get_monitor_cache_info_controller( redis: Redis = Depends(redis_getter) ) -> JSONResponse: - """获取缓存监控统计信息""" + """ + 获取缓存监控统计信息 + + 返回: + - JSONResponse: 包含缓存监控统计信息的JSON响应 + """ result = await CacheService.get_cache_monitor_statistical_info_service(redis=redis) logger.info('获取缓存监控信息成功') return SuccessResponse(data=result, msg='获取缓存监控信息成功') @@ -37,7 +42,12 @@ async def get_monitor_cache_info_controller( description="获取缓存名称列表" ) async def get_monitor_cache_name_controller() -> JSONResponse: - """获取缓存名称列表""" + """ + 获取缓存名称列表 + + 返回: + - JSONResponse: 包含缓存名称列表的JSON响应 + """ result = await CacheService.get_cache_monitor_cache_name_service() logger.info('获取缓存名称列表成功') return SuccessResponse(data=result, msg='获取缓存名称列表成功') @@ -53,7 +63,15 @@ async def get_monitor_cache_key_controller( cache_name: str, redis: Redis = Depends(redis_getter) ) -> JSONResponse: - """获取指定缓存名称下的键名列表""" + """ + 获取指定缓存名称下的键名列表 + + 参数: + - cache_name (str): 缓存名称 + + 返回: + - JSONResponse: 包含缓存键名列表的JSON响应 + """ result = await CacheService.get_cache_monitor_cache_key_service(redis=redis, cache_name=cache_name) logger.info(f'获取缓存{cache_name}的键名列表成功') return SuccessResponse(data=result, msg=f'获取缓存{cache_name}的键名列表成功') @@ -70,7 +88,16 @@ async def get_monitor_cache_value_controller( cache_key: str, redis: Redis = Depends(redis_getter) )-> JSONResponse: - """获取指定缓存键的值""" + """ + 获取指定缓存键的值 + + 参数: + - cache_name (str): 缓存名称 + - cache_key (str): 缓存键 + + 返回: + - JSONResponse: 包含缓存值的JSON响应 + """ result = await CacheService.get_cache_monitor_cache_value_service(redis=redis, cache_name=cache_name, cache_key=cache_key) logger.info(f'获取缓存{cache_name}:{cache_key}的值成功') return SuccessResponse(data=result, msg=f'获取缓存{cache_name}:{cache_key}的值成功') @@ -86,7 +113,15 @@ async def clear_monitor_cache_name_controller( cache_name: str, redis: Redis = Depends(redis_getter) ) -> JSONResponse: - """清除指定缓存名称下的所有缓存""" + """ + 清除指定缓存名称下的所有缓存 + + 参数: + - cache_name (str): 缓存名称 + + 返回: + - JSONResponse: 包含清除结果的JSON响应 + """ result = await CacheService.clear_cache_monitor_cache_name_service(redis=redis, cache_name=cache_name) if not result: raise CustomException(msg='清除缓存失败', data=result) @@ -104,7 +139,15 @@ async def clear_monitor_cache_key_controller( cache_key: str, redis: Redis = Depends(redis_getter) ) -> JSONResponse: - """清除指定缓存键""" + """ + 清除指定缓存键 + + 参数: + - cache_key (str): 缓存键 + + 返回: + - JSONResponse: 包含清除结果的JSON响应 + """ result = await CacheService.clear_cache_monitor_cache_key_service(redis=redis, cache_key=cache_key) if not result: raise CustomException(msg='清除缓存失败', data=result) @@ -121,7 +164,12 @@ async def clear_monitor_cache_key_controller( async def clear_monitor_cache_all_controller( redis: Redis = Depends(redis_getter) ) -> JSONResponse: - """清除所有缓存""" + """ + 清除所有缓存 + + 返回: + - JSONResponse: 包含清除结果的JSON响应 + """ result = await CacheService.clear_cache_monitor_all_service(redis=redis) if not result: raise CustomException(msg='清除缓存失败', data=result) diff --git a/backend/app/api/v1/module_monitor/cache/service.py b/backend/app/api/v1/module_monitor/cache/service.py index 2b763cf0..eeaa5351 100644 --- a/backend/app/api/v1/module_monitor/cache/service.py +++ b/backend/app/api/v1/module_monitor/cache/service.py @@ -15,10 +15,13 @@ class CacheService: @classmethod async def get_cache_monitor_statistical_info_service(cls, redis: Redis)->dict: """ - 获取缓存监控信息service - - :param redis: Redis对象 - :return: 缓存监控信息 + 获取缓存监控信息。 + + 参数: + - redis (Redis): Redis 对象。 + + 返回: + - dict: 缓存监控信息字典。 """ info = await RedisCURD(redis).info() db_size = await RedisCURD(redis).db_size() @@ -34,9 +37,10 @@ async def get_cache_monitor_statistical_info_service(cls, redis: Redis)->dict: @classmethod async def get_cache_monitor_cache_name_service(cls)->list: """ - 获取缓存名称列表信息service - - :return: 缓存名称列表信息 + 获取缓存名称列表信息。 + + 返回: + - list: 缓存名称列表信息。 """ name_list = [] for key_config in RedisInitKeyConfig: @@ -54,11 +58,14 @@ async def get_cache_monitor_cache_name_service(cls)->list: @classmethod async def get_cache_monitor_cache_key_service(cls, redis: Redis, cache_name: str)->list: """ - 获取缓存键名列表信息service - - :param redis: Redis对象 - :param cache_name: 缓存名称 - :return: 缓存键名列表信息 + 获取缓存键名列表信息。 + + 参数: + - redis (Redis): Redis 对象。 + - cache_name (str): 缓存名称。 + + 返回: + - list: 缓存键名列表信息。 """ cache_keys = await RedisCURD(redis).get_keys(f'{cache_name}*') cache_key_list = [key.split(':', 1)[1] for key in cache_keys if key.startswith(f'{cache_name}:')] @@ -68,12 +75,15 @@ async def get_cache_monitor_cache_key_service(cls, redis: Redis, cache_name: str @classmethod async def get_cache_monitor_cache_value_service(cls, redis: Redis, cache_name: str, cache_key: str)->dict: """ - 获取缓存内容信息service - - :param redis: Redis对象 - :param cache_name: 缓存名称 - :param cache_key: 缓存键名 - :return: 缓存内容信息 + 获取缓存内容信息。 + + 参数: + - redis (Redis): Redis 对象。 + - cache_name (str): 缓存名称。 + - cache_key (str): 缓存键名。 + + 返回: + - dict: 缓存内容信息字典。 """ cache_value = await RedisCURD(redis).get(f'{cache_name}:{cache_key}') @@ -82,11 +92,14 @@ async def get_cache_monitor_cache_value_service(cls, redis: Redis, cache_name: s @classmethod async def clear_cache_monitor_cache_name_service(cls, redis: Redis, cache_name: str)->bool: """ - 清除缓存名称对应所有键值service - - :param redis: Redis对象 - :param cache_name: 缓存名称 - :return: 操作缓存响应信息 + 清除指定缓存名称对应的所有键值。 + + 参数: + - redis (Redis): Redis 对象。 + - cache_name (str): 缓存名称。 + + 返回: + - bool: 是否清理成功。 """ cache_keys = await RedisCURD(redis).get_keys(f'{cache_name}*') if cache_keys: @@ -97,11 +110,14 @@ async def clear_cache_monitor_cache_name_service(cls, redis: Redis, cache_name: @classmethod async def clear_cache_monitor_cache_key_service(cls, redis: Redis, cache_key: str)->bool: """ - 清除缓存名称对应所有键值service - - :param redis: Redis对象 - :param cache_key: 缓存键名 - :return: 操作缓存响应信息 + 清除匹配指定键名的所有键值。 + + 参数: + - redis (Redis): Redis 对象。 + - cache_key (str): 缓存键名。 + + 返回: + - bool: 是否清理成功。 """ cache_keys = await RedisCURD(redis).get_keys(f'*{cache_key}') if cache_keys: @@ -112,10 +128,13 @@ async def clear_cache_monitor_cache_key_service(cls, redis: Redis, cache_key: st @classmethod async def clear_cache_monitor_all_service(cls, redis: Redis)->bool: """ - 清除所有缓存service - - :param redis: Redis对象 - :return: 操作缓存响应信息 + 清除所有缓存。 + + 参数: + - redis (Redis): Redis 对象。 + + 返回: + - bool: 是否清理成功。 """ cache_keys = await RedisCURD(redis).get_keys() if cache_keys: diff --git a/backend/app/api/v1/module_monitor/online/controller.py b/backend/app/api/v1/module_monitor/online/controller.py index 785ecc60..b360f931 100644 --- a/backend/app/api/v1/module_monitor/online/controller.py +++ b/backend/app/api/v1/module_monitor/online/controller.py @@ -28,7 +28,17 @@ async def get_online_list_controller( paging_query: PaginationQueryParam = Depends(), search: OnlineQueryParam = Depends() )->JSONResponse: - # 获取全量数据 + """ + 获取在线用户列表 + + 参数: + - redis (Redis): Redis异步客户端实例。 + - paging_query (PaginationQueryParam): 分页查询参数模型。 + - search (OnlineQueryParam): 查询参数模型。 + + 返回: + - JSONResponse: 包含在线用户列表的JSON响应。 + """ result_dict_list = await OnlineService.get_online_list_service(redis=redis, search=search) result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= paging_query.page_no, page_size = paging_query.page_size) logger.info('获取成功') @@ -46,6 +56,16 @@ async def delete_online_controller( session_id: str = Body(..., description="会话编号"), redis: Redis = Depends(redis_getter), )->JSONResponse: + """ + 强制下线指定在线用户 + + 参数: + - session_id (str): 在线用户会话ID。 + - redis (Redis): Redis异步客户端实例。 + + 返回: + - JSONResponse: 包含操作结果的JSON响应。 + """ is_ok = await OnlineService.delete_online_service(redis=redis, session_id=session_id) if is_ok: logger.info("强制下线成功") @@ -63,6 +83,15 @@ async def delete_online_controller( async def clear_online_controller( redis: Redis = Depends(redis_getter), )->JSONResponse: + """ + 清除所有在线用户 + + 参数: + - redis (Redis): Redis异步客户端实例。 + + 返回: + - JSONResponse: 包含操作结果的JSON响应。 + """ is_ok = await OnlineService.clear_online_service(redis=redis) if is_ok: logger.info("清除所有在线用户成功") diff --git a/backend/app/api/v1/module_monitor/online/service.py b/backend/app/api/v1/module_monitor/online/service.py index f7dc7794..d61712c7 100644 --- a/backend/app/api/v1/module_monitor/online/service.py +++ b/backend/app/api/v1/module_monitor/online/service.py @@ -18,6 +18,13 @@ class OnlineService: async def get_online_list_service(cls, redis: Redis, search: Optional[OnlineQueryParam] = None) -> List[Dict]: """ 获取在线用户列表信息(支持分页和搜索) + + 参数: + - redis (Redis): Redis异步客户端实例。 + - search (Optional[OnlineQueryParam]): 查询参数模型。 + + 返回: + - List[Dict]: 在线用户详情字典列表。 """ keys = await RedisCURD(redis).get_keys(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*") @@ -43,7 +50,16 @@ async def get_online_list_service(cls, redis: Redis, search: Optional[OnlineQuer @classmethod async def delete_online_service(cls, redis: Redis, session_id: str) -> bool: - """强制下线在线用户""" + """ + 强制下线指定在线用户 + + 参数: + - redis (Redis): Redis异步客户端实例。 + - session_id (str): 在线用户会话ID。 + + 返回: + - bool: 如果操作成功则返回True,否则返回False。 + """ # 删除 token await RedisCURD(redis).delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}") await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:{session_id}") @@ -54,7 +70,15 @@ async def delete_online_service(cls, redis: Redis, session_id: str) -> bool: @classmethod async def clear_online_service(cls, redis: Redis) -> bool: - """强制下线在线用户""" + """ + 强制下线所有在线用户 + + 参数: + - redis (Redis): Redis异步客户端实例。 + + 返回: + - bool: 如果操作成功则返回True,否则返回False。 + """ # 删除 token await RedisCURD(redis).clear(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*") await RedisCURD(redis).clear(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:*") @@ -65,7 +89,16 @@ async def clear_online_service(cls, redis: Redis) -> bool: @staticmethod def _match_search_conditions(online_info: Dict, search: Optional[OnlineQueryParam]) -> bool: - """检查是否匹配搜索条件""" + """ + 检查是否匹配搜索条件 + + 参数: + - online_info (Dict): 在线用户信息字典。 + - search (Optional[OnlineQueryParam]): 查询参数模型。 + + 返回: + - bool: 如果匹配则返回True,否则返回False。 + """ if not search: return True diff --git a/backend/app/api/v1/module_monitor/resource/controller.py b/backend/app/api/v1/module_monitor/resource/controller.py index 33023873..093bacbe 100644 --- a/backend/app/api/v1/module_monitor/resource/controller.py +++ b/backend/app/api/v1/module_monitor/resource/controller.py @@ -35,7 +35,17 @@ async def get_directory_list_controller( page: PaginationQueryParam = Depends(), search: ResourceSearchQueryParam = Depends(), ) -> JSONResponse: - """获取目录列表""" + """ + 获取目录列表 + + 参数: + - request (Request): FastAPI请求对象,用于获取基础URL。 + - page (PaginationQueryParam): 分页查询参数模型。 + - search (ResourceSearchQueryParam): 资源查询参数模型。 + + 返回: + - JSONResponse: 包含目录列表的JSON响应。 + """ # 获取资源列表(与案例模块保持一致的分页实现) result_dict_list = await ResourceService.get_resources_list_service( search=search, @@ -62,7 +72,17 @@ async def upload_file_controller( request: Request, target_path: Optional[str] = Form(None, description="目标目录路径") ) -> JSONResponse: - """上传文件""" + """ + 上传文件 + + 参数: + - file (UploadFile): 要上传的文件对象。 + - request (Request): FastAPI请求对象,用于获取基础URL。 + - target_path (Optional[str]): 目标目录路径,默认为None。 + + 返回: + - JSONResponse: 包含上传文件信息的JSON响应。 + """ result_dict = await ResourceService.upload_file_service( file=file, target_path=target_path, @@ -82,7 +102,16 @@ async def download_file_controller( request: Request, path: str = Query(..., description="文件路径") ) -> FileResponse: - """下载文件""" + """ + 下载文件 + + 参数: + - request (Request): FastAPI请求对象,用于获取基础URL。 + - path (str): 文件路径。 + + 返回: + - FileResponse: 包含文件内容的文件响应。 + """ file_path = await ResourceService.download_file_service( file_path=path, base_url=str(request.base_url) @@ -109,7 +138,15 @@ async def download_file_controller( async def delete_files_controller( paths: List[str] = Body(..., description="文件路径列表") ) -> JSONResponse: - """删除文件""" + """ + 删除文件 + + 参数: + - paths (List[str]): 文件路径列表。 + + 返回: + - JSONResponse: 包含删除结果的JSON响应。 + """ await ResourceService.delete_file_service(paths=paths) logger.info(f"删除文件成功: {paths}") return SuccessResponse(msg="删除文件成功") @@ -124,7 +161,15 @@ async def delete_files_controller( async def move_file_controller( data: ResourceMoveSchema ) -> JSONResponse: - """移动文件""" + """ + 移动文件 + + 参数: + - data (ResourceMoveSchema): 移动文件参数模型。 + + 返回: + - JSONResponse: 包含移动结果的JSON响应。 + """ await ResourceService.move_file_service(data=data) logger.info(f"移动文件成功: {data.source_path} -> {data.target_path}") return SuccessResponse(msg="移动文件成功") @@ -139,7 +184,15 @@ async def move_file_controller( async def copy_file_controller( data: ResourceCopySchema ) -> JSONResponse: - """复制文件""" + """ + 复制文件 + + 参数: + - data (ResourceCopySchema): 复制文件参数模型。 + + 返回: + - JSONResponse: 包含复制结果的JSON响应。 + """ await ResourceService.copy_file_service(data=data) logger.info(f"复制文件成功: {data.source_path} -> {data.target_path}") return SuccessResponse(msg="复制文件成功") @@ -154,7 +207,15 @@ async def copy_file_controller( async def rename_file_controller( data: ResourceRenameSchema ) -> JSONResponse: - """重命名文件""" + """ + 重命名文件 + + 参数: + - data (ResourceRenameSchema): 重命名文件参数模型。 + + 返回: + - JSONResponse: 包含重命名结果的JSON响应。 + """ await ResourceService.rename_file_service(data=data) logger.info(f"重命名文件成功: {data.old_path} -> {data.new_name}") return SuccessResponse(msg="重命名文件成功") @@ -169,7 +230,15 @@ async def rename_file_controller( async def create_directory_controller( data: ResourceCreateDirSchema ) -> JSONResponse: - """创建目录""" + """ + 创建目录 + + 参数: + - data (ResourceCreateDirSchema): 创建目录参数模型。 + + 返回: + - JSONResponse: 包含创建目录结果的JSON响应。 + """ await ResourceService.create_directory_service(data=data) logger.info(f"创建目录成功: {data.parent_path}/{data.dir_name}") return SuccessResponse(msg="创建目录成功") @@ -185,7 +254,16 @@ async def export_resource_list_controller( request: Request, search: ResourceSearchQueryParam = Depends() ) -> StreamingResponse: - """导出资源列表""" + """ + 导出资源列表 + + 参数: + - request (Request): FastAPI请求对象,用于获取基础URL。 + - search (ResourceSearchQueryParam): 资源查询参数模型。 + + 返回: + - StreamingResponse: 包含导出资源列表的流式响应。 + """ # 获取搜索结果 result_dict_list = await ResourceService.get_resources_list_service( search=search, diff --git a/backend/app/api/v1/module_monitor/resource/service.py b/backend/app/api/v1/module_monitor/resource/service.py index 3d729fda..4e6bc034 100644 --- a/backend/app/api/v1/module_monitor/resource/service.py +++ b/backend/app/api/v1/module_monitor/resource/service.py @@ -35,14 +35,27 @@ class ResourceService: @classmethod def _get_resource_root(cls) -> str: - """获取资源管理根目录""" + """ + 获取资源管理根目录 + + 返回: + - str: 资源管理根目录路径。 + """ if not settings.STATIC_ENABLE: raise CustomException(msg='静态文件服务未启用') return str(settings.STATIC_ROOT) @classmethod def _get_safe_path(cls, path: Optional[str] = None) -> str: - """获取安全的文件路径""" + """ + 获取安全的文件路径 + + 参数: + - path (Optional[str]): 原始文件路径。 + + 返回: + - str: 安全的文件路径。 + """ resource_root = cls._get_resource_root() if not path: @@ -72,7 +85,15 @@ def _get_safe_path(cls, path: Optional[str] = None) -> str: @classmethod def _path_exists(cls, path: str) -> bool: - """检查路径是否存在""" + """ + 检查路径是否存在 + + 参数: + - path (str): 要检查的路径。 + + 返回: + - bool: 如果路径存在则返回True,否则返回False。 + """ try: safe_path = cls._get_safe_path(path) return os.path.exists(safe_path) @@ -81,7 +102,16 @@ def _path_exists(cls, path: str) -> bool: @classmethod def _generate_http_url(cls, file_path: str, base_url: Optional[str] = None) -> str: - """生成文件的HTTP URL""" + """ + 生成文件的HTTP URL + + 参数: + - file_path (str): 文件的绝对路径。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - str: 文件的HTTP URL。 + """ resource_root = cls._get_resource_root() try: relative_path = os.path.relpath(file_path, resource_root) @@ -108,7 +138,16 @@ def _generate_http_url(cls, file_path: str, base_url: Optional[str] = None) -> s @classmethod def _get_file_info(cls, file_path: str, base_url: Optional[str] = None) -> Dict[str, Any]: - """获取文件信息""" + """ + 获取文件或目录的详细信息,如名称、大小、创建时间、修改时间、路径、深度、HTTP URL、是否隐藏、是否为目录等。 + + 参数: + - file_path (str): 文件或目录的路径。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - Dict[str, Any]: 文件或目录的详细信息字典。 + """ try: safe_path = cls._get_safe_path(file_path) if not os.path.exists(safe_path): @@ -160,7 +199,17 @@ def _get_file_info(cls, file_path: str, base_url: Optional[str] = None) -> Dict[ @classmethod async def get_directory_list_service(cls, path: Optional[str] = None, include_hidden: bool = False, base_url: Optional[str] = None) -> Dict: - """获取目录列表""" + """ + 获取目录列表 + + 参数: + - path (Optional[str]): 目录路径。如果未指定,将使用静态文件根目录。 + - include_hidden (bool): 是否包含隐藏文件。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - Dict: 包含目录列表和统计信息的字典。 + """ try: # 如果没有指定路径,使用静态文件根目录 if path is None: @@ -219,7 +268,17 @@ async def get_directory_list_service(cls, path: Optional[str] = None, include_hi @classmethod async def get_resources_list_service(cls, search: Optional[ResourceSearchQueryParam] = None, order_by: Optional[str] = None, base_url: Optional[str] = None) -> List[Dict]: - """搜索资源列表(用于分页和导出)""" + """ + 搜索资源列表(用于分页和导出) + + 参数: + - search (Optional[ResourceSearchQueryParam]): 查询参数模型。 + - order_by (Optional[str]): 排序参数。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - List[Dict]: 资源详情字典列表。 + """ try: # 确定搜索路径 if search and hasattr(search, 'path') and search.path: @@ -273,7 +332,15 @@ async def get_resources_list_service(cls, search: Optional[ResourceSearchQueryPa @classmethod async def export_resource_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出资源列表""" + """ + 导出资源列表 + + 参数: + - data_list (List[Dict[str, Any]]): 资源详情字典列表。 + + 返回: + - bytes: Excel文件的二进制数据。 + """ mapping_dict = { 'name': '文件名', 'path': '文件路径', @@ -295,7 +362,16 @@ async def export_resource_service(cls, data_list: List[Dict[str, Any]]) -> bytes @classmethod async def _get_directory_stats(cls, path: str, include_hidden: bool = False) -> Dict[str, int]: - """递归获取目录统计信息""" + """ + 递归获取目录统计信息 + + 参数: + - path (str): 目录路径。 + - include_hidden (bool): 是否包含隐藏文件。 + + 返回: + - Dict[str, int]: 包含文件数、目录数和总大小的字典。 + """ stats = {'files': 0, 'dirs': 0, 'size': 0} try: @@ -322,7 +398,16 @@ async def _get_directory_stats(cls, path: str, include_hidden: bool = False) -> @classmethod def _sort_results(cls, results: List[Dict], order_by: Optional[str] = None) -> List[Dict]: - """排序搜索结果""" + """ + 排序搜索结果 + + 参数: + - results (List[Dict]): 资源详情字典列表。 + - order_by (Optional[str]): 排序参数。 + + 返回: + - List[Dict]: 排序后的资源详情字典列表。 + """ try: # 默认按名称升序排序 if not order_by: @@ -363,7 +448,17 @@ def sort_key(item): @classmethod async def upload_file_service(cls, file: UploadFile, target_path: Optional[str] = None, base_url: Optional[str] = None) -> Dict: - """上传文件到指定目录""" + """ + 上传文件到指定目录 + + 参数: + - file (UploadFile): 上传的文件对象。 + - target_path (Optional[str]): 目标目录路径。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - Dict: 包含文件信息的字典。 + """ if not file or not file.filename: raise CustomException(msg="请选择要上传的文件") @@ -426,7 +521,16 @@ async def upload_file_service(cls, file: UploadFile, target_path: Optional[str] @classmethod async def download_file_service(cls, file_path: str, base_url: Optional[str] = None) -> str: - """下载文件(返回文件路径)""" + """ + 下载文件(返回文件路径) + + 参数: + - file_path (str): 文件路径。 + - base_url (Optional[str]): 基础URL,用于生成完整URL。 + + 返回: + - str: 文件访问URL。 + """ try: safe_path = cls._get_safe_path(file_path) @@ -449,7 +553,15 @@ async def download_file_service(cls, file_path: str, base_url: Optional[str] = N @classmethod async def delete_file_service(cls, paths: List[str]) -> None: - """删除文件或目录""" + """ + 删除文件或目录 + + 参数: + - paths (List[str]): 文件或目录路径列表。 + + 返回: + - None + """ if not paths: raise CustomException(msg='删除失败,删除路径不能为空') @@ -474,7 +586,15 @@ async def delete_file_service(cls, paths: List[str]) -> None: @classmethod async def batch_delete_service(cls, paths: List[str]) -> Dict[str, List[str]]: - """批量删除文件或目录""" + """ + 批量删除文件或目录 + + 参数: + - paths (List[str]): 文件或目录路径列表。 + + 返回: + - Dict[str, List[str]]: 包含成功删除路径和失败删除路径的字典。 + """ if not paths: raise CustomException(msg='删除失败,删除路径不能为空') @@ -509,7 +629,15 @@ async def batch_delete_service(cls, paths: List[str]) -> Dict[str, List[str]]: @classmethod async def move_file_service(cls, data: ResourceMoveSchema) -> None: - """移动文件或目录""" + """ + 移动文件或目录 + + 参数: + - data (ResourceMoveSchema): 包含源路径和目标路径的模型。 + + 返回: + - None + """ try: source_path = cls._get_safe_path(data.source_path) target_path = cls._get_safe_path(data.target_path) @@ -544,7 +672,15 @@ async def move_file_service(cls, data: ResourceMoveSchema) -> None: @classmethod async def copy_file_service(cls, data: ResourceCopySchema) -> None: - """复制文件或目录""" + """ + 复制文件或目录 + + 参数: + - data (ResourceCopySchema): 包含源路径和目标路径的模型。 + + 返回: + - None + """ try: source_path = cls._get_safe_path(data.source_path) target_path = cls._get_safe_path(data.target_path) @@ -576,7 +712,15 @@ async def copy_file_service(cls, data: ResourceCopySchema) -> None: @classmethod async def rename_file_service(cls, data: ResourceRenameSchema) -> None: - """重命名文件或目录""" + """ + 重命名文件或目录 + + 参数: + - data (ResourceRenameSchema): 包含旧路径和新名称的模型。 + + 返回: + - None + """ try: old_path = cls._get_safe_path(data.old_path) @@ -602,7 +746,15 @@ async def rename_file_service(cls, data: ResourceRenameSchema) -> None: @classmethod async def create_directory_service(cls, data: ResourceCreateDirSchema) -> None: - """创建目录""" + """ + 创建目录 + + 参数: + - data (ResourceCreateDirSchema): 包含父目录路径和目录名称的模型。 + + 返回: + - None + """ try: parent_path = cls._get_safe_path(data.parent_path) @@ -634,7 +786,15 @@ async def create_directory_service(cls, data: ResourceCreateDirSchema) -> None: @classmethod def _format_file_size(cls, size_bytes: int) -> str: - """格式化文件大小""" + """ + 格式化文件大小 + + 参数: + - size_bytes (int): 文件大小(字节) + + 返回: + - str: 格式化后的文件大小字符串(例如:"123.45MB") + """ if size_bytes == 0: return "0B" diff --git a/backend/app/api/v1/module_monitor/server/controller.py b/backend/app/api/v1/module_monitor/server/controller.py index 13705bc0..37c753c8 100644 --- a/backend/app/api/v1/module_monitor/server/controller.py +++ b/backend/app/api/v1/module_monitor/server/controller.py @@ -20,7 +20,12 @@ dependencies=[Depends(AuthPermission(["monitor:server:query"]))] ) async def get_monitor_server_info_controller() -> JSONResponse: - # 获取全量数据 + """ + 查询服务器监控信息 + + 返回: + - JSONResponse: 包含服务器监控信息的JSON响应。 + """ result_dict = await ServerService.get_server_monitor_info_service() logger.info(f'获取服务器监控信息成功: {result_dict}') diff --git a/backend/app/api/v1/module_monitor/server/service.py b/backend/app/api/v1/module_monitor/server/service.py index b5595a7f..244b70e4 100644 --- a/backend/app/api/v1/module_monitor/server/service.py +++ b/backend/app/api/v1/module_monitor/server/service.py @@ -23,7 +23,12 @@ class ServerService: @classmethod async def get_server_monitor_info_service(cls) -> Dict: - """获取服务器监控信息""" + """ + 获取服务器监控信息 + + 返回: + - Dict: 包含服务器监控信息的字典。 + """ return ServerMonitorSchema( cpu=cls._get_cpu_info(), mem=cls._get_memory_info(), @@ -34,7 +39,12 @@ async def get_server_monitor_info_service(cls) -> Dict: @classmethod def _get_cpu_info(cls) -> CpuInfoSchema: - """获取CPU信息""" + """ + 获取CPU信息 + + 返回: + - CpuInfoSchema: CPU信息模型。 + """ cpu_times = psutil.cpu_times_percent() cpu_num=psutil.cpu_count(logical=True) if not cpu_num: @@ -48,7 +58,12 @@ def _get_cpu_info(cls) -> CpuInfoSchema: @classmethod def _get_memory_info(cls) -> MemoryInfoSchema: - """获取内存信息""" + """ + 获取内存信息 + + 返回: + - MemoryInfoSchema: 内存信息模型。 + """ memory = psutil.virtual_memory() return MemoryInfoSchema( total=bytes2human(memory.total), @@ -59,7 +74,12 @@ def _get_memory_info(cls) -> MemoryInfoSchema: @classmethod def _get_system_info(cls) -> SysInfoSchema: - """获取系统信息""" + """ + 获取系统信息 + + 返回: + - SysInfoSchema: 系统信息模型。 + """ hostname = socket.gethostname() return SysInfoSchema( computer_ip=socket.gethostbyname(hostname), @@ -71,7 +91,12 @@ def _get_system_info(cls) -> SysInfoSchema: @classmethod def _get_python_info(cls) -> PyInfoSchema: - """获取Python解释器信息""" + """ + 获取Python解释器信息 + + 返回: + - PyInfoSchema: Python解释器信息模型。 + """ current_process = psutil.Process() memory = psutil.virtual_memory() process_memory = current_process.memory_info() @@ -93,7 +118,12 @@ def _get_python_info(cls) -> PyInfoSchema: @classmethod def _get_disk_info(cls) -> List[DiskInfoSchema]: - """获取磁盘信息""" + """ + 获取磁盘信息 + + 返回: + - List[DiskInfoSchema]: 磁盘信息模型列表。 + """ disk_info = [] for partition in psutil.disk_partitions(): try: @@ -118,7 +148,15 @@ def _get_disk_info(cls) -> List[DiskInfoSchema]: @classmethod def _calculate_run_time(cls,start_time: float) -> str: - """计算运行时间""" + """ + 计算运行时间 + + 参数: + - start_time (float): 进程启动时间(时间戳) + + 返回: + - str: 格式化后的运行时间字符串(例如:"1天2小时3分钟") + """ difference = time.time() - start_time days = int(difference // (24 * 60 * 60)) hours = int((difference % (24 * 60 * 60)) // (60 * 60)) diff --git a/backend/app/api/v1/module_system/auth/controller.py b/backend/app/api/v1/module_system/auth/controller.py index 55bb200a..e0c9d614 100644 --- a/backend/app/api/v1/module_system/auth/controller.py +++ b/backend/app/api/v1/module_system/auth/controller.py @@ -39,6 +39,20 @@ async def login_for_access_token_controller( login_form: CustomOAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(db_getter), ) -> Union[JSONResponse, Dict]: + """ + 用户登录 + + 参数: + - request (Request): FastAPI请求对象 + - login_form (CustomOAuth2PasswordRequestForm): 登录表单数据 + - db (AsyncSession): 数据库会话对象 + + 返回: + - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型 + + 异常: + - CustomException: 认证失败时抛出异常。 + """ login_token = await LoginService.authenticate_user_service(request=request, redis=redis, login_form=login_form, db=db) logger.info(f"用户{login_form.username}登录成功") @@ -56,6 +70,19 @@ async def get_new_token_controller( db: AsyncSession = Depends(db_getter), redis: Redis = Depends(redis_getter) ) -> JSONResponse: + """ + 刷新token + + 参数: + - request (Request): FastAPI请求对象 + - payload (RefreshTokenPayloadSchema): 刷新令牌负载模型 + + 返回: + - JWTOutSchema: 包含新的访问令牌和刷新令牌的响应模型 + + 异常: + - CustomException: 刷新令牌失败时抛出异常。 + """ # 解析当前的访问Token以获取用户名 new_token = await LoginService.refresh_token_service(db=db, request=request, redis=redis, refresh_token=payload) token_dict = new_token.model_dump() @@ -67,6 +94,18 @@ async def get_new_token_controller( async def get_captcha_for_login_controller( redis: Redis = Depends(redis_getter) ) -> JSONResponse: + """ + 获取登录验证码 + + 参数: + - redis (Redis): Redis客户端对象 + + 返回: + - CaptchaOutSchema: 包含验证码图片和key的响应模型 + + 异常: + - CustomException: 获取验证码失败时抛出异常。 + """ # 获取验证码 captcha = await CaptchaService.get_captcha_service(redis=redis) logger.info(f"获取验证码成功") @@ -78,6 +117,19 @@ async def logout_controller( payload: LogoutPayloadSchema, redis: Redis = Depends(redis_getter) ) -> JSONResponse: + """ + 退出登录 + + 参数: + - payload (LogoutPayloadSchema): 退出登录负载模型 + - redis (Redis): Redis客户端对象 + + 返回: + - JSONResponse: 包含退出登录结果的响应模型 + + 异常: + - CustomException: 退出登录失败时抛出异常。 + """ if await LoginService.logout_service(redis=redis, token=payload): logger.info('退出成功') return SuccessResponse(msg='退出成功') diff --git a/backend/app/api/v1/module_system/auth/service.py b/backend/app/api/v1/module_system/auth/service.py index b2e72dcc..d9358e4b 100644 --- a/backend/app/api/v1/module_system/auth/service.py +++ b/backend/app/api/v1/module_system/auth/service.py @@ -47,16 +47,16 @@ async def authenticate_user_service(cls, request: Request, redis: Redis, login_f """ 用户认证 - Args: - request: 请求对象 - login_form: 登录表单 - db: 数据库会话 + 参数: + - request (Request): FastAPI请求对象 + - login_form (CustomOAuth2PasswordRequestForm): 登录表单数据 + - db (AsyncSession): 数据库会话对象 - Returns: - UserModel: 认证通过的用户对象 + 返回: + - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型 - Raises: - CustomException: 认证失败时抛出异常 + 异常: + - CustomException: 认证失败时抛出异常。 """ # 判断是否来自API文档 referer = request.headers.get('referer', '') @@ -98,12 +98,17 @@ async def create_token_service(cls, request: Request, redis: Redis, user: UserMo """ 创建访问令牌和刷新令牌 - Args: - username: 用户名 - request: 请求对象 + 参数: + - request (Request): FastAPI请求对象 + - redis (Redis): Redis客户端对象 + - user (UserModel): 用户模型对象 + - login_type (str): 登录类型 - Returns: - JWTOutSchema: 包含访问令牌和刷新令牌的响应对象 + 返回: + - JWTOutSchema: 包含访问令牌和刷新令牌的响应模型 + + 异常: + - CustomException: 创建令牌失败时抛出异常。 """ # 生成会话编号 session_id = str(uuid.uuid4()) @@ -181,14 +186,17 @@ async def refresh_token_service(cls, db: AsyncSession, redis: Redis, request: Re """ 刷新访问令牌 - Args: - refresh_token: 刷新令牌 + 参数: + - db (AsyncSession): 数据库会话对象 + - redis (Redis): Redis客户端对象 + - request (Request): FastAPI请求对象 + - refresh_token (RefreshTokenPayloadSchema): 刷新令牌数据 - Returns: - JWTOutSchema: 新的令牌对象 + 返回: + - JWTOutSchema: 新的令牌对象 - Raises: - CustomException: 刷新令牌无效时抛出异常 + 异常: + - CustomException: 刷新令牌无效时抛出异常 """ token_payload: JWTPayloadSchema = decode_access_token(token = refresh_token.refresh_token) if not token_payload.is_refresh: @@ -250,12 +258,15 @@ async def logout_service(cls, redis: Redis, token: LogoutPayloadSchema) -> bool: """ 退出登录 - Args: - request: 请求对象 - token: 令牌 + 参数: + - redis (Redis): Redis客户端对象 + - token (LogoutPayloadSchema): 退出登录令牌数据 + + 返回: + - bool: 退出成功返回True - Returns: - bool: 退出成功返回True + 异常: + - CustomException: 令牌无效时抛出异常 """ payload: JWTPayloadSchema = decode_access_token(token=token.token) session_info = json.loads(payload.sub) @@ -281,14 +292,14 @@ async def get_captcha_service(cls, redis: Redis) -> Dict[str, Union[CaptchaKey, """ 获取验证码 - Args: - request: 请求对象 + 参数: + - redis (Redis): Redis客户端对象 - Returns: - Dict: 包含验证码key和base64图片的字典 + 返回: + - Dict[str, Union[CaptchaKey, CaptchaBase64]]: 包含验证码key和base64图片的字典 - Raises: - CustomException: 验证码服务未启用时抛出异常 + 异常: + - CustomException: 验证码服务未启用时抛出异常 """ if not settings.CAPTCHA_ENABLE: raise CustomException(msg="未开启验证码服务") @@ -319,16 +330,16 @@ async def check_captcha_service(cls, redis: Redis, key: str, captcha: str) -> bo """ 校验验证码 - Args: - request: 请求对象 - key: 验证码key - captcha: 用户输入的验证码 + 参数: + - redis (Redis): Redis客户端对象 + - key (str): 验证码key + - captcha (str): 用户输入的验证码 - Returns: - bool: 验证通过返回True + 返回: + - bool: 验证通过返回True - Raises: - CustomException: 验证码无效或错误时抛出异常 + 异常: + - CustomException: 验证码无效或错误时抛出异常 """ if not captcha: raise CustomException(msg="验证码不能为空") diff --git a/backend/app/api/v1/module_system/dept/controller.py b/backend/app/api/v1/module_system/dept/controller.py index 8becab23..8f43fc34 100644 --- a/backend/app/api/v1/module_system/dept/controller.py +++ b/backend/app/api/v1/module_system/dept/controller.py @@ -25,6 +25,19 @@ async def get_dept_tree_controller( search: DeptQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:dept:query"])) ) -> JSONResponse: + """ + 查询部门树 + + 参数: + - search (DeptQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含部门树的响应模型 + + 异常: + - CustomException: 查询部门树失败时抛出异常。 + """ order_by = [{"order": "asc"}] result_dict_list = await DeptService.get_dept_tree_service(search=search, auth=auth, order_by=order_by) logger.info(f"查询部门树成功") @@ -36,6 +49,19 @@ async def get_obj_detail_controller( id: int = Path(..., description="部门ID"), auth: AuthSchema = Depends(AuthPermission(["system:dept:query"])) ) -> JSONResponse: + """ + 查询部门详情 + + 参数: + - id (int): 部门ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含部门详情的响应模型 + + 异常: + - CustomException: 查询部门详情失败时抛出异常。 + """ result_dict = await DeptService.get_dept_detail_service(id=id, auth=auth) logger.info(f"查询部门详情成功 {id}") return SuccessResponse(data=result_dict, msg="查询部门详情成功") @@ -46,6 +72,19 @@ async def create_obj_controller( data: DeptCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:dept:create"])) ) -> JSONResponse: + """ + 创建部门 + + 参数: + - data (DeptCreateSchema): 创建部门负载模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建部门结果的响应模型 + + 异常: + - CustomException: 创建部门失败时抛出异常。 + """ result_dict = await DeptService.create_dept_service(data=data, auth=auth) logger.info(f"创建部门成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建部门成功") @@ -57,6 +96,20 @@ async def update_obj_controller( id: int = Path(..., description="部门ID"), auth: AuthSchema = Depends(AuthPermission(["system:dept:update"])) ) -> JSONResponse: + """ + 修改部门 + + 参数: + - data (DeptUpdateSchema): 修改部门负载模型 + - id (int): 部门ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改部门结果的响应模型 + + 异常: + - CustomException: 修改部门失败时抛出异常。 + """ result_dict = await DeptService.update_dept_service(auth=auth, id=id, data=data) logger.info(f"修改部门成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改部门成功") @@ -67,6 +120,19 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:dept:delete"])) ) -> JSONResponse: + """ + 删除部门 + + 参数: + - ids (list[int]): 部门ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除部门结果的响应模型 + + 异常: + - CustomException: 删除部门失败时抛出异常。 + """ await DeptService.delete_dept_service(ids=ids, auth=auth) logger.info(f"删除部门成功: {ids}") return SuccessResponse(msg="删除部门成功") @@ -77,6 +143,19 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:dept:patch"])) ) -> JSONResponse: + """ + 批量修改部门状态 + + 参数: + - data (BatchSetAvailable): 批量修改部门状态负载模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含批量修改部门状态结果的响应模型 + + 异常: + - CustomException: 批量修改部门状态失败时抛出异常。 + """ await DeptService.batch_set_available_service(data=data, auth=auth) logger.info(f"批量修改部门状态成功: {data.ids}") return SuccessResponse(msg="批量修改部门状态成功") diff --git a/backend/app/api/v1/module_system/dept/crud.py b/backend/app/api/v1/module_system/dept/crud.py index 4a0bb5ca..cecfb3a7 100644 --- a/backend/app/api/v1/module_system/dept/crud.py +++ b/backend/app/api/v1/module_system/dept/crud.py @@ -18,10 +18,13 @@ def __init__(self, auth: AuthSchema) -> None: async def get_by_id_crud(self, id: int) -> Optional[DeptModel]: """ - 根据id获取部门信息 + 根据 id 获取部门信息。 - :param id: 部门ID - :return: 部门信息 + 参数: + - id (int): 部门 ID。 + + 返回: + - DeptModel | None: 部门信息,未找到返回 None。 """ obj = await self.get(id=id) if not obj: @@ -30,36 +33,52 @@ async def get_by_id_crud(self, id: int) -> Optional[DeptModel]: async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DeptModel]: """ - 获取部门列表 + 获取部门列表。 + + 参数: + - search (Dict | None): 搜索条件。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 部门列表 + 返回: + - Sequence[DeptModel]: 部门列表。 """ return await self.list(search=search, order_by=order_by) async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DeptModel]: """ - 获取部门树形列表 + 获取部门树形列表。 + + 参数: + - search (Dict | None): 搜索条件。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 部门树形列表 + 返回: + - Sequence[DeptModel]: 部门树形列表。 """ return await self.tree_list(search=search, order_by=order_by, children_attr='children') async def set_available_crud(self, ids: List[int], status: bool) -> None: """ - 批量设置部门可用状态 + 批量设置部门可用状态。 - :param ids: 部门ID列表 - :param status: 可用状态 + 参数: + - ids (List[int]): 部门 ID 列表。 + - status (bool): 可用状态。 + + 返回: + - None """ await self.set(ids=ids, status=status) async def get_name_crud(self, id: int) -> Optional[str]: """ - 根据id获取部门名称 + 根据 id 获取部门名称。 + + 参数: + - id (int): 部门 ID。 + + 返回: + - str | None: 部门名称,未找到返回 None。 """ obj = await self.get(id=id) return obj.name if obj else None diff --git a/backend/app/api/v1/module_system/dept/service.py b/backend/app/api/v1/module_system/dept/service.py index f40accc4..5c72e315 100644 --- a/backend/app/api/v1/module_system/dept/service.py +++ b/backend/app/api/v1/module_system/dept/service.py @@ -29,11 +29,14 @@ class DeptService: @classmethod async def get_dept_detail_service(cls, auth: AuthSchema, id: int) -> Dict: """ - 获取部门详情service + 获取部门详情。 - :param auth: 认证对象 - :param id: 部门ID - :return: 部门详情对象 + 参数: + - auth (AuthSchema): 认证对象。 + - id (int): 部门 ID。 + + 返回: + - Dict: 部门详情对象。 """ dept = await DeptCRUD(auth).get_by_id_crud(id=id) if dept and dept.parent_id: @@ -45,12 +48,15 @@ async def get_dept_detail_service(cls, auth: AuthSchema, id: int) -> Dict: @classmethod async def get_dept_tree_service(cls, auth: AuthSchema, search: Optional[DeptQueryParam]= None, order_by: Optional[List[Dict]] = None) -> List[Dict]: """ - 获取部门树形列表service + 获取部门树形列表。 + + 参数: + - auth (AuthSchema): 认证对象。 + - search (DeptQueryParam | None): 查询参数对象。 + - order_by (List[Dict] | None): 排序参数。 - :param auth: 认证对象 - :param search: 查询参数对象 - :param order_by: 排序参数 - :return: 部门树形列表对象 + 返回: + - List[Dict]: 部门树形列表对象。 """ # 使用树形结构查询,预加载children关系 dept_list = await DeptCRUD(auth).get_tree_list_crud(search=search.__dict__, order_by=order_by) @@ -62,11 +68,17 @@ async def get_dept_tree_service(cls, auth: AuthSchema, search: Optional[DeptQuer @classmethod async def create_dept_service(cls, auth: AuthSchema, data: DeptCreateSchema) -> Dict: """ - 创建部门service + 创建部门。 + + 参数: + - auth (AuthSchema): 认证对象。 + - data (DeptCreateSchema): 部门创建对象。 - :param auth: 认证对象 - :param data: 部门创建对象 - :return: 新创建的部门对象 + 返回: + - Dict: 新创建的部门对象。 + + 异常: + - CustomException: 当部门已存在时抛出。 """ dept = await DeptCRUD(auth).get(name=data.name) if dept: @@ -77,11 +89,18 @@ async def create_dept_service(cls, auth: AuthSchema, data: DeptCreateSchema) -> @classmethod async def update_dept_service(cls, auth: AuthSchema, id:int, data: DeptUpdateSchema) -> Dict: """ - 更新部门service + 更新部门。 + + 参数: + - auth (AuthSchema): 认证对象。 + - id (int): 部门 ID。 + - data (DeptUpdateSchema): 部门更新对象。 - :param auth: 认证对象 - :param data: 部门更新对象 - :return: 更新后的部门对象 + 返回: + - Dict: 更新后的部门对象。 + + 异常: + - CustomException: 当部门不存在或名称重复时抛出。 """ dept = await DeptCRUD(auth).get_by_id_crud(id=id) if not dept: @@ -99,10 +118,17 @@ async def update_dept_service(cls, auth: AuthSchema, id:int, data: DeptUpdateSch @classmethod async def delete_dept_service(cls, auth: AuthSchema, ids: list[int]) -> None: """ - 删除部门service + 删除部门。 + + 参数: + - auth (AuthSchema): 认证对象。 + - ids (List[int]): 部门 ID 列表。 - :param auth: 认证对象 - :param id: 部门ID + 返回: + - None + + 异常: + - CustomException: 当删除对象为空或部门不存在时抛出。 """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') @@ -115,10 +141,14 @@ async def delete_dept_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def batch_set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: """ - 批量设置部门可用状态service + 批量设置部门可用状态。 + + 参数: + - auth (AuthSchema): 认证对象。 + - data (BatchSetAvailable): 批量设置可用状态对象。 - :param auth: 认证对象 - :param data: 批量设置可用状态对象 + 返回: + - None """ dept_list = await DeptCRUD(auth).get_list_crud() total_ids = [] diff --git a/backend/app/api/v1/module_system/dict/controller.py b/backend/app/api/v1/module_system/dict/controller.py index e0ca7a46..34f067ea 100644 --- a/backend/app/api/v1/module_system/dict/controller.py +++ b/backend/app/api/v1/module_system/dict/controller.py @@ -31,6 +31,19 @@ async def get_type_detail_controller( id: int = Path(..., description="字典类型ID"), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: + """ + 获取字典类型详情 + + 参数: + - id (int): 字典类型ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含字典类型详情的响应模型 + + 异常: + - CustomException: 获取字典类型详情失败时抛出异常。 + """ result_dict = await DictTypeService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取字典类型详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取字典类型详情成功") @@ -41,6 +54,20 @@ async def get_type_list_controller( search: DictTypeQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: + """ + 查询字典类型列表 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (DictTypeQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含字典类型列表的响应模型 + + 异常: + - CustomException: 查询字典类型列表失败时抛出异常。 + """ result_dict_list = await DictTypeService.get_obj_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) logger.info(f"查询字典类型列表成功") @@ -50,6 +77,18 @@ async def get_type_list_controller( async def get_type_loptionselect_controller( auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: + """ + 获取全部字典类型 + + 参数: + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含全部字典类型的响应模型 + + 异常: + - CustomException: 获取字典类型列表失败时抛出异常。 + """ result_dict_list = await DictTypeService.get_obj_list_service(auth=auth) logger.info(f"获取字典类型列表成功") return SuccessResponse(data=result_dict_list, msg="获取字典类型列表成功") @@ -60,6 +99,20 @@ async def create_type_controller( redis: Redis = Depends(redis_getter), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:create"])) ) -> JSONResponse: + """ + 创建字典类型 + + 参数: + - data (DictTypeCreateSchema): 创建字典类型负载模型 + - redis (Redis): Redis数据库连接 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建字典类型结果的响应模型 + + 异常: + - CustomException: 创建字典类型失败时抛出异常。 + """ result_dict = await DictTypeService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建字典类型成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建字典类型成功") @@ -71,6 +124,21 @@ async def update_type_controller( id: int = Path(..., description="字典类型ID"), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:update"])) ) -> JSONResponse: + """ + 修改字典类型 + + 参数: + - data (DictTypeUpdateSchema): 修改字典类型负载模型 + - redis (Redis): Redis数据库连接 + - id (int): 字典类型ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改字典类型结果的响应模型 + + 异常: + - CustomException: 修改字典类型失败时抛出异常。 + """ result_dict = await DictTypeService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"修改字典类型成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改字典类型成功") @@ -81,6 +149,20 @@ async def delete_type_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:delete"])) ) -> JSONResponse: + """ + 删除字典类型 + + 参数: + - redis (Redis): Redis数据库连接 + - ids (list[int]): 字典类型ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除字典类型结果的响应模型 + + 异常: + - CustomException: 删除字典类型失败时抛出异常。 + """ await DictTypeService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除字典类型成功: {ids}") return SuccessResponse(msg="删除字典类型成功") @@ -90,6 +172,19 @@ async def batch_set_available_dict_type_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:dict_type:patch"])) ) -> JSONResponse: + """ + 批量修改字典类型状态 + + 参数: + - data (BatchSetAvailable): 批量修改字典类型状态负载模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含批量修改字典类型状态结果的响应模型 + + 异常: + - CustomException: 批量修改字典类型状态失败时抛出异常。 + """ await DictTypeService.set_obj_available_service(auth=auth, data=data) logger.info(f"批量修改字典类型状态成功: {data.ids}") return SuccessResponse(msg="批量修改字典类型状态成功") @@ -99,6 +194,19 @@ async def export_type_list_controller( search: DictTypeQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:dict_type:export"])) ) -> StreamingResponse: + """ + 导出字典类型 + + 参数: + - search (DictTypeQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出字典类型结果的响应模型 + + 异常: + - CustomException: 导出字典类型失败时抛出异常。 + """ # 获取全量数据 result_dict_list = await DictTypeService.get_obj_list_service(search=search, auth=auth) export_result = await DictTypeService.export_obj_service(data_list=result_dict_list) @@ -117,6 +225,19 @@ async def get_data_detail_controller( id: int = Path(..., description="字典数据ID"), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:query"])) ) -> JSONResponse: + """ + 获取字典数据详情 + + 参数: + - id (int): 字典数据ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含字典数据详情的响应模型 + + 异常: + - CustomException: 获取字典数据详情失败时抛出异常。 + """ result_dict = await DictDataService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取字典数据详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取字典数据详情成功") @@ -127,7 +248,24 @@ async def get_data_list_controller( search: DictDataQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:query"])) ) -> JSONResponse: - result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by) + """ + 查询字典数据 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (DictDataQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含字典数据列表的响应模型 + + 异常: + - CustomException: 查询字典数据列表失败时抛出异常。 + """ + order_by = [{"order": "asc"}] + if page.order_by: + order_by = page.order_by + result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=order_by) result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size) logger.info(f"查询字典数据列表成功") return SuccessResponse(data=result_dict, msg="查询字典数据列表成功") @@ -138,6 +276,20 @@ async def create_data_controller( redis: Redis = Depends(redis_getter), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:create"])) ) -> JSONResponse: + """ + 创建字典数据 + + 参数: + - data (DictDataCreateSchema): 创建字典数据负载模型 + - redis (Redis): Redis数据库连接 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建字典数据结果的响应模型 + + 异常: + - CustomException: 创建字典数据失败时抛出异常。 + """ result_dict = await DictDataService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建字典数据成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建字典数据成功") @@ -149,6 +301,21 @@ async def update_data_controller( id: int = Path(..., description="字典数据ID"), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:update"])) ) -> JSONResponse: + """ + 修改字典数据 + + 参数: + - data (DictDataUpdateSchema): 修改字典数据负载模型 + - redis (Redis): Redis数据库连接 + - id (int): 字典数据ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改字典数据结果的响应模型 + + 异常: + - CustomException: 修改字典数据失败时抛出异常。 + """ result_dict = await DictDataService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"修改字典数据成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改字典数据成功") @@ -159,6 +326,20 @@ async def delete_data_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:delete"])) ) -> JSONResponse: + """ + 删除字典数据 + + 参数: + - redis (Redis): Redis数据库连接 + - ids (list[int]): 字典数据ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除字典数据结果的响应模型 + + 异常: + - CustomException: 删除字典数据失败时抛出异常。 + """ await DictDataService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除字典数据成功: {ids}") return SuccessResponse(msg="删除字典数据成功") @@ -168,6 +349,19 @@ async def batch_set_available_dict_data_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:dict_data:patch"])) ) -> JSONResponse: + """ + 批量修改字典数据状态 + + 参数: + - data (BatchSetAvailable): 批量修改字典数据状态负载模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含批量修改字典数据状态结果的响应模型 + + 异常: + - CustomException: 批量修改字典数据状态失败时抛出异常。 + """ await DictDataService.set_obj_available_service(auth=auth, data=data) logger.info(f"批量修改字典数据状态成功: {data.ids}") return SuccessResponse(msg="批量修改字典数据状态成功") @@ -178,7 +372,20 @@ async def export_data_list_controller( page: PaginationQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:dict_data:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出字典数据 + + 参数: + - search (DictDataQueryParam): 查询参数模型 + - page (PaginationQueryParam): 分页参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出字典数据结果的响应模型 + + 异常: + - CustomException: 导出字典数据失败时抛出异常。 + """ result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by) export_result = await DictDataService.export_obj_service(data_list=result_dict_list) logger.info('导出字典数据成功') @@ -195,8 +402,20 @@ async def export_data_list_controller( async def get_init_dict_data_controller( dict_type: str, redis: Redis = Depends(redis_getter) -): - # 获取全量数据 +) -> JSONResponse: + """ + 根据字典类型获取数据 + + 参数: + - dict_type (str): 字典类型 + - redis (Redis): Redis数据库连接 + + 返回: + - JSONResponse: 包含根据字典类型获取数据结果的响应模型 + + 异常: + - CustomException: 根据字典类型获取数据失败时抛出异常。 + """ dict_data_query_result = await DictDataService.get_init_dict_service( redis=redis, dict_type=dict_type ) diff --git a/backend/app/api/v1/module_system/dict/crud.py b/backend/app/api/v1/module_system/dict/crud.py index e92c0ddd..0696a145 100644 --- a/backend/app/api/v1/module_system/dict/crud.py +++ b/backend/app/api/v1/module_system/dict/crud.py @@ -12,32 +12,88 @@ class DictTypeCRUD(CRUDBase[DictTypeModel, DictTypeCreateSchema, DictTypeUpdateS """数据字典类型数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化数据字典类型CRUD""" + """ + 初始化数据字典类型CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=DictTypeModel, auth=auth) async def get_obj_by_id_crud(self, id: int) -> Optional[DictTypeModel]: - """获取数据字典类型详情""" + """ + 获取数据字典类型详情 + + 参数: + - id (int): 数据字典类型ID + + 返回: + - Optional[DictTypeModel]: 数据字典类型模型,如果不存在则为None + """ return await self.get(id=id) async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DictTypeModel]: - """获取数据字典类型列表""" + """ + 获取数据字典类型列表 + + 参数: + - search (Optional[Dict]): 查询参数,默认值为None + - order_by (Optional[List[Dict[str, str]]]): 排序参数,默认值为None + + 返回: + - Sequence[DictTypeModel]: 数据字典类型模型序列 + """ return await self.list(search=search, order_by=order_by) async def create_obj_crud(self, data: DictTypeCreateSchema) -> Optional[DictTypeModel]: - """创数据字典类型""" + """ + 创建数据字典类型 + + 参数: + - data (DictTypeCreateSchema): 数据字典类型创建模型 + + 返回: + - Optional[DictTypeModel]: 创建的数据字典类型模型,如果创建失败则为None + """ return await self.create(data=data) async def update_obj_crud(self, id: int, data: DictTypeUpdateSchema) -> Optional[DictTypeModel]: - """更新数据字典类型""" + """ + 更新数据字典类型 + + 参数: + - id (int): 数据字典类型ID + - data (DictTypeUpdateSchema): 数据字典类型更新模型 + + 返回: + - Optional[DictTypeModel]: 更新的数据字典类型模型,如果更新失败则为None + """ return await self.update(id=id, data=data) async def delete_obj_crud(self, ids: List[int]) -> None: - """删除数据字典类型""" + """ + 删除数据字典类型 + + 参数: + - ids (List[int]): 数据字典类型ID列表 + + 返回: + - None + """ return await self.delete(ids=ids) async def set_obj_available_crud(self, ids: List[int], status: bool) -> None: - """设置数据字典类型的可用状态""" + """ + 设置数据字典类型的可用状态 + + 参数: + - ids (List[int]): 数据字典类型ID列表 + - status (bool): 可用状态,True表示可用,False表示不可用 + + 返回: + - None + """ return await self.set(ids=ids, status=status) @@ -45,30 +101,86 @@ class DictDataCRUD(CRUDBase[DictDataModel, DictDataCreateSchema, DictDataUpdateS """数据字典数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化数据字典数据CRUD""" + """ + 初始化数据字典数据CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=DictDataModel, auth=auth) async def get_obj_by_id_crud(self, id: int) -> Optional[DictDataModel]: - """获取数据字典数据详情""" + """ + 获取数据字典数据详情 + + 参数: + - id (int): 数据字典数据ID + + 返回: + - Optional[DictDataModel]: 数据字典数据模型,如果不存在则为None + """ return await self.get(id=id) async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[DictDataModel]: - """获取数据字典数据列表""" + """ + 获取数据字典数据列表 + + 参数: + - search (Optional[Dict]): 查询参数,默认值为None + - order_by (Optional[List[Dict[str, str]]]): 排序参数,默认值为None + + 返回: + - Sequence[DictDataModel]: 数据字典数据模型序列 + """ return await self.list(search=search, order_by=order_by) async def create_obj_crud(self, data: DictDataCreateSchema) -> Optional[DictDataModel]: - """创建数据字典数据""" + """ + 创建数据字典数据 + + 参数: + - data (DictDataCreateSchema): 数据字典数据创建模型 + + 返回: + - Optional[DictDataModel]: 创建的数据字典数据模型,如果创建失败则为None + """ return await self.create(data=data) async def update_obj_crud(self, id: int, data: DictDataUpdateSchema) -> Optional[DictDataModel]: - """更新数据字典数据""" + """ + 更新数据字典数据 + + 参数: + - id (int): 数据字典数据ID + - data (DictDataUpdateSchema): 数据字典数据更新模型 + + 返回: + - Optional[DictDataModel]: 更新的数据字典数据模型,如果更新失败则为None + """ return await self.update(id=id, data=data) async def delete_obj_crud(self, ids: List[int]) -> None: - """删除数据字典数据""" + """ + 删除数据字典数据 + + 参数: + - ids (List[int]): 数据字典数据ID列表 + + 返回: + - None + """ return await self.delete(ids=ids) async def set_obj_available_crud(self, ids: List[int], status: bool) -> None: - """设置数据字典数据的可用状态""" + """ + 设置数据字典数据的可用状态 + + 参数: + - ids (List[int]): 数据字典数据ID列表 + - status (bool): 可用状态,True表示可用,False表示不可用 + + 返回: + - None + """ return await self.set(ids=ids, status=status) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/dict/service.py b/backend/app/api/v1/module_system/dict/service.py index e00d4f09..0f4e7406 100644 --- a/backend/app/api/v1/module_system/dict/service.py +++ b/backend/app/api/v1/module_system/dict/service.py @@ -24,16 +24,48 @@ class DictTypeService: @classmethod async def get_obj_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取数据字典类型详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 数据字典类型ID + + 返回: + - Dict: 数据字典类型详情字典 + """ obj = await DictTypeCRUD(auth).get_obj_by_id_crud(id=id) return DictTypeOutSchema.model_validate(obj).model_dump() @classmethod async def get_obj_list_service(cls, auth: AuthSchema, search: Optional[DictTypeQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """ + 获取数据字典类型列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (DictTypeQueryParam | None): 搜索条件模型 + - order_by (List[Dict[str, str]] | None): 排序字段列表 + + 返回: + - List[Dict]: 数据字典类型详情字典列表 + """ obj_list = await DictTypeCRUD(auth).get_obj_list_crud(search=search.__dict__, order_by=order_by) return [DictTypeOutSchema.model_validate(obj).model_dump() for obj in obj_list] @classmethod async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: DictTypeCreateSchema) -> Dict: + """ + 创建数据字典类型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - data (DictTypeCreateSchema): 数据字典类型创建模型 + + 返回: + - Dict: 数据字典类型详情字典 + """ exist_obj = await DictTypeCRUD(auth).get(dict_name=data.dict_name) if exist_obj: raise CustomException(msg='创建失败,该数据字典类型已存在') @@ -57,6 +89,18 @@ async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: DictType @classmethod async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: DictTypeUpdateSchema) -> Dict: + """ + 更新数据字典类型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - id (int): 数据字典类型ID + - data (DictTypeUpdateSchema): 数据字典类型更新模型 + + 返回: + - Dict: 数据字典类型详情字典 + """ exist_obj = await DictTypeCRUD(auth).get_obj_by_id_crud(id=id) if not exist_obj: raise CustomException(msg='更新失败,该数据字典类型不存在') @@ -110,6 +154,17 @@ async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: @classmethod async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int]) -> None: + """ + 删除数据字典类型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - ids (list[int]): 数据字典类型ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -133,11 +188,29 @@ async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int] @classmethod async def set_obj_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: + """ + 设置数据字典类型状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置状态模型 + + 返回: + - None + """ await DictTypeCRUD(auth).set_obj_available_crud(ids=data.ids, status=data.status) @classmethod async def export_obj_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出字典类型列表""" + """ + 导出数据字典类型列表 + + 参数: + - data_list (List[Dict[str, Any]]): 数据字典类型列表 + + 返回: + - bytes: Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'dict_name': '字典名称', @@ -167,17 +240,46 @@ class DictDataService: @classmethod async def get_obj_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取数据字典数据详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 数据字典数据ID + + 返回: + - Dict: 数据字典数据详情字典 + """ obj = await DictDataCRUD(auth).get_obj_by_id_crud(id=id) return DictDataOutSchema.model_validate(obj).model_dump() @classmethod async def get_obj_list_service(cls, auth: AuthSchema, search: Optional[DictDataQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """ + 获取数据字典数据列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (DictDataQueryParam | None): 搜索条件模型 + - order_by (List[Dict[str, str]] | None): 排序字段列表 + + 返回: + - List[Dict]: 数据字典数据详情字典列表 + """ obj_list = await DictDataCRUD(auth).get_obj_list_crud(search=search.__dict__, order_by=order_by) return [DictDataOutSchema.model_validate(obj).model_dump() for obj in obj_list] @classmethod async def init_dict_service(cls, redis: Redis): - """应用初始化: 获取所有字典类型对应的字典数据信息并缓存service""" + """ + 应用初始化: 获取所有字典类型对应的字典数据信息并缓存service + + 参数: + - redis (Redis): Redis客户端 + + 返回: + - None + """ async with AsyncSessionLocal() as session: async with session.begin(): auth = AuthSchema(db=session) @@ -209,7 +311,16 @@ async def init_dict_service(cls, redis: Redis): @classmethod async def get_init_dict_service(cls, redis: Redis, dict_type: str)->List[Dict]: - """从缓存获取字典数据列表信息service""" + """ + 从缓存获取字典数据列表信息service + + 参数: + - redis (Redis): Redis客户端 + - dict_type (str): 字典类型 + + 返回: + - List[Dict]: 字典数据列表 + """ redis_key = f"{RedisInitKeyConfig.SYSTEM_DICT.key}:{dict_type}" obj_list_dict = await RedisCURD(redis).get(redis_key) if not obj_list_dict: @@ -218,6 +329,17 @@ async def get_init_dict_service(cls, redis: Redis, dict_type: str)->List[Dict]: @classmethod async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: DictDataCreateSchema) -> Dict: + """ + 创建数据字典数据 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - data (DictDataCreateSchema): 数据字典数据创建模型 + + 返回: + - Dict: 数据字典数据详情字典 + """ exist_obj = await DictDataCRUD(auth).get(dict_label=data.dict_label) if exist_obj: raise CustomException(msg='创建失败,该字典数据已存在') @@ -243,6 +365,18 @@ async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: DictData @classmethod async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: DictDataUpdateSchema) -> Dict: + """ + 更新数据字典数据 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - id (int): 数据字典数据ID + - data (DictDataUpdateSchema): 数据字典数据更新模型 + + 返回: + - Dict: 数据字典数据详情字典 + """ exist_obj = await DictDataCRUD(auth).get_obj_by_id_crud(id=id) if not exist_obj: raise CustomException(msg='更新失败,该字典数据不存在') @@ -298,6 +432,17 @@ async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: @classmethod async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int]) -> None: + """ + 删除数据字典数据 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis客户端 + - ids (list[int]): 数据字典数据ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') @@ -319,11 +464,29 @@ async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int] @classmethod async def set_obj_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: + """ + 批量修改数据字典数据状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量修改数据字典数据状态负载模型 + + 返回: + - None + """ await DictDataCRUD(auth).set_obj_available_crud(ids=data.ids, status=data.status) @classmethod async def export_obj_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出字典数据列表""" + """ + 导出数据字典数据列表 + + 参数: + - data_list (List[Dict[str, Any]]): 数据字典数据列表 + + 返回: + - bytes: Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'dict_sort': '字典排序', diff --git a/backend/app/api/v1/module_system/log/controller.py b/backend/app/api/v1/module_system/log/controller.py index c0f97d9c..b8b0e271 100644 --- a/backend/app/api/v1/module_system/log/controller.py +++ b/backend/app/api/v1/module_system/log/controller.py @@ -24,7 +24,17 @@ async def get_obj_list_controller( search: OperationLogQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:log:query"])) ) -> JSONResponse: - """ 查询日志 """ + """ + 查询日志 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (OperationLogQueryParam): 日志查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含分页日志详情的 JSON 响应模型 + """ order_by = [{"created_at": "desc"}] if page.order_by: order_by = page.order_by @@ -39,7 +49,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="操作日志ID"), auth: AuthSchema = Depends(AuthPermission(["system:log:query"])) ) -> JSONResponse: - """ 详情日志 """ + """ + 获取日志详情 + + 参数: + - id (int): 操作日志ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含日志详情的 JSON 响应模型 + """ result_dict = await OperationLogService.get_log_detail_service(id=id, auth=auth) logger.info(f"查询日志成功 {id}") return SuccessResponse(data=result_dict, msg="获取日志详情成功") @@ -50,7 +69,16 @@ async def delete_obj_log_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:log:delete"])) ) -> JSONResponse: - """ 删除日志 """ + """ + 删除日志 + + 参数: + - ids (list[int]): 日志 ID 列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除结果的 JSON 响应模型 + """ await OperationLogService.delete_log_service(ids=ids, auth=auth) logger.info(f"删除日志成功 {ids}") return SuccessResponse(msg="删除日志成功") @@ -61,7 +89,16 @@ async def export_obj_list_controller( search: OperationLogQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:log:export"])) ) -> StreamingResponse: - """ 导出日志 """ + """ + 导出日志 + + 参数: + - search (OperationLogQueryParam): 日志查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出日志的流式响应模型 + """ operation_log_list = await OperationLogService.get_log_list_service(search=search, auth=auth) operation_log_export_result = await OperationLogService.export_log_list_service(operation_log_list=operation_log_list) logger.info('导出日志成功') diff --git a/backend/app/api/v1/module_system/log/crud.py b/backend/app/api/v1/module_system/log/crud.py index d257386e..b35b0800 100644 --- a/backend/app/api/v1/module_system/log/crud.py +++ b/backend/app/api/v1/module_system/log/crud.py @@ -9,38 +9,51 @@ class OperationLogCRUD(CRUDBase[OperationLogModel, OperationLogCreateSchema, OperationLogCreateSchema]): - """操作日志数据层""" + """ + 操作日志数据层。 + """ def __init__(self, auth: AuthSchema) -> None: - """初始化操作日志CRUD""" + """ + 初始化操作日志CRUD。 + """ self.auth = auth super().__init__(model=OperationLogModel, auth=auth) async def create_crud(self, data: OperationLogCreateSchema) -> Optional[OperationLogModel]: """ - 创建操作日志记录 + 创建操作日志记录。 + + 参数: + - data (OperationLogCreateSchema): 操作日志创建模型。 - :param data: 操作日志创建模型 - :return: 操作日志记录 + 返回: + - OperationLogModel | None: 创建后的日志记录。 """ return await self.create(data=data) async def get_by_id_crud(self, id: int) -> Optional[OperationLogModel]: """ - 根据ID获取操作日志详情 + 根据ID获取操作日志详情。 - :param id: 操作日志ID - :return: 操作日志记录 + 参数: + - id (int): 操作日志ID。 + + 返回: + - OperationLogModel | None: 操作日志记录。 """ return await self.get(id=id) async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[OperationLogModel]: """ - 获取操作日志列表 + 获取操作日志列表。 + + 参数: + - search (Dict | None): 搜索条件字典。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 操作日志列表 + 返回: + - Sequence[OperationLogModel]: 操作日志列表。 """ return await self.list(search=search, order_by=order_by) diff --git a/backend/app/api/v1/module_system/log/service.py b/backend/app/api/v1/module_system/log/service.py index ab9349ef..deb2e52a 100644 --- a/backend/app/api/v1/module_system/log/service.py +++ b/backend/app/api/v1/module_system/log/service.py @@ -20,28 +20,65 @@ class OperationLogService: @classmethod async def get_log_detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取日志详情""" + """ + 获取日志详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 日志 ID + + 返回: + - Dict: 日志详情字典 + """ log = await OperationLogCRUD(auth).get_by_id_crud(id=id) log_dict = OperationLogOutSchema.model_validate(log).model_dump() return log_dict @classmethod async def get_log_list_service(cls, auth: AuthSchema, search: Optional[OperationLogQueryParam], order_by: Optional[List[Dict]] = None) -> List[Dict]: - """获取日志列表""" + """ + 获取日志列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[OperationLogQueryParam]): 日志查询参数模型 + - order_by (Optional[List[Dict]]): 排序字段列表 + + 返回: + - List[Dict]: 日志详情字典列表 + """ log_list = await OperationLogCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by) log_dict_list = [OperationLogOutSchema.model_validate(log).model_dump() for log in log_list] return log_dict_list @classmethod async def create_log_service(cls, auth: AuthSchema, data: OperationLogCreateSchema) -> Dict: - """创建日志""" + """ + 创建日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (OperationLogCreateSchema): 日志创建模型 + + 返回: + - Dict: 日志详情字典 + """ new_log = await OperationLogCRUD(auth).create(data=data) new_log_dict = OperationLogOutSchema.model_validate(new_log).model_dump() return new_log_dict @classmethod async def delete_log_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除日志""" + """ + 删除日志 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 日志 ID 列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') await OperationLogCRUD(auth).delete(ids=ids) @@ -51,11 +88,11 @@ async def export_log_list_service(cls, operation_log_list: List[Dict[str, Any]]) """ 导出日志信息 - Args: - operation_log_list: 操作日志信息列表 + 参数: + - operation_log_list (List[Dict[str, Any]]): 操作日志信息列表 - Returns: - bytes: 操作日志信息excel的二进制数据 + 返回: + - bytes: 操作日志信息excel的二进制数据 """ # 操作日志字段映射 mapping_dict = { diff --git a/backend/app/api/v1/module_system/menu/controller.py b/backend/app/api/v1/module_system/menu/controller.py index ead2330e..ee4dd31b 100644 --- a/backend/app/api/v1/module_system/menu/controller.py +++ b/backend/app/api/v1/module_system/menu/controller.py @@ -21,9 +21,18 @@ @MenuRouter.get("/tree", summary="查询菜单树", description="查询菜单树") async def get_menu_tree_controller( - search: MenuQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) + search: MenuQueryParam = Depends(), + auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) ) -> JSONResponse: + """ + 查询菜单树。 + + 参数: + - search (MenuQueryParam): 查询参数模型。 + + 返回: + - JSONResponse: 包含菜单树的 JSON 响应。 + """ order_by = [{"order": "asc"}] result_dict_list = await MenuService.get_menu_tree_service(search=search, auth=auth, order_by=order_by) logger.info(f"查询菜单树成功") @@ -32,9 +41,18 @@ async def get_menu_tree_controller( @MenuRouter.get("/detail/{id}", summary="查询菜单详情", description="查询菜单详情") async def get_obj_detail_controller( - id: int = Path(..., description="菜单ID"), - auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) + id: int = Path(..., description="菜单ID"), + auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) ) -> JSONResponse: + """ + 查询菜单详情。 + + 参数: + - id (int): 菜单ID。 + + 返回: + - JSONResponse: 包含菜单详情的 JSON 响应。 + """ result_dict = await MenuService.get_menu_detail_service(id=id, auth=auth) logger.info(f"查询菜单情成功 {id}") return SuccessResponse(data=result_dict, msg="获取菜单成功") @@ -45,6 +63,15 @@ async def create_obj_controller( data: MenuCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:menu:create"])) ) -> JSONResponse: + """ + 创建菜单。 + + 参数: + - data (MenuCreateSchema): 菜单创建模型。 + + 返回: + - JSONResponse: 包含创建菜单的 JSON 响应。 + """ result_dict = await MenuService.create_menu_service(data=data, auth=auth) logger.info(f"创建菜单成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建菜单成功") @@ -56,6 +83,16 @@ async def update_obj_controller( id: int = Path(..., description="菜单ID"), auth: AuthSchema = Depends(AuthPermission(["system:menu:update"])) ) -> JSONResponse: + """ + 修改菜单。 + + 参数: + - id (int): 菜单ID。 + - data (MenuUpdateSchema): 菜单更新模型。 + + 返回: + - JSONResponse: 包含修改菜单的 JSON 响应。 + """ result_dict = await MenuService.update_menu_service(id=id, data=data, auth=auth) logger.info(f"修改菜单成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改菜单成功") @@ -66,6 +103,15 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:menu:delete"])) ) -> JSONResponse: + """ + 删除菜单。 + + 参数: + - ids (list[int]): 菜单ID列表。 + + 返回: + - JSONResponse: 包含删除菜单的 JSON 响应。 + """ await MenuService.delete_menu_service(ids=ids, auth=auth) logger.info(f"删除菜单成功: {ids}") return SuccessResponse(msg="删除菜单成功") @@ -76,6 +122,15 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:menu:patch"])) ) -> JSONResponse: + """ + 批量修改菜单状态。 + + 参数: + - data (BatchSetAvailable): 批量修改菜单状态模型。 + + 返回: + - JSONResponse: 批量修改菜单状态的 JSON 响应。 + """ await MenuService.set_menu_available_service(data=data, auth=auth) logger.info(f"批量修改菜单状态成功: {data.ids}") return SuccessResponse(msg="批量修改菜单状态成功") \ No newline at end of file diff --git a/backend/app/api/v1/module_system/menu/crud.py b/backend/app/api/v1/module_system/menu/crud.py index 9c5ccf49..d647d3d9 100644 --- a/backend/app/api/v1/module_system/menu/crud.py +++ b/backend/app/api/v1/module_system/menu/crud.py @@ -18,10 +18,13 @@ def __init__(self, auth: AuthSchema) -> None: async def get_by_id_crud(self, id: int) -> Optional[MenuModel]: """ - 根据id获取菜单信息 + 根据 id 获取菜单信息。 - :param id: 菜单ID - :return: 菜单信息 + 参数: + - id (int): 菜单 ID。 + + 返回: + - MenuModel | None: 菜单信息,未找到返回 None。 """ obj = await self.get(id=id) if not obj: @@ -30,29 +33,39 @@ async def get_by_id_crud(self, id: int) -> Optional[MenuModel]: async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[MenuModel]: """ - 获取菜单列表 + 获取菜单列表。 + + 参数: + - search (Dict | None): 搜索条件。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 菜单列表 + 返回: + - Sequence[MenuModel]: 菜单列表。 """ return await self.list(search=search, order_by=order_by) async def get_tree_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[MenuModel]: """ - 获取菜单树形列表 + 获取菜单树形列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 菜单树形列表 + 参数: + - search (Dict | None): 搜索条件。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 + + 返回: + - Sequence[MenuModel]: 菜单树形列表。 """ return await self.tree_list(search=search, order_by=order_by, children_attr='children') async def set_available_crud(self, ids: List[int], status: bool) -> None: """ - 批量设置菜单可用状态 + 批量设置菜单可用状态。 + + 参数: + - ids (List[int]): 菜单 ID 列表。 + - status (bool): 可用状态。 - :param ids: 菜单ID列表 - :param status: 可用状态 + 返回: + - None """ await self.set(ids=ids, status=status) diff --git a/backend/app/api/v1/module_system/menu/service.py b/backend/app/api/v1/module_system/menu/service.py index 0e6b135d..cb29d01d 100644 --- a/backend/app/api/v1/module_system/menu/service.py +++ b/backend/app/api/v1/module_system/menu/service.py @@ -28,6 +28,16 @@ class MenuService: @classmethod async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取菜单详情。 + + 参数: + - auth (AuthSchema): 认证对象。 + - id (int): 菜单ID。 + + 返回: + - Dict: 菜单详情对象。 + """ menu = await MenuCRUD(auth).get_by_id_crud(id=id) if menu and menu.parent_id: parent = await MenuCRUD(auth).get_by_id_crud(id=menu.parent_id) @@ -40,12 +50,15 @@ async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> Dict: @classmethod async def get_menu_tree_service(cls, auth: AuthSchema, search: Optional[MenuQueryParam] = None, order_by: Optional[List[Dict]] = None) -> List[Dict]: """ - 获取菜单树形列表service + 获取菜单树形列表。 + + 参数: + - auth (AuthSchema): 认证对象。 + - search (MenuQueryParam | None): 查询参数对象。 + - order_by (List[Dict] | None): 排序参数列表。 - :param auth: 认证对象 - :param search: 查询参数对象 - :param order_by: 排序参数 - :return: 菜单树形列表对象 + 返回: + - List[Dict]: 菜单树形列表对象。 """ # 使用树形结构查询,预加载children关系 menu_list = await MenuCRUD(auth).get_tree_list_crud(search=search.__dict__, order_by=order_by) @@ -56,6 +69,16 @@ async def get_menu_tree_service(cls, auth: AuthSchema, search: Optional[MenuQuer @classmethod async def create_menu_service(cls, auth: AuthSchema, data: MenuCreateSchema) -> Dict: + """ + 创建菜单。 + + 参数: + - auth (AuthSchema): 认证对象。 + - data (MenuCreateSchema): 创建参数对象。 + + 返回: + - Dict: 创建的菜单对象。 + """ menu = await MenuCRUD(auth).get(name=data.name) if menu: raise CustomException(msg='创建失败,该菜单已存在') @@ -66,6 +89,17 @@ async def create_menu_service(cls, auth: AuthSchema, data: MenuCreateSchema) -> @classmethod async def update_menu_service(cls, auth: AuthSchema,id:int, data: MenuUpdateSchema) -> Dict: + """ + 更新菜单。 + + 参数: + - auth (AuthSchema): 认证对象。 + - id (int): 菜单ID。 + - data (MenuUpdateSchema): 更新参数对象。 + + 返回: + - Dict: 更新的菜单对象。 + """ menu = await MenuCRUD(auth).get_by_id_crud(id=id) if not menu: raise CustomException(msg='更新失败,该菜单不存在') @@ -87,6 +121,16 @@ async def update_menu_service(cls, auth: AuthSchema,id:int, data: MenuUpdateSche @classmethod async def delete_menu_service(cls, auth: AuthSchema, ids: list[int]) -> None: + """ + 删除菜单。 + + 参数: + - auth (AuthSchema): 认证对象。 + - ids (list[int]): 菜单ID列表。 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -98,7 +142,14 @@ async def delete_menu_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def set_menu_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: """ - 递归获取所有父、子级菜单,然后批量修改菜单可用状态 + 递归获取所有父、子级菜单,然后批量修改菜单可用状态。 + + 参数: + - auth (AuthSchema): 认证对象。 + - data (BatchSetAvailable): 批量设置可用参数对象。 + + 返回: + - None """ menu_list = await MenuCRUD(auth).get_list_crud() total_ids = [] diff --git a/backend/app/api/v1/module_system/notice/controller.py b/backend/app/api/v1/module_system/notice/controller.py index 8c5f847e..cd01bd8e 100644 --- a/backend/app/api/v1/module_system/notice/controller.py +++ b/backend/app/api/v1/module_system/notice/controller.py @@ -27,6 +27,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="公告ID"), auth: AuthSchema = Depends(AuthPermission(["system:notice:query"])) ) -> JSONResponse: + """ + 获取公告详情。 + + 参数: + - id (int): 公告ID。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含公告详情的响应模型。 + """ result_dict = await NoticeService.get_notice_detail_service(id=id, auth=auth) logger.info(f"获取公告详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取公告详情成功") @@ -37,6 +47,17 @@ async def get_obj_list_controller( search: NoticeQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:notice:query"])) ) -> JSONResponse: + """ + 查询公告。 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型。 + - search (NoticeQueryParam): 查询公告参数模型。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含分页公告详情的响应模型。 + """ result_dict_list = await NoticeService.get_notice_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) logger.info(f"查询公告列表成功") @@ -47,6 +68,16 @@ async def create_obj_controller( data: NoticeCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:notice:create"])) ) -> JSONResponse: + """ + 创建公告。 + + 参数: + - data (NoticeCreateSchema): 创建公告负载模型。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含创建公告结果的响应模型。 + """ result_dict = await NoticeService.create_notice_service(auth=auth, data=data) logger.info(f"创建公告成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建公告成功") @@ -57,6 +88,17 @@ async def update_obj_controller( id: int = Path(..., description="公告ID"), auth: AuthSchema = Depends(AuthPermission(["system:notice:update"])) ) -> JSONResponse: + """ + 修改公告。 + + 参数: + - data (NoticeUpdateSchema): 修改公告负载模型。 + - id (int): 公告ID。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含修改公告结果的响应模型。 + """ result_dict = await NoticeService.update_notice_service(auth=auth, id=id, data=data) logger.info(f"修改公告成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改公告成功") @@ -66,6 +108,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:notice:delete"])) ) -> JSONResponse: + """ + 删除公告。 + + 参数: + - ids (list[int]): 公告ID列表。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含删除公告结果的响应模型。 + """ await NoticeService.delete_notice_service(auth=auth, ids=ids) logger.info(f"删除公告成功: {ids}") return SuccessResponse(msg="删除公告成功") @@ -75,6 +127,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:notice:patch"])) ) -> JSONResponse: + """ + 批量修改公告状态。 + + 参数: + - data (BatchSetAvailable): 批量修改公告状态负载模型。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含批量修改公告状态结果的响应模型。 + """ await NoticeService.set_notice_available_service(auth=auth, data=data) logger.info(f"批量修改公告状态成功: {data.ids}") return SuccessResponse(msg="批量修改公告状态成功") @@ -84,7 +146,16 @@ async def export_obj_list_controller( search: NoticeQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:notice:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出公告。 + + 参数: + - search (NoticeQueryParam): 查询公告参数模型。 + - auth (AuthSchema): 认证信息模型。 + + 返回: + - StreamingResponse: 包含导出公告的流式响应模型。 + """ result_dict_list = await NoticeService.get_notice_list_service(search=search, auth=auth) export_result = await NoticeService.export_notice_service(notice_list=result_dict_list) logger.info('导出公告成功') @@ -102,6 +173,15 @@ async def export_obj_list_controller( async def get_obj_list_available_controller( auth: AuthSchema = Depends(get_current_user) ) -> JSONResponse: + """ + 获取全局启用公告。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + + 返回: + - JSONResponse: 包含分页已启用公告详情的响应模型。 + """ result_dict_list = await NoticeService.get_notice_list_available_service(auth=auth) result_dict = await PaginationService.paginate(data_list= result_dict_list) logger.info(f"查询已启用公告列表成功") diff --git a/backend/app/api/v1/module_system/notice/crud.py b/backend/app/api/v1/module_system/notice/crud.py index aad22923..a88efda8 100644 --- a/backend/app/api/v1/module_system/notice/crud.py +++ b/backend/app/api/v1/module_system/notice/crud.py @@ -12,30 +12,86 @@ class NoticeCRUD(CRUDBase[NoticeModel, NoticeCreateSchema, NoticeUpdateSchema]): """公告数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化CRUD""" + """ + 初始化公告数据层。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + """ self.auth = auth super().__init__(model=NoticeModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[NoticeModel]: - """获取公告详情""" + """ + 根据ID获取公告详情。 + + 参数: + - id (int): 公告ID。 + + 返回: + - Optional[NoticeModel]: 公告模型实例。 + """ return await self.get(id=id) async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[NoticeModel]: - """获取公告列表""" + """ + 获取公告列表。 + + 参数: + - search (Optional[Dict]): 查询参数。 + - order_by (Optional[List[Dict[str, str]]]): 排序参数。 + + 返回: + - Sequence[NoticeModel]: 公告模型实例列表。 + """ return await self.list(search=search, order_by=order_by) async def create_crud(self, data: NoticeCreateSchema) -> Optional[NoticeModel]: - """创建公告""" + """ + 创建公告。 + + 参数: + - data (NoticeCreateSchema): 公告创建模型。 + + 返回: + - Optional[NoticeModel]: 公告模型实例。 + """ return await self.create(data=data) async def update_crud(self, id: int, data: NoticeUpdateSchema) -> Optional[NoticeModel]: - """更新公告""" + """ + 更新公告。 + + 参数: + - id (int): 公告ID。 + - data (NoticeUpdateSchema): 公告更新模型。 + + 返回: + - Optional[NoticeModel]: 公告模型实例。 + """ return await self.update(id=id, data=data) async def delete_crud(self, ids: List[int]) -> None: - """删除公告""" + """ + 删除公告。 + + 参数: + - ids (List[int]): 公告ID列表。 + + 返回: + - None + """ return await self.delete(ids=ids) async def set_available_crud(self, ids: List[int], status: bool) -> None: - """设置公告的可用状态""" + """ + 设置公告的可用状态。 + + 参数: + - ids (List[int]): 公告ID列表。 + - status (bool): 可用状态。 + + 返回: + - None + """ return await self.set(ids=ids, status=status) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/notice/service.py b/backend/app/api/v1/module_system/notice/service.py index 036e078f..b69b81d0 100644 --- a/backend/app/api/v1/module_system/notice/service.py +++ b/backend/app/api/v1/module_system/notice/service.py @@ -19,21 +19,64 @@ class NoticeService: @classmethod async def get_notice_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取公告详情。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - id (int): 公告ID。 + + 返回: + - Dict: 公告详情字典。 + """ notice_obj = await NoticeCRUD(auth).get_by_id_crud(id=id) return NoticeOutSchema.model_validate(notice_obj).model_dump() @classmethod async def get_notice_list_available_service(cls, auth: AuthSchema) -> List[Dict]: + """ + 获取可用的公告列表。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + + 返回: + - List[Dict]: 可用公告详情字典列表。 + """ notice_obj_list = await NoticeCRUD(auth).get_list_crud(search={'status': True}) return [NoticeOutSchema.model_validate(notice_obj).model_dump() for notice_obj in notice_obj_list] @classmethod async def get_notice_list_service(cls, auth: AuthSchema, search: Optional[NoticeQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """ + 获取公告列表。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - search (Optional[NoticeQueryParam]): 查询参数模型。 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表。 + + 返回: + - List[Dict]: 公告详情字典列表。 + """ notice_obj_list = await NoticeCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by) return [NoticeOutSchema.model_validate(notice_obj).model_dump() for notice_obj in notice_obj_list] @classmethod async def create_notice_service(cls, auth: AuthSchema, data: NoticeCreateSchema) -> Dict: + """ + 创建公告。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - data (NoticeCreateSchema): 创建公告负载模型。 + + 返回: + - Dict: 创建的公告详情字典。 + + 异常: + - CustomException: 创建失败,该公告通知已存在。 + """ notice = await NoticeCRUD(auth).get(notice_title=data.notice_title) if notice: raise CustomException(msg='创建失败,该公告通知已存在') @@ -42,6 +85,20 @@ async def create_notice_service(cls, auth: AuthSchema, data: NoticeCreateSchema) @classmethod async def update_notice_service(cls, auth: AuthSchema, id: int, data: NoticeUpdateSchema) -> Dict: + """ + 更新公告。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - id (int): 公告ID。 + - data (NoticeUpdateSchema): 更新公告负载模型。 + + 返回: + - Dict: 更新的公告详情字典。 + + 异常: + - CustomException: 更新失败,该公告通知不存在或公告通知标题重复。 + """ notice = await NoticeCRUD(auth).get_by_id_crud(id=id) if not notice: raise CustomException(msg='更新失败,该公告通知不存在') @@ -53,6 +110,16 @@ async def update_notice_service(cls, auth: AuthSchema, id: int, data: NoticeUpda @classmethod async def delete_notice_service(cls, auth: AuthSchema, ids: list[int]) -> None: + """ + 删除公告。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - ids (list[int]): 删除的ID列表。 + + 异常: + - CustomException: 删除失败,删除对象不能为空或该公告通知不存在。 + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -63,11 +130,29 @@ async def delete_notice_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def set_notice_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: + """ + 批量设置公告状态。 + + 参数: + - auth (AuthSchema): 认证信息模型。 + - data (BatchSetAvailable): 批量设置可用负载模型。 + + 异常: + - CustomException: 批量设置失败,该公告通知不存在。 + """ await NoticeCRUD(auth).set_available_crud(ids=data.ids, status=data.status) @classmethod async def export_notice_service(cls, notice_list: List[Dict[str, Any]]) -> bytes: - """导出公告列表""" + """ + 导出公告列表。 + + 参数: + - notice_list (List[Dict[str, Any]]): 公告详情字典列表。 + + 返回: + - bytes: Excel 文件的字节流。 + """ mapping_dict = { 'id': '编号', 'notice_title': '公告标题', diff --git a/backend/app/api/v1/module_system/params/controller.py b/backend/app/api/v1/module_system/params/controller.py index 8a9b5b52..d8ee0dd4 100644 --- a/backend/app/api/v1/module_system/params/controller.py +++ b/backend/app/api/v1/module_system/params/controller.py @@ -25,6 +25,16 @@ async def get_type_detail_controller( id: int = Path(..., description="参数ID"), auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: + """ + 获取参数详情 + + 参数: + - id (int): 参数ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含参数详情的 JSON 响应 + """ result_dict = await ParamsService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取参数详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取参数详情成功") @@ -35,6 +45,16 @@ async def get_obj_by_key_controller( config_key: str = Path(..., description="配置键"), auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: + """ + 根据配置键获取参数详情 + + 参数: + - config_key (str): 配置键 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含参数详情的 JSON 响应 + """ result_dict = await ParamsService.get_obj_by_key_service(config_key=config_key, auth=auth) logger.info(f"根据配置键获取参数详情成功 {config_key}") return SuccessResponse(data=result_dict, msg="根据配置键获取参数详情成功") @@ -45,6 +65,16 @@ async def get_config_value_by_key_controller( config_key: str = Path(..., description="配置键"), auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: + """ + 根据配置键获取参数值 + + 参数: + - config_key (str): 配置键 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含参数值的 JSON 响应 + """ result_value = await ParamsService.get_config_value_by_key_service(config_key=config_key, auth=auth) logger.info(f"根据配置键获取参数值成功 {config_key}") return SuccessResponse(data=result_value, msg="根据配置键获取参数值成功") @@ -56,6 +86,17 @@ async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: ParamsQueryParam = Depends(), ) -> JSONResponse: + """ + 获取参数列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - page (PaginationQueryParam): 分页查询参数模型 + - search (ParamsQueryParam): 参数查询参数模型 + + 返回: + - JSONResponse: 包含参数列表的 JSON 响应 + """ result_dict_list = await ParamsService.get_obj_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) logger.info(f"获取参数列表成功") @@ -68,6 +109,17 @@ async def create_obj_controller( redis: Redis = Depends(redis_getter), auth: AuthSchema = Depends(AuthPermission(["system:param:create"])) ) -> JSONResponse: + """ + 创建参数 + + 参数: + - data (ParamsCreateSchema): 参数创建模型 + - redis (Redis): Redis 客户端实例 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含创建参数结果的 JSON 响应 + """ result_dict = await ParamsService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建参数成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建参数成功") @@ -80,6 +132,18 @@ async def update_objs_controller( redis: Redis = Depends(redis_getter), auth: AuthSchema = Depends(AuthPermission(["system:param:update"])) ) -> JSONResponse: + """ + 修改参数 + + 参数: + - data (ParamsUpdateSchema): 参数更新模型 + - id (int): 参数ID + - redis (Redis): Redis 客户端实例 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含修改参数结果的 JSON 响应 + """ result_dict = await ParamsService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"更新参数成功 {result_dict}") return SuccessResponse(data=result_dict, msg="更新参数成功") @@ -91,6 +155,17 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:param:delete"])) ) -> JSONResponse: + """ + 删除参数 + + 参数: + - redis (Redis): Redis 客户端实例 + - ids (list[int]): 参数ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 包含删除参数结果的 JSON 响应 + """ await ParamsService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除参数成功: {ids}") return SuccessResponse(msg="删除参数成功") @@ -101,7 +176,16 @@ async def export_obj_list_controller( search: ParamsQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:param:export"])) ) -> StreamingResponse: - # 获取全量数据 + """ + 导出参数 + + 参数: + - search (ParamsQueryParam): 参数查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 包含导出参数的 Excel 文件流响应 + """ result_dict_list = await ParamsService.get_obj_list_service(search=search, auth=auth) export_result = await ParamsService.export_obj_service(data_list=result_dict_list) logger.info('导出参数成功') @@ -120,6 +204,16 @@ async def upload_file_controller( file: UploadFile, request: Request ) -> JSONResponse: + """ + 上传文件 + + 参数: + - file (UploadFile): 上传的文件对象 + - request (Request): 请求对象 + + 返回: + - JSONResponse: 包含上传文件结果的 JSON 响应 + """ result_str = await ParamsService.upload_service(base_url=str(request.base_url), file=file) logger.info(f"上传文件: {result_str}") return SuccessResponse(data=result_str, msg='上传文件成功') @@ -129,6 +223,15 @@ async def upload_file_controller( async def get_init_obj_controller( redis: Redis = Depends(redis_getter), ) -> JSONResponse: + """ + 获取初始化缓存参数 + + 参数: + - redis (Redis): Redis 客户端实例 + + 返回: + - JSONResponse: 获取初始化缓存参数的 JSON 响应 + """ result_dict = await ParamsService.get_init_config_service(redis=redis) logger.info(f"获取初始化缓存参数成功 {result_dict}") return SuccessResponse(data=result_dict, msg="获取初始化缓存参数成功") \ No newline at end of file diff --git a/backend/app/api/v1/module_system/params/crud.py b/backend/app/api/v1/module_system/params/crud.py index b68f9d5c..1bb70ca0 100644 --- a/backend/app/api/v1/module_system/params/crud.py +++ b/backend/app/api/v1/module_system/params/crud.py @@ -12,30 +12,85 @@ class ParamsCRUD(CRUDBase[ParamsModel, ParamsCreateSchema, ParamsUpdateSchema]): """配置管理数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化配置CRUD""" + """ + 初始化配置CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=ParamsModel, auth=auth) async def get_obj_by_id_crud(self, id: int) -> Optional[ParamsModel]: - """获取配置管理型详情""" + """ + 获取配置管理型详情 + + 参数: + - id (int): 配置管理型ID + + 返回: + - Optional[ParamsModel]: 配置管理型模型实例 + """ return await self.get(id=id) async def get_obj_by_key_crud(self, key: str) -> Optional[ParamsModel]: - """根据key获取配置管理型详情""" + """ + 根据key获取配置管理型详情 + + 参数: + - key (str): 配置管理型key + + 返回: + - Optional[ParamsModel]: 配置管理型模型实例 + """ return await self.get(config_key=key) async def get_obj_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[ParamsModel]: - """获取配置管理型列表""" + """ + 获取配置管理型列表 + + 参数: + - search (Dict | None): 查询参数对象。 + - order_by (List[Dict[str, str]] | None): 排序参数列表。 + + 返回: + - Sequence[ParamsModel]: 配置管理型模型实例列表 + """ return await self.list(search=search, order_by=order_by) async def create_obj_crud(self, data: ParamsCreateSchema) -> Optional[ParamsModel]: - """创建配置管理型""" + """ + 创建配置管理型 + + 参数: + - data (ParamsCreateSchema): 创建配置管理型负载模型 + + 返回: + - Optional[ParamsModel]: 配置管理型模型实例 + """ return await self.create(data=data) async def update_obj_crud(self, id: int, data: ParamsUpdateSchema) -> Optional[ParamsModel]: - """更新配置管理型""" + """ + 更新配置管理型 + + 参数: + - id (int): 配置管理型ID + - data (ParamsUpdateSchema): 更新配置管理型负载模型 + + 返回: + - Optional[ParamsModel]: 配置管理型模型实例 + """ return await self.update(id=id, data=data) async def delete_obj_crud(self, ids: List[int]) -> None: - """删除配置管理型""" + """ + 删除配置管理型 + + 参数: + - ids (List[int]): 配置管理型ID列表 + + 返回: + - None + """ return await self.delete(ids=ids) diff --git a/backend/app/api/v1/module_system/params/service.py b/backend/app/api/v1/module_system/params/service.py index 33afb37a..c1564fc3 100644 --- a/backend/app/api/v1/module_system/params/service.py +++ b/backend/app/api/v1/module_system/params/service.py @@ -27,12 +27,31 @@ class ParamsService: """ @classmethod async def get_obj_detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """ + 获取配置详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 配置管理型ID + + 返回: + - Dict: 配置管理型模型实例字典表示 + """ obj = await ParamsCRUD(auth).get_obj_by_id_crud(id=id) return ParamsOutSchema.model_validate(obj).model_dump() @classmethod async def get_obj_by_key_service(cls, auth: AuthSchema, config_key: str) -> Dict: - """根据配置键获取配置详情""" + """ + 根据配置键获取配置详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - config_key (str): 配置管理型key + + 返回: + - Dict: 配置管理型模型实例字典表示 + """ obj = await ParamsCRUD(auth).get_obj_by_key_crud(key=config_key) if not obj: raise CustomException(msg=f'配置键 {config_key} 不存在') @@ -40,7 +59,16 @@ async def get_obj_by_key_service(cls, auth: AuthSchema, config_key: str) -> Dict @classmethod async def get_config_value_by_key_service(cls, auth: AuthSchema, config_key: str) -> str | None: - """根据配置键获取配置值""" + """ + 根据配置键获取配置值 + + 参数: + - auth (AuthSchema): 认证信息模型 + - config_key (str): 配置管理型key + + 返回: + - str | None: 配置值字符串或None + """ obj = await ParamsCRUD(auth).get_obj_by_key_crud(key=config_key) if not obj: raise CustomException(msg=f'配置键 {config_key} 不存在') @@ -48,6 +76,17 @@ async def get_config_value_by_key_service(cls, auth: AuthSchema, config_key: str @classmethod async def get_obj_list_service(cls, auth: AuthSchema, search: Optional[ParamsQueryParam] = None, order_by: Optional[List[Dict[str, str]]]= None) -> List[Dict]: + """ + 获取配置管理型列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (ParamsQueryParam | None): 查询参数对象 + - order_by (List[Dict[str, str]] | None): 排序参数列表 + + 返回: + - List[Dict]: 配置管理型模型实例字典列表表示 + """ obj_list = None if search: obj_list = await ParamsCRUD(auth).get_obj_list_crud(search=search.__dict__, order_by=order_by) @@ -57,6 +96,17 @@ async def get_obj_list_service(cls, auth: AuthSchema, search: Optional[ParamsQue @classmethod async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: ParamsCreateSchema) -> Dict: + """ + 创建配置管理型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis 客户端实例 + - data (ParamsCreateSchema): 配置管理型创建模型 + + 返回: + - Dict: 新创建的配置管理型模型实例字典表示 + """ exist_obj = await ParamsCRUD(auth).get(config_key=data.config_key) if exist_obj: raise CustomException(msg='创建失败,该配置key已存在') @@ -82,6 +132,18 @@ async def create_obj_service(cls, auth: AuthSchema, redis: Redis, data: ParamsCr @classmethod async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: ParamsUpdateSchema) -> Dict: + """ + 更新配置管理型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis 客户端实例 + - id (int): 配置管理型ID + - data (ParamsUpdateSchema): 配置管理型更新模型 + + 返回: + - Dict: 更新后的配置管理型模型实例字典表示 + """ exist_obj = await ParamsCRUD(auth).get_obj_by_id_crud(id=id) if not exist_obj: raise CustomException(msg='更新失败,该数系统配置不存在') @@ -112,6 +174,17 @@ async def update_obj_service(cls, auth: AuthSchema, redis: Redis, id:int, data: @classmethod async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int]) -> None: + """ + 删除配置管理型 + + 参数: + - auth (AuthSchema): 认证信息模型 + - redis (Redis): Redis 客户端实例 + - ids (list[int]): 配置管理型ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -140,7 +213,15 @@ async def delete_obj_service(cls, auth: AuthSchema, redis: Redis, ids: list[int] @classmethod async def export_obj_service(cls, data_list: List[Dict[str, Any]]) -> bytes: - """导出系统配置列表""" + """ + 导出系统配置列表 + + 参数: + - data_list (List[Dict[str, Any]]): 系统配置模型实例字典列表表示 + + 返回: + - bytes: Excel文件二进制数据 + """ mapping_dict = { 'id': '编号', 'config_name': '参数名称', @@ -165,7 +246,16 @@ async def export_obj_service(cls, data_list: List[Dict[str, Any]]) -> bytes: @classmethod async def upload_service(cls, base_url: str, file: UploadFile) -> Dict: - """上传文件""" + """ + 上传文件 + + 参数: + - base_url (str): 基础URL + - file (UploadFile): 上传的文件对象 + + 返回: + - Dict: 上传文件的响应模型实例字典表示 + """ filename, filepath, file_url = await UploadUtil.upload_file(file=file, base_url=base_url) return UploadResponseSchema( @@ -177,6 +267,15 @@ async def upload_service(cls, base_url: str, file: UploadFile) -> Dict: @classmethod async def init_config_service(cls, redis: Redis) -> None: + """ + 初始化系统配置 + + 参数: + - redis (Redis): Redis 客户端实例 + + 返回: + - None + """ async with AsyncSessionLocal() as session: async with session.begin(): auth = AuthSchema(db=session) @@ -202,7 +301,15 @@ async def init_config_service(cls, redis: Redis) -> None: @classmethod async def get_init_config_service(cls, redis: Redis) -> List[Dict]: - """获取系统配置""" + """ + 获取系统配置 + + 参数: + - redis (Redis): Redis 客户端实例 + + 返回: + - List[Dict]: 系统配置模型实例字典列表表示 + """ redis_keys = await RedisCURD(redis).get_keys(f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:*") redis_configs = await RedisCURD(redis).mget(redis_keys) configs = [] @@ -220,10 +327,14 @@ async def get_init_config_service(cls, redis: Redis) -> List[Dict]: @classmethod async def get_system_config_for_middleware(cls, redis: Redis) -> Dict[str, Any]: - """获取中间件所需的系统配置 + """ + 获取中间件所需的系统配置 + + 参数: + - redis (Redis): Redis 客户端实例 - 返回: - Dict: 包含演示模式、IP白名单、API白名单和IP黑名单的配置字典 + 返回: + - Dict[str, Any]: 包含演示模式、IP白名单、API白名单和IP黑名单的配置字典 """ # 定义需要获取的配置键 config_keys = [ diff --git a/backend/app/api/v1/module_system/position/controller.py b/backend/app/api/v1/module_system/position/controller.py index f44e0b29..2870197d 100644 --- a/backend/app/api/v1/module_system/position/controller.py +++ b/backend/app/api/v1/module_system/position/controller.py @@ -29,6 +29,17 @@ async def get_obj_list_controller( search: PositionQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:position:query"])), ) -> JSONResponse: + """ + 查询岗位列表 + + 参数: + - page (PaginationQueryParam): 分页查询参数 + - search (PositionQueryParam): 查询参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 分页查询结果 + """ order_by = [{"order": "asc"}] if page.order_by: order_by = page.order_by @@ -43,6 +54,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="岗位ID"), auth: AuthSchema = Depends(AuthPermission(["system:position:query"])), ) -> JSONResponse: + """ + 查询岗位详情 + + 参数: + - id (int): 岗位ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 岗位详情对象 + """ result_dict = await PositionService.get_position_detail_service(id=id, auth=auth) logger.info(f"查询岗位详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取岗位详情成功") @@ -53,6 +74,16 @@ async def create_obj_controller( data: PositionCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:position:create"])), ) -> JSONResponse: + """ + 创建岗位 + + 参数: + - data (PositionCreateSchema): 创建岗位模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 岗位详情对象 + """ result_dict = await PositionService.create_position_service(data=data, auth=auth) logger.info(f"创建岗位成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建岗位成功") @@ -64,6 +95,17 @@ async def update_obj_controller( id: int = Path(..., description="岗位ID"), auth: AuthSchema = Depends(AuthPermission(["system:position:update"])), ) -> JSONResponse: + """ + 修改岗位 + + 参数: + - data (PositionUpdateSchema): 修改岗位模型 + - id (int): 岗位ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 岗位详情对象 + """ result_dict = await PositionService.update_position_service(id=id, data=data, auth=auth) logger.info(f"修改岗位成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改岗位成功") @@ -74,6 +116,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:position:delete"])), ) -> JSONResponse: + """ + 删除岗位 + + 参数: + - ids (list[int]): ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 成功消息 + """ await PositionService.delete_position_service(ids=ids, auth=auth) logger.info(f"删除岗位成功: {ids}") return SuccessResponse(msg="删除岗位成功") @@ -84,6 +136,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:position:patch"])), ) -> JSONResponse: + """ + 批量修改岗位状态 + + 参数: + - data (BatchSetAvailable): 批量修改岗位状态模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 成功消息 + """ await PositionService.set_position_available_service(data=data, auth=auth) logger.info(f"批量修改岗位状态成功: {data.ids}") return SuccessResponse(msg="批量修改岗位状态成功") @@ -94,7 +156,16 @@ async def export_obj_list_controller( search: PositionQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:position:export"])), ) -> StreamingResponse: - # 获取全量数据 + """ + 导出岗位 + + 参数: + - search (PositionQueryParam): 查询参数 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 岗位Excel文件流 + """ position_query_result = await PositionService.get_position_list_service(search=search, auth=auth) position_export_result = await PositionService.export_position_list_service(position_list=position_query_result) logger.info('导出岗位成功') diff --git a/backend/app/api/v1/module_system/position/crud.py b/backend/app/api/v1/module_system/position/crud.py index d7734c27..996cafbe 100644 --- a/backend/app/api/v1/module_system/position/crud.py +++ b/backend/app/api/v1/module_system/position/crud.py @@ -12,41 +12,62 @@ class PositionCRUD(CRUDBase[PositionModel, PositionCreateSchema, PositionUpdateS """岗位模块数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化岗位CRUD""" + """ + 初始化岗位CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=PositionModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[PositionModel]: """ - 根据id获取岗位信息 + 根据 id 获取岗位信息。 + + 参数: + - id (int): 岗位 ID。 - :param id: 岗位ID - :return: 岗位信息 + 返回: + - PositionModel | None: 岗位信息,未找到返回 None。 """ return await self.get(id=id) async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[PositionModel]: """ - 获取岗位列表 + 获取岗位列表。 - :param search: 搜索条件 - :param order_by: 排序字段 - :return: 岗位列表 + 参数: + - search (Dict | None): 搜索条件。 + - order_by (List[Dict[str, str]] | None): 排序字段列表。 + + 返回: + - Sequence[PositionModel]: 岗位列表。 """ return await self.list(search=search, order_by=order_by) async def set_available_crud(self, ids: List[int], status: bool) -> None: """ - 批量设置岗位可用状态 + 批量设置岗位可用状态。 + + 参数: + - ids (List[int]): 岗位 ID 列表。 + - status (bool): 可用状态。 - :param ids: 岗位ID列表 - :param status: 可用状态 + 返回: + - None """ await self.set(ids=ids, status=status) async def get_name_crud(self, ids: List[int]) -> List[str]: """ - 根据id列表获取岗位名称 + 根据 id 列表获取岗位名称。 + + 参数: + - ids (List[int]): 岗位 ID 列表。 + + 返回: + - List[str]: 岗位名称列表。 """ position_names = [] for id in ids: diff --git a/backend/app/api/v1/module_system/position/service.py b/backend/app/api/v1/module_system/position/service.py index 0fad68ba..80b9c36a 100644 --- a/backend/app/api/v1/module_system/position/service.py +++ b/backend/app/api/v1/module_system/position/service.py @@ -20,19 +20,47 @@ class PositionService: @classmethod async def get_position_detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取岗位详情""" + """ + 获取岗位详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 岗位ID + + 返回: + - Dict: 岗位详情对象 + """ position = await PositionCRUD(auth).get_by_id_crud(id=id) return PositionOutSchema.model_validate(position).model_dump() @classmethod async def get_position_list_service(cls, auth: AuthSchema, search: Optional[PositionQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """获取岗位列表""" + """ + 获取岗位列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (PositionQueryParam | None): 查询参数对象 + - order_by (List[Dict[str, str]] | None): 排序参数列表 + + 返回: + - List[Dict]: 岗位列表对象 + """ position_list = await PositionCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by) return [PositionOutSchema.model_validate(position).model_dump() for position in position_list] @classmethod async def create_position_service(cls, auth: AuthSchema, data: PositionCreateSchema) -> Dict: - """创建岗位""" + """ + 创建岗位 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (PositionCreateSchema): 岗位创建模型 + + 返回: + - Dict: 创建的岗位对象 + """ position = await PositionCRUD(auth).get(name=data.name) if position: raise CustomException(msg='创建失败,该岗位已存在') @@ -41,7 +69,17 @@ async def create_position_service(cls, auth: AuthSchema, data: PositionCreateSch @classmethod async def update_position_service(cls, auth: AuthSchema, id:int, data: PositionUpdateSchema) -> Dict: - """更新岗位""" + """ + 更新岗位 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 岗位ID + - data (PositionUpdateSchema): 岗位更新模型 + + 返回: + - Dict: 更新的岗位对象 + """ position = await PositionCRUD(auth).get_by_id_crud(id=id) if not position: raise CustomException(msg='更新失败,该岗位不存在') @@ -53,7 +91,16 @@ async def update_position_service(cls, auth: AuthSchema, id:int, data: PositionU @classmethod async def delete_position_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除岗位""" + """ + 删除岗位 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 岗位ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -64,12 +111,29 @@ async def delete_position_service(cls, auth: AuthSchema, ids: list[int]) -> None @classmethod async def set_position_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """设置岗位状态""" + """ + 设置岗位状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置状态模型 + + 返回: + - None + """ await PositionCRUD(auth).set_available_crud(ids=data.ids, status=data.status) @classmethod async def export_position_list_service(cls, position_list: List[Dict[str, Any]]) -> bytes: - """导出岗位列表""" + """ + 导出岗位列表 + + 参数: + - position_list (List[Dict[str, Any]]): 岗位列表对象 + + 返回: + - bytes: 导出的Excel文件字节流 + """ mapping_dict = { 'id': '编号', 'name': '岗位名称', diff --git a/backend/app/api/v1/module_system/role/controller.py b/backend/app/api/v1/module_system/role/controller.py index 4a29224c..b1f2b690 100644 --- a/backend/app/api/v1/module_system/role/controller.py +++ b/backend/app/api/v1/module_system/role/controller.py @@ -30,6 +30,17 @@ async def get_obj_list_controller( search: RoleQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:role:query"])), ) -> JSONResponse: + """ + 查询角色 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (RoleQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 分页查询结果JSON响应 + """ order_by = [{"order": "asc"}] if page.order_by: order_by = page.order_by @@ -44,6 +55,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="角色ID"), auth: AuthSchema = Depends(AuthPermission(["system:role:query"])), ) -> JSONResponse: + """ + 查询角色详情 + + 参数: + - id (int): 角色ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 角色详情JSON响应 + """ result_dict = await RoleService.get_role_detail_service(id=id, auth=auth) logger.info(f"获取角色详情成功 {id}") return SuccessResponse(data=result_dict, msg="获取角色详情成功") @@ -54,6 +75,16 @@ async def create_obj_controller( data: RoleCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:role:create"])), ) -> JSONResponse: + """ + 创建角色 + + 参数: + - data (RoleCreateSchema): 创建角色模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 创建角色JSON响应 + """ result_dict = await RoleService.create_role_service(data=data, auth=auth) logger.info(f"创建角色成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建角色成功") @@ -65,6 +96,17 @@ async def update_obj_controller( id: int = Path(..., description="角色ID"), auth: AuthSchema = Depends(AuthPermission(["system:role:update"])), ) -> JSONResponse: + """ + 修改角色 + + 参数: + - data (RoleUpdateSchema): 修改角色模型 + - id (int): 角色ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 修改角色JSON响应 + """ result_dict = await RoleService.update_role_service(id=id, data=data, auth=auth) logger.info(f"修改角色成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改角色成功") @@ -75,6 +117,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:role:delete"])), ) -> JSONResponse: + """ + 删除角色 + + 参数: + - ids (list[int]): ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 删除角色JSON响应 + """ await RoleService.delete_role_service(ids=ids, auth=auth) logger.info(f"删除角色成功: {ids}") return SuccessResponse(msg="删除角色成功") @@ -85,6 +137,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:role:patch"])), ) -> JSONResponse: + """ + 批量修改角色状态 + + 参数: + - data (BatchSetAvailable): 批量修改角色状态模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 批量修改角色状态JSON响应 + """ await RoleService.set_role_available_service(data=data, auth=auth) logger.info(f"批量修改角色状态成功: {data.ids}") return SuccessResponse(msg="批量修改角色状态成功") @@ -95,6 +157,16 @@ async def set_role_permission_controller( data: RolePermissionSettingSchema, auth: AuthSchema = Depends(AuthPermission(["system:role:permission"])), ) -> JSONResponse: + """ + 角色授权 + + 参数: + - data (RolePermissionSettingSchema): 角色授权模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 角色授权JSON响应 + """ await RoleService.set_role_permission_service(data=data, auth=auth) logger.info(f"设置角色权限成功: {data}") return SuccessResponse(msg="授权角色成功") @@ -105,7 +177,16 @@ async def export_obj_list_controller( search: RoleQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:role:export"])), ) -> StreamingResponse: - # 获取全量数据 + """ + 导出角色 + + 参数: + - search (RoleQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - 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) logger.info('导出角色成功') diff --git a/backend/app/api/v1/module_system/role/crud.py b/backend/app/api/v1/module_system/role/crud.py index e249ad09..7ed42a1c 100644 --- a/backend/app/api/v1/module_system/role/crud.py +++ b/backend/app/api/v1/module_system/role/crud.py @@ -14,19 +14,51 @@ class RoleCRUD(CRUDBase[RoleModel, RoleCreateSchema, RoleUpdateSchema]): """角色模块数据层""" def __init__(self, auth: AuthSchema) -> None: + """ + 初始化角色模块数据层 + + 参数: + - auth (AuthSchema): 认证信息模型 + """ self.auth = auth super().__init__(model=RoleModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[RoleModel]: - """根据id获取角色信息""" + """ + 根据id获取角色信息 + + 参数: + - id (int): 角色ID + + 返回: + - Optional[RoleModel]: 角色模型对象 + """ return await self.get(id=id) async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[RoleModel]: - """获取角色列表""" + """ + 获取角色列表 + + 参数: + - search (Optional[Dict]): 查询参数 + - order_by (Optional[List[Dict[str, str]]]): 排序参数 + + 返回: + - Sequence[RoleModel]: 角色模型对象列表 + """ return await self.list(search=search, order_by=order_by) async def set_role_menus_crud(self, role_ids: List[int], menu_ids: List[int]) -> None: - """设置角色的菜单权限""" + """ + 设置角色的菜单权限 + + 参数: + - role_ids (List[int]): 角色ID列表 + - menu_ids (List[int]): 菜单ID列表 + + 返回: + - None + """ roles = await self.list(search={"id": ("in", role_ids)}) menus = await MenuCRUD(self.auth).get_list_crud(search={"id": ("in", menu_ids)}) @@ -37,11 +69,29 @@ async def set_role_menus_crud(self, role_ids: List[int], menu_ids: List[int]) -> await self.db.flush() async def set_role_data_scope_crud(self, role_ids: List[int], data_scope: int) -> None: - """设置角色的数据范围""" + """ + 设置角色的数据范围 + + 参数: + - role_ids (List[int]): 角色ID列表 + - data_scope (int): 数据范围 + + 返回: + - None + """ await self.set(ids=role_ids, data_scope=data_scope) async def set_role_depts_crud(self, role_ids: List[int], dept_ids: List[int]) -> None: - """设置角色的部门权限""" + """ + 设置角色的部门权限 + + 参数: + - role_ids (List[int]): 角色ID列表 + - dept_ids (List[int]): 部门ID列表 + + 返回: + - None + """ roles = await self.list(search={"id": ("in", role_ids)}) depts = await DeptCRUD(self.auth).get_list_crud(search={"id": ("in", dept_ids)}) @@ -52,5 +102,14 @@ async def set_role_depts_crud(self, role_ids: List[int], dept_ids: List[int]) -> await self.db.flush() async def set_available_crud(self, ids: List[int], status: bool) -> None: - """设置角色的可用状态""" + """ + 设置角色的可用状态 + + 参数: + - ids (List[int]): 角色ID列表 + - status (bool): 可用状态 + + 返回: + - None + """ await self.set(ids=ids, status=status) diff --git a/backend/app/api/v1/module_system/role/service.py b/backend/app/api/v1/module_system/role/service.py index 0b613540..be777aa4 100644 --- a/backend/app/api/v1/module_system/role/service.py +++ b/backend/app/api/v1/module_system/role/service.py @@ -21,19 +21,47 @@ class RoleService: @classmethod async def get_role_detail_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取角色详情""" + """ + 获取角色详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 角色ID + + 返回: + - Dict: 角色详情字典 + """ role = await RoleCRUD(auth).get_by_id_crud(id=id) return RoleOutSchema.model_validate(role).model_dump() @classmethod async def get_role_list_service(cls, auth: AuthSchema, search: Optional[RoleQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: - """获取角色列表""" + """ + 获取角色列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (Optional[RoleQueryParam]): 查询参数模型 + - order_by (Optional[List[Dict[str, str]]]): 排序参数列表 + + 返回: + - List[Dict]: 角色详情字典列表 + """ role_list = await RoleCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by) return [RoleOutSchema.model_validate(role).model_dump() for role in role_list] @classmethod async def create_role_service(cls, auth: AuthSchema, data: RoleCreateSchema) -> Dict: - """创建角色""" + """ + 创建角色 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (RoleCreateSchema): 创建角色模型 + + 返回: + - Dict: 新创建的角色详情字典 + """ role = await RoleCRUD(auth).get(name=data.name) if role: raise CustomException(msg='创建失败,该角色已存在') @@ -42,7 +70,17 @@ async def create_role_service(cls, auth: AuthSchema, data: RoleCreateSchema) -> @classmethod async def update_role_service(cls, auth: AuthSchema, id: int, data: RoleUpdateSchema) -> Dict: - """更新角色""" + """ + 更新角色 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 角色ID + - data (RoleUpdateSchema): 更新角色模型 + + 返回: + - Dict: 更新后的角色详情字典 + """ role = await RoleCRUD(auth).get_by_id_crud(id=id) if not role: raise CustomException(msg='更新失败,该角色不存在') @@ -54,7 +92,16 @@ async def update_role_service(cls, auth: AuthSchema, id: int, data: RoleUpdateSc @classmethod async def delete_role_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除角色""" + """ + 删除角色 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 角色ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -65,7 +112,16 @@ async def delete_role_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def set_role_permission_service(cls, auth: AuthSchema, data: RolePermissionSettingSchema) -> None: - """设置角色权限""" + """ + 设置角色权限 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (RolePermissionSettingSchema): 角色权限设置模型 + + 返回: + - None + """ # 设置角色菜单权限 await RoleCRUD(auth).set_role_menus_crud(role_ids=data.role_ids, menu_ids=data.menu_ids) @@ -80,12 +136,29 @@ async def set_role_permission_service(cls, auth: AuthSchema, data: RolePermissio @classmethod async def set_role_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """设置角色可用状态""" + """ + 设置角色可用状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置可用状态模型 + + 返回: + - None + """ await RoleCRUD(auth).set_available_crud(ids=data.ids, status=data.status) @classmethod async def export_role_list_service(cls, role_list: List[Dict[str, Any]]) -> bytes: - """导出角色列表""" + """ + 导出角色列表 + + 参数: + - role_list (List[Dict[str, Any]]): 角色详情字典列表 + + 返回: + - bytes: Excel文件字节流 + """ # 字段映射配置 mapping_dict = { 'id': '角色编号', diff --git a/backend/app/api/v1/module_system/user/controller.py b/backend/app/api/v1/module_system/user/controller.py index a2efd5d8..c14b47c7 100644 --- a/backend/app/api/v1/module_system/user/controller.py +++ b/backend/app/api/v1/module_system/user/controller.py @@ -34,6 +34,15 @@ async def get_current_user_info_controller( auth: AuthSchema = Depends(get_current_user) ) -> JSONResponse: + """ + 查询当前用户信息 + + 参数: + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 当前用户信息JSON响应 + """ result_dict = await UserService.get_current_user_info_service(auth=auth) logger.info(f"获取当前用户信息成功") return SuccessResponse(data=result_dict, msg='获取当前用户信息成功') @@ -44,6 +53,16 @@ async def user_avatar_upload_controller( file: UploadFile, request: Request ) -> JSONResponse: + """ + 上传当前用户头像 + + 参数: + - file (UploadFile): 上传的文件 + - request (Request): 请求对象 + + 返回: + - JSONResponse: 上传头像JSON响应 + """ result_str = await UserService.upload_avatar_service(base_url=str(request.base_url), file=file) logger.info(f"上传头像成功: {result_str}") return SuccessResponse(data=result_str, msg='上传头像成功') @@ -54,6 +73,16 @@ async def update_current_user_info_controller( data: CurrentUserUpdateSchema, auth: AuthSchema = Depends(get_current_user) ) -> JSONResponse: + """ + 更新当前用户基本信息 + + 参数: + - data (CurrentUserUpdateSchema): 当前用户更新模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 更新当前用户基本信息JSON响应 + """ result_dict = await UserService.update_current_user_info_service(data=data, auth=auth) logger.info(f"更新当前用户基本信息成功: {result_dict}") return SuccessResponse(data=result_dict, msg='更新当前用户基本信息成功') @@ -64,6 +93,16 @@ async def change_current_user_password_controller( data: UserChangePasswordSchema, auth: AuthSchema = Depends(get_current_user) ) -> JSONResponse: + """ + 修改当前用户密码 + + 参数: + - data (UserChangePasswordSchema): 用户密码修改模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 修改密码JSON响应 + """ result_dict = await UserService.change_user_password_service(data=data, auth=auth) logger.info(f"修改密码成功: {result_dict}") return SuccessResponse(data=result_dict, msg='修改密码成功, 请重新登录') @@ -73,6 +112,16 @@ async def reset_password_controller( data: ResetPasswordSchema, auth: AuthSchema = Depends(get_current_user) ) -> JSONResponse: + """ + 重置密码 + + 参数: + - data (ResetPasswordSchema): 重置密码模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 重置密码JSON响应 + """ result_dict = await UserService.reset_user_password_service(data=data, auth=auth) logger.info(f"重置密码成功: {result_dict}") return SuccessResponse(data=result_dict, msg='重置密码成功') @@ -82,6 +131,16 @@ async def register_user_controller( data: UserRegisterSchema, db: AsyncSession = Depends(db_getter), ) -> JSONResponse: + """ + 注册用户 + + 参数: + - data (UserRegisterSchema): 用户注册模型 + - db (AsyncSession): 异步数据库会话 + + 返回: + - JSONResponse: 注册用户JSON响应 + """ auth = AuthSchema(db=db) user_register_result = await UserService.register_user_service(data=data, auth=auth) logger.info(f"{data.username} 注册用户成功: {user_register_result}") @@ -93,6 +152,16 @@ async def forget_password_controller( data: UserForgetPasswordSchema, db: AsyncSession = Depends(db_getter), ) -> JSONResponse: + """ + 忘记密码 + + 参数: + - data (UserForgetPasswordSchema): 用户忘记密码模型 + - db (AsyncSession): 异步数据库会话 + + 返回: + - JSONResponse: 忘记密码JSON响应 + """ auth = AuthSchema(db=db) user_forget_password_result = await UserService.forget_password_service(data=data, auth=auth) logger.info(f"{data.username} 重置密码成功: {user_forget_password_result}") @@ -105,6 +174,17 @@ async def get_obj_list_controller( search: UserQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:user:query"])), ) -> JSONResponse: + """ + 查询用户 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (UserQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 分页查询结果JSON响应 + """ result_dict_list = await UserService.get_user_list_service(search=search, auth=auth, 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) logger.info(f"查询用户成功") @@ -116,6 +196,16 @@ async def get_obj_detail_controller( id: int = Path(..., description="用户ID"), auth: AuthSchema = Depends(AuthPermission(["system:user:query"])), ) -> JSONResponse: + """ + 查询用户详情 + + 参数: + - id (int): 用户ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 用户详情JSON响应 + """ result_dict = await UserService.get_detail_by_id_service(id=id, auth=auth) logger.info(f"获取用户详情成功 {id}") return SuccessResponse(data=result_dict, msg='获取用户详情成功') @@ -126,6 +216,20 @@ async def create_obj_controller( data: UserCreateSchema, auth: AuthSchema = Depends(AuthPermission(["system:user:create"])), ) -> JSONResponse: + """ + 创建用户 + + **注意**: + - 创建用户时, 默认密码为: + - 创建用户时, 默认用户状态为: 启用 + + 参数: + - data (UserCreateSchema): 用户创建模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 创建用户JSON响应 + """ result_dict = await UserService.create_user_service(data=data, auth=auth) logger.info(f"创建用户成功: {result_dict}") return SuccessResponse(data=result_dict, msg="创建用户成功") @@ -137,6 +241,17 @@ async def update_obj_controller( id: int = Path(..., description="用户ID"), auth: AuthSchema = Depends(AuthPermission(["system:user:update"])), ) -> JSONResponse: + """ + 修改用户 + + 参数: + - data (UserUpdateSchema): 用户修改模型 + - id (int): 用户ID + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 修改用户JSON响应 + """ result_dict = await UserService.update_user_service(id=id, data=data, auth=auth) logger.info(f"修改用户成功: {result_dict}") return SuccessResponse(data=result_dict, msg="修改用户成功") @@ -147,6 +262,16 @@ async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), auth: AuthSchema = Depends(AuthPermission(["system:user:delete"])), ) -> JSONResponse: + """ + 删除用户 + + 参数: + - ids (list[int]): 用户ID列表 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 删除用户JSON响应 + """ await UserService.delete_user_service(ids=ids, auth=auth) logger.info(f"删除用户成功: {ids}") return SuccessResponse(msg="删除用户成功") @@ -157,6 +282,16 @@ async def batch_set_available_obj_controller( data: BatchSetAvailable, auth: AuthSchema = Depends(AuthPermission(["system:user:patch"])), ) -> JSONResponse: + """ + 批量修改用户状态 + + 参数: + - data (BatchSetAvailable): 批量修改用户状态模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 批量修改用户状态JSON响应 + """ await UserService.set_user_available_service(data=data, auth=auth) logger.info(f"批量修改用户状态成功: {data.ids}") return SuccessResponse(msg="批量修改用户状态成功") @@ -164,6 +299,12 @@ async def batch_set_available_obj_controller( @UserRouter.post('/import/template', summary="获取用户导入模板", description="获取用户导入模板", dependencies=[Depends(AuthPermission(["system:user:import"]))]) async def export_obj_template_controller()-> StreamingResponse: + """ + 获取用户导入模板 + + 返回: + - StreamingResponse: 用户导入模板流响应 + """ user_import_template_result = await UserService.get_import_template_user_service() logger.info('获取用户导入模板成功') @@ -183,7 +324,17 @@ async def export_obj_list_controller( search: UserQueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["system:user:export"])), ) -> StreamingResponse: - # 获取全量数据 + """ + 导出用户 + + 参数: + - page (PaginationQueryParam): 分页查询参数模型 + - search (UserQueryParam): 查询参数模型 + - auth (AuthSchema): 认证信息模型 + + 返回: + - StreamingResponse: 用户导出模板流响应 + """ user_list = await UserService.get_user_list_service(auth=auth, search=search, order_by=page.order_by) user_export_result = await UserService.export_user_list_service(user_list) logger.info('导出用户成功') @@ -202,6 +353,16 @@ async def import_obj_list_controller( file: UploadFile, auth: AuthSchema = Depends(AuthPermission(["system:user:import"])) ) -> JSONResponse: + """ + 导入用户 + + 参数: + - file (UploadFile): 用户导入文件 + - auth (AuthSchema): 认证信息模型 + + 返回: + - JSONResponse: 导入用户JSON响应 + """ batch_import_result = await UserService.batch_import_user_service(file=file, auth=auth, update_support=True) logger.info(f"导入用户成功: {batch_import_result}") return SuccessResponse(data=batch_import_result, msg="导入用户成功") diff --git a/backend/app/api/v1/module_system/user/crud.py b/backend/app/api/v1/module_system/user/crud.py index bc28973d..4bfd5cca 100644 --- a/backend/app/api/v1/module_system/user/crud.py +++ b/backend/app/api/v1/module_system/user/crud.py @@ -17,18 +17,24 @@ class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]): """用户模块数据层""" def __init__(self, auth: AuthSchema) -> None: - """初始化用户CRUD""" + """ + 初始化用户CRUD + + 参数: + - auth (AuthSchema): 认证信息模型 + """ + self.auth = auth super().__init__(model=UserModel, auth=auth) async def get_by_id_crud(self, id: int) -> Optional[UserModel]: """ 根据id获取用户信息 - Args: - id: 用户ID - - Returns: - Optional[UserModel]: 用户信息 + 参数: + - id (int): 用户ID + + 返回: + - Optional[UserModel]: 用户信息,如果不存在则为None """ return await self.get(id=id) @@ -36,11 +42,11 @@ async def get_by_username_crud(self, username: str) -> Optional[UserModel]: """ 根据用户名获取用户信息 - Args: - username: 用户名 - - Returns: - Optional[UserModel]: 用户信息 + 参数: + - username (str): 用户名 + + 返回: + - Optional[UserModel]: 用户信息,如果不存在则为None """ return await self.get(username=username) @@ -48,11 +54,11 @@ async def get_by_mobile_crud(self, mobile: str) -> Optional[UserModel]: """ 根据手机号获取用户信息 - Args: - mobile: 手机号 - - Returns: - Optional[UserModel]: 用户信息 + 参数: + - mobile (str): 手机号 + + 返回: + - Optional[UserModel]: 用户信息,如果不存在则为None """ return await self.get(mobile=mobile) @@ -60,11 +66,11 @@ async def get_list_crud(self, search: Optional[Dict] = None, order_by: Optional[ """ 获取用户列表 - Args: - search: 搜索条件 - order_by: 排序字段 - - Returns: + 参数: + - search (Dict | None): 查询参数对象。 + - order_by (List[Dict[str, str]] | None): 排序参数列表。 + + 返回: Sequence[UserModel]: 用户列表 """ return await self.list(search=search, order_by=order_by) @@ -73,11 +79,11 @@ async def update_last_login_crud(self, id: int) -> Optional[UserModel]: """ 更新用户最后登录时间 - Args: - id: 用户ID - - Returns: - Optional[UserModel]: 更新后的用户信息 + 参数: + - id (int): 用户ID + + 返回: + - Optional[UserModel]: 更新后的用户信息 """ return await self.update(id=id, data={"last_login": datetime.now()}) @@ -85,9 +91,12 @@ async def set_available_crud(self, ids: List[int], status: bool) -> None: """ 批量设置用户可用状态 - Args: - ids: 用户ID列表 - status: 可用状态 + 参数: + - ids (List[int]): 用户ID列表 + - status (bool): 可用状态 + + 返回: + - None: """ await self.set(ids=ids, status=status) @@ -95,9 +104,12 @@ async def set_user_roles_crud(self, user_ids: List[int], role_ids: List[int]) -> """ 批量设置用户角色 - Args: - user_ids: 用户ID列表 - role_ids: 角色ID列表 + 参数: + - user_ids (List[int]): 用户ID列表 + - role_ids (List[int]): 角色ID列表 + + 返回: + - None: """ user_objs = await self.list(search={"id": ("in", user_ids)}) if role_ids: @@ -116,9 +128,12 @@ async def set_user_positions_crud(self, user_ids: List[int], position_ids: List[ """ 批量设置用户岗位 - Args: - user_ids: 用户ID列表 - position_ids: 岗位ID列表 + 参数: + - user_ids (List[int]): 用户ID列表 + - position_ids (List[int]): 岗位ID列表 + + 返回: + - None: """ user_objs = await self.list(search={"id": ("in", user_ids)}) if position_ids: @@ -136,12 +151,12 @@ async def change_password_crud(self, id: int, password_hash: str) -> Optional[Us """ 修改用户密码 - Args: - id: 用户ID - password_hash: 密码哈希值 - - Returns: - Optional[UserModel]: 更新后的用户信息 + 参数: + - id (int): 用户ID + - password_hash (str): 密码哈希值 + + 返回: + - Optional[UserModel]: 更新后的用户信息 """ return await self.update(id=id, data=UserUpdateSchema(password=password_hash)) @@ -151,12 +166,12 @@ async def forget_password_crud(self, id: int, password_hash: str) -> Optional[Us """ 重置密码 - Args: - id: 用户ID - password_hash: 密码哈希值 - - Returns: - Optional[UserModel]: 更新后的用户信息 + 参数: + - id (int): 用户ID + - password_hash (str): 密码哈希值 + + 返回: + - Optional[UserModel]: 更新后的用户信息 """ return await self.update(id=id, data=UserUpdateSchema(password=password_hash)) @@ -164,11 +179,11 @@ async def register_user_crud(self, data: UserForgetPasswordSchema) -> Optional[U """ 用户注册 - Args: - data: 用户注册信息 - - Returns: - Optional[UserModel]: 注册成功的用户信息,如果用户名已存在则返回None + 参数: + - data (UserForgetPasswordSchema): 用户注册信息 + + 返回: + - Optional[UserModel]: 注册成功的用户信息,如果用户名已存在则返回None """ if await self.get_by_username_crud(username=data.username): return None diff --git a/backend/app/api/v1/module_system/user/service.py b/backend/app/api/v1/module_system/user/service.py index ad2107e0..82a00035 100644 --- a/backend/app/api/v1/module_system/user/service.py +++ b/backend/app/api/v1/module_system/user/service.py @@ -37,7 +37,16 @@ class UserService: @classmethod async def get_detail_by_id_service(cls, auth: AuthSchema, id: int) -> Dict: - """获取用户详情""" + """ + 根据ID获取用户详情 + + 参数: + - auth (AuthSchema): 认证信息模型 + - id (int): 用户ID + + 返回: + - Dict: 用户详情字典 + """ user = await UserCRUD(auth).get_by_id_crud(id=id) if not user: raise CustomException(msg="用户不存在") @@ -54,6 +63,17 @@ async def get_detail_by_id_service(cls, auth: AuthSchema, id: int) -> Dict: @classmethod async def get_user_list_service(cls, auth: AuthSchema, search: Optional[UserQueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """ + 获取用户列表 + + 参数: + - auth (AuthSchema): 认证信息模型 + - search (UserQueryParam | None): 查询参数对象。 + - order_by (List[Dict[str, str]] | None): 排序参数列表。 + + 返回: + - List[Dict]: 用户详情字典列表 + """ user_list = await UserCRUD(auth).get_list_crud(search=search.__dict__, order_by=order_by) user_dict_list = [] for user in user_list: @@ -69,6 +89,16 @@ async def get_user_list_service(cls, auth: AuthSchema, search: Optional[UserQuer @classmethod async def create_user_service(cls, data: UserCreateSchema, auth: AuthSchema) -> Dict: + """ + 创建用户 + + 参数: + - data (UserCreateSchema): 用户创建信息 + - auth (AuthSchema): 认证信息模型 + + 返回: + - Dict: 创建后的用户详情字典 + """ if not data.username: raise CustomException(msg="用户名不能为空") # 检查用户名是否存在 @@ -99,6 +129,17 @@ async def create_user_service(cls, data: UserCreateSchema, auth: AuthSchema) -> @classmethod async def update_user_service(cls, id: int, data: UserUpdateSchema, auth: AuthSchema) -> Dict: + """ + 更新用户 + + 参数: + - id (int): 用户ID + - data (UserUpdateSchema): 用户更新信息 + - auth (AuthSchema): 认证信息模型 + + 返回: + - Dict: 更新后的用户详情字典 + """ if not data.username: raise CustomException(msg="用户名不能为空") # 检查用户是否存在 @@ -152,7 +193,16 @@ async def update_user_service(cls, id: int, data: UserUpdateSchema, auth: AuthSc @classmethod async def delete_user_service(cls, auth: AuthSchema, ids: list[int]) -> None: - """删除用户""" + """ + 删除用户 + + 参数: + - auth (AuthSchema): 认证信息模型 + - ids (list[int]): 用户ID列表 + + 返回: + - None + """ if len(ids) < 1: raise CustomException(msg='删除失败,删除对象不能为空') for id in ids: @@ -176,7 +226,15 @@ async def delete_user_service(cls, auth: AuthSchema, ids: list[int]) -> None: @classmethod async def get_current_user_info_service(cls, auth: AuthSchema) -> Dict: - """获取当前用户信息""" + """ + 获取当前用户信息 + + 参数: + - auth (AuthSchema): 认证信息模型 + + 返回: + - Dict: 当前用户详情字典 + """ # 获取用户基本信息 if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") @@ -212,7 +270,16 @@ async def get_current_user_info_service(cls, auth: AuthSchema) -> Dict: @classmethod async def update_current_user_info_service(cls, auth: AuthSchema, data: CurrentUserUpdateSchema) -> Dict: - """更新当前用户信息""" + """ + 更新当前用户信息 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (CurrentUserUpdateSchema): 当前用户更新信息 + + 返回: + - Dict: 更新后的当前用户详情字典 + """ if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") user = await UserCRUD(auth).get_by_id_crud(id=auth.user.id) @@ -224,7 +291,16 @@ async def update_current_user_info_service(cls, auth: AuthSchema, data: CurrentU @classmethod async def set_user_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: - """设置用户状态""" + """ + 设置用户状态 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (BatchSetAvailable): 批量设置用户状态数据 + + 返回: + - None + """ for id in data.ids: user = await UserCRUD(auth).get_by_id_crud(id=id) if not user: @@ -235,7 +311,16 @@ async def set_user_available_service(cls, auth: AuthSchema, data: BatchSetAvaila @classmethod async def upload_avatar_service(cls, base_url: str, file: UploadFile) -> Dict: - """上传头像""" + """ + 上传用户头像 + + 参数: + - base_url (str): 基础URL + - file (UploadFile): 上传的文件 + + 返回: + - Dict: 上传头像响应字典 + """ if not file: raise CustomException(msg="请选择要上传的文件") filename, filepath, file_url = await UploadUtil.upload_file(file=file, base_url=base_url) @@ -249,7 +334,16 @@ async def upload_avatar_service(cls, base_url: str, file: UploadFile) -> Dict: @classmethod async def change_user_password_service(cls, auth: AuthSchema, data: UserChangePasswordSchema) -> Dict: - """修改用户密码""" + """ + 修改用户密码 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (UserChangePasswordSchema): 用户密码修改数据 + + 返回: + - Dict: 更新后的当前用户详情字典 + """ if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") if not data.old_password or not data.new_password: @@ -269,7 +363,16 @@ async def change_user_password_service(cls, auth: AuthSchema, data: UserChangePa @classmethod async def reset_user_password_service(cls, auth: AuthSchema, data: ResetPasswordSchema) -> Dict: - """修改用户密码""" + """ + 重置用户密码 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (ResetPasswordSchema): 用户密码重置数据 + + 返回: + - Dict: 更新后的当前用户详情字典 + """ if not data.password: raise CustomException(msg='密码不能为空') @@ -285,7 +388,16 @@ async def reset_user_password_service(cls, auth: AuthSchema, data: ResetPassword @classmethod async def register_user_service(cls, auth: AuthSchema, data: UserRegisterSchema) -> Dict: - """用户注册""" + """ + 用户注册 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (UserRegisterSchema): 用户注册数据 + + 返回: + - Dict: 注册后的用户详情字典 + """ # 检查用户名是否存在 username_ok = await UserCRUD(auth).get_by_username_crud(username=data.username) if username_ok: @@ -305,7 +417,16 @@ async def register_user_service(cls, auth: AuthSchema, data: UserRegisterSchema) @classmethod async def forget_password_service(cls, auth: AuthSchema, data: UserForgetPasswordSchema) -> Dict: - """用户忘记密码""" + """ + 用户忘记密码 + + 参数: + - auth (AuthSchema): 认证信息模型 + - data (UserForgetPasswordSchema): 用户忘记密码数据 + + 返回: + - Dict: 更新后的当前用户详情字典 + """ user = await UserCRUD(auth).get_by_username_crud(username=data.username) if not user: raise CustomException(msg="用户不存在") @@ -317,7 +438,17 @@ async def forget_password_service(cls, auth: AuthSchema, data: UserForgetPasswor @classmethod async def batch_import_user_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: - """批量导入用户""" + """ + 批量导入用户 + + 参数: + - auth (AuthSchema): 认证信息模型 + - file (UploadFile): 上传的Excel文件 + - update_support (bool, optional): 是否支持更新已存在用户. 默认值为False. + + 返回: + - str: 导入结果消息 + """ header_dict = { '部门编号': 'dept_id', @@ -349,29 +480,17 @@ async def batch_import_user_service(cls, auth: AuthSchema, file: UploadFile, upd # 验证必填字段 required_fields = ['username', 'name', 'dept_id'] for field in required_fields: - if df[field].isnull().any(): - missing_rows = df[df[field].isnull()].index.tolist() - raise CustomException(msg=f"{[k for k,v in header_dict.items() if v == field][0]}不能为空,第{[i+1 for i in missing_rows]}行") + missing_rows = df[df[field].isnull()].index.tolist() + raise CustomException(msg=f"{[k for k,v in header_dict.items() if v == field][0]}不能为空,第{[i+1 for i in missing_rows]}行") error_msgs = [] success_count = 0 + count = 0 # 处理每一行数据 for index, row in df.iterrows(): try: - # 数据转换前的类型检查 - try: - dept_id = int(row['dept_id']) - except ValueError: - error_msgs.append(f"第{index+1}行: 部门编号必须是数字") - continue - - # 检查部门是否存在 - dept = await DeptCRUD(auth).get_by_id_crud(id=dept_id) - if not dept: - error_msgs.append(f"第{index+1}行: 部门ID {dept_id} 不存在") - continue - + count = count + 1 # 数据转换 gender = 1 if row['gender'] == '男' else (2 if row['gender'] == '女' else 1) status = True if row['status'] == '正常' else False @@ -384,7 +503,7 @@ async def batch_import_user_service(cls, auth: AuthSchema, file: UploadFile, upd "mobile": str(row['mobile']).strip(), "gender": gender, "status": status, - "dept_id": dept_id, + "dept_id": int(row['dept_id']), "password": PwdUtil.set_password_hash(password="123456") # 设置默认密码 } @@ -396,14 +515,14 @@ async def batch_import_user_service(cls, auth: AuthSchema, file: UploadFile, upd await UserCRUD(auth).update(id=exists_user.id, data=user_update_data) success_count += 1 else: - error_msgs.append(f"第{index+1}行: 用户 {user_data['username']} 已存在") + error_msgs.append(f"第{count}行: 用户 {user_data['username']} 已存在") else: user_create_data = UserCreateSchema(**user_data) await UserCRUD(auth).create(data=user_create_data) success_count += 1 except Exception as e: - error_msgs.append(f"第{index+1}行: {str(e)}") + error_msgs.append(f"第{count}行: 异常{str(e)}") continue # 返回详细的导入结果 @@ -418,7 +537,12 @@ async def batch_import_user_service(cls, auth: AuthSchema, file: UploadFile, upd @classmethod async def get_import_template_user_service(cls) -> bytes: - """获取用户导入模板""" + """ + 获取用户导入模板 + + 返回: + - bytes: Excel文件字节流 + """ header_list = ['部门编号', '用户名', '名称', '邮箱', '手机号', '性别', '状态'] selector_header_list = ['性别', '状态'] option_list = [{'性别': ['男', '女', '未知']}, {'状态': ['正常', '停用']}] @@ -430,7 +554,15 @@ async def get_import_template_user_service(cls) -> bytes: @classmethod async def export_user_list_service(cls, user_list: List[Dict[str, Any]]) -> bytes: - """导出用户列表""" + """ + 导出用户列表为Excel文件 + + 参数: + - user_list (List[Dict[str, Any]]): 用户列表 + + 返回: + - bytes: Excel文件字节流 + """ if not user_list: raise CustomException(msg="没有数据可导出") diff --git a/backend/app/common/constant.py b/backend/app/common/constant.py index 157e1c3d..aadcf4f4 100644 --- a/backend/app/common/constant.py +++ b/backend/app/common/constant.py @@ -178,10 +178,14 @@ class RET(Enum): def __init__(self, code: int, msg: str): """ - 初始化返回码 + 初始化返回码。 - :param code: 错误码 - :param msg: 错误信息 + 参数: + - code (int): 错误码。 + - msg (str): 错误信息。 + + 返回: + - None """ self._code = code self._msg = msg diff --git a/backend/app/common/request.py b/backend/app/common/request.py index 087c095f..4b5a2fb5 100644 --- a/backend/app/common/request.py +++ b/backend/app/common/request.py @@ -25,14 +25,20 @@ class PaginationService: @staticmethod async def paginate(data_list: List[Any], page_no: Optional[int] = None, page_size: Optional[int] = None) -> Dict[str, Any]: """ - 输入数据列表data_list和分页信息,返回分页或非分页数据列表结果。 - 如果未传入page_no和page_size,则返回全部数据。 - - :param data_list: 原始数据列表 - :param page_no: 当前页码,默认为None - :param page_size: 当前页面数据量,默认为None - :return: 分页或非分页数据对象 - :raises: CustomException 当分页参数不合法时抛出 + 分页数据处理。 + 输入数据列表和分页信息,返回分页或非分页数据列表结果。 + 未传入 page_no 和 page_size 时,返回全部数据。 + + 参数: + - data_list (List[Any]): 原始数据列表。 + - page_no (int | None): 当前页码,默认 None。 + - page_size (int | None): 每页数据量,默认 None。 + + 返回: + - Dict[str, Any]: 分页或非分页数据对象。 + + 异常: + - CustomException: 当分页参数不合法时抛出。 """ total = len(data_list) diff --git a/backend/app/common/response.py b/backend/app/common/response.py index 85ae89f2..421a34e2 100644 --- a/backend/app/common/response.py +++ b/backend/app/common/response.py @@ -30,10 +30,15 @@ def __init__( """ 初始化成功响应类 - :param data: 响应数据 - :param msg: 响应消息 - :param code: 业务状态码 - :param status_code: HTTP状态码 + 参数: + - data (Any | None): 响应数据。 + - msg (str): 响应消息。 + - code (int): 业务状态码。 + - status_code (int): HTTP 状态码。 + - success (bool): 操作是否成功。 + + 返回: + - None """ content = ResponseSchema( code=code, @@ -59,10 +64,15 @@ def __init__( """ 初始化错误响应类 - :param data: 响应数据 - :param msg: 响应消息 - :param code: 业务状态码 - :param status_code: HTTP状态码 + 参数: + - data (Any | None): 响应数据。 + - msg (str): 响应消息。 + - code (int): 业务状态码。 + - status_code (int): HTTP 状态码。 + - success (bool): 操作是否成功。 + + 返回: + - None """ content = ResponseSchema( code=code, @@ -88,10 +98,15 @@ def __init__( """ 初始化流式响应类 - :param data: 响应数据 - :param msg: 响应消息 - :param code: 业务状态码 - :param status_code: HTTP状态码 + 参数: + - data (Any): 响应数据。 + - status_code (int): HTTP 状态码。 + - headers (Mapping[str, str] | None): 响应头。 + - media_type (str | None): 媒体类型。 + - background (BackgroundTask | None): 后台任务。 + + 返回: + - None """ super().__init__( content=data, @@ -117,11 +132,17 @@ def __init__( ): """ 初始化文件响应类 - :param file_path: 文件路径 - :param media_type: 文件类型 - :param headers: 响应头 - :param background: 后台任务 - :param status_code: HTTP状态码 + + 参数: + - file_path (str): 文件路径。 + - filename (str): 文件名。 + - media_type (str): 文件类型。 + - headers (Mapping[str, str] | None): 响应头。 + - background (BackgroundTask | None): 后台任务。 + - status_code (int): HTTP 状态码。 + + 返回: + - None """ super().__init__( path=file_path, diff --git a/backend/app/config/setting.py b/backend/app/config/setting.py index f320a2c0..b64feb3e 100755 --- a/backend/app/config/setting.py +++ b/backend/app/config/setting.py @@ -209,7 +209,7 @@ class Settings(BaseSettings): # ================================================= # # ******************* 代码生成配置 ****************** # # ================================================= # - author: str = 'insistence' # 作者 + author: str = 'FastapiAdmin' # 作者 package_name: str = 'module_generator.gencode' # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool auto_remove_pre: bool = False # 自动去除表前缀,默认是True table_prefix: str = 'gen_' # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) @@ -365,7 +365,6 @@ def get_settings() -> Settings: if not env_file.exists(): raise FileNotFoundError(f"环境配置文件不存在: {env_file}") - # return Settings(_env_file=env_file) return Settings(_env_file=env_file) settings = get_settings() diff --git a/backend/app/core/ap_scheduler.py b/backend/app/core/ap_scheduler.py index ebf347ea..aee30735 100644 --- a/backend/app/core/ap_scheduler.py +++ b/backend/app/core/ap_scheduler.py @@ -64,7 +64,16 @@ class SchedulerUtil: """ @classmethod - def scheduler_event_listener(cls, event: JobEvent | JobExecutionEvent): + def scheduler_event_listener(cls, event: JobEvent | JobExecutionEvent) -> None: + """ + 监听任务执行事件。 + + 参数: + - event (JobEvent | JobExecutionEvent): 任务事件对象。 + + 返回: + - None + """ # 延迟导入避免循环导入 from app.api.v1.module_application.job.schema import JobLogCreateSchema @@ -118,9 +127,10 @@ def scheduler_event_listener(cls, event: JobEvent | JobExecutionEvent): @classmethod async def init_system_scheduler(cls): """ - 应用启动时初始化定时任务 - - :return: + 应用启动时初始化定时任务。 + + 返回: + - None """ # 延迟导入避免循环导入 from app.api.v1.module_application.job.crud import JobCRUD @@ -139,9 +149,10 @@ async def init_system_scheduler(cls): @classmethod async def close_system_scheduler(cls): """ - 关闭 - - :return: + 关闭系统定时任务。 + + 返回: + - None """ scheduler.shutdown(wait=False) logger.info('关闭定时任务成功') @@ -149,27 +160,36 @@ async def close_system_scheduler(cls): @classmethod def get_job(cls, job_id: Union[str, int]) -> Optional[Job]: """ - 获取 - - :param job_id: 任务id - :return: 任务对象 + 根据任务ID获取任务对象。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - Optional[Job]: 任务对象,未找到则为 None。 """ return scheduler.get_job(job_id=str(job_id)) @classmethod def get_all_jobs(cls) -> List[Job]: """ - 获取任务列表 - :return: 任务列表 + 获取全部调度任务列表。 + + 返回: + - List[Job]: 任务列表。 """ return scheduler.get_jobs() @classmethod def add_job(cls, job_info): """ - 创建 - :param job_info: 任务对象信息 - :return: + 根据任务配置创建并添加调度任务。 + + 参数: + - job_info (Any): 任务对象信息(包含触发器、函数、参数等)。 + + 返回: + - Job: 新增的任务对象。 """ # 动态导入模块 # 1. 解析调用目标 @@ -254,10 +274,13 @@ def add_job(cls, job_info): @classmethod def remove_job(cls, job_id: Union[str, int]) -> None: """ - 删除 - - :param job_id: 任务id - :return: None + 根据任务ID删除调度任务。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - None """ query_job = cls.get_job(job_id=str(job_id)) if query_job: @@ -266,18 +289,26 @@ def remove_job(cls, job_id: Union[str, int]) -> None: @classmethod def clear_jobs(cls): """ - 删除 - :param job_group: 任务组名 - :return: + 删除所有调度任务。 + + 返回: + - None """ scheduler.remove_all_jobs() @classmethod def modify_job(cls, job_id: Union[str, int]) -> Job: """ - 更新(如果是运行中,则是下次次执行生效) - :param job_id: 任务id - :return: 更新后的任务对象 + 更新指定任务的配置(运行中的任务下次执行生效)。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - Job: 更新后的任务对象。 + + 异常: + - CustomException: 当任务不存在时抛出。 """ query_job = cls.get_job(job_id=str(job_id)) if not query_job: @@ -287,9 +318,16 @@ def modify_job(cls, job_id: Union[str, int]) -> Job: @classmethod def pause_job(cls, job_id: Union[str, int]): """ - 暂停(只有状态是运行中时才可以暂停, 已终止不可以) - :param job_id: 任务id - :return: + 暂停指定任务(仅运行中可暂停,已终止不可)。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - None + + 异常: + - ValueError: 当任务不存在时抛出。 """ logger.info(f"开始获取全部任务:{cls.get_all_jobs()}, 状态: {cls.get_job_status()}") query_job = cls.get_job(job_id=str(job_id)) @@ -301,9 +339,16 @@ def pause_job(cls, job_id: Union[str, int]): @classmethod def resume_job(cls, job_id: Union[str, int]): """ - 恢复(只有状态是暂停中时才可以恢复, 已终止不可以) - :param job_id: 任务id - :return: + 恢复指定任务(仅暂停中可恢复,已终止不可)。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - None + + 异常: + - ValueError: 当任务不存在时抛出。 """ logger.info(f"开始获取全部任务:{cls.get_all_jobs()}, 状态: {cls.get_job_status()}") @@ -316,9 +361,16 @@ def resume_job(cls, job_id: Union[str, int]): @classmethod def reschedule_job(cls, job_id: Union[str, int]) -> Optional[Job]: """ - 重启 - :param job_id: 任务id - :return: 重启后的任务对象 + 重启指定任务的触发器。 + + 参数: + - job_id (str | int): 任务ID。 + + 返回: + - None + + 异常: + - CustomException: 当任务不存在时抛出。 """ logger.info(f"开始获取全部任务:{cls.get_all_jobs()}, 状态: {cls.get_job_status()}") query_job = cls.get_job(job_id=str(job_id)) @@ -338,17 +390,24 @@ def import_jobs(cls): @classmethod def print_jobs(cls,jobstore: Any | None = None, out: Any | None = None): """ - 打印 - :return: + 打印调度任务列表。 + + 参数: + - jobstore (Any | None): 任务存储别名。 + - out (Any | None): 输出目标。 + + 返回: + - None """ scheduler.print_jobs(jobstore=jobstore, out=out) @classmethod def get_job_status(cls) -> str: """ - 获取调度器的当前状态 - - :return: 状态字符串 ('stopped', 'running', 'paused') + 获取调度器当前状态。 + + 返回: + - str: 状态字符串('stopped' | 'running' | 'paused' | 'unknown')。 """ #: constant indicating a scheduler's stopped state STATE_STOPPED = 0 diff --git a/backend/app/core/base_crud.py b/backend/app/core/base_crud.py index 03207182..7cfd8f0d 100644 --- a/backend/app/core/base_crud.py +++ b/backend/app/core/base_crud.py @@ -29,9 +29,12 @@ def __init__(self, model: Type[ModelType], auth: AuthSchema) -> None: """ 初始化CRUDBase类 - Args: - model: 数据模型类 - auth: 认证信息 + 参数: + - model (Type[ModelType]): 数据模型类。 + - auth (AuthSchema): 认证信息。 + + 返回: + - None """ self.model = model self.auth = auth @@ -42,14 +45,17 @@ async def get(self, **kwargs) -> Optional[ModelType]: """ 根据条件获取单个对象 - Args: - **kwargs: 查询条件 + 参数: + - **kwargs: 查询条件 + + 返回: + - Optional[ModelType]: 对象实例 - Returns: - Optional[ModelType]: 对象实例 + 返回: + - Optional[ModelType]: 对象实例 - Raises: - CustomException: 查询失败时抛出异常 + 异常: + - CustomException: 查询失败时抛出异常 """ try: conditions = await self.__build_conditions(**kwargs) @@ -73,15 +79,15 @@ async def list(self, search: Optional[Dict] = None, order_by: Optional[List[Dict """ 根据条件获取对象列表和总数 - Args: - search: 查询条件,格式为 {'id': value, 'name': value} - order_by: 排序字段,格式为 [{'id': 'asc'}, {'name': 'desc'}] + 参数: + - search (Optional[Dict]): 查询条件,格式为 {'id': value, 'name': value} + - order_by (Optional[List[Dict[str, str]]]): 排序字段,格式为 [{'id': 'asc'}, {'name': 'desc'}] - Returns: - Sequence[ModelType]: 对象列表 + 返回: + - Sequence[ModelType]: 对象列表和总数 - Raises: - CustomException: 查询失败时抛出异常 + 异常: + - CustomException: 查询失败时抛出异常 """ try: conditions = await self.__build_conditions(**search) if search else [] @@ -100,16 +106,16 @@ async def tree_list(self, search: Optional[Dict] = None, order_by: Optional[List """ 获取树形结构数据列表 - Args: - search: 查询条件 - order_by: 排序字段 - children_attr: 子节点属性名 + 参数: + - search (Optional[Dict]): 查询条件 + - order_by (Optional[List[Dict[str, str]]]): 排序字段 + - children_attr (str): 子节点属性名 - Returns: - Sequence[ModelType]: 树形结构数据列表 + 返回: + - Sequence[ModelType]: 树形结构数据列表 - Raises: - CustomException: 查询失败时抛出异常 + 异常: + - CustomException: 查询失败时抛出异常 """ try: from sqlalchemy.orm import selectinload @@ -133,6 +139,22 @@ async def tree_list(self, search: Optional[Dict] = None, order_by: Optional[List raise CustomException(msg=f"树形列表查询失败: {str(e)}") async def page(self, offset: int, limit: int, order_by: List[Dict[str, str]], search: Dict, out_schema: Type[OutSchemaType]) -> Dict: + """ + 获取分页数据 + + 参数: + - offset (int): 偏移量 + - limit (int): 每页数量 + - order_by (List[Dict[str, str]]): 排序字段 + - search (Dict): 查询条件 + - out_schema (Type[OutSchemaType]): 输出数据模型 + + 返回: + - Dict: 分页数据 + + 异常: + - CustomException: 查询失败时抛出异常 + """ try: from sqlalchemy.orm import selectinload @@ -177,14 +199,14 @@ async def create(self, data: Union[CreateSchemaType, Dict]) -> ModelType: """ 创建新对象 - Args: - data: 对象属性 + 参数: + - data (Union[CreateSchemaType, Dict]): 对象属性 - Returns: - ModelType: 新创建的对象实例 + 返回: + - ModelType: 新创建的对象实例 - Raises: - CustomException: 创建失败时抛出异常 + 异常: + - CustomException: 创建失败时抛出异常 """ try: obj_dict = data if isinstance(data, dict) else data.model_dump() @@ -206,15 +228,15 @@ async def update(self, id: int, data: Union[UpdateSchemaType, Dict]) -> ModelTyp """ 更新对象 - Args: - id: 对象ID - data: 更新的属性及值 + 参数: + - id (int): 对象ID + - data (Union[UpdateSchemaType, Dict]): 更新的属性及值 - Returns: - ModelType: 更新后的对象实例 + 返回: + - ModelType: 更新后的对象实例 - Raises: - CustomException: 更新失败时抛出异常 + 异常: + - CustomException: 更新失败时抛出异常 """ try: obj_dict = data if isinstance(data, dict) else data.model_dump(exclude_unset=True, exclude={"id"}) @@ -236,11 +258,11 @@ async def delete(self, ids: List[int]) -> None: """ 删除对象 - Args: - ids: 对象ID列表 + 参数: + - ids (List[int]): 对象ID列表 - Raises: - CustomException: 删除失败时抛出异常 + 异常: + - CustomException: 删除失败时抛出异常 """ try: sql = delete(self.model).where(self.model.id.in_(ids)) @@ -254,8 +276,8 @@ async def clear(self) -> None: """ 清空对象表 - Raises: - CustomException: 清空失败时抛出异常 + 异常: + - CustomException: 清空失败时抛出异常 """ try: sql = delete(self.model) @@ -268,12 +290,12 @@ async def set(self, ids: List[int], **kwargs) -> None: """ 批量更新对象 - Args: - ids: 对象ID列表 - **kwargs: 更新的属性及值 + 参数: + - ids (List[int]): 对象ID列表 + - **kwargs: 更新的属性及值 - Raises: - CustomException: 更新失败时抛出异常 + 异常: + - CustomException: 更新失败时抛出异常 """ try: sql = update(self.model).where(self.model.id.in_(ids)).values(**kwargs) @@ -283,7 +305,18 @@ async def set(self, ids: List[int], **kwargs) -> None: raise CustomException(msg=f"批量更新失败: {str(e)}") async def __filter_permissions(self, sql: Select) -> Select: - """过滤数据权限""" + """ + 过滤数据权限 + + 参数: + - sql (Select): SQL查询对象 + + 返回: + - Select: 过滤后的数据查询对象 + + 异常: + - CustomException: 权限过滤失败时抛出异常 + """ # 如果不需要检查数据权限,则直接返回 if not self.current_user or not self.auth.check_data_scope: return sql @@ -357,11 +390,14 @@ def __order_by(self, order_by: List[Dict[str, str]]) -> List[ColumnElement]: """ 获取排序字段 - Args: - order_by: 排序字段列表,格式为 [{'id': 'asc'}, {'name': 'desc'}] + 参数: + - order_by (List[Dict[str, str]]): 排序字段列表,格式为 [{'id': 'asc'}, {'name': 'desc'}] + + 返回: + - List[ColumnElement]: 排序字段列表 - Returns: - List[ColumnElement]: 排序字段列表 + 异常: + - CustomException: 排序字段不存在时抛出异常 """ columns = [] for order in order_by: @@ -374,11 +410,14 @@ async def __build_conditions(self, **kwargs) -> List[ColumnElement]: """ 构建查询条件 - Args: - **kwargs: 查询参数 + 参数: + - **kwargs: 查询参数 + + 返回: + - List[ColumnElement]: SQL条件表达式列表 - Returns: - List[ColumnElement]: SQL条件表达式列表 + 异常: + - CustomException: 查询参数不存在时抛出异常 """ conditions = [] for key, value in kwargs.items(): diff --git a/backend/app/core/base_model.py b/backend/app/core/base_model.py index 272557d1..f9160103 100644 --- a/backend/app/core/base_model.py +++ b/backend/app/core/base_model.py @@ -26,16 +26,6 @@ class MappedBase(AsyncAttrs, DeclarativeBase): __abstract__ = True - @declared_attr.directive - def __tablename__(cls) -> str: - """生成表名""" - return cls.__name__.lower() - - @declared_attr.directive - def __table_args__(cls) -> dict: - """表配置""" - return {'comment': cls.__doc__ or ''} - class ModelMixin(MappedBase): """ diff --git a/backend/app/core/base_params.py b/backend/app/core/base_params.py index e083af22..a4ed2a5b 100644 --- a/backend/app/core/base_params.py +++ b/backend/app/core/base_params.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from typing import Optional, List, Dict +from typing import Optional from fastapi import Query @@ -14,11 +14,15 @@ def __init__( order_by: Optional[str] = Query(default=None, description="排序字段,格式:field1,asc;field2,desc"), ) -> None: """ - 初始化分页查询参数 + 初始化分页查询参数。 - :param page_no: 当前页码,默认None - :param page_size: 每页数量,默认None,最大100 - :param order_by: 排序字段 + 参数: + - page_no (int | None): 当前页码,默认 None。 + - page_size (int | None): 每页数量,默认 None,最大 100。 + - order_by (str | None): 排序字段,格式 'field,asc;field2,desc'。 + + 返回: + - None """ self.page_no = page_no self.page_size = page_size diff --git a/backend/app/core/base_schema.py b/backend/app/core/base_schema.py index 53d6fe7b..f8de0f73 100644 --- a/backend/app/core/base_schema.py +++ b/backend/app/core/base_schema.py @@ -41,9 +41,7 @@ class BatchSetAvailable(BaseModel): class UploadResponseSchema(BaseModel): - """ - 上传响应模型 - """ + """上传响应模型""" model_config = ConfigDict(from_attributes=True) file_path: Optional[str] = Field(default=None, description='新文件映射路径') @@ -52,8 +50,6 @@ class UploadResponseSchema(BaseModel): file_url: Optional[str] = Field(default=None, description='新文件访问地址') class DownloadFileSchema(BaseModel): - """ - 下载文件模型 - """ + """下载文件模型""" file_path: str = Field(..., description='新文件映射路径') file_name: str = Field(..., description='新文件名称') diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 6960d509..5d38f042 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -16,7 +16,7 @@ from app.core.logger import logger from app.config.setting import settings from app.core.exceptions import CustomException -from app.core.base_model import MappedBase + # 同步数据库引擎 engine: Engine = create_engine( @@ -52,7 +52,12 @@ ) def session_connect() -> AsyncSession: - """获取数据库会话""" + """ + 获取异步数据库会话连接。 + + 返回: + - AsyncSession: 异步数据库会话连接。 + """ try: if not settings.SQL_DB_ENABLE: raise CustomException(msg="请先开启数据库连接", data="请启用 app/config/setting.py: SQL_DB_ENABLE") @@ -61,7 +66,16 @@ def session_connect() -> AsyncSession: raise CustomException(msg=f"数据库连接失败: {e}") async def redis_connect(app: FastAPI, status: bool) -> Redis | None: - """创建或关闭Redis连接""" + """ + 创建或关闭Redis连接。 + + 参数: + - app (FastAPI): FastAPI应用实例。 + - status (bool): 连接状态,True为创建连接,False为关闭连接。 + + 返回: + - Redis | None: Redis连接实例,如果连接失败则返回None。 + """ if not settings.REDIS_ENABLE: raise CustomException(msg="请先开启Redis连接", data="请启用 app/core/config.py: REDIS_ENABLE") @@ -91,7 +105,16 @@ async def redis_connect(app: FastAPI, status: bool) -> Redis | None: logger.info('Redis连接已关闭') async def mongodb_connect(app: FastAPI, status: bool) -> AsyncIOMotorClient | None: - """创建或关闭MongoDB连接""" + """ + 创建或关闭MongoDB连接。 + + 参数: + - app (FastAPI): FastAPI应用实例。 + - status (bool): 连接状态,True为创建连接,False为关闭连接。 + + 返回: + - AsyncIOMotorClient | None: MongoDB异步客户端实例,如果连接失败则返回None。 + """ if not settings.MONGO_DB_ENABLE: raise CustomException(msg="请先开启MongoDB连接", data="请启用 app/core/config.py: MONGO_DB_ENABLE") diff --git a/backend/app/core/dependencies.py b/backend/app/core/dependencies.py index 9c5cf3bc..51431f7d 100644 --- a/backend/app/core/dependencies.py +++ b/backend/app/core/dependencies.py @@ -20,17 +20,35 @@ async def db_getter() -> AsyncGenerator[AsyncSession, None]: - """获取数据库会话连接""" + """获取数据库会话连接 + + 返回: + - AsyncSession: 数据库会话连接 + """ async with session_connect() as session: async with session.begin(): yield session async def redis_getter(request: Request) -> Redis: - """获取Redis连接""" + """获取Redis连接 + + 参数: + - request (Request): 请求对象 + + 返回: + - Redis: Redis连接 + """ return request.app.state.redis async def mongo_getter(request: Request) -> AsyncIOMotorDatabase: - """获取MongoDB连接""" + """获取MongoDB连接 + + 参数: + - request (Request): 请求对象 + + 返回: + - AsyncIOMotorDatabase: MongoDB连接 + """ return request.app.state.mongo async def get_current_user( @@ -39,19 +57,19 @@ async def get_current_user( redis: Redis = Depends(redis_getter), db: AsyncSession = Depends(db_getter) ) -> AuthSchema: - """ - 获取并验证当前用户信息 + """获取并验证当前用户信息 - Args: - request: 请求对象 - token: 认证token - db: 数据库会话 - - Returns: - AuthSchema: 包含用户信息的认证对象 + 参数: + - request (Request): 请求对象。 + - token (str): 认证token。 + - redis (Redis): Redis连接。 + - db (AsyncSession): 数据库会话连接。 + + 返回: + - AuthSchema: 包含用户信息的认证对象。 - Raises: - CustomException: 认证失败时抛出异常 + 异常: + - CustomException: 认证失败时抛出异常。 """ # 处理Bearer token if token.startswith('Bearer'): @@ -107,29 +125,26 @@ def __init__(self, permissions: Optional[list[str]] = None, check_data_scope: bo """ 初始化权限验证 - Args: - permissions: 权限标识列表 - check_data_scope: 是否启用严格模式校验 + 参数: + - permissions (Optional[list[str]]): 权限标识列表。 + - check_data_scope (bool): 是否启用严格模式校验。 """ self.permissions = set(permissions) if permissions else None self.check_data_scope = check_data_scope - async def __call__( - self, - auth: AuthSchema = Depends(get_current_user), - ) -> AuthSchema: + async def __call__(self, auth: AuthSchema = Depends(get_current_user)) -> AuthSchema: """ 执行权限验证 - Args: - request: 请求对象 - auth: 认证信息 + 参数: + - request (Request): 请求对象。 + - auth (AuthSchema): 认证信息。 - Returns: - AuthSchema: 认证对象 + 返回: + - AuthSchema: 认证对象 - Raises: - CustomException: 权限验证失败时抛出异常 + 异常: + - CustomException: 权限验证失败时抛出异常。 """ auth.check_data_scope = self.check_data_scope diff --git a/backend/app/core/exceptions.py b/backend/app/core/exceptions.py index 0301ebd8..691c0e4d 100644 --- a/backend/app/core/exceptions.py +++ b/backend/app/core/exceptions.py @@ -25,11 +25,17 @@ def __init__( success: bool = False ) -> None: """ - 初始化异常 - :param msg: 错误消息 - :param code: 业务状态码 - :param status_code: HTTP状态码 - :param data: 附加数据 + 初始化异常对象。 + + 参数: + - msg (str): 错误消息。 + - code (int): 业务状态码。 + - status_code (int): HTTP 状态码。 + - data (Any | None): 附加数据。 + - success (bool): 是否成功标记,默认 False。 + + 返回: + - None """ super().__init__(msg) # 调用父类初始化方法 self.status_code = status_code @@ -39,29 +45,63 @@ def __init__( self.success = success def __str__(self) -> str: - """返回异常消息""" + """返回异常消息 + + 返回: + - str: 异常消息 + """ return self.msg def handle_exception(app: FastAPI): """ - 全局异常处理 + 注册全局异常处理器。 + + 参数: + - app (FastAPI): 应用实例。 + + 返回: + - None """ @app.exception_handler(CustomException) async def CustomExceptionHandler(request: Request, exc: CustomException) -> JSONResponse: - """自定义异常处理器""" + """自定义异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (CustomException): 自定义异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误信息: {exc.msg}, 错误详情: {exc.data}") return ErrorResponse(msg=exc.msg, code=exc.code, status_code=exc.status_code, data=exc.data) @app.exception_handler(HTTPException) async def HttpExceptionHandler(request: Request, exc: HTTPException) -> JSONResponse: - """HTTP异常处理器""" + """HTTP异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (HTTPException): HTTP异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误详情: {exc.detail}") return ErrorResponse(msg=exc.detail, status_code=exc.status_code) @app.exception_handler(RequestValidationError) async def ValidationExceptionHandler(request: Request, exc: RequestValidationError) -> JSONResponse: - """请求参数验证异常处理器""" + """请求参数验证异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (RequestValidationError): 请求参数验证异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ error_mapping = { "Field required": "请求失败,缺少必填项!", "value is not a valid list": "类型错误,提交参数应该为列表!", @@ -75,30 +115,71 @@ async def ValidationExceptionHandler(request: Request, exc: RequestValidationErr @app.exception_handler(ResponseValidationError) async def ResponseValidationHandle(request: Request, exc: ResponseValidationError) -> JSONResponse: + """响应参数验证异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (ResponseValidationError): 响应参数验证异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误详情: {exc}") return ErrorResponse(msg=str(exc), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, data=exc.body) @app.exception_handler(SQLAlchemyError) async def SQLAlchemyExceptionHandler(request: Request, exc: SQLAlchemyError) -> JSONResponse: - """数据库异常处理器""" + """数据库异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (SQLAlchemyError): 数据库异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ error_msg = f'数据库操作失败: {exc}' logger.error(f"请求地址: {request.url}, 错误详情: {error_msg}") return ErrorResponse(msg=error_msg, status_code=status.HTTP_400_BAD_REQUEST, data=str(exc)) @app.exception_handler(ValueError) async def ValueExceptionHandler(request: Request, exc: ValueError) -> JSONResponse: - """值异常处理器""" + """值异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (ValueError): 值异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误详情: {exc}") return ErrorResponse(msg=str(exc)) @app.exception_handler(FieldValidationError) async def FieldValidationExceptionHandler(request: Request, exc: FieldValidationError) -> JSONResponse: - """字段验证异常处理器""" + """字段验证异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (FieldValidationError): 字段验证异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误信息: {exc.message}, 错误详情: {exc}") return ErrorResponse(msg=str(exc)) @app.exception_handler(Exception) async def AllExceptionHandler(request: Request, exc: Exception) -> JSONResponse: - """全局异常处理器""" + """全局异常处理器 + + 参数: + - request (Request): 请求对象。 + - exc (Exception): 异常实例。 + + 返回: + - JSONResponse: 包含错误信息的 JSON 响应。 + """ logger.error(f"请求地址: {request.url}, 错误详情: {exc}") return ErrorResponse(msg='服务器内部错误', status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, data=str(exc)) diff --git a/backend/app/core/logger.py b/backend/app/core/logger.py index 693ecca5..dc203844 100644 --- a/backend/app/core/logger.py +++ b/backend/app/core/logger.py @@ -1,14 +1,11 @@ # -*- coding: utf-8 -*-""" - -import os import time from datetime import datetime, timedelta import logging from logging.handlers import TimedRotatingFileHandler from typing import Optional, Dict, Any from pathlib import Path -import typing from app.config.setting import settings @@ -26,7 +23,9 @@ def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, super().__init__(filename, when, interval, backupCount, encoding, delay, utc) def doRollover(self) -> None: - """优化后的日志轮换,使用自定义命名格式""" + """ + 优化后的日志轮换,使用自定义命名格式 + """ # 使用流上下文管理确保资源正确释放 if self.stream: self.stream.close() diff --git a/backend/app/core/mongo_crud.py b/backend/app/core/mongo_crud.py index de8a89df..6555f694 100644 --- a/backend/app/core/mongo_crud.py +++ b/backend/app/core/mongo_crud.py @@ -24,10 +24,14 @@ def __init__( ): """ 初始化 MongoDB CURD 类。 - - :param db: 数据库连接 - :param collection: 集合名称 - :param schema: 序列化对象 + + 参数: + - db (AsyncIOMotorDatabase): 数据库连接。 + - collection (str): 集合名称。 + - schema (Any | None): 序列化对象,传入模型以进行编码,默认 None。 + + 返回: + - None """ self.db = db self.collection = db[collection] @@ -36,10 +40,16 @@ def __init__( async def get(self, _id: Optional[str] = None, **kwargs) -> Optional[Dict]: """ 获取单个数据,默认使用 ID 查询,否则使用关键词查询。 - - :param _id: 数据 ID - :param kwargs: 查询条件 - :return: 数据字典 + + 参数: + - _id (str | None): 数据 ID,若提供则按 ID 查询。 + - kwargs (Dict[str, Any]): 查询条件键值对。 + + 返回: + - Dict | None: 查询到的数据字典,未找到返回 None。 + + 异常: + - CustomException: 当 ID 无效或查询发生错误时抛出。 """ try: if _id: @@ -57,9 +67,15 @@ async def get(self, _id: Optional[str] = None, **kwargs) -> Optional[Dict]: async def create(self, data: Union[Dict, Any]) -> InsertOneResult: """ 创建数据。 - - :param data: 要创建的数据 - :return: 插入结果 + + 参数: + - data (Dict | Any): 要创建的数据,可为字典或可编码对象。 + + 返回: + - InsertOneResult: 插入操作结果。 + + 异常: + - CustomException: 创建失败时抛出。 """ try: if not isinstance(data, dict): @@ -82,11 +98,17 @@ async def create(self, data: Union[Dict, Any]) -> InsertOneResult: async def update(self, _id: str, data: Union[Dict, Any], upsert: bool = False) -> UpdateResult: """ 更新数据。 - - :param _id: 数据 ID - :param data: 要更新的数据 - :param upsert: 不存在是否插入 - :return: 更新结果 + + 参数: + - _id (str): 数据 ID。 + - data (Dict | Any): 要更新的数据,可为字典或可编码对象。 + - upsert (bool): 不存在是否插入,默认 False。 + + 返回: + - UpdateResult: 更新操作结果。 + + 异常: + - CustomException: ID 无效或更新失败时抛出。 """ try: if not isinstance(data, dict): @@ -111,9 +133,15 @@ async def update(self, _id: str, data: Union[Dict, Any], upsert: bool = False) - async def delete(self, _id: Union[str, List[str]]) -> DeleteResult: """ 删除数据,支持批量删除。 - - :param _id: 单个ID或ID列表 - :return: 删除结果 + + 参数: + - _id (str | List[str]): 单个 ID 或 ID 列表。 + + 返回: + - DeleteResult: 删除操作结果。 + + 异常: + - CustomException: ID 无效或未删除任何数据时抛出。 """ try: if isinstance(_id, list): @@ -138,12 +166,18 @@ async def list( ) -> List[Dict]: """ 查询数据列表。 - - :param page_no: 页码 - :param page_size: 每页数量 - :param order_by: 排序条件 [{'field': 'field_name', 'direction': 1}] - :param kwargs: 查询条件 - :return: 数据列表 + + 参数: + - page_no (int | None): 页码,默认 1。 + - page_size (int | None): 每页数量,默认 10。 + - order_by (List[Dict] | None): 排序条件,形如 [{'field': '字段名', 'direction': 1}]。 + - kwargs (Dict[str, Any]): 查询条件键值对。 + + 返回: + - List[Dict]: 数据列表。 + + 异常: + - CustomException: 查询失败时抛出。 """ try: params = self.filter_condition(**kwargs) @@ -166,9 +200,15 @@ async def list( async def count(self, **kwargs) -> int: """ 获取数据总数。 - - :param kwargs: 查询条件 - :return: 数据总数 + + 参数: + - kwargs (Dict[str, Any]): 查询条件键值对。 + + 返回: + - int: 数据总数。 + + 异常: + - CustomException: 统计失败时抛出。 """ try: params = self.filter_condition(**kwargs) @@ -180,9 +220,15 @@ async def count(self, **kwargs) -> int: def filter_condition(**kwargs) -> Dict: """ 构建过滤条件。 - - :param kwargs: 查询参数 - :return: 过滤条件字典 + + 参数: + - kwargs (Dict[str, Any]): 查询参数,支持 ('like'|'between'|'ObjectId'|'in'|'gt'|'gte'|'lt'|'lte') 等操作。 + + 返回: + - Dict: 过滤条件字典。 + + 异常: + - CustomException: 当 ObjectId 格式无效时抛出。 """ params = {} for k, v in kwargs.items(): diff --git a/backend/app/core/redis_crud.py b/backend/app/core/redis_crud.py index 176e3aa6..0cde7400 100644 --- a/backend/app/core/redis_crud.py +++ b/backend/app/core/redis_crud.py @@ -10,18 +10,18 @@ class RedisCURD: """缓存工具类""" - def __init__(self, redis: Redis): + def __init__(self, redis: Redis) -> None: """初始化""" self.redis = redis async def mget(self, keys: list) -> list: """批量获取缓存 - Args: - keys: 键名列表 + 参数: + - keys (list): 键名列表 - Returns: - list: 返回缓存值列表 + 返回: + - list: 返回缓存值列表,如果获取失败则返回空列表 """ try: data = await self.redis.mget(*[str(key) for key in keys]) @@ -31,7 +31,14 @@ async def mget(self, keys: list) -> list: return [] async def get_keys(self, pattern: str = "*") -> list: - """获取缓存键名""" + """获取缓存键名 + + 参数: + - pattern (str, optional): 匹配模式,默认值为"*"。 + + 返回: + - list: 返回匹配的缓存键名列表,如果获取失败则返回空列表 + """ try: keys = await self.redis.keys(f"{pattern}") return keys @@ -41,7 +48,14 @@ async def get_keys(self, pattern: str = "*") -> list: async def get(self, key: str) -> Any: - """获取缓存""" + """获取缓存 + + 参数: + - key (str): 缓存键名 + + 返回: + - Any: 返回缓存值,如果缓存不存在则返回None + """ try: data = await self.redis.get(f"{key}") @@ -55,7 +69,16 @@ async def get(self, key: str) -> Any: return None async def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool: - """设置缓存""" + """设置缓存 + + 参数: + - key (str): 缓存键名 + - value (Any): 缓存值 + - expire (Optional[int], optional): 过期时间,单位为秒,默认值为None。 + + 返回: + - bool: 如果设置缓存成功则返回True,否则返回False + """ try: # 根据数据类型选择序列化方式 if isinstance(value, (int, float, str)): @@ -79,7 +102,14 @@ async def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool: return False async def delete(self, *keys: str) -> bool: - """删除缓存""" + """删除缓存 + + 参数: + - keys (str): 缓存键名 + + 返回: + - bool: 如果删除缓存成功则返回True,否则返回False + """ try: await self.redis.delete(*keys) return True @@ -88,7 +118,14 @@ async def delete(self, *keys: str) -> bool: return False async def clear(self, pattern: str = "*") -> bool: - """清空缓存""" + """清空缓存 + + 参数: + - pattern (str, optional): 匹配模式,默认值为"*"。 + + 返回: + - bool: 如果清空缓存成功则返回True,否则返回False + """ try: keys = await self.redis.keys(f"{pattern}") if keys: @@ -99,7 +136,14 @@ async def clear(self, pattern: str = "*") -> bool: return False async def exists(self, key: str) -> bool: - """判断缓存是否存在""" + """判断缓存是否存在 + + 参数: + - key (str): 缓存键名 + + 返回: + - bool: 如果缓存存在则返回True,否则返回False + """ try: return await self.redis.exists(f"{key}") except Exception as e: @@ -107,7 +151,14 @@ async def exists(self, key: str) -> bool: return False async def ttl(self, key: str) -> int: - """获取缓存过期时间""" + """获取缓存过期时间 + + 参数: + - key (str): 缓存键名 + + 返回: + - int: 返回缓存过期时间,单位为秒,如果缓存没有设置过期时间则返回-1 + """ try: return await self.redis.ttl(f"{key}") except Exception as e: @@ -115,7 +166,15 @@ async def ttl(self, key: str) -> int: return -1 async def expire(self, key: str, expire: int) -> bool: - """设置缓存过期时间""" + """设置缓存过期时间 + + 参数: + - key (str): 缓存键名 + - expire (int): 过期时间,单位为秒 + + 返回: + - bool: 如果设置缓存过期时间成功则返回True,否则返回False + """ try: return await self.redis.expire(f"{key}", expire) except Exception as e: @@ -123,7 +182,11 @@ async def expire(self, key: str, expire: int) -> bool: return False async def info(self) -> dict: - """获取缓存信息""" + """获取缓存信息 + + 返回: + - dict: 返回缓存信息字典,如果获取失败则返回空字典 + """ try: return await self.redis.info() except Exception as e: @@ -131,14 +194,22 @@ async def info(self) -> dict: return {} async def db_size(self) -> int: - """获取数据库大小""" + """获取数据库大小 + + 返回: + - int: 返回数据库大小,如果获取失败则返回0 + """ try: return await self.redis.dbsize() except Exception as e: logger.error(f"获取数据库大小失败: {str(e)}") return 0 - async def commandstats(self): - """获取命令统计信息""" + async def commandstats(self) -> dict: + """获取命令统计信息 + + 返回: + - dict: 返回命令统计信息字典,如果获取失败则返回空字典 + """ try: return await self.redis.info("commandstats") except Exception as e: @@ -146,7 +217,16 @@ async def commandstats(self): return {} async def hash_set(self, name: str, key: str, value: Any) -> bool: - """设置哈希缓存""" + """设置哈希缓存 + + 参数: + - name (str): 哈希缓存名称 + - key (str): 哈希缓存键名 + - value (Any): 哈希缓存值 + + 返回: + - bool: 如果设置哈希缓存成功则返回True,否则返回False + """ try: self.redis.hset(name=name, key=key, value=value) return True @@ -155,7 +235,15 @@ async def hash_set(self, name: str, key: str, value: Any) -> bool: return False async def hash_get(self, name: str, keys: list[str]) -> Awaitable[List[Any]] | List[Any]: - """获取哈希缓存""" + """获取哈希缓存 + + 参数: + - name (str): 哈希缓存名称 + - keys (list[str]): 哈希缓存键名列表 + + 返回: + - Awaitable[List[Any]] | List[Any]: 返回哈希缓存值列表,如果获取失败则返回空列表 + """ try: data = self.redis.hmget(name=name, keys=keys) return data diff --git a/backend/app/core/router_class.py b/backend/app/core/router_class.py index efe2cb6a..fa90924a 100644 --- a/backend/app/core/router_class.py +++ b/backend/app/core/router_class.py @@ -23,9 +23,28 @@ class OperationLogRoute(APIRoute): """操作日志路由装饰器""" def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: - original_route_handler = super().get_route_handler() + """ + 自定义路由处理程序,在每个路由处理之前或之后执行特定的操作。 + + 参数: + - request (Request): FastAPI请求对象。 + 返回: + - Response: FastAPI响应对象。 + """ + original_route_handler = super().get_route_handler() + async def custom_route_handler(request: Request) -> Response: + """ + 自定义路由处理程序,在每个路由处理之前或之后执行特定的操作。 + + 参数: + - request (Request): FastAPI请求对象。 + 描述: + - 该方法在每个路由处理之前被调用,用于记录操作日志。 + 返回: + - Response: FastAPI响应对象。 + """ start_time = time.time() # 请求前的处理 response: Response = await original_route_handler(request) diff --git a/backend/app/core/security.py b/backend/app/core/security.py index 960a2142..36a0bd44 100644 --- a/backend/app/core/security.py +++ b/backend/app/core/security.py @@ -31,7 +31,18 @@ def __init__( ) async def __call__(self, request: Request) -> Optional[str]: - """重写认证方法,校验token""" + """ + 重写认证方法,校验token + + 参数: + - request (Request): FastAPI请求对象。 + + 返回: + - Optional[str]: 校验通过的token,如果校验失败则返回None。 + + 异常: + - CustomException: 认证失败时抛出,状态码为401。 + """ authorization = request.headers.get("Authorization") scheme, token = get_authorization_scheme_param(authorization) @@ -43,7 +54,20 @@ async def __call__(self, request: Request) -> Optional[str]: class CustomOAuth2PasswordRequestForm(OAuth2PasswordRequestForm): - """自定义登录表单,扩展验证码等字段""" + """ + 自定义登录表单,扩展验证码等字段 + + 参数: + - grant_type (str | None): 授权类型,默认值为None,正则表达式为'password'。 + - scope (str): 作用域,默认值为空字符串。 + - client_id (Optional[str]): 客户端ID,默认值为None。 + - client_secret (Optional[str]): 客户端密钥,默认值为None。 + - username (str): 用户名。 + - password (str): 密码。 + - captcha_key (Optional[str]): 验证码键,默认值为空字符串。 + - captcha (Optional[str]): 验证码值,默认值为空字符串。 + - login_type (Optional[str]): 登录类型,默认值为"PC端",描述为"PC端 | 移动端"。 + """ def __init__( self, @@ -78,7 +102,15 @@ def __init__( def create_access_token(payload: JWTPayloadSchema) -> str: - """生成JWT访问令牌""" + """ + 生成JWT访问令牌 + + 参数: + - payload (JWTPayloadSchema): JWT有效载荷,包含用户信息等。 + + 返回: + - str: 生成的JWT访问令牌。 + """ payload_dict = payload.model_dump() return jwt.encode( payload=payload_dict, @@ -88,7 +120,18 @@ def create_access_token(payload: JWTPayloadSchema) -> str: def decode_access_token(token: str) -> JWTPayloadSchema: - """解析JWT访问令牌""" + """ + 解析JWT访问令牌 + + 参数: + - token (str): JWT访问令牌字符串。 + + 返回: + - JWTPayloadSchema: 解析后的JWT有效载荷,包含用户信息等。 + + 异常: + - CustomException: 解析失败时抛出,状态码为401。 + """ if not token: raise CustomException(msg="认证不存在,请重新登录", code=10401, status_code=401) diff --git a/backend/app/core/serialize.py b/backend/app/core/serialize.py index de736df4..22b14ee2 100644 --- a/backend/app/core/serialize.py +++ b/backend/app/core/serialize.py @@ -10,23 +10,23 @@ class Serialize(Generic[ModelType, SchemaType]): """ - 序列化工具类,提供模型、Schema和字典之间的转换功能 + 序列化工具类,提供模型、Schema 和字典之间的转换功能 """ @classmethod def schema_to_model(cls,schema: Type[SchemaType], model: Type[ModelType]) -> ModelType: """ - 将Pydantic Schema转换为SQLAlchemy模型 + 将 Pydantic Schema 转换为 SQLAlchemy 模型 - Args: - schema: Pydantic Schema实例 - model: SQLAlchemy模型类 + 参数: + - schema (Type[SchemaType]): Pydantic Schema 实例。 + - model (Type[ModelType]): SQLAlchemy 模型类。 - Returns: - SQLAlchemy模型实例 + 返回: + - ModelType: SQLAlchemy 模型实例。 - Raises: - Exception: 转换过程中可能抛出的异常 + 异常: + - ValueError: 转换过程中可能抛出的异常。 """ try: return model(**cls.model_to_dict(model, schema)) @@ -36,17 +36,17 @@ def schema_to_model(cls,schema: Type[SchemaType], model: Type[ModelType]) -> Mod @classmethod def model_to_dict(cls, model: Type[ModelType], schema: Type[SchemaType]) -> Dict[str, Any]: """ - 将SQLAlchemy模型转换为Pydantic Schema + 将 SQLAlchemy 模型转换为 Pydantic Schema - Args: - model: SQLAlchemy模型实例 - schema: Pydantic Schema类 + 参数: + - model (Type[ModelType]): SQLAlchemy 模型实例。 + - schema (Type[SchemaType]): Pydantic Schema 类。 - Returns: - 包含模型数据的字典 + 返回: + - Dict[str, Any]: 包含模型数据的字典。 - Raises: - Exception: 转换过程中可能抛出的异常 + 异常: + - ValueError: 转换过程中可能抛出的异常。 """ try: return schema.model_validate(model).model_dump() diff --git a/backend/app/core/validator.py b/backend/app/core/validator.py index 3def990c..a6967f3f 100644 --- a/backend/app/core/validator.py +++ b/backend/app/core/validator.py @@ -35,11 +35,16 @@ def datetime_validator(value: Union[str, datetime]) -> datetime: """ - 日期格式验证器 + 日期格式验证器。 - :param value: 日期值 - :return: 格式化后的日期 - :raises: CustomException 日期格式无效时抛出 + 参数: + - value (str | datetime): 日期值。 + + 返回: + - datetime: 格式化后的日期。 + + 异常: + - CustomException: 日期格式无效时抛出。 """ pattern = "%Y-%m-%d %H:%M:%S" try: @@ -53,13 +58,19 @@ def datetime_validator(value: Union[str, datetime]) -> datetime: # 如果 value 是 None 或其他类型,抛出异常 raise CustomException(code=RET.ERROR.code, msg="无效的日期格式") + def email_validator(value: str) -> str: """ - 邮箱地址验证器 + 邮箱地址验证器。 + + 参数: + - value (str): 邮箱地址。 - :param value: 邮箱地址 - :return: 验证后的邮箱地址 - :raises: CustomException 邮箱格式无效时抛出 + 返回: + - str: 验证后的邮箱地址。 + + 异常: + - CustomException: 邮箱格式无效时抛出。 """ if not value: raise CustomException(code=RET.ERROR.code, msg="邮箱地址不能为空") @@ -71,13 +82,19 @@ def email_validator(value: str) -> str: return value + def mobile_validator(value: Optional[str]) -> Optional[str]: """ - 手机号验证器 + 手机号验证器。 + + 参数: + - value (str | None): 手机号。 - :param value: 手机号 - :return: 验证后的手机号 - :raises: CustomException 手机号格式无效时抛出 + 返回: + - str | None: 验证后的手机号。 + + 异常: + - CustomException: 手机号格式无效时抛出。 """ if not value: return value @@ -92,13 +109,19 @@ def mobile_validator(value: Optional[str]) -> Optional[str]: return value + def menu_request_validator(data): """ - 菜单请求数据验证器 + 菜单请求数据验证器。 + + 参数: + - data (Any): 请求数据。 + + 返回: + - Any: 验证后的请求数据。 - :param data: 请求数据 - :return: 验证后的请求数据 - :raises: CustomException 请求数据无效时抛出 + 异常: + - CustomException: 请求数据无效时抛出。 """ menu_types = {1: "目录", 2: "功能", 3: "权限", 4: "外链"} @@ -116,13 +139,19 @@ def menu_request_validator(data): return data + def role_permission_request_validator(data): """ - 角色权限设置数据验证器 + 角色权限设置数据验证器。 + + 参数: + - data (Any): 请求数据。 + + 返回: + - Any: 验证后的请求数据。 - :param data: 请求数据 - :return: 验证后的请求数据 - :raises: CustomException 请求数据无效时抛出 + 异常: + - CustomException: 请求数据无效时抛出。 """ data_scopes = { 1: "仅本人数据权限", diff --git a/backend/app/module_task/scheduler_test.py b/backend/app/module_task/scheduler_test.py index edc7a6ff..feee0972 100644 --- a/backend/app/module_task/scheduler_test.py +++ b/backend/app/module_task/scheduler_test.py @@ -5,9 +5,13 @@ from app.core.logger import logger -def job(*args, **kwargs): +def job(*args, **kwargs) -> None: """ 定时任务执行同步函数示例 + + 参数: + - args: 位置参数。 + - kwargs: 关键字参数。 """ try: print(f"开始执行任务: {args}-{kwargs}") @@ -17,9 +21,13 @@ def job(*args, **kwargs): logger.error(f"同步任务执行失败: {e}") raise -async def async_job(*args, **kwargs): +async def async_job(*args, **kwargs) -> None: """ 定时任务执行异步函数示例 + + 参数: + - args: 位置参数。 + - kwargs: 关键字参数。 """ try: print(f"开始执行任务: {args}-{kwargs}") diff --git a/backend/app/plugin/init_app.py b/backend/app/plugin/init_app.py index 954de81e..662ae389 100644 --- a/backend/app/plugin/init_app.py +++ b/backend/app/plugin/init_app.py @@ -20,12 +20,19 @@ from app.api.v1.module_system.params.service import ParamsService from app.api.v1.module_system.dict.service import DictDataService from app.api.v1 import router +from app.utils.console import run as console_run @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: """ - 自定义生命周期 + 自定义 FastAPI 应用生命周期。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - AsyncGenerator[Any, Any]: 生命周期上下文生成器。 """ logger.info(settings.BANNER + '\n' + f'{settings.TITLE} 服务开始启动...') @@ -41,6 +48,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: logger.info('✅️ 初始化定时任务完成...') logger.info(f'✅️ {settings.TITLE} 服务成功启动...') + # 控制台输出优化:展示服务信息与文档地址 + console_run(settings.SERVER_HOST, settings.SERVER_PORT, settings.RELOAD, settings.WORKERS) yield @@ -50,7 +59,13 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: def register_middlewares(app: FastAPI) -> None: """ - 注册中间件 + 注册全局中间件。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - None """ for middleware in settings.MIDDLEWARE_LIST[::-1]: if not middleware: @@ -60,19 +75,37 @@ def register_middlewares(app: FastAPI) -> None: def register_exceptions(app: FastAPI) -> None: """ - 异常捕捉 + 统一注册异常处理器。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - None """ handle_exception(app) def register_routers(app: FastAPI) -> None: """ - 注册根路由 + 注册根路由。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - None """ app.include_router(router=router) def register_files(app: FastAPI) -> None: """ - 注册文件相关配置 + 注册静态资源挂载和文件相关配置。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - None """ # 挂载静态文件目录 if settings.STATIC_ENABLE: @@ -82,7 +115,13 @@ def register_files(app: FastAPI) -> None: def reset_api_docs(app: FastAPI) -> None: """ - 自定义配置接口本地静态文档 + 使用本地静态资源自定义 API 文档页面(Swagger UI 与 ReDoc)。 + + 参数: + - app (FastAPI): FastAPI 应用实例。 + + 返回: + - None """ @app.get(settings.DOCS_URL, include_in_schema=False) diff --git a/backend/app/scripts/initialize.py b/backend/app/scripts/initialize.py index ec474ff4..84d0ea37 100644 --- a/backend/app/scripts/initialize.py +++ b/backend/app/scripts/initialize.py @@ -27,6 +27,9 @@ class InitializeData: """ def __init__(self) -> None: + """ + 初始化数据库和基础数据 + """ # 按照依赖关系排序:先创建基础表,再创建关联表 self.prepare_init_models = [ # 部门表(自引用,需要先创建) @@ -53,7 +56,9 @@ def __init__(self) -> None: ] async def __init_create_table(self) -> None: - """初始化表结构(第一阶段)""" + """ + 初始化表结构(第一阶段) + """ try: # 使用引擎创建所有表 async with async_engine.begin() as conn: @@ -64,7 +69,12 @@ async def __init_create_table(self) -> None: raise async def __init_data(self, db: AsyncSession) -> None: - """初始化基础数据""" + """ + 初始化基础数据 + + 参数: + - db (AsyncSession): 异步数据库会话。 + """ for model in self.prepare_init_models: table_name = model.__tablename__ @@ -98,7 +108,16 @@ async def __init_data(self, db: AsyncSession) -> None: raise def __create_objects_with_children(self, data: List[Dict], model_class) -> List: - """通用递归创建对象函数,处理嵌套的 children 数据""" + """ + 通用递归创建对象函数,处理嵌套的 children 数据 + + 参数: + - data (List[Dict]): 包含嵌套 children 数据的列表。 + - model_class: 对应的 SQLAlchemy 模型类。 + + 返回: + - List: 包含创建的对象的列表。 + """ objs = [] def create_object(obj_data: Dict): @@ -120,7 +139,15 @@ def create_object(obj_data: Dict): return objs async def __get_data(self, filename: str) -> List[Dict]: - """读取初始化数据文件""" + """ + 读取初始化数据文件 + + 参数: + - filename (str): 文件名(不包含扩展名)。 + + 返回: + - List[Dict]: 解析后的 JSON 数据列表。 + """ json_path = Path.joinpath(settings.SCRIPT_DIR, f'{filename}.json') if not json_path.exists(): return [] diff --git a/backend/app/utils/ai_util.py b/backend/app/utils/ai_util.py index 4e5f6c9c..73bd2b51 100644 --- a/backend/app/utils/ai_util.py +++ b/backend/app/utils/ai_util.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from typing import Any, AsyncGenerator from openai import AsyncOpenAI, OpenAI from openai.types.chat.chat_completion import ChatCompletion import httpx @@ -9,6 +10,9 @@ class AIClient: + """ + AI客户端类,用于与OpenAI API交互。 + """ def __init__(self): self.model = settings.OPENAI_MODEL @@ -25,8 +29,16 @@ def __init__(self): http_client=self.http_client ) - async def process(self, query: str): - """处理查询并返回流式响应""" + async def process(self, query: str) -> AsyncGenerator[str, None]: + """ + 处理查询并返回流式响应 + + 参数: + - query (str): 用户查询。 + + 返回: + - AsyncGenerator[str, None]: 流式响应内容。 + """ system_prompt = """你是一个有用的AI助手,可以帮助用户回答问题和提供帮助。请用中文回答用户的问题。""" try: @@ -49,8 +61,10 @@ async def process(self, query: str): logger.error(f"AI处理查询失败: {str(e)}") yield f"抱歉,处理您的请求时出现了错误: {str(e)}" - async def close(self): - """关闭客户端连接""" + async def close(self) -> None: + """ + 关闭客户端连接 + """ if hasattr(self, 'client'): await self.client.close() if hasattr(self, 'http_client'): diff --git a/backend/app/utils/captcha_util.py b/backend/app/utils/captcha_util.py index f0c2fa3c..9172ddd5 100644 --- a/backend/app/utils/captcha_util.py +++ b/backend/app/utils/captcha_util.py @@ -17,8 +17,10 @@ class CaptchaUtil: @classmethod def generate_captcha(cls) -> Tuple[str, str]: """ - 生成带有噪声和干扰的验证码图片:4位随机字符 - :return: [base64编码的图片字符串, 验证码值] + 生成带有噪声和干扰的验证码图片(4位随机字符)。 + + 返回: + - Tuple[str, str]: [base64图片字符串, 验证码值]。 """ # 生成4位随机验证码 chars = string.digits + string.ascii_letters @@ -81,8 +83,10 @@ def generate_captcha(cls) -> Tuple[str, str]: @classmethod def captcha_arithmetic(cls) -> Tuple[str, int]: """ - 创建验证码图片: 加减乘运算 - :return: [base64编码的图片字符串, 计算结果] + 创建验证码图片(加减乘运算)。 + + 返回: + - Tuple[str, int]: [base64图片字符串, 计算结果]。 """ # 创建空白图像,使用随机浅色背景 background_color = tuple(random.randint(230, 255) for _ in range(3)) diff --git a/backend/app/utils/common_util.py b/backend/app/utils/common_util.py index 02efbf98..7730d6f0 100644 --- a/backend/app/utils/common_util.py +++ b/backend/app/utils/common_util.py @@ -12,7 +12,7 @@ from app.core.exceptions import CustomException -def worship(): +def worship() -> None: print(""" ______ _ _ | ____| | | /\ (_) @@ -28,9 +28,13 @@ def worship(): def import_module(module: str, desc: str) -> Any: """ 动态导入模块 - :param module: 模块名称 - :param desc: 模块描述 - :return: 模块对象 + + 参数: + - module (str): 模块名称。 + - desc (str): 模块描述。 + + 返回: + - Any: 模块对象。 """ try: module_path, module_class = module.rsplit(".", 1) @@ -44,12 +48,17 @@ def import_module(module: str, desc: str) -> Any: raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}") -async def import_modules_async(modules: list, desc: str, **kwargs): +async def import_modules_async(modules: list, desc: str, **kwargs) -> None: """ 异步导入模块列表 - :param modules: 模块列表 - :param desc: 模块描述 - :param kwargs: 额外参数 + + 参数: + - modules (list[str]): 模块列表。 + - desc (str): 模块描述。 + - kwargs: 额外参数。 + + 返回: + - None """ for module in modules: if not module: @@ -68,26 +77,39 @@ async def import_modules_async(modules: list, desc: str, **kwargs): def get_random_character() -> str: - """生成随机字符串""" + """ + 生成随机字符串 + + 返回: + - str: 随机字符串。 + """ return uuid.uuid4().hex def get_parent_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, int]: """ - 获取父级ID映射字典 - :param model_list: 模型列表 - :return: {id: parent_id} 映射字典 + 获取父级 ID 映射字典 + + 参数: + - model_list (Sequence[DeclarativeBase]): 模型列表。 + + 返回: + - Dict[int, int]: {id: parent_id} 映射字典。 """ return {item.id: item.parent_id for item in model_list} def get_parent_recursion(id: int, id_map: Dict[int, int], ids: Optional[List[int]] = None) -> List[int]: """ - 递归获取所有父级ID - :param id: 当前ID - :param id_map: ID映射字典 - :param ids: 已收集的ID列表 - :return: 所有父级ID列表 + 递归获取所有父级 ID + + 参数: + - id (int): 当前 ID。 + - id_map (Dict[int, int]): ID 映射字典。 + - ids (List[int] | None): 已收集的 ID 列表。 + + 返回: + - List[int]: 所有父级 ID 列表。 """ ids = ids or [] if id in ids: @@ -101,9 +123,13 @@ def get_parent_recursion(id: int, id_map: Dict[int, int], ids: Optional[List[int def get_child_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, List[int]]: """ - 获取子级ID映射字典 - :param model_list: 模型列表 - :return: {id: [child_ids]} 映射字典 + 获取子级 ID 映射字典 + + 参数: + - model_list (Sequence[DeclarativeBase]): 模型列表。 + + 返回: + - Dict[int, List[int]]: {id: [child_ids]} 映射字典。 """ data_map = {} for model in model_list: @@ -115,11 +141,15 @@ def get_child_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, List[in def get_child_recursion(id: int, id_map: Dict[int, List[int]], ids: Optional[List[int]] = None) -> List[int]: """ - 递归获取所有子级ID - :param id: 当前ID - :param id_map: ID映射字典 - :param ids: 已收集的ID列表 - :return: 所有子级ID列表 + 递归获取所有子级 ID + + 参数: + - id (int): 当前 ID。 + - id_map (Dict[int, List[int]]): ID 映射字典。 + - ids (List[int] | None): 已收集的 ID 列表。 + + 返回: + - List[int]: 所有子级 ID 列表。 """ ids = ids or [] ids.append(id) @@ -132,8 +162,11 @@ def traversal_to_tree(nodes: list[dict[str, Any]]) -> list[dict[str, Any]]: """ 通过遍历算法构造树形结构 - :param nodes: 树节点列表 - :return: + 参数: + - nodes (list[dict[str, Any]]): 树节点列表。 + + 返回: + - list[dict[str, Any]]: 构造后的树形结构列表。 """ tree: list[dict[str, Any]] = [] node_dict = {node['id']: node for node in nodes} @@ -169,9 +202,12 @@ def recursive_to_tree(nodes: list[dict[str, Any]], *, parent_id: int | None = No """ 通过递归算法构造树形结构(性能影响较大) - :param nodes: 树节点列表 - :param parent_id: 父节点 ID,默认为 None 表示根节点 - :return: + 参数: + - nodes (list[dict[str, Any]]): 树节点列表。 + - parent_id (int | None): 父节点 ID,默认为 None 表示根节点。 + + 返回: + - list[dict[str, Any]]: 构造后的树形结构列表。 """ tree: list[dict[str, Any]] = [] for node in nodes: @@ -186,9 +222,13 @@ def recursive_to_tree(nodes: list[dict[str, Any]], *, parent_id: int | None = No def bytes2human(n: int, format_str: str = '%(value).1f%(symbol)s') -> str: """ 字节数转人类可读格式 - :param n: 字节数 - :param format_str: 格式化字符串 - :return: 可读的字节字符串,如 '1.5MB' + + 参数: + - n (int): 字节数。 + - format_str (str): 格式化字符串,默认 '%(value).1f%(symbol)s'。 + + 返回: + - str: 可读的字节字符串,如 '1.5MB'。 """ symbols = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') prefix = {s: 1 << (i + 1) * 10 for i, s in enumerate(symbols[1:])} @@ -208,8 +248,11 @@ def get_filepath_from_url(url: str) -> Path: """ 工具方法:根据请求参数获取文件路径 - :param url: 请求参数中的url参数 - :return: 文件路径 + 参数: + - url (str): 请求参数中的 url 参数。 + + 返回: + - Path: 文件路径。 """ file_info = url.split('?')[1].split('&') task_id = file_info[0].split('=')[1] diff --git a/backend/app/utils/console.py b/backend/app/utils/console.py index 7e12ee08..b5a13071 100644 --- a/backend/app/utils/console.py +++ b/backend/app/utils/console.py @@ -1,5 +1,33 @@ # -*- coding: utf-8 -*- from rich import get_console +from rich.panel import Panel +from rich.prompt import IntPrompt +from rich.table import Table +from rich.text import Text + +from app.config.setting import settings console = get_console() + +def run(host: str, port: int, reload: bool, workers: int) -> None: + url = f'http://{host}:{port}' + base_url = f'{url}{settings.ROOT_PATH}' + docs_url = base_url + settings.DOCS_URL + redoc_url = base_url + settings.REDOC_URL + + panel_content = Text() + panel_content.append(f'当前版本: v{settings.VERSION}') + panel_content.append(f'\n服务地址: {url}') + panel_content.append(f'\n根路由前缀: {settings.ROOT_PATH}') + panel_content.append(f'\n运行环境: {getattr(settings.ENVIRONMENT, "value", settings.ENVIRONMENT)}') + panel_content.append(f'\n数据库类型: {settings.DATABASE_TYPE}') + panel_content.append(f'\n日志级别: {settings.LOGGER_LEVEL}') + panel_content.append(f'\n重载: {reload} 进程: {workers}') + panel_content.append('\n官方地址: https://service.fastapiadmin.com') + + if settings.DEBUG: + panel_content.append(f'\n\n📖 Swagger 文档: {docs_url}', style='yellow') + panel_content.append(f'\n📚 Redoc 文档: {redoc_url}', style='blue') + + console.print(Panel(panel_content, title='FastAPI_Vue3_Admin 服务信息', border_style='purple', padding=(1, 2))) diff --git a/backend/app/utils/cron_util.py b/backend/app/utils/cron_util.py index fbc69e74..2eaea93d 100644 --- a/backend/app/utils/cron_util.py +++ b/backend/app/utils/cron_util.py @@ -10,7 +10,18 @@ class CronUtil: """ @classmethod - def __valid_range(cls, search_str: str, start_range: int, end_range: int): + def __valid_range(cls, search_str: str, start_range: int, end_range: int) -> bool: + """ + 校验范围表达式的合法性。 + + 参数: + - search_str (str): 范围表达式。 + - start_range (int): 开始范围。 + - end_range (int): 结束范围。 + + 返回: + - bool: 校验是否通过。 + """ match = re.match(r'^(\d+)-(\d+)$', search_str) if match: start, end = int(match.group(1)), int(match.group(2)) @@ -18,9 +29,21 @@ def __valid_range(cls, search_str: str, start_range: int, end_range: int): return False @classmethod - def __valid_sum( - cls, search_str: str, start_range_a: int, start_range_b: int, end_range_a: int, end_range_b: int, sum_range: int - ): + def __valid_sum(cls, search_str: str, start_range_a: int, start_range_b: int, end_range_a: int, end_range_b: int, sum_range: int) -> bool: + """ + 校验和表达式的合法性。 + + 参数: + - search_str (str): 和表达式。 + - start_range_a (int): 开始范围A。 + - start_range_b (int): 开始范围B。 + - end_range_a (int): 结束范围A。 + - end_range_b (int): 结束范围B。 + - sum_range (int): 总和范围。 + + 返回: + - bool: 校验是否通过。 + """ match = re.match(r'^(\d+)/(\d+)$', search_str) if match: start, end = int(match.group(1)), int(match.group(2)) @@ -32,12 +55,15 @@ def __valid_sum( return False @classmethod - def validate_second_or_minute(cls, second_or_minute: str): + def validate_second_or_minute(cls, second_or_minute: str) -> bool: """ - 校验秒或分钟值是否正确 + 校验秒或分钟字段的合法性。 + + 参数: + - second_or_minute (str): 秒或分钟值。 - :param second_or_minute: 秒或分钟值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( second_or_minute == '*' @@ -49,12 +75,15 @@ def validate_second_or_minute(cls, second_or_minute: str): return False @classmethod - def validate_hour(cls, hour: str): + def validate_hour(cls, hour: str) -> bool: """ - 校验小时值是否正确 + 校验小时字段的合法性。 - :param hour: 小时值 - :return: 校验结果 + 参数: + - hour (str): 小时值。 + + 返回: + - bool: 校验是否通过。 """ if ( hour == '*' @@ -66,12 +95,15 @@ def validate_hour(cls, hour: str): return False @classmethod - def validate_day(cls, day: str): + def validate_day(cls, day: str) -> bool: """ - 校验日值是否正确 + 校验日期字段的合法性。 + + 参数: + - day (str): 日值。 - :param day: 日值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( day in ['*', '?', 'L'] @@ -84,12 +116,15 @@ def validate_day(cls, day: str): return False @classmethod - def validate_month(cls, month: str): + def validate_month(cls, month: str) -> bool: """ - 校验月值是否正确 + 校验月份字段的合法性。 + + 参数: + - month (str): 月值。 - :param month: 月值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( month == '*' @@ -101,12 +136,15 @@ def validate_month(cls, month: str): return False @classmethod - def validate_week(cls, week: str): + def validate_week(cls, week: str) -> bool: """ - 校验周值是否正确 + 校验星期字段的合法性。 - :param week: 周值 - :return: 校验结果 + 参数: + - week (str): 周值。 + + 返回: + - bool: 校验是否通过。 """ if ( week in ['*', '?'] @@ -119,12 +157,15 @@ def validate_week(cls, week: str): return False @classmethod - def validate_year(cls, year: str): + def validate_year(cls, year: str) -> bool: """ - 校验年值是否正确 + 校验年份字段的合法性。 + + 参数: + - year (str): 年值。 - :param year: 年值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ current_year = int(datetime.now().year) future_years = [current_year + i for i in range(9)] @@ -143,12 +184,15 @@ def validate_year(cls, year: str): return False @classmethod - def validate_cron_expression(cls, cron_expression: str): + def validate_cron_expression(cls, cron_expression: str) -> bool | None: """ - 校验Cron表达式是否正确 + 校验 Cron 表达式是否正确。 + + 参数: + - cron_expression (str): Cron 表达式。 - :param cron_expression: Cron表达式 - :return: 校验结果 + 返回: + - bool | None: 校验是否通过。 """ values = cron_expression.split() if len(values) != 6 and len(values) != 7: diff --git a/backend/app/utils/excel_util.py b/backend/app/utils/excel_util.py index feab9dc9..c8bfb6e2 100644 --- a/backend/app/utils/excel_util.py +++ b/backend/app/utils/excel_util.py @@ -15,11 +15,14 @@ class ExcelUtil: @classmethod def __mapping_list(cls, list_data: List[Dict[str, Any]], mapping_dict: Dict) -> List: """ - 工具方法:将list数据中的字段名映射为对应的中文字段名 + 工具方法:将列表数据中的字段名映射为对应的中文字段名。 - :param list_data: 数据列表 - :param mapping_dict: 映射字典 - :return: 映射后的数据列表 + 参数: + - list_data (List[Dict[str, Any]]): 数据列表。 + - mapping_dict (Dict): 字段名映射字典。 + + 返回: + - List: 映射后的数据列表。 """ mapping_data = [{mapping_dict.get(key): item.get(key) for key in mapping_dict} for item in list_data] @@ -28,31 +31,32 @@ def __mapping_list(cls, list_data: List[Dict[str, Any]], mapping_dict: Dict) -> @classmethod def get_excel_template(cls, header_list: List[str], selector_header_list: List[str], option_list: List[Dict[str, List[str]]]) -> bytes: """ - 生成Excel模板文件 + 生成 Excel 模板文件。 - Args: - header_list: 表头列表 - selector_header_list: 需要设置下拉选择的表头列表 - option_list: 下拉选项配置列表 + 参数: + - header_list (List[str]): 表头列表。 + - selector_header_list (List[str]): 需要设置下拉选择的表头列表。 + - option_list (List[Dict[str, List[str]]]): 下拉选项配置列表。 - Returns: - bytes: Excel文件的二进制数据 + 返回: + - bytes: Excel 文件的二进制数据。 """ wb = Workbook() ws = wb.active if not ws: - raise ValueError("Worksheet is None") + raise ValueError("不存在活动工作表") # 设置表头样式 header_fill = PatternFill(start_color='ababab', end_color='ababab', fill_type='solid') - center_align = Alignment(horizontal='center') # 写入表头 for col_num, header in enumerate(header_list, 1): cell = ws.cell(row=1, column=col_num) cell.value = header cell.fill = header_fill - cell.alignment = center_align + # 设置水平居中对齐 + cell.alignment = Alignment(horizontal='center') + # 设置列宽度为16 ws.column_dimensions[get_column_letter(col_num)].width = 12 # 设置下拉选择 @@ -71,18 +75,21 @@ def get_excel_template(cls, header_list: List[str], selector_header_list: List[s buffer = io.BytesIO() wb.save(buffer) buffer.seek(0) - return buffer.getvalue() + # 读取字节数据 + excel_data = buffer.getvalue() + return excel_data @classmethod def export_list2excel(cls, list_data: List[Dict[str, Any]], mapping_dict: Dict) -> bytes: """ - 将列表数据导出为Excel + 将列表数据导出为 Excel 文件。 - Args: - list_data: 要导出的数据列表 + 参数: + - list_data (List[Dict[str, Any]]): 要导出的数据列表。 + - mapping_dict (Dict): 字段名映射字典。 - Returns: - bytes: Excel文件的二进制数据 + 返回: + - bytes: Excel 文件的二进制数据。 """ mapping_data = cls.__mapping_list(list_data, mapping_dict) df = pd.DataFrame(mapping_data) diff --git a/backend/app/utils/gen_util.py b/backend/app/utils/gen_util.py index 2553329d..d6e2ce33 100644 --- a/backend/app/utils/gen_util.py +++ b/backend/app/utils/gen_util.py @@ -6,16 +6,7 @@ from app.common.constant import GenConstant from app.config.setting import settings from app.utils.string_util import StringUtil -from app.api.v1.module_generator.gencode.schema import ( - GenTableOptionSchema, - GenDBTableSchema, - GenTableBaseSchema, - GenTableSchema, - GenTableOutSchema, - GenTableColumnSchema, - GenTableColumnOutSchema, - GenTableColumnDeleteSchema -) +from app.api.v1.module_generator.gencode.schema import GenTableSchema, GenTableColumnSchema class GenUtils: @@ -26,9 +17,11 @@ def init_table(cls, gen_table: GenTableSchema) -> None: """ 初始化表信息 - param gen_table: 业务表对象 - param oper_name: 操作人 - :return: + 参数: + - gen_table (GenTableSchema): 业务表对象。 + + 返回: + - None """ # 只有当字段为None时才设置默认值 if gen_table.class_name is None: @@ -44,15 +37,17 @@ def init_table(cls, gen_table: GenTableSchema) -> None: if gen_table.function_author is None: gen_table.function_author = settings.author - @classmethod def init_column_field(cls, column: GenTableColumnSchema, table: GenTableSchema) -> None: """ 初始化列属性字段 - param column: 业务表字段对象 - param table: 业务表对象 - :return: + 参数: + - column (GenTableColumnSchema): 业务表字段对象。 + - table (GenTableSchema): 业务表对象。 + + 返回: + - None """ data_type = cls.get_db_type(column.column_type or "") column_name = column.column_name or "" @@ -133,9 +128,12 @@ def arrays_contains(cls, arr: List[str], target_value: str) -> bool: """ 校验数组是否包含指定值 - param arr: 数组 - param target_value: 需要校验的值 - :return: 校验结果 + 参数: + - arr (List[str]): 数组。 + - target_value (str): 需要校验的值。 + + 返回: + - bool: 校验结果。 """ return target_value in arr @@ -144,14 +142,25 @@ def get_module_name(cls, package_name: str) -> str: """ 获取模块名 - param package_name: 包名 - :return: 模块名 + 参数: + - package_name (str): 包名。 + + 返回: + - str: 模块名。 """ return package_name.split('.')[-1] @classmethod def get_package_name(cls, table_name: str) -> str: - """获取包名""" + """ + 获取包名 + + 参数: + - table_name (str): 业务表名。 + + 返回: + - str: 包名。 + """ return settings.package_name # 可配置的包名 @classmethod @@ -159,18 +168,24 @@ def get_business_name(cls, table_name: str) -> str: """ 获取业务名 - param table_name: 业务表名 - :return: 业务名 + 参数: + - table_name (str): 业务表名。 + + 返回: + - str: 业务名。 """ return table_name.split('_')[-1] @classmethod def convert_class_name(cls, table_name: str) -> str: """ - 表名转换成Python类名 + 表名转换成 Python 类名 + + 参数: + - table_name (str): 业务表名。 - param table_name: 业务表名 - :return: Python类名 + 返回: + - str: Python 类名。 """ auto_remove_pre = settings.auto_remove_pre table_prefix = settings.table_prefix @@ -184,9 +199,12 @@ def replace_first(cls, replacement: str, search_list: List[str]) -> str: """ 批量替换前缀 - param replacement: 需要被替换的字符串 - param search_list: 可替换的字符串列表 - :return: 替换后的字符串 + 参数: + - replacement (str): 需要被替换的字符串。 + - search_list (List[str]): 可替换的字符串列表。 + + 返回: + - str: 替换后的字符串。 """ for search_string in search_list: if replacement.startswith(search_string): @@ -198,8 +216,11 @@ def replace_text(cls, text: str) -> str: """ 关键字替换 - param text: 需要被替换的字符串 - :return: 替换后的字符串 + 参数: + - text (str): 需要被替换的字符串。 + + 返回: + - str: 替换后的字符串。 """ return re.sub(r'(?:表|测试)', '', text) @@ -208,8 +229,11 @@ def get_db_type(cls, column_type: str) -> str: """ 获取数据库类型字段 - param column_type: 字段类型 - :return: 数据库类型 + 参数: + - column_type (str): 字段类型。 + + 返回: + - str: 数据库类型。 """ if '(' in column_type: return column_type.split('(')[0] @@ -220,8 +244,11 @@ def get_column_length(cls, column_type: str) -> int: """ 获取字段长度 - param column_type: 字段类型 - :return: 字段长度 + 参数: + - column_type (str): 字段类型。 + + 返回: + - int: 字段长度。 """ if '(' in column_type: length = len(column_type.split('(')[1].split(')')[0]) @@ -233,8 +260,11 @@ def split_column_type(cls, column_type: str) -> List[str]: """ 拆分列类型 - param column_type: 字段类型 - :return: 拆分结果 + 参数: + - column_type (str): 字段类型。 + + 返回: + - List[str]: 拆分结果。 """ if '(' in column_type and ')' in column_type: return column_type.split('(')[1].split(')')[0].split(',') @@ -245,8 +275,11 @@ def to_camel_case(cls, text: str) -> str: """ 将字符串转换为驼峰命名 - param text: 需要转换的字符串 - :return: 驼峰命名 + 参数: + - text (str): 需要转换的字符串。 + + 返回: + - str: 驼峰命名。 """ parts = text.split('_') return parts[0] + ''.join(word.capitalize() for word in parts[1:]) diff --git a/backend/app/utils/hash_bcrpy_util.py b/backend/app/utils/hash_bcrpy_util.py index 21f4eefc..02593209 100644 --- a/backend/app/utils/hash_bcrpy_util.py +++ b/backend/app/utils/hash_bcrpy_util.py @@ -31,12 +31,12 @@ def verify_password(cls, plain_password: str, password_hash: str) -> bool: """ 校验密码是否匹配 - Args: - plain_password: 明文密码 - password_hash: 加密后的密码哈希值 + 参数: + - plain_password (str): 明文密码。 + - password_hash (str): 加密后的密码哈希值。 - Returns: - bool: 密码是否匹配 + 返回: + - bool: 密码是否匹配。 """ return PwdContext.verify(plain_password, password_hash) @@ -45,11 +45,11 @@ def set_password_hash(cls, password: str) -> str: """ 对密码进行加密 - Args: - password: 明文密码 + 参数: + - password (str): 明文密码。 - Returns: - str: 加密后的密码哈希值 + 返回: + - str: 加密后的密码哈希值。 """ return PwdContext.hash(password) @@ -58,11 +58,11 @@ def check_password_strength(cls, password: str) -> Optional[str]: """ 检查密码强度 - Args: - password: 明文密码 + 参数: + - password (str): 明文密码。 - Returns: - str: 如果密码强度不够返回提示信息,否则返回None + 返回: + - Optional[str]: 如果密码强度不够返回提示信息,否则返回None。 """ if len(password) < 6: return "密码长度至少6位" @@ -80,19 +80,25 @@ class AESCipher: def __init__(self, key: bytes | str) -> None: """ - 初始化 AES 加密器 + 初始化 AES 加密器。 - :param key: 密钥,16/24/32 bytes 或 16 进制字符串 - :return: + 参数: + - key (bytes | str): 密钥,16/24/32 bytes 或 16 进制字符串。 + + 返回: + - None """ self.key = key if isinstance(key, bytes) else bytes.fromhex(key) def encrypt(self, plaintext: bytes | str) -> bytes: """ - AES 加密 + AES 加密。 + + 参数: + - plaintext (bytes | str): 加密前的明文。 - :param plaintext: 加密前的明文 - :return: + 返回: + - bytes: 加密后的密文(前16字节为随机IV)。 """ if not isinstance(plaintext, bytes): plaintext = str(plaintext).encode('utf-8') @@ -106,10 +112,13 @@ def encrypt(self, plaintext: bytes | str) -> bytes: def decrypt(self, ciphertext: bytes | str) -> str: """ - AES 解密 + AES 解密。 - :param ciphertext: 解密前的密文,bytes 或 16 进制字符串 - :return: + 参数: + - ciphertext (bytes | str): 解密前的密文,bytes 或 16 进制字符串。 + + 返回: + - str: 解密后的明文。 """ ciphertext = ciphertext if isinstance(ciphertext, bytes) else bytes.fromhex(ciphertext) iv = ciphertext[:16] @@ -128,10 +137,13 @@ class Md5Cipher: @staticmethod def encrypt(plaintext: bytes | str) -> str: """ - MD5 加密 + MD5 加密。 + + 参数: + - plaintext (bytes | str): 加密前的明文。 - :param plaintext: 加密前的明文 - :return: + 返回: + - str: MD5 十六进制摘要。 """ md5 = hashlib.md5() if not isinstance(plaintext, bytes): @@ -145,19 +157,28 @@ class ItsDCipher: def __init__(self, key: bytes | str) -> None: """ - 初始化 ItsDangerous 加密器 + 初始化 ItsDangerous 加密器。 - :param key: 密钥,16/24/32 bytes 或 16 进制字符串 - :return: + 参数: + - key (bytes | str): 密钥,16/24/32 bytes 或 16 进制字符串。 + + 返回: + - None """ self.key = key if isinstance(key, bytes) else bytes.fromhex(key) def encrypt(self, plaintext: Any) -> str: """ - ItsDangerous 加密 + ItsDangerous 加密。 + + 参数: + - plaintext (Any): 加密前的明文。 - :param plaintext: 加密前的明文 - :return: + 返回: + - str: 加密后的密文(URL安全)。 + + 异常: + - Exception: 加密失败时使用 MD5 作为降级,错误已记录。 """ serializer = URLSafeSerializer(self.key) try: @@ -169,10 +190,16 @@ def encrypt(self, plaintext: Any) -> str: def decrypt(self, ciphertext: str) -> Any: """ - ItsDangerous 解密 + ItsDangerous 解密。 + + 参数: + - ciphertext (str): 解密前的密文。 - :param ciphertext: 解密前的密文 - :return: + 返回: + - Any: 解密后的明文;失败时返回原密文。 + + 异常: + - Exception: 解密失败时记录错误并返回原密文。 """ serializer = URLSafeSerializer(self.key) try: diff --git a/backend/app/utils/ip_local_util.py b/backend/app/utils/ip_local_util.py index 21a180fe..5f33a87c 100644 --- a/backend/app/utils/ip_local_util.py +++ b/backend/app/utils/ip_local_util.py @@ -14,66 +14,41 @@ class IpLocalUtil: @classmethod def is_valid_ip(cls, ip: str) -> bool: """ - 校验IP格式是否合法 + 校验IP格式是否合法。 - :param ip: IP地址 - :return: 是否合法 + 参数: + - ip (str): IP地址。 + + 返回: + - bool: 是否合法。 """ ip_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' return bool(re.match(ip_pattern, ip)) - + @classmethod def is_private_ip(cls, ip: str) -> bool: """ - 校验IP是否为内网IP - - :param ip: IP地址 - :return: 是否为内网IP - """ - ip_parts = list(map(int, ip.split('.'))) - - # 检查是否为 10.0.0.0/8 - if ip_parts[0] == 10: - return True + 判断是否为内网IP。 - # 检查是否为 172.16.0.0/12 - if ip_parts[0] == 172 and 16 <= ip_parts[1] <= 31: - return True + 参数: + - ip (str): IP地址。 - # 检查是否为 192.168.0.0/16 - if ip_parts[0] == 192 and ip_parts[1] == 168: - return True - - # 检查是否为 127.0.0.0/8 - if ip_parts[0] == 127: - return True - - return False - - @classmethod - async def _make_api_request(cls, client, url): + 返回: + - bool: 是否为内网IP。 """ - 单独的 API 请求方法,包含重试机制 - """ - max_retries = 3 - for attempt in range(max_retries): - try: - response = await client.get(url, timeout=10) - if response.status_code == 200: - return response - except Exception as e: - if attempt < max_retries - 1: - continue - logger.error(f"请求 {url} 失败: {e}") - return None + priv_pattern = r'^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)' + return bool(re.match(priv_pattern, ip)) @classmethod async def get_ip_location(cls, ip: str) -> str | None: """ - 获取IP归属地信息 + 获取IP归属地信息。 + + 参数: + - ip (str): IP地址。 - :param ip: IP地址 - :return: IP归属地信息 + 返回: + - str | None: IP归属地信息,失败时返回"未知"或None。 """ # 校验IP格式 if not cls.is_valid_ip(ip): @@ -105,3 +80,27 @@ async def get_ip_location(cls, ip: str) -> str | None: except Exception as e: logger.error(f"获取IP归属地失败: {e}") return "未知" + + @classmethod + async def _make_api_request(cls, client, url): + """ + 单独的 API 请求方法,包含重试机制。 + + 参数: + - client (AsyncClient): httpx 异步客户端。 + - url (str): 请求 URL。 + + 返回: + - Response | None: 响应对象,失败时返回None。 + """ + max_retries = 3 + for attempt in range(max_retries): + try: + response = await client.get(url, timeout=10) + if response.status_code == 200: + return response + except Exception as e: + if attempt < max_retries - 1: + continue + logger.error(f"请求 {url} 失败: {e}") + return None diff --git a/backend/app/utils/jinja2_template_util.py b/backend/app/utils/jinja2_template_util.py index ab0bd849..59a3a8a1 100644 --- a/backend/app/utils/jinja2_template_util.py +++ b/backend/app/utils/jinja2_template_util.py @@ -8,7 +8,6 @@ from app.common.constant import GenConstant from app.config.setting import settings -from app.core.exceptions import CustomException from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableColumnOutSchema from app.utils.string_util import StringUtil @@ -21,19 +20,27 @@ class Jinja2TemplateInitializerUtil: @classmethod def init_jinja2(cls): """ - 初始化 Jinja2 模板引擎 - - :return: Jinja2 环境对象 + 初始化 Jinja2 模板引擎。 + + 参数: + - 无 + + 返回: + - Environment: Jinja2 环境对象。 + + 异常: + - RuntimeError: 初始化失败时抛出。 """ try: # 修复模板路径,使用正确的相对路径 env = Environment( loader=FileSystemLoader(settings.TEMPLATE_DIR), - autoescape=select_autoescape(['html', 'xml']), - keep_trailing_newline=True, - trim_blocks=True, - lstrip_blocks=True, + autoescape=select_autoescape(['html', 'xml', 'jinja']), # 自动转义HTML + trim_blocks=True, # 删除多余的空行 + lstrip_blocks=True, # 删除行首空格 + keep_trailing_newline=True, # 保留行尾换行符 + enable_async=True, # 开启异步支持 ) env.filters.update( { @@ -62,23 +69,48 @@ class Jinja2TemplateUtil: @classmethod def get_env(cls) -> Environment: - """获取模板环境对象""" + """ + 获取模板环境对象。 + + 参数: + - 无 + + 返回: + - Environment: Jinja2 环境对象。 + + 异常: + - RuntimeError: 初始化失败时抛出。 + """ if cls._env is None: cls._env = Jinja2TemplateInitializerUtil.init_jinja2() return cls._env @classmethod def get_template(cls, template_path: str) -> Template: - """获取模板""" + """ + 获取模板。 + + 参数: + - template_path (str): 模板路径。 + + 返回: + - Template: Jinja2 模板对象。 + + 异常: + - TemplateNotFound: 模板未找到时抛出。 + """ return cls.get_env().get_template(template_path) @classmethod def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]: """ - 准备模板变量 - - :param gen_table: 生成表的配置信息 - :return: 模板上下文字典 + 准备模板变量。 + + 参数: + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - Dict[str, Any]: 模板上下文字典。 """ # 处理options为None的情况 options = gen_table.options or '{}' @@ -113,7 +145,7 @@ def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]: 'columns': gen_table.columns or [], 'table': gen_table, 'dicts': cls.get_dicts(gen_table), - 'dbType': settings.DATABASE_TYPE, + 'db_type': settings.DATABASE_TYPE, 'column_not_add_show': GenConstant.COLUMNNAME_NOT_ADD_SHOW, 'column_not_edit_show': GenConstant.COLUMNNAME_NOT_EDIT_SHOW, 'primary_key': gen_table.pk_column.python_field if gen_table.pk_column else '' @@ -131,11 +163,14 @@ def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]: @classmethod def set_menu_context(cls, context: Dict, gen_table: GenTableOutSchema): """ - 设置菜单上下文 - - :param context: 模板上下文字典 - :param gen_table: 生成表的配置信息 - :return: 新的模板上下文字典 + 设置菜单上下文。 + + 参数: + - context (Dict): 模板上下文字典。 + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - Dict: 更新后的模板上下文字典。 """ # 处理options为None的情况 options = gen_table.options or '{}' @@ -148,11 +183,14 @@ def set_menu_context(cls, context: Dict, gen_table: GenTableOutSchema): @classmethod def set_tree_context(cls, context: Dict, gen_table: GenTableOutSchema): """ - 设置树形结构上下文 - - :param context: 模板上下文字典 - :param gen_table: 生成表的配置信息 - :return: 新的模板上下文字典 + 设置树形结构上下文。 + + 参数: + - context (Dict): 模板上下文字典。 + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - Dict: 更新后的模板上下文字典。 """ # 处理options为None的情况 options = gen_table.options or '{}' @@ -168,11 +206,14 @@ def set_tree_context(cls, context: Dict, gen_table: GenTableOutSchema): @classmethod def set_sub_context(cls, context: Dict, gen_table: GenTableOutSchema): """ - 设置子表上下文 - - :param context: 模板上下文字典 - :param gen_table: 生成表的配置信息 - :return: 新的模板上下文字典 + 设置子表上下文。 + + 参数: + - context (Dict): 模板上下文字典。 + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - Dict: 更新后的模板上下文字典。 """ sub_table = gen_table.sub_table sub_table_name = gen_table.sub_table_name or '' @@ -191,11 +232,14 @@ def set_sub_context(cls, context: Dict, gen_table: GenTableOutSchema): @classmethod def get_template_list(cls, tpl_category: str, tpl_web_type: str): """ - 获取模板列表 - - :param tpl_category: 生成模板类型 - :param tpl_web_type: 前端类型 - :return: 模板列表 + 获取模板列表。 + + 参数: + - tpl_category (str): 生成模板类型。 + - tpl_web_type (str): 前端类型。 + + 返回: + - List[str]: 模板路径列表。 """ use_web_type = 'vue' # 处理空值情况 @@ -222,17 +266,19 @@ def get_template_list(cls, tpl_category: str, tpl_web_type: str): templates.append(f'{use_web_type}/index-tree.vue.j2') elif category == GenConstant.TPL_SUB: templates.append(f'{use_web_type}/index.vue.j2') - # templates.append('python/sub-domain.python.jinja2') return templates @classmethod def get_file_name(cls, template: List[str], gen_table: GenTableOutSchema): """ - 根据模板生成文件名 + 根据模板生成文件名。 - :param template: 模板列表 - :param gen_table: 生成表的配置信息 - :return: 模板生成文件名 + 参数: + - template (List[str]): 模板列表。 + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - str: 模板生成的文件名。 """ package_name = gen_table.package_name or '' module_name = gen_table.module_name or '' @@ -242,40 +288,48 @@ def get_file_name(cls, template: List[str], gen_table: GenTableOutSchema): python_path = f'{cls.BACKEND_PROJECT_PATH}/{package_name.replace(".", "/")}' if package_name else cls.BACKEND_PROJECT_PATH if 'controller.py.j2' in template: - return f'{python_path}/controller/{business_name}_controller.py' + return f'{python_path}/app/api/v1/{module_name}/{business_name}/controller.py' elif 'crud.py.j2' in template: - return f'{python_path}/crud/{business_name}_crud.py' + return f'{python_path}/app/api/v1/{module_name}/{business_name}/crud.py' elif 'model.py.j2' in template: - return f'{python_path}/entity/model/{business_name}_model.py' + return f'{python_path}/app/api/v1/{module_name}/{business_name}/model.py' elif 'service.py.j2' in template: - return f'{python_path}/service/{business_name}_service.py' + return f'{python_path}/app/api/v1/{module_name}/{business_name}/service.py' + elif 'param.py.j2' in template: + return f'{python_path}/app/api/v1/{module_name}/{business_name}/param.py' elif 'schema.py.j2' in template: - return f'{python_path}/entity/schema/{business_name}_schema.py' + return f'{python_path}/app/api/v1/{module_name}/{business_name}/schema.py' elif 'sql.j2' in template: - return f'{cls.BACKEND_PROJECT_PATH}/sql/{business_name}_menu.sql' + return f'{cls.BACKEND_PROJECT_PATH}/sql/{module_name}/{business_name}_menu.sql' elif 'api.ts.j2' in template: - return f'{vue_path}/api/{module_name}/{business_name}.ts' + return f'{vue_path}/src/api/{module_name}/{business_name}.ts' elif 'index.vue.j2' in template or 'index-tree.vue.j2' in template: - return f'{vue_path}/views/{module_name}/{business_name}/index.vue' + return f'{vue_path}/src/views/{module_name}/{business_name}/index.vue' return '' @classmethod def get_package_prefix(cls, package_name: str) -> str: """ - 获取包前缀 + 获取包前缀。 - :param package_name: 包名 - :return: 包前缀 + 参数: + - package_name (str): 包名。 + + 返回: + - str: 包前缀。 """ return package_name[: package_name.rfind('.')] @classmethod def get_vo_import_list(cls, gen_table: GenTableOutSchema): """ - 获取vo模板导入包列表 + 获取 VO 模板导入包列表。 - :param gen_table: 生成表的配置信息 - :return: 导入包列表 + 参数: + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - List[str]: 导入包列表。 """ columns = gen_table.columns or [] import_list = set() @@ -301,10 +355,13 @@ def get_vo_import_list(cls, gen_table: GenTableOutSchema): @classmethod def get_do_import_list(cls, gen_table: GenTableOutSchema) -> List[str]: """ - 获取do模板导入包列表 + 获取 DO 模板导入包列表。 - :param gen_table: 生成表的配置信息 - :return: 导入包列表 + 参数: + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - List[str]: 导入包列表。 """ columns = gen_table.columns or [] import_list = set() @@ -334,10 +391,13 @@ def get_do_import_list(cls, gen_table: GenTableOutSchema) -> List[str]: @classmethod def get_db_type(cls, column_type: str) -> str: """ - 获取数据库类型字段 + 获取数据库字段类型。 - param column_type: 字段类型 - :return: 数据库类型 + 参数: + - column_type (str): 字段类型字符串。 + + 返回: + - str: 数据库类型(去除长度等修饰)。 """ if '(' in column_type: return column_type.split('(')[0] @@ -346,11 +406,14 @@ def get_db_type(cls, column_type: str) -> str: @classmethod def merge_same_imports(cls, imports: List[str], import_start: str) -> List[str]: """ - 合并相同的导入语句 + 合并相同的导入语句。 - :param imports: 导入语句列表 - :param import_start: 导入语句的起始字符串 - :return: 合并后的导入语句列表 + 参数: + - imports (List[str]): 导入语句列表。 + - import_start (str): 导入语句的起始字符串。 + + 返回: + - List[str]: 合并后的导入语句列表。 """ merged_imports = [] _imports = [] @@ -370,10 +433,13 @@ def merge_same_imports(cls, imports: List[str], import_start: str) -> List[str]: @classmethod def get_dicts(cls, gen_table: GenTableOutSchema): """ - 获取字典列表 + 获取字典列表。 - :param gen_table: 生成表的配置信息 - :return: 字典列表 + 参数: + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - str: 以逗号分隔的字典类型字符串。 """ columns = gen_table.columns or [] dicts = set() @@ -388,11 +454,14 @@ def get_dicts(cls, gen_table: GenTableOutSchema): @classmethod def add_dicts(cls, dicts: Set[str], columns: List[GenTableColumnOutSchema]): """ - 添加字典列表 + 添加字典类型到集合。 - :param dicts: 字典列表 - :param columns: 字段列表 - :return: 新的字典列表 + 参数: + - dicts (Set[str]): 字典类型集合。 + - columns (List[GenTableColumnOutSchema]): 字段列表。 + + 返回: + - Set[str]: 更新后的字典类型集合。 """ for column in columns: # 处理column.super_column, column.dict_type, column.html_type为None的情况 @@ -412,21 +481,27 @@ def add_dicts(cls, dicts: Set[str], columns: List[GenTableColumnOutSchema]): @classmethod def get_permission_prefix(cls, module_name: str | None, business_name: str | None) -> str: """ - 获取权限前缀 + 获取权限前缀。 - :param module_name: 模块名 - :param business_name: 业务名 - :return: 权限前缀 + 参数: + - module_name (str | None): 模块名。 + - business_name (str | None): 业务名。 + + 返回: + - str: 权限前缀字符串。 """ return f'{module_name}:{business_name}' @classmethod def get_parent_menu_id(cls, params_obj: Dict): """ - 获取上级菜单ID + 获取上级菜单ID。 - :param params_obj: 菜单参数字典 - :return: 上级菜单ID + 参数: + - params_obj (Dict): 菜单参数字典。 + + 返回: + - str: 上级菜单ID。 """ if params_obj and params_obj.get(GenConstant.PARENT_MENU_ID): return params_obj.get(GenConstant.PARENT_MENU_ID) @@ -435,10 +510,13 @@ def get_parent_menu_id(cls, params_obj: Dict): @classmethod def get_tree_code(cls, params_obj: Dict): """ - 获取树编码 + 获取树编码。 - :param params_obj: 菜单参数字典 - :return: 树编码 + 参数: + - params_obj (Dict): 菜单参数字典。 + + 返回: + - str: 树编码(驼峰格式)。 """ if GenConstant.TREE_CODE in params_obj: tree_code = params_obj.get(GenConstant.TREE_CODE) @@ -450,10 +528,13 @@ def get_tree_code(cls, params_obj: Dict): @classmethod def get_tree_parent_code(cls, params_obj: Dict): """ - 获取树父编码 + 获取树父编码。 - :param params_obj: 菜单参数字典 - :return: 树父编码 + 参数: + - params_obj (Dict): 菜单参数字典。 + + 返回: + - str: 树父编码(驼峰格式)。 """ if GenConstant.TREE_PARENT_CODE in params_obj: tree_parent_code = params_obj.get(GenConstant.TREE_PARENT_CODE) @@ -465,10 +546,13 @@ def get_tree_parent_code(cls, params_obj: Dict): @classmethod def get_tree_name(cls, params_obj: Dict): """ - 获取树名称 + 获取树名称。 - :param params_obj: 菜单参数字典 - :return: 树名称 + 参数: + - params_obj (Dict): 菜单参数字典。 + + 返回: + - str: 树名称(驼峰格式)。 """ if GenConstant.TREE_NAME in params_obj: tree_name = params_obj.get(GenConstant.TREE_NAME) @@ -480,10 +564,13 @@ def get_tree_name(cls, params_obj: Dict): @classmethod def get_expand_column(cls, gen_table: GenTableOutSchema): """ - 获取展开列 + 获取展开列位置序号。 - :param gen_table: 生成表的配置信息 - :return: 展开列 + 参数: + - gen_table (GenTableOutSchema): 生成表的配置信息。 + + 返回: + - int: 展开列在列表中的序号(从 1 开始)。 """ # 处理options为None的情况 options = gen_table.options or '{}' @@ -505,10 +592,13 @@ def get_expand_column(cls, gen_table: GenTableOutSchema): @classmethod def to_camel_case(cls, text: str) -> str: """ - 将字符串转换为驼峰命名 + 将字符串转换为驼峰命名。 - :param text: 待转换的字符串 - :return: 转换后的驼峰命名字符串 + 参数: + - text (str): 待转换的字符串。 + + 返回: + - str: 转换后的驼峰命名字符串。 """ parts = text.split('_') return parts[0] + ''.join(word.capitalize() for word in parts[1:]) @@ -516,10 +606,13 @@ def to_camel_case(cls, text: str) -> str: @classmethod def get_sqlalchemy_type(cls, column_type): """ - 获取SQLAlchemy类型 + 获取 SQLAlchemy 类型。 - :param column_type: 列类型 - :return: SQLAlchemy类型 + 参数: + - column_type (Any): 列类型或包含 `column_type` 属性的对象。 + + 返回: + - str: SQLAlchemy 类型字符串。 """ # 适配可能传入的是对象而非字符串的情况 if hasattr(column_type, 'column_type'): diff --git a/backend/app/utils/re_util.py b/backend/app/utils/re_util.py index cdc6525a..3d13a556 100644 --- a/backend/app/utils/re_util.py +++ b/backend/app/utils/re_util.py @@ -1,15 +1,17 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- -import re +import re def search_string(pattern: str, text: str) -> re.Match[str] | None: """ 全字段正则匹配 - :param pattern: 正则表达式模式 - :param text: 待匹配的文本 - :return: + 参数: + - pattern (str): 正则表达式模式。 + - text (str): 待匹配的文本。 + + 返回: + - re.Match[str] | None: 匹配结果。 """ if not pattern or not text: return None @@ -22,9 +24,12 @@ def match_string(pattern: str, text: str) -> re.Match[str] | None: """ 从字段开头正则匹配 - :param pattern: 正则表达式模式 - :param text: 待匹配的文本 - :return: + 参数: + - pattern (str): 正则表达式模式。 + - text (str): 待匹配的文本。 + + 返回: + - re.Match[str] | None: 匹配结果。 """ if not pattern or not text: return None @@ -37,8 +42,11 @@ def is_phone(number: str) -> re.Match[str] | None: """ 检查手机号码格式 - :param number: 待检查的手机号码 - :return: + 参数: + - number (str): 待检查的手机号码。 + + 返回: + - re.Match[str] | None: 匹配结果。 """ if not number: return None @@ -51,8 +59,11 @@ def is_git_url(url: str) -> re.Match[str] | None: """ 检查 git URL 格式 - :param url: 待检查的 URL - :return: + 参数: + - url (str): 待检查的 URL。 + + 返回: + - re.Match[str] | None: 匹配结果。 """ if not url: return None diff --git a/backend/app/utils/string_util.py b/backend/app/utils/string_util.py index bd37db0d..e2b8d510 100644 --- a/backend/app/utils/string_util.py +++ b/backend/app/utils/string_util.py @@ -14,8 +14,11 @@ def is_blank(cls, string: str) -> bool: """ 校验字符串是否为''或全空格 - :param string: 需要校验的字符串 - :return: 校验结果 + 参数: + - string (str): 需要校验的字符串。 + + 返回: + - bool: 校验结果。 """ if string is None: return False @@ -33,8 +36,11 @@ def is_empty(cls, string) -> bool: """ 校验字符串是否为''或None - :param string: 需要校验的字符串 - :return: 校验结果 + 参数: + - string (str | None): 需要校验的字符串。 + + 返回: + - bool: 校验结果。 """ return string is None or len(string) == 0 @@ -43,18 +49,24 @@ def is_not_empty(cls, string: str) -> bool: """ 校验字符串是否不是''和None - :param string: 需要校验的字符串 - :return: 校验结果 + 参数: + - string (str): 需要校验的字符串。 + + 返回: + - bool: 校验结果。 """ return not cls.is_empty(string) @classmethod def is_http(cls, link: str): """ - 判断是否为http(s)://开头 + 判断是否为 http(s):// 开头 - :param link: 链接 - :return: 是否为http(s)://开头 + 参数: + - link (str): 链接。 + + 返回: + - bool: 是否为 http(s):// 开头。 """ return link.startswith(CommonConstant.HTTP) or link.startswith(CommonConstant.HTTPS) @@ -63,9 +75,12 @@ def contains_ignore_case(cls, search_str: str, compare_str: str): """ 查找指定字符串是否包含指定字符串同时忽略大小写 - :param search_str: 查找的字符串 - :param compare_str: 比对的字符串 - :return: 查找结果 + 参数: + - search_str (str): 查找的字符串。 + - compare_str (str): 比对的字符串。 + + 返回: + - bool: 查找结果。 """ if compare_str and search_str: return compare_str.lower() in search_str.lower() @@ -74,11 +89,14 @@ def contains_ignore_case(cls, search_str: str, compare_str: str): @classmethod def contains_any_ignore_case(cls, search_str: str, compare_str_list: List[str]): """ - 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时忽略大小写 + 查找指定字符串是否包含列表中的任意一个字符串(忽略大小写) + + 参数: + - search_str (str): 查找的字符串。 + - compare_str_list (List[str]): 比对的字符串列表。 - :param search_str: 查找的字符串 - :param compare_str_list: 比对的字符串列表 - :return: 查找结果 + 返回: + - bool: 查找结果。 """ if search_str and compare_str_list: return any([cls.contains_ignore_case(search_str, compare_str) for compare_str in compare_str_list]) @@ -87,11 +105,14 @@ def contains_any_ignore_case(cls, search_str: str, compare_str_list: List[str]): @classmethod def equals_ignore_case(cls, search_str: str, compare_str: str): """ - 比较两个字符串是否相等同时忽略大小写 + 比较两个字符串是否相等(忽略大小写) - :param search_str: 查找的字符串 - :param compare_str: 比对的字符串 - :return: 比较结果 + 参数: + - search_str (str): 查找的字符串。 + - compare_str (str): 比对的字符串。 + + 返回: + - bool: 比较结果。 """ if search_str and compare_str: return search_str.lower() == compare_str.lower() @@ -100,11 +121,14 @@ def equals_ignore_case(cls, search_str: str, compare_str: str): @classmethod def equals_any_ignore_case(cls, search_str: str, compare_str_list: List[str]): """ - 比较指定字符串是否与指定字符串列表中的任意一个字符串相等同时忽略大小写 + 判断指定字符串是否与列表中任意一个字符串相等(忽略大小写) + + 参数: + - search_str (str): 查找的字符串。 + - compare_str_list (List[str]): 比对的字符串列表。 - :param search_str: 查找的字符串 - :param compare_str_list: 比对的字符串列表 - :return: 比较结果 + 返回: + - bool: 比较结果。 """ if search_str and compare_str_list: return any([cls.equals_ignore_case(search_str, compare_str) for compare_str in compare_str_list]) @@ -115,9 +139,12 @@ def startswith_case(cls, search_str: str, compare_str: str): """ 查找指定字符串是否以指定字符串开头 - :param search_str: 查找的字符串 - :param compare_str: 比对的字符串 - :return: 查找结果 + 参数: + - search_str (str): 查找的字符串。 + - compare_str (str): 比对的字符串。 + + 返回: + - bool: 查找结果。 """ if compare_str and search_str: return search_str.startswith(compare_str) @@ -126,11 +153,14 @@ def startswith_case(cls, search_str: str, compare_str: str): @classmethod def startswith_any_case(cls, search_str: str, compare_str_list: List[str]): """ - 查找指定字符串是否以指定字符串列表中的任意一个字符串开头 + 查找指定字符串是否以列表中任意一个字符串开头 - :param search_str: 查找的字符串 - :param compare_str_list: 比对的字符串列表 - :return: 查找结果 + 参数: + - search_str (str): 查找的字符串。 + - compare_str_list (List[str]): 比对的字符串列表。 + + 返回: + - bool: 查找结果。 """ if search_str and compare_str_list: return any([cls.startswith_case(search_str, compare_str) for compare_str in compare_str_list]) @@ -139,10 +169,13 @@ def startswith_any_case(cls, search_str: str, compare_str_list: List[str]): @classmethod def convert_to_camel_case(cls, name: str) -> str: """ - 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串 + 将下划线大写方式命名的字符串转换为驼峰式;若输入为空则返回空字符串。 + + 参数: + - name (str): 下划线大写方式命名的字符串。 - :param name: 转换前的下划线大写方式命名的字符串 - :return: 转换后的驼峰式命名的字符串 + 返回: + - str: 转换后的驼峰式命名的字符串。 """ if not name: return '' @@ -161,9 +194,12 @@ def get_mapping_value_by_key_ignore_case(cls, mapping: Dict[str, str], key: str) """ 根据忽略大小写的键获取字典中的对应的值 - param mapping: 字典 - param key: 字典的键 - :return: 字典键对应的值 + 参数: + - mapping (Dict[str, str]): 字典。 + - key (str): 字典的键。 + + 返回: + - str: 字典键对应的值,未匹配则返回空字符串。 """ for k, v in mapping.items(): if key.lower() == k.lower(): diff --git a/backend/app/utils/time_util.py b/backend/app/utils/time_util.py index 91309734..9d19a741 100644 --- a/backend/app/utils/time_util.py +++ b/backend/app/utils/time_util.py @@ -14,10 +14,13 @@ class TimeUtil: @classmethod def object_format_datetime(cls, obj: Any) -> Any: """ - 格式化对象中的datetime类型属性 + 格式化对象中的 datetime 属性为默认字符串格式。 - :param obj: 输入对象 - :return: 格式化后的对象 + 参数: + - obj (Any): 输入对象。 + + 返回: + - Any: 格式化后的对象。 """ for attr in dir(obj): if not attr.startswith('_'): # 跳过私有属性 @@ -29,20 +32,26 @@ def object_format_datetime(cls, obj: Any) -> Any: @classmethod def list_format_datetime(cls, lst: List[Any]) -> List[Any]: """ - 格式化对象列表中所有对象的datetime类型属性 + 格式化列表内每个对象的 datetime 属性。 + + 参数: + - lst (List[Any]): 对象列表。 - :param lst: 对象列表 - :return: 格式化后的对象列表 + 返回: + - List[Any]: 格式化后的对象列表。 """ return [cls.object_format_datetime(obj) for obj in lst] @classmethod def format_datetime_dict_list(cls, dicts: List[Dict]) -> List[Dict]: """ - 递归格式化字典列表中的datetime值 + 递归格式化字典列表中的 datetime 值为默认字符串格式。 + + 参数: + - dicts (List[Dict]): 字典列表。 - :param dicts: 字典列表 - :return: 格式化后的字典列表 + 返回: + - List[Dict]: 格式化后的字典列表。 """ def _format_value(value: Any) -> Any: if isinstance(value, dict): @@ -56,7 +65,18 @@ def _format_value(value: Any) -> Any: return [_format_value(item) for item in dicts] @classmethod - def __valid_range(cls, search_str: str, start_range: int, end_range: int): + def __valid_range(cls, search_str: str, start_range: int, end_range: int) -> bool: + """ + 校验范围字符串是否合法。 + + 参数: + - search_str (str): 范围字符串(例如:"1-5")。 + - start_range (int): 允许的最小范围值。 + - end_range (int): 允许的最大范围值。 + + 返回: + - bool: 校验是否通过。 + """ match = re.match(r'^(\d+)-(\d+)$', search_str) if match: start, end = int(match.group(1)), int(match.group(2)) @@ -64,9 +84,21 @@ def __valid_range(cls, search_str: str, start_range: int, end_range: int): return False @classmethod - def __valid_sum( - cls, search_str: str, start_range_a: int, start_range_b: int, end_range_a: int, end_range_b: int, sum_range: int - ): + def __valid_sum(cls, search_str: str, start_range_a: int, start_range_b: int, end_range_a: int, end_range_b: int, sum_range: int) -> bool: + """ + 校验和字符串是否合法。 + + 参数: + - search_str (str): 和字符串(例如:"1/5")。 + - start_range_a (int): 允许的最小范围值A。 + - start_range_b (int): 允许的最大范围值A。 + - end_range_a (int): 允许的最小范围值B。 + - end_range_b (int): 允许的最大范围值B。 + - sum_range (int): 允许的最大和值。 + + 返回: + - bool: 校验是否通过。 + """ match = re.match(r'^(\d+)/(\d+)$', search_str) if match: start, end = int(match.group(1)), int(match.group(2)) @@ -80,10 +112,13 @@ def __valid_sum( @classmethod def validate_second_or_minute(cls, second_or_minute: str): """ - 校验秒或分钟值是否正确 + 校验秒或分钟字段的合法性。 + + 参数: + - second_or_minute (str): 秒或分钟值。 - :param second_or_minute: 秒或分钟值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( second_or_minute == '*' @@ -97,10 +132,13 @@ def validate_second_or_minute(cls, second_or_minute: str): @classmethod def validate_hour(cls, hour: str): """ - 校验小时值是否正确 + 校验小时字段的合法性。 - :param hour: 小时值 - :return: 校验结果 + 参数: + - hour (str): 小时值。 + + 返回: + - bool: 校验是否通过。 """ if ( hour == '*' @@ -114,10 +152,13 @@ def validate_hour(cls, hour: str): @classmethod def validate_day(cls, day: str): """ - 校验日值是否正确 + 校验日期字段的合法性。 + + 参数: + - day (str): 日值。 - :param day: 日值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( day in ['*', '?', 'L'] @@ -132,10 +173,13 @@ def validate_day(cls, day: str): @classmethod def validate_month(cls, month: str): """ - 校验月值是否正确 + 校验月份字段的合法性。 - :param month: 月值 - :return: 校验结果 + 参数: + - month (str): 月值。 + + 返回: + - bool: 校验是否通过。 """ if ( month == '*' @@ -149,10 +193,13 @@ def validate_month(cls, month: str): @classmethod def validate_week(cls, week: str): """ - 校验周值是否正确 + 校验星期字段的合法性。 + + 参数: + - week (str): 周值。 - :param week: 周值 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ if ( week in ['*', '?'] @@ -167,10 +214,13 @@ def validate_week(cls, week: str): @classmethod def validate_year(cls, year: str): """ - 校验年值是否正确 + 校验年份字段的合法性。 - :param year: 年值 - :return: 校验结果 + 参数: + - year (str): 年值。 + + 返回: + - bool: 校验是否通过。 """ current_year = int(datetime.now().year) future_years = [current_year + i for i in range(9)] @@ -191,7 +241,7 @@ def validate_year(cls, year: str): @classmethod def validate_cron_expression(cls, cron_expression: str): """ - 校验Cron表达式是否正确 + 校验 Cron 表达式是否正确。 * * * * * * | | | | | | | | | | | +--- 星期(0-7,0和7都表示星期日) @@ -199,10 +249,13 @@ def validate_cron_expression(cls, cron_expression: str): | | | +------- 日期(1-31) | | +--------- 小时(0-23) | +----------- 分钟(0-59) - +------------- 秒(0-59),**部分环境不支持秒字段** + +------------- 秒(0-59),部分环境不支持秒字段。 + + 参数: + - cron_expression (str): Cron 表达式。 - :param cron_expression: Cron表达式 - :return: 校验结果 + 返回: + - bool: 校验是否通过。 """ values = cron_expression.split() if len(values) != 6 and len(values) != 7: diff --git a/backend/app/utils/upload_util.py b/backend/app/utils/upload_util.py index ac4ae7d7..494de12f 100644 --- a/backend/app/utils/upload_util.py +++ b/backend/app/utils/upload_util.py @@ -3,14 +3,11 @@ import random import mimetypes from datetime import datetime -import time from typing import List, Dict, Tuple import aiofiles from fastapi import UploadFile from pathlib import Path -from urllib.parse import urljoin # 添加URL拼接工具导入 -from sqlalchemy.orm.writeonly import strategies -import oss2 +from urllib.parse import urljoin from app.config.setting import settings from app.core.exceptions import CustomException @@ -23,17 +20,41 @@ class UploadUtil: @staticmethod def generate_random_number() -> str: - """生成3位数字构成的字符串""" + """ + 生成3位随机数字字符串。 + + 返回: + - str: 三位随机数字字符串。 + """ return f'{random.randint(1, 999):03}' @staticmethod def check_file_exists(filepath: str) -> bool: - """检查文件是否存在""" + """ + 检查文件是否存在。 + + 参数: + - filepath (str): 文件路径。 + + 返回: + - bool: 文件是否存在。 + """ return Path(filepath).exists() @staticmethod def check_file_extension(file: UploadFile) -> bool: - """检查文件后缀是否合法""" + """ + 检查文件后缀是否合法。 + + 参数: + - file (UploadFile): 上传的文件对象。 + + 返回: + - bool: 文件后缀是否合法。 + + 异常: + - CustomException: 文件类型不支持时抛出。 + """ if file.content_type: file_extension = mimetypes.guess_extension(file.content_type) if file_extension and file_extension in settings.ALLOWED_EXTENSIONS: @@ -45,7 +66,15 @@ def check_file_extension(file: UploadFile) -> bool: @staticmethod def check_file_timestamp(filename: str) -> bool: - """校验文件时间戳是否合法""" + """ + 校验文件时间戳是否合法。 + + 参数: + - filename (str): 文件名(包含时间戳片段)。 + + 返回: + - bool: 时间戳是否合法。 + """ try: name_parts = filename.rsplit('.', 1)[0].split('_') timestamp = name_parts[-1].split(settings.UPLOAD_MACHINE)[0] @@ -56,7 +85,15 @@ def check_file_timestamp(filename: str) -> bool: @staticmethod def check_file_machine(filename: str) -> bool: - """校验文件机器码是否合法""" + """ + 校验文件机器码是否合法。 + + 参数: + - filename (str): 文件名。 + + 返回: + - bool: 机器码是否合法。 + """ try: name_without_ext = filename.rsplit('.', 1)[0] return len(name_without_ext) >= 4 and name_without_ext[-4] == settings.UPLOAD_MACHINE @@ -65,7 +102,15 @@ def check_file_machine(filename: str) -> bool: @staticmethod def check_file_random_code(filename: str) -> bool: - """校验文件随机码是否合法""" + """ + 校验文件随机码是否合法。 + + 参数: + - filename (str): 文件名。 + + 返回: + - bool: 随机码是否合法(000–999)。 + """ try: code = filename.rsplit('.', 1)[0][-3:] return code.isdigit() and 1 <= int(code) <= 999 @@ -74,7 +119,15 @@ def check_file_random_code(filename: str) -> bool: @staticmethod def check_file_size(file: UploadFile) -> bool: - """校验文件大小是否合法""" + """ + 校验文件大小是否合法。 + + 参数: + - file (UploadFile): 上传的文件对象。 + + 返回: + - bool: 文件大小是否合法(未提供 size 返回 False)。 + """ if file.size: return file.size <= settings.MAX_FILE_SIZE else: @@ -82,14 +135,31 @@ def check_file_size(file: UploadFile) -> bool: @classmethod def generate_file_name(cls, filename: str) -> str: - """生成文件名称""" + """ + 生成文件名称。 + + 参数: + - filename (str): 原始文件名(包含拓展名)。 + + 返回: + - str: 生成的文件名(包含时间戳、机器码、随机码)。 + """ name, ext = filename.rsplit(".", 1) timestamp = datetime.now().strftime("%Y%m%d%H%M%S") return f'{name}_{timestamp}{settings.UPLOAD_MACHINE}{cls.generate_random_number()}.{ext}' @staticmethod def generate_file(filepath: Path, chunk_size: int = 8192): - """根据文件生成二进制数据""" + """ + 根据文件生成二进制数据迭代器。 + + 参数: + - filepath (Path): 文件路径。 + - chunk_size (int): 分块大小,默认 8192 字节。 + + 返回: + - Iterator[bytes]: 文件二进制数据分块迭代器。 + """ with filepath.open('rb') as f: while chunk := f.read(chunk_size): yield chunk @@ -97,8 +167,13 @@ def generate_file(filepath: Path, chunk_size: int = 8192): @staticmethod def delete_file(filepath: Path) -> bool: """ - 删除文件 - :return: 删除是否成功 + 删除文件。 + + 参数: + - filepath (Path): 文件路径。 + + 返回: + - bool: 删除是否成功。 """ try: filepath.unlink(missing_ok=True) @@ -109,11 +184,17 @@ def delete_file(filepath: Path) -> bool: @classmethod async def upload_file(cls, file: UploadFile, base_url: str) -> Tuple[str, Path, str]: """ - 文件上传 - :param file: 上传的文件对象 - :param base_url: 基础URL - :raises CustomException: 当文件类型不支持或大小超限时抛出 - :return: (文件名, 文件路径, 文件URL)的元组 + 文件上传。 + + 参数: + - file (UploadFile): 上传的文件对象。 + - base_url (str): 基础 URL。 + + 返回: + - Tuple[str, Path, str]: (文件名, 文件路径, 文件 URL)。 + + 异常: + - CustomException: 当文件类型不支持或大小超限时抛出。 """ # 文件校验 if not all([cls.check_file_extension(file), cls.check_file_size(file)]): @@ -148,20 +229,27 @@ async def upload_file(cls, file: UploadFile, base_url: str) -> Tuple[str, Path, @staticmethod def get_file_tree(file_path: str) -> List[Dict]: """ - 获取文件树结构 - :param file_path: 文件路径 - :return: 文件树列表 + 获取文件树结构。 + + 参数: + - file_path (str): 文件路径。 + + 返回: + - List[Dict]: 文件树列表。 """ return [{"name": item.name, "is_dir": item.is_dir()} for item in Path(file_path).iterdir()] @classmethod async def download_file(cls, file_path: str) -> str: """ - 下载文件,生成新的文件名 - :param file_path: 文件路径 - :return: 文件下载信息 + 下载文件,生成新的文件名。 + + 参数: + - file_path (str): 文件路径。 + + 返回: + - str: 文件下载信息。 """ - # 解析文件路径 filename = cls.generate_file(Path(file_path)) return str(filename) \ No newline at end of file diff --git a/backend/templates/python/controller.py.j2 b/backend/templates/python/controller.py.j2 index 359443cd..8a6f47f1 100644 --- a/backend/templates/python/controller.py.j2 +++ b/backend/templates/python/controller.py.j2 @@ -1,113 +1,125 @@ # -*- coding:utf-8 -*- -from fastapi import APIRouter, Depends, UploadFile +from fastapi import APIRouter, Depends, UploadFile, Body, Path from fastapi.responses import StreamingResponse, JSONResponse from app.common.response import SuccessResponse, StreamResponse from app.core.dependencies import AuthPermission from app.core.router_class import OperationLogRoute from app.api.v1.module_system.auth.schema import AuthSchema -from app.api.v1.module_system.user.schema import UserOutSchema from app.common.request import PaginationService from app.core.base_params import PaginationQueryParam from app.utils.common_util import bytes2file_response +from app.core.logger import logger +from app.core.base_schema import BatchSetAvailable -from {{ package_name }}.service.{{ table_name }}_service import {{ table_name|snake_to_pascal_case }}Service -from {{ package_name }}.schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema -from {{ package_name }}.param import {{ table_name|snake_to_pascal_case }}QueryParam +from .service import {{ table_name|snake_to_pascal_case }}Service +from .schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema +from .param import {{ table_name|snake_to_pascal_case }}QueryParam -{{ table_name }}Controller = APIRouter(route_class=OperationLogRoute, prefix='/{{ module_name }}/{{ business_name }}', tags=["{{ function_name }}模块"]) - - -@{{ table_name }}Controller.get('/list', summary="查询{{ function_name }}列表", description="查询{{ function_name }}列表") -async def get_{{ table_name }}_list( - page: PaginationQueryParam = Depends(), - search: {{ table_name|snake_to_pascal_case }}QueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:list"])) -): - {{ table_name }}_result_list = await {{ table_name|snake_to_pascal_case }}Service.get_{{ table_name }}_list_service(auth, search, page.order_by) - {{ table_name }}_result = await PaginationService.paginate( - data_list={{ table_name }}_result_list, - page_no=page.page_no, - page_size=page.page_size - ) - return SuccessResponse(data={{ table_name }}_result) - - -@{{ table_name }}Controller.get('/{id}', summary="获取{{ function_name }}详细信息", description="获取{{ function_name }}详细信息") -async def get_{{ table_name }}_by_id( - id: int, - auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:query"])) -): - {{ table_name }} = await {{ table_name|snake_to_pascal_case }}Service.get_{{ table_name }}_by_id_service(auth, id) - return SuccessResponse(data={{ table_name }}) - - -@{{ table_name }}Controller.post('', summary="新增{{ function_name }}", description="新增{{ function_name }}") -async def add_{{ table_name }} ( - add_model: {{ table_name|snake_to_pascal_case }}CreateSchema, - auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:add"])), - current_user: UserOutSchema = Depends(lambda auth: auth.user) -): - add_result = await {{ table_name|snake_to_pascal_case }}Service.add_{{ table_name }}_service(auth, add_model) - return SuccessResponse(msg="新增成功") - - -@{{ table_name }}Controller.put('/{id}', summary="修改{{ function_name }}", description="修改{{ function_name }}") -async def update_{{ table_name }}( - id: int, - edit_model: {{ table_name|snake_to_pascal_case }}UpdateSchema, - auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:edit"])), - current_user: UserOutSchema = Depends(lambda auth: auth.user) -): - update_result = await {{ table_name|snake_to_pascal_case }}Service.update_{{ table_name }}_service(auth, id, edit_model) - return SuccessResponse(msg="修改成功") - - -@{{ table_name }}Controller.delete('/{ids}', summary="删除{{ function_name }}", description="删除{{ function_name }}") -async def del_{{ table_name }}( - ids: str, - auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:remove"])) -): - id_list = [int(id) for id in ids.split(',')] - del_result = await {{ table_name|snake_to_pascal_case }}Service.del_{{ table_name }}_service(auth, id_list) - return SuccessResponse(msg="删除成功") +{{ table_name|snake_to_pascal_case }}Router = APIRouter(route_class=OperationLogRoute, prefix='/{{ module_name }}/{{ business_name }}', tags=["{{ function_name }}模块"]) +@{{ table_name|snake_to_pascal_case }}Router.get("/detail/{id}", summary="获取{{ function_name }}详情", description="获取{{ function_name }}详情") +async def get_obj_detail_controller( + id: int = Path(..., description="ID"), + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:query"])) +) -> JSONResponse: + """获取{{ function_name }}详情接口""" + result_dict = await {{ table_name|snake_to_pascal_case }}Service.detail_service(auth=auth, id=id) + logger.info(f"获取{{ function_name }}详情成功 {id}") + return SuccessResponse(data=result_dict, msg="获取{{ function_name }}详情成功") + +@{{ table_name|snake_to_pascal_case }}Router.get("/list", summary="查询{{ function_name }}列表", description="查询{{ function_name }}列表") +async def get_obj_list_controller( + page: PaginationQueryParam = Depends(), + search: {{ table_name|snake_to_pascal_case }}QueryParam = Depends(), + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:query"])) +) -> JSONResponse: + """查询{{ function_name }}列表接口""" + result_dict_list = await {{ table_name|snake_to_pascal_case }}Service.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) + logger.info("查询{{ function_name }}列表成功") + return SuccessResponse(data=result_dict, msg="查询{{ function_name }}列表成功") + +@{{ table_name|snake_to_pascal_case }}Router.post("/create", summary="创建{{ function_name }}", description="创建{{ function_name }}") +async def create_obj_controller( + data: {{ table_name|snake_to_pascal_case }}CreateSchema, + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:create"])) +) -> JSONResponse: + """创建{{ function_name }}接口""" + result_dict = await {{ table_name|snake_to_pascal_case }}Service.create_service(auth=auth, data=data) + logger.info("创建{{ function_name }}成功") + return SuccessResponse(data=result_dict, msg="创建{{ function_name }}成功") + +@{{ table_name|snake_to_pascal_case }}Router.put("/update/{id}", summary="修改{{ function_name }}", description="修改{{ function_name }}") +async def update_obj_controller( + data: {{ table_name|snake_to_pascal_case }}UpdateSchema, + id: int = Path(..., description="ID"), + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:update"])) +) -> JSONResponse: + """修改{{ function_name }}接口""" + result_dict = await {{ table_name|snake_to_pascal_case }}Service.update_service(auth=auth, id=id, data=data) + logger.info("修改{{ function_name }}成功") + return SuccessResponse(data=result_dict, msg="修改{{ function_name }}成功") + +@{{ table_name|snake_to_pascal_case }}Router.delete("/delete", summary="删除{{ function_name }}", description="删除{{ function_name }}") +async def delete_obj_controller( + ids: list[int] = Body(..., description="ID列表"), + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:delete"])) +) -> JSONResponse: + """删除{{ function_name }}接口""" + await {{ table_name|snake_to_pascal_case }}Service.delete_service(auth=auth, ids=ids) + logger.info(f"删除{{ function_name }}成功: {ids}") + return SuccessResponse(msg="删除{{ function_name }}成功") + +@{{ table_name|snake_to_pascal_case }}Router.patch("/available/setting", summary="批量修改{{ function_name }}状态", description="批量修改{{ function_name }}状态") +async def batch_set_available_obj_controller( + data: BatchSetAvailable, + auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:patch"])) +) -> JSONResponse: + """批量修改{{ function_name }}状态接口""" + await {{ table_name|snake_to_pascal_case }}Service.set_available_service(auth=auth, data=data) + logger.info(f"批量修改{{ function_name }}状态成功: {data.ids}") + return SuccessResponse(msg="批量修改{{ function_name }}状态成功") -@{{ table_name }}Controller.post('/export', summary="导出{{ function_name }}", description="导出{{ function_name }}") -async def export_{{ table_name }}( +@{{ table_name|snake_to_pascal_case }}Router.post('/export', summary="导出{{ function_name }}", description="导出{{ function_name }}") +async def export_obj_list_controller( search: {{ table_name|snake_to_pascal_case }}QueryParam = Depends(), auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:export"])) ) -> StreamingResponse: - # 获取全量数据 - {{ table_name }}_list = await {{ table_name|snake_to_pascal_case }}Service.get_{{ table_name }}_list_service(auth=auth, search=search) - export_result = await {{ table_name|snake_to_pascal_case }}Service.export_{{ table_name }}_list_service({{ table_name }}_list) + """导出{{ function_name }}接口""" + result_dict_list = await {{ table_name|snake_to_pascal_case }}Service.list_service(search=search, auth=auth) + export_result = await {{ table_name|snake_to_pascal_case }}Service.batch_export_service(obj_list=result_dict_list) + logger.info('导出{{ function_name }}成功') + return StreamResponse( data=bytes2file_response(export_result), media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers = { + headers={ 'Content-Disposition': 'attachment; filename={{ table_name }}.xlsx' } ) - -@{{ table_name }}Controller.post('/import', summary="导入{{ function_name }}", description="导入{{ function_name }}") -async def import_{{ table_name }}( +@{{ table_name|snake_to_pascal_case }}Router.post('/import', summary="导入{{ function_name }}", description="导入{{ function_name }}") +async def import_obj_list_controller( file: UploadFile, auth: AuthSchema = Depends(AuthPermission(["{{ permission_prefix }}:import"])) ) -> JSONResponse: - batch_import_result = await {{ table_name|snake_to_pascal_case }}Service.import_{{ table_name }}_service(file=file, auth=auth, update_support=True) - return SuccessResponse(data=batch_import_result, msg="导入成功") + """导入{{ function_name }}接口""" + batch_import_result = await {{ table_name|snake_to_pascal_case }}Service.batch_import_service(file=file, auth=auth, update_support=True) + logger.info("导入{{ function_name }}成功") + + return SuccessResponse(data=batch_import_result, msg="导入{{ function_name }}成功") + +@{{ table_name|snake_to_pascal_case }}Router.post('/download/template', summary="获取{{ function_name }}导入模板", description="获取{{ function_name }}导入模板", dependencies=[Depends(AuthPermission(["{{ permission_prefix }}:download"]))]) +async def export_obj_template_controller() -> StreamingResponse: + """获取{{ function_name }}导入模板接口""" + example_import_template_result = await {{ table_name|snake_to_pascal_case }}Service.import_template_download_service() + logger.info('获取{{ function_name }}导入模板成功') - -@{{ tableName }}Controller.post('/download/template', summary="获取{{ functionName }}导入模板", description="获取{{ functionName }}导入模板") -async def export_{{ tableName }}_template( - auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:import"])) -) -> StreamingResponse: - {{ tableName }}_import_template_result = await {{ tableName|snake_to_pascal_case }}Service.get_import_template_{{ tableName }}_service() return StreamResponse( - data=bytes2file_response({{ tableName }}_import_template_result), + data=bytes2file_response(example_import_template_result), media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - headers = { - 'Content-Disposition': f'attachment; filename={{ tableName }}_import_template.xlsx' + headers={ + 'Content-Disposition': 'attachment; filename={{ table_name }}_template.xlsx' } ) \ No newline at end of file diff --git a/backend/templates/python/crud.py.j2 b/backend/templates/python/crud.py.j2 index 626120e6..db8e07a3 100644 --- a/backend/templates/python/crud.py.j2 +++ b/backend/templates/python/crud.py.j2 @@ -1,93 +1,40 @@ # -*- coding:utf-8 -*- -from typing import List, Optional -from sqlalchemy import delete, func, select, update -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.orm import selectinload -from {{ package_name }}.model.{{ table_name }}_model import {{ table_name|snake_to_pascal_case }}Model -from {{ package_name }}.schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema +from typing import Dict, List, Optional, Sequence + from app.core.base_crud import CRUDBase from app.api.v1.module_system.auth.schema import AuthSchema +from .model import {{ table_name|snake_to_pascal_case }}Model +from .schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema -class {{ table_name|snake_to_pascal_case }}Dao(CRUDBase[{{ table_name|snake_to_pascal_case }}Model, {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema]): - """ - {{ function_name }}模块数据库操作层 - """ +class {{ table_name|snake_to_pascal_case }}CRUD(CRUDBase[{{ table_name|snake_to_pascal_case }}Model, {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema]): + """{{ function_name }}数据层""" def __init__(self, auth: AuthSchema) -> None: """初始化CRUD""" super().__init__(model={{ table_name|snake_to_pascal_case }}Model, auth=auth) - async def get_{{ table_name }}_by_id(self, db: AsyncSession, {{ table_name }}_id: int): - """ - 根据{{ table_name }}id获取{{ function_name }}信息 - - :param db: orm对象 - :param {{ table_name }}_id: {{ table_name }}id - :return: {{ function_name }}信息对象 - """ - {{ table_name }}_info = ( - ( - await db.execute( - select({{ table_name|snake_to_pascal_case }}Model) - .where({{ table_name|snake_to_pascal_case }}Model.id == {{ table_name }}_id) - ) - ) - .scalars() - .first() - ) - - return {{ table_name }}_info - - async def get_{{ table_name }}_list(self, db: AsyncSession, search: {{ table_name|snake_to_pascal_case }}QueryParam = None, order_by: Optional[str] = None): - """ - 根据查询参数获取{{ function_name }}列表信息 - - :param db: orm对象 - :param search: 查询参数对象 - :param order_by: 排序字段 - :return: {{ function_name }}列表信息对象 - """ - query = select({{ table_name|snake_to_pascal_case }}Model) - - # 添加查询条件 - if search and hasattr(search, '__dict__'): - for attr, value in search.__dict__.items(): - if value is not None and hasattr({{ table_name|snake_to_pascal_case }}Model, attr): - if isinstance(value, tuple) and len(value) == 2: - operator, val = value - if operator == "like": - query = query.where(getattr({{ table_name|snake_to_pascal_case }}Model, attr).like(f"%{val}%")) - elif operator == "between": - query = query.where(getattr({{ table_name|snake_to_pascal_case }}Model, attr).between(val[0], val[1])) - else: - query = query.where(getattr({{ table_name|snake_to_pascal_case }}Model, attr) == val) - else: - query = query.where(getattr({{ table_name|snake_to_pascal_case }}Model, attr) == value) - - # 添加排序 - if order_by: - # 这里应该解析order_by参数并应用排序 - query = query.order_by({{ table_name|snake_to_pascal_case }}Model.created_at.desc()) - else: - query = query.order_by({{ table_name|snake_to_pascal_case }}Model.id.desc()) - - # 执行查询 - result = await db.execute(query) - all_data = list(result.scalars().all()) - - return all_data - - async def delete(self, db: AsyncSession, ids: List[int]) -> int: - """ - 删除{{ function_name }}信息 - - :param db: orm对象 - :param ids: {{ function_name }}id列表 - :return: 删除的记录数 - """ - result = await db.execute( - delete({{ table_name|snake_to_pascal_case }}Model).where({{ table_name|snake_to_pascal_case }}Model.id.in_(ids)) - ) - return result.rowcount \ No newline at end of file + async def get_by_id_crud(self, id: int) -> Optional[{{ table_name|snake_to_pascal_case }}Model]: + """详情""" + return await self.get(id=id) + + async def list_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[{{ table_name|snake_to_pascal_case }}Model]: + """列表查询""" + return await self.list(search=search, order_by=order_by) + + async def create_crud(self, data: {{ table_name|snake_to_pascal_case }}CreateSchema) -> Optional[{{ table_name|snake_to_pascal_case }}Model]: + """创建""" + return await self.create(data=data) + + async def update_crud(self, id: int, data: {{ table_name|snake_to_pascal_case }}UpdateSchema) -> Optional[{{ table_name|snake_to_pascal_case }}Model]: + """更新""" + return await self.update(id=id, data=data) + + async def delete_crud(self, ids: List[int]) -> None: + """批量删除""" + return await self.delete(ids=ids) + + async def set_available_crud(self, ids: List[int], status: bool) -> None: + """批量设置可用状态""" + return await self.set(ids=ids, status=status) \ No newline at end of file diff --git a/backend/templates/python/model.py.j2 b/backend/templates/python/model.py.j2 index 2bf47c53..466c706b 100644 --- a/backend/templates/python/model.py.j2 +++ b/backend/templates/python/model.py.j2 @@ -10,7 +10,7 @@ from app.core.base_model import CreatorMixin class {{ table_name|snake_to_pascal_case }}Model(CreatorMixin): """ - {{ function_name }} + {{ function_name }}表 """ __tablename__ = '{{ table_name }}' @@ -20,8 +20,6 @@ class {{ table_name|snake_to_pascal_case }}Model(CreatorMixin): {{ column.column_name }}: Mapped[Optional[{{ column.python_type }}]] = mapped_column({{ column.column_type|get_sqlalchemy_type }}, nullable=True, comment='{{ column.column_comment }}') {% endfor %} - def __repr__(self): - return f"<{{ table_name|snake_to_pascal_case }}Model(id={self.id})>" {% if sub_table %} diff --git a/backend/templates/python/param.py.j2 b/backend/templates/python/param.py.j2 index ead70267..1bec9cce 100644 --- a/backend/templates/python/param.py.j2 +++ b/backend/templates/python/param.py.j2 @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from datetime import datetime from typing import Optional from fastapi import Query @@ -16,11 +15,15 @@ class {{ table_name|snake_to_pascal_case }}QueryParam: {{ column.column_name }}: Optional[{{ column.python_type }}] = Query(None, description="{{ column.column_comment }}"), {% endif %} {% endfor %} + {% for column in columns %} + {% if column.column_name == 'status' %} + status: Optional[bool] = Query(None, description="是否启用"), + {% endif %} + {% endfor %} + creator: Optional[int] = Query(None, description="创建人"), start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2025-01-01 00:00:00"), end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2025-12-31 23:59:59"), - creator: Optional[int] = Query(None, description="创建人"), ) -> None: - super().__init__() # 模糊查询字段 {% for column in columns %} @@ -31,9 +34,12 @@ class {{ table_name|snake_to_pascal_case }}QueryParam: # 精确查询字段 self.creator_id = creator - + {% for column in columns %} + {% if column.column_name == 'status' %} + self.status = status + {% endif %} + {% endfor %} + # 时间范围查询 if start_time and end_time: - start_datetime = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') - end_datetime = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S') - self.created_at = ("between", (start_datetime, end_datetime)) \ No newline at end of file + self.created_at = ("between", (start_time, end_time)) \ No newline at end of file diff --git a/backend/templates/python/schema.py.j2 b/backend/templates/python/schema.py.j2 index a6cc6e80..dc6307c8 100644 --- a/backend/templates/python/schema.py.j2 +++ b/backend/templates/python/schema.py.j2 @@ -1,9 +1,7 @@ # -*- coding:utf-8 -*- -from datetime import datetime -from typing import List, Optional +from typing import Optional from pydantic import BaseModel, ConfigDict, Field -from pydantic.alias_generators import to_camel from app.core.base_schema import BaseSchema @@ -13,8 +11,6 @@ class {{ table_name|snake_to_pascal_case }}CreateSchema(BaseModel): {{ function_name }}新增模型 """ - model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) - {% for column in columns %} {{ column.column_name }}: Optional[{{ column.python_type }}] = Field(default=None, description='{{ column.column_comment }}') {% endfor %} @@ -24,19 +20,11 @@ class {{ table_name|snake_to_pascal_case }}UpdateSchema({{ table_name|snake_to_p """ {{ function_name }}更新模型 """ - pass + ... class {{ table_name|snake_to_pascal_case }}OutSchema({{ table_name|snake_to_pascal_case }}CreateSchema, BaseSchema): """ {{ function_name }}响应模型 """ - model_config = ConfigDict(from_attributes=True) - - id: Optional[int] = Field(default=None, description='编号') - create_by: Optional[str] = Field(default=None, description='创建者') - create_time: Optional[datetime] = Field(default=None, description='创建时间') - update_by: Optional[str] = Field(default=None, description='更新者') - update_time: Optional[datetime] = Field(default=None, description='更新时间') - remark: Optional[str] = Field(default=None, description='备注') \ No newline at end of file diff --git a/backend/templates/python/service.py.j2 b/backend/templates/python/service.py.j2 index e7cbafa7..90930c64 100644 --- a/backend/templates/python/service.py.j2 +++ b/backend/templates/python/service.py.j2 @@ -1,204 +1,90 @@ # -*- coding:utf-8 -*- import io -from datetime import datetime -from sqlalchemy.ext.asyncio import AsyncSession from typing import Any, List, Dict, Optional from fastapi import UploadFile import pandas as pd +from app.core.base_schema import BatchSetAvailable from app.core.exceptions import CustomException -from app.common.response import SuccessResponse -from app.api.v1.module_system.user.schema import UserOutSchema -from app.api.v1.module_system.auth.schema import AuthSchema from app.utils.excel_util import ExcelUtil -from {{ package_name }}.crud.{{ table_name }}_crud import {{ table_name|snake_to_pascal_case }}Dao -from {{ package_name }}.schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema -from {{ package_name }}.param import {{ table_name|snake_to_pascal_case }}QueryParam +from app.core.logger import logger +from app.api.v1.module_system.auth.schema import AuthSchema +from .schema import {{ table_name|snake_to_pascal_case }}CreateSchema, {{ table_name|snake_to_pascal_case }}UpdateSchema, {{ table_name|snake_to_pascal_case }}OutSchema +from .param import {{ table_name|snake_to_pascal_case }}QueryParam +from .crud import {{ table_name|snake_to_pascal_case }}CRUD class {{ table_name|snake_to_pascal_case }}Service: """ {{ function_name }}服务层 """ - + @classmethod - async def get_{{ table_name }}_list_service( - cls, auth: AuthSchema, search: {{ table_name|snake_to_pascal_case }}QueryParam = None, order_by: Optional[str] = None - ): - """ - 获取{{ function_name }}列表信息service - - :param auth: 认证信息 - :param search: 查询参数对象 - :param order_by: 排序字段 - :return: {{ function_name }}列表信息对象 - """ - # 确保db是AsyncSession类型 - if not isinstance(auth.db, AsyncSession): - raise CustomException(msg='数据库会话类型不正确') - - {{ table_name }}_dao = {{ table_name|snake_to_pascal_case }}Dao(auth=auth) - {{ table_name }}_list_result = await {{ table_name }}_dao.get_{{ table_name }}_list(auth.db, search, order_by) - - return {{ table_name }}_list_result - + async def detail_service(cls, auth: AuthSchema, id: int) -> Dict: + """详情""" + obj = await {{ table_name|snake_to_pascal_case }}CRUD(auth).get_by_id_crud(id=id) + if not obj: + raise CustomException(msg="该数据不存在") + return {{ table_name|snake_to_pascal_case }}OutSchema.model_validate(obj).model_dump() + @classmethod - async def get_{{ table_name }}_by_id_service(cls, auth: AuthSchema, {{ table_name }}_id: int): - """ - 根据{{ table_name }}id获取{{ function_name }}信息service - - :param auth: 认证信息 - :param {{ table_name }}_id: {{ table_name }}id - :return: {{ function_name }}信息对象 - """ - # 确保db是AsyncSession类型 - if not isinstance(auth.db, AsyncSession): - raise CustomException(msg='数据库会话类型不正确') - - {{ table_name }}_dao = {{ table_name|snake_to_pascal_case }}Dao(auth=auth) - {{ table_name }} = await {{ table_name }}_dao.get_{{ table_name }}_by_id(auth.db, {{ table_name }}_id) - if {{ table_name }}: - return {{ table_name }} - else: - raise CustomException(msg='{{ function_name }}不存在') - + async def list_service(cls, auth: AuthSchema, search: Optional[{{ table_name|snake_to_pascal_case }}QueryParam] = None, order_by: Optional[List[Dict[str, str]]] = None) -> List[Dict]: + """列表查询""" + search_dict = search.__dict__ if search else None + obj_list = await {{ table_name|snake_to_pascal_case }}CRUD(auth).list_crud(search=search_dict, order_by=order_by) + return [{{ table_name|snake_to_pascal_case }}OutSchema.model_validate(obj).model_dump() for obj in obj_list] + @classmethod - async def add_{{ table_name }}_service( - cls, auth: AuthSchema, data: {{ table_name|snake_to_pascal_case }}CreateSchema - ) -> SuccessResponse: - """ - 新增{{ function_name }}service - - :param auth: 认证信息 - :param data: 新增{{ function_name }}对象 - :return: 新增{{ function_name }}结果 - """ - # 确保db是AsyncSession类型 - if not isinstance(auth.db, AsyncSession): - raise CustomException(msg='数据库会话类型不正确') - - {{ table_name }}_dao = {{ table_name|snake_to_pascal_case }}Dao(auth=auth) - - try: - data_dict = data.model_dump() - data_dict['create_time'] = datetime.now() - await {{ table_name }}_dao.create(data=data_dict) - if isinstance(auth.db, AsyncSession): - await auth.db.commit() - return SuccessResponse(msg='新增成功') - except Exception as e: - if isinstance(auth.db, AsyncSession): - try: - await auth.db.rollback() - except: - pass # 忽略回滚错误 - raise CustomException(msg=f'新增失败: {str(e)}') - + async def create_service(cls, auth: AuthSchema, data: {{ table_name|snake_to_pascal_case }}CreateSchema) -> Dict: + """创建""" + obj = await {{ table_name|snake_to_pascal_case }}CRUD(auth).create_crud(data=data) + return {{ table_name|snake_to_pascal_case }}OutSchema.model_validate(obj).model_dump() + @classmethod - async def update_{{ table_name }}_service(cls, auth: AuthSchema, id: int, data: {{ table_name|snake_to_pascal_case }}UpdateSchema) -> SuccessResponse: - """ - 编辑{{ function_name }}service - - :param auth: 认证信息 - :param id: {{ function_name }}ID - :param data: 编辑{{ function_name }}对象 - :return: 编辑{{ function_name }}结果 - """ - # 确保db是AsyncSession类型 - if not isinstance(auth.db, AsyncSession): - raise CustomException(msg='数据库会话类型不正确') - - {{ table_name }}_dao = {{ table_name|snake_to_pascal_case }}Dao(auth=auth) - - # 检查记录是否存在 - {{ table_name }}_info = await cls.get_{{ table_name }}_by_id_service(auth, id) - if {{ table_name }}_info: - try: - data_dict = data.model_dump(exclude_unset=True) - data_dict['update_time'] = datetime.now() - await {{ table_name }}_dao.update(id=id, data=data_dict) - if isinstance(auth.db, AsyncSession): - await auth.db.commit() - return SuccessResponse(msg='更新成功') - except Exception as e: - if isinstance(auth.db, AsyncSession): - try: - await auth.db.rollback() - except: - pass # 忽略回滚错误 - raise CustomException(msg=f'更新失败: {str(e)}') - else: - raise CustomException(msg='{{ function_name }}不存在') - + async def update_service(cls, auth: AuthSchema, id: int, data: {{ table_name|snake_to_pascal_case }}UpdateSchema) -> Dict: + """更新""" + obj = await {{ table_name|snake_to_pascal_case }}CRUD(auth).get_by_id_crud(id=id) + if not obj: + raise CustomException(msg='更新失败,该数据不存在') + obj = await {{ table_name|snake_to_pascal_case }}CRUD(auth).update_crud(id=id, data=data) + return {{ table_name|snake_to_pascal_case }}OutSchema.model_validate(obj).model_dump() + @classmethod - async def del_{{ table_name }}_service(cls, auth: AuthSchema, ids: List[int]) -> SuccessResponse: - """ - 删除{{ function_name }}service - - :param auth: 认证信息 - :param ids: {{ function_name }}id列表 - :return: 删除{{ function_name }}结果 - """ - # 确保db是AsyncSession类型 - if not isinstance(auth.db, AsyncSession): - raise CustomException(msg='数据库会话类型不正确') - - {{ table_name }}_dao = {{ table_name|snake_to_pascal_case }}Dao(auth=auth) - - try: - await {{ table_name }}_dao.delete(ids=ids) - if isinstance(auth.db, AsyncSession): - await auth.db.commit() - return SuccessResponse(msg='删除成功') - except Exception as e: - if isinstance(auth.db, AsyncSession): - try: - await auth.db.rollback() - except: - pass # 忽略回滚错误 - raise CustomException(msg=f'删除失败: {str(e)}') - + async def delete_service(cls, auth: AuthSchema, ids: List[int]) -> None: + """删除""" + if len(ids) < 1: + raise CustomException(msg='删除失败,删除对象不能为空') + for id in ids: + obj = await {{ table_name|snake_to_pascal_case }}CRUD(auth).get_by_id_crud(id=id) + if not obj: + raise CustomException(msg=f'删除失败,ID为{id}的数据不存在') + await {{ table_name|snake_to_pascal_case }}CRUD(auth).delete_crud(ids=ids) + @classmethod - async def export_{{ table_name }}_list_service(cls, {{ table_name }}_list: List[Dict[str, Any]]) -> bytes: - """ - 导出{{ function_name }}列表service - - :param {{ table_name }}_list: {{ function_name }}列表数据 - :return: 导出的Excel文件二进制数据 - """ - # 定义字段映射 + async def set_available_service(cls, auth: AuthSchema, data: BatchSetAvailable) -> None: + """批量设置状态""" + await {{ table_name|snake_to_pascal_case }}CRUD(auth).set_available_crud(ids=data.ids, status=data.status) + + @classmethod + async def batch_export_service(cls, obj_list: List[Dict[str, Any]]) -> bytes: + """批量导出""" mapping_dict = { 'id': '编号', {% for column in columns %} '{{ column.column_name }}': '{{ column.column_comment }}', {% endfor %} - 'create_by': '创建者', - 'create_time': '创建时间', - 'update_by': '更新者', - 'update_time': '更新时间', - 'remark': '备注' + 'created_at': '创建时间', + 'updated_at': '更新时间', + 'creator': '创建者', } - # 复制数据并进行必要的转换 - data = {{ table_name }}_list.copy() - for item in data: - # 在这里可以添加特定字段的转换逻辑 - pass - + data = obj_list.copy() return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict) @classmethod - async def import_{{ table_name }}_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: - """ - 导入{{ function_name }}service - - :param auth: 认证信息 - :param file: 上传的Excel文件 - :param update_support: 是否支持更新 - :return: 导入结果信息 - """ - # 定义表头映射 + async def batch_import_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str: + """批量导入""" header_dict = { {% for column in columns %} '{{ column.column_comment }}': '{{ column.column_name }}', @@ -206,7 +92,6 @@ class {{ table_name|snake_to_pascal_case }}Service: } try: - # 读取Excel文件 contents = await file.read() df = pd.read_excel(io.BytesIO(contents)) await file.close() @@ -214,61 +99,49 @@ class {{ table_name|snake_to_pascal_case }}Service: if df.empty: raise CustomException(msg="导入文件为空") - # 检查表头是否完整 missing_headers = [header for header in header_dict.keys() if header not in df.columns] if missing_headers: raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}") - # 重命名列名 df.rename(columns=header_dict, inplace=True) error_msgs = [] success_count = 0 + count = 0 - # 处理每一行数据 for index, row in df.iterrows(): + count += 1 try: - # 构建数据对象 - data = {} - {% for column in columns %} - data['{{ column.column_name }}'] = row['{{ column.column_name }}'] - {% endfor %} - - # 处理导入逻辑 - # 这里需要根据实际情况调整,比如检查是否已存在相同记录 - await {{ table_name|snake_to_pascal_case }}Dao(auth).create(data=data) + data = { + {% for column in columns %} + "{{ column.column_name }}": row['{{ column.column_name }}'], + {% endfor %} + } + await {{ table_name|snake_to_pascal_case }}CRUD(auth).create(data=data) success_count += 1 - except Exception as e: - error_msgs.append(f"第{index+1}行: {str(e)}") + error_msgs.append(f"第{count}行: {str(e)}") continue - # 返回详细的导入结果 result = f"成功导入 {success_count} 条数据" if error_msgs: result += "\n错误信息:\n" + "\n".join(error_msgs) return result except Exception as e: + logger.error(f"批量导入失败: {str(e)}") raise CustomException(msg=f"导入失败: {str(e)}") @classmethod - async def get_import_template_{{ table_name }}_service(cls) -> bytes: - """ - 获取{{ function_name }}导入模板service - - :return: Excel模板文件二进制数据 - """ + async def import_template_download_service(cls) -> bytes: + """下载导入模板""" header_list = [ {% for column in columns %} '{{ column.column_comment }}', {% endfor %} ] - selector_header_list = [] # 需要下拉选择的列 - option_list = [] # 下拉选项配置 - return ExcelUtil.get_excel_template( header_list=header_list, - selector_header_list=selector_header_list, - option_list=option_list + selector_header_list=[], + option_list=[] ) \ No newline at end of file diff --git a/backend/templates/sql/sql.sql.j2 b/backend/templates/sql/sql.sql.j2 index 4ef8bf54..3d98e48d 100644 --- a/backend/templates/sql/sql.sql.j2 +++ b/backend/templates/sql/sql.sql.j2 @@ -1,37 +1,123 @@ --- 菜单 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}', '{{parent_menu_id}}', '1', '{{business_name}}', '{{module_name}}/{{business_name}}/index', 1, 0, 'C', '0', '0', '{{permission_prefix}}:list', '#', 'admin', sysdate(), '', null, '{{function_name}}菜单'); +-- 统一的菜单 SQL(兼容 MySQL / PostgreSQL),对齐到 system_menu 表结构 --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:query', '#', 'admin', sysdate(), '', null, ''); - -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:add', '#', 'admin', sysdate(), '', null, ''); +{# 布尔值与保留字列名处理 #} +{% set b_true = 1 if db_type == 'mysql' else true %} +{% set b_false = 0 if db_type == 'mysql' else false %} +{% set order_col = '`order`' if db_type == 'mysql' else '"order"' %} -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:edit', '#', 'admin', sysdate(), '', null, ''); +{# 公共字段列表(按实际库字段) #} +{# name, type, order, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at #} -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:remove', '#', 'admin', sysdate(), '', null, ''); +{% if db_type == 'mysql' %} +-- 父菜单(类型=2:菜单) +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ( + '{{ function_name }}', + 2, + 1, + {{ b_true }}, + '{{ permission_prefix }}:query', + NULL, + '{{ business_name|snake_to_pascal_case }}', + '/{{ module_name }}/{{ business_name }}', + '{{ module_name }}/{{ business_name }}/index', + NULL, + {{ b_false }}, + {{ b_true }}, + {{ b_false }}, + '{{ function_name }}', + NULL, + {{ b_false }}, + {{ parent_menu_id if parent_menu_id else 'NULL' }}, + '{{ function_name }}菜单', + now(), + now() +); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:export', '#', 'admin', sysdate(), '', null, ''); +-- 获取父菜单ID(MySQL) +SELECT @parentId := LAST_INSERT_ID(); -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('{{function_name}}导入', @parentId, '6', '#', '', 1, 0, 'F', '0', '0', '{{permission_prefix}}:import', '#', 'admin', sysdate(), '', null, ''); +-- 按钮权限(类型=3:按钮/权限) +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, @parentId, '', now(), now()); +{% elif db_type == 'postgresql' %} +-- 父菜单 + 子按钮(PostgreSQL 使用 CTE 获取父ID) +WITH parent AS ( + INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) + VALUES ( + '{{ function_name }}', + 2, + 1, + {{ b_true }}, + '{{ permission_prefix }}:query', + NULL, + '{{ business_name|snake_to_pascal_case }}', + '/{{ module_name }}/{{ business_name }}', + '{{ module_name }}/{{ business_name }}/index', + NULL, + {{ b_false }}, + {{ b_true }}, + {{ b_false }}, + '{{ function_name }}', + NULL, + {{ b_false }}, + {{ parent_menu_id if parent_menu_id else 'NULL' }}, + '{{ function_name }}菜单', + now(), + now() + ) RETURNING id +) +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, id, '', now(), now() FROM parent; +INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +SELECT '{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, id, '', now(), now() FROM parent; -{% for column in columns %} -{% set pythonField = column.python_field %} -{% set column_comment = column.column_comment if column.column_comment else '' %} -{% set parentheseIndex = column_comment.find("(") %} -{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %} -{% if column.is_list %} -INSERT INTO `sys_table` (`table_name`, `field_name`, `prop`, `label`, `sequence`) VALUES ('{{ table_name }}', '{{ column.python_field }}', '{{ pythonField }}', '{{ comment }}', {{ loop.index0 }}); -{% endif %} -{% endfor %} -INSERT INTO `sys_table` (`table_name`, `field_name`, `prop`, `label`, `sequence`, `fixed`) VALUES ('{{ table_name }}', 'operate', 'operate', '操作', {{ columns|length }}, '2'); +{% else %} +-- 未识别的数据库类型,默认按 MySQL 处理 +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}', 2, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, '{{ business_name|snake_to_pascal_case }}', '/{{ module_name }}/{{ business_name }}', '{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id if parent_menu_id else 'NULL' }}, '{{ function_name }}菜单', now(), now()); +SELECT @parentId := LAST_INSERT_ID(); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}查询', 3, 1, {{ b_true }}, '{{ permission_prefix }}:query', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}查询', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}新增', 3, 2, {{ b_true }}, '{{ permission_prefix }}:create', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}新增', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}修改', 3, 3, {{ b_true }}, '{{ permission_prefix }}:update', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}修改', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}删除', 3, 4, {{ b_true }}, '{{ permission_prefix }}:delete', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}删除', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}导出', 3, 5, {{ b_true }}, '{{ permission_prefix }}:export', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导出', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}导入', 3, 6, {{ b_true }}, '{{ permission_prefix }}:import', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}导入', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permission_prefix }}:patch', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}批量状态修改', NULL, {{ b_false }}, @parentId, '', now(), now()); +INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at) +VALUES ('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, @parentId, '', now(), now()); +{% endif %} \ No newline at end of file diff --git a/backend/templates/vue/api.ts.j2 b/backend/templates/vue/api.ts.j2 index c9262118..ace5bd73 100644 --- a/backend/templates/vue/api.ts.j2 +++ b/backend/templates/vue/api.ts.j2 @@ -1,53 +1,124 @@ -import request from '@/utils/request' - -// 查询{{ function_name }}列表 -export function list{{ business_name|snake_to_pascal_case }}(query) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/list', - method: 'get', - params: query - }) -} +import request from "@/utils/request"; -// 查询{{ function_name }}详细 -export function get{{ business_name|snake_to_pascal_case }}(id) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/' + id, - method: 'get' - }) -} +const API_PATH = "/{{ module_name }}/{{ business_name }}"; -// 新增{{ function_name }} -export function add{{ business_name|snake_to_pascal_case }}(data) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/create', - method: 'post', - data: data - }) -} +// 参考 demo.ts 的风格,提供标准的 CRUD 与导入/导出 API(TypeScript) +const {{ business_name|replace('_', ' ')|title|replace(' ', '') }}API = { + // 列表查询 + list(query: {{ business_name|replace('_', ' ')|title|replace(' ', '') }}PageQuery) { + return request>>({ + url: `${API_PATH}/list`, + method: "get", + params: query, + }); + }, + + // 详情查询 + detail(id: number) { + return request>({ + url: `${API_PATH}/detail/${id}`, + method: "get", + }); + }, + + // 新增 + create(data: {{ business_name|replace('_', ' ')|title|replace(' ', '') }}Form) { + return request({ + url: `${API_PATH}/create`, + method: "post", + data, + }); + }, + + // 修改(带主键) + update(id: number, data: {{ business_name|replace('_', ' ')|title|replace(' ', '') }}Form) { + return request({ + url: `${API_PATH}/update/${id}`, + method: "put", + data, + }); + }, + + // 删除(支持批量) + delete(ids: number[]) { + return request({ + url: `${API_PATH}/delete`, + method: "delete", + data: ids, + }); + }, + + // 导出 + export(query: {{ business_name|replace('_', ' ')|title|replace(' ', '') }}PageQuery) { + return request({ + url: `${API_PATH}/export`, + method: "post", + data: query, + responseType: "blob", + }); + }, + + // 下载导入模板 + downloadTemplate() { + return request({ + url: `${API_PATH}/download/template`, + method: "post", + responseType: "blob", + }); + }, + + // 导入 + import(data: any) { + return request({ + url: `${API_PATH}/import`, + method: "post", + data, + headers: { "Content-Type": "multipart/form-data" }, + }); + }, +}; + +export default {{ business_name|replace('_', ' ')|title|replace(' ', '') }}API; + +// ------------------------------ +// TS 类型声明 +// ------------------------------ -// 修改{{ function_name }} -export function update{{ business_name|snake_to_pascal_case }}(data) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/update', - method: 'put', - data: data - }) +export interface {{ business_name|replace('_', ' ')|title|replace(' ', '') }}PageQuery extends PageQuery { + {% for column in columns %} + {% if column.is_query == "1" and column.query_type != "BETWEEN" %} + {{ column.python_field }}?: {{ + 'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio') + else 'number' if column.is_pk == '1' + else 'string' + }}; + {% endif %} + {% endfor %} + // 时间范围查询(按示例统一字段名,如需按字段拆分可在页面层处理) + start_time?: string; + end_time?: string; } -// 删除{{ function_name }} -export function del{{ business_name|snake_to_pascal_case }}(ids) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/' + ids, - method: 'delete' - }) +export interface {{ business_name|replace('_', ' ')|title|replace(' ', '') }}Table { + {% for column in columns %} + {{ column.python_field }}?: {{ + 'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio') + else 'number' if column.is_pk == '1' + else 'string' + }}; + {% endfor %} + creator?: creatorType; } -// 导入{{ function_name }} -export function import{{ business_name|snake_to_pascal_case }}(data) { - return request({ - url: '/{{ module_name }}/{{ business_name }}/import', - method: 'post', - data: data - }) +export interface {{ business_name|replace('_', ' ')|title|replace(' ', '') }}Form { + id?: number; + {% for column in columns %} + {% if column.is_insert == "1" or column.is_edit == "1" %} + {{ column.python_field }}?: {{ + 'boolean' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio') + else 'number' if column.is_pk == '1' + else 'string' + }}; + {% endif %} + {% endfor %} } diff --git a/backend/templates/vue/index.vue.j2 b/backend/templates/vue/index.vue.j2 index 8242193e..ceecd2e6 100644 --- a/backend/templates/vue/index.vue.j2 +++ b/backend/templates/vue/index.vue.j2 @@ -1,597 +1,423 @@