diff --git a/backend/app/api/v1/module_generator/gencode/crud.py b/backend/app/api/v1/module_generator/gencode/crud.py index f2a95a0c..d61dd6c7 100644 --- a/backend/app/api/v1/module_generator/gencode/crud.py +++ b/backend/app/api/v1/module_generator/gencode/crud.py @@ -325,6 +325,24 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]): log.error(f"创建表时发生错误: {e}") return False + async def execute_sql(self, sql: str) -> bool: + """ + 执行SQL语句。 + + 参数: + - sql (str): 要执行的SQL语句。 + + 返回: + - bool: 是否执行成功。 + """ + try: + # 执行SQL但不手动提交事务,由框架管理事务生命周期 + await self.auth.db.execute(text(sql)) + return True + except Exception as e: + log.error(f"执行SQL时发生错误: {e}") + return False + class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, GenTableColumnSchema]): """代码生成业务表字段模块数据库操作层""" diff --git a/backend/app/api/v1/module_generator/gencode/service.py b/backend/app/api/v1/module_generator/gencode/service.py index 5383ad1d..51bc1329 100644 --- a/backend/app/api/v1/module_generator/gencode/service.py +++ b/backend/app/api/v1/module_generator/gencode/service.py @@ -2,6 +2,7 @@ import io import os +from pathlib import Path import zipfile from typing import Any from sqlglot.expressions import Add, Alter, Create, Delete, Drop, Expression, Insert, Table, TruncateTable, Update @@ -185,6 +186,43 @@ class GenTableService: except Exception as e: raise CustomException(msg=f'创建表结构失败: {str(e)}') + @classmethod + @handle_service_exception + async def execute_sql_service(cls, auth: AuthSchema, gen_table: GenTableOutSchema) -> bool: + """ + 执行菜单 SQL(INSERT / DO 块)并写入 sys_menu。 + - 仅处理菜单 SQL,不再混杂建表逻辑; + - 文件不存在时给出友好提示; + - 统一异常信息,日志与业务提示分离。 + """ + sql_path = f'{BASE_DIR}/sql/menu/{gen_table.module_name}/{gen_table.business_name}.sql' + + # 文件存在性前置检查,避免多余解析开销 + if not os.path.isfile(sql_path): + raise CustomException(msg=f'菜单 SQL 文件不存在: {sql_path}') + + sql = Path(sql_path).read_text(encoding='utf-8').strip() + if not sql: + raise CustomException(msg='菜单 SQL 文件内容为空') + + # 仅做语法校验,不限制关键字;真正的语义安全由数据库权限控制 + try: + statements = sqlglot_parse(sql, dialect=settings.DATABASE_TYPE) + if not statements: + raise CustomException(msg='菜单 SQL 语法解析失败,请检查文件内容') + except Exception as e: + log.error(f'菜单 SQL 解析异常: {e}') + raise CustomException(msg='菜单 SQL 语法错误,请检查文件内容') + + # 执行 SQL + try: + await GenTableCRUD(auth).execute_sql(sql) + log.info(f'成功执行菜单 SQL: {sql_path}') + return True + except Exception as e: + log.error(f'菜单 SQL 执行失败: {e}') + raise CustomException(msg='菜单 SQL 执行失败,请确认语句及数据库状态') + @classmethod def __is_valid_create_table(cls, sql_statements: list[Expression | None]) -> bool: """ @@ -349,6 +387,8 @@ class GenTableService: f.write(render_content) except Exception as e: raise CustomException(msg=f'渲染模板失败,表名:{gen_table_schema.table_name},详细错误信息:{str(e)}') + + await cls.execute_sql_service(auth, gen_table_schema) return True @classmethod diff --git a/backend/app/api/v1/module_generator/gencode/templates/python/schema.py.j2 b/backend/app/api/v1/module_generator/gencode/templates/python/schema.py.j2 index 17bd9060..70d0aa22 100644 --- a/backend/app/api/v1/module_generator/gencode/templates/python/schema.py.j2 +++ b/backend/app/api/v1/module_generator/gencode/templates/python/schema.py.j2 @@ -53,7 +53,7 @@ class {{ class_name }}QueryParam: {% endif %} {% endfor %} {% for column in columns %} - {% if column.query_type == 'EQ' %} + {% if column.query_type == 'EQ' and column.column_name not in ['created_time', 'updated_time'] %} {{ column.column_name }}: {{ column.python_type }} | None = Query(None, description="{{ column.column_comment }}"), {% endif %} {% endfor %} @@ -70,7 +70,7 @@ class {{ class_name }}QueryParam: {% if column.query_type == 'LIKE' %} # 模糊查询字段 self.{{ column.column_name }} = ("like", {{ column.column_name }}) - {% elif column.query_type == 'EQ' and column.column_name %} + {% elif column.query_type == 'EQ' and column.column_name not in ['created_time', 'updated_time'] %} # 精确查询字段 self.{{ column.column_name }} = {{ column.column_name }} {% endif %} diff --git a/backend/app/api/v1/module_generator/gencode/templates/sql/sql.sql.j2 b/backend/app/api/v1/module_generator/gencode/templates/sql/sql.sql.j2 index d9511e0a..b05cd4b4 100644 --- a/backend/app/api/v1/module_generator/gencode/templates/sql/sql.sql.j2 +++ b/backend/app/api/v1/module_generator/gencode/templates/sql/sql.sql.j2 @@ -12,7 +12,7 @@ INSERT INTO {{ sys_menu }} (`name`, `type`, {{ order_col }}, `permission`, `icon`, `route_name`, `route_path`, `component_path`, `redirect`, `hidden`, `keep_alive`, `always_show`, `title`, `params`, `affix`, `parent_id`, `uuid`, `status`, `description`, `created_time`, `updated_time`) VALUES -('{{ function_name }}', 2, 9999, '{{ permission_prefix }}:query', '{{ icon }}', '{{ business_name|snake_to_camel }}', '/{{ module_name }}/{{ business_name }}', '/{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id }}, {{ set_uuid }}, '0', '{{ function_name }}菜单', NOW(), NOW()); +('{{ function_name }}', 2, 9999, '{{ permission_prefix }}:query', '{{ icon }}', '{{ business_name|snake_to_camel }}', '/{{ module_name }}/{{ business_name }}', '{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id }}, {{ set_uuid }}, '0', '{{ function_name }}菜单', NOW(), NOW()); -- 获取父菜单ID(MySQL) SELECT @parentId := LAST_INSERT_ID(); @@ -39,7 +39,7 @@ BEGIN INSERT INTO {{ sys_menu }} (name, type, {{ order_col }}, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, uuid, status, description, created_time, updated_time ) VALUES -('{{ function_name }}', 2, 9999, '{{ permission_prefix }}:query', '{{ icon }}', '{{ business_name|snake_to_camel }}', '/{{ module_name }}/{{ business_name }}', '/{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id }}, {{ set_uuid }}, '0', '{{ function_name }}菜单', NOW(), NOW()) +('{{ function_name }}', 2, 9999, '{{ permission_prefix }}:query', '{{ icon }}', '{{ business_name|snake_to_camel }}', '/{{ module_name }}/{{ business_name }}', '{{ module_name }}/{{ business_name }}/index', NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}', NULL, {{ b_false }}, {{ parent_menu_id }}, {{ set_uuid }}, '0', '{{ function_name }}菜单', NOW(), NOW()) RETURNING id INTO parent_id; -- 按钮权限(类型=3:按钮/权限) diff --git a/backend/app/api/v1/module_generator/gencode/templates/ts/api.ts.j2 b/backend/app/api/v1/module_generator/gencode/templates/ts/api.ts.j2 index 14520ce2..57b28ce3 100644 --- a/backend/app/api/v1/module_generator/gencode/templates/ts/api.ts.j2 +++ b/backend/app/api/v1/module_generator/gencode/templates/ts/api.ts.j2 @@ -1,6 +1,6 @@ import request from "@/utils/request"; -const API_PATH = "/{{ module_name }}/{{ business_name|lower }}"; +const API_PATH = "/{{ package_name }}/{{ business_name|lower }}"; const {{ class_name }}API = { // 列表查询 @@ -95,7 +95,7 @@ export default {{ class_name }}API; // 列表查询参数 export interface {{ class_name }}PageQuery extends PageQuery { {% for column in columns %} - {% if column.is_query and column.column != "BETWEEN" %} + {% if column.is_query and column.column != "BETWEEN" and column.column_name not in ['created_time', 'updated_time'] %} {{ column.column_name }}?: {{ 'string' if ('status' in (column.python_field|lower)) or (column.html_type == 'radio') else 'number' if column.is_pk == '1' @@ -110,7 +110,6 @@ export interface {{ class_name }}PageQuery extends PageQuery { // 列表展示项 export interface {{ class_name }}Table extends BaseType{ - index?: number; {% for column in columns %} {% if column.column_name not in ['id', 'uuid', 'status', 'description', 'created_time', 'updated_time'] %} {{ column.column_name }}?: {{ diff --git a/backend/app/api/v1/module_generator/gencode/templates/vue/index.vue.j2 b/backend/app/api/v1/module_generator/gencode/templates/vue/index.vue.j2 index f23f50ab..2b3b4860 100644 --- a/backend/app/api/v1/module_generator/gencode/templates/vue/index.vue.j2 +++ b/backend/app/api/v1/module_generator/gencode/templates/vue/index.vue.j2 @@ -16,7 +16,43 @@ {% set column_comment = column.column_comment if column.column_comment else '' %} {% set parentheseIndex = column_comment.find("(") %} {% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %} - {% if column.html_type == "input" %} + {% if column.column_name == "status" %} + + + + + + + {% elif column.column_name == "created_id"%} + + + + {% elif column.column_name == "updated_id"%} + + + + {% elif column.column_name == "created_time"%} + + + + {% elif column.column_name == "updated_time"%} + + + + {% elif column.html_type == "input" %} @@ -39,38 +75,6 @@ {% endif %} {% endif %} {% endfor %} - - - - - - - - - - - - - - - - - - -