package utils import ( "fmt" ) // PluginConfig 插件配置结构 type PluginConfig struct { Tag string Type string Args any } // PluginNode 表示插件节点及其依赖关系 type PluginNode struct { Index int // 在原始数组中的索引 Config PluginConfig // 插件配置 Deps []string // 依赖的插件标签 } // TopologicalSort 对插件配置进行拓扑排序 // 返回排序后的插件配置列表,如果存在循环依赖则返回错误 func TopologicalSort(plugins []PluginConfig) ([]PluginConfig, error) { // 构建依赖图:graph[A] = [B, C] 表示 A 依赖 B 和 C graph := buildDependencyGraph(plugins) // 反转图:reversedGraph[B] = [A] 表示 B 被 A 依赖 // 这样计算入度时更直观 reversedGraph := make(map[string][]string) allNodes := make(map[string]bool) for node := range graph { allNodes[node] = true for _, dep := range graph[node] { allNodes[dep] = true reversedGraph[dep] = append(reversedGraph[dep], node) } } // 计算入度(依赖的数量) inDegree := make(map[string]int) for node := range allNodes { inDegree[node] = len(graph[node]) } // 找出所有入度为0的节点(不依赖任何其他节点) queue := []string{} for node, degree := range inDegree { if degree == 0 { queue = append(queue, node) } } // BFS遍历 result := []PluginConfig{} for len(queue) > 0 { node := queue[0] queue = queue[1:] result = append(result, findPluginConfigByTag(plugins, node)) // 对于所有依赖当前节点的节点,减少它们的入度 for _, dependent := range reversedGraph[node] { inDegree[dependent]-- if inDegree[dependent] == 0 { queue = append(queue, dependent) } } } // 检测循环依赖 if len(result) != len(plugins) { return nil, fmt.Errorf("检测到循环依赖,无法进行拓扑排序") } return result, nil } // buildDependencyGraph 构建插件依赖图 func buildDependencyGraph(plugins []PluginConfig) map[string][]string { graph := make(map[string][]string) for _, p := range plugins { graph[p.Tag] = extractDependencies(p) } return graph } // extractDependencies 从插件配置中提取依赖关系 // 解析配置中的 $plugin_name 引用 func extractDependencies(config PluginConfig) []string { var deps []string // 将配置转换为字符串进行正则匹配 configStr := fmt.Sprintf("%+v", config.Args) // 调试:打印配置字符串 // fmt.Printf("DEBUG: Plugin %s, configStr: %s\n", config.Tag, configStr) // 1. 查找 $xxx 格式的引用 i := 0 for i < len(configStr) { if i+1 < len(configStr) && configStr[i] == '$' { // 找到变量名的开始 start := i + 1 // 查找变量名的结束(遇到非字母数字下划线字符) end := start for end < len(configStr) && (isAlphaNumeric(configStr[end]) || configStr[end] == '_') { end++ } if start < end { dep := configStr[start:end] // 排除一些常见的关键字,避免误识别 if dep != "primary" && dep != "secondary" && dep != "timeout" && dep != "china_ip" { deps = append(deps, dep) } } i = end } else { i++ } } // 2. 特殊处理:检查 entry 字段(用于 server 插件) // 服务器插件使用 "entry: plugin_name" 而不是 "$plugin_name" // 搜索配置字符串中的 "entry:" 模式 entryPrefix := "entry:" entryIdx := 0 for { idx := stringIndexFrom(configStr, entryPrefix, entryIdx) if idx == -1 { break } // 找到 entry: 后面的值 start := idx + len(entryPrefix) // 跳过空格 for start < len(configStr) && configStr[start] == ' ' { start++ } // 提取 entry 值(直到遇到空格或特殊字符) end := start for end < len(configStr) && isAlphaNumeric(configStr[end]) { end++ } if start < end { entryValue := configStr[start:end] deps = append(deps, entryValue) // fmt.Printf("DEBUG: Found entry dependency for %s: %s\n", config.Tag, entryValue) } entryIdx = end } // 调试:打印最终依赖列表 // if len(deps) > 0 { // fmt.Printf("DEBUG: Plugin %s depends on: %v\n", config.Tag, deps) // } return deps } // stringIndexFrom 从指定位置开始查找子串 func stringIndexFrom(s, substr string, from int) int { if from >= len(s) { return -1 } for i := from; i < len(s)-len(substr)+1; i++ { if s[i:i+len(substr)] == substr { return i } } return -1 } // isAlphaNumeric 判断字符是否为字母、数字或下划线 func isAlphaNumeric(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' } // findPluginConfigByTag 根据标签查找插件配置 func findPluginConfigByTag(plugins []PluginConfig, tag string) PluginConfig { for _, p := range plugins { if p.Tag == tag { return p } } // 理论上不会出现找不到的情况,因为我们是从现有标签构建的图 return PluginConfig{} } // ValidateConfigOrder 验证配置顺序是否正确 // 如果配置顺序有问题,返回建议的正确顺序 func ValidateConfigOrder(plugins []PluginConfig) ([]PluginConfig, error) { sorted, err := TopologicalSort(plugins) if err != nil { return nil, err } return sorted, nil }