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.
324 lines
8.3 KiB
324 lines
8.3 KiB
6 days ago
|
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()
|
||
|
}
|
||
|
}
|