213 lines
12 KiB
Plaintext
213 lines
12 KiB
Plaintext
================================================================================
|
||
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 上生成或人工补注释。
|