签入脚本

This commit is contained in:
小蚂蚁云
2026-10-20 11:37:25 +08:00
commit 7f5e8470af
909 changed files with 4045080 additions and 0 deletions
+212
View File
@@ -0,0 +1,212 @@
================================================================================
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 上生成或人工补注释。