Co-authored-by: Cursor <cursoragent@cursor.com>
28 KiB
HoTime 代码生成
前置依赖:生成前请先满足 数据库设计规范(表命名、外键、COMMENT、必有字段)。代码生成器依赖规范的表结构与字段备注,才能正确识别类型、选项、外键关联与显示标签。
code 包提供了 HoTime 框架的自动代码生成功能,能够根据数据库表结构自动生成 CRUD 接口代码和配置文件。
目录
一、功能概述
代码生成器可以:
- 自动读取数据库表结构 - 支持 MySQL、SQLite 和达梦 DM8
- 生成 CRUD 接口 - 增删改查、搜索、分页
- 生成配置文件 - 表字段配置、菜单配置、权限配置
- 智能字段识别 - 根据字段名自动识别类型和权限
- 支持表关联 - 自动识别外键关系
配置体系概览:
config.json
└── codeConfig[] # 代码生成配置数组(支持多套)
├── config # 菜单权限配置文件路径(如 admin.json)
├── configDB # 数据库生成的完整配置
├── rule # 字段规则配置文件路径(如 rule.json)
├── table # 管理员表名
├── name # 生成代码的包名
└── mode # 生成模式
二、使用方法
2.1 基础配置
在 config.json 中配置开发模式与代码生成:
{
"mode": 2,
"codeConfig": [
{
"table": "admin",
"config": "config/admin.json",
"rule": "config/rule.json",
"mode": 0
}
]
}
2.2 启动应用
package main
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
func main() {
app := Init("config/config.json")
// 代码生成器在 Init 时自动执行
// 会读取数据库结构并生成配置
app.Run(Router{
// 路由配置
})
}
2.3 开发模式
在 config.json 中设置 "mode": 2(开发模式)时:
- 自动读取数据库表结构
- 自动生成/更新配置文件
- 自动生成代码(如果
codeConfig.mode=1)
2.4 推荐开发流程
- 设置
config.json中mode: 2(开发模式) - 按 数据库设计规范 设计表结构并添加字段备注
- 启动应用,自动生成配置
- 检查生成的配置文件,按需调整
- 生产环境改为
mode: 0
三、配置体系
3.1 codeConfig
配置结构
{
"codeConfig": [
{
"config": "config/admin.json",
"configDB": "config/adminDB.json",
"mode": 0,
"name": "",
"rule": "config/rule.json",
"table": "admin"
}
]
}
配置项说明
| 字段 | 类型 | 必须 | 说明 |
|---|---|---|---|
table |
string | ✅ | 管理员/用户表名,用于身份验证和权限控制 |
config |
string | ✅ | 菜单权限配置文件路径,用于定义菜单结构、权限控制 |
configDB |
string | ❌ | 代码生成器输出的完整配置文件(有则每次自动生成) |
rule |
string | ❌ | 字段规则配置文件路径,无则使用默认规则 |
name |
string | ❌ | 生成的代码包名和目录名,为空则不生成独立代码文件 |
mode |
int | ❌ | 生成模式:0=仅配置不生成代码(内嵌模式),非 0=生成代码文件 |
运行模式
- mode=0(内嵌模式):不生成独立代码文件,使用框架内置的通用控制器
- mode≠0(生成模式):为每张表生成独立的 Go 控制器文件
多配置支持
可以配置多个独立的代码生成实例,适用于多端场景:
{
"codeConfig": [
{
"config": "config/admin.json",
"table": "admin",
"rule": "config/rule.json"
},
{
"config": "config/user.json",
"table": "user",
"rule": "config/rule.json"
}
]
}
3.2 菜单权限配置(admin.json)
配置文件更新机制
- 首次运行:代码生成器会根据数据库表结构自动创建配置文件(如 admin.json)
- 后续运行:配置文件不会自动更新,避免覆盖手动修改的内容
- 重新生成:如需重新生成,删除配置文件后重新运行即可
- 参考更新:可参考
configDB指定的文件(如 adminDB.json)查看最新的数据库结构变化,手动调整配置
完整配置结构
{
"id": "唯一标识(自动生成)",
"name": "admin",
"label": "管理平台名称",
"labelConfig": { ... },
"menus": [ ... ],
"flow": { ... }
}
label / labelConfig
{
"label": "HoTime管理平台",
"labelConfig": {
"show": "开启",
"add": "添加",
"delete": "删除",
"edit": "编辑",
"info": "查看详情",
"download": "下载清单"
}
}
| 操作 | 说明 |
|---|---|
| show | 显示/查看列表权限 |
| add | 添加数据权限 |
| delete | 删除数据权限 |
| edit | 编辑数据权限 |
| info | 查看详情权限 |
| download | 下载/导出权限 |
menus 配置
定义菜单结构,支持多级嵌套(目前只支持两级菜单):
{
"menus": [
{
"label": "系统管理",
"name": "sys",
"icon": "Setting",
"auth": ["show"],
"menus": [
{
"label": "用户管理",
"table": "user",
"auth": ["show", "add", "delete", "edit", "info", "download"]
},
{
"label": "角色管理",
"table": "role",
"auth": ["show", "add", "delete", "edit", "info"]
}
]
},
{
"label": "文章管理",
"table": "article",
"icon": "Document",
"auth": ["show", "add", "edit", "info"]
}
]
}
| 字段 | 类型 | 说明 |
|---|---|---|
| label | string | 菜单显示名称 |
| name | string | 菜单标识(用于分组,不绑定表时使用) |
| table | string | 绑定的数据表名(用于自动生成 CRUD) |
| icon | string | 菜单图标名称(Element Plus 图标名,如 Setting、User、Document) |
| auth | array | 权限数组,定义该菜单/表拥有的操作权限 |
| menus | array | 子菜单数组(支持嵌套) |
name vs table:
- table:绑定数据表,拥有该表的增删查改等权限,自动生成 CRUD 接口
- name:自定义功能标识,不绑定表,前端根据 name 和 auth 显示自定义内容(如首页 home、仪表盘 dashboard)
- 配置了
menus子菜单 → 作为分组 - 没有
menus→ 作为独立自定义功能入口
- 配置了
// 使用 table:绑定数据表
{ "label": "用户管理", "table": "user", "auth": ["show", "add", "edit", "delete"] }
// 使用 name:自定义功能(无子菜单)
{ "label": "首页", "name": "home", "icon": "House", "auth": ["show"] }
// 使用 name:分组功能(有子菜单)
{ "label": "系统管理", "name": "sys", "icon": "Setting", "auth": ["show"], "menus": [...] }
自动分组规则:代码生成器会根据表名的 _ 分词自动分组:
- 表名
sys_logs、sys_menus、sys_config→ 归入sys分组 - 表名
article、article_tag→ 归入article分组
分组的显示名称(label)使用该分组下第一张表的名字,如果表有备注则使用备注名。
auth 配置
| 权限 | 对应接口 | 说明 |
|---|---|---|
| show | /search | 列表查询 |
| add | /add | 新增数据 |
| delete | /remove | 删除数据 |
| edit | /update | 编辑数据 |
| info | /info | 查看详情 |
| download | /search?download=1 | 导出数据 |
auth 数组可以自由增删。新增的权限项会在前端菜单/功能中显示,并在角色管理(role)的权限设置中供管理员分配:
{
"label": "文章管理",
"table": "article",
"auth": ["show", "add", "edit", "delete", "info", "publish", "audit", "top"]
}
上例中 publish(发布)、audit(审核)、top(置顶)为自定义权限。
flow 配置(数据权限)
flow 用于限制用户只能操作自己权限范围内的数据。
{
"flow": {
"role": {
"table": "role",
"stop": true,
"sql": {
"id": "role_id"
}
},
"article": {
"table": "article",
"stop": false,
"sql": {
"admin_id": "id"
}
},
"org": {
"table": "org",
"stop": false,
"sql": {
"parent_ids[~]": ",org_id,"
}
}
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
| table | string | 表名 |
| stop | bool | 是否禁止修改自身关联的数据 |
| sql | object | 数据过滤条件,自动填充查询/操作条件 |
stop:防止用户修改自己当前关联的敏感数据。例如当前用户 role_id = 1,配置 stop: true 后,不能修改 role 表中 id = 1 的记录,但可修改其他 role(若有权限)。典型用途:防止用户提升自己的角色权限、修改自己所属组织。
sql:格式为 { "目标表字段": "当前用户字段" }。
| 格式 | 含义 | SQL 等价 |
|---|---|---|
"field": "user_field" |
精确匹配 | field = 用户.user_field |
"field[~]": ",value," |
模糊匹配 | field LIKE '%,value,%' |
示例效果(当前用户 { "id": 5, "role_id": 2, "org_id": 10 }):
| 表 | 查询条件 | stop 效果 |
|---|---|---|
| role | WHERE id = 2 |
不能修改 id=2 的角色 |
| article | WHERE admin_id = 5 |
可以修改自己的文章 |
| org | WHERE parent_ids LIKE '%,10,%' |
可以修改下级组织 |
完整 admin.json 示例
{
"id": "74a8a59407fa7d6c7fcdc85742dbae57",
"name": "admin",
"label": "后台管理系统",
"labelConfig": {
"show": "开启",
"add": "添加",
"delete": "删除",
"edit": "编辑",
"info": "查看详情",
"download": "下载清单"
},
"menus": [
{
"label": "系统管理",
"name": "sys",
"icon": "Setting",
"auth": ["show"],
"menus": [
{
"label": "日志管理",
"table": "logs",
"auth": ["show", "download"]
},
{
"label": "角色管理",
"table": "role",
"auth": ["show", "add", "delete", "edit", "info"]
},
{
"label": "组织管理",
"table": "org",
"auth": ["show", "add", "delete", "edit", "info"]
},
{
"label": "员工管理",
"table": "admin",
"auth": ["show", "add", "delete", "edit", "info", "download"]
}
]
}
],
"flow": {
"admin": {
"table": "admin",
"stop": false,
"sql": { "role_id": "role_id" }
},
"role": {
"table": "role",
"stop": true,
"sql": { "admin_id": "id", "id": "role_id" }
},
"org": {
"table": "org",
"stop": false,
"sql": { "admin_id": "id" }
},
"logs": {
"table": "logs",
"stop": false,
"sql": {}
}
}
}
说明:生成器还会在配置中产生
tables(字段列、搜索项等)。首次生成可参考configDB(如 adminDB.json)中的结构;持久化自定义请写入config指定的文件(如 admin.json),因 configDB 会在每次启动时重新生成。
3.3 字段规则配置(rule.json)
定义字段在增删改查操作中的默认行为。
配置结构
[
{
"name": "id",
"add": false,
"edit": false,
"info": true,
"list": true,
"must": false,
"strict": true,
"type": ""
},
{
"name": "status",
"list": true,
"add": true,
"edit": true,
"info": true,
"must": false,
"strict": false,
"type": "select"
},
{
"name": "user.special_field",
"list": true,
"add": true,
"edit": true,
"info": true,
"type": "text",
"strict": true
}
]
字段属性说明
| 属性 | 类型 | 说明 |
|---|---|---|
| name | string | 字段名或关键词;支持 表名.字段名 精确匹配 |
| add | bool | 新增时是否显示 |
| edit | bool | 编辑时是否显示 |
| info | bool | 详情页是否显示 |
| list | bool | 列表页是否显示 |
| must | bool | 是否必填(见下方) |
| strict | bool | 是否严格匹配字段名(true=完全匹配,false=包含匹配) |
| type | string | 字段类型(影响前端控件和数据处理) |
must 必填字段规则
自动识别:
- MySQL:字段为
NOT NULL(IS_NULLABLE='NO')时自动must=true - SQLite:字段为主键(
pk=1)时自动must=true
规则覆盖:rule.json 中的 must 会覆盖数据库自动识别结果。
效果:
- 前端:
must=true显示必填标记(*),提交时校验 - 后端:新增时若
must=true字段为空,返回「请求参数不足」
type 类型说明
| 类型 | 说明 | 前端控件 |
|---|---|---|
| (空) | 普通文本 | 文本输入框 |
| text | 文本 | 文本输入框 |
| number | 数字 | 数字输入框 |
| select | 选择 | 下拉选择框(根据注释自动生成选项) |
| time | 时间(datetime) | 日期时间选择器 |
| unixTime | 时间戳 | 日期时间选择器(存储为 Unix 时间戳) |
| password | 密码 | 密码输入框(自动 MD5 加密) |
| textArea | 多行文本 | 文本域 |
| image | 图片 | 图片上传 |
| file | 文件 | 文件上传 |
| money | 金额 | 金额输入框 |
| auth | 权限 | 权限树选择器 |
| form | 表单 | 动态表单 |
| index | 索引 | 隐藏字段(用于 parent_ids 等) |
| tree | 树形选择 | 树形选择器 |
| table | 动态表 | 表名选择器 |
| table_id | 动态表ID | 根据 table 字段动态关联 |
内置字段规则(可在 rule.json 中覆盖)
主键和索引:
{"name": "id", "add": false, "list": true, "edit": false, "info": true, "strict": true}
{"name": "sn", "add": false, "list": true, "edit": false, "info": true}
{"name": "parent_ids", "add": false, "list": false, "edit": false, "info": false, "type": "index", "strict": true}
{"name": "index", "add": false, "list": false, "edit": false, "info": false, "type": "index", "strict": true}
层级关系:
{"name": "parent_id", "add": true, "list": true, "edit": true, "info": true}
{"name": "level", "add": false, "list": false, "edit": false, "info": true}
时间字段:
{"name": "create_time", "add": false, "list": false, "edit": false, "info": true, "type": "time", "strict": true}
{"name": "modify_time", "add": false, "list": true, "edit": false, "info": true, "type": "time", "strict": true}
{"name": "time", "add": true, "list": true, "edit": true, "info": true, "type": "time"}
状态字段:
{"name": "status", "add": true, "list": true, "edit": true, "info": true, "type": "select"}
{"name": "state", "add": true, "list": true, "edit": true, "info": true, "type": "select"}
{"name": "sex", "add": true, "list": true, "edit": true, "info": true, "type": "select"}
敏感字段:
{"name": "password", "add": true, "list": false, "edit": true, "info": false, "type": "password"}
{"name": "pwd", "add": true, "list": false, "edit": true, "info": false, "type": "password"}
{"name": "delete", "add": false, "list": false, "edit": false, "info": false}
{"name": "version", "add": false, "list": false, "edit": false, "info": false}
媒体字段:
{"name": "image", "add": true, "list": false, "edit": true, "info": true, "type": "image"}
{"name": "img", "add": true, "list": false, "edit": true, "info": true, "type": "image"}
{"name": "avatar", "add": true, "list": false, "edit": true, "info": true, "type": "image"}
{"name": "icon", "add": true, "list": false, "edit": true, "info": true, "type": "image"}
{"name": "file", "add": true, "list": false, "edit": true, "info": true, "type": "file"}
文本字段:
{"name": "info", "add": true, "list": false, "edit": true, "info": true, "type": "textArea"}
{"name": "content", "add": true, "list": false, "edit": true, "info": true, "type": "textArea"}
{"name": "description", "add": true, "list": false, "edit": true, "info": true}
{"name": "note", "add": true, "list": false, "edit": true, "info": true}
{"name": "address", "add": true, "list": true, "edit": true, "info": true}
特殊字段:
{"name": "amount", "add": true, "list": true, "edit": true, "info": true, "type": "money", "strict": true}
{"name": "auth", "add": true, "list": false, "edit": true, "info": true, "type": "auth", "strict": true}
{"name": "rule", "add": true, "list": true, "edit": true, "info": true, "type": "form"}
{"name": "table", "add": false, "list": true, "edit": false, "info": true, "type": "table"}
{"name": "table_id", "add": false, "list": true, "edit": false, "info": true, "type": "table_id"}
自定义字段规则
[
{
"name": "company_name",
"add": true,
"edit": true,
"info": true,
"list": true,
"must": true,
"strict": true,
"type": ""
},
{
"name": "user.nickname",
"add": true,
"edit": true,
"info": true,
"list": true,
"strict": true,
"type": ""
}
]
3.4 配置检查清单
codeConfig
- config 文件路径正确
- rule 文件路径正确
- table 指定的管理员表存在
菜单权限配置
- 所有 table 指向的表在数据库中存在
- auth 数组包含需要的权限
- menus 结构正确(有子菜单用 name,无子菜单用 table)
- flow 配置的 sql 条件字段存在
字段规则配置
- strict=true 的规则字段名完全匹配
- type 类型与前端控件需求一致
- 敏感字段(password 等)的 list 和 info 为 false
四、生成规则与产物
4.1 默认字段规则
代码生成器内置的字段识别一览(完整内置规则见 3.3):
| 字段名 | 列表显示 | 新增 | 编辑 | 详情 | 类型 |
|---|---|---|---|---|---|
id |
✅ | ❌ | ❌ | ✅ | number |
name |
✅ | ✅ | ✅ | ✅ | text |
status |
✅ | ✅ | ✅ | ✅ | select |
create_time |
❌ | ❌ | ❌ | ✅ | time |
modify_time |
✅ | ❌ | ❌ | ✅ | time |
password |
❌ | ✅ | ✅ | ❌ | password |
image/img/avatar |
❌ | ✅ | ✅ | ✅ | image |
file |
❌ | ✅ | ✅ | ✅ | file |
content/info |
❌ | ✅ | ✅ | ✅ | textArea |
parent_id |
✅ | ✅ | ✅ | ✅ | number |
parent_ids/index |
❌ | ❌ | ❌ | ❌ | index |
delete |
❌ | ❌ | ❌ | ❌ | - |
4.2 数据类型映射
| 数据库类型 | 生成类型 |
|---|---|
int, integer, float, double, decimal |
number |
char, varchar, text, blob |
text |
date, datetime, time, timestamp, year |
time |
4.3 字段备注解析
字段 COMMENT 的完整语法、选项写法与设计约定,以 数据库设计规范 为准。此处仅说明生成器如何消费备注。
支持从数据库字段备注中提取标签、提示与选项(括号支持 ()、()、{}):
-- 格式: 标签名(提示信息):选项1-名称1,选项2-名称2
status TINYINT COMMENT '状态(用户账号状态):0-禁用,1-启用'
parent_id INT COMMENT '父级ID(顶级为NULL)'
phone VARCHAR(20) COMMENT '手机号(请输入11位手机号)'
生成的配置示例:
{
"name": "status",
"label": "状态",
"type": "select",
"ps": "用户账号状态",
"options": [
{"name": "禁用", "value": "0"},
{"name": "启用", "value": "1"}
]
}
ps 字段在前端的展示效果:
- 编辑/新增页面:输入框右侧灰色小字提示
- 表格/详情页面:鼠标悬停字段名时气泡提示
4.4 外键关联
代码生成器会自动识别 _id 结尾的字段作为外键:
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
role_id INT, -- 自动关联 role 表
org_id INT -- 自动关联 org 表
);
生成的配置会包含 link 和 value 字段:
{
"name": "role_id",
"type": "number",
"label": "角色",
"link": "role",
"value": "name"
}
parent_id 会被识别为树形结构的父级关联:
{
"name": "parent_id",
"type": "number",
"label": "上级",
"link": "org",
"value": "name"
}
表命名、外键命名与 COMMENT 约定见 数据库设计规范。
树形表查询参数(parent_id / showself / showall)
带 parent_id 列的表,通用 CRUD 的 search 接口支持树查询参数,语义如下(code/makecode.go 树节点分支):
| 请求参数组合 | WHERE 语义 | 典型场景 |
|---|---|---|
不传 parent_id 或 parent_id=0 |
id=当前用户锚点 OR parent_id IS NULL(返回根层) |
树侧栏根节点加载 |
parent_id=X |
parent_id=X,仅直接子级 |
树节点懒加载展开 |
parent_id=X&showall=1 |
parent_ids LIKE '%,X,%',X 及其全部子孙(X 自身的 parent_ids 含 ,X,) |
列表按树节点过滤 |
parent_id=X&showall=1&showself=1 |
在 showall 基础上再 OR id=X,显式保证含 X 自身 |
管理端列表树筛选(Table.vue) |
规则:showself=1 仅在 showall=1 时生效。普通子级查询(仅 parent_id=X)不会把 id=X 的行本身混入结果——否则前端树会把节点当作自己的子级,造成无限嵌套(历史缺陷,已修复;回归用例见 example/app/makecode_tree_test.go,go test ./app/ -count=1 -run 'TestApi/admin/department/search')。
通用列表字段筛选语义(text 类型只走模糊匹配)
通用 CRUD 的 search 接口为每个 list 可见字段自动生成筛选条件(code/makecode.go (*MakeCode).Search),按字段最终生效的 type(见 4.2 数据类型映射,可被 3.3 字段规则配置(rule.json) 覆盖)分两种语义:
| 字段 type | 请求 ?字段名=值 的 WHERE 语义 |
说明 |
|---|---|---|
含 "text" 子串(text/textArea) |
字段名 LIKE '%值%'(模糊,仅此一条) |
前端筛选框允许只填部分关键词 |
其他(number/select/time/money… 等非 text) |
字段名 = 值(精确等值) |
维持现状不变 |
规则:文本类型字段只生成模糊匹配条件,不再叠加同名等值条件。历史缺陷:曾对所有 list 字段先生成等值条件 字段名=值,再对 text 类型额外叠加模糊条件 字段名[~]=值,两者在 where 里以 AND 叠加——前端筛选框只传部分关键词时等值条件必不命中,AND 之后整条查询恒为空(下游"文本列筛选查不出数据"的通用根因)。已修复;keyword+keywordtable 全局关键词搜索逻辑不受影响。回归用例见 example/app/makecode_tree_test.go 的「文本字段部分关键词命中模糊匹配」「非文本字段等值筛选行为不变」两个用例,go test ./app/ -count=1 -run 'TestApi/admin/department/search'。
4.5 生成的代码结构
内嵌模式 (mode=0)
不生成代码文件,使用框架内置控制器,只生成配置文件:
config/
├── admin.json # 接口/菜单权限配置
├── adminDB.json # 数据库结构配置(可选)
└── rule.json # 字段规则
生成模式 (mode≠0)
生成独立的控制器代码:
admin/ # 生成的包目录(由 codeConfig.name 决定)
├── init.go # 包初始化和路由注册
├── user.go # user 表控制器
├── role.go # role 表控制器
└── ... # 其他表控制器
生成的控制器结构
package admin
var userCtr = Ctr{
"info": func(that *Context) {
// 查询单条记录
},
"add": func(that *Context) {
// 新增记录
},
"update": func(that *Context) {
// 更新记录
},
"remove": func(that *Context) {
// 删除记录
},
"search": func(that *Context) {
// 搜索列表(分页)
},
}
4.6 多库与备注注意事项
达梦 DM8
代码生成器已原生支持达梦 DM8,会自动读取表结构和字段备注。注意:
- 表注释通过
COMMENT ON TABLE单独设置,建表后需额外执行 - 字段备注通过
COMMENT ON COLUMN设置 - 已对 DM 的
USER_TAB_COLUMNS与 MySQLinformation_schema差异做兼容
COMMENT ON TABLE "user" IS '用户管理';
COMMENT ON COLUMN "user"."state" IS '状态:0-正常,1-异常,2-隐藏';
COMMENT ON COLUMN "user"."name" IS '用户名';
SQLite 备注替代方案
SQLite 不支持表/字段 COMMENT,生成器会使用表名/字段名作为默认显示名称。可利用配置覆盖机制手动设置:
- 首次运行生成配置文件(如 admin.json)
- 在菜单中设置表的
label - 在
tables.表名.columns中设置字段label、ps、options等
{
"tables": {
"user": {
"label": "用户管理",
"columns": [
{
"name": "name",
"label": "用户名",
"ps": "请输入用户名",
"must": true
},
{
"name": "status",
"label": "状态",
"type": "select",
"options": [
{"name": "正常", "value": "0"},
{"name": "禁用", "value": "1"}
]
}
]
}
}
}
注意:configDB 文件每次启动会重新生成。持久化修改应写入 config 指定的文件(如 admin.json)。
4.7 最佳实践
- 字段命名:使用规范字段名(
id、name、status、create_time、modify_time、xxx_id、parent_id、avatar、content等),便于自动识别。完整约定见 数据库设计规范。 - 自定义扩展:默认规则不足时,可修改
rule.json、使用生成模式后手动改代码,或直接调整配置文件中的字段属性。 - 生产环境:开发用
mode: 2,上线改为mode: 0,避免运行时反复改写配置/代码。
五、相关文档
- 数据库设计规范(代码生成前置依赖)
- HoTimeDB 使用说明
- 快速上手
- Common 工具类