调整工具类的方法,优化方法调用及增加迁移工具及其用法
This commit is contained in:
216
migration/helper.go
Normal file
216
migration/helper.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/config"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RunMigrationsFromConfig 从配置文件运行迁移(便捷方法)
|
||||
//
|
||||
// 注意:推荐使用独立的迁移工具(templates/migrate/main.go),而不是在应用代码中直接调用。
|
||||
// 独立工具可以实现零耦合、独立部署。
|
||||
//
|
||||
// 此方法主要用于:
|
||||
// 1. 独立迁移工具内部调用(推荐)
|
||||
// 2. 简单场景下在应用启动时调用(不推荐,会导致耦合)
|
||||
//
|
||||
// 用法:
|
||||
//
|
||||
// import "git.toowon.com/jimmy/go-common/migration"
|
||||
// migration.RunMigrationsFromConfig("config.json", "migrations")
|
||||
func RunMigrationsFromConfig(configFile, migrationsDir string) error {
|
||||
// 加载配置
|
||||
cfg, err := loadConfigFromFileOrEnv(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 连接数据库
|
||||
db, err := connectDB(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接数据库失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建迁移器
|
||||
migrator := NewMigrator(db)
|
||||
|
||||
// 加载迁移文件
|
||||
migrations, err := LoadMigrationsFromFiles(migrationsDir, "*.sql")
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载迁移文件失败: %w", err)
|
||||
}
|
||||
|
||||
if len(migrations) == 0 {
|
||||
fmt.Printf("在目录 '%s' 中没有找到迁移文件\n", migrationsDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
migrator.AddMigrations(migrations...)
|
||||
|
||||
// 执行迁移
|
||||
if err := migrator.Up(); err != nil {
|
||||
return fmt.Errorf("执行迁移失败: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✓ 迁移执行成功")
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunMigrationsFromConfigWithCommand 从配置文件运行迁移(支持命令)
|
||||
// command: "up", "down", "status"
|
||||
func RunMigrationsFromConfigWithCommand(configFile, migrationsDir, command string) error {
|
||||
// 加载配置
|
||||
cfg, err := loadConfigFromFileOrEnv(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 连接数据库
|
||||
db, err := connectDB(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接数据库失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建迁移器
|
||||
migrator := NewMigrator(db)
|
||||
|
||||
// 加载迁移文件
|
||||
migrations, err := LoadMigrationsFromFiles(migrationsDir, "*.sql")
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载迁移文件失败: %w", err)
|
||||
}
|
||||
|
||||
if len(migrations) == 0 {
|
||||
fmt.Printf("在目录 '%s' 中没有找到迁移文件\n", migrationsDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
migrator.AddMigrations(migrations...)
|
||||
|
||||
// 执行命令
|
||||
switch command {
|
||||
case "up":
|
||||
if err := migrator.Up(); err != nil {
|
||||
return fmt.Errorf("执行迁移失败: %w", err)
|
||||
}
|
||||
fmt.Println("✓ 迁移执行成功")
|
||||
|
||||
case "down":
|
||||
if err := migrator.Down(); err != nil {
|
||||
return fmt.Errorf("回滚迁移失败: %w", err)
|
||||
}
|
||||
fmt.Println("✓ 迁移回滚成功")
|
||||
|
||||
case "status":
|
||||
status, err := migrator.Status()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取迁移状态失败: %w", err)
|
||||
}
|
||||
printMigrationStatus(status)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("未知命令: %s (支持: up, down, status)", command)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadConfigFromFileOrEnv 从文件或环境变量加载配置
|
||||
func loadConfigFromFileOrEnv(configFile string) (*config.Config, error) {
|
||||
// 优先从环境变量加载
|
||||
if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" {
|
||||
return &config.Config{
|
||||
Database: &config.DatabaseConfig{
|
||||
DSN: dbURL,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 尝试从配置文件加载
|
||||
if configFile != "" {
|
||||
if _, err := os.Stat(configFile); err == nil {
|
||||
return config.LoadFromFile(configFile)
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试默认路径
|
||||
defaultPaths := []string{"config.json", "../config.json"}
|
||||
for _, path := range defaultPaths {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return config.LoadFromFile(path)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("未找到配置文件,也未设置环境变量 DATABASE_URL")
|
||||
}
|
||||
|
||||
// connectDB 连接数据库
|
||||
func connectDB(cfg *config.Config) (*gorm.DB, error) {
|
||||
if cfg.Database == nil {
|
||||
return nil, fmt.Errorf("数据库配置为空")
|
||||
}
|
||||
|
||||
dsn, err := cfg.GetDatabaseDSN()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var db *gorm.DB
|
||||
switch cfg.Database.Type {
|
||||
case "mysql":
|
||||
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
case "postgres":
|
||||
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
case "sqlite":
|
||||
db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的数据库类型: %s", cfg.Database.Type)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 配置连接池
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sqlDB.SetMaxOpenConns(10)
|
||||
sqlDB.SetMaxIdleConns(5)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// printMigrationStatus 打印迁移状态
|
||||
func printMigrationStatus(status []MigrationStatus) {
|
||||
if len(status) == 0 {
|
||||
fmt.Println("没有找到迁移")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\n迁移状态:")
|
||||
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
fmt.Printf("%-20s %-40s %-10s\n", "版本", "描述", "状态")
|
||||
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
|
||||
for _, s := range status {
|
||||
statusText := "待执行"
|
||||
if s.Applied {
|
||||
statusText = "✓ 已应用"
|
||||
}
|
||||
fmt.Printf("%-20s %-40s %-10s\n", s.Version, s.Description, statusText)
|
||||
}
|
||||
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user