# 3.2 数据库建表 :::tip 概述 建表是模块开发的第一步。本项目对表结构有强约束:统一审计列、int 自增主键、del_flag 软删、uk_* 唯一索引、列注释枚举。这些规范不是建议——它们是全局钩子、字典系统、代码生成器正常工作的前提。 ::: ## 统一规范(25 张表齐备) ```sql -- 审计列(每张表必有) id int NOT NULL AUTO_INCREMENT COMMENT '主键ID', -- 数据库自增(禁用雪花) create_user varchar(50) DEFAULT '' COMMENT '创建人', create_time datetime DEFAULT (now()) COMMENT '创建时间', update_user varchar(50) DEFAULT '' COMMENT '更新人', update_time datetime DEFAULT (now()) COMMENT '更新时间', del_flag int DEFAULT 0 COMMENT '删除标志:0-正常 1-已删除', -- 租户级表(9 张)另有: tenant_id int DEFAULT 0 COMMENT '租户ID:0-平台级', PRIMARY KEY (id) ``` 关键决策(与直觉不同的点): ::: warning 关键约束 1. **主键统一 int 数据库自增**(2026-10-13 定稿):禁用雪花算法(`utility/id` 已删除);新增后用 `result.LastInsertId()` 回读;hooks 不填 id。 2. **删除一律软删**:deleteHook 把物理删除改写为 `UPDATE del_flag=1`,查询由 `dao.Ctx` 恒过滤 `del_flag=0`。 3. **无物理外键**:全部逻辑外键(如 article.category_id),跨表一致性由业务层保证。 ::: ## 唯一索引(uk_*,13 个定稿) 唯一索引是并发唯一性的最终保证(`UniqueFields` 预检仅为 UX 层,存在 TOCTOU 竞态)。命名规范 `uk_` 前缀: ```sql -- 示例:goframe_user 的三个唯一索引(均含 del_flag,软删行不占坑) UNIQUE KEY uk_tenant_username (tenant_id, username, del_flag) UNIQUE KEY uk_tenant_mobile (tenant_id, mobile, del_flag) UNIQUE KEY uk_tenant_code (tenant_id, code, del_flag) -- 全局唯一(平台级表) UNIQUE KEY uk_code (code) -- config / dict / param / file_template / tenant UNIQUE KEY uk_tenant_name (tenant_id, name, del_flag) -- dept / level / position / category ``` ::: tip 索引范围纪律 含 tenant_id 的唯一索引,模块唯一预检(UniqueFields 的 `Group:"tenant_id"` 或手写检查按行现租户)**范围必须等于索引范围**,否则漏报 1062 或跨租户误报。 ::: ## 列注释枚举(字典与生成器的数据源) 注释写法 `字段名:1-正常 2-禁用`(`-`、`—`、`=`、`=` 均可作分隔符),生成器解析为前端选项并自动建字典(编码规范 `{模块驼峰}_{字段驼峰}`): ```sql status int DEFAULT 1 COMMENT '状态:1-正常 2-禁用', -- 常见枚举定稿: -- article.status 0-正常 1-下架 -- menu.type 0-菜单 1-权限节点 -- job.status 0-未发布 1-运行中 2-暂停 -- job.execute_policy 1-立即执行 2-执行一次 3-放弃执行 -- job.is_sync 1-同步 0-异步 -- role.data_scope 1-全部 2-本部门及以下 3-本部门 4-仅本人 -- login_log.log_type 1-登录 2-退出 ``` ## 案例演示表(goframe_example) ```sql 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 图片字段(注释含"图片"触发生成器图片识别)。 ## 建表/变更工作流 ```text 1) 以 document/mysql/ 定稿为基准补 ALTER (含列注释枚举、uk_* 唯一索引;索引命名 uk_/idx_ 规范) 2) gf gen dao -c hack/config.yaml 刷新 dao/entity/do(service 手维护) 3) 同步 document/ 其余四方言脚本(人工校对) 4) 租户级表 → 修改 internal/model/hooks 的 tenantTables 清单 (生成器不自动维护此清单!) 5) go build ./... 重启;必要时 go run ./cmd/migrator 同步到其他环境库 ``` ## 租户级表判定 新增表是否带 `tenant_id` 决定它是否需要加入 `internal/model/hooks/hooks.go` 的 `tenantTables` 清单: | 类型 | 现有表 | 行为 | |------|--------|------| | 租户级(9 张) | user/role/dept/level/position/article/category/notice/link | 自动过滤 `tenant_id IN(0,自身)`、插入自动盖章 | | 平台级 | 其余 16 张 | 无租户过滤(超管可见全量) | 漏加清单的后果:租户级表不隔离,所有租户互相可见数据——务必同步。