package sop import ( "encoding/json" "fmt" "git.iwork-ai.com/xdc/iqudo-top1/internal/model" ) var allowedNodeTypes = map[string]bool{"start": true, "message": true, "question": true, "form": true, "choice": true, "condition": true, "knowledge": true, "escalate": true, "finish": true} func ValidateGraph(startNodeKey string, nodes []model.SOPNode, edges []model.SOPEdge) []string { var problems []string if startNodeKey == "" { problems = append(problems, "未设置开始节点") } nodeMap := make(map[string]model.SOPNode, len(nodes)) startCount := 0 finishCount := 0 for _, node := range nodes { if node.NodeKey == "" { problems = append(problems, "存在没有标识的节点") continue } if _, exists := nodeMap[node.NodeKey]; exists { problems = append(problems, fmt.Sprintf("节点标识重复:%s", node.NodeKey)) } nodeMap[node.NodeKey] = node if !allowedNodeTypes[node.Type] { problems = append(problems, fmt.Sprintf("节点 %s 类型不支持", node.Title)) } if node.Type == "start" { startCount++ } if node.Type == "finish" || node.Type == "escalate" { finishCount++ } } if startCount != 1 { problems = append(problems, "流程必须且只能包含一个开始节点") } if finishCount == 0 { problems = append(problems, "流程至少需要一个结束或转人工节点") } if _, exists := nodeMap[startNodeKey]; !exists && startNodeKey != "" { problems = append(problems, "开始节点不存在") } adjacency := make(map[string][]string) outgoing := make(map[string]int) for _, edge := range edges { if _, exists := nodeMap[edge.SourceNodeKey]; !exists { problems = append(problems, fmt.Sprintf("连线起点不存在:%s", edge.SourceNodeKey)) } if _, exists := nodeMap[edge.TargetNodeKey]; !exists { problems = append(problems, fmt.Sprintf("连线终点不存在:%s", edge.TargetNodeKey)) } if len(edge.Condition) > 0 && !json.Valid(edge.Condition) { problems = append(problems, fmt.Sprintf("连线 %s -> %s 的条件不是有效 JSON", edge.SourceNodeKey, edge.TargetNodeKey)) } adjacency[edge.SourceNodeKey] = append(adjacency[edge.SourceNodeKey], edge.TargetNodeKey) outgoing[edge.SourceNodeKey]++ } for _, node := range nodes { if node.Type != "finish" && node.Type != "escalate" && outgoing[node.NodeKey] == 0 { problems = append(problems, fmt.Sprintf("节点“%s”没有下一步", node.Title)) } } visited := map[string]bool{} var walk func(string) walk = func(key string) { if visited[key] { return } visited[key] = true for _, next := range adjacency[key] { walk(next) } } if startNodeKey != "" { walk(startNodeKey) } for key, node := range nodeMap { if !visited[key] { problems = append(problems, fmt.Sprintf("节点“%s”无法从开始节点到达", node.Title)) } } return problems }