package migration import ( "gofaster/internal/auth/model" "regexp" "go.uber.org/zap" "gorm.io/gorm" ) // CreateRouteTables 创建路由相关表 func CreateRouteTables(db *gorm.DB, log *zap.Logger) error { log.Info("开始创建路由相关表...") // 创建菜单表 if err := db.AutoMigrate(&model.Menu{}); err != nil { log.Error("创建菜单表失败", zap.Error(err)) return err } log.Info("✅ 菜单表创建完成") // 创建路由映射表 log.Info("🔧 开始创建route_mappings表...") log.Info("📋 RouteMapping模型字段:", zap.String("ID", "uint"), zap.String("UpdatedAt", "time.Time"), zap.String("BackendRoute", "string"), zap.String("HTTPMethod", "string"), zap.String("ResourceID", "*uint"), zap.String("Module", "string"), zap.String("Description", "string"), zap.String("Status", "int")) if err := db.AutoMigrate(&model.RouteMapping{}); err != nil { log.Error("❌ 创建路由映射表失败", zap.Error(err)) return err } log.Info("✅ 路由映射表创建完成") // 创建菜单路由关联表 if err := db.AutoMigrate(&model.MenuRoute{}); err != nil { log.Error("创建菜单路由关联表失败", zap.Error(err)) return err } log.Info("✅ 菜单路由关联表创建完成") // 创建前台路由表 if err := db.AutoMigrate(&model.FrontendRoute{}); err != nil { log.Error("创建前台路由表失败", zap.Error(err)) return err } log.Info("✅ 前台路由表创建完成") // 创建前后台路由关系表 if err := db.AutoMigrate(&model.FrontendBackendRoute{}); err != nil { log.Error("创建前后台路由关系表失败", zap.Error(err)) return err } log.Info("✅ 前后台路由关系表创建完成") // 为现有Resource表添加菜单相关字段 if err := addMenuFieldsToResource(db, log); err != nil { log.Error("为Resource表添加菜单字段失败", zap.Error(err)) return err } log.Info("✅ Resource表菜单字段添加完成") // 处理RouteMapping表的字段变更 if err := updateRouteMappingFields(db, log); err != nil { log.Error("更新RouteMapping表字段失败", zap.Error(err)) return err } log.Info("✅ RouteMapping表字段更新完成") log.Info("路由相关表创建完成") return nil } // updateRouteMappingFields 更新RouteMapping表的字段 func updateRouteMappingFields(db *gorm.DB, log *zap.Logger) error { log.Info("🔧 开始更新route_mappings表字段...") // 检查表是否存在 var tableExists bool err := db.Raw("SELECT COUNT(*) > 0 FROM information_schema.tables WHERE table_name = 'route_mappings'").Scan(&tableExists).Error if err != nil { log.Error("❌ 检查表是否存在失败", zap.Error(err)) return err } log.Info("📊 表存在状态检查", zap.Bool("tableExists", tableExists)) if !tableExists { log.Info("⚠️ route_mappings表不存在,跳过字段更新") return nil } // 获取当前表结构 log.Info("📋 获取当前表结构...") var columns []struct { ColumnName string `gorm:"column:COLUMN_NAME"` DataType string `gorm:"column:DATA_TYPE"` } if err := db.Raw("SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.columns WHERE table_name = 'route_mappings' ORDER BY ORDINAL_POSITION").Scan(&columns).Error; err != nil { log.Error("❌ 获取表结构失败", zap.Error(err)) } else { log.Info("📋 当前表结构:") for _, col := range columns { log.Info(" - " + col.ColumnName + " (" + col.DataType + ")") } } // 删除不需要的字段 fieldsToRemove := []string{"created_at", "frontend_route", "auth_group", "menu_id"} log.Info("🗑️ 准备删除字段", zap.Strings("fields", fieldsToRemove)) for _, field := range fieldsToRemove { var hasField bool err := db.Raw("SELECT COUNT(*) > 0 FROM information_schema.columns WHERE table_name = 'route_mappings' AND column_name = ?", field).Scan(&hasField).Error if err != nil { log.Warn("⚠️ 检查字段失败", zap.String("field", field), zap.Error(err)) continue } log.Info("🔍 字段检查结果", zap.String("field", field), zap.Bool("exists", hasField)) if hasField { // 删除字段 log.Info("🗑️ 执行DDL: ALTER TABLE route_mappings DROP COLUMN " + field) if err := db.Exec("ALTER TABLE route_mappings DROP COLUMN " + field).Error; err != nil { log.Warn("❌ 删除字段失败", zap.String("field", field), zap.Error(err)) } else { log.Info("✅ 删除字段成功", zap.String("field", field)) } } else { log.Info("ℹ️ 字段不存在,跳过删除", zap.String("field", field)) } } // 更新现有记录的路径格式(将:var改为{var}) log.Info("🔄 开始更新路径格式...") if err := updatePathFormat(db, log); err != nil { log.Error("❌ 更新路径格式失败", zap.Error(err)) return err } log.Info("✅ route_mappings表字段更新完成") return nil } // updatePathFormat 更新路径格式:将:var改为{var} func updatePathFormat(db *gorm.DB, log *zap.Logger) error { log.Info("🔍 查找需要更新路径格式的记录...") // 获取所有需要更新的记录 var mappings []struct { ID uint `gorm:"column:id"` BackendRoute string `gorm:"column:backend_route"` } query := "SELECT id, backend_route FROM route_mappings WHERE backend_route LIKE '%:%'" log.Info("📝 执行查询", zap.String("query", query)) if err := db.Raw(query).Scan(&mappings).Error; err != nil { log.Error("❌ 查询需要更新的记录失败", zap.Error(err)) return err } log.Info("📊 找到需要更新路径格式的记录", zap.Int("count", len(mappings))) if len(mappings) == 0 { log.Info("ℹ️ 没有需要更新路径格式的记录") return nil } // 更新每条记录 updatedCount := 0 for _, mapping := range mappings { // 转换路径格式 newPath := convertPathFormat(mapping.BackendRoute) log.Info("🔄 路径转换", zap.Uint("id", mapping.ID), zap.String("old", mapping.BackendRoute), zap.String("new", newPath)) if newPath != mapping.BackendRoute { updateQuery := "UPDATE route_mappings SET backend_route = ? WHERE id = ?" log.Info("📝 执行更新", zap.String("query", updateQuery), zap.String("newPath", newPath), zap.Uint("id", mapping.ID)) if err := db.Exec(updateQuery, newPath, mapping.ID).Error; err != nil { log.Error("❌ 更新路径失败", zap.Uint("id", mapping.ID), zap.Error(err)) } else { log.Info("✅ 更新路径成功", zap.Uint("id", mapping.ID), zap.String("old", mapping.BackendRoute), zap.String("new", newPath)) updatedCount++ } } else { log.Info("ℹ️ 路径无需更新", zap.Uint("id", mapping.ID)) } } log.Info("📊 路径格式更新完成", zap.Int("total", len(mappings)), zap.Int("updated", updatedCount)) return nil } // convertPathFormat 转换路径格式:将:var格式改为{var}格式 func convertPathFormat(path string) string { // 使用正则表达式替换:var格式为{var}格式 re := regexp.MustCompile(`:([a-zA-Z0-9_]+)`) return re.ReplaceAllString(path, "{$1}") } // addMenuFieldsToResource 为Resource表添加菜单相关字段 func addMenuFieldsToResource(db *gorm.DB, log *zap.Logger) error { // 检查IsMenu字段是否存在 var hasIsMenu bool err := db.Raw("SELECT COUNT(*) > 0 FROM information_schema.columns WHERE table_name = 'resources' AND column_name = 'is_menu'").Scan(&hasIsMenu).Error if err != nil { return err } if !hasIsMenu { // 添加IsMenu字段 if err := db.Exec("ALTER TABLE resources ADD COLUMN is_menu BOOLEAN DEFAULT FALSE").Error; err != nil { return err } log.Info("添加is_menu字段到resources表") } // 移除旧的MenuID字段(如果存在) var hasMenuID bool err = db.Raw("SELECT COUNT(*) > 0 FROM information_schema.columns WHERE table_name = 'resources' AND column_name = 'menu_id'").Scan(&hasMenuID).Error if err != nil { return err } if hasMenuID { // 删除MenuID字段 if err := db.Exec("ALTER TABLE resources DROP COLUMN menu_id").Error; err != nil { log.Warn("删除menu_id字段失败,可能已被删除", zap.Error(err)) } else { log.Info("删除menu_id字段从resources表") } } return nil }