// dev 环境自动建表(仅 go run 生效) // // 安全约定:表结构迁移只允许发生在开发模式(go run 启动)。 // go build 打包出的二进制启动时绝不会执行任何迁移——生产库的结构 // 变更一律通过 sql/ 目录下的脚本人工执行,防止 GORM 自动改表出问题。 // // dev 下也只做最保守的一件事:补建"库里不存在的表"。 // 已存在的表一律不动(不加列、不加索引),因为手写 SQL 的索引命名 // (uk_username 等)与 GORM 默认命名不同,全量 AutoMigrate 会重复建索引。 package database import ( "log" "os" "strings" "nl-game-api-gin/internal/model" ) // IsDevRun 判断当前进程是否由 go run 启动: // go run 会先把临时二进制编译到 go-build 缓存目录(如 %TEMP%\go-buildXXXX)再运行, // 而 go build 的产物路径不含该特征,因此打包后的服务永远不会命中 dev 分支。 func IsDevRun() bool { exe, err := os.Executable() if err != nil { return false } return strings.Contains(exe, "go-build") } // AutoMigrateMissing 遍历全部 GORM 模型,为缺失的表建表(含索引),已存在的表跳过。 // 新增 model 后在下方清单登记一行,dev 启动即可自动建表;种子数据仍需执行对应 SQL 脚本。 func AutoMigrateMissing() error { models := []interface{ TableName() string }{ // 用户与登录 &model.User{}, &model.LoginLog{}, // 游戏与对局记录 &model.Game{}, &model.GameRecord{}, &model.BattleRecord{}, // 积分与签到 &model.PointRecord{}, &model.SignIn{}, // 商城:购物车 / 订单 / 已购游戏 / 道具 &model.Cart{}, &model.Order{}, &model.OrderItem{}, &model.UserGame{}, &model.Prop{}, &model.UserProp{}, // 社交:好友 / 私聊 &model.Friend{}, &model.ChatMessage{}, // 主题与站点配置 &model.Theme{}, &model.SiteConfig{}, // VIP 等级 &model.VipLevel{}, // 关卡进度 / 云存档 / 皮肤 &model.GameProgress{}, &model.GameSave{}, &model.GameSkin{}, &model.UserSkin{}, } created := 0 for _, m := range models { if DB.Migrator().HasTable(m) { continue } if err := DB.Migrator().CreateTable(m); err != nil { return err } created++ log.Printf("[dev迁移] 已创建缺失表 %s(种子数据请执行 sql/ 对应脚本)", m.TableName()) } if created == 0 { log.Println("[dev迁移] 全部表已存在,无需创建") } return nil }