这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开源项目的制作,全程记录分享。
在上一期,我们进行了 “实现管理员注销接口、API 请求函数命名规范”,本期将完成:增加 config 模型、数据表自动迁移优化
增加 config 模型
得先把config模型加进来,因为站点的名称、备案号这些站点基础配置是存储在里边的,未来后台的【系统配置】功能也主要就是管理这个表中的数据。
作者之前的项目已经设计过完善的config数据表,这里直接将建表 SQL 给它复制过来,让 CC 转换为 GORM 模型:
新建config数据模型,基于以下SQL的表结构:
CREATETABLE`config`(`id`intUNSIGNEDNOTNULLAUTO_INCREMENTCOMMENT'ID',`name`varchar(30)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'变量名',`group`varchar(30)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'分组',`title`varchar(50)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'变量标题',`tip`varchar(100)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'变量描述',`type`varchar(30)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'变量输入组件类型',`value`longtextCHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNULLCOMMENT'变量值',`content`longtextCHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNULLCOMMENT'字典数据',`rule`varchar(100)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'验证规则',`extend`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'扩展属性',`input_extend`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'输入框扩展属性',`allow_del`tinyintUNSIGNEDNOTNULLDEFAULT0COMMENT'允许删除:0=否,1=是',`weigh`intNOTNULLDEFAULT0COMMENT'权重',PRIMARYKEY(`id`)USINGBTREE,UNIQUEINDEX`name`(`name`ASC)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=1CHARACTERSET=utf8mb4COLLATE=utf8mb4_unicode_ciCOMMENT='系统配置'ROW_FORMAT=DYNAMIC;AI 生成的模型如下:
funcinit(){Register(&Config{})}// Config 系统配置模型typeConfigstruct{IDuint`gorm:"comment:ID;primarykey;autoIncrement" json:"id"`Namestring`gorm:"comment:变量名;type:varchar(50);uniqueIndex;not null;default:''" json:"name"`Groupstring`gorm:"comment:分组;type:varchar(50);not null;default:''" json:"group"`Titlestring`gorm:"comment:变量标题;type:varchar(50);not null;default:''" json:"title"`Tipstring`gorm:"comment:变量描述;type:varchar(100);not null;default:''" json:"tip"`Typestring`gorm:"comment:变量输入组件类型;type:varchar(50);not null;default:''" json:"type"`Valuestring`gorm:"comment:变量值;type:longtext" json:"value"`Contentstring`gorm:"comment:字典数据;type:longtext" json:"content"`Rulestring`gorm:"comment:验证规则;type:varchar(100);not null;default:''" json:"rule"`Extendstring`gorm:"comment:扩展属性;type:varchar(255);not null;default:''" json:"extend"`InputExtendstring`gorm:"comment:输入框扩展属性;type:varchar(255);not null;default:''" json:"input_extend"`AllowDeluint8`gorm:"comment:允许删除:0=否,1=是;not null;default:0" json:"allow_del"`Weighint`gorm:"comment:权重;not null;default:0" json:"weigh"`}这时候发现了一个问题,config表内是有初始数据的,而且 GORM 的自动迁移终究不是长久之计,只适用于开发环境(不支持删除字段、不支持字段改名、字段类型变更风险、无版本记录等)。
所以,此时我就得将 GORM 的自动迁移改为更加适合生产环境的,然后再向config表插入数据。
数据表自动迁移优化
我们需要一个更加适合生产环境的数据表迁移工具,最起码得有版本控制,看了很多工具:
| 工具 | star数 | 备注 |
|---|---|---|
| https://github.com/golang-migrate/migrate | 18.7k | 纯 SQL 驱动的迁移工具,不支持通过模型生成 SQL,生态第一 |
| https://github.com/ariga/atlas | 8.5k | 新兴的迁移工具,支持通过模型生成 SQL,支持的语言和框架很多,感觉杂而不精 |
| https://github.com/go-gormigrate/gormigrate | 1.2k | star 太少了 |
其他看过的就不一一列出了,感觉golang-migrate/migrate挺好的,一个是官方背书,二是用户多,原生 SQL 驱动也方便 DBA 审核 SQL,至于不支持通过模型生成 SQL,AI 时代不是问题。接下来做规划如下:
- 使用
https://github.com/golang-migrate/migrate实现生产级数据库迁移功能 - 项目需要同时支持
golang-migrate/migrate的CLI模式和库模式,库模式于cmd/migrate/main.go建立自定义命令来执行:迁移、回滚、查看状态、创建新的迁移文件 - 于
cmd/migrate/migrations建立迁移文件,请参考现有模型写好SQL
同时支持 CLI 模式和库模式的意思是:
# 即可以这样执行迁移migrate-database"postgres://user:pass@host:port/dbname?sslmode=disable"-pathcmd/migrate/migrations up# 也可以执行项目自定义命令执行迁移tmp/aigo.exe migrate up自定义命令也就是库模式下,不需要填写-database和-path参数,也不需要单独安装migrate CLI,是我最喜欢的模式。
CLI 模式其实是天然支持的,只要你安装了migrate CLI即可使用,安装后能额外执行:force、drop等命令。
将以上内容发给 CC:
后续 CC 询问以何种方式实现自定义命令,这里选择了spf13/cobra,然后:
库模式下迁移文件以何种方式加载? ❯1. go:embed 内嵌(推荐) up/down/status 用 go:embed 内嵌的迁移文件(编译后二进制自包含,便于部署);create 命令按--dir写入磁盘目录。开发时创建后需重新编译才能 up。2. 文件系统路径 所有操作直接读写 cmd/migrate/migrations 目录,与 golang-migrate CLI 模式完全一致,创建后立即可用。但运行二进制时需能访问到该目录。意思也很简单,第一个是内嵌到编译产物内,生产直接就能用,第二个是文件系统内存储,生产要用迁移,得先上传文件,所以这里当然是选择内嵌。
第一轮完成后,再将原有项目 config 数据表的数据导出来,整理了一下可能会用得上的配置项,让 CC 基于该 SQL 生成一个 seed 迁移,主要包含站点名称、自定义后台入口、备案号、邮件发送等系统基础配置:
INSERTINTO`config`VALUES(1,'config_group','basic','配置分组','','array','[{\"key\":\"basic\",\"value\":\"基础配置\"},{\"key\":\"mail\",\"value\":\"邮件配置\"},{\"key\":\"config_quick_entrance\",\"value\":\"快捷配置入口\"}]',NULL,'required','','',0,-1);INSERTINTO`config`VALUES(2,'name','basic','站点名称','','string','AI GO ADMIN',NULL,'required','','',0,99);INSERTINTO`config`VALUES(3,'entrance','basic','自定义后台入口','','string','/admin',NULL,'required','','',0,1);INSERTINTO`config`VALUES(4,'record_number','basic','域名备案号','','string','渝ICP备8888888号-1',NULL,'','','',0,0);INSERTINTO`config`VALUES(5,'version','basic','系统版本号','','string','v1.0.0',NULL,'required','','',0,0);INSERTINTO`config`VALUES(6,'timezone','basic','时区','','string','Asia/Shanghai',NULL,'required','','',0,0);INSERTINTO`config`VALUES(7,'no_access_ip','basic','禁止访问 IP','禁止访问站点的ip列表,一行一个','textarea',NULL,NULL,'','','',0,0);INSERTINTO`config`VALUES(8,'smtp_server','mail','SMTP 服务器','','string','smtp.com',NULL,'','','',0,9);INSERTINTO`config`VALUES(9,'smtp_port','mail','SMTP 端口','','string','465',NULL,'','','',0,8);INSERTINTO`config`VALUES(10,'smtp_user','mail','SMTP 用户','','string',NULL,NULL,'','','',0,7);INSERTINTO`config`VALUES(11,'smtp_pass','mail','SMTP 密码','','string',NULL,NULL,'','','',0,6);INSERTINTO`config`VALUES(12,'smtp_verification','mail','SMTP 验证方式','','select','SSL','{\"SSL\":\"SSL\",\"TLS\":\"TLS\"}','','','',0,5);INSERTINTO`config`VALUES(13,'smtp_sender_mail','mail','SMTP 发件人邮箱','','string',NULL,NULL,'email','','',0,4);INSERTINTO`config`VALUES(14,'config_quick_entrance','quick_entrance','快捷配置入口','','array','[]',NULL,'','','',0,0);完工后,人工review发现了以下问题:
缺少表前缀配置应用
cmd\migrate\migrations中的 SQL,表名全是admins、tokens这种不带表前缀的,但是我们的系统支持表前缀,只是默认为空。
这里让 AI 在 SQL 中以固定字符串__PREFIX__表示表前缀,执行迁移前将该字符串替换为项目数据库配置中的实际表前缀。
项目命令规划
现在多了一个迁移命令,将原来的cmd/api/main.go改名为cmd/serve/serve.go(作为单独的服务启动命令),然后新增cmd/main.go文件作为统一命令入口,从现在起:
# 启动 API 服务go run cmd/main.go serve# 应用数据库迁移go run cmd/main.go migrate up# 或编译后再运行go build-o./tmp/aigo.exe ./cmd/main.go tmp/aigo.exe serve tmp/aigo.exe migrate up同时serve作为默认命令,是可以省略的。
同步修改 AGENTS.md / README.md / .air.toml
这次对数据迁移做了改动,项目启动流程和迁移功能介绍都需要重新写,直接让 CC 帮忙完成,再人工整理一下。
放弃原生的 Migrate CLI 工具
https://github.com/golang-migrate/migrate是支持CLI和库双模式的,正常情况下我整合了库模式,CLI天然就能用,此时突然发现原生的Migrate CLI不支持传递表前缀啊,这里只能再看看CLI还有那些命令是我们当前不支持的,让 CC 全部实现一遍(version 和 drop 子命令),然后直接放弃CLI模式。