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.
 
 
 
 
 
 

353 lines
9.3 KiB

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