You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
8.3 KiB
323 lines
8.3 KiB
package service |
|
|
|
import ( |
|
"fmt" |
|
"gofaster/internal/auth/model" |
|
"gofaster/internal/auth/repository" |
|
"reflect" |
|
"strings" |
|
|
|
"github.com/gin-gonic/gin" |
|
"go.uber.org/zap" |
|
"gorm.io/gorm" |
|
) |
|
|
|
// RouteInfo 路由信息结构 |
|
type RouteInfo struct { |
|
Path string `json:"path"` |
|
Method string `json:"method"` |
|
Module string `json:"module"` |
|
Description string `json:"description"` |
|
} |
|
|
|
// RouteSyncService 路由同步服务 |
|
type RouteSyncService struct { |
|
routeMappingRepo *repository.RouteMappingRepository |
|
resourceRepo repository.ResourceRepository |
|
log *zap.Logger |
|
} |
|
|
|
// NewRouteSyncService 创建路由同步服务实例 |
|
func NewRouteSyncService( |
|
routeMappingRepo *repository.RouteMappingRepository, |
|
resourceRepo repository.ResourceRepository, |
|
log *zap.Logger, |
|
) *RouteSyncService { |
|
return &RouteSyncService{ |
|
routeMappingRepo: routeMappingRepo, |
|
resourceRepo: resourceRepo, |
|
log: log, |
|
} |
|
} |
|
|
|
// SyncRoutes 同步路由信息到数据库 |
|
func (s *RouteSyncService) SyncRoutes(router *gin.Engine) error { |
|
s.log.Info("开始同步后台路由信息...") |
|
|
|
// 收集所有路由信息 |
|
routes := s.collectRoutes(router) |
|
|
|
// 同步到数据库 |
|
createdCount, updatedCount, err := s.syncToDatabase(routes) |
|
if err != nil { |
|
s.log.Error("路由同步失败", zap.Error(err)) |
|
return err |
|
} |
|
|
|
s.log.Info("路由同步完成", |
|
zap.Int("总路由数", len(routes)), |
|
zap.Int("新增数", createdCount), |
|
zap.Int("更新数", updatedCount), |
|
) |
|
|
|
return nil |
|
} |
|
|
|
// collectRoutes 收集路由信息 |
|
func (s *RouteSyncService) collectRoutes(router *gin.Engine) []RouteInfo { |
|
var routes []RouteInfo |
|
|
|
// 遍历所有注册的路由 |
|
for _, route := range router.Routes() { |
|
if route.Method != "" && route.Path != "" { |
|
module := s.extractModuleFromPath(route.Path) |
|
description := s.generateDescription(route.Method, route.Path) |
|
|
|
routes = append(routes, RouteInfo{ |
|
Path: route.Path, |
|
Method: route.Method, |
|
Module: module, |
|
Description: description, |
|
}) |
|
} |
|
} |
|
|
|
return routes |
|
} |
|
|
|
// collectRoutesFromGroup 从路由组收集路由信息 |
|
func (s *RouteSyncService) collectRoutesFromGroup(group interface{}, routes *[]RouteInfo) { |
|
// 使用反射获取路由组信息 |
|
val := reflect.ValueOf(group) |
|
if val.Kind() == reflect.Ptr { |
|
val = val.Elem() |
|
} |
|
|
|
// 尝试获取路由组的方法 |
|
if val.Kind() == reflect.Struct { |
|
// 这里需要根据实际的gin路由组结构来提取路由信息 |
|
// 由于gin的路由组结构比较复杂,我们主要依赖Routes()方法 |
|
} |
|
} |
|
|
|
// extractModuleFromPath 从路径中提取模块名 |
|
func (s *RouteSyncService) extractModuleFromPath(path string) string { |
|
// 移除开头的斜杠 |
|
path = strings.TrimPrefix(path, "/") |
|
|
|
// 分割路径 |
|
parts := strings.Split(path, "/") |
|
if len(parts) == 0 { |
|
return "unknown" |
|
} |
|
|
|
// 第一个部分通常是模块名 |
|
module := parts[0] |
|
|
|
// 映射常见的模块名 |
|
switch module { |
|
case "api": |
|
if len(parts) > 1 { |
|
return parts[1] // 如 /api/auth -> auth |
|
} |
|
return "api" |
|
case "auth": |
|
return "auth" |
|
case "workflow": |
|
return "workflow" |
|
case "user": |
|
return "user" |
|
case "role": |
|
return "role" |
|
case "permission": |
|
return "permission" |
|
case "resource": |
|
return "resource" |
|
default: |
|
return module |
|
} |
|
} |
|
|
|
// generateDescription 生成路由描述 |
|
func (s *RouteSyncService) generateDescription(method, path string) string { |
|
module := s.extractModuleFromPath(path) |
|
|
|
switch method { |
|
case "GET": |
|
if strings.Contains(path, "/:id") { |
|
return fmt.Sprintf("获取%s详情", module) |
|
} |
|
return fmt.Sprintf("获取%s列表", module) |
|
case "POST": |
|
return fmt.Sprintf("创建%s", module) |
|
case "PUT": |
|
return fmt.Sprintf("更新%s", module) |
|
case "DELETE": |
|
return fmt.Sprintf("删除%s", module) |
|
case "PATCH": |
|
return fmt.Sprintf("部分更新%s", module) |
|
default: |
|
return fmt.Sprintf("%s操作", method) |
|
} |
|
} |
|
|
|
// syncToDatabase 同步路由信息到数据库 |
|
func (s *RouteSyncService) syncToDatabase(routes []RouteInfo) (int, int, error) { |
|
createdCount := 0 |
|
updatedCount := 0 |
|
|
|
for _, route := range routes { |
|
// 检查是否已存在 |
|
existing, err := s.routeMappingRepo.FindByBackendRoute(route.Path, route.Method) |
|
if err != nil && err != gorm.ErrRecordNotFound { |
|
s.log.Error("查询路由映射失败", |
|
zap.String("path", route.Path), |
|
zap.String("method", route.Method), |
|
zap.Error(err), |
|
) |
|
continue |
|
} |
|
|
|
// 创建或更新路由映射 |
|
mapping := &model.RouteMapping{ |
|
BackendRoute: route.Path, |
|
HTTPMethod: route.Method, |
|
AuthGroup: s.getAuthGroupByMethod(route.Method), |
|
Module: route.Module, |
|
Description: route.Description, |
|
Status: 1, |
|
} |
|
|
|
if existing == nil { |
|
// 创建新的路由映射 |
|
if err := s.routeMappingRepo.Create(mapping); err != nil { |
|
s.log.Error("创建路由映射失败", |
|
zap.String("path", route.Path), |
|
zap.String("method", route.Method), |
|
zap.Error(err), |
|
) |
|
continue |
|
} |
|
createdCount++ |
|
s.log.Debug("创建路由映射", |
|
zap.String("path", route.Path), |
|
zap.String("method", route.Method), |
|
) |
|
} else { |
|
// 更新现有路由映射 |
|
mapping.ID = existing.ID |
|
if err := s.routeMappingRepo.Update(mapping); err != nil { |
|
s.log.Error("更新路由映射失败", |
|
zap.String("path", route.Path), |
|
zap.String("method", route.Method), |
|
zap.Error(err), |
|
) |
|
continue |
|
} |
|
updatedCount++ |
|
s.log.Debug("更新路由映射", |
|
zap.String("path", route.Path), |
|
zap.String("method", route.Method), |
|
) |
|
} |
|
} |
|
|
|
return createdCount, updatedCount, nil |
|
} |
|
|
|
// getAuthGroupByMethod 根据HTTP方法获取权限分组 |
|
func (s *RouteSyncService) getAuthGroupByMethod(method string) string { |
|
// 修改型操作 |
|
editMethods := []string{"POST", "PUT", "PATCH", "DELETE"} |
|
for _, editMethod := range editMethods { |
|
if method == editMethod { |
|
return "Edit" |
|
} |
|
} |
|
|
|
// 读取型操作 |
|
return "Read" |
|
} |
|
|
|
// GetSyncStatus 获取同步状态 |
|
func (s *RouteSyncService) GetSyncStatus() (map[string]interface{}, error) { |
|
// 获取数据库中的路由映射总数 |
|
totalMappings, err := s.routeMappingRepo.FindAll() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// 按模块统计 |
|
moduleStats := make(map[string]int) |
|
for _, mapping := range totalMappings { |
|
moduleStats[mapping.Module]++ |
|
} |
|
|
|
return map[string]interface{}{ |
|
"total_mappings": len(totalMappings), |
|
"module_stats": moduleStats, |
|
"last_sync": "应用启动时自动同步", |
|
}, nil |
|
} |
|
|
|
// SyncFrontendRoute 同步前端路由映射 |
|
func (s *RouteSyncService) SyncFrontendRoute(routeMapping *model.RouteMapping) error { |
|
s.log.Info("同步前端路由映射", |
|
zap.String("frontendRoute", routeMapping.FrontendRoute), |
|
zap.String("backendRoute", routeMapping.BackendRoute), |
|
zap.String("module", routeMapping.Module)) |
|
|
|
// 检查是否已存在相同的前端路由 |
|
existing, err := s.routeMappingRepo.FindByFrontendRoute(routeMapping.FrontendRoute) |
|
if err != nil && err != gorm.ErrRecordNotFound { |
|
return err |
|
} |
|
|
|
if len(existing) > 0 { |
|
// 更新现有记录 |
|
for _, existingMapping := range existing { |
|
if existingMapping.HTTPMethod == routeMapping.HTTPMethod { |
|
// 更新匹配的记录 |
|
routeMapping.ID = existingMapping.ID |
|
return s.routeMappingRepo.Update(routeMapping) |
|
} |
|
} |
|
} |
|
|
|
// 创建新记录 |
|
return s.routeMappingRepo.Create(routeMapping) |
|
} |
|
|
|
// BatchSyncFrontendRoutes 批量同步前端路由 |
|
func (s *RouteSyncService) BatchSyncFrontendRoutes(routeMappings []model.RouteMapping) (int, int, []string) { |
|
successCount := 0 |
|
errorCount := 0 |
|
var errors []string |
|
|
|
for _, mapping := range routeMappings { |
|
if err := s.SyncFrontendRoute(&mapping); err != nil { |
|
errorCount++ |
|
errorMsg := fmt.Sprintf("同步路由失败 %s -> %s: %s", |
|
mapping.FrontendRoute, mapping.BackendRoute, err.Error()) |
|
errors = append(errors, errorMsg) |
|
s.log.Error("批量同步路由失败", zap.Error(err)) |
|
} else { |
|
successCount++ |
|
} |
|
} |
|
|
|
return successCount, errorCount, errors |
|
} |
|
|
|
// GetFrontendRoutes 获取前端路由列表 |
|
func (s *RouteSyncService) GetFrontendRoutes(module, authGroup string) ([]model.RouteMapping, error) { |
|
if module != "" && authGroup != "" { |
|
return s.routeMappingRepo.FindByModuleAndAuthGroup(module, authGroup) |
|
} else if module != "" { |
|
return s.routeMappingRepo.FindByModule(module) |
|
} else if authGroup != "" { |
|
return s.routeMappingRepo.FindByAuthGroup(authGroup) |
|
} else { |
|
return s.routeMappingRepo.FindAll() |
|
} |
|
}
|
|
|