Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/generator/code-engine.md
T
2026-10-08 17:50:29 +08:00

234 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 10.5 代码生成引擎
:::tip 概述
代码生成引擎基于模板文件,将表结构信息渲染为实际代码文件。渲染使用 PHP 原生 `include` + `extract` 机制,模板变量由 `GeneratorService::buildTemplateVars()` 统一构建,预览与生成的编排由 `GeneratorLogic` 负责。
:::
## 生成文件清单
### 后端文件(4 个文件,输出路径见 `config/generator.php` 的 `backend_outputs`)
| 文件 | 模板 | 说明 |
|------|------|------|
| `app/Http/Controllers/Api/{Name}Controller.php` | controller.php.tpl | 控制器,含分页/列表/详情/增删改/批量删除/导入/导出 |
| `app/Services/{Name}Service.php` | service.php.tpl | 业务服务,含属性配置、serializeMaps、导入导出方法 |
| `app/Models/{Name}.php` | model.php.tpl | 数据模型,含软删除(is_delete)、审计字段自动填充;树形结构额外生成 `children()` 自关联 |
| `app/Http/Requests/{Name}Request.php` | request.php.tpl | FormRequest 验证器,按字段分析自动生成验证规则(camelCase 字段名、required 规则、add/update 场景) |
### 前端文件(ui/ 普通列表模板 — 6 个文件)
| 文件 | 模板 | 说明 |
|------|------|------|
| `ui/src/api/{app}/{name}.ts` | api.ts.tpl | API 接口模块(page/list/detail/add/update/delete/import/export) |
| `ui/src/views/{app}/{name}/index.vue` | index.vue.tpl | 列表页(含搜索、表格、新增/编辑/详情弹窗、导入导出按钮) |
| `ui/src/views/{app}/{name}/edit.vue` | edit.vue.tpl | 编辑弹窗(新增/复用) |
| `ui/src/views/{app}/{name}/detail.vue` | detail.vue.tpl | 详情弹窗 |
| `ui/src/views/{app}/{name}/columns.ts` | columns.ts.tpl | 表格列配置(含图片 ElAvatar、选项 ElTag、链接等渲染) |
| `ui/src/views/{app}/{name}/querySchemas.ts` | querySchemas.ts.tpl | 查询表单配置(按字段类型自动选 Input/Select/InputNumber/DatePicker) |
### 前端文件(ui2/ 树形模板 — 4 个文件)
| 文件 | 模板 | 说明 |
|------|------|------|
| `ui/src/api/{app}/{name}.ts` | api.ts.tpl | API 接口模块 |
| `ui/src/views/{app}/{name}/index.vue` | index.vue.tpl | 树形列表页(Tree + Table 左右布局) |
| `ui/src/views/{app}/{name}/edit.vue` | edit.vue.tpl | 编辑弹窗(含上级节点选择) |
| `ui/src/views/{app}/{name}/detail.vue` | detail.vue.tpl | 详情弹窗 |
:::info 模板选择
表结构中包含 `parent_id` 或 `pid` 字段时自动切换为 `ui2/` 树形模板(`GeneratorService::buildTemplateVars()` 内检测,结果体现在 `is_tree` 变量),否则使用 `ui/` 普通列表模板。树形模板不生成 `columns.ts` 和 `querySchemas.ts`。
:::
## GeneratorService 核心方法
```php
// app/Services/GeneratorService.php
/**
* 获取可生成的表列表
*
* 读取当前数据库所有表,返回表名、注释、引擎、行数等信息。
* 支持 MySQL / PostgreSQL / SQL Server / Oracle / SQLite。
*/
public function getTableList(): array { ... }
/**
* 解析单表结构
*
* 读取列信息、主键、索引与表注释,输出字段归一化结果。
*/
public function parseTableStructure(string $tableName): array { ... }
/**
* 构建模板变量
*
* 解析表结构 → 字段分析 → 构建全部模板变量。
* 是生成引擎的核心,决定模板中可用的所有变量。
* 自动检测树形结构(parent_id / pid),切换 template_type 为 ui2。
*/
public function buildTemplateVars(string $tableName, string $moduleComment = '', string $templateType = 'ui'): array { ... }
/**
* 渲染模板
*
* 使用 PHP extract + include 机制渲染 .tpl 文件。
* 模板不存在或渲染抛错时,带模板文件名上下文重新抛出异常。
*/
public function renderTemplate(string $templatePath, array $vars): string { ... }
/**
* 生成单个文件
*
* 已存在且未开启覆盖 → skipped;目录不存在自动创建(0755)。
*/
public function generateFile(string $path, string $content, bool $overwrite = false): array { ... }
/**
* 创建菜单和权限节点
*
* 在 GENERATOR_PARENT_MENU_ID 指定的父菜单下创建主菜单(type=0)
* 和 9 个标准权限节点(type=1):
* page / list / detail / add / update / delete / batchDelete / import / export。
* path 已存在则跳过(幂等),排序值按父级 max(sort) + 5 递增。
*/
public function createMenu(string $moduleComment, string $moduleName): array { ... }
/**
* 创建字典数据
*
* 遍历 serialize_maps,为带选项的字段自动创建字典主记录和字典项。
* 幂等:字典编码已存在则跳过。
*/
public function createDictData(string $moduleComment, string $moduleName, array $serializeMaps, array $fields): array { ... }
/**
* 注册模块路由
*
* 在 routes/api.php 末尾的中间件组闭合 `});` 之前插入标准 CRUD 路由组。
* 分组注释取表注释(为空时回退模块名),幂等以路由路径判断。
* 树形结构不生成 page 路由。
*/
public function registerRoute(string $moduleName, string $controllerName, bool $isTree = false, string $moduleComment = ''): array { ... }
/**
* 获取生成的文件列表
*
* 返回后端与前端应生成的文件相对路径,供生成结果核对或展示。
*/
public function getGenerateFiles(string $moduleName, string $templateType = 'ui'): array { ... }
```
## GeneratorLogic 编排方法
```php
// app/Services/GeneratorLogic.php — 面向控制器的编排层
// 获取表列表(含分页参数处理)
public function getTableList(array $params = []): array { ... }
// 预览生成代码(不写入文件):渲染全部模板,返回内容 + 是否已存在
public function preview(array $params): array { ... }
// 生成代码(写入文件):写后端 → 写前端 → 创建菜单(可选)→ 创建字典 → 注册路由
public function generate(array $params): array { ... }
// 批量生成:逐表调用 generate,单表失败不中断整体
public function batchGenerate(array $params): array { ... }
```
`generate()` 参数:`tableNames`(必填)、`templateType`(ui/ui2,可省略)、`generateMenu`(默认 true)、`overwrite`(默认 false,不覆盖已存在文件)。
## 字段分析流程
```text
原始表字段(数据库列信息)
│
▼
类型归一化(db_type → type 类名)
│ int → IntegerField, varchar → CharField, text → TextField
│ datetime → DateTimeField, decimal → DecimalField, ...
▼
属性分析(详见 10.4 表结构解析引擎)
│
├─ 图片字段 → 注释/字段名匹配 /图片|封面|头像|logo|icon|image|avatar|photo/i 且类型为 varchar
├─ 富文本 → 注释匹配 /内容|详情|正文/i 且类型为 text
├─ 多行文本 → 注释匹配 /备注|说明|描述|介绍|简介/i 且类型为 varchar
├─ 选项字段 → 注释中解析 "数字-文字" 格式(如 "1-正常 2-停用")
├─ 状态字段 → 字段名 status 且无选项时,自动补 0=正常 1=停用
├─ 模糊搜索 → 字段名匹配 /名称|标题|name|title|username|realname/i 且类型为 varchar
├─ 唯一字段 → 字段名匹配 /编码|code|username|mobile|email|account/i
├─ 排序字段 → 字段名为 sort(标记 has_sort)
└─ 排除字段 → id / tenant_id / create_user / create_time / update_user / update_time / is_delete
不进表单与列表
▼
serialize_maps 构建
│ 有 choices 的字段自动生成映射:字段名 → "{模块名}_{字段名}"
│ 如 status → "example_status"
▼
输出分组字段列表
│ $fields['all'] 全部字段
│ $fields['list'] 列表展示字段(text 长文本默认不进列表)
│ $fields['form'] 表单字段(全量生成规则,含 id → validate_rules / validate_rules_update)
│ $fields['filter'] 筛选字段(含模糊搜索和精确匹配)
│ $fields['file'] 单文件/图片字段
│ $fields['multi'] 多文件字段(逗号分隔的多条路径,如图集)
│ $fields['content'] 富文本字段
│ $fields['like'] 模糊搜索字段
│ $fields['eq'] 精确匹配字段(含选项字段)
│ $fields['unique'] 唯一字段
│ $fields['serialize_maps'] 枚举映射(字段 → 字典编码)
│ $fields['field_labels'] / casts / hidden 字段中文名 / 类型转换 / 敏感字段
│ $fields['validate_rules'] / validate_rules_update / validate_messages / validate_fields FormRequest 规则
```
## 渲染机制
```php
// 渲染原理(简化,见 GeneratorService::renderTemplate)
public function renderTemplate(string $templatePath, array $vars): string
{
if (! file_exists($templatePath)) {
throw new \Exception('模板文件不存在: '.$templatePath);
}
extract($vars); // 将变量注入当前作用域
ob_start(); // 开启输出缓冲
try {
include $templatePath; // 执行模板文件
} catch (\Throwable $e) {
ob_end_clean(); // 丢弃半成品输出
throw new \Exception('模板渲染失败 ['.basename($templatePath).'] '.$e->getMessage());
}
return ob_get_clean(); // 返回渲染结果
}
```
:::warning 模板编写规范
- 使用 `<?php echo ?>` 输出变量,与 `resources/templates/` 现有模板保持一致(现有模板均不含短标签)
- 文件编码必须为 UTF-8 无 BOM
- 控制结构(foreach/if)使用完整的 `<?php ?>` 标签
- 模板渲染异常会带文件名抛出,便于定位是哪张模板出错
:::
## 生成后自动操作
代码生成后,引擎会自动执行以下操作:
| 操作 | 说明 | 幂等性 |
|------|------|--------|
| 写入文件 | 生成 Controller / Service / Model / Request / API / Vue 文件 | 文件已存在则跳过(除非 `overwrite=true`) |
| 创建菜单 | 在 `GENERATOR_PARENT_MENU_ID` 父菜单下创建主菜单和 9 个权限节点 | path 已存在则跳过 |
| 创建字典 | 为带选项的字段自动创建字典和字典项 | 字典编码已存在则跳过 |
| 注册路由 | 在 `routes/api.php` 插入 CRUD 路由组(分组注释取表注释,如 `// --- 案例演示 ---`) | 同模块路由已存在则跳过 |
:::warning 生成后需手动生效
- 路由写入 `routes/api.php` 后,若生产环境执行过 `php artisan route:cache`,需重新执行 `php artisan route:cache`
- 菜单与权限写入数据库后,需重新登录或刷新前端动态菜单才能看到新菜单
:::
## 相关文档
- [10.4 表结构解析引擎](/zh/generator/parser.html)
- [10.6 自定义模板开发](/zh/generator/templates.html)
- [10.7 配置文件详解](/zh/generator/config.html)