:::tip 本章概要 代码生成引擎的工作原理,包括配置解析、模板渲染、文件写入和自动注册的完整流程。 ::: # 代码生成引擎 `src/modules/generator/code_generator.py` 是代码生成器的执行层,负责接收配置字典、渲染模板、写入文件、自动注册路由和菜单。 ## 核心类:CodeGenerator ```python from modules.generator.code_generator import CodeGenerator generator = CodeGenerator(config) generator.generate(auto=True) ``` ### 构造参数 | 参数 | 说明 | |:----|:----| | `config` | 配置字典(由 `parser.parse()` 或 JSON 文件提供) | 构造时自动: - 调用 `normalize_config()` 标准化配置(幂等,已标准化的配置原样返回) - 设置输出目录:`src/modules/{app_name}` - 初始化 Jinja2 模板环境(`trim_blocks=True`, `lstrip_blocks=True`) ## generate() 主方法 ```python def generate(self, auto=False, request=None): ``` ### 执行流程 ``` 1. 校验输出目录不存在(避免覆盖已有模块) 2. 创建输出目录 src/modules/{app_name}/ 3. 准备模板上下文数据 4. 渲染后端模板 → 写入 modules 目录 5. 渲染 endpoint 模板 → 写入 api/v1/endpoints/ 目录 6. 渲染前端模板 → 写入 ui/src/views/ 和 ui/src/api/ 7. 自动注册到 router.py 8. 自动创建菜单 + 权限节点 ``` ### 参数 | 参数 | 说明 | |:----|:----| | `auto` | 自动模式(当前无交互差异) | | `request` | Tornado 请求对象(用于获取操作人信息) | ## 配置标准化 `normalize_config()` 将 parser 产出的配置转为模板可用的标准格式: ```python # parser 产出的字段 {"name": "type", "db_type": "Integer", "comment": "案例类型:1-类型1 2-类型2"} # 标准化后 { "name": "type", "camel_name": "type", # 驼峰命名 "db_type": "Integer", "form_type": "int", # Pydantic 类型 "comment": "案例类型:1-类型1 2-类型2", "py_comment": "案例类型:1-类型1 2-类型2", # 转义后 "default_repr": "1", # 默认值表示 "is_status": False, # 状态字段标识 "is_sort": False, # 排序字段标识 } ``` ## 模板上下文 ### _prepare_context() 准备后端模板的渲染上下文: | 变量 | 类型 | 说明 | |:----|:----|:----| | `app_name` | str | 模块名(如 `example`) | | `module_comment` | str | 中文名(如 `案例`) | | `model_class_name` | str | 类名(如 `Example`) | | `permission_prefix` | str | 权限前缀(如 `sys:example`) | | `display_field` | str | 显示字段(如 `name`) | | `has_sort` | bool | 有排序字段 | | `has_status` | bool | 有状态字段 | | `has_status_route` | bool | 生成状态路由 | | `has_image_field` | bool | 有图片字段 | | `has_rich_text_field` | bool | 有富文本字段 | | `serialize_maps` | dict | 枚举映射 | | `sort_field` | str | 排序字段名 | | `fields` | list | 全部字段 | | `form_fields` | list | 表单字段 | | `filter_fields` | list | 精确匹配字段 | | `like_fields` | list | 模糊查询字段 | ### _prepare_vue_context() 在后端上下文基础上扩展前端专用变量: | 变量 | 类型 | 说明 | |:----|:----|:----| | `module_name` | str | 驼峰模块名 | | `model_class_name_camel` | str | 驼峰类名(首字母小写) | | `list_fields` | list | 列表展示字段 | | `searchable_fields` | list | 可搜索字段 | | `editable_fields` | list | 可编辑字段 | | `has_export` | bool | 有导出功能 | | `route_prefix` | str | 路由前缀 | | `api_path` | str | API 路径 | | `is_tree_structure` | bool | 是否树形结构 | | `parent_id_field` | str | 父级字段名 | | `has_search_form` | bool | 显示搜索表单 | | `show_selection` | bool | 显示复选框 | ## 文件生成 ### 后端文件 | 模板 | 输出路径 | 说明 | |:----|:----|:----| | `__init__.py.tpl` | `src/modules/{name}/__init__.py` | 模块初始化 | | `models.py.tpl` | `src/modules/{name}/models.py` | ORM 模型 | | `schemas.py.tpl` | `src/modules/{name}/schemas.py` | Pydantic 表单 | | `repository.py.tpl` | `src/modules/{name}/repository.py` | 数据访问层 | | `service.py.tpl` | `src/modules/{name}/service.py` | 业务逻辑层 | | `endpoint.py.tpl` | `src/modules/{name}.py` | HTTP 端点 | ### 前端文件(普通列表) | 模板 | 输出路径 | |:----|:----| | `ui/index.vue.tpl` | `ui/src/views/tool/{name}/index.vue` | | `ui/edit.vue.tpl` | `ui/src/views/tool/{name}/edit.vue` | | `ui/detail.vue.tpl` | `ui/src/views/tool/{name}/detail.vue` | | `ui/columns.ts.tpl` | `ui/src/views/tool/{name}/columns.ts` | | `ui/querySchemas.ts.tpl` | `ui/src/views/tool/{name}/querySchemas.ts` | | `ui/api.ts.tpl` | `ui/src/api/tool/{name}.ts` | ### 前端文件(树状列表) | 模板 | 输出路径 | |:----|:----| | `ui2/index.vue.tpl` | `ui/src/views/tool/{name}/index.vue` | | `ui2/edit.vue.tpl` | `ui/src/views/tool/{name}/edit.vue` | | `ui2/detail.vue.tpl` | `ui/src/views/tool/{name}/detail.vue` | | `ui/api.ts.tpl` | `ui/src/api/tool/{name}.ts` | 树状列表与普通列表共用 `api.ts.tpl`。 ## 自动注册 ### 路由注册 `_register_module()` 在 `src/api/v1/router.py` 中追加: ```python # 导入语句(插入到最后一个 from api.v1.endpoints 导入之后) from api.v1.endpoints.example import router as example_router # 注册语句(追加到文件末尾) v1._RESOURCE_MODULES 追加(example_router, prefix="/example", tags=["案例"]) ``` 幂等操作:如果导入语句已存在,跳过。 ### 菜单创建 `_create_menus()` 在数据库中创建: 1. **二级菜单**(type=0):页面路由 - name: 模块中文名 - path: `/tool/{module_camel}` - component: `/tool/{module_camel}/index` - parent_id: 189(开发工具) 2. **三级权限节点**(type=1)× 10 个: | 权限 | 标识 | |:----|:----| | 分页查询 | `{prefix}:page` | | 列表查询 | `{prefix}:list` | | 详情查询 | `{prefix}:detail` | | 新增 | `{prefix}:add` | | 编辑 | `{prefix}:update` | | 删除 | `{prefix}:delete` | | 批量删除 | `{prefix}:batchDelete` | | 状态管理 | `{prefix}:status` | | 导入 | `{prefix}:import` | | 导出 | `{prefix}:export` | 幂等操作:如果 path 已存在,跳过。创建后自动清除所有用户权限缓存。 ## 总结 代码生成引擎根据数据库表结构和配置信息,通过 Jinja2 模板渲染生成后端(models/schemas/repository/service/endpoint)和前端(index.vue/edit.vue/columns.ts/querySchemas.ts/api.ts)全套代码,生成后自动注册路由和权限。