package migration import ( "fmt" "log" "path/filepath" "sort" "sync" "gorm.io/gorm" "gorm.io/gorm/logger" ) var Migrate = &Migration{ version: make(map[string]func(db *gorm.DB, version string) error), } type Migration struct { db *gorm.DB version map[string]func(db *gorm.DB, version string) error mutex sync.Mutex } func (e *Migration) GetDb() *gorm.DB { return e.db } func (e *Migration) SetDb(db *gorm.DB) { e.db = db } func (e *Migration) SetVersion(k string, f func(db *gorm.DB, version string) error) { e.mutex.Lock() defer e.mutex.Unlock() if _, exists := e.version[k]; exists { panic(fmt.Sprintf("duplicate migration version registered: %s", k)) } e.version[k] = f } func (e *Migration) Migrate() { versions := make([]string, 0) for k := range e.version { versions = append(versions, k) } if !sort.StringsAreSorted(versions) { sort.Strings(versions) } var err error var count int64 // GoAuto 改动(#48):版本存在性检查用 Warn 级会话,不回显 SQL。 // 上游每个版本都会打一条 SELECT count(*),几十个迁移就是几十行——真正 // 值得看的是下面 e.db.Debug() 执行的那些「确实要跑」的迁移。 checkDB := e.db.Session(&gorm.Session{Logger: e.db.Logger.LogMode(logger.Warn)}) applied, skipped := 0, 0 for _, v := range versions { err = checkDB.Table("sys_migration").Where("version = ?", v).Count(&count).Error if err != nil { log.Fatalln(err) } if count > 0 { // GoAuto 改动(#48):上游这里是 log.Println(count),每个已应用的 // 迁移打印一个裸 "1"——没有说明是什么、也没说是哪个版本。而且 log // 写的是 stderr,启动脚本的 `2>&1 |` 会把它变成一整块红色错误, // 看起来像迁移失败了。汇总成一行,走 stdout。 skipped++ count = 0 continue } err = (e.version[v])(e.db.Debug(), v) if err != nil { log.Fatalln(err) } applied++ } fmt.Printf("迁移完成:新执行 %d 个,跳过 %d 个(已应用)\n", applied, skipped) } func GetFilename(s string) string { s = filepath.Base(s) return s[:13] }