From f4b55334e2853d9093fcb31fcd055251765c5d54 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Sat, 4 Oct 2025 18:55:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0IP=E5=BD=92=E5=B1=9E?= =?UTF-8?q?=E5=9C=B0=E5=B7=A5=E5=85=B7=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor: 简化AuthPermission调用方式 style: 更新日志输出格式和内容 fix: 修复BaseSchema字段可选性 perf: 优化数据库连接日志显示 chore: 更新requirements.txt添加sqlglot依赖 docs: 更新配置注释说明 --- .../app/api/v1/module_ai/mcp/controller.py | 12 +- .../v1/module_application/myapp/controller.py | 12 +- .../api/v1/module_common/file/controller.py | 4 +- .../api/v1/module_example/demo/controller.py | 18 +- .../v1/module_generator/gencode/controller.py | 135 +++--- .../api/v1/module_generator/gencode/crud.py | 116 +++-- .../api/v1/module_generator/gencode/param.py | 10 - .../api/v1/module_generator/gencode/schema.py | 36 +- .../v1/module_generator/gencode/service.py | 455 +++++++----------- .../api/v1/module_monitor/cache/controller.py | 14 +- .../api/v1/module_monitor/job/controller.py | 28 +- .../v1/module_monitor/online/controller.py | 6 +- .../v1/module_monitor/resource/controller.py | 18 +- .../v1/module_monitor/server/controller.py | 2 +- .../api/v1/module_system/dept/controller.py | 12 +- .../api/v1/module_system/dict/controller.py | 30 +- .../app/api/v1/module_system/dict/service.py | 6 +- .../api/v1/module_system/log/controller.py | 8 +- .../api/v1/module_system/menu/controller.py | 12 +- .../api/v1/module_system/notice/controller.py | 14 +- .../api/v1/module_system/params/controller.py | 18 +- .../api/v1/module_system/params/service.py | 4 +- .../v1/module_system/position/controller.py | 14 +- .../api/v1/module_system/role/controller.py | 16 +- .../api/v1/module_system/user/controller.py | 18 +- backend/app/api/v1/module_system/user/crud.py | 2 +- backend/app/common/constant.py | 367 +++++--------- backend/app/config/setting.py | 10 +- backend/app/core/base_model.py | 126 +---- backend/app/core/base_schema.py | 6 +- backend/app/core/database.py | 4 +- backend/app/plugin/init_app.py | 12 +- backend/app/scripts/initialize.py | 16 +- backend/app/utils/common_util.py | 149 +++++- .../gencode => utils}/gen_util.py | 64 +-- .../gencode => utils}/ip_local_util.py | 0 .../jinja2_template_util.py} | 182 +++---- backend/app/utils/jinja2_util.py | 53 -- backend/requirements.txt | 3 +- backend/templates/python/controller.py.j2 | 16 +- 40 files changed, 867 insertions(+), 1161 deletions(-) rename backend/app/{api/v1/module_generator/gencode => utils}/gen_util.py (84%) rename backend/app/{api/v1/module_generator/gencode => utils}/ip_local_util.py (100%) rename backend/app/{api/v1/module_generator/gencode/template_util.py => utils/jinja2_template_util.py} (80%) delete mode 100644 backend/app/utils/jinja2_util.py diff --git a/backend/app/api/v1/module_ai/mcp/controller.py b/backend/app/api/v1/module_ai/mcp/controller.py index e10865f1..4001431f 100644 --- a/backend/app/api/v1/module_ai/mcp/controller.py +++ b/backend/app/api/v1/module_ai/mcp/controller.py @@ -21,7 +21,7 @@ MCPRouter = APIRouter(route_class=OperationLogRoute, prefix="/mcp", tags=["MCP @MCPRouter.post("/chat", summary="智能对话", description="与MCP智能助手进行对话") async def chat_controller( query: ChatQuerySchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:chat"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:chat"])) ) -> StreamingResponse: """智能对话接口""" user_name = auth.user.name if auth.user else "未知用户" @@ -43,7 +43,7 @@ async def chat_controller( @MCPRouter.get("/detail/{id}", summary="获取 MCP 服务器详情", description="获取 MCP 服务器详情") async def detail_controller( id: int = Path(..., description="MCP ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:query"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:query"])) ) -> JSONResponse: result_dict = await McpService.detail_service(auth=auth, id=id) logger.info(f"获取 MCP 服务器详情成功 {id}") @@ -54,7 +54,7 @@ async def detail_controller( async def list_controller( page: PaginationQueryParam = Depends(), search: McpQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:query"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:query"])) ) -> JSONResponse: 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) @@ -65,7 +65,7 @@ async def list_controller( @MCPRouter.post("/create", summary="创建 MCP 服务器", description="创建 MCP 服务器") async def create_controller( data: McpCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:create"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:create"])) ) -> JSONResponse: result_dict = await McpService.create_service(auth=auth, data=data) logger.info(f"创建 MCP 服务器成功: {result_dict}") @@ -76,7 +76,7 @@ async def create_controller( async def update_controller( data: McpUpdateSchema, id: int = Path(..., description="MCP ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:update"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:update"])) ) -> JSONResponse: result_dict = await McpService.update_service(auth=auth, id=id, data=data) logger.info(f"修改 MCP 服务器成功: {result_dict}") @@ -86,7 +86,7 @@ async def update_controller( @MCPRouter.delete("/delete", summary="删除 MCP 服务器", description="删除 MCP 服务器") async def delete_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["ai:mcp:delete"])) + auth: AuthSchema = Depends(AuthPermission(["ai:mcp:delete"])) ) -> JSONResponse: await McpService.delete_service(auth=auth, ids=ids) logger.info(f"删除 MCP 服务器成功: {ids}") diff --git a/backend/app/api/v1/module_application/myapp/controller.py b/backend/app/api/v1/module_application/myapp/controller.py index f7e93424..71708732 100644 --- a/backend/app/api/v1/module_application/myapp/controller.py +++ b/backend/app/api/v1/module_application/myapp/controller.py @@ -24,7 +24,7 @@ MyAppRouter = APIRouter(route_class=OperationLogRoute, prefix="/myapp", tags=[" @MyAppRouter.get("/detail/{id}", summary="获取应用详情", description="获取应用详情") async def get_obj_detail_controller( id: int = Path(..., description="应用ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:query"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:query"])) ) -> JSONResponse: result_dict = await ApplicationService.detail_service(id=id, auth=auth) logger.info(f"获取应用详情成功 {id}") @@ -34,7 +34,7 @@ async def get_obj_detail_controller( async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: ApplicationQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:query"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:query"])) ) -> JSONResponse: 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) @@ -44,7 +44,7 @@ async def get_obj_list_controller( @MyAppRouter.post("/create", summary="创建应用", description="创建应用") async def create_obj_controller( data: ApplicationCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:create"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:create"])) ) -> JSONResponse: result_dict = await ApplicationService.create_service(auth=auth, data=data) logger.info(f"创建应用成功: {result_dict}") @@ -54,7 +54,7 @@ async def create_obj_controller( async def update_obj_controller( data: ApplicationUpdateSchema, id: int = Path(..., description="应用ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:update"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:update"])) ) -> JSONResponse: result_dict = await ApplicationService.update_service(auth=auth, id=id, data=data) logger.info(f"修改应用成功: {result_dict}") @@ -63,7 +63,7 @@ async def update_obj_controller( @MyAppRouter.delete("/delete", summary="删除应用", description="删除应用") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:delete"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:delete"])) ) -> JSONResponse: await ApplicationService.delete_service(auth=auth, ids=ids) logger.info(f"删除应用成功: {ids}") @@ -72,7 +72,7 @@ async def delete_obj_controller( @MyAppRouter.patch("/available/setting", summary="批量修改应用状态", description="批量修改应用状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["application:myapp:patch"])) + auth: AuthSchema = Depends(AuthPermission(["application:myapp:patch"])) ) -> JSONResponse: await ApplicationService.set_available_service(auth=auth, data=data) logger.info(f"批量修改应用状态成功: {data.ids}") diff --git a/backend/app/api/v1/module_common/file/controller.py b/backend/app/api/v1/module_common/file/controller.py index 748afda9..3f2a183c 100644 --- a/backend/app/api/v1/module_common/file/controller.py +++ b/backend/app/api/v1/module_common/file/controller.py @@ -13,7 +13,7 @@ from .service import FileService FileRouter = APIRouter(route_class=OperationLogRoute, prefix="/file", tags=["文件管理"]) -@FileRouter.post("/upload", summary="上传文件", description="上传文件",dependencies=[Depends(AuthPermission(permissions=["common:file:upload"]))]) +@FileRouter.post("/upload", summary="上传文件", description="上传文件",dependencies=[Depends(AuthPermission(["common:file:upload"]))]) async def upload_controller( file: UploadFile, request: Request, @@ -22,7 +22,7 @@ async def upload_controller( logger.info(f"上传文件成功 {result_dict}") return SuccessResponse(data=result_dict, msg="上传文件成功") -@FileRouter.post("/download", summary="下载文件", description="下载文件", dependencies=[Depends(AuthPermission(permissions=["common:file:download"]))]) +@FileRouter.post("/download", summary="下载文件", description="下载文件", dependencies=[Depends(AuthPermission(["common:file:download"]))]) async def download_controller( background_tasks: BackgroundTasks, file_path: str = Body(..., description="文件路径"), diff --git a/backend/app/api/v1/module_example/demo/controller.py b/backend/app/api/v1/module_example/demo/controller.py index 31a952b8..5e80a570 100644 --- a/backend/app/api/v1/module_example/demo/controller.py +++ b/backend/app/api/v1/module_example/demo/controller.py @@ -26,7 +26,7 @@ DemoRouter = APIRouter(route_class=OperationLogRoute, prefix="/demo", tags=["示 @DemoRouter.get("/detail/{id}", summary="获取示例详情", description="获取示例详情") async def get_obj_detail_controller( id: int = Path(..., description="示例ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:query"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:query"])) ) -> JSONResponse: result_dict = await DemoService.detail_service(id=id, auth=auth) logger.info(f"获取示例详情成功 {id}") @@ -36,7 +36,7 @@ async def get_obj_detail_controller( async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: DemoQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:query"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:query"])) ) -> JSONResponse: 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) @@ -46,7 +46,7 @@ async def get_obj_list_controller( @DemoRouter.post("/create", summary="创建示例", description="创建示例") async def create_obj_controller( data: DemoCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:create"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:create"])) ) -> JSONResponse: result_dict = await DemoService.create_service(auth=auth, data=data) logger.info(f"创建示例成功: {result_dict.get('name')}") @@ -56,7 +56,7 @@ async def create_obj_controller( async def update_obj_controller( data: DemoUpdateSchema, id: int = Path(..., description="示例ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:update"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:update"])) ) -> JSONResponse: result_dict = await DemoService.update_service(auth=auth, id=id, data=data) logger.info(f"修改示例成功: {result_dict.get('name')}") @@ -65,7 +65,7 @@ async def update_obj_controller( @DemoRouter.delete("/delete", summary="删除示例", description="删除示例") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:delete"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:delete"])) ) -> JSONResponse: await DemoService.delete_service(auth=auth, ids=ids) logger.info(f"删除示例成功: {ids}") @@ -74,7 +74,7 @@ async def delete_obj_controller( @DemoRouter.patch("/available/setting", summary="批量修改示例状态", description="批量修改示例状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:patch"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:patch"])) ) -> JSONResponse: await DemoService.set_available_service(auth=auth, data=data) logger.info(f"批量修改示例状态成功: {data.ids}") @@ -83,7 +83,7 @@ async def batch_set_available_obj_controller( @DemoRouter.post('/export', summary="导出示例", description="导出示例") async def export_obj_list_controller( search: DemoQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:export"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await DemoService.list_service(search=search, auth=auth) @@ -101,13 +101,13 @@ async def export_obj_list_controller( @DemoRouter.post('/import', summary="导入示例", description="导入示例") async def import_obj_list_controller( file: UploadFile, - auth: AuthSchema = Depends(AuthPermission(permissions=["demo:example:import"])) + auth: AuthSchema = Depends(AuthPermission(["demo:example:import"])) ) -> JSONResponse: 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(permissions=["demo:example:download"]))]) +@DemoRouter.post('/download/template', summary="获取示例导入模板", description="获取示例导入模板", dependencies=[Depends(AuthPermission(["demo:example:download"]))]) async def export_obj_template_controller() -> StreamingResponse: example_import_template_result = await DemoService.import_template_download_service() logger.info('获取示例导入模板成功') diff --git a/backend/app/api/v1/module_generator/gencode/controller.py b/backend/app/api/v1/module_generator/gencode/controller.py index 4d909491..ac1d5130 100644 --- a/backend/app/api/v1/module_generator/gencode/controller.py +++ b/backend/app/api/v1/module_generator/gencode/controller.py @@ -1,9 +1,8 @@ # -*- coding:utf-8 -*- -from datetime import datetime from typing import List from fastapi import APIRouter, Depends, Query, Body, Path -from fastapi.responses import StreamingResponse, JSONResponse +from fastapi.responses import JSONResponse from app.common.response import SuccessResponse, ErrorResponse, StreamResponse from app.core.dependencies import AuthPermission @@ -11,9 +10,8 @@ from app.core.router_class import OperationLogRoute from app.core.base_params import PaginationQueryParam from app.common.request import PaginationService from app.api.v1.module_system.auth.schema import AuthSchema -from app.api.v1.module_system.user.schema import UserOutSchema from .param import GenTableQueryParam -from .schema import GenTableDeleteSchema, GenTableUpdateSchema, GenTableOutSchema +from .schema import GenTableDeleteSchema, GenTableSchema, GenTableOutSchema from .service import GenTableColumnService, GenTableService from app.utils.common_util import bytes2file_response from app.core.logger import logger @@ -22,38 +20,59 @@ from app.core.logger import logger GenRouter = APIRouter(route_class=OperationLogRoute, prefix='/gencode', tags=["代码生成模块"]) +@GenRouter.get("/list", summary="查询代码生成业务表列表", description="查询代码生成业务表列表") +async def gen_table_list_controller( + page: PaginationQueryParam = Depends(), + search: GenTableQueryParam = Depends(), + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) +) -> JSONResponse: + 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('获取代码生成业务表列表成功') + return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功") + + +@GenRouter.get("/db/list", summary="查询数据库表列表", description="查询数据库表列表") +async def get_gen_db_table_list_controller( + page: PaginationQueryParam = Depends(), + search: GenTableQueryParam = Depends(), + auth: AuthSchema = Depends(AuthPermission(["generator:dblist:query"])) +) -> JSONResponse: + 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('获取数据库表列表成功') + return SuccessResponse(data=result_dict, msg="获取数据库表列表成功") + + +@GenRouter.post("/import", summary="导入表结构", description="导入表结构") +async def import_gen_table_controller( + table_names: List[str] = Query(..., description="表名列表", embed=True), + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:import"])), +) -> JSONResponse: + 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('导入表结构成功') + return SuccessResponse(msg="导入表结构成功", data=result) + + @GenRouter.get("/detail/{table_id}", summary="获取业务表详细信息", description="获取业务表详细信息") -async def get_gen_table_detail_controller( +async def gen_table_detail_controller( table_id: int = Path(..., description="业务表ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"])) + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) ) -> JSONResponse: gen_table = await GenTableService.get_gen_table_by_id_service(auth, table_id) gen_tables = await GenTableService.get_gen_table_all_service(auth) - gen_columns = await GenTableColumnService.get_gen_table_column_list_by_table_id_service(auth, table_id) - gen_table_detail_result = dict(info=gen_table, rows=gen_columns, tables=gen_tables) + gen_table_detail_result = dict(info=gen_table, rows=gen_table.columns, tables=gen_tables) logger.info(f'获取table_id为{table_id}的信息成功') return SuccessResponse(data=gen_table_detail_result, msg="获取业务表详细信息成功") -@GenRouter.get("/list", summary="查询代码生成业务表列表", description="查询代码生成业务表列表") -async def get_gen_table_list_controller( - page: PaginationQueryParam = Depends(), - search: GenTableQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"])) -) -> JSONResponse: - result_dict_list = await GenTableService.get_gen_table_list_service(auth=auth, query_object=search, is_page=False) - result_dict = await PaginationService.paginate(data_list=result_dict_list["items"], page_no=page.page_no, page_size=page.page_size) - logger.info('获取代码生成业务表列表成功') - return SuccessResponse(data=result_dict, msg="获取代码生成业务表列表成功") - - @GenRouter.post("/create", summary="创建表结构", description="创建表结构") async def create_table_controller( sql: str = Query(..., description="SQL语句"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:create"])), - current_user: UserOutSchema = Depends(lambda auth: auth.user) + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:create"])), ) -> JSONResponse: - result = await GenTableService.create_table_service(auth, sql, current_user) + result = await GenTableService.create_table_service(auth, sql) logger.info('创建表结构成功') return SuccessResponse(msg="创建表结构成功", data=result) @@ -61,54 +80,30 @@ async def create_table_controller( @GenRouter.put("/update/{table_id}", summary="编辑业务表信息", description="编辑业务表信息") async def update_gen_table_controller( table_id: int = Path(..., description="业务表ID"), - data: GenTableUpdateSchema = Body(..., description="业务表信息"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:update"])), - current_user: UserOutSchema = Depends(lambda auth: auth.user) + data: GenTableSchema = Body(..., description="业务表信息"), + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:update"])), ) -> JSONResponse: - # 创建一个新的字典来包含所有数据,包括审计字段 - update_data = data.model_dump() - update_data['update_by'] = current_user.username - update_data['updated_at'] = datetime.now() - - # 创建一个新的GenTableUpdateSchema实例 - updated_data = GenTableUpdateSchema(**update_data) - - await GenTableService.validate_edit(updated_data) - edit_gen_result = await GenTableService.update_gen_table_service(auth, updated_data, table_id) + await GenTableService.validate_edit(data) + result_dict = await GenTableService.update_gen_table_service(auth, data, table_id) logger.info('编辑业务表信息成功') - return SuccessResponse(data=edit_gen_result, msg="编辑业务表信息成功") + return SuccessResponse(data=result_dict, msg="编辑业务表信息成功") @GenRouter.delete("/delete", summary="删除业务表信息", description="删除业务表信息") async def delete_gen_table_controller( - table_ids: str = Body(..., description="ID列表,用逗号分隔"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:delete"])) + data: GenTableDeleteSchema = Body(..., description="业务表ID列表"), + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:delete"])) ) -> JSONResponse: - delete_gen_table = GenTableDeleteSchema(table_ids=table_ids) - result = await GenTableService.delete_gen_table_service(auth, delete_gen_table) + result = await GenTableService.delete_gen_table_service(auth, data) logger.info('删除业务表信息成功') - return result + return SuccessResponse(msg="删除业务表信息成功", data=result) -@GenRouter.post("/import", summary="导入表结构", description="导入表结构") -async def import_gen_table_controller( - tables: List[str] = Body(..., description="表名列表", embed=True), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:import"])), - current_user: UserOutSchema = Depends(lambda auth: auth.user) -) -> JSONResponse: - table_names = tables if tables else [] - 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, current_user) - logger.info('导入表结构成功') - return result - - -@GenRouter.patch("/batch/out", summary="批量生成代码", description="批量生成代码") +@GenRouter.patch("/batch/output", summary="批量生成代码", description="批量生成代码") async def batch_gen_code_controller( - tables: str = Query(..., description="表名列表,用逗号分隔"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:operate"])) + table_names: List[str] = Query(None, description="表名列表"), + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:operate"])) ) -> StreamResponse: - table_names = tables.split(',') if tables else [] batch_gen_code_result = await GenTableService.batch_gen_code_service(auth, table_names) logger.info('批量生成代码成功') return StreamResponse( @@ -118,10 +113,10 @@ async def batch_gen_code_controller( ) -@GenRouter.post("/out/path/{table_name}", summary="生成代码到指定路径", description="生成代码到指定路径") +@GenRouter.post("/output/{table_name}", summary="生成代码到指定路径", description="生成代码到指定路径") async def gen_code_local_controller( table_name: str = Path(..., description="表名"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:code"])) + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:code"])) ) -> JSONResponse: from app.config.setting import settings if not settings.allow_overwrite: @@ -135,29 +130,17 @@ async def gen_code_local_controller( @GenRouter.get("/preview/{table_id}", summary="预览代码", description="预览代码") async def preview_code_controller( table_id: int = Path(..., description="业务表ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:gencode:query"])) + auth: AuthSchema = Depends(AuthPermission(["generator:gencode:query"])) ) -> JSONResponse: preview_code_result = await GenTableService.preview_code_service(auth, table_id) logger.info('预览代码成功') return SuccessResponse(data=preview_code_result, msg="预览代码成功") -@GenRouter.get("/db/list", summary="查询数据库表列表", description="查询数据库表列表") -async def get_gen_db_table_list_controller( - page: PaginationQueryParam = Depends(), - search: GenTableQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:dblist:query"])) -) -> JSONResponse: - 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('获取数据库表列表成功') - return SuccessResponse(data=result_dict, msg="获取数据库表列表成功") - - -@GenRouter.post("/sync/db/{table_name}", summary="同步数据库", description="同步数据库") +@GenRouter.post("/synch_db/{table_name}", summary="同步数据库", description="同步数据库") async def sync_db_controller( table_name: str = Path(..., description="表名"), - auth: AuthSchema = Depends(AuthPermission(permissions=["generator:db:sync"])) + auth: AuthSchema = Depends(AuthPermission(["generator:db:sync"])) ) -> JSONResponse: result = await GenTableService.sync_db_service(auth, table_name) logger.info('同步数据库成功') diff --git a/backend/app/api/v1/module_generator/gencode/crud.py b/backend/app/api/v1/module_generator/gencode/crud.py index 3b2b4551..d8f4f10c 100644 --- a/backend/app/api/v1/module_generator/gencode/crud.py +++ b/backend/app/api/v1/module_generator/gencode/crud.py @@ -3,23 +3,24 @@ from sqlalchemy.engine.row import Row from sqlalchemy import and_, delete, select, text, update from sqlalchemy.orm import selectinload +from sqlglot.expressions import Expression from typing import List, Optional, Sequence, Dict from app.core.logger import logger - -from .model import GenTableModel, GenTableColumnModel from app.config.setting import settings -from app.common.request import PaginationService +from app.core.base_crud import CRUDBase +from app.api.v1.module_system.auth.schema import AuthSchema +from .param import GenTableQueryParam, GenTableColumnQueryParam +from .model import GenTableModel, GenTableColumnModel from .schema import ( GenTableSchema, + GenTableOutSchema, GenTableDeleteSchema, GenTableColumnSchema, + GenTableColumnOutSchema, GenTableColumnDeleteSchema, GenDBTableSchema, ) -from .param import GenTableQueryParam, GenTableColumnQueryParam -from app.core.base_crud import CRUDBase -from app.api.v1.module_system.auth.schema import AuthSchema class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): @@ -70,12 +71,27 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): ) return gen_table + + async def get_gen_table_all(self) -> Sequence[GenTableModel]: + """ + 获取所有业务表信息 - async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None): + :return: 所有业务表信息列表 + """ + gen_table_all = ( + await self.db.execute( + select(GenTableModel) + .options(selectinload(GenTableModel.columns)) + ) + ).scalars().all() + + return gen_table_all + + async def get_gen_table_list(self, search: Optional[GenTableQueryParam] = None) -> Sequence[GenTableModel]: """ 根据查询参数获取代码生成业务表列表信息 - :param query_object: 查询参数对象 + :param search: 查询参数对象 :return: 代码生成业务表列表信息对象 """ # 构建查询条件 @@ -90,7 +106,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): # 获取所有数据 result = await self.db.execute(query) - gen_table_all = list(result.scalars().all()) + gen_table_all = result.scalars().all() return gen_table_all @@ -105,7 +121,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): await self.db.flush() return gen_table - async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema): + async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> GenTableSchema: """ 修改 """ @@ -119,12 +135,12 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): await self.db.commit() return edit_model - async def delete_gen_table(self, delete_model: GenTableDeleteSchema) -> None: + async def delete_gen_table(self, data: GenTableDeleteSchema) -> None: """ 删除 """ await self.db.execute( - delete(GenTableModel).where(GenTableModel.id.in_(delete_model.table_ids)) + delete(GenTableModel).where(GenTableModel.id.in_(data.table_ids)) ) await self.db.flush() @@ -212,7 +228,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): dict_data.append(dict_row) return dict_data - async def get_db_table_list_by_names(self, table_names: List[str]) -> list[Dict]: + async def get_db_table_list_by_names(self, table_names: List[str]) -> list[GenDBTableSchema]: """ 根据业务表名称组获取数据库列表信息 @@ -280,14 +296,14 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): # 检查row是否为Row对象 if isinstance(row, Row): # 使用._mapping获取字典 - dict_row = GenDBTableSchema(**dict(row._mapping)).model_dump() + dict_row = GenDBTableSchema(**dict(row._mapping)) dict_data.append(dict_row) else: - dict_row = GenDBTableSchema(**dict(row)).model_dump() + dict_row = GenDBTableSchema(**dict(row)) dict_data.append(dict_row) return dict_data - async def create_table_by_sql(self, sql: str) -> bool: + async def create_table_by_sql(self, sql_statements: List[Expression | None]) -> None: """ 根据sql语句创建表结构 @@ -296,16 +312,16 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): :return: """ try: - await self.db.execute(text(sql)) - # 提交事务 - await self.db.commit() - await self.db.flush() - return True + for sql_statement in sql_statements: + # 检查sql_statement是否为空 + if not sql_statement: + continue + sql = sql_statement.sql(dialect=settings.DATABASE_TYPE) + await self.db.execute(text(sql)) except Exception as e: # 如果发生异常,回滚事务 await self.db.rollback() logger.error(f"创建表时发生错误: {e}") - return False class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, GenTableColumnSchema]): @@ -315,35 +331,11 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen """初始化CRUD""" super().__init__(model=GenTableColumnModel, auth=auth) - async def get_by_id_crud(self, column_id: int) -> Optional[GenTableColumnModel]: - """详情""" - return await self.get(id=column_id) - - async def list_crud( - self, - search: Optional[Dict] = None, - order_by: Optional[List[Dict[str, str]]] = None, - ) -> Sequence[GenTableColumnModel]: - """列表查询""" - return await self.list(search=search, order_by=order_by) - - async def create_crud( - self, data: GenTableColumnSchema - ) -> Optional[GenTableColumnModel]: - """创建""" - return await self.create(data=data) - - async def update_crud( - self, id: int, data: GenTableColumnSchema - ) -> Optional[GenTableColumnModel]: - """更新""" - return await self.update(id=id, data=data) - - async def delete_crud(self, data: GenTableColumnDeleteSchema) -> None: - """批量删除""" - return await self.delete(ids=data.column_ids) - - async def get_gen_db_table_columns_by_name(self, table_name: str) -> List[GenTableColumnSchema]: + async def get_gen_table_column_list_by_table_id(self, table_id: int) -> Optional[GenTableColumnModel]: + """根据业务表ID获取业务表字段列表信息""" + return await self.get(table_id=table_id) + + async def get_gen_db_table_columns_by_name(self, table_name: str | None) -> List[GenTableColumnOutSchema]: """ 根据业务表名称获取业务表字段列表信息 @@ -412,7 +404,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen ).fetchall() return [ - GenTableColumnSchema( + GenTableColumnOutSchema( column_name=row[0], is_required=row[1], is_pk=row[2], @@ -423,3 +415,23 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen ) for row in gen_db_table_columns_raw ] + + async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None) -> Sequence[GenTableColumnModel]: + """根据业务表ID查询业务表字段列表""" + return await self.list(search=search, order_by=order_by) + + async def create_gen_table_column_crud(self, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]: + """创建业务表字段""" + return await self.create(data=data) + + async def update_gen_table_column_crud(self, id: int, data: GenTableColumnSchema) -> Optional[GenTableColumnModel]: + """更新业务表字段""" + return await self.update(id=id, data=data) + + async def delete_gen_table_column_by_table_id_dao(self, data: GenTableDeleteSchema) -> None: + """根据业务表ID批量删除""" + return await self.delete(ids=data.table_ids) + + async def delete_gen_table_column_by_column_id_dao(self, data: GenTableColumnDeleteSchema) -> None: + """根据业务表字段ID批量删除""" + 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 e9e35f1f..2a14df8f 100644 --- a/backend/app/api/v1/module_generator/gencode/param.py +++ b/backend/app/api/v1/module_generator/gencode/param.py @@ -35,16 +35,6 @@ class GenTableColumnQueryParam: def __init__( self, column_name: Optional[str] = Query(None, description="列名称"), - 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"), ) -> None: # 模糊查询字段 self.column_name = ("like", column_name) - - # 精确查询字段 - self.creator_id = creator - - # 时间范围查询 - if start_time and end_time: - self.created_at = ("between", (start_time, end_time)) \ No newline at end of file diff --git a/backend/app/api/v1/module_generator/gencode/schema.py b/backend/app/api/v1/module_generator/gencode/schema.py index 7922eb5c..c237a3e3 100644 --- a/backend/app/api/v1/module_generator/gencode/schema.py +++ b/backend/app/api/v1/module_generator/gencode/schema.py @@ -1,10 +1,9 @@ # -*- coding:utf-8 -*- -from typing import Any, List, Literal, Optional +from typing import List, Literal, Optional from pydantic import BaseModel, ConfigDict, Field, model_validator from pydantic.alias_generators import to_camel -from app.utils.string_util import StringUtil from app.common.constant import GenConstant from app.core.base_schema import BaseSchema @@ -18,6 +17,7 @@ class GenTableOptionModel(BaseModel): tree_name: Optional[str] = Field(default=None, description='tree_name') tree_parent_code: Optional[str] = Field(default=None, description='tree_parent_code') + class GenDBTableSchema(BaseModel): model_config = ConfigDict(from_attributes=True) @@ -34,12 +34,13 @@ class GenTableBaseSchema(BaseModel): """ model_config = ConfigDict(from_attributes=True) + table_id: Optional[int] = Field(default=None, description='编号') table_name: Optional[str] = Field(default=None, description='表名称') table_comment: Optional[str] = Field(default=None, description='表描述') sub_table_name: Optional[str] = Field(default=None, description='关联子表的表名') sub_table_fk_name: Optional[str] = Field(default=None, description='子表关联的外键名') class_name: Optional[str] = Field(default=None, description='实体类名称') - tpl_category: Optional[Literal['crud', 'tree']] = Field(default=None, description='使用的模板(crud单表操作 tree树表操作)') + tpl_category: Optional[str] = Field(default=None, description='使用的模板(crud单表操作 tree树表操作)') tpl_web_type: Optional[str] = Field(default=None, description='前端模板类型(element-ui模版 element-plus模版)') package_name: Optional[str] = Field(default=None, description='生成包路径') module_name: Optional[str] = Field(default=None, description='生成模块名') @@ -51,16 +52,17 @@ class GenTableBaseSchema(BaseModel): options: Optional[str] = Field(default=None, description='其它生成选项') description: Optional[str] = Field(default=None, description='功能描述') - params: Optional[Any] = Field(default=None, description='前端传递过来的表附加信息,转换成json字符串后放到options') + params: Optional[GenTableOptionModel] = Field(default=None, description='前端传递过来的表附加信息,转换成json字符串后放到options') class GenTableSchema(GenTableBaseSchema): """ 代码生成业务表更新模型 """ - pk_column: Optional['GenTableColumnSchema'] = Field(default=None, description='主键信息') + + pk_column: Optional['GenTableColumnOutSchema'] = Field(default=None, description='主键信息') sub_table: Optional['GenTableSchema'] = Field(default=None, description='子表信息') - columns: Optional[List['GenTableColumnSchema']] = Field(default=None, description='表列信息') + columns: Optional[List['GenTableColumnOutSchema']] = Field(default=None, description='表列信息') tree_code: Optional[str] = Field(default=None, description='树编码字段tree_code') tree_parent_code: Optional[str] = Field(default=None, description='树父编码字段') tree_name: Optional[str] = Field(default=None, description='树名称字段ree_name') @@ -78,13 +80,17 @@ class GenTableSchema(GenTableBaseSchema): return self +class GenTableOutSchema(GenTableSchema, BaseSchema): + model_config = ConfigDict(from_attributes=True) + + class GenTableDeleteSchema(BaseModel): """ 删除代码生成业务表模型 """ model_config = ConfigDict(alias_generator=to_camel) - table_ids: str = Field(..., description='需要删除的代码生成业务表ID') + table_ids: List[int] = Field(..., description='需要删除的代码生成业务表ID列表') class GenTableColumnSchema(BaseModel): @@ -114,6 +120,22 @@ class GenTableColumnSchema(BaseModel): description: Optional[str] = Field(default=None, description='功能描述') +class GenTableColumnOutSchema(GenTableColumnSchema, BaseSchema): + model_config = ConfigDict(from_attributes=True) + + cap_python_field: Optional[str] = Field(default=None, description='字段大写形式') + pk: Optional[bool] = Field(default=None, description='是否主键') + increment: Optional[bool] = Field(default=None, description='是否自增') + required: Optional[bool] = Field(default=None, description='是否必填') + unique: Optional[bool] = Field(default=None, description='是否唯一') + insert: Optional[bool] = Field(default=None, description='是否为插入字段') + edit: Optional[bool] = Field(default=None, description='是否编辑字段') + list: Optional[bool] = Field(default=None, description='是否列表字段') + query: Optional[bool] = Field(default=None, description='是否查询字段') + super_column: Optional[bool] = Field(default=None, description='是否为基类字段') + usable_column: Optional[bool] = Field(default=None, description='是否为基类字段白名单') + + class GenTableColumnDeleteSchema(BaseModel): """ 删除代码生成业务表字段模型 diff --git a/backend/app/api/v1/module_generator/gencode/service.py b/backend/app/api/v1/module_generator/gencode/service.py index 5061b4e5..a24f2278 100644 --- a/backend/app/api/v1/module_generator/gencode/service.py +++ b/backend/app/api/v1/module_generator/gencode/service.py @@ -4,23 +4,21 @@ import io import json import os import zipfile -from datetime import datetime -from typing import Any, List, Dict, Optional, Sequence -from sqlalchemy.ext.asyncio import AsyncSession +from typing import Any, List, Dict, Literal, Optional +from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update +from sqlglot import parse as sqlglot_parse from app.config.setting import settings -from app.core.base_model import CamelCaseUtil from app.core.exceptions import CustomException -from app.utils.gen_util import GenUtils -from app.utils.template_util import TemplateInitializer, TemplateUtils from app.common.constant import GenConstant 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 .schema import GenTableCreateSchema, GenTableUpdateSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnCreateSchema, GenTableColumnUpdateSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema +from app.utils.common_util import CamelCaseUtil +from app.utils.gen_util import GenUtils +from app.utils.jinja2_template_util import Jinja2TemplateInitializerUtil, Jinja2TemplateUtil +from .schema import GenTableSchema, GenTableOutSchema, GenTableOutSchema, GenTableDeleteSchema, GenTableColumnSchema, GenTableColumnOutSchema, GenTableColumnDeleteSchema from .param import GenTableQueryParam from .crud import GenTableColumnCRUD, GenTableCRUD -from .model import GenTableModel, GenTableColumnModel # 定义默认的GenConfig值 @@ -40,197 +38,129 @@ class GenTableService: @classmethod async def get_gen_table_list_service( - cls, auth: AuthSchema, query_object: GenTableQueryParam, is_page: bool = False - ) -> Dict: + cls, auth: AuthSchema, search: GenTableQueryParam + ) -> List[Dict]: """获取代码生成业务表列表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table_list_result = await gen_table_dao.get_gen_table_list(query_object, is_page) - return gen_table_list_result + 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]: """获取数据库列表信息""" # 确保db是AsyncSession类型 - gen_db_table_list_result = await GenTableCRUD(auth=auth).get_gen_db_table_list(search, order_by) + 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]: """根据表名称组获取数据库列表信息""" - gen_db_table_list_result = await GenTableCRUD(auth=auth).get_gen_db_table_list_by_names(table_names) + gen_db_table_list_result = await GenTableCRUD(auth=auth).get_db_table_list_by_names(table_names) return [GenTableOutSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_db_table_list_result)] @classmethod async def import_gen_table_service( - cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema], current_user: UserOutSchema - ) -> SuccessResponse: + cls, auth: AuthSchema, gen_table_list: List[GenTableOutSchema] + ) -> Literal[True] | None: """导入表结构""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') try: - gen_table_dao = GenTableCRUD(auth=auth) - gen_table_column_dao = GenTableColumnCRUD(auth=auth) - for table in gen_table_list: table_name = table.table_name - GenUtils.init_table(table, current_user.username) - add_gen_table = await gen_table_dao.create(data=table.model_dump()) + GenUtils.init_table(table) + add_gen_table = await GenTableCRUD(auth).add_gen_table(table) if add_gen_table: - # 使用id而不是table_id - table.id = add_gen_table.id - gen_table_columns = await gen_table_column_dao.get_gen_db_table_columns_by_name(db, table_name or "") + table.table_id = add_gen_table.id + gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) for column in [ - GenTableColumnOutSchema(**gen_table_column) + GenTableColumnSchema(**gen_table_column) for gen_table_column in CamelCaseUtil.transform_result(gen_table_columns) ]: GenUtils.init_column_field(column, table) - await gen_table_column_dao.create(data=column.model_dump()) - if isinstance(db, AsyncSession): - await db.commit() - return SuccessResponse(msg='导入成功') + await GenTableColumnCRUD(auth).create_gen_table_column_crud(column) + return True except Exception as e: - if isinstance(db, AsyncSession): - try: - await db.rollback() - except: - pass # 忽略回滚错误 raise CustomException(msg=f'导入失败, {str(e)}') @classmethod - async def create_table_service(cls, auth: AuthSchema, sql: str, current_user: UserOutSchema) -> SuccessResponse: + async def create_table_service(cls, auth: AuthSchema, sql: str) -> Literal[True] | None: """创建表结构""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - try: + sql_statements = sqlglot_parse(sql, dialect=settings.DATABASE_TYPE) + # 校验sql语句是否为合法的建表语句 + if not cls.__is_valid_create_table(sql_statements): + raise CustomException(msg='sql语句不是合法的建表语句') + table_names = cls.__get_table_names(sql_statements) # 执行SQL语句创建表 - await gen_table_dao.create_table_by_sql_dao(db, [sql]) - if isinstance(db, AsyncSession): - await db.commit() - return SuccessResponse(msg='创建表结构成功') + await GenTableCRUD(auth=auth).create_table_by_sql(sql_statements) + 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 except Exception as e: - if isinstance(db, AsyncSession): - try: - await db.rollback() - except: - pass # 忽略回滚错误 raise CustomException(msg=f'创建表结构失败: {str(e)}') + + @classmethod + def __is_valid_create_table(cls, sql_statements: List[Expression | None]) -> bool: + """ + 校验sql语句是否为合法的建表语句 + + :param sql_statements: sql语句的ast列表 + :return: 校验结果 + """ + validate_create = [isinstance(sql_statement, Create) for sql_statement in sql_statements] + validate_forbidden_keywords = [ + isinstance( + sql_statement, + (Add, Alter, Delete, Drop, Insert, TruncateTable, Update), + ) + for sql_statement in sql_statements + ] + if not any(validate_create) or any(validate_forbidden_keywords): + return False + return True + + @classmethod + def __get_table_names(cls, sql_statements: List[Expression | None]) -> List[str]: + """ + 获取sql语句中所有的建表表名 + + :param sql_statements: sql语句的ast列表 + :return: 建表表名列表 + """ + table_names = [] + for sql_statement in sql_statements: + if isinstance(sql_statement, Create): + table_names.append(sql_statement.find(Table).name) + return table_names @classmethod - async def update_gen_table_service(cls, auth: AuthSchema, page_object: GenTableUpdateSchema, table_id: int) -> Dict[str, Any]: - """编辑业务表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table_column_dao = GenTableColumnCRUD(auth=auth) - - edit_gen_table = page_object.model_dump(exclude_unset=True, by_alias=True) + async def update_gen_table_service(cls, auth: AuthSchema, data: GenTableSchema, table_id: int) -> Dict[str, Any]: + """编辑业务表信息""" + 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: try: - # 确保options字段存在且为有效JSON - if 'options' not in edit_gen_table or edit_gen_table['options'] is None: - edit_gen_table['options'] = '{}' # 默认空对象 - else: - # 验证options是否为有效的JSON - try: - json.loads(edit_gen_table['options']) - except json.JSONDecodeError: - edit_gen_table['options'] = '{}' - - await gen_table_dao.update(id=table_id, data=edit_gen_table) - if hasattr(page_object, 'columns') and page_object.columns: - for gen_table_column in page_object.columns: - # 为列添加更新信息 - gen_table_column_dict = gen_table_column.model_dump() - gen_table_column_dict['update_by'] = getattr(page_object, 'update_by', '') - gen_table_column_dict['update_time'] = datetime.now() - # 检查是否有id属性 - column_id = getattr(gen_table_column, 'id', None) - if column_id is not None: - await gen_table_column_dao.update( - id=column_id, - data=gen_table_column_dict - ) - if isinstance(db, AsyncSession): - await db.commit() - return {"is_success": True, "message": "更新成功"} + edit_gen_table['options'] = json.dumps(edit_gen_table.get('params')) + result = await GenTableCRUD(auth).edit_gen_table(table_id, edit_gen_table) + for gen_table_column in data.columns: + await GenTableColumnCRUD(auth).update_gen_table_column_crud(table_id, gen_table_column) + return result.model_dump() except Exception as e: - if isinstance(db, AsyncSession): - try: - await db.rollback() - except: - pass # 忽略回滚错误 raise CustomException(msg=f'更新失败: {str(e)}') else: raise CustomException(msg='业务表不存在') @classmethod - async def delete_gen_table_service(cls, auth: AuthSchema, page_object: GenTableDeleteSchema) -> SuccessResponse: + async def delete_gen_table_service(cls, auth: AuthSchema, data: GenTableDeleteSchema) -> None: """删除业务表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table_column_dao = GenTableColumnCRUD(auth=auth) - - if page_object.table_ids: - table_id_list = page_object.table_ids.split(',') - try: - for table_id in table_id_list: - await gen_table_dao.delete(ids=[int(table_id)]) - # 删除相关的字段信息 - # 这里需要先查询出所有相关的column_id,然后删除 - columns = await gen_table_column_dao.get_gen_table_column_list_by_table_id(db, int(table_id)) - if columns: - column_ids = [column.id for column in columns] - await gen_table_column_dao.delete(ids=column_ids) - if isinstance(db, AsyncSession): - await db.commit() - return SuccessResponse(msg='删除成功') - except Exception as e: - if isinstance(db, AsyncSession): - try: - await db.rollback() - except: - pass # 忽略回滚错误 - raise CustomException(msg=f'删除失败: {str(e)}') - else: - raise CustomException(msg='传入业务表id为空') + try: + await GenTableCRUD(auth=auth).delete_gen_table(data) + await GenTableColumnCRUD(auth=auth).delete_gen_table_column_by_table_id_dao(data) + except Exception as e: + raise CustomException(msg=f'删除失败: {str(e)}') @classmethod async def get_gen_table_by_id_service(cls, auth: AuthSchema, table_id: int) -> GenTableOutSchema: """获取需要生成的业务表详细信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table = await gen_table_dao.get_gen_table_by_id(db, table_id) + gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_id(table_id) if gen_table: result = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table))) return result @@ -240,42 +170,38 @@ class GenTableService: @classmethod async def get_gen_table_all_service(cls, auth: AuthSchema) -> List[GenTableOutSchema]: """获取所有业务表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_tables = await gen_table_dao.get_gen_table_all(db) - result = [] - for table in gen_tables: - table_info = await cls.set_table_from_options(GenTableOutSchema(**CamelCaseUtil.transform_result(table))) - result.append(table_info) + gen_table_all = await GenTableCRUD(auth=auth).get_gen_table_all() + result = [GenTableOutSchema(**gen_table) for gen_table in CamelCaseUtil.transform_result(gen_table_all)] return result @classmethod async def preview_code_service(cls, auth: AuthSchema, table_id: int) -> Dict[Any, Any]: - """预览代码""" - gen_table = await cls.get_gen_table_by_id_service(auth, table_id) - await cls.set_sub_table(auth, gen_table) - await cls._set_pk_column(gen_table) - env = TemplateInitializer.init_jinja2() - context = TemplateUtils.prepare_context(gen_table) - template_list = TemplateUtils.get_template_list( - gen_table.tpl_category or "", - gen_table.tpl_web_type or "" + """ + 预览代码service + + :param auth: 认证对象 + :param table_id: 业务表id + :return: 预览数据列表 + """ + gen_table = GenTableOutSchema( + **CamelCaseUtil.transform_result(await GenTableCRUD(auth).get_gen_table_by_id(table_id)) ) + await cls.set_sub_table(auth, gen_table) + await cls.set_pk_column(gen_table) + env = Jinja2TemplateInitializerUtil.init_jinja2() + context = Jinja2TemplateUtil.prepare_context(gen_table) + template_list = Jinja2TemplateUtil.get_template_list(gen_table.tpl_category, gen_table.tpl_web_type) preview_code_result = {} for template in template_list: render_content = env.get_template(template).render(**context) preview_code_result[template] = render_content return preview_code_result + @classmethod async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse: """生成代码至指定路径""" - env = TemplateInitializer.init_jinja2() + env = Jinja2TemplateInitializerUtil.init_jinja2() render_info = await cls.__get_gen_render_info(auth, table_name) for template in render_info[0]: try: @@ -292,11 +218,17 @@ class GenTableService: @classmethod async def batch_gen_code_service(cls, auth: AuthSchema, table_names: List[str]) -> bytes: - """批量生成代码""" + """ + 批量生成代码service + + :param auth: 认证对象 + :param table_names: 业务表名称组 + :return: 下载代码结果 + """ zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: for table_name in table_names: - env = TemplateInitializer.init_jinja2() + 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]) @@ -307,93 +239,58 @@ class GenTableService: return zip_data @classmethod - async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> SuccessResponse: + async def sync_db_service(cls, auth: AuthSchema, table_name: str) -> None: """同步数据库""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table_column_dao = GenTableColumnCRUD(auth=auth) - - gen_table = await gen_table_dao.get_gen_table_by_name(db, table_name) - if gen_table: - table = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table)) - table_columns = table.columns or [] # 确保不为None - table_column_map = {column.column_name: column for column in table_columns} - query_db_table_columns = await gen_table_column_dao.get_gen_db_table_columns_by_name(db, table_name) - db_table_columns = [ - GenTableColumnOutSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns) - ] - if not db_table_columns: - raise CustomException('同步数据失败,原表结构不存在') - db_table_column_names = [column.column_name for column in db_table_columns] - try: - for column in db_table_columns: - GenUtils.init_column_field(column, table) - if column.column_name in table_column_map: - prev_column = table_column_map[column.column_name] - # 使用getattr安全访问id属性 - column_id = getattr(prev_column, 'id', None) - if column_id is not None: - # 为column设置id属性 - column.id = column_id - if getattr(column, 'list', False): # 使用getattr安全访问属性 - column.dict_type = prev_column.dict_type - column.query_type = prev_column.query_type - if ( - 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 - column.html_type = prev_column.html_type - # 使用getattr安全访问id属性 - column_id = getattr(column, 'id', None) - if column_id is not None: - await gen_table_column_dao.update(id=column_id, data=column.model_dump(by_alias=True)) - else: - await gen_table_column_dao.create(data=column.model_dump(by_alias=True)) - 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: - # 使用getattr安全访问id属性 - column_id = getattr(column, 'id', None) - if column_id is not None: - await gen_table_column_dao.delete(ids=[column_id]) - if isinstance(db, AsyncSession): - await db.commit() - return SuccessResponse(msg='同步成功') - except Exception as e: - if isinstance(db, AsyncSession): - try: - await db.rollback() - except: - pass # 忽略回滚错误 - raise CustomException(msg=f'同步失败: {str(e)}') - else: - raise CustomException('业务表不存在') + gen_table = await GenTableCRUD(auth).get_gen_table_by_name(table_name) + table = GenTableSchema(**CamelCaseUtil.transform_result(gen_table)) + table_columns = table.columns + 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) + db_table_columns = [ + GenTableColumnOutSchema(**column) for column in CamelCaseUtil.transform_result(query_db_table_columns) + ] + if not db_table_columns: + raise CustomException(msg='同步数据失败,原表结构不存在') + db_table_column_names = [column.column_name for column in db_table_columns] + try: + for column in db_table_columns: + GenUtils.init_column_field(column, table) + if column.column_name in table_column_map: + prev_column = table_column_map[column.column_name] + column.id = prev_column.id + if column.list: + column.dict_type = prev_column.dict_type + column.query_type = prev_column.query_type + if ( + 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 + column.html_type = prev_column.html_type + await GenTableColumnCRUD(auth).update_gen_table_column_crud(column.id,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: + await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_dao(column.id) + except Exception as e: + raise e + @classmethod async def set_sub_table(cls, auth: AuthSchema, gen_table: GenTableOutSchema) -> None: """设置主子表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') if gen_table.sub_table_name: gen_table_dao = GenTableCRUD(auth=auth) - sub_table = await gen_table_dao.get_gen_table_by_name(db, gen_table.sub_table_name) + sub_table = await gen_table_dao.get_gen_table_by_name(gen_table.sub_table_name) if sub_table: gen_table.sub_table = GenTableOutSchema(**CamelCaseUtil.transform_result(sub_table)) @classmethod - async def _set_pk_column(cls, gen_table: GenTableOutSchema) -> None: + async def set_pk_column(cls, gen_table: GenTableOutSchema) -> None: """设置主键列信息""" if gen_table.columns: for column in gen_table.columns: @@ -425,7 +322,7 @@ class GenTableService: return gen_table @classmethod - async def validate_edit(cls, edit_gen_table: GenTableUpdateSchema) -> None: + async def validate_edit(cls, edit_gen_table: GenTableSchema) -> None: """编辑保存参数校验""" if edit_gen_table.tpl_category == GenConstant.TPL_TREE: # 从options字段获取参数,而不是params @@ -448,29 +345,26 @@ class GenTableService: @classmethod async def __get_gen_render_info(cls, auth: AuthSchema, table_name: str) -> List[Any]: - """获取生成代码渲染模板相关信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_dao = GenTableCRUD(auth=auth) - gen_table = await gen_table_dao.get_gen_table_by_name(db, table_name) - if gen_table: - gen_table_schema = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table)) - await cls.set_sub_table(auth, gen_table_schema) - await cls._set_pk_column(gen_table_schema) - context = TemplateUtils.prepare_context(gen_table_schema) - template_list = TemplateUtils.get_template_list( - gen_table_schema.tpl_category or "", - gen_table_schema.tpl_web_type or "" - ) - output_files = [TemplateUtils.get_file_name([template], gen_table_schema)[0] for template in template_list] + """ + 获取生成代码渲染模板相关信息 - return [template_list, output_files, context, gen_table_schema] - else: - raise CustomException(msg=f'业务表 {table_name} 不存在') + :param auth: 认证对象 + :param table_name: 业务表名称 + :return: 生成代码渲染模板相关信息 + """ + gen_table = await GenTableCRUD(auth=auth).get_gen_table_by_name(table_name) + gen_table_schema = GenTableOutSchema(**CamelCaseUtil.transform_result(gen_table)) + await cls.set_sub_table(auth, gen_table_schema) + await cls.set_pk_column(gen_table_schema) + context = Jinja2TemplateUtil.prepare_context(gen_table_schema) + template_list = Jinja2TemplateUtil.get_template_list( + gen_table_schema.tpl_category or "", + gen_table_schema.tpl_web_type or "" + ) + output_files = [Jinja2TemplateUtil.get_file_name([template], gen_table_schema)[0] for template in template_list] + + return [template_list, output_files, context, gen_table_schema] + @classmethod def __get_gen_path(cls, gen_table: GenTableOutSchema, template: str) -> Optional[str]: @@ -478,10 +372,10 @@ class GenTableService: try: gen_path = gen_table.gen_path or "" if gen_path == '/': - file_name = TemplateUtils.get_file_name([template], gen_table)[0] + file_name = Jinja2TemplateUtil.get_file_name([template], gen_table)[0] return os.path.join(os.getcwd(), GEN_PATH, file_name) else: - file_name = TemplateUtils.get_file_name([template], gen_table)[0] + file_name = Jinja2TemplateUtil.get_file_name([template], gen_table)[0] return os.path.join(gen_path, file_name) except Exception: return None @@ -493,14 +387,7 @@ class GenTableColumnService: @classmethod async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[GenTableColumnOutSchema]: """获取业务表字段列表信息""" - if not auth.db: - raise CustomException(msg='数据库连接不存在') - # 确保db是AsyncSession类型 - db = auth.db - if not isinstance(db, AsyncSession): - raise CustomException(msg='数据库连接类型不正确') - gen_table_column_dao = GenTableColumnCRUD(auth=auth) - gen_table_column_list_result = await gen_table_column_dao.get_gen_table_column_list_by_table_id(db, table_id) + gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({"table_id": table_id}) return [ GenTableColumnOutSchema(**gen_table_column) for gen_table_column in CamelCaseUtil.transform_result(gen_table_column_list_result) diff --git a/backend/app/api/v1/module_monitor/cache/controller.py b/backend/app/api/v1/module_monitor/cache/controller.py index 371824f0..aa6c62dd 100644 --- a/backend/app/api/v1/module_monitor/cache/controller.py +++ b/backend/app/api/v1/module_monitor/cache/controller.py @@ -17,7 +17,7 @@ CacheRouter = APIRouter(route_class=OperationLogRoute, prefix="/cache", tags=[" @CacheRouter.get( '/info', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))], + dependencies=[Depends(AuthPermission(['monitor:cache:query']))], summary="获取缓存监控信息", description="获取缓存监控信息" ) @@ -32,7 +32,7 @@ async def get_monitor_cache_info_controller( @CacheRouter.get( '/get/names', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))], + dependencies=[Depends(AuthPermission(['monitor:cache:query']))], summary="获取缓存名称列表", description="获取缓存名称列表" ) @@ -45,7 +45,7 @@ async def get_monitor_cache_name_controller() -> JSONResponse: @CacheRouter.get( '/get/keys/{cache_name}', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))], + dependencies=[Depends(AuthPermission(['monitor:cache:query']))], summary="获取缓存键名列表", description="获取缓存键名列表" ) @@ -61,7 +61,7 @@ async def get_monitor_cache_key_controller( @CacheRouter.get( '/get/value/{cache_name}/{cache_key}', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:query']))], + dependencies=[Depends(AuthPermission(['monitor:cache:query']))], summary="获取缓存值", description="获取缓存值" ) @@ -78,7 +78,7 @@ async def get_monitor_cache_value_controller( @CacheRouter.delete( '/delete/name/{cache_name}', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))], + dependencies=[Depends(AuthPermission(['monitor:cache:delete']))], summary="清除指定缓存名称的所有缓存", description="清除指定缓存名称的所有缓存" ) @@ -96,7 +96,7 @@ async def clear_monitor_cache_name_controller( @CacheRouter.delete( '/delete/key/{cache_key}', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))], + dependencies=[Depends(AuthPermission(['monitor:cache:delete']))], summary="清除指定缓存键", description="清除指定缓存键" ) @@ -114,7 +114,7 @@ async def clear_monitor_cache_key_controller( @CacheRouter.delete( '/delete/all', - dependencies=[Depends(AuthPermission(permissions=['monitor:cache:delete']))], + dependencies=[Depends(AuthPermission(['monitor:cache:delete']))], summary="清除所有缓存", description="清除所有缓存" ) diff --git a/backend/app/api/v1/module_monitor/job/controller.py b/backend/app/api/v1/module_monitor/job/controller.py index 390a0aa3..caacc5b1 100644 --- a/backend/app/api/v1/module_monitor/job/controller.py +++ b/backend/app/api/v1/module_monitor/job/controller.py @@ -25,7 +25,7 @@ JobRouter = APIRouter(route_class=OperationLogRoute, prefix="/job", tags=["定 @JobRouter.get("/detail/{id}", summary="获取定时任务详情", description="获取定时任务详情") async def get_obj_detail_controller( id: int = Path(..., description="定时任务ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"])) ) -> JSONResponse: result_dict = await JobService.get_job_detail_service(id=id, auth=auth) logger.info(f"获取定时任务详情成功 {id}") @@ -35,7 +35,7 @@ async def get_obj_detail_controller( async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: JobQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"])) ) -> JSONResponse: 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) @@ -45,7 +45,7 @@ async def get_obj_list_controller( @JobRouter.post("/create", summary="创建定时任务", description="创建定时任务") async def create_obj_controller( data: JobCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:create"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:create"])) ) -> JSONResponse: result_dict = await JobService.create_job_service(auth=auth, data=data) logger.info(f"创建定时任务成功: {result_dict}") @@ -55,7 +55,7 @@ async def create_obj_controller( async def update_obj_controller( data: JobUpdateSchema, id: int = Path(..., description="定时任务ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:update"])) ) -> JSONResponse: result_dict = await JobService.update_job_service(auth=auth, id=id, data=data) logger.info(f"修改定时任务成功: {result_dict}") @@ -64,7 +64,7 @@ async def update_obj_controller( @JobRouter.delete("/delete", summary="删除定时任务", description="删除定时任务") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"])) ) -> JSONResponse: await JobService.delete_job_service(auth=auth, ids=ids) logger.info(f"删除定时任务成功: {ids}") @@ -73,7 +73,7 @@ async def delete_obj_controller( @JobRouter.post('/export', summary="导出定时任务", description="导出定时任务") async def export_obj_list_controller( search: JobQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:export"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await JobService.get_job_list_service(search=search, auth=auth) @@ -90,7 +90,7 @@ async def export_obj_list_controller( @JobRouter.delete("/clear", summary="清空定时任务日志", description="清空定时任务日志") async def clear_obj_log_controller( - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"])) ) -> JSONResponse: await JobService.clear_job_service(auth=auth) logger.info(f"清空定时任务成功") @@ -100,13 +100,13 @@ async def clear_obj_log_controller( async def option_obj_controller( id: int = Body(..., description="定时任务ID"), option: int = Body(..., description="操作类型 1: 暂停 2: 恢复 3: 重启"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:update"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:update"])) ) -> JSONResponse: 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(permissions=["monitor:job:query"]))]) +@JobRouter.get("/log", summary="获取定时任务日志", description="获取定时任务日志", dependencies=[Depends(AuthPermission(["monitor:job:query"]))]) async def get_job_log_controller(): data = [ { @@ -134,7 +134,7 @@ async def get_job_log_controller(): @JobRouter.get("/log/detail/{id}", summary="获取定时任务日志详情", description="获取定时任务日志详情") async def get_job_log_detail_controller( id: int = Path(..., description="定时任务日志ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"])) ) -> JSONResponse: result_dict = await JobLogService.get_job_log_detail_service(id=id, auth=auth) logger.info(f"获取定时任务日志详情成功 {id}") @@ -145,7 +145,7 @@ async def get_job_log_detail_controller( async def get_job_log_list_controller( page: PaginationQueryParam = Depends(), search: JobLogQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:query"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:query"])) ) -> JSONResponse: order_by = [{"create_time": "desc"}] result_dict_list = await JobLogService.get_job_log_list_service(auth=auth, search=search, order_by=order_by) @@ -157,7 +157,7 @@ async def get_job_log_list_controller( @JobRouter.delete("/log/delete", summary="删除定时任务日志", description="删除定时任务日志") async def delete_job_log_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"])) ) -> JSONResponse: await JobLogService.delete_job_log_service(auth=auth, ids=ids) logger.info(f"删除定时任务日志成功: {ids}") @@ -166,7 +166,7 @@ async def delete_job_log_controller( @JobRouter.delete("/log/clear", summary="清空定时任务日志", description="清空定时任务日志") async def clear_job_log_controller( - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:delete"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:delete"])) ) -> JSONResponse: await JobLogService.clear_job_log_service(auth=auth) logger.info(f"清空定时任务日志成功") @@ -176,7 +176,7 @@ async def clear_job_log_controller( @JobRouter.post('/log/export', summary="导出定时任务日志", description="导出定时任务日志") async def export_job_log_list_controller( search: JobLogQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["monitor:job:export"])) + auth: AuthSchema = Depends(AuthPermission(["monitor:job:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await JobLogService.get_job_log_list_service(search=search, auth=auth) diff --git a/backend/app/api/v1/module_monitor/online/controller.py b/backend/app/api/v1/module_monitor/online/controller.py index 7acbb949..785ecc60 100644 --- a/backend/app/api/v1/module_monitor/online/controller.py +++ b/backend/app/api/v1/module_monitor/online/controller.py @@ -19,7 +19,7 @@ OnlineRouter = APIRouter(route_class=OperationLogRoute, prefix="/online", tags=[ @OnlineRouter.get( '/list', - dependencies=[Depends(AuthPermission(permissions=['monitor:online:query']))], + dependencies=[Depends(AuthPermission(['monitor:online:query']))], summary="获取在线用户列表", description="获取在线用户列表" ) @@ -38,7 +38,7 @@ async def get_online_list_controller( @OnlineRouter.delete( '/delete', - dependencies=[Depends(AuthPermission(permissions=['monitor:online:delete']))], + dependencies=[Depends(AuthPermission(['monitor:online:delete']))], summary="强制下线", description="强制下线" ) @@ -56,7 +56,7 @@ async def delete_online_controller( @OnlineRouter.delete( '/clear', - dependencies=[Depends(AuthPermission(permissions=['monitor:online:delete']))], + dependencies=[Depends(AuthPermission(['monitor:online:delete']))], summary="清除所有在线用户", description="清除所有在线用户" ) diff --git a/backend/app/api/v1/module_monitor/resource/controller.py b/backend/app/api/v1/module_monitor/resource/controller.py index 63a0927c..33023873 100644 --- a/backend/app/api/v1/module_monitor/resource/controller.py +++ b/backend/app/api/v1/module_monitor/resource/controller.py @@ -28,7 +28,7 @@ ResourceRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", ta "/list", summary="获取目录列表", description="获取指定目录下的文件和子目录列表", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:query"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:query"]))] ) async def get_directory_list_controller( request: Request, @@ -56,7 +56,7 @@ async def get_directory_list_controller( "/upload", summary="上传文件", description="上传文件到指定目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:upload"]))]) + dependencies=[Depends(AuthPermission(["monitor:resource:upload"]))]) async def upload_file_controller( file: UploadFile, request: Request, @@ -76,7 +76,7 @@ async def upload_file_controller( "/download", summary="下载文件", description="下载指定文件", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:download"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:download"]))] ) async def download_file_controller( request: Request, @@ -104,7 +104,7 @@ async def download_file_controller( "/delete", summary="删除文件", description="删除指定文件或目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:delete"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:delete"]))] ) async def delete_files_controller( paths: List[str] = Body(..., description="文件路径列表") @@ -119,7 +119,7 @@ async def delete_files_controller( "/move", summary="移动文件", description="移动文件或目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:move"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:move"]))] ) async def move_file_controller( data: ResourceMoveSchema @@ -134,7 +134,7 @@ async def move_file_controller( "/copy", summary="复制文件", description="复制文件或目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:copy"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:copy"]))] ) async def copy_file_controller( data: ResourceCopySchema @@ -149,7 +149,7 @@ async def copy_file_controller( "/rename", summary="重命名文件", description="重命名文件或目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:rename"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:rename"]))] ) async def rename_file_controller( data: ResourceRenameSchema @@ -164,7 +164,7 @@ async def rename_file_controller( "/create-dir", summary="创建目录", description="在指定路径创建新目录", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:create_dir"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:create_dir"]))] ) async def create_directory_controller( data: ResourceCreateDirSchema @@ -179,7 +179,7 @@ async def create_directory_controller( "/export", summary="导出资源列表", description="导出资源列表", - dependencies=[Depends(AuthPermission(permissions=["monitor:resource:export"]))] + dependencies=[Depends(AuthPermission(["monitor:resource:export"]))] ) async def export_resource_list_controller( request: Request, diff --git a/backend/app/api/v1/module_monitor/server/controller.py b/backend/app/api/v1/module_monitor/server/controller.py index b8465259..13705bc0 100644 --- a/backend/app/api/v1/module_monitor/server/controller.py +++ b/backend/app/api/v1/module_monitor/server/controller.py @@ -17,7 +17,7 @@ ServerRouter = APIRouter(route_class=OperationLogRoute, prefix="/server", tags=[ '/info', summary="查询服务器监控信息", description="查询服务器监控信息", - dependencies=[Depends(AuthPermission(permissions=["monitor:server:query"]))] + dependencies=[Depends(AuthPermission(["monitor:server:query"]))] ) async def get_monitor_server_info_controller() -> JSONResponse: # 获取全量数据 diff --git a/backend/app/api/v1/module_system/dept/controller.py b/backend/app/api/v1/module_system/dept/controller.py index 420ba8e3..8becab23 100644 --- a/backend/app/api/v1/module_system/dept/controller.py +++ b/backend/app/api/v1/module_system/dept/controller.py @@ -23,7 +23,7 @@ DeptRouter = APIRouter(route_class=OperationLogRoute, prefix="/dept", tags=["部 @DeptRouter.get("/tree", summary="查询部门树", description="查询部门树") async def get_dept_tree_controller( search: DeptQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:query"])) ) -> JSONResponse: order_by = [{"order": "asc"}] result_dict_list = await DeptService.get_dept_tree_service(search=search, auth=auth, order_by=order_by) @@ -34,7 +34,7 @@ async def get_dept_tree_controller( @DeptRouter.get("/detail/{id}", summary="查询部门详情", description="查询部门详情") async def get_obj_detail_controller( id: int = Path(..., description="部门ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:query"])) ) -> JSONResponse: result_dict = await DeptService.get_dept_detail_service(id=id, auth=auth) logger.info(f"查询部门详情成功 {id}") @@ -44,7 +44,7 @@ async def get_obj_detail_controller( @DeptRouter.post("/create", summary="创建部门", description="创建部门") async def create_obj_controller( data: DeptCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:create"])) ) -> JSONResponse: result_dict = await DeptService.create_dept_service(data=data, auth=auth) logger.info(f"创建部门成功: {result_dict}") @@ -55,7 +55,7 @@ async def create_obj_controller( async def update_obj_controller( data: DeptUpdateSchema, id: int = Path(..., description="部门ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:update"])) ) -> JSONResponse: result_dict = await DeptService.update_dept_service(auth=auth, id=id, data=data) logger.info(f"修改部门成功: {result_dict}") @@ -65,7 +65,7 @@ async def update_obj_controller( @DeptRouter.delete("/delete", summary="删除部门", description="删除部门") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:delete"])) ) -> JSONResponse: await DeptService.delete_dept_service(ids=ids, auth=auth) logger.info(f"删除部门成功: {ids}") @@ -75,7 +75,7 @@ async def delete_obj_controller( @DeptRouter.patch("/available/setting", summary="批量修改部门状态", description="批量修改部门状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dept:patch"])) + auth: AuthSchema = Depends(AuthPermission(["system:dept:patch"])) ) -> JSONResponse: await DeptService.batch_set_available_service(data=data, auth=auth) logger.info(f"批量修改部门状态成功: {data.ids}") diff --git a/backend/app/api/v1/module_system/dict/controller.py b/backend/app/api/v1/module_system/dict/controller.py index 7f99fd74..e0ca7a46 100644 --- a/backend/app/api/v1/module_system/dict/controller.py +++ b/backend/app/api/v1/module_system/dict/controller.py @@ -29,7 +29,7 @@ DictRouter = APIRouter(route_class=OperationLogRoute, prefix="/dict", tags=["字 @DictRouter.get("/type/detail/{id}", summary="获取字典类型详情", description="获取字典类型详情") async def get_type_detail_controller( id: int = Path(..., description="字典类型ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: result_dict = await DictTypeService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取字典类型详情成功 {id}") @@ -39,7 +39,7 @@ async def get_type_detail_controller( async def get_type_list_controller( page: PaginationQueryParam = Depends(), search: DictTypeQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: 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) @@ -48,7 +48,7 @@ async def get_type_list_controller( @DictRouter.get("/type/optionselect", summary="获取全部字典类型", description="获取全部字典类型") async def get_type_loptionselect_controller( - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:query"])) ) -> JSONResponse: result_dict_list = await DictTypeService.get_obj_list_service(auth=auth) logger.info(f"获取字典类型列表成功") @@ -58,7 +58,7 @@ async def get_type_loptionselect_controller( async def create_type_controller( data: DictTypeCreateSchema, redis: Redis = Depends(redis_getter), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:create"])) ) -> JSONResponse: result_dict = await DictTypeService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建字典类型成功: {result_dict}") @@ -69,7 +69,7 @@ async def update_type_controller( data: DictTypeUpdateSchema, redis: Redis = Depends(redis_getter), id: int = Path(..., description="字典类型ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:update"])) ) -> JSONResponse: result_dict = await DictTypeService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"修改字典类型成功: {result_dict}") @@ -79,7 +79,7 @@ async def update_type_controller( async def delete_type_controller( redis: Redis = Depends(redis_getter), ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:delete"])) ) -> JSONResponse: await DictTypeService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除字典类型成功: {ids}") @@ -88,7 +88,7 @@ async def delete_type_controller( @DictRouter.patch("/type/available/setting", summary="批量修改字典类型状态", description="批量修改字典类型状态") async def batch_set_available_dict_type_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:patch"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:patch"])) ) -> JSONResponse: await DictTypeService.set_obj_available_service(auth=auth, data=data) logger.info(f"批量修改字典类型状态成功: {data.ids}") @@ -97,7 +97,7 @@ async def batch_set_available_dict_type_controller( @DictRouter.post('/type/export', summary="导出字典类型", description="导出字典类型") async def export_type_list_controller( search: DictTypeQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_type:export"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_type:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await DictTypeService.get_obj_list_service(search=search, auth=auth) @@ -115,7 +115,7 @@ async def export_type_list_controller( @DictRouter.get("/data/detail/{id}", summary="获取字典数据详情", description="获取字典数据详情") async def get_data_detail_controller( id: int = Path(..., description="字典数据ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:query"])) ) -> JSONResponse: result_dict = await DictDataService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取字典数据详情成功 {id}") @@ -125,7 +125,7 @@ async def get_data_detail_controller( async def get_data_list_controller( page: PaginationQueryParam = Depends(), search: DictDataQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:query"])) + 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) result_dict = await PaginationService.paginate(data_list= result_dict_list, page_no= page.page_no, page_size = page.page_size) @@ -136,7 +136,7 @@ async def get_data_list_controller( async def create_data_controller( data: DictDataCreateSchema, redis: Redis = Depends(redis_getter), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:create"])) ) -> JSONResponse: result_dict = await DictDataService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建字典数据成功: {result_dict}") @@ -147,7 +147,7 @@ async def update_data_controller( data: DictDataUpdateSchema, redis: Redis = Depends(redis_getter), id: int = Path(..., description="字典数据ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:update"])) ) -> JSONResponse: result_dict = await DictDataService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"修改字典数据成功: {result_dict}") @@ -157,7 +157,7 @@ async def update_data_controller( async def delete_data_controller( redis: Redis = Depends(redis_getter), ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:delete"])) ) -> JSONResponse: await DictDataService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除字典数据成功: {ids}") @@ -166,7 +166,7 @@ async def delete_data_controller( @DictRouter.patch("/data/available/setting", summary="批量修改字典数据状态", description="批量修改字典数据状态") async def batch_set_available_dict_data_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:patch"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:patch"])) ) -> JSONResponse: await DictDataService.set_obj_available_service(auth=auth, data=data) logger.info(f"批量修改字典数据状态成功: {data.ids}") @@ -176,7 +176,7 @@ async def batch_set_available_dict_data_controller( async def export_data_list_controller( search: DictDataQueryParam = Depends(), page: PaginationQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:dict_data:export"])) + auth: AuthSchema = Depends(AuthPermission(["system:dict_data:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await DictDataService.get_obj_list_service(auth=auth, search=search, order_by=page.order_by) diff --git a/backend/app/api/v1/module_system/dict/service.py b/backend/app/api/v1/module_system/dict/service.py index 3abd5fd6..1996d8eb 100644 --- a/backend/app/api/v1/module_system/dict/service.py +++ b/backend/app/api/v1/module_system/dict/service.py @@ -183,14 +183,14 @@ class DictDataService: auth = AuthSchema(db=session) obj_list = await DictTypeCRUD(auth).get_obj_list_crud() if not obj_list: - logger.warning("未找到任何字典类型数据") + logger.warning("❗️ 未找到任何字典类型数据") return for obj in obj_list: dict_type = obj.dict_type dict_data_list = await DictDataCRUD(auth).get_obj_list_crud(search={'dict_type': dict_type}) if not dict_data_list: - logger.warning(f"字典类型 {dict_type} 未找到对应的字典数据") + logger.warning(f"❗️ 字典类型 {dict_type} 未找到对应的字典数据") continue dict_data = [DictDataOutSchema.model_validate(row).model_dump() for row in dict_data_list if row] @@ -204,7 +204,7 @@ class DictDataService: value=value, ) except Exception as e: - logger.error(f"初始化字典数据失败: {e}") + logger.error(f"❌️ 初始化字典数据失败: {e}") raise CustomException(msg=f"初始化字典数据失败 {e}") @classmethod diff --git a/backend/app/api/v1/module_system/log/controller.py b/backend/app/api/v1/module_system/log/controller.py index 9bbce836..c0f97d9c 100644 --- a/backend/app/api/v1/module_system/log/controller.py +++ b/backend/app/api/v1/module_system/log/controller.py @@ -22,7 +22,7 @@ LogRouter = APIRouter(route_class=OperationLogRoute, prefix="/log", tags=["日 async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: OperationLogQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:log:query"])) ) -> JSONResponse: """ 查询日志 """ order_by = [{"created_at": "desc"}] @@ -37,7 +37,7 @@ async def get_obj_list_controller( @LogRouter.get("/detail/{id}", summary="日志详情", description="日志详情") async def get_obj_detail_controller( id: int = Path(..., description="操作日志ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:log:query"])) ) -> JSONResponse: """ 详情日志 """ result_dict = await OperationLogService.get_log_detail_service(id=id, auth=auth) @@ -48,7 +48,7 @@ async def get_obj_detail_controller( @LogRouter.delete("/delete", summary="删除日志", description="删除日志") async def delete_obj_log_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:log:delete"])) ) -> JSONResponse: """ 删除日志 """ await OperationLogService.delete_log_service(ids=ids, auth=auth) @@ -59,7 +59,7 @@ async def delete_obj_log_controller( @LogRouter.post("/export", summary="导出日志", description="导出日志") async def export_obj_list_controller( search: OperationLogQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:log:export"])) + auth: AuthSchema = Depends(AuthPermission(["system:log:export"])) ) -> StreamingResponse: """ 导出日志 """ operation_log_list = await OperationLogService.get_log_list_service(search=search, auth=auth) diff --git a/backend/app/api/v1/module_system/menu/controller.py b/backend/app/api/v1/module_system/menu/controller.py index da4858ce..ead2330e 100644 --- a/backend/app/api/v1/module_system/menu/controller.py +++ b/backend/app/api/v1/module_system/menu/controller.py @@ -22,7 +22,7 @@ MenuRouter = APIRouter(route_class=OperationLogRoute, prefix="/menu", tags=["菜 @MenuRouter.get("/tree", summary="查询菜单树", description="查询菜单树") async def get_menu_tree_controller( search: MenuQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) ) -> JSONResponse: order_by = [{"order": "asc"}] result_dict_list = await MenuService.get_menu_tree_service(search=search, auth=auth, order_by=order_by) @@ -33,7 +33,7 @@ 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(permissions=["system:menu:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:query"])) ) -> JSONResponse: result_dict = await MenuService.get_menu_detail_service(id=id, auth=auth) logger.info(f"查询菜单情成功 {id}") @@ -43,7 +43,7 @@ async def get_obj_detail_controller( @MenuRouter.post("/create", summary="创建菜单", description="创建菜单") async def create_obj_controller( data: MenuCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:create"])) ) -> JSONResponse: result_dict = await MenuService.create_menu_service(data=data, auth=auth) logger.info(f"创建菜单成功: {result_dict}") @@ -54,7 +54,7 @@ async def create_obj_controller( async def update_obj_controller( data: MenuUpdateSchema, id: int = Path(..., description="菜单ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:update"])) ) -> JSONResponse: result_dict = await MenuService.update_menu_service(id=id, data=data, auth=auth) logger.info(f"修改菜单成功: {result_dict}") @@ -64,7 +64,7 @@ async def update_obj_controller( @MenuRouter.delete("/delete", summary="删除菜单", description="删除菜单") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:delete"])) ) -> JSONResponse: await MenuService.delete_menu_service(ids=ids, auth=auth) logger.info(f"删除菜单成功: {ids}") @@ -74,7 +74,7 @@ async def delete_obj_controller( @MenuRouter.patch("/available/setting", summary="批量修改菜单状态", description="批量修改菜单状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:menu:patch"])) + auth: AuthSchema = Depends(AuthPermission(["system:menu:patch"])) ) -> JSONResponse: await MenuService.set_menu_available_service(data=data, auth=auth) logger.info(f"批量修改菜单状态成功: {data.ids}") diff --git a/backend/app/api/v1/module_system/notice/controller.py b/backend/app/api/v1/module_system/notice/controller.py index 604364f7..8c5f847e 100644 --- a/backend/app/api/v1/module_system/notice/controller.py +++ b/backend/app/api/v1/module_system/notice/controller.py @@ -25,7 +25,7 @@ NoticeRouter = APIRouter(route_class=OperationLogRoute, prefix="/notice", tags=[ @NoticeRouter.get("/detail/{id}", summary="获取公告详情", description="获取公告详情") async def get_obj_detail_controller( id: int = Path(..., description="公告ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:query"])) ) -> JSONResponse: result_dict = await NoticeService.get_notice_detail_service(id=id, auth=auth) logger.info(f"获取公告详情成功 {id}") @@ -35,7 +35,7 @@ async def get_obj_detail_controller( async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: NoticeQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:query"])) ) -> 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) @@ -45,7 +45,7 @@ async def get_obj_list_controller( @NoticeRouter.post("/create", summary="创建公告", description="创建公告") async def create_obj_controller( data: NoticeCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:create"])) ) -> JSONResponse: result_dict = await NoticeService.create_notice_service(auth=auth, data=data) logger.info(f"创建公告成功: {result_dict}") @@ -55,7 +55,7 @@ async def create_obj_controller( async def update_obj_controller( data: NoticeUpdateSchema, id: int = Path(..., description="公告ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:update"])) ) -> JSONResponse: result_dict = await NoticeService.update_notice_service(auth=auth, id=id, data=data) logger.info(f"修改公告成功: {result_dict}") @@ -64,7 +64,7 @@ async def update_obj_controller( @NoticeRouter.delete("/delete", summary="删除公告", description="删除公告") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:delete"])) ) -> JSONResponse: await NoticeService.delete_notice_service(auth=auth, ids=ids) logger.info(f"删除公告成功: {ids}") @@ -73,7 +73,7 @@ async def delete_obj_controller( @NoticeRouter.patch("/available/setting", summary="批量修改公告状态", description="批量修改公告状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:patch"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:patch"])) ) -> JSONResponse: await NoticeService.set_notice_available_service(auth=auth, data=data) logger.info(f"批量修改公告状态成功: {data.ids}") @@ -82,7 +82,7 @@ async def batch_set_available_obj_controller( @NoticeRouter.post('/export', summary="导出公告", description="导出公告") async def export_obj_list_controller( search: NoticeQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:notice:export"])) + auth: AuthSchema = Depends(AuthPermission(["system:notice:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await NoticeService.get_notice_list_service(search=search, auth=auth) diff --git a/backend/app/api/v1/module_system/params/controller.py b/backend/app/api/v1/module_system/params/controller.py index 31e83f36..8a9b5b52 100644 --- a/backend/app/api/v1/module_system/params/controller.py +++ b/backend/app/api/v1/module_system/params/controller.py @@ -23,7 +23,7 @@ ParamsRouter = APIRouter(route_class=OperationLogRoute, prefix="/param", tags=[" @ParamsRouter.get("/detail/{id}", summary="获取参数详情", description="获取参数详情") async def get_type_detail_controller( id: int = Path(..., description="参数ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: result_dict = await ParamsService.get_obj_detail_service(id=id, auth=auth) logger.info(f"获取参数详情成功 {id}") @@ -33,7 +33,7 @@ async def get_type_detail_controller( @ParamsRouter.get("/key/{config_key}", summary="根据配置键获取参数详情", description="根据配置键获取参数详情") async def get_obj_by_key_controller( config_key: str = Path(..., description="配置键"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: result_dict = await ParamsService.get_obj_by_key_service(config_key=config_key, auth=auth) logger.info(f"根据配置键获取参数详情成功 {config_key}") @@ -43,7 +43,7 @@ async def get_obj_by_key_controller( @ParamsRouter.get("/value/{config_key}", summary="根据配置键获取参数值", description="根据配置键获取参数值") async def get_config_value_by_key_controller( config_key: str = Path(..., description="配置键"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:query"])) ) -> JSONResponse: result_value = await ParamsService.get_config_value_by_key_service(config_key=config_key, auth=auth) logger.info(f"根据配置键获取参数值成功 {config_key}") @@ -52,7 +52,7 @@ async def get_config_value_by_key_controller( @ParamsRouter.get("/list", summary="获取参数列表", description="获取参数列表") async def get_obj_list_controller( - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:param:query"])), page: PaginationQueryParam = Depends(), search: ParamsQueryParam = Depends(), ) -> JSONResponse: @@ -66,7 +66,7 @@ async def get_obj_list_controller( async def create_obj_controller( data: ParamsCreateSchema, redis: Redis = Depends(redis_getter), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:create"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:create"])) ) -> JSONResponse: result_dict = await ParamsService.create_obj_service(auth=auth, redis=redis, data=data) logger.info(f"创建参数成功: {result_dict}") @@ -78,7 +78,7 @@ async def update_objs_controller( data: ParamsUpdateSchema, id: int = Path(..., description="参数ID"), redis: Redis = Depends(redis_getter), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:update"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:update"])) ) -> JSONResponse: result_dict = await ParamsService.update_obj_service(auth=auth, redis=redis, id=id, data=data) logger.info(f"更新参数成功 {result_dict}") @@ -89,7 +89,7 @@ async def update_objs_controller( async def delete_obj_controller( redis: Redis = Depends(redis_getter), ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:delete"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:delete"])) ) -> JSONResponse: await ParamsService.delete_obj_service(auth=auth, redis=redis, ids=ids) logger.info(f"删除参数成功: {ids}") @@ -99,7 +99,7 @@ async def delete_obj_controller( @ParamsRouter.post('/export', summary="导出参数", description="导出参数") async def export_obj_list_controller( search: ParamsQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:param:export"])) + auth: AuthSchema = Depends(AuthPermission(["system:param:export"])) ) -> StreamingResponse: # 获取全量数据 result_dict_list = await ParamsService.get_obj_list_service(search=search, auth=auth) @@ -115,7 +115,7 @@ async def export_obj_list_controller( ) -@ParamsRouter.post("/upload", summary="上传文件", dependencies=[Depends(AuthPermission(permissions=["system:param:upload"]))]) +@ParamsRouter.post("/upload", summary="上传文件", dependencies=[Depends(AuthPermission(["system:param:upload"]))]) async def upload_file_controller( file: UploadFile, request: Request diff --git a/backend/app/api/v1/module_system/params/service.py b/backend/app/api/v1/module_system/params/service.py index 3f43f36b..638802e0 100644 --- a/backend/app/api/v1/module_system/params/service.py +++ b/backend/app/api/v1/module_system/params/service.py @@ -194,10 +194,10 @@ class ParamsService: value=value, ) if not result: - logger.error(f"初始化系统配置失败: {config_obj_dict}") + logger.error(f"❌️ 初始化系统配置失败: {config_obj_dict}") raise CustomException(msg="初始化系统配置失败") except Exception as e: - logger.error(f"初始化系统配置失败: {e}") + logger.error(f"❌️ 初始化系统配置失败: {e}") raise CustomException(msg="初始化系统配置失败") @classmethod diff --git a/backend/app/api/v1/module_system/position/controller.py b/backend/app/api/v1/module_system/position/controller.py index b8d63c4c..f44e0b29 100644 --- a/backend/app/api/v1/module_system/position/controller.py +++ b/backend/app/api/v1/module_system/position/controller.py @@ -27,7 +27,7 @@ PositionRouter = APIRouter(route_class=OperationLogRoute, prefix="/position", ta async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: PositionQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:query"])), ) -> JSONResponse: order_by = [{"order": "asc"}] if page.order_by: @@ -41,7 +41,7 @@ async def get_obj_list_controller( @PositionRouter.get("/detail/{id}", summary="查询岗位详情", description="查询岗位详情") async def get_obj_detail_controller( id: int = Path(..., description="岗位ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:query"])), ) -> JSONResponse: result_dict = await PositionService.get_position_detail_service(id=id, auth=auth) logger.info(f"查询岗位详情成功 {id}") @@ -51,7 +51,7 @@ async def get_obj_detail_controller( @PositionRouter.post("/create", summary="创建岗位", description="创建岗位") async def create_obj_controller( data: PositionCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:create"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:create"])), ) -> JSONResponse: result_dict = await PositionService.create_position_service(data=data, auth=auth) logger.info(f"创建岗位成功: {result_dict}") @@ -62,7 +62,7 @@ async def create_obj_controller( async def update_obj_controller( data: PositionUpdateSchema, id: int = Path(..., description="岗位ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:update"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:update"])), ) -> JSONResponse: result_dict = await PositionService.update_position_service(id=id, data=data, auth=auth) logger.info(f"修改岗位成功: {result_dict}") @@ -72,7 +72,7 @@ async def update_obj_controller( @PositionRouter.delete("/delete", summary="删除岗位", description="删除岗位") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:delete"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:delete"])), ) -> JSONResponse: await PositionService.delete_position_service(ids=ids, auth=auth) logger.info(f"删除岗位成功: {ids}") @@ -82,7 +82,7 @@ async def delete_obj_controller( @PositionRouter.patch("/available/setting", summary="批量修改岗位状态", description="批量修改岗位状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:patch"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:patch"])), ) -> JSONResponse: await PositionService.set_position_available_service(data=data, auth=auth) logger.info(f"批量修改岗位状态成功: {data.ids}") @@ -92,7 +92,7 @@ async def batch_set_available_obj_controller( @PositionRouter.post('/export', summary="导出岗位", description="导出岗位") async def export_obj_list_controller( search: PositionQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:position:export"])), + auth: AuthSchema = Depends(AuthPermission(["system:position:export"])), ) -> StreamingResponse: # 获取全量数据 position_query_result = await PositionService.get_position_list_service(search=search, auth=auth) diff --git a/backend/app/api/v1/module_system/role/controller.py b/backend/app/api/v1/module_system/role/controller.py index 24271e61..4a29224c 100644 --- a/backend/app/api/v1/module_system/role/controller.py +++ b/backend/app/api/v1/module_system/role/controller.py @@ -28,7 +28,7 @@ RoleRouter = APIRouter(route_class=OperationLogRoute, prefix="/role", tags=["角 async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: RoleQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:query"])), ) -> JSONResponse: order_by = [{"order": "asc"}] if page.order_by: @@ -42,7 +42,7 @@ async def get_obj_list_controller( @RoleRouter.get("/detail/{id}", summary="查询角色详情", description="查询角色详情") async def get_obj_detail_controller( id: int = Path(..., description="角色ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:query"])), ) -> JSONResponse: result_dict = await RoleService.get_role_detail_service(id=id, auth=auth) logger.info(f"获取角色详情成功 {id}") @@ -52,7 +52,7 @@ async def get_obj_detail_controller( @RoleRouter.post("/create", summary="创建角色", description="创建角色") async def create_obj_controller( data: RoleCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:create"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:create"])), ) -> JSONResponse: result_dict = await RoleService.create_role_service(data=data, auth=auth) logger.info(f"创建角色成功: {result_dict}") @@ -63,7 +63,7 @@ async def create_obj_controller( async def update_obj_controller( data: RoleUpdateSchema, id: int = Path(..., description="角色ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:update"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:update"])), ) -> JSONResponse: result_dict = await RoleService.update_role_service(id=id, data=data, auth=auth) logger.info(f"修改角色成功: {result_dict}") @@ -73,7 +73,7 @@ async def update_obj_controller( @RoleRouter.delete("/delete", summary="删除角色", description="删除角色") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:delete"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:delete"])), ) -> JSONResponse: await RoleService.delete_role_service(ids=ids, auth=auth) logger.info(f"删除角色成功: {ids}") @@ -83,7 +83,7 @@ async def delete_obj_controller( @RoleRouter.patch("/available/setting", summary="批量修改角色状态", description="批量修改角色状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:patch"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:patch"])), ) -> JSONResponse: await RoleService.set_role_available_service(data=data, auth=auth) logger.info(f"批量修改角色状态成功: {data.ids}") @@ -93,7 +93,7 @@ async def batch_set_available_obj_controller( @RoleRouter.patch("/permission/setting", summary="角色授权", description="角色授权") async def set_role_permission_controller( data: RolePermissionSettingSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:permission"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:permission"])), ) -> JSONResponse: await RoleService.set_role_permission_service(data=data, auth=auth) logger.info(f"设置角色权限成功: {data}") @@ -103,7 +103,7 @@ async def set_role_permission_controller( @RoleRouter.post('/export', summary="导出角色", description="导出角色") async def export_obj_list_controller( search: RoleQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:role:export"])), + auth: AuthSchema = Depends(AuthPermission(["system:role:export"])), ) -> StreamingResponse: # 获取全量数据 role_query_result = await RoleService.get_role_list_service(search=search, auth=auth) diff --git a/backend/app/api/v1/module_system/user/controller.py b/backend/app/api/v1/module_system/user/controller.py index e3622273..a2efd5d8 100644 --- a/backend/app/api/v1/module_system/user/controller.py +++ b/backend/app/api/v1/module_system/user/controller.py @@ -103,7 +103,7 @@ async def forget_password_controller( async def get_obj_list_controller( page: PaginationQueryParam = Depends(), search: UserQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:query"])), ) -> JSONResponse: 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) @@ -114,7 +114,7 @@ async def get_obj_list_controller( @UserRouter.get("/detail/{id}", summary="查询用户详情", description="查询用户详情") async def get_obj_detail_controller( id: int = Path(..., description="用户ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:query"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:query"])), ) -> JSONResponse: result_dict = await UserService.get_detail_by_id_service(id=id, auth=auth) logger.info(f"获取用户详情成功 {id}") @@ -124,7 +124,7 @@ async def get_obj_detail_controller( @UserRouter.post("/create", summary="创建用户", description="创建用户") async def create_obj_controller( data: UserCreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:create"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:create"])), ) -> JSONResponse: result_dict = await UserService.create_user_service(data=data, auth=auth) logger.info(f"创建用户成功: {result_dict}") @@ -135,7 +135,7 @@ async def create_obj_controller( async def update_obj_controller( data: UserUpdateSchema, id: int = Path(..., description="用户ID"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:update"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:update"])), ) -> JSONResponse: result_dict = await UserService.update_user_service(id=id, data=data, auth=auth) logger.info(f"修改用户成功: {result_dict}") @@ -145,7 +145,7 @@ async def update_obj_controller( @UserRouter.delete("/delete", summary="删除用户", description="删除用户") async def delete_obj_controller( ids: list[int] = Body(..., description="ID列表"), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:delete"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:delete"])), ) -> JSONResponse: await UserService.delete_user_service(ids=ids, auth=auth) logger.info(f"删除用户成功: {ids}") @@ -155,14 +155,14 @@ async def delete_obj_controller( @UserRouter.patch("/available/setting", summary="批量修改用户状态", description="批量修改用户状态") async def batch_set_available_obj_controller( data: BatchSetAvailable, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:patch"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:patch"])), ) -> JSONResponse: await UserService.set_user_available_service(data=data, auth=auth) logger.info(f"批量修改用户状态成功: {data.ids}") return SuccessResponse(msg="批量修改用户状态成功") -@UserRouter.post('/import/template', summary="获取用户导入模板", description="获取用户导入模板", dependencies=[Depends(AuthPermission(permissions=["system:user:import"]))]) +@UserRouter.post('/import/template', summary="获取用户导入模板", description="获取用户导入模板", dependencies=[Depends(AuthPermission(["system:user:import"]))]) async def export_obj_template_controller()-> StreamingResponse: user_import_template_result = await UserService.get_import_template_user_service() logger.info('获取用户导入模板成功') @@ -181,7 +181,7 @@ async def export_obj_template_controller()-> StreamingResponse: async def export_obj_list_controller( page: PaginationQueryParam = Depends(), search: UserQueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:export"])), + auth: AuthSchema = Depends(AuthPermission(["system:user:export"])), ) -> StreamingResponse: # 获取全量数据 user_list = await UserService.get_user_list_service(auth=auth, search=search, order_by=page.order_by) @@ -200,7 +200,7 @@ async def export_obj_list_controller( @UserRouter.post('/import/data', summary="导入用户", description="导入用户") async def import_obj_list_controller( file: UploadFile, - auth: AuthSchema = Depends(AuthPermission(permissions=["system:user:import"])) + auth: AuthSchema = Depends(AuthPermission(["system:user:import"])) ) -> JSONResponse: batch_import_result = await UserService.batch_import_user_service(file=file, auth=auth, update_support=True) logger.info(f"导入用户成功: {batch_import_result}") diff --git a/backend/app/api/v1/module_system/user/crud.py b/backend/app/api/v1/module_system/user/crud.py index 30fa3fce..72dd9937 100644 --- a/backend/app/api/v1/module_system/user/crud.py +++ b/backend/app/api/v1/module_system/user/crud.py @@ -79,7 +79,7 @@ class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]): Returns: Optional[UserModel]: 更新后的用户信息 """ - return await self.update(id=id, data=UserUpdateSchema(last_login=datetime.now())) + return await self.update(id=id, data={"last_login": datetime.now()}) async def set_available_crud(self, ids: List[int], status: bool) -> None: """ diff --git a/backend/app/common/constant.py b/backend/app/common/constant.py index 372730da..157e1c3d 100644 --- a/backend/app/common/constant.py +++ b/backend/app/common/constant.py @@ -3,235 +3,6 @@ from enum import Enum from app.config.setting import settings -class GenConfig: - def __init__(self): - pass - - # 作者 - author: str = 'Richard' - # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool - packageName: str = '' - # 自动去除表前缀,默认是True - autoRemovePre: bool = True - # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) - tablePrefix: str = 'system_' - # 是否允许生成文件覆盖到本地(自定义路径),默认不允许 - allowOverwrite: bool = False - - -class GenConstants: - """代码生成通用常量""" - - # 单表(增删改查) - TPL_CRUD = "crud" - - # 树表(增删改查) - TPL_TREE = "tree" - - # 主子表(增删改查) - TPL_SUB = "sub" - - # 树编码字段 - TREE_CODE = "treeCode" - - # 树父编码字段 - TREE_PARENT_CODE = "treeParentCode" - - # 树名称字段 - TREE_NAME = "treeName" - - # 上级菜单ID字段 - PARENT_MENU_ID = "parentMenuId" - - # 上级菜单名称字段 - PARENT_MENU_NAME = "parentMenuName" - - # 数据库字符串类型 - TYPE_STRING = ["char", "varchar", "nvarchar", "varchar2"] - - # 数据库文本类型 - TYPE_TEXT = ["tinytext", "text", "mediumtext", "longtext"] - - # 数据库时间类型 - TYPE_DATE_TIME = ["datetime", "time", "date", "timestamp" ] - - # 数据库数字类型 - TYPE_NUMBER = ["tinyint", "smallint", "mediumint", "int", "number", "integer", "bigint", "float", "float", "double", "decimal"] - - # 页面不需要编辑字段 - COLUMN_NAME_NOT_EDIT = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"] - - # 页面不需要显示的列表字段 - COLUMN_NAME_NOT_LIST = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by"] - - # 页面不需要查询字段 - COLUMN_NAME_NOT_QUERY = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by", "update_time", "remark"] - - # Dao基类字段 - DAO_COLUMN_NOT_EDIT = ["create_by", "dept_id", "create_time", "del_flag", "update_time"] - - # Entity基类字段 - BASE_ENTITY = ['id', 'create_time', 'update_time', "create_by", "dept_id", 'del_flag'] - - # Tree基类字段 - TREE_ENTITY = ["parentName", "parentId", "orderNum", "ancestors"] - - # 文本框 - HTML_INPUT = "input" - - # 文本域 - HTML_TEXTAREA = "textarea" - - # 下拉框 - HTML_SELECT = "select" - - # 单选框 - HTML_RADIO = "radio" - - # 复选框 - HTML_CHECKBOX = "checkbox" - - # 日期控件 - HTML_DATETIME = "datetime" - - # 图片上传控件 - HTML_IMAGE_UPLOAD = "imageUpload" - - # 文件上传控件 - HTML_FILE_UPLOAD = "fileUpload" - - # 富文本控件 - HTML_EDITOR = "editor" - - # 模糊查询 - QUERY_LIKE = "LIKE" - - # 相等查询 - QUERY_EQ = "EQ" - - # 需要 - REQUIRE = "1" - - # MySQL -> SQLAlchemy 类型映射 - MYSQL_TO_SQLALCHEMY = { - # Numeric Types - "TINYINT": "SmallInteger", - "SMALLINT": "SmallInteger", - "MEDIUMINT": "Integer", - "INT": "Integer", - "INTEGER": "Integer", - "BIGINT": "BigInteger", - "FLOAT": "Float", - "DOUBLE": "Float", - "DECIMAL": "Numeric", - "NUMERIC": "Numeric", - - # String Types - "CHAR": "String", - "VARCHAR": "String", - "TEXT": "Text", - "TINYTEXT": "Text", - "MEDIUMTEXT": "Text", - "LONGTEXT": "Text", - "BLOB": "LargeBinary", - "TINYBLOB": "LargeBinary", - "MEDIUMBLOB": "LargeBinary", - "LONGBLOB": "LargeBinary", - - # Date and Time Types - "DATE": "Date", - "DATETIME": "DateTime", - "TIMESTAMP": "DateTime", - "TIME": "Time", - "YEAR": "Integer", # MySQL YEAR type is commonly represented as Integer in SQLAlchemy - - # Binary Types - "BINARY": "Binary", - "VARBINARY": "Binary", - - # Enum and Set Types - "ENUM": "Enum", - "SET": "Enum", # Set can be represented using Enum type in SQLAlchemy - - # JSON Types - "JSON": "JSON", # SQLAlchemy supports JSON type from 1.3.0 version - - # Spatial Types (less common in typical usage) - "GEOMETRY": "String", # Can be represented as String or Binary - "POINT": "String", # Represented as String in SQLAlchemy - "LINESTRING": "String", # Represented as String in SQLAlchemy - "POLYGON": "String", # Represented as String in SQLAlchemy - - # Other Types - "BIT": "Boolean", - "BOOL": "Boolean", - "UUID": "String", # UUIDs in SQLAlchemy can be represented as String - "BINARY": "Binary", # MySQL BINARY type corresponds to SQLAlchemy's Binary - } - - MYSQL_TO_PYTHON = { - # 字符串类型 - "VARCHAR": "str", - "CHAR": "str", - "TEXT": "str", - "TINYTEXT": "str", - "MEDIUMTEXT": "str", - "LONGTEXT": "str", - # 数值类型 - "INT": "int", - "TINYINT": "int", - "SMALLINT": "int", - "MEDIUMINT": "int", - "BIGINT": "int", - "FLOAT": "float", - "DOUBLE": "float", - "DECIMAL": "float", - "NUMERIC": "float", - "BIT": "bool", # 位字段,0 或 1 - # 日期和时间类型 - "DATETIME": "datetime", - "TIMESTAMP": "datetime", - "DATE": "datetime.date", - "TIME": "datetime.time", - "YEAR": "int", # 存储年份 - "TINYINT UNSIGNED": "int", # 无符号小整数类型 - # 布尔类型 - "BOOLEAN": "bool", - "BOOL": "bool", # 布尔类型,通常与 BOOLEAN 相同 - # JSON 数据类型 - "JSON": "dict", # JSON 数据存储为字典 - # 二进制类型 - "BLOB": "bytes", - "TINYBLOB": "bytes", - "MEDIUMBLOB": "bytes", - "LONGBLOB": "bytes", - # 枚举和集合类型 - "ENUM": "str", # 枚举类型作为字符串 - "SET": "list", # 集合类型作为列表 - # 时间单位类型 - "DATE": "datetime.date", # 仅日期 - "TIME": "datetime.time", # 仅时间 - # 大文本类型 - "LONGTEXT": "str", - "MEDIUMTEXT": "str", - "TINYTEXT": "str", - # UUID - "UUID": "str", # UUID 一般作为字符串 - # 用于二进制数据 - "BINARY": "bytes", # 固定长度的二进制数据 - "VARBINARY": "bytes", # 可变长度的二进制数据 - # 其他数据类型 - "GEOMETRY": "bytes", # 空间数据类型,通常存储为字节流 - "POINT": "bytes", # 点数据类型 - "LINESTRING": "bytes", # 线数据类型 - "POLYGON": "bytes", # 多边形数据类型 - "MULTIPOINT": "bytes", # 多点数据类型 - "MULTILINESTRING": "bytes", # 多线数据类型 - "MULTIPOLYGON": "bytes", # 多多边形数据类型 - "GEOMETRYCOLLECTION": "bytes", # 几何集合类型 - } - - class RET(Enum): """ 系统返回码枚举 @@ -587,22 +358,43 @@ class GenConstant: DB_TO_PYTHON_TYPE_MAPPING: 数据库类型与python类型映射 """ - TPL_CRUD = 'crud' - TPL_TREE = 'tree' - TPL_SUB = 'sub' - TREE_CODE = 'treeCode' - TREE_PARENT_CODE = 'treeParentCode' - TREE_NAME = 'treeName' - PARENT_MENU_ID = 'parentMenuId' - PARENT_MENU_NAME = 'parentMenuName' + # 单表(增删改查) + TPL_CRUD = "crud" + + # 树表(增删改查) + TPL_TREE = "tree" + + # 主子表(增删改查) + TPL_SUB = "sub" + + # 树编码字段 + TREE_CODE = "treeCode" + + # 树父编码字段 + TREE_PARENT_CODE = "treeParentCode" + + # 树名称字段 + TREE_NAME = "treeName" + + # 上级菜单ID字段 + PARENT_MENU_ID = "parentMenuId" + + # 上级菜单名称字段 + PARENT_MENU_NAME = "parentMenuName" + + # 数据库字符串类型 COLUMNTYPE_STR = ( ['character varying', 'varchar', 'character', 'char'] if settings.DATABASE_TYPE == 'postgresql' else ['char', 'varchar', 'nvarchar', 'varchar2'] ) + + # 数据库文本类型 COLUMNTYPE_TEXT = ( ['text', 'citext'] if settings.DATABASE_TYPE == 'postgresql' else ['tinytext', 'text', 'mediumtext', 'longtext'] ) + + # 数据库时间类型 COLUMNTYPE_TIME = ( [ 'date', @@ -617,6 +409,8 @@ class GenConstant: if settings.DATABASE_TYPE == 'postgresql' else ['datetime', 'time', 'date', 'timestamp'] ) + + # 数据库字空间类型 COLUMNTYPE_GEOMETRY = ( ['point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle'] if settings.DATABASE_TYPE == 'postgresql' @@ -631,6 +425,8 @@ class GenConstant: 'geometrycollection', ] ) + + # 数据库数字类型 COLUMNTYPE_NUMBER = [ 'tinyint', 'smallint', @@ -644,28 +440,74 @@ class GenConstant: 'double', 'decimal', ] + # 页面不需要显示的添加字段 COLUMNNAME_NOT_ADD_SHOW = ['create_by', 'create_time'] + + # 页面不需要显示的编辑字段 COLUMNNAME_NOT_EDIT_SHOW = ['updated_at'] - COLUMNNAME_NOT_EDIT = ['id', 'created_at', 'updated_at', 'del_flag'] - COLUMNNAME_NOT_LIST = ['id', 'created_at', 'updated_at', 'del_flag'] - COLUMNNAME_NOT_QUERY = ['id', 'created_at', 'updated_at', 'del_flag', 'remark'] - BASE_ENTITY = ['createdAt', 'updatedAt', 'remark'] + + # 页面不需要编辑字段 + COLUMNNAME_NOT_EDIT = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"] + + # 页面不需要显示的列表字段 + COLUMNNAME_NOT_LIST = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_time"] + + # 页面不需要查询字段 + COLUMNNAME_NOT_QUERY = ["id", "create_by", "dept_id", "create_time", "del_flag", "update_by", "update_time", "remark"] + + # Crud基类字段 + CRUD_COLUMN_NOT_EDIT = ["create_by", "dept_id", "create_time", "del_flag", "update_time"] + + # 实体基类字段 + BASE_ENTITY = ['id', 'create_time', 'update_time', "create_by", "dept_id", 'del_flag'] + + # Tree基类字段 TREE_ENTITY = ['parentName', 'parentId', 'orderNum', 'ancestors', 'children'] + + # 文本框 HTML_INPUT = 'input' + + # 文本域 HTML_TEXTAREA = 'textarea' + + # 下拉框 HTML_SELECT = 'select' + + # 单选框 HTML_RADIO = 'radio' + + # 复选框 HTML_CHECKBOX = 'checkbox' + + # 日期控件 HTML_DATETIME = 'datetime' + + # 图片上传控件 HTML_IMAGE_UPLOAD = 'imageUpload' + + # 文件上传控件 HTML_FILE_UPLOAD = 'fileUpload' + + # 富文本控件 HTML_EDITOR = 'editor' + + # 高精度计算类型 TYPE_DECIMAL = 'Decimal' + + # 时间类型 TYPE_DATE = ['date', 'time', 'datetime'] + + # 模糊查询 QUERY_LIKE = 'LIKE' + + # 相等查询 QUERY_EQ = 'EQ' + + # 需要 REQUIRE = '1' - DB_TO_SQLALCHEMY_TYPE_MAPPING = ( + + # 数据库类型与sqlalchemy类型映射 + DB_TO_SQLALCHEMY = ( { 'boolean': 'Boolean', 'smallint': 'SmallInteger', @@ -734,6 +576,7 @@ class GenConstant: 'DOUBLE': 'Float', 'DECIMAL': 'DECIMAL', 'BIT': 'Integer', + "NUMERIC": "Numeric", # 日期和时间类型 'DATE': 'Date', 'TIME': 'Time', @@ -767,9 +610,14 @@ class GenConstant: 'MULTILINESTRING': 'Geometry', 'MULTIPOLYGON': 'Geometry', 'GEOMETRYCOLLECTION': 'Geometry', + # 其他类型 + "BOOL": "Boolean", + "UUID": "String", } ) - DB_TO_PYTHON_TYPE_MAPPING = ( + + # 数据库类型与python类型映射 + DB_TO_PYTHON = ( { 'boolean': 'bool', 'smallint': 'int', @@ -836,14 +684,21 @@ class GenConstant: 'BIGINT': 'int', 'FLOAT': 'float', 'DOUBLE': 'float', + "NUMERIC": "float", 'DECIMAL': 'Decimal', 'BIT': 'int', # 日期和时间类型 - 'DATE': 'date', - 'TIME': 'time', - 'DATETIME': 'datetime', - 'TIMESTAMP': 'datetime', + 'DATE': 'datetime.date', + 'TIME': 'datetime.time', + 'DATETIME': 'datetime.datetime', + 'TIMESTAMP': 'datetime.datetime', 'YEAR': 'int', + "TINYINT UNSIGNED": "int", # 无符号小整数类型 + # 布尔类型 + "BOOLEAN": "bool", + "BOOL": "bool", # 布尔类型,通常与 BOOLEAN 相同 + # UUID + "UUID": "str", # UUID 一般作为字符串 # 字符串类型 'CHAR': 'str', 'VARCHAR': 'str', @@ -859,18 +714,18 @@ class GenConstant: 'LONGBLOB': 'bytes', # 枚举和集合类型 'ENUM': 'str', - 'SET': 'str', + 'SET': 'list', # JSON 类型 'JSON': 'dict', # 空间数据类型(通常需要特殊处理) - 'GEOMETRY': 'bytes', - 'POINT': 'bytes', - 'LINESTRING': 'bytes', - 'POLYGON': 'bytes', - 'MULTIPOINT': 'bytes', - 'MULTILINESTRING': 'bytes', - 'MULTIPOLYGON': 'bytes', - 'GEOMETRYCOLLECTION': 'bytes', + "GEOMETRY": "bytes", # 空间数据类型,通常存储为字节流 + "POINT": "bytes", # 点数据类型 + "LINESTRING": "bytes", # 线数据类型 + "POLYGON": "bytes", # 多边形数据类型 + "MULTIPOINT": "bytes", # 多点数据类型 + "MULTILINESTRING": "bytes", # 多线数据类型 + "MULTIPOLYGON": "bytes", # 多多边形数据类型 + "GEOMETRYCOLLECTION": "bytes", # 几何集合类型 } ) diff --git a/backend/app/config/setting.py b/backend/app/config/setting.py index c5f1af59..6225f0f5 100755 --- a/backend/app/config/setting.py +++ b/backend/app/config/setting.py @@ -243,11 +243,11 @@ class Settings(BaseSettings): # ================================================= # # ******************* 代码生成配置 ****************** # # ================================================= # - author: str = 'insistence' - package_name: str = 'module_admin.system' - auto_remove_pre: bool = False - table_prefix: str = 'gen_' - allow_overwrite: bool = False + author: str = 'insistence' # 作者 + package_name: str = 'module_generator.gencode' # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + auto_remove_pre: bool = False # 自动去除表前缀,默认是True + table_prefix: str = 'gen_' # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + allow_overwrite: bool = False # 是否允许生成文件覆盖到本地(自定义路径),默认不允许 GEN_PATH: Path = BASE_DIR.joinpath('app/api/v1/module_generator/gen_backend_code') diff --git a/backend/app/core/base_model.py b/backend/app/core/base_model.py index f075c35e..3ff51d50 100644 --- a/backend/app/core/base_model.py +++ b/backend/app/core/base_model.py @@ -4,15 +4,11 @@ 提供跨数据库兼容的基础模型类和类型装饰器 """ -import re from datetime import datetime -from typing import Literal, Optional, Dict, Any, Union - +from typing import Optional from sqlalchemy import Integer, DateTime, Text from sqlalchemy.ext.asyncio import AsyncAttrs from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, declared_attr, mapped_column -from sqlalchemy.engine.row import Row -from sqlalchemy.orm.collections import InstrumentedList class MappedBase(AsyncAttrs, DeclarativeBase): @@ -51,7 +47,7 @@ class ModelMixin(MappedBase): description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, default=None, comment="备注/描述") created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, comment='创建时间') updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment='更新时间') - + class CreatorMixin(ModelMixin): """ @@ -72,121 +68,3 @@ class CreatorMixin(ModelMixin): viewonly=True, uselist=False # 明确指定返回单个对象 ) - - -class SqlalchemyUtil: - """ - sqlalchemy工具类 - """ - - @classmethod - def base_to_dict( - cls, obj: Union[MappedBase, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' - ) -> dict[str, Any] | dict[Any, Any]: - """ - 将sqlalchemy模型对象转换为字典 - - :param obj: sqlalchemy模型对象或普通字典 - :param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case' - :return: 字典结果 - """ - if isinstance(obj, MappedBase): - base_dict = obj.__dict__.copy() - base_dict.pop('_sa_instance_state', None) - for name, value in base_dict.items(): - if isinstance(value, InstrumentedList): - base_dict[name] = cls.serialize_result(value, 'snake_to_camel') - elif isinstance(obj, dict): - base_dict = obj.copy() - if transform_case == 'snake_to_camel': - return {CamelCaseUtil.snake_to_camel(k): v for k, v in base_dict.items()} - elif transform_case == 'camel_to_snake': - return {SnakeCaseUtil.camel_to_snake(k): v for k, v in base_dict.items()} - - return base_dict - - @classmethod - def serialize_result( - cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' - ) -> Any: - """ - 将sqlalchemy查询结果序列化 - - :param result: sqlalchemy查询结果 - :param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case' - :return: 序列化结果 - """ - if isinstance(result, (MappedBase, dict)): - return cls.base_to_dict(result, transform_case) - elif isinstance(result, list): - return [cls.serialize_result(row, transform_case) for row in result] - elif isinstance(result, Row): - if all([isinstance(row, MappedBase) for row in result]): - return [cls.base_to_dict(row, transform_case) for row in result] - elif any([isinstance(row, MappedBase) for row in result]): - return [cls.serialize_result(row, transform_case) for row in result] - else: - result_dict = result._asdict() - if transform_case == 'snake_to_camel': - return {CamelCaseUtil.snake_to_camel(k): v for k, v in result_dict.items()} - elif transform_case == 'camel_to_snake': - return {SnakeCaseUtil.camel_to_snake(k): v for k, v in result_dict.items()} - return result_dict - return result - - -class CamelCaseUtil: - """ - 下划线形式(snake_case)转小驼峰形式(camelCase)工具方法 - """ - - @classmethod - def snake_to_camel(cls, snake_str: str) -> str: - """ - 下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase) - - :param snake_str: 下划线形式字符串 - :return: 小驼峰形式字符串 - """ - # 分割字符串 - words = snake_str.split('_') - # 小驼峰命名,第一个词首字母小写,其余词首字母大写 - return words[0] + ''.join(word.capitalize() for word in words[1:]) - - @classmethod - def transform_result(cls, result: Any) -> Any: - """ - 针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法 - - :param result: 输入数据 - :return: 小驼峰形式结果 - """ - return SqlalchemyUtil.serialize_result(result=result, transform_case='snake_to_camel') - - -class SnakeCaseUtil: - """ - 小驼峰形式(camelCase)转下划线形式(snake_case)工具方法 - """ - - @classmethod - def camel_to_snake(cls, camel_str: str) -> str: - """ - 小驼峰形式字符串(camelCase)转换为下划线形式字符串(snake_case) - - :param camel_str: 小驼峰形式字符串 - :return: 下划线形式字符串 - """ - # 在大写字母前添加一个下划线,然后将整个字符串转为小写 - words = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str) - return re.sub('([a-z0-9])([A-Z])', r'\1_\2', words).lower() - - @classmethod - def transform_result(cls, result: Any) -> Any: - """ - 针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法 - - :param result: 输入数据 - :return: 小驼峰形式结果 - """ - return SqlalchemyUtil.serialize_result(result=result, transform_case='camel_to_snake') \ No newline at end of file diff --git a/backend/app/core/base_schema.py b/backend/app/core/base_schema.py index f8f1d0b8..53d6fe7b 100644 --- a/backend/app/core/base_schema.py +++ b/backend/app/core/base_schema.py @@ -26,10 +26,10 @@ class BaseSchema(BaseModel): """通用输出模型,包含基础字段和审计字段""" model_config = ConfigDict(from_attributes=True) - id: int = Field(description="主键ID") + id: Optional[int] = Field(default=None, description="主键ID") description: Optional[str] = Field(default=None, description="描述") - created_at: DateTimeStr = Field(description="创建时间") - updated_at: DateTimeStr = Field(description="更新时间") + created_at: Optional[DateTimeStr] = Field(default=None, description="创建时间") + updated_at: Optional[DateTimeStr] = Field(default=None, description="更新时间") creator_id: Optional[int] = Field(default=None, description="创建人ID") creator: Optional[UserInfoSchema] = Field(default=None, description="创建人信息") diff --git a/backend/app/core/database.py b/backend/app/core/database.py index b16ceea0..6960d509 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -77,7 +77,7 @@ async def redis_connect(app: FastAPI, status: bool) -> Redis | None: ) app.state.redis = rd if await rd.ping(): - logger.info("Redis连接成功...") + logger.info("✅️ Redis连接成功...") return rd raise CustomException(msg="Redis连接失败") except exceptions.AuthenticationError as e: @@ -113,4 +113,4 @@ async def mongodb_connect(app: FastAPI, status: bool) -> AsyncIOMotorClient | No raise ValueError(f"MongoDB连接失败: {e}") else: app.state.mongo_client.close() - logger.info("MongoDB连接已关闭") \ No newline at end of file + logger.info("❌️ MongoDB连接已关闭") \ No newline at end of file diff --git a/backend/app/plugin/init_app.py b/backend/app/plugin/init_app.py index 9473ed23..954de81e 100644 --- a/backend/app/plugin/init_app.py +++ b/backend/app/plugin/init_app.py @@ -30,17 +30,17 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: logger.info(settings.BANNER + '\n' + f'{settings.TITLE} 服务开始启动...') await InitializeData().init_db() - logger.info(f"初始化 {settings.DATABASE_TYPE} 数据库初始化完成...") + logger.info(f"✅️ 初始化 {settings.DATABASE_TYPE} 数据库初始化完成...") await import_modules_async(modules=settings.EVENT_LIST, desc="全局事件", app=app, status=True) - logger.info("初始化全局事件完成...") + logger.info("✅️ 初始化全局事件完成...") await ParamsService().init_config_service(redis=app.state.redis) - logger.info("初始化Redis系统配置完成...") + logger.info("✅️ 初始化Redis系统配置完成...") await DictDataService().init_dict_service(redis=app.state.redis) - logger.info('初始化Redis数据字典完成...') + logger.info('✅️ 初始化Redis数据字典完成...') await SchedulerUtil.init_system_scheduler() - logger.info('初始化定时任务完成...') + logger.info('✅️ 初始化定时任务完成...') - logger.info(f'{settings.TITLE} 服务成功启动...') + logger.info(f'✅️ {settings.TITLE} 服务成功启动...') yield diff --git a/backend/app/scripts/initialize.py b/backend/app/scripts/initialize.py index 0433da05..ec474ff4 100644 --- a/backend/app/scripts/initialize.py +++ b/backend/app/scripts/initialize.py @@ -58,9 +58,9 @@ class InitializeData: # 使用引擎创建所有表 async with async_engine.begin() as conn: await conn.run_sync(MappedBase.metadata.create_all) - logger.info("数据库表结构初始化完成") + logger.info("✅️ 数据库表结构初始化完成") except Exception as e: - logger.error(f"数据库表结构初始化失败: {str(e)}") + logger.error(f"❌️ 数据库表结构初始化失败: {str(e)}") raise async def __init_data(self, db: AsyncSession) -> None: @@ -72,12 +72,12 @@ class InitializeData: count_result = await db.execute(select(func.count()).select_from(model)) existing_count = count_result.scalar() if existing_count and existing_count > 0: - logger.warning(f"跳过 {table_name} 表数据初始化(表已存在 {existing_count} 条记录)") + logger.warning(f"⚠️ 跳过 {table_name} 表数据初始化(表已存在 {existing_count} 条记录)") continue data = await self.__get_data(table_name) if not data: - logger.warning(f"跳过 {table_name} 表,无初始化数据") + logger.warning(f"⚠️ 跳过 {table_name} 表,无初始化数据") continue try: @@ -91,10 +91,10 @@ class InitializeData: objs = [model(**item) for item in data] db.add_all(objs) await db.flush() - logger.info(f"已向 {table_name} 表写入初始化数据") + logger.info(f"✅️ 已向 {table_name} 表写入初始化数据") except Exception as e: - logger.error(f"初始化 {table_name} 表数据失败: {str(e)}") + logger.error(f"❌️ 初始化 {table_name} 表数据失败: {str(e)}") raise def __create_objects_with_children(self, data: List[Dict], model_class) -> List: @@ -129,10 +129,10 @@ class InitializeData: with open(json_path, 'r', encoding='utf-8') as f: return json.loads(f.read()) except json.JSONDecodeError as e: - logger.error(f"解析 {json_path} 失败: {str(e)}") + logger.error(f"❌️ 解析 {json_path} 失败: {str(e)}") raise except Exception as e: - logger.error(f"读取 {json_path} 失败: {str(e)}") + logger.error(f"❌️ 读取 {json_path} 失败: {str(e)}") raise async def init_db(self) -> None: diff --git a/backend/app/utils/common_util.py b/backend/app/utils/common_util.py index 8a7b3b7c..93349ec3 100644 --- a/backend/app/utils/common_util.py +++ b/backend/app/utils/common_util.py @@ -1,16 +1,20 @@ # -*- coding: utf-8 -*- -from pathlib import Path import importlib -from types import ModuleType +import re import uuid -from typing import Any, Generator, List, Dict, Sequence, Optional +from pathlib import Path +from typing import Any, Generator, List, Dict, Literal, Sequence, Optional, Union +from sqlalchemy.engine.row import Row from sqlalchemy.orm import DeclarativeBase +from sqlalchemy.orm.collections import InstrumentedList from app.config import setting +from app.core.base_model import MappedBase from app.core.logger import logger from app.core.exceptions import CustomException + def worship(): print(""" ______ _ _ @@ -23,6 +27,7 @@ def worship(): |_| """) + def import_module(module: str, desc: str) -> Any: """ 动态导入模块 @@ -35,10 +40,10 @@ def import_module(module: str, desc: str) -> Any: module = importlib.import_module(module_path) return getattr(module, module_class) except ModuleNotFoundError: - logger.error(f"导入{desc}失败,未找到模块:{module}") + logger.error(f"❗️ 导入{desc}失败,未找到模块:{module}") raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}") except AttributeError: - logger.error(f"导入{desc}失败,未找到模块方法:{module}") + logger.error(f"❗ ️导入{desc}失败,未找到模块方法:{module}") raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}") @@ -58,10 +63,10 @@ async def import_modules_async(modules: list, desc: str, **kwargs): module_obj = importlib.import_module(module_path) await getattr(module_obj, module_name)(**kwargs) except ModuleNotFoundError: - logger.error(f"导入{desc}失败,未找到模块:{module}") + logger.error(f"❌️ 导入{desc}失败,未找到模块:{module}") raise ModuleNotFoundError(f"导入{desc}失败,未找到模块:{module}") except AttributeError: - logger.error(f"导入{desc}失败,未找到模块方法:{module}") + logger.error(f"❌️ 导入{desc}失败,未找到模块方法:{module}") raise AttributeError(f"导入{desc}失败,未找到模块方法:{module}") @@ -79,11 +84,7 @@ def get_parent_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, int]: 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]: +def get_parent_recursion(id: int, id_map: Dict[int, int], ids: Optional[List[int]] = None) -> List[int]: """ 递归获取所有父级ID :param id: 当前ID @@ -115,11 +116,7 @@ def get_child_id_map(model_list: Sequence[DeclarativeBase]) -> Dict[int, List[in return data_map -def get_child_recursion( - id: int, - id_map: Dict[int, List[int]], - ids: Optional[List[int]] = None -) -> List[int]: +def get_child_recursion(id: int, id_map: Dict[int, List[int]], ids: Optional[List[int]] = None) -> List[int]: """ 递归获取所有子级ID :param id: 当前ID @@ -224,3 +221,121 @@ def get_filepath_from_url(url: str) -> Path: filepath = setting.settings.STATIC_ROOT.joinpath(task_path, task_id, file_name) return filepath + + +class SqlalchemyUtil: + """ + sqlalchemy工具类 + """ + + @classmethod + def base_to_dict( + cls, obj: Union[MappedBase, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' + ) -> dict[str, Any] | dict[Any, Any]: + """ + 将sqlalchemy模型对象转换为字典 + + :param obj: sqlalchemy模型对象或普通字典 + :param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case' + :return: 字典结果 + """ + if isinstance(obj, MappedBase): + base_dict = obj.__dict__.copy() + base_dict.pop('_sa_instance_state', None) + for name, value in base_dict.items(): + if isinstance(value, InstrumentedList): + base_dict[name] = cls.serialize_result(value, 'snake_to_camel') + elif isinstance(obj, dict): + base_dict = obj.copy() + if transform_case == 'snake_to_camel': + return {CamelCaseUtil.snake_to_camel(k): v for k, v in base_dict.items()} + elif transform_case == 'camel_to_snake': + return {SnakeCaseUtil.camel_to_snake(k): v for k, v in base_dict.items()} + + return base_dict + + @classmethod + def serialize_result( + cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' + ) -> Any: + """ + 将sqlalchemy查询结果序列化 + + :param result: sqlalchemy查询结果 + :param transform_case: 转换得到的结果形式,可选的有'no_case'(不转换)、'snake_to_camel'(下划线转小驼峰)、'camel_to_snake'(小驼峰转下划线),默认为'no_case' + :return: 序列化结果 + """ + if isinstance(result, (MappedBase, dict)): + return cls.base_to_dict(result, transform_case) + elif isinstance(result, list): + return [cls.serialize_result(row, transform_case) for row in result] + elif isinstance(result, Row): + if all([isinstance(row, MappedBase) for row in result]): + return [cls.base_to_dict(row, transform_case) for row in result] + elif any([isinstance(row, MappedBase) for row in result]): + return [cls.serialize_result(row, transform_case) for row in result] + else: + result_dict = result._asdict() + if transform_case == 'snake_to_camel': + return {CamelCaseUtil.snake_to_camel(k): v for k, v in result_dict.items()} + elif transform_case == 'camel_to_snake': + return {SnakeCaseUtil.camel_to_snake(k): v for k, v in result_dict.items()} + return result_dict + return result + + +class CamelCaseUtil: + """ + 下划线形式(snake_case)转小驼峰形式(camelCase)工具方法 + """ + + @classmethod + def snake_to_camel(cls, snake_str: str) -> str: + """ + 下划线形式字符串(snake_case)转换为小驼峰形式字符串(camelCase) + + :param snake_str: 下划线形式字符串 + :return: 小驼峰形式字符串 + """ + # 分割字符串 + words = snake_str.split('_') + # 小驼峰命名,第一个词首字母小写,其余词首字母大写 + return words[0] + ''.join(word.capitalize() for word in words[1:]) + + @classmethod + def transform_result(cls, result: Any) -> Any: + """ + 针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法 + + :param result: 输入数据 + :return: 小驼峰形式结果 + """ + return SqlalchemyUtil.serialize_result(result=result, transform_case='snake_to_camel') + + +class SnakeCaseUtil: + """ + 小驼峰形式(camelCase)转下划线形式(snake_case)工具方法 + """ + + @classmethod + def camel_to_snake(cls, camel_str: str) -> str: + """ + 小驼峰形式字符串(camelCase)转换为下划线形式字符串(snake_case) + + :param camel_str: 小驼峰形式字符串 + :return: 下划线形式字符串 + """ + # 在大写字母前添加一个下划线,然后将整个字符串转为小写 + words = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', words).lower() + + @classmethod + def transform_result(cls, result: Any) -> Any: + """ + 针对不同类型将下划线形式(snake_case)批量转换为小驼峰形式(camelCase)方法 + + :param result: 输入数据 + :return: 小驼峰形式结果 + """ + return SqlalchemyUtil.serialize_result(result=result, transform_case='camel_to_snake') \ No newline at end of file diff --git a/backend/app/api/v1/module_generator/gencode/gen_util.py b/backend/app/utils/gen_util.py similarity index 84% rename from backend/app/api/v1/module_generator/gencode/gen_util.py rename to backend/app/utils/gen_util.py index e2a6cac1..a327a1ca 100644 --- a/backend/app/api/v1/module_generator/gencode/gen_util.py +++ b/backend/app/utils/gen_util.py @@ -1,11 +1,22 @@ +# -*- coding: utf-8 -*- + import re -from datetime import datetime from typing import List from app.common.constant import GenConstant from app.config.setting import settings -from app.api.v1.module_generator.gencode.schema import GenTableColumnOutSchema as GenTableColumnSchema, GenTableOutSchema as GenTableSchema -from .string_util import StringUtil +from app.utils.string_util import StringUtil +from app.api.v1.module_generator.gencode.schema import ( + GenTableOptionModel, + GenDBTableSchema, + GenTableBaseSchema, + GenTableSchema, + GenTableOutSchema, + GenTableDeleteSchema, + GenTableColumnSchema, + GenTableColumnOutSchema, + GenTableColumnDeleteSchema +) class GenUtils: @@ -20,14 +31,13 @@ class GenUtils: param oper_name: 操作人 :return: """ - gen_table.class_name = cls.convert_class_name(gen_table.table_name) - gen_table.package_name = settings.package_name - gen_table.module_name = cls.get_module_name(settings.package_name) - gen_table.business_name = cls.get_business_name(gen_table.table_name) - gen_table.function_name = cls.replace_text(gen_table.table_comment) + gen_table.class_name = cls.convert_class_name(gen_table.table_name or "") + gen_table.package_name = cls.get_package_name(gen_table.table_name or "") + gen_table.module_name = cls.get_module_name(settings.package_name or "") + gen_table.business_name = cls.get_business_name(gen_table.table_name or "") + gen_table.function_name = cls.replace_text(gen_table.table_comment or "") gen_table.function_author = settings.author - gen_table.created_at = datetime.now() - gen_table.updated_at = datetime.now() + @classmethod def init_column_field(cls, column: GenTableColumnSchema, table: GenTableSchema) -> None: @@ -38,22 +48,20 @@ class GenUtils: param table: 业务表对象 :return: """ - data_type = cls.get_db_type(column.column_type) - column_name = column.column_name - column.table_id = table.id + data_type = cls.get_db_type(column.column_type or "") + column_name = column.column_name or "" + column.table_id = table.table_id # 设置Python字段名 - column.python_field = cls.to_camel_case(column_name) + column.python_field = column_name # 设置默认类型 - column.python_type = StringUtil.get_mapping_value_by_key_ignore_case( - GenConstant.DB_TO_PYTHON_TYPE_MAPPING, data_type - ) + column.python_type = GenConstant.DB_TO_PYTHON.get(data_type.upper(), "Any") column.query_type = GenConstant.QUERY_EQ if cls.arrays_contains(GenConstant.COLUMNTYPE_STR, data_type) or cls.arrays_contains( GenConstant.COLUMNTYPE_TEXT, data_type ): # 字符串长度超过500设置为文本域 - column_length = cls.get_column_length(column.column_type) + column_length = cls.get_column_length(column.column_type or "") html_type = ( GenConstant.HTML_TEXTAREA if column_length >= 500 or cls.arrays_contains(GenConstant.COLUMNTYPE_TEXT, data_type) @@ -69,13 +77,13 @@ class GenUtils: column.is_insert = GenConstant.REQUIRE # 编辑字段 - if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.pk: + if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.is_pk == '1': column.is_edit = GenConstant.REQUIRE # 列表字段 - if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.pk: + if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.is_pk == '1': column.is_list = GenConstant.REQUIRE # 查询字段 - if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.pk: + if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.is_pk == '1': column.is_query = GenConstant.REQUIRE # 查询字段类型 @@ -96,11 +104,8 @@ class GenUtils: # 内容字段设置富文本控件 elif column_name.lower().endswith('content'): column.html_type = GenConstant.HTML_EDITOR - - column.create_by = table.create_by - column.create_time = datetime.now() - column.update_by = table.update_by - column.update_time = datetime.now() + else: + column.html_type = GenConstant.HTML_INPUT @classmethod def arrays_contains(cls, arr: List[str], target_value: str) -> bool: @@ -123,6 +128,11 @@ class GenUtils: """ return package_name.split('.')[-1] + @classmethod + def get_package_name(cls, table_name: str) -> str: + """获取包名""" + return settings.package_name # 可配置的包名 + @classmethod def get_business_name(cls, table_name: str) -> str: """ @@ -170,7 +180,7 @@ class GenUtils: param text: 需要被替换的字符串 :return: 替换后的字符串 """ - return re.sub(r'(?:表|若依)', '', text) + return re.sub(r'(?:表|测试)', '', text) @classmethod def get_db_type(cls, column_type: str) -> str: diff --git a/backend/app/api/v1/module_generator/gencode/ip_local_util.py b/backend/app/utils/ip_local_util.py similarity index 100% rename from backend/app/api/v1/module_generator/gencode/ip_local_util.py rename to backend/app/utils/ip_local_util.py diff --git a/backend/app/api/v1/module_generator/gencode/template_util.py b/backend/app/utils/jinja2_template_util.py similarity index 80% rename from backend/app/api/v1/module_generator/gencode/template_util.py rename to backend/app/utils/jinja2_template_util.py index 00067ee2..2f85bc9c 100644 --- a/backend/app/api/v1/module_generator/gencode/template_util.py +++ b/backend/app/utils/jinja2_template_util.py @@ -1,20 +1,20 @@ # -*- coding:utf-8 -*- import json -import os from datetime import datetime -from jinja2 import Environment, FileSystemLoader -from typing import Dict, List, Set +from jinja2.environment import Environment +from jinja2 import Environment, FileSystemLoader, select_autoescape, Template +from typing import Dict, List, Any, Set + from app.common.constant import GenConstant from app.config.setting import settings -from app.api.v1.module_generator.gencode.schema import GenTableOutSchema as GenTableSchema, GenTableColumnOutSchema as GenTableColumnSchema -from app.core.base_model import CamelCaseUtil, SnakeCaseUtil from app.core.exceptions import CustomException -from .string_util import StringUtil -from . import jinja2_util +from app.utils.common_util import CamelCaseUtil, SnakeCaseUtil +from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableColumnOutSchema +from app.utils.string_util import StringUtil -class TemplateInitializer: +class Jinja2TemplateInitializerUtil: """ 模板引擎初始化类 """ @@ -29,9 +29,9 @@ class TemplateInitializer: try: # 修复模板路径,使用正确的相对路径 - template_dir = settings.TEMPLATE_DIR env = Environment( - loader=FileSystemLoader(template_dir), + loader=FileSystemLoader(settings.TEMPLATE_DIR), + autoescape=select_autoescape(['html', 'xml']), keep_trailing_newline=True, trim_blocks=True, lstrip_blocks=True, @@ -40,10 +40,7 @@ class TemplateInitializer: { 'camel_to_snake': SnakeCaseUtil.camel_to_snake, 'snake_to_camel': CamelCaseUtil.snake_to_camel, - 'snake_to_pascal_case': jinja2_util.snake_to_pascal_case, - 'is_base_column': jinja2_util.is_base_column, - 'get_column_options': jinja2_util.get_column_options, - 'get_sqlalchemy_type': jinja2_util.get_sqlalchemy_type, + 'get_sqlalchemy_type': Jinja2TemplateUtil.get_sqlalchemy_type, } ) return env @@ -51,18 +48,34 @@ class TemplateInitializer: raise RuntimeError(f'初始化Jinja2模板引擎失败: {e}') -class TemplateUtils: +class Jinja2TemplateUtil: """ - 模板工具类 + 模板处理工具类 """ # 项目路径 FRONTEND_PROJECT_PATH = 'frontend' BACKEND_PROJECT_PATH = 'backend' - DEFAULT_PARENT_MENU_ID = '3' - + # 默认上级菜单,系统工具 + DEFAULT_PARENT_MENU_ID = "3" + + # 环境对象 + _env = None + @classmethod - def prepare_context(cls, gen_table: GenTableSchema): + def get_env(cls) -> Environment: + """获取模板环境对象""" + if cls._env is None: + cls._env = Jinja2TemplateInitializerUtil.init_jinja2() + return cls._env + + @classmethod + def get_template(cls, template_path: str) -> Template: + """获取模板""" + return cls.get_env().get_template(template_path) + + @classmethod + def prepare_context(cls, gen_table: GenTableOutSchema) -> dict[str, Any]: """ 准备模板变量 @@ -77,7 +90,7 @@ class TemplateUtils: package_name = gen_table.package_name tpl_category = gen_table.tpl_category function_name = gen_table.function_name - + context = { 'tplCategory': tpl_category, 'tableName': gen_table.table_name, @@ -111,9 +124,9 @@ class TemplateUtils: cls.set_sub_context(context, gen_table) return context - + @classmethod - def set_menu_context(cls, context: Dict, gen_table: GenTableSchema): + def set_menu_context(cls, context: Dict, gen_table: GenTableOutSchema): """ 设置菜单上下文 @@ -122,12 +135,11 @@ class TemplateUtils: :return: 新的模板上下文字典 """ options = gen_table.options - if options: - params_obj = json.loads(options) - context['parentMenuId'] = cls.get_parent_menu_id(params_obj) - + params_obj = json.loads(options) + context['parentMenuId'] = cls.get_parent_menu_id(params_obj) + @classmethod - def set_tree_context(cls, context: Dict, gen_table: GenTableSchema): + def set_tree_context(cls, context: Dict, gen_table: GenTableOutSchema): """ 设置树形结构上下文 @@ -136,15 +148,14 @@ class TemplateUtils: :return: 新的模板上下文字典 """ options = gen_table.options - if options: - params_obj = json.loads(options) - context['treeCode'] = cls.get_tree_code(params_obj) - context['treeParentCode'] = cls.get_tree_parent_code(params_obj) - context['treeName'] = cls.get_tree_name(params_obj) - context['expandColumn'] = cls.get_expand_column(gen_table) - + params_obj = json.loads(options) + context['treeCode'] = cls.get_tree_code(params_obj) + context['treeParentCode'] = cls.get_tree_parent_code(params_obj) + context['treeName'] = cls.get_tree_name(params_obj) + context['expandColumn'] = cls.get_expand_column(gen_table) + @classmethod - def set_sub_context(cls, context: Dict, gen_table: GenTableSchema): + def set_sub_context(cls, context: Dict, gen_table: GenTableOutSchema): """ 设置子表上下文 @@ -155,12 +166,8 @@ class TemplateUtils: sub_table = gen_table.sub_table sub_table_name = gen_table.sub_table_name sub_table_fk_name = gen_table.sub_table_fk_name - # 修复类型检查问题,确保sub_table存在 - if sub_table: - sub_class_name = sub_table.class_name or "" - else: - sub_class_name = "" - sub_table_fk_class_name = StringUtil.convert_to_camel_case(sub_table_fk_name or "") + sub_class_name = sub_table.class_name + sub_table_fk_class_name = StringUtil.convert_to_camel_case(sub_table_fk_name) context['subTable'] = sub_table context['subTableName'] = sub_table_name context['subTableFkName'] = sub_table_fk_name @@ -182,13 +189,17 @@ class TemplateUtils: if tpl_web_type == 'element-plus': use_web_type = 'vue/v3' templates = [ + # Python相关模板 'python/controller.py.j2', - 'python/crud.py.j2', - 'python/model.py.j2', - 'python/schema.py.j2', 'python/service.py.j2', + 'python/crud.py.j2', + 'python/schema.py.j2', + 'python/param.py.j2', + 'python/model.py.j2', + # Vue相关模板 + 'ts/api.ts.j2', + # SQL脚本模板 'sql/sql.j2', - 'vue/api.js.j2', ] if tpl_category == GenConstant.TPL_CRUD: templates.append(f'{use_web_type}/index.vue.j2') @@ -198,9 +209,9 @@ class TemplateUtils: 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: GenTableSchema): + def get_file_name(cls, template: List[str], gen_table: GenTableOutSchema): """ 根据模板生成文件名 @@ -216,25 +227,25 @@ class TemplateUtils: python_path = f'{cls.BACKEND_PROJECT_PATH}/{package_name.replace(".", "/")}' if 'controller.py.j2' in template: - return f'{python_path}/{business_name}_controller.py' + return f'{python_path}/controller/{business_name}_controller.py' elif 'crud.py.j2' in template: - return f'{python_path}/{business_name}_crud.py' + return f'{python_path}/crud/{business_name}_crud.py' elif 'model.py.j2' in template: - return f'{python_path}/{business_name}_model.py' + return f'{python_path}/entity/model/{business_name}_model.py' elif 'service.py.j2' in template: - return f'{python_path}/{business_name}_service.py' + return f'{python_path}/service/{business_name}_service.py' elif 'schema.py.j2' in template: - return f'{python_path}/{business_name}_schema.py' + return f'{python_path}/entity/schema/{business_name}_schema.py' elif 'sql.j2' in template: return f'{cls.BACKEND_PROJECT_PATH}/sql/{business_name}_menu.sql' - elif 'api.js.j2' in template: - return f'{vue_path}/api/{module_name}/{business_name}.js' + elif 'api.ts.j2' in template: + return f'{vue_path}/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 '' @classmethod - def get_package_prefix(cls, package_name: str): + def get_package_prefix(cls, package_name: str) -> str: """ 获取包前缀 @@ -244,7 +255,7 @@ class TemplateUtils: return package_name[: package_name.rfind('.')] @classmethod - def get_vo_import_list(cls, gen_table: GenTableSchema): + def get_vo_import_list(cls, gen_table: GenTableOutSchema): """ 获取vo模板导入包列表 @@ -258,8 +269,7 @@ class TemplateUtils: import_list.add(f'from datetime import {column.python_type}') elif column.python_type == GenConstant.TYPE_DECIMAL: import_list.add('from decimal import Decimal') - # 修复类型检查问题,确保sub_table存在且有columns属性 - if gen_table.sub and gen_table.sub_table: + if gen_table.sub: sub_columns = gen_table.sub_table.columns or [] for sub_column in sub_columns: if sub_column.python_type in GenConstant.TYPE_DATE: @@ -267,9 +277,9 @@ class TemplateUtils: elif sub_column.python_type == GenConstant.TYPE_DECIMAL: import_list.add('from decimal import Decimal') return cls.merge_same_imports(list(import_list), 'from datetime import') - + @classmethod - def get_do_import_list(cls, gen_table: GenTableSchema): + def get_do_import_list(cls, gen_table: GenTableOutSchema) -> List[str]: """ 获取do模板导入包列表 @@ -284,16 +294,15 @@ class TemplateUtils: if data_type in GenConstant.COLUMNTYPE_GEOMETRY: import_list.add('from geoalchemy2 import Geometry') import_list.add( - f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, data_type)}' + f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}' ) - # 修复类型检查问题,确保sub_table存在且有columns属性 - if gen_table.sub and gen_table.sub_table: + if gen_table.sub: import_list.add('from sqlalchemy import ForeignKey') sub_columns = gen_table.sub_table.columns or [] for sub_column in sub_columns: data_type = cls.get_db_type(sub_column.column_type) import_list.add( - f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, data_type)}' + f'from sqlalchemy import {StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_SQLALCHEMY, data_type)}' ) return cls.merge_same_imports(list(import_list), 'from sqlalchemy import') @@ -308,7 +317,7 @@ class TemplateUtils: if '(' in column_type: return column_type.split('(')[0] return column_type - + @classmethod def merge_same_imports(cls, imports: List[str], import_start: str) -> List[str]: """ @@ -332,9 +341,9 @@ class TemplateUtils: merged_imports.append(merged_datetime_import) return merged_imports - + @classmethod - def get_dicts(cls, gen_table: GenTableSchema): + def get_dicts(cls, gen_table: GenTableOutSchema): """ 获取字典列表 @@ -345,12 +354,11 @@ class TemplateUtils: dicts = set() cls.add_dicts(dicts, columns) if gen_table.sub_table is not None: - sub_columns = gen_table.sub_table.columns or [] - cls.add_dicts(dicts, sub_columns) + cls.add_dicts(dicts, gen_table.sub_table.columns) return ', '.join(dicts) @classmethod - def add_dicts(cls, dicts: Set[str], columns: List): + def add_dicts(cls, dicts: Set[str], columns: List[GenTableColumnOutSchema]): """ 添加字典列表 @@ -369,7 +377,7 @@ class TemplateUtils: dicts.add(f"'{column.dict_type}'") @classmethod - def get_permission_prefix(cls, module_name: str, business_name: str): + def get_permission_prefix(cls, module_name: str | None, business_name: str | None) -> str: """ 获取权限前缀 @@ -378,7 +386,7 @@ class TemplateUtils: :return: 权限前缀 """ return f'{module_name}:{business_name}' - + @classmethod def get_parent_menu_id(cls, params_obj: Dict): """ @@ -390,7 +398,7 @@ class TemplateUtils: if params_obj and params_obj.get(GenConstant.PARENT_MENU_ID): return params_obj.get(GenConstant.PARENT_MENU_ID) return cls.DEFAULT_PARENT_MENU_ID - + @classmethod def get_tree_code(cls, params_obj: Dict): """ @@ -400,9 +408,9 @@ class TemplateUtils: :return: 树编码 """ if GenConstant.TREE_CODE in params_obj: - return cls.to_camel_case(params_obj.get(GenConstant.TREE_CODE, 'treeCode')) + return cls.to_camel_case(params_obj.get(GenConstant.TREE_CODE)) return '' - + @classmethod def get_tree_parent_code(cls, params_obj: Dict): """ @@ -412,9 +420,9 @@ class TemplateUtils: :return: 树父编码 """ if GenConstant.TREE_PARENT_CODE in params_obj: - return cls.to_camel_case(params_obj.get(GenConstant.TREE_PARENT_CODE, 'treeParentCode')) + return cls.to_camel_case(params_obj.get(GenConstant.TREE_PARENT_CODE)) return '' - + @classmethod def get_tree_name(cls, params_obj: Dict): """ @@ -424,11 +432,11 @@ class TemplateUtils: :return: 树名称 """ if GenConstant.TREE_NAME in params_obj: - return cls.to_camel_case(params_obj.get(GenConstant.TREE_NAME, 'treeName')) + return cls.to_camel_case(params_obj.get(GenConstant.TREE_NAME)) return '' - + @classmethod - def get_expand_column(cls, gen_table: GenTableSchema): + def get_expand_column(cls, gen_table: GenTableOutSchema): """ 获取展开列 @@ -436,18 +444,16 @@ class TemplateUtils: :return: 展开列 """ options = gen_table.options - if not options: - return 0 params_obj = json.loads(options) tree_name = params_obj.get(GenConstant.TREE_NAME) num = 0 - for column in gen_table.columns or []: + for column in gen_table.columns: if column.list: num += 1 if column.column_name == tree_name: break return num - + @classmethod def to_camel_case(cls, text: str) -> str: """ @@ -458,7 +464,7 @@ class TemplateUtils: """ parts = text.split('_') return parts[0] + ''.join(word.capitalize() for word in parts[1:]) - + @classmethod def get_sqlalchemy_type(cls, column_type: str): """ @@ -472,18 +478,18 @@ class TemplateUtils: if column_type_list[0] in GenConstant.COLUMNTYPE_STR: sqlalchemy_type = ( StringUtil.get_mapping_value_by_key_ignore_case( - GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type_list[0] + GenConstant.DB_TO_SQLALCHEMY, column_type_list[0] ) + '(' + column_type_list[1] ) else: sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case( - GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type_list[0] + GenConstant.DB_TO_SQLALCHEMY, column_type_list[0] ) else: sqlalchemy_type = StringUtil.get_mapping_value_by_key_ignore_case( - GenConstant.DB_TO_SQLALCHEMY_TYPE_MAPPING, column_type + GenConstant.DB_TO_SQLALCHEMY, column_type ) return sqlalchemy_type diff --git a/backend/app/utils/jinja2_util.py b/backend/app/utils/jinja2_util.py deleted file mode 100644 index 32f98e36..00000000 --- a/backend/app/utils/jinja2_util.py +++ /dev/null @@ -1,53 +0,0 @@ -import re - -from app.common.constant import GenConstants - - -def snake_to_pascal_case(value): - """将下划线命名 (snake_case) 转换大驼峰""" - return ''.join(word.capitalize() for word in value.split('_')) - - -def snake_to_camel(snake_str): - """将下划线命名 (snake_case) 转换小驼峰""" - components = snake_str.split('_') - return components[0] + ''.join(x.title() for x in components[1:]) - -def snake_2_colon(snake_str: str) -> str: - """将下划线命名 (snake_case) 转换冒号分隔""" - return snake_str.replace('_', ':') - -def is_base_column(column_name: str) -> bool: - """判断是否是基础字段""" - return column_name in GenConstants.BASE_ENTITY - -def get_sqlalchemy_type(mysql_field_type: str) -> str: - """mysql_field_type 转sqlalchemy类型""" - if mysql_field_type: - base_type = mysql_field_type.split("(", 1)[0] - if base_type.upper() in GenConstants.MYSQL_TO_SQLALCHEMY.keys(): - sqlalchemy_type = GenConstants.MYSQL_TO_SQLALCHEMY[base_type.upper()] - if sqlalchemy_type == 'String' : - match = re.search(r'\((.*?)\)', mysql_field_type) - if match: - return f'{sqlalchemy_type}({match.group(1)})' - else: - return f'{sqlalchemy_type}' - else: - return f'{sqlalchemy_type}' - return "String" - -def get_column_options(col) -> str: - options = [] - # 主键 - if col['isPk'] == "1": - options.append("primary_key=True") - # 是否允许为空 - if col['isRequired'] == "1": - options.append("nullable=False") - # 自增 - if col["isIncrement"] == "1": - options.append("autoincrement=True") - # 注释 - options.append(f"comment='{col['columnComment']}'") - return ", ".join(options) diff --git a/backend/requirements.txt b/backend/requirements.txt index 97c66bcc..57dc09ac 100755 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -35,4 +35,5 @@ PyMySQL==1.1.2 # mysql 异步操作数据库基于 pymysql:aiomys cryptography==45.0.2 # mysql8 密码加密 openai==1.55.2 # ai 大模型 oss2==2.18.4 # 阿里云对象存储 -rich==13.9.4 \ No newline at end of file +rich==13.9.4 +sqlglot[rs]==27.8.0 # sql 解析 \ No newline at end of file diff --git a/backend/templates/python/controller.py.j2 b/backend/templates/python/controller.py.j2 index 00a950a5..dd812aa2 100644 --- a/backend/templates/python/controller.py.j2 +++ b/backend/templates/python/controller.py.j2 @@ -22,7 +22,7 @@ from {{ packageName }}.param import {{ tableName|snake_to_pascal_case }}QueryPar async def get_{{ tableName }}_list( page: PaginationQueryParam = Depends(), search: {{ tableName|snake_to_pascal_case }}QueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:list"])) + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:list"])) ): {{ tableName }}_result_list = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_list_service(auth, search, page.order_by) {{ tableName }}_result = await PaginationService.paginate( @@ -36,7 +36,7 @@ async def get_{{ tableName }}_list( @{{ tableName|snake_to_camel }}Controller.get('/{id}', summary="获取{{ functionName }}详细信息", description="获取{{ functionName }}详细信息") async def get_{{ tableName }}_by_id( id: int, - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:query"])) + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:query"])) ): {{ tableName }} = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_by_id_service(auth, id) return SuccessResponse(data={{ tableName }}) @@ -45,7 +45,7 @@ async def get_{{ tableName }}_by_id( @{{ tableName|snake_to_camel }}Controller.post('', summary="新增{{ functionName }}", description="新增{{ functionName }}") async def add_{{ tableName }} ( add_model: {{ tableName|snake_to_pascal_case }}CreateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:add"])), + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:add"])), current_user: UserOutSchema = Depends(lambda auth: auth.user) ): add_result = await {{ tableName|snake_to_pascal_case }}Service.add_{{ tableName }}_service(auth, add_model) @@ -56,7 +56,7 @@ async def add_{{ tableName }} ( async def update_{{ tableName }}( id: int, edit_model: {{ tableName|snake_to_pascal_case }}UpdateSchema, - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:edit"])), + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:edit"])), current_user: UserOutSchema = Depends(lambda auth: auth.user) ): update_result = await {{ tableName|snake_to_pascal_case }}Service.update_{{ tableName }}_service(auth, id, edit_model) @@ -66,7 +66,7 @@ async def update_{{ tableName }}( @{{ tableName|snake_to_camel }}Controller.delete('/{ids}', summary="删除{{ functionName }}", description="删除{{ functionName }}") async def del_{{ tableName }}( ids: str, - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:remove"])) + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:remove"])) ): id_list = [int(id) for id in ids.split(',')] del_result = await {{ tableName|snake_to_pascal_case }}Service.del_{{ tableName }}_service(auth, id_list) @@ -76,7 +76,7 @@ async def del_{{ tableName }}( @{{ tableName|snake_to_camel }}Controller.post('/export', summary="导出{{ functionName }}", description="导出{{ functionName }}") async def export_{{ tableName }}( search: {{ tableName|snake_to_pascal_case }}QueryParam = Depends(), - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:export"])) + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:export"])) ) -> StreamingResponse: # 获取全量数据 {{ tableName }}_list = await {{ tableName|snake_to_pascal_case }}Service.get_{{ tableName }}_list_service(auth=auth, search=search) @@ -93,7 +93,7 @@ async def export_{{ tableName }}( @{{ tableName|snake_to_camel }}Controller.post('/import', summary="导入{{ functionName }}", description="导入{{ functionName }}") async def import_{{ tableName }}( file: UploadFile, - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:import"])) + auth: AuthSchema = Depends(AuthPermission(["{{ permissionPrefix }}:import"])) ) -> JSONResponse: batch_import_result = await {{ tableName|snake_to_pascal_case }}Service.import_{{ tableName }}_service(file=file, auth=auth, update_support=True) return SuccessResponse(data=batch_import_result, msg="导入成功") @@ -101,7 +101,7 @@ async def import_{{ tableName }}( @{{ tableName|snake_to_camel }}Controller.post('/download/template', summary="获取{{ functionName }}导入模板", description="获取{{ functionName }}导入模板") async def export_{{ tableName }}_template( - auth: AuthSchema = Depends(AuthPermission(permissions=["{{ permissionPrefix }}:import"])) + 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(