package service import ( "fmt" "gofaster/internal/auth/model" "gofaster/internal/auth/repository" "reflect" "regexp" "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 swaggerParser *SwaggerParser } // NewRouteSyncService 创建路由同步服务实例 func NewRouteSyncService( routeMappingRepo *repository.RouteMappingRepository, resourceRepo repository.ResourceRepository, log *zap.Logger, ) *RouteSyncService { swaggerParser := NewSwaggerParser() // 解析Controller目录中的Swagger注释 controllerDir := "./internal/auth/controller" if err := swaggerParser.ParseControllerDirectory(controllerDir); err != nil { log.Warn("解析Swagger注释失败", zap.Error(err)) // 尝试其他可能的路径 alternativePaths := []string{ "internal/auth/controller", "backend/internal/auth/controller", "./backend/internal/auth/controller", } for _, altPath := range alternativePaths { if err := swaggerParser.ParseControllerDirectory(altPath); err == nil { log.Info("成功解析Swagger注释", zap.String("path", altPath)) break } } } else { log.Info("成功解析Swagger注释", zap.String("path", controllerDir)) } return &RouteSyncService{ routeMappingRepo: routeMappingRepo, resourceRepo: resourceRepo, log: log, swaggerParser: swaggerParser, } } // 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 } } // convertPathFormat 转换路径格式:将:var格式改为{var}格式 func (s *RouteSyncService) convertPathFormat(path string) string { // 使用正则表达式替换:var格式为{var}格式 re := regexp.MustCompile(`:([a-zA-Z0-9_]+)`) return re.ReplaceAllString(path, "{$1}") } // generateDescription 生成路由描述 func (s *RouteSyncService) generateDescription(method, path string) string { // 优先从Swagger注释中获取@Summary if s.swaggerParser != nil { summary := s.swaggerParser.GetSummary(method, path) if summary != "" { return summary } } // 回退到原有逻辑 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 } // 转换路径格式:将:var格式改为{var}格式 convertedPath := s.convertPathFormat(route.Path) s.log.Debug("🔄 路径格式转换", zap.String("original", route.Path), zap.String("converted", convertedPath)) // 创建或更新路由映射 mapping := &model.RouteMapping{ BackendRoute: convertedPath, HTTPMethod: route.Method, Module: route.Module, Description: route.Description, Status: 1, } s.log.Debug("📝 准备写入数据库", zap.String("backend_route", mapping.BackendRoute), zap.String("http_method", mapping.HTTPMethod), zap.String("module", mapping.Module), zap.String("description", mapping.Description), zap.Int("status", mapping.Status)) 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 } // 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 } // SyncRouteMapping 同步路由映射 func (s *RouteSyncService) SyncRouteMapping(routeMapping *model.RouteMapping) error { s.log.Info("同步路由映射", zap.String("backendRoute", routeMapping.BackendRoute), zap.String("httpMethod", routeMapping.HTTPMethod), zap.String("module", routeMapping.Module)) // 检查是否已存在相同的路由 existing, err := s.routeMappingRepo.FindByBackendRoute(routeMapping.BackendRoute, routeMapping.HTTPMethod) if err != nil && err != gorm.ErrRecordNotFound { return err } if existing != nil { // 更新现有记录 routeMapping.ID = existing.ID return s.routeMappingRepo.Update(routeMapping) } // 创建新记录 return s.routeMappingRepo.Create(routeMapping) } // BatchSyncRouteMappings 批量同步路由映射 func (s *RouteSyncService) BatchSyncRouteMappings(routeMappings []model.RouteMapping) (int, int, []string) { successCount := 0 errorCount := 0 var errors []string for _, mapping := range routeMappings { if err := s.SyncRouteMapping(&mapping); err != nil { errorCount++ errorMsg := fmt.Sprintf("同步路由失败 %s %s: %s", mapping.HTTPMethod, mapping.BackendRoute, err.Error()) errors = append(errors, errorMsg) s.log.Error("批量同步路由失败", zap.Error(err)) } else { successCount++ } } return successCount, errorCount, errors } // GetRouteMappings 获取路由映射列表 func (s *RouteSyncService) GetRouteMappings(module string) ([]model.RouteMapping, error) { if module != "" { return s.routeMappingRepo.FindByModule(module) } else { return s.routeMappingRepo.FindAll() } }