签入版本

This commit is contained in:
小蚂蚁云
2026-10-20 21:53:26 +08:00
parent 5cc3da657c
commit b33799aa63
427 changed files with 32547 additions and 1 deletions
@@ -0,0 +1,68 @@
# 10.2 CLI 命令行使用
:::tip 概述
CLI 入口 cmd/generator:`go run ./cmd/generator -tables "表名|注释"`。支持预览(-preview)与批量统计(-batch),读取同一份 config.yaml,产出与 HTTP 入口完全一致。
:::
## 命令格式
```sh
# 一键生成(文件 + 菜单 + 字典 + 路由,全部幂等)
go run ./cmd/generator -tables "goframe_example|案例演示表"
# 多表(逗号分隔)
go run ./cmd/generator -tables "goframe_example|案例演示表,goframe_file_template|文件模板"
# 仅预览(不落盘,打印文件清单)
go run ./cmd/generator -tables "goframe_example|案例演示表" -preview
# 批量统计(逐表输出生成/跳过/覆盖/失败计数)
go run ./cmd/generator -tables "goframe_example|案例演示表" -batch
```
`-tables` 格式:`"表名|表注释"`——注释可省略(`|` 后内容被忽略);表注释用于模块中文名(日志标题/字典名)。
## 执行流程(CLI 侧)
```text
1. 读取 manifest/config/config.yaml(generator.* 配置)
2. 空白 import internal/logic → 全部服务 init 注册
3. 调用 service.Generator().Preview / Generate / BatchGenerate
——与 HTTP 入口同一套核心逻辑
4. 输出分组结果:
+ 生成(Generated) - 跳过(Skipped) * 覆盖(Overwritten) ! 失败(Failed)
5. 提示:"下一步: go build ./... 并重启服务"
```
## 生成后必做
```sh
go build ./... && ./bin/admin-server.exe # 编译并重启(init 注册/路由生效)
```
然后:
```text
1. 登录 → 系统管理 → 角色管理 → 为角色勾选新模块权限节点
2. 访问新菜单验证增删改查
3. 租户级新表:把表名加入 internal/model/hooks 的 tenantTables(重启生效)
```
## CLI 与 HTTP 的选择
| 场景 | 推荐 |
|------|------|
| 批量生成多表 | CLI(命令行批量更顺手) |
| 团队协作/远程环境 | HTTP 管理页(可视化预览) |
| CI 流水线 | CLI |
| 预览确认再生成 | 两者皆可(-preview / preview 接口) |
## 常见问题
| 症状 | 原因 |
|------|------|
| `unknown table` | 表不存在或库连接错(查 config.yaml database) |
| 注释/枚举缺失 | 非 MySQL 库内省不到注释——建议 MySQL 上生成或人工补注释 |
| 提示"路由已存在" | registerRouter 幂等——正常(router.go 已含该注册行) |
| 生成后编译失败 | gofmt 失败会报错中断;查输出 Failed 项 |
| CLI panic 注册冲突 | RegisterJobHandler 重名等 init 冲突——查 internal/task |
@@ -0,0 +1,102 @@
# 10.5 代码生成引擎
:::tip 概述
生成引擎按模板渲染全部产物:后端八件套(统一 gofmt)+ 前端五件/三件,然后经 writeFile 四态落盘决策,最后执行幂等副作用。本节给出产物清单与落盘决策。
:::
## 产物清单(renderTable)
### 后端八件套(Go 产物统一 gofmt,format.Source 失败即报错)
| 产物 | 模板 |
|------|------|
| api/v1/{module}.go | templates/api.tpl |
| internal/controller/{module}.go | templates/controller.tpl |
| internal/logic/{module}.go | templates/logic.tpl |
| internal/service/{module}.go | templates/service.tpl |
| internal/dao/{module}.go | templates/dao_index.tpl(挂钩子+Ctx) |
| internal/dao/internal/{module}.go | templates/dao_internal.tpl |
| internal/model/entity/{module}.go | templates/entity.tpl |
| internal/model/do/{module}.go | templates/do.tpl |
### 前端文件(模板定界符 [[ ]],规避 Vue 插值冲突)
```text
ui 普通模板:
src/api/{域}/{moduleCamel}.ts
src/views/{域}/{moduleCamel}/
index.vue edit.vue detail.vue columns.ts querySchemas.ts
ui2 树形模板:
src/api/{域}/{moduleCamel}.ts
src/views/{域}/{moduleCamel}/
index.vue edit.vue detail.vue
```
## 落盘决策(writeFile 四态)
```text
目标文件存在?
/ \
否 是
│ │
写入 generator.overwrite?
Generated / \
true false/缺省
│ │
覆盖写 跳过保留
Overwritten Skipped
\ /
任一失败 → Failed(MkdirAll/WriteFile 错误)
```
覆盖开关唯一入口:`generator.overwrite`(config.yaml)——true=生成即覆盖,false/未配置=缺失补生成、存在跳过。
## skipTables 保护
```text
skipTables:仅关联表 role_menu、user_role
跳过文件写入(这两个表无独立模块,关联逻辑归 role/user logic)
但幂等副作用(菜单/字典/路由)照常执行——buildVars 结果供副作用使用
```
## 生成产物质量保障
```text
* Go 产物统一 gofmt(format.Source 失败即报错不落盘——坏格式不入库)
* 生成的 logic 是 ModuleSpec 基座风格完整实现(含筛选/树形/字典/导入导出声明)
* dao 四件与 gf gen dao 同构(挂全局钩子 + Ctx 覆写)
* service 接口含中文方法注释(与手维护风格一致)
* 前端 api 函数含 JSDoc 注释
```
## 结果报告结构
```text
Generate(一键):
Generated[] / Skipped[] / Overwritten[] / Failed[]
Menu{Success, MenuId, Message}
Dict[]{Dict, Name, Items, Status}
Route{Success, Message}
BatchGenerate:
List[]{Table, Success, Generated, Skipped, Overwritten, Failed,
Message, Menu, Dict, Route}
Message="生成 N,跳过 N,覆盖 N,失败 N"
Preview:
List[]{Path, Content}(渲染结果全文,不落盘)
```
## 生成后 Checklist
```text
□ go build ./... 编译通过
□ 重启服务(路由/服务注册生效)
□ 角色管理勾选新权限节点
□ 租户级新表 → hooks.tenantTables 登记(再重启)
□ 访问新菜单:增删改查/导入导出冒烟
□ querySchemas options 改字典驱动(生成的是静态示例选项)
□ 复杂列/复杂筛选手工扩展
□ /api.json 核对接口清单
```
@@ -0,0 +1,79 @@
# 10.7 配置文件详解
:::tip 概述
生成器配置在 config.yaml 的 generator 块:parentMenuId(菜单挂载父级)与 overwrite(覆盖开关,唯一入口)。CLI 与 HTTP 共用同一份配置。
:::
## 配置项
```yaml
generator:
parentMenuId: 189 # 新模块菜单挂载的父级ID
overwrite: false # 是否覆盖已存在文件:true 生成即覆盖,
# false 或未配置时缺失补生成、存在跳过
```
## parentMenuId(菜单挂载位置)
```text
作用:createMenu 创建的主菜单挂到哪个父节点下
默认:189("代码生成"或指定的模块分组菜单 ID)
操作:
1. 菜单管理页面查目标父菜单的 ID
2. 修改 parentMenuId 为该 ID
3. 生成——新模块菜单出现在该父级下(sort=同级 MAX+5)
注意:
* 父级不存在时菜单挂载失败(看 Menu.Message)
* 路径仍为 /{domain}/{moduleCamel}(与挂载位置无关)
```
## overwrite(覆盖开关,唯一入口)
| 值 | 行为 | 适用 |
|----|------|------|
| false / 未配置 | 缺失补生成、存在跳过(Skipped) | **默认/推荐**——保护手工修改 |
| true | 生成即覆盖(Overwritten) | 全新表首次生成;或有意整体重生成 |
::: warning 覆盖风险
overwrite=true 会覆盖同名文件(**包括手工修改过的**)。批量覆盖前:
1. git 提交或备份手工改动
2. 或临时改回 false 按需补生成
3. 覆盖结果单独计入 Overwritten——生成后核对该列表
:::
## 其他相关配置
```yaml
# 数据库(生成器内省元数据的来源)
database:
default:
type: "mysql" # 非 MySQL 内省不到注释/枚举(建议 MySQL 上生成)
link: "..."
# 菜单/字典等副作用还受定稿数据影响(parentMenuId 对应的菜单需存在)
```
## 多环境配置
```text
CLI 与服务读同一份 manifest/config/config.yaml:
本地生成 → 改本地配置
服务器上生成 → 改服务器配置(或 GF_ 环境变量覆盖不改文件)
覆盖开关无环境变量特例时直接改配置文件(生成器为开发期工具,
生产环境通常不部署生成需求)
```
## 配置实践建议
```text
日常开发:overwrite: false(保护手工扩展)
新表首生成:保持 false 即可(文件不存在 = 全新写入)
重新生成同表:
1. 先 false 生成看 Skipped 清单(了解哪些文件已有)
2. 有意覆盖的模块备份后临时 true
3. 生成后 diff 核对合并手工改动
parentMenuId:团队约定统一挂载点(如"开发工具"分组)
```
@@ -0,0 +1,100 @@
# 10.8 实战案例
:::tip 概述
本节以数据库中真实存在的 `goframe_example`(案例演示表)为实操对象,演示生成器全流程:预览 → 生成(幂等)→ 副作用核对。你可以直接执行每条命令。开发新模块时把表名换成自己的表即可。
:::
## 实操对象:goframe_example
```sql
-- 案例演示表(定稿脚本已含,可直接 SELECT 查看结构)
CREATE TABLE `goframe_example` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '案例名称',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '案例图片',
`type` int NOT NULL DEFAULT 0 COMMENT '案例类型',
`status` int NOT NULL DEFAULT 1 COMMENT '状态:1-正常 2-禁用',
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`create_user` varchar(50) NOT NULL DEFAULT '' COMMENT '创建人',
`create_time` datetime NULL DEFAULT (now()) COMMENT '创建时间',
`update_user` varchar(50) NOT NULL DEFAULT '' COMMENT '更新人',
`update_time` datetime NULL DEFAULT (now()) COMMENT '更新时间',
`del_flag` int NOT NULL DEFAULT 0 COMMENT '删除标志:0-正常 1-已删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='案例演示表';
```
规范自检(生成器识别依据):审计列齐备 ✓;status 注释枚举(自动识别 `example_status` 字典)✓;avatar 注释含"图片"(识别为图片列 → FileFields)✓;无 parent_id(用 ui 普通模板)✓。
## 第一步:预览(不落盘)
```sh
go run ./cmd/generator -tables "goframe_example|案例演示表" -preview
```
输出将产出的文件清单与渲染全文——这是核对"筛选字段/枚举识别/命名"的最佳时机。对照第 3 章([3.10 完整代码清单](../guide/code-list))的真实产物,你会看到模板渲染结果与项目内 example 模块实现同构。
## 第二步:生成(体验幂等保护)
```sh
go run ./cmd/generator -tables "goframe_example|案例演示表"
```
因为 example 模块已完整实现,本次执行将演示生成器的**幂等保护**:
```text
文件产出(overwrite=false 时):
api/v1/example.go · controller · logic · service · dao 四件 · 前端文件
——全部 Skipped(已存在,保护你的手工修改)
副作用(幂等):
Menu: "已存在"(按 path+type=0 查重命中)
Dict: example_status "已存在"(按 code 查重命中)
Route: "路由已存在"(router.go 已含 Bind 行)
```
如需体验全新写入,可临时把 `generator.overwrite` 改为 `true` 覆盖重生成(**先 git 提交保护手工改动**),观察 Overwritten 清单后用 git diff 审查合并。
## 第三步:理解"新模块"流程(换表名即可)
```sh
# 真实新模块:建好表后,把表名换成你的表
go run ./cmd/generator -tables "goframe_xxx|中文名"
# 多表示例(均为库中真实存在的表)
go run ./cmd/generator -tables "goframe_example|案例演示表,goframe_file_template|文件模板"
```
新模块生成后的完整链路:
```text
1. go build ./... && 重启(init 注册/路由生效)
2. 角色管理 → 勾选新模块权限节点(7 固定 + status/import/export)
3. 访问新菜单冒烟:增删改查/导入导出
4. 租户级新表 → internal/model/hooks 的 tenantTables 登记(再重启)
5. 定制收尾:
querySchemas options 改字典驱动(getDictItemList)
columns 金额/复杂列 render
spec 钩子扩展业务(BeforeAdd 默认值等)
```
## 建表侧配合(新模块第一杠杆)
```sql
-- 注释写好,生成器才能"全自动":
`status` int DEFAULT 1 COMMENT '状态:1-正常 2-禁用', -- → 字典 + Select
`cover` varchar(255) DEFAULT '' COMMENT '封面图片', -- → FileFields 图片列
`content` text COMMENT '内容富文本', -- → ContentFields 富文本
UNIQUE KEY uk_code (code) -- → 唯一性语义清晰
```
## 经验总结
```text
* 用库中真实表练手(如 goframe_example)——每条命令可直接执行验证
* 注释写得好 → 字典/标签/前端选项全自动(枚举注释是最大杠杆)
* 无 parent_id 用 ui 模板;树形表(有 parent_id/pid)自动切 ui2
* 生成器产出即可运行——先跑通再定制,不要反过来
* 重生成前 git commit(手工改动与生成产物分离管理)
* 租户级表务必登记 tenantTables(生成器管不到这一步)
```
@@ -0,0 +1,81 @@
# 10.9 常见问题
:::tip 概述
生成器 FAQ 按"识别/产出/副作用/覆盖"分组。核心认知:生成器是"首次产出"工具,已生成模块的演进走标准开发流程(见第 3 章)。
:::
## 识别类
### Q:枚举没识别/字典没建
注释格式须为 `字段名:1-正常 2-禁用`(`-`、`—`、`=`、`=` 均可分隔)。非 MySQL 库内省不到列注释——在 MySQL 上生成,或人工补注释后重新生成。
### Q:图片/富文本列识别错
图片:注释含 图片/头像/封面,或列名含 avatar/cover/image/icon/photo。
富文本:注释含 富文本,或 TextField 且列名含 content/body/editor。
不满足则手工在 logic 的 FileFields/ContentFields 补声明。
### Q:域名归属错了(前端路径不对)
genDomainMap 决定域(user→system、config→data、article→content、operation_log→monitor、默认 tool)。自定义表未匹配时落 tool——前端文件会生成到 tool 目录,可生成后移动并改菜单 path(或接受 tool 归属)。
### Q:筛选字段不符合预期
筛选字段生成规则:CharField 非图片非 URL 列作 LIKE;带枚举的 Integer/Boolean → Select;*_id 外键 → InputNumber。URL 列(website/site/url/link)自动排除。生成后手工调 querySchemas。
## 产出类
### Q:生成后编译失败
看 Failed 报告项。Go 产物 gofmt 失败会中断该文件。常见:表结构特殊类型映射异常——检查列类型/注释后重试。
### Q:生成的 logic 需要改很多
生成器产出的是"完整可用的标准实现"——标准 CRUD/筛选/字典/文件字段开箱即用。复杂业务(多表联动/特殊流程)在钩子里扩展或手写方法(合法逃生舱),不必强求全部塞进声明。
### Q:前端页面路由 404
菜单 component 值 = `/{domain}/{moduleCamel}/index`,与生成的 views 文件路径一致才有效。检查:菜单是否创建成功(Menu.Message)、角色是否勾选、重启是否执行。
## 副作用类
### Q:菜单/权限节点没创建
看 Menu 报告项 Message。常见:parentMenuId 指向不存在的父级;或 path+type=0 查重命中(已存在,幂等跳过——正常)。
### Q:字典创建了但页面选项还是静态的
querySchemas 生成的是静态 options 示例——手工改为字典驱动(getDictItemList)。后端 {field}Text 已由 SerializeMaps 声明自动填充。
### Q:路由注册失败
Route.Message 提示原因:router.go 不存在 / 缺 controller 导入 / 未找到锚点(最后一个 Bind 行)。手工在 registerControllers 末尾补 `Bind(s, new(controller.CXxx))` 后重启。
### Q:租户级表数据不隔离
生成器不维护 tenantTables——手工把表名加入 `internal/model/hooks/hooks.go` 后重启(漏登=跨租户泄漏,安全红线)。
## 覆盖类
### Q:重复生成会覆盖手工修改吗
默认不会(overwrite=false 时已存在文件 Skipped)。overwrite=true 时全部覆盖(计入 Overwritten)——覆盖前务必备份或临时改回 false。
### Q:改了模板后已有模块会更新吗
不会——已存在文件跳过。模板变更只影响新生成文件;已有模块需手动合并模板差异。
### Q:Skipped 太多想全部重新生成
```text
1. git status 确认手工改动已提交
2. config.yaml 临时 overwrite: true
3. 生成(核对 Overwritten 清单)
4. git diff 审查合并手工改动
5. go build + 重启 + 冒烟
```
### Q:skipTables 是什么
关联表 role_menu/user_role 整表跳过文件生成(无独立模块,关联逻辑归 role/user logic),但菜单/字典/路由副作用照常执行。
@@ -0,0 +1,93 @@
# 10.1 概述与架构
:::tip 概述
代码生成器根据表结构一键产出前后端全套文件,并同步创建菜单、权限节点、字典数据、路由注册行(全部幂等)。双入口:HTTP(管理页面)与 CLI(cmd/generator)。核心在 internal/logic/generator.go + generator_side.go。
:::
## 定位
```text
输入:表名列表("表名|注释")
产出:
后端八件套(api/controller/logic/service + dao index/internal + entity + do)
前端文件(api/{域}/{模块}.ts + views/{域}/{模块}/ 五件或三件)
幂等副作用:菜单 + 权限节点 + 字典 + router.go 注册行
约束:生成的 logic 是 ModuleSpec 基座风格的完整可用实现,
go build 后即可运行;复杂业务再手工扩展钩子
```
## 双入口
```text
HTTP(/api/generator,管理页面"代码生成"):
GET /api/generator/page sys:generator:page 数据表清单(分页)
POST /api/generator/preview sys:generator:generator 预览(不落盘)
POST /api/generator/generate sys:generator:generator 一键生成
POST /api/generator/batchGenerate sys:generator:batchGenerator 批量生成
日志 logType=generate(19 代码生成)
CLI(仓库根目录,读同一份 config.yaml):
go run ./cmd/generator -tables "goframe_example|案例演示表"
CLI 空白 import internal/logic 注册全部服务,直接调用 service.Generator()
```
## 总体流水线
```text
输入 tables[]("表名|注释")
│
▼
buildVars:TableFields 内省 → 类型/注释解析 → 字段分桶(列表/表单/筛选)
│ 命名:module/Pascal/camelLower;域归属;ui|ui2 模板类型
▼
skipTables? ──是(role_menu/user_role)──▶ 仅 buildVars(副作用用),跳过文件
│ 否
▼
renderTable:逐模板渲染(后端 {{ }} / 前端 [[ ]])→ go 产物 gofmt
│
▼
writeFile×N(四态:新写/跳过/覆盖/失败)
│
▼
副作用(幂等):createMenu(+权限节点) → createDict(枚举字段) → registerRouter
│
▼
结果报告(Generated/Skipped/Overwritten/Failed + Menu/Dict/Route)
│
▼
提示:go build ./... 并重启服务
```
## 域归属与模板类型
```text
genDomainMap 域归属(决定前端 api/views 目录):
user/role/menu/dept/level/position/tenant → system
config/param/dict/notice/city → data
article/category/link → content
operation_log/login_log/job/… → monitor
example/file_template/… → tool(默认)
模板类型:有 parent_id/pid 列 → ui2(树形模板),否则 ui(普通列表模板)
排序默认:有 sort 列 → "sort asc",否则 "id desc"
```
## 与 gf gen dao 的分工
| 工具 | 用途 |
|------|------|
| `gf gen dao -c hack/config.yaml` | **表结构变更**后刷新已有模块 dao(自定义模板挂钩子+Ctx;overwriteDao:false 保留手工扩展) |
| 生成器 | **新模块**一键产出(dao 四件与 gf gen dao 同构,生成后统一 gofmt 可直接编译) |
| `gf gen service` | **禁止整体运行**(service 接口为手工重组维护) |
## 保护策略速览
```text
skipTables:仅关联表 role_menu/user_role 整表跳过文件生成
(副作用照常执行)
文件粒度四态:缺失补生成(Generated)/ 已存在跳过(Skipped)
/ 开覆盖时覆盖(Overwritten)/ 失败(Failed)
覆盖开关唯一入口:generator.overwrite(config.yaml)
```
详见 [10.5 代码生成引擎](./code-engine) 与 [10.7 配置文件详解](./config)。
@@ -0,0 +1,97 @@
# 10.4 表结构解析引擎
:::tip 概述
解析引擎负责把数据库表翻译为生成元数据:表注释(MySQL information_schema / 非 MySQL 降级)、字段类型映射、注释智能解析(枚举/标签/图片/富文本识别)、字段分桶(列表/表单/筛选)。
:::
## 元数据来源
```text
MySQL:查 information_schema.TABLES
(表注释/引擎/行数/自增/排序规则/创建时间)
非 MySQL:gdb 元数据降级(仅表名,注释留空,模块名回退表名)
字段元数据:g.DB().TableFields(tableName)
按元数据 Index 还原物理列序
```
::: tip 生成建议
非 MySQL 库内省不到注释与枚举——**建议在 MySQL 上生成**,或生成后人工补注释再重新生成。
:::
## 类型映射(buildField,正则按 MySQL 类型串推断)
```text
tinyint(1) → BooleanField
int 族 → IntegerField
decimal/numeric → DecimalField
float/double → FloatField
date → DateField
datetime/timestamp → DateTimeField
blob/binary → BinaryField
text 族 → TextField
varchar/char/enum → CharField
Go 类型:bigint→int64;decimal/float→float64;时间列→*gtime.Time;
int 族→int;其余 string
行取值方法:.Int() / .Float64() / .String()
```
## 注释智能解析
```text
枚举:"状态:1-正常 2-禁用"(reChoice 支持 - — - = 分隔)
→ Choices 集合,标签类型 1→success 2→danger 3→warning 其余 info
表单标签:cleanLabel 去掉枚举尾巴保留中文名("状态")
图片字段:注释含 图片/头像/封面
或列名含 avatar/cover/image/icon/photo
富文本: 注释含 富文本
或 TextField 且列名含 content/body/editor
```
图片/富文本识别驱动 FileFields/ContentFields 声明与前端组件选型。
## 字段分桶(buildVars)
```text
审计列排除:
id 进列表(树形/分页需要 Id)
create_user/create_time 仅标记 HasCreateUser/HasCreateTime
update_user/update_time/del_flag/tenant_id 不进前后端字段
salt 不进表单
列表字段:排除富文本/password/salt/BinaryField
表单字段:全部业务列(BinaryField 除外)
筛选字段:CharField 非图片非 URL 列(website/site/url/link 不作模糊条件);
带枚举的 Integer/Boolean → Select;*_id 外键 Integer → InputNumber
显示字段(模糊搜索列)优先级:
name/title/label/username/realname/nickname/category_name/
job_name/dept_name/role_name/position_name/level_name
未命中取列表首列
```
## 命名推导
```text
moduleName = 表名去 goframe_ 前缀(config_item)
Pascal = 结构体类名(ConfigItem)
camelLower = 前端模块名(configItem)
权限前缀 = sys:{camelLower}(sys:configItem)
路由前缀 = moduleName(snake)
域归属 = genDomainMap(未匹配默认 tool)
模板类型 = 有 parent_id/pid 列 → ui2 树形,否则 ui 普通
```
## 建表侧的配合
解析质量取决于建表规范:
```text
1. 列注释按枚举规范写("状态:1-正常 2-禁用")→ 字典自动创建
2. 图片列注释含"图片"或用 avatar/cover 等列名 → FileFields 识别
3. 富文本列用 content 列名或注释"富文本" → ContentFields 识别
4. 索引 uk_* 规范命名 → 唯一性语义清晰
5. 审计列齐全(解析器自动排除)
```
@@ -0,0 +1,76 @@
# 10.6 模板开发
:::tip 概述
生成器模板在 templates/ 磁盘目录:后端模板用 Go text/template 默认 {{ }} 定界;前端模板因与 Vue 插值冲突改用 [[ ]] 定界。修改模板即改变生成产物风格。
:::
## 模板清单
```text
templates/
├── api.tpl api/v1/{module}.go
├── controller.tpl internal/controller/{module}.go
├── logic.tpl internal/logic/{module}.go(ModuleSpec 基座风格)
├── service.tpl internal/service/{module}.go(接口三件套)
├── dao_index.tpl internal/dao/{module}.go(挂钩子+Ctx 覆写)
├── dao_internal.tpl internal/dao/internal/{module}.go
├── entity.tpl internal/model/entity/{module}.go
├── do.tpl internal/model/do/{module}.go
├── ui/ 前端普通模板(定界符 [[ ]])
│ ├── api.ts.tpl
│ ├── columns.ts.tpl
│ ├── querySchemas.ts.tpl
│ ├── index.vue.tpl
│ ├── edit.vue.tpl
│ └── detail.vue.tpl
└── ui2/ 前端树形模板(无 columns/querySchemas)
├── api.ts.tpl index.vue.tpl edit.vue.tpl detail.vue.tpl
```
## 两套定界符(关键差异)
| 模板 | 定界符 | 原因 |
|------|--------|------|
| 后端 *.tpl | `{{ }}`(Go text/template 默认) | 无冲突 |
| templates/ui* | `[[ ]]`(Delims("[[","]]")) | Vue 模板插值也是 {{ }},会冲突 |
```text
后端模板片段:{{.Module}} {{range .Fields}}…
前端模板片段:[[.Module]] [[range .Fields]]… ← 生成后再渲染为 Vue 的 {{ }}
```
## 模板函数
```text
containsAny 字符串包含任一子串(字段分类判定)
isUrlField URL 列名判定(website/site/url/link 不作模糊筛选)
(其余上下文:buildVars 产出的 module/Pascal/Fields/Choices/HasStatus…)
```
## 修改模板的流程
```text
1. 编辑 templates/*.tpl(保持定界符约定)
2. 用 -preview 预览渲染结果(不落盘核对)
3. 生成样例表验证(建议用 example 表回归对比)
4. go build 检查产物可编译
5. 注意:overwrite=false 时已存在文件不会更新——
验证用新表或临时开 overwrite
```
## 与其他"模板"的区分
::: warning 两套模板互不相干
+ `templates/*.tpl`(本节)= 代码生成器渲染模板,随仓库版本管理
+ `goframe_file_template` 表 = 业务功能(在线管理文件/代码片段模板)
修改生成产物风格改 templates/*.tpl;不要去 file_template 表里找。
:::
## 定制建议
+ 团队规范(注释风格/方法命名)可改进模板一次、全组受益
+ 新增生成字段类型(如开关列特殊渲染)改 buildVars + 模板两侧
+ 模板变更走版本管理 + 团队评审(影响所有新生成模块)
+ 已生成模块不会随模板更新(除非 overwrite 重生成并人工合并)
+ hack/tpl/dao_index.tpl 是 gf gen dao 的自定义模板(与生成器模板同构但独立)——改一处需评估另一处
@@ -0,0 +1,80 @@
# 10.3 Web 管理界面
:::tip 概述
管理界面位于「工具 → 代码生成」(ui/src/views/tool/generator/):数据表清单分页浏览、代码预览、一键生成、批量生成。与 CLI 共用同一套生成核心。
:::
## 页面功能
| 功能 | 接口 | 权限 |
|------|------|------|
| 数据表清单 | GET /api/generator/page | sys:generator:page |
| 代码预览 | POST /api/generator/preview | sys:generator:generator |
| 一键生成 | POST /api/generator/generate | sys:generator:generator |
| 批量生成 | POST /api/generator/batchGenerate | sys:generator:batchGenerator |
页面文件:`ui/src/views/tool/generator/`(index.vue / edit.vue / columns.ts / querySchemas.ts——页面自身也是三件套模式)。
## 使用流程
```text
1. 打开「工具 → 代码生成」→ 数据表清单(含表注释/行数/引擎)
2. 选择目标表 → 「预览」查看将产出的文件与内容(不落盘)
3. 确认 → 「生成」→ 结果报告:
Generated[] / Skipped[] / Overwritten[] / Failed[]
Menu{Success, MenuId, Message}
Dict[]{Dict, Name, Items, Status}
Route{Success, Message}
4. 后端 go build + 重启
5. 角色管理勾选新权限节点 → 访问新菜单验证
```
## 预览的价值
```text
preview 返回 List[]{Path, Content}(渲染结果全文,不落盘):
* 生成前核对文件路径与命名(域名/驼峰是否符合预期)
* 核对筛选字段/枚举识别是否正确
* 作为 code review 的第一道关口
* 对照学习生成模板的产出风格
```
## 批量生成报告
```text
BatchGenerate 返回 List[]{Table, Success, Generated, Skipped, Overwritten, Failed, Message, Menu, Dict, Route}
Message 格式:"生成 N,跳过 N,覆盖 N,失败 N"
批量语义:
每表独立成败(一表失败不影响其他)
幂等副作用逐表执行(菜单已存在则跳过)
```
## 权限与安全
```text
生成器权限码特例:sys:generator:generator | sys:generator:batchGenerator
(非 sys:generator:generate——注意拼写)
日志 logType=generate(19 代码生成)——生成操作全留痕
生成器写文件系统(api/controller/logic 等目录)——收紧 generator 相关权限,
非管理员不应授予
```
## 界面操作注意
::: warning 覆盖确认
`generator.overwrite: true` 时生成即覆盖同名文件(含手工修改过的)——批量覆盖前:
1. 确认 config.yaml overwrite 设置
2. 手工改动多的模块临时改回 false
3. 或先 git 提交/备份再覆盖
:::
## 排障
| 症状 | 原因 |
|------|------|
| 表清单为空 | database 连接错/无表;非 MySQL 时注释列为空属正常 |
| 预览报错 | 模板渲染失败——查表结构特殊列(类型/注释) |
| 生成成功但菜单没出现 | 菜单创建成功但角色未勾选;或 parentMenuId 配置错(挂到了不存在的父级) |
| 生成成功但路由 404 | router.go 注册行写入失败(看 Route.Message);或未重启 |
| 字典没创建 | 枚举注释格式不对(须 `字段:1-x 2-y`);看 Dict 报告项状态 |