这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开源项目的制作,全程记录分享。

上一期我们刚刚搞定 config 模型和自动迁移的优化,这期趁热打铁,把管理员规则表这块也搬过来。管理员规则表是后台系统的核心表之一——菜单结构、权限节点全都存在里面,这一步绕不开。
增加 admin_rule 模型及数据表迁移
管理员规则表,说白了就是后台导航菜单和权限节点的数据载体。既然上一期已经对自动迁移机制做了优化,这一期直接复用就行。具体操作分两步:
第一步,把管理员规则表的 SQL 给到 AI,让它帮忙生成对应的 Go 模型和迁移脚本。模型放在 internal/model/admin.go,迁移文件则是 cmd/migrate/migrations/000001_admin.up.sql 和 000001_admin.down.sql。
原始 SQL 长这样——从建表语句就能看出字段比较全面:id、pid、type(dir/menu/node 三种类型)、title、name、path、icon、open_type、url、component、keepalive、extend、remark、weigh、status,再加上 create_time 和 update_time。
CREATE TABLE `ba_admin_rule` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`pid` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '上级规则',
`type` enum('dir','menu','node') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'menu' COMMENT '规则类型:dir=规则目录,menu=菜单项,node=权限节点',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '规则标题',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '规则名称',
`path` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单路由路径',
`icon` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '图标',
`open_type` enum('tab','link','iframe') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '菜单打开方式:tab=选项卡,link=链接,iframe=Iframe',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单 Url',
`component` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单组件路径',
`keepalive` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '缓存:0=关闭,1=开启',
`extend` enum('none','add_route_only','add_menu_only') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none' COMMENT '扩展属性:none=无,add_route_only=只添加为路由,add_menu_only=只添加为菜单',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
`weigh` int NOT NULL DEFAULT 0 COMMENT '权重',
`status` tinyint UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态:0=禁用,1=启用',
`update_time` bigint UNSIGNED NULL DEFAULT NULL COMMENT '更新时间',
`create_time` bigint UNSIGNED NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `pid`(`pid` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单和权限规则表' ROW_FORMAT = DYNAMIC;
第二步,从 src/layouts/admin/index.vue 的临时 MOCK 数据里随便抽几行权限数据出来,打印后手动插入。
AI 生成的迁移 SQL 长这样(PostgreSQL 版):
CREATE TABLE IF NOT EXISTS "__PREFIX__admin_rule" (
"id" bigserial PRIMARY KEY,
"pid" bigint DEFAULT NULL,
"type" varchar(50) NOT NULL DEFAULT '',
"title" varchar(50) NOT NULL DEFAULT '',
"name" varchar(50) NOT NULL DEFAULT '',
"path" varchar(255) NOT NULL DEFAULT '',
"icon" varchar(50) NOT NULL DEFAULT '',
"open_type" varchar(50) NOT NULL DEFAULT '',
"url" varchar(255) NOT NULL DEFAULT '',
"component" varchar(255) NOT NULL DEFAULT '',
"keepalive" smallint NOT NULL DEFAULT 0,
"extend" varchar(50) NOT NULL DEFAULT '',
"remark" varchar(255) NOT NULL DEFAULT '',
"weigh" bigint NOT NULL DEFAULT 0,
"status" smallint NOT NULL DEFAULT 1,
"update_time" timestamptz,
"create_time" timestamptz
);CREATE INDEX IF NOT EXISTS "__PREFIX__idx_admin_rule_pid" ON "__PREFIX__admin_rule" ("pid");COMMENT ON TABLE "__PREFIX__admin_rule" IS '菜单和权限规则表';
COMMENT ON COLUMN "__PREFIX__admin_rule"."id" IS 'ID';
COMMENT ON COLUMN "__PREFIX__admin_rule"."pid" IS '上级规则';
COMMENT ON COLUMN "__PREFIX__admin_rule"."type" IS '规则类型:dir=规则目录,menu=菜单项,node=权限节点';
COMMENT ON COLUMN "__PREFIX__admin_rule"."title" IS '规则标题';
COMMENT ON COLUMN "__PREFIX__admin_rule"."name" IS '规则名称';
COMMENT ON COLUMN "__PREFIX__admin_rule"."path" IS '菜单路由路径';
COMMENT ON COLUMN "__PREFIX__admin_rule"."icon" IS '菜单图标';
COMMENT ON COLUMN "__PREFIX__admin_rule"."open_type" IS '菜单打开方式:tab=选项卡,link=链接,iframe=Iframe';
COMMENT ON COLUMN "__PREFIX__admin_rule"."url" IS '菜单URL';
COMMENT ON COLUMN "__PREFIX__admin_rule"."component" IS '菜单组件路径';
COMMENT ON COLUMN "__PREFIX__admin_rule"."keepalive" IS '缓存:0=关闭,1=开启';
COMMENT ON COLUMN "__PREFIX__admin_rule"."extend" IS '扩展属性:add_route_only=只添加为路由,add_menu_only=只添加为菜单';
COMMENT ON COLUMN "__PREFIX__admin_rule"."remark" IS '备注';
COMMENT ON COLUMN "__PREFIX__admin_rule"."weigh" IS '权重';
COMMENT ON COLUMN "__PREFIX__admin_rule"."status" IS '状态:0=禁用,1=启用';
COMMENT ON COLUMN "__PREFIX__admin_rule"."update_time" IS '更新时间';
COMMENT ON COLUMN "__PREFIX__admin_rule"."create_time" IS '创建时间';
拿到迁移 SQL 之后,按惯例手动微调了一下,然后让 CC(Claude Code)基于我修改后的 @internal/middleware/admin_auth.go 重新整理管理员权限模型。
结果翻车了——@ 的文件搞错了,它以为我要整的是中间件逻辑,直接给我整出了 admin_group 和 admin_group_access 表的迁移数据,还把 admin 控制器里查询 admin_group 获取登录用户权限的逻辑也给生成了。
本来只是想根据迁移 SQL 更新一下 AdminRule 模型,结果绕了一大圈,还浪费了不少 tokens。这种 AI 理解偏差在开发中确实时有发生,关键要看住,别让它跑太远。
最终生成的 AdminRule 模型倒是没毛病:
// AdminRule 菜单和权限规则模型
type AdminRule struct {
ID uint `gorm:"comment:ID;primarykey;autoIncrement" json:"id"`
Pid *uint `gorm:"comment:上级规则" json:"pid"`
Type string `gorm:"comment:规则类型:dir=规则目录,menu=菜单项,node=权限节点;type:varchar(50);not null;default:''" json:"type"`
Title string `gorm:"comment:规则标题;type:varchar(50);not null;default:''" json:"title"`
Name string `gorm:"comment:规则名称;type:varchar(50);not null;default:''" json:"name"`
Path string `gorm:"comment:菜单路由路径;type:varchar(255);not null;default:''" json:"path"`
Icon string `gorm:"comment:菜单图标;type:varchar(50);not null;default:''" json:"icon"`
OpenType string `gorm:"comment:菜单打开方式:tab=选项卡,link=链接,iframe=Iframe;type:varchar(50);not null;default:''" json:"open_type"`
URL string `gorm:"comment:菜单URL;type:varchar(255);not null;default:''" json:"url"`
Component string `gorm:"comment:菜单组件路径;type:varchar(255);not null;default:''" json:"component"`
Keepalive uint8 `gorm:"comment:缓存:0=关闭,1=开启;not null;default:0" json:"keepalive"`
Extend string `gorm:"comment:扩展属性:add_route_only=只添加为路由,add_menu_only=只添加为菜单;type:varchar(50);not null;default:''" json:"extend"`
Remark string `gorm:"comment:备注;type:varchar(255);not null;default:''" json:"remark"`
Weigh int `gorm:"comment:权重;not null;default:0" json:"weigh"`
Status uint8 `gorm:"comment:状态:0=禁用,1=启用;not null;default:1" json:"status"`
UpdateTime time.Time `gorm:"comment:更新时间" json:"update_time"`
CreateTime time.Time `gorm:"comment:创建时间" json:"create_time"`
}func (AdminRule) TableName() string {
return "admin_rules"
}
模型字段完整,GORM 标签和 JSON 序列化也都没问题。TableName 返回的 admin_rules 跟迁移脚本保持一致。这样一来,后台的菜单和权限体系就有了数据结构支撑,下一步就可以把前端的菜单树和后端权限校验串联起来了。
