Files
EasyGoAdmin_GoFrame_EleVue/document/wiki/07_代码生成器.txt
T
2026-10-20 11:37:25 +08:00

213 lines
12 KiB
Plaintext
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.
================================================================================
EasyGoAdmin 后端开发者文档 · 07 代码生成器(tool 域 + CLI)
================================================================================
代码位置:internal/logic/generator.go(核心)、internal/logic/generator_side.go
(副作用:菜单/字典/路由)、cmd/generator/main.go(CLI)、templates/(模板)
--------------------------------------------------------------------------------
1. 双入口
--------------------------------------------------------------------------------
HTTP(/api/generator,管理页面“代码生成”):
GET /api/generator/page perm sys:generator:page 数据表清单(分页)
POST /api/generator/preview perm sys:generator:generator 预览(不落盘)
POST /api/generator/generate perm sys:generator:generator 一键生成
POST /api/generator/batchGenerate perm sys:generator:batchGenerator 批量生成
日志 logType=generate(19 代码生成)
CLI(仓库根目录执行,读取同一份 config.yaml):
go run ./cmd/generator -tables "goframe_order|订单表,goframe_sku|商品规格"
go run ./cmd/generator -tables "goframe_order|订单表" -preview # 仅预览清单
go run ./cmd/generator -tables "goframe_order|订单表" -batch # 逐表统计输出
-tables 格式:"表名|表注释"(注释可省略,|后内容被忽略),逗号分隔多表。
CLI 空白 import internal/logic 以注册全部服务,直接调用
service.Generator().Preview/Generate/BatchGenerate。
生成后手动执行 go build ./... 并重启服务。
--------------------------------------------------------------------------------
2. 表结构内省
--------------------------------------------------------------------------------
MySQL:查 information_schema.TABLES(表注释/引擎/行数/自增/排序规则/创建时间)
非 MySQL:gdb 元数据降级(仅表名,注释留空,模块名回退表名)。
字段元数据:g.DB().TableFields(tableName),按元数据 Index 还原物理列序。
类型映射(buildField,正则按 MySQL 类型串推断):
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()。
注释智能解析:
枚举:"状态:1-正常 2-禁用"(reChoice 支持 - — - = 分隔)→ Choices 集合,
标签类型 1→success 2→danger 3→warning 其余 info。
表单标签:cleanLabel 去掉枚举尾巴保留中文名。
图片字段:注释含 图片/头像/封面 或列名含 avatar/cover/image/icon/photo。
富文本:注释含 富文本 或 TextField 且列名含 content/body/editor。
字段集合划分(buildVars):
审计列排除: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,
未命中取列表首列。
模块命名:moduleName = 表名去 goframe_ 前缀;Pascal → 结构体类名(ConfigItem);
camelLower → 前端模块名(configItem);权限前缀 sys:{camelLower};
路由前缀 = moduleName(snake,如 /api/config_item 实际以生成模板为准)。
域归属:genDomainMap(user/role/…→system,config/…→data,article/…→content,
operation_log/…→monitor,example/…→tool),未匹配默认 tool。
模板类型:有 parent_id/pid 列 → ui2(树形),否则 ui(普通列表)。
排序:有 sort 列 → "sort asc",否则 "id desc"。
--------------------------------------------------------------------------------
3. 产出文件(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 插值冲突):
ui 普通:src/api/{domain}/{moduleCamel}.ts、
src/views/{domain}/{moduleCamel}/{index.vue, edit.vue, detail.vue,
columns.ts, querySchemas.ts}
ui2 树形:src/api/{domain}/{moduleCamel}.ts、
src/views/{domain}/{moduleCamel}/{index.vue, edit.vue, detail.vue}
模板语法:Go text/template;后端模板默认 {{ }};templates/ui* 用 Delims("[[","]])。
模板函数:containsAny、isUrlField(URL 列名判定)。
--------------------------------------------------------------------------------
4. 落盘保护策略
--------------------------------------------------------------------------------
skipTables(整表跳过文件生成):仅关联表 role_menu、user_role——
跳过文件写入,但幂等副作用(菜单/字典/路由)照常执行。
writeFile 文件粒度四态:
fileWritten 目标不存在 → 新写入(计入 Generated)
fileExists 已存在且未开覆盖 → 跳过(计入 Skipped,保留手工代码)
fileOverwritten 已存在且开启覆盖 → 覆盖(计入 Overwritten)
fileFailed 写入/建目录失败(计入 Failed)
覆盖开关唯一入口:generator.overwrite(config.yaml;true=生成即覆盖,
false/未配置=缺失补生成、存在跳过)。
4.1 writeFile 落盘决策图
目标文件存在?
/ \
否 是
│ │
写入 generator.overwrite?
Generated / \
true false/缺省
│ │
覆盖写 跳过保留
Overwritten Skipped
\ /
任一失败 → Failed(MkdirAll/WriteFile 错误)
4.2 生成器总体流水线图
输入 tables[]("表名|注释")
│
▼
buildVars:TableFields 内省 → 类型/注释解析 → 字段分桶(列表/表单/筛选)
│ 命名:module/Pascal/camelLower;域归属;ui|ui2 模板类型
▼
skipTables? ──是(role_menu/user_role)──▶ 仅 buildVars(副作用用),跳过文件
│ 否
▼
renderTable:逐模板渲染(后端 {{ }} / 前端 [[ ]])→ go 产物 gofmt
│
▼
writeFile×N(决策图见 4.1)
│
▼
副作用(幂等):createMenu(+权限节点) → createDict(枚举字段) → registerRouter
│
▼
结果报告(Generated/Skipped/Overwritten/Failed + Menu/Dict/Route)
│
▼
提示:go build ./... 并重启服务
--------------------------------------------------------------------------------
5. 幂等副作用(generator_side.go)
--------------------------------------------------------------------------------
5.1 菜单 + 权限节点(createMenu)
主菜单:parent_id = generator.parentMenuId(默认 189),
path="/{domain}/{moduleCamel}",component=path+"/index",
type=0,sort=同级 MAX(sort)+5,note="代码生成器自动创建"。
按 path+type=0 查重 → 已存在直接返回(幂等)。
权限节点(type=1,挂在主菜单下,permission={PermissionPrefix}:{action}):
page/list/detail/add/update/delete/batchDelete 固定 7 个;
HasStatus 追加 :status;HasImport/HasExport 追加 :import/:export
(生成器默认二者均为 true)。sort 逐个 +5。
5.2 字典(createDict)
对每个带枚举注释的表单字段:code = {moduleCamel}_{camelField}
(如 user_status),按 code 查重 → 已存在跳过;
否则建 dict(name="{模块中文名}-{字段中文名}",note 自动创建标注)+
逐 Choice 建 dict_item(name=label,value,sort+5)。
结果逐项返回(已存在/创建成功/失败原因)。
5.3 路由注册(registerRouter)
目标文件 internal/router/router.go:
已含 new(controller.C{ModelClass}) → 返回“路由已存在”(幂等);
在最后一个 "\n\tBind(s, new(" 行之后插入
"\n\tBind(s, new(controller.C{ModelClass})) // 代码生成器注册"。
失败情形(返回消息,不中断):路由文件不存在、缺 controller 导入、
未找到锚点。
6. 生成器模板(templates/ 磁盘)与 file_template 表的区分(易混淆)
两套“模板”互不相干:
* templates/*.tpl(磁盘目录):代码生成器的渲染模板,随仓库版本管理,
generator.go 直接读取渲染;修改模板即改变生成产物风格。
* goframe_file_template 表 + /api/file/template 模块:业务功能(在线管理
文件/代码片段模板),提供 getTemplateByCode 查询;与代码生成器流水线
无调用关系。
修改生成产物改 templates/*.tpl;不要到 file_template 表里找生成器模板。
--------------------------------------------------------------------------------
7. 结果报告结构
--------------------------------------------------------------------------------
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}(渲染结果全文,不落盘)。
CLI 输出按上述分组打印(+ 生成 / - 跳过 / * 覆盖 / ! 失败),并提示
“下一步: go build ./... 并重启服务”。
--------------------------------------------------------------------------------
8. 与 gf gen dao 的分工
--------------------------------------------------------------------------------
gf gen dao -c hack/config.yaml 表结构变更后刷新已有模块 dao
(自定义模板 hack/tpl/dao_index.tpl:挂全局钩子 + Ctx 覆写;
overwriteDao:false 保留手工扩展)
生成器 新模块一键产出(dao 四件与 gf gen dao 同构,
生成后统一 gofmt,可直接编译)
gf gen service 禁止整体运行(service 接口为手工重组维护)
--------------------------------------------------------------------------------
9. 注意事项
--------------------------------------------------------------------------------
* generator.overwrite=true 会覆盖同名文件(包括手工修改过的)——批量覆盖前务必备份
或临时改回 false。
* 生成的 logic 是 ModuleSpec 基座风格的完整可用实现(含筛选/树形/字典/导入导出),
复杂业务需再手工扩展钩子。
* 新模块若为租户级表,需同步把表名加入 internal/model/hooks 的 tenantTables。
* 菜单创建后需在角色管理中为相关角色勾选权限节点方可访问。
* 非 MySQL 库生成时注释/枚举解析缺失,建议在 MySQL 上生成或人工补注释。