package main import ( "flag" "fmt" "os" "git.toowon.com/jimmy/go-common/factory" "git.toowon.com/jimmy/go-common/migration" ) // 数据库迁移 CLI 脚手架。 // // mkdir -p cmd/migrate // cp templates/migrate/main.go cmd/migrate/main.go // go build -o bin/migrate cmd/migrate/main.go // ./bin/migrate up|status|down // // 默认已接好:Factory 初始化、日志、Database Warmup、Migrator、Close 收口。 // 配置优先级:-config/-dir > 环境变量 CONFIG_FILE/MIGRATIONS_DIR > 默认值。 func main() { var ( configFile string migrationsDir string showHelp bool ) flag.StringVar(&configFile, "config", "", "配置文件路径(默认 config.json)") flag.StringVar(&configFile, "c", "", "配置文件路径(简写)") flag.StringVar(&migrationsDir, "dir", "", "迁移目录(默认 migrations)") flag.StringVar(&migrationsDir, "d", "", "迁移目录(简写)") flag.BoolVar(&showHelp, "help", false, "显示帮助") flag.BoolVar(&showHelp, "h", false, "显示帮助(简写)") flag.Parse() if showHelp { printHelp() return } command := "up" if args := flag.Args(); len(args) > 0 { command = args[0] } if command != "up" && command != "down" && command != "status" { fmt.Fprintf(os.Stderr, "未知命令: %s\n\n", command) printHelp() os.Exit(1) } if configFile == "" { configFile = envOr("CONFIG_FILE", "config.json") } if migrationsDir == "" { migrationsDir = envOr("MIGRATIONS_DIR", "migrations") } app := factory.MustInit(configFile, buildOptions()...) defer app.Close() log := app.MustLogger() if err := app.Warmup(factory.ModuleLogger, factory.ModuleDatabase); err != nil { log.Error("warmup failed", map[string]any{"error": err.Error()}) os.Exit(1) } m, err := app.Migrator(migrationsDir) if err != nil { log.Error("create migrator failed", map[string]any{"error": err.Error()}) os.Exit(1) } switch command { case "up": if err := m.Up(); err != nil { log.Error("migrate up failed", map[string]any{"error": err.Error()}) os.Exit(1) } log.Info("migrate up ok", map[string]any{"dir": migrationsDir}) fmt.Println("✓ 迁移执行成功") case "down": if err := m.Down(); err != nil { log.Error("migrate down failed", map[string]any{"error": err.Error()}) os.Exit(1) } log.Info("migrate down ok", map[string]any{"dir": migrationsDir}) fmt.Println("✓ 迁移回滚成功") case "status": status, err := m.Status() if err != nil { log.Error("migrate status failed", map[string]any{"error": err.Error()}) os.Exit(1) } printStatus(status) } } // buildOptions 组装 Factory Option(自定义 DB / Logger 等在此注入) func buildOptions() []factory.Option { // 默认走 config.json。需要替换时例如: // // return []factory.Option{ // factory.WithLogger(customLogger), // factory.WithDatabase(db), // } return nil } func printStatus(status []migration.MigrationStatus) { if len(status) == 0 { fmt.Println("没有找到迁移") return } fmt.Println("\n迁移状态:") fmt.Printf("%-20s %-40s %-10s\n", "版本", "描述", "状态") for _, s := range status { st := "待执行" if s.Applied { st = "已应用" } fmt.Printf("%-20s %-40s %-10s\n", s.Version, s.Description, st) } fmt.Println() } func envOr(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func printHelp() { fmt.Println(`数据库迁移工具 用法: migrate [命令] [选项] 命令: up 执行待执行迁移(默认) down 回滚最后一个迁移 status 查看状态 选项: -config, -c 配置文件(默认: config.json,可用 CONFIG_FILE) -dir, -d 迁移目录(默认: migrations,可用 MIGRATIONS_DIR) -help, -h 帮助 示例: migrate up migrate up -config /etc/app/config.json -dir db/migrations CONFIG_FILE=config.json migrate status`) }