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
}