|
|
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 |
|
|
}
|
|
|
|