Files
xuanzhi-service/server/.ai-transition/remake/gorm-auto-migrate-and-upgrade-sql.md
wdh-home 1e33640629
Some checks failed
CI / init (pull_request) Has been cancelled
CI / Frontend node 18.16.0 (pull_request) Has been cancelled
CI / Backend go (1.22) (pull_request) Has been cancelled
CI / release-pr (pull_request) Has been cancelled
CI / devops-test (1.22, 18.16.0) (pull_request) Has been cancelled
CI / release-please (pull_request) Has been cancelled
CI / devops-prod (1.22, 18.x) (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
基础项目
2026-04-26 15:32:21 +08:00

87 lines
5.3 KiB
Markdown
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.
# GORM 自动迁移与升级 SQL 交接说明
## 背景
- 当前服务启动时会执行 `main.go -> initialize.RegisterTables() -> initialize.bizModel()`
- `initialize/gorm_biz.go` 中注册了 `book` 业务模型,并调用 `db.AutoMigrate(...)`
- 因此即使没有手动执行 `.sql` 文件,只要服务启动并连接到数据库,`book` 相关业务表也可能被 GORM 自动创建。
## AutoMigrate 的信息来源
- GORM 不读取 `.ai-specs/doc-sql/*.sql` 中的基线 SQL。
- 表名来自各模型的 `TableName()`
- 字段名来自 Go struct 字段名的 snake_case 映射。
- 字段类型、默认值、非空、索引、唯一索引、注释主要来自 `gorm` tag。
- 基础字段来自模型嵌入结构,例如 `model/book/base.go``HardDeleteModel`
示例:
```go
Title string `gorm:"type:varchar(255);not null;comment:书名主标题"`
BookType string `gorm:"type:varchar(64);not null;index;comment:书籍类型字典值,对应 book_type"`
Rating float64 `gorm:"type:numeric(3,1);not null;default:0.0;comment:书籍评分,范围 0-10"`
```
## AutoMigrate 能做什么
- 新表不存在时,按 Model 创建表。
- 新字段不存在时,通常会补充字段。
- 新索引不存在时,通常会尝试创建索引。
- 部分字段类型、默认值、注释可能会按数据库驱动能力尝试调整。
## AutoMigrate 不能当作升级 SQL
- 它不知道旧版本到新版本的业务升级意图。
- 它不会可靠删除废弃字段。
- 它不会可靠删除废弃索引。
- 字段重命名通常会被识别为新增字段,旧字段仍保留。
- 字段类型收缩、非空约束、唯一约束、CHECK 约束、历史数据回填等需要人工判断。
- 它不能保证生产升级顺序、幂等性、兼容性和数据安全。
结论:`AutoMigrate` 是开发期快速补结构工具,不是正式数据库版本迁移方案。
## 升级 SQL 维护规则
- 修改 `.ai-specs/doc-sql/*.sql` 并导致表结构、字段、索引、约束、默认值或注释变化时,必须同步维护升级 SQL。
- 修改 `.ai-specs/doc-dict/*.md` 并导致系统字典主数据或字典项变化时,必须同步维护初始化数据和升级 SQL。
- 升级 SQL 固定放在 `.ai-transition/database-upgrade-doc`
- 当前版本号以 `.ai-specs/sys-specs/database-upgrade-doc-spec.md``当前数据库表结构版本` 为准。
- 当前为 `v1` 时,兼容变更写入 `.ai-transition/database-upgrade-doc/v1.sql`
- 升级 SQL 只写从既有数据库升级到当前目标结构的变更,不重复粘贴完整建表 SQL。
- 同一次业务变更涉及多张表时,写入同一个当前版本 SQL 文件,并按表分组。
- 字典主数据升级按 `sys_dictionaries.type` 防重复。
- 字典明细升级按 `sys_dictionary_details.sys_dictionary_id + value` 防重复。
- 固定值域字典需要写 `sys_dictionaries``sys_dictionary_details`;动态值域字典默认只写 `sys_dictionaries`
- 字典升级 SQL 禁止裸 `INSERT`,必须兼容重复执行、手工已建数据和部分明细缺失。
- 字典升级 SQL 命中已软删数据时,恢复启用数据必须同步置空 `deleted_at`
## 常见变更处理
- 新增字段:写 `ALTER TABLE ... ADD COLUMN ...`,并补 `COMMENT ON COLUMN`
- 新增 `NOT NULL` 字段:先给默认值或先回填历史数据,再追加 `NOT NULL`
- 修改字段类型:先评估历史数据是否兼容,再写 `ALTER TABLE ... ALTER COLUMN ... TYPE ...`
- 删除字段:必须人工确认无业务读写依赖,再写 `ALTER TABLE ... DROP COLUMN IF EXISTS ...`
- 新增普通索引:写 `CREATE INDEX IF NOT EXISTS idx_<table>_<field> ...`
- 删除索引:写 `DROP INDEX IF EXISTS ...`
- 新增唯一索引:先排查历史重复数据,必要时先清洗,再创建唯一索引。
- 修改字段名:优先写 `ALTER TABLE ... RENAME COLUMN ...`,不要依赖 GORM 自动识别。
- 修改注释:写 `COMMENT ON TABLE``COMMENT ON COLUMN`
- 新增固定值域字典:同步修改 `source/system/dictionary.go``source/system/dictionary_detail.go` 和当前版本 SQL。
- 新增动态值域字典:同步修改 `source/system/dictionary.go` 和当前版本 SQL没有固定值项时不改 `dictionary_detail.go`
- 修改固定字典项展示信息:升级 SQL 先 `UPDATE` 已有 `value`,再 `INSERT` 缺失项。
- 废弃固定字典项:优先将 `status` 改为 `false`,不要直接删除历史值项。
## 配置建议
- 开发环境可以保留 `config.yaml``system.disable-auto-migrate: false`,便于快速补表。
- 生产或正式联调环境建议设置 `system.disable-auto-migrate: true`,统一使用 `.ai-transition/database-upgrade-doc/*.sql` 做可审计升级。
- 如果生产环境仍开启 `AutoMigrate`,必须接受它可能自动补字段或索引、但不会完整处理删除和复杂变更的风险。
## 交接检查清单
- 已确认 `.ai-specs/doc-sql/*.sql` 是目标结构说明,不会被程序自动执行。
- 已确认真实自动建表入口是 `initialize/gorm_biz.go``AutoMigrate`
- 已确认 Model 的 `gorm` tag 是 AutoMigrate 生成 DDL 的主要依据。
- 已确认结构升级 SQL 需要人工维护,不能指望 GORM 生成。
- 已确认生产环境是否关闭自动迁移,并形成团队约定。