Files
iqudo-top1/codes/internal/sop/validator.go
2026-08-06 21:48:42 +08:00

91 lines
2.8 KiB
Go

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
}