feat: complete SOP execution workflow

This commit is contained in:
Eric 1549169735@qq.com
2026-08-09 00:29:26 +08:00
parent e32025d2ca
commit c5ab886b70
63 changed files with 1229 additions and 215 deletions

View File

@@ -54,6 +54,8 @@ func ValidateGraph(startNodeKey string, nodes []model.SOPNode, edges []model.SOP
}
if _, exists := nodeMap[startNodeKey]; !exists && startNodeKey != "" {
problems = append(problems, "开始节点不存在")
} else if startNodeKey != "" && nodeMap[startNodeKey].Type != "start" {
problems = append(problems, "开始节点标识必须指向 start 类型节点")
}
adjacency := make(map[string][]string)
@@ -77,6 +79,9 @@ func ValidateGraph(startNodeKey string, nodes []model.SOPNode, edges []model.SOP
if node.Type != "finish" && node.Type != "escalate" && outgoing[node.NodeKey] == 0 {
problems = append(problems, fmt.Sprintf("节点“%s”没有下一步", node.Title))
}
if (node.Type == "finish" || node.Type == "escalate") && outgoing[node.NodeKey] > 0 {
problems = append(problems, fmt.Sprintf("结束节点“%s”不能配置下一步", node.Title))
}
}
visited := map[string]bool{}
@@ -147,6 +152,9 @@ func ValidateForPublish(startNodeKey string, nodes []model.SOPNode, edges []mode
problems = append(problems, fmt.Sprintf("节点“%s”引用的字段 %s 不存在", node.Title, fieldKey))
} else {
collected[fieldKey] = true
if node.Type == "choice" {
validateChoiceOptions(node, config, fieldMap[fieldKey], &problems)
}
}
case "form":
keys, _ := config["field_keys"].([]interface{})
@@ -218,12 +226,57 @@ func ValidateForPublish(startNodeKey string, nodes []model.SOPNode, edges []mode
return problems
}
func validateChoiceOptions(node model.SOPNode, config map[string]interface{}, field model.ScenarioField, problems *[]string) {
values, ok := config["options"].([]interface{})
if !ok || len(values) == 0 {
*problems = append(*problems, fmt.Sprintf("选择节点“%s”没有配置选项", node.Title))
return
}
if field.FieldType != "text" && field.FieldType != "select" {
*problems = append(*problems, fmt.Sprintf("选择节点“%s”只能写入文本或单选字段", node.Title))
}
seen := map[string]bool{}
for _, value := range values {
option, ok := value.(string)
if !ok || option == "" {
*problems = append(*problems, fmt.Sprintf("选择节点“%s”包含无效选项", node.Title))
continue
}
if seen[option] {
*problems = append(*problems, fmt.Sprintf("选择节点“%s”包含重复选项", node.Title))
}
seen[option] = true
if field.FieldType == "select" && !jsonStringArrayContains(field.Options, option) {
*problems = append(*problems, fmt.Sprintf("选择节点“%s”的选项“%s”不在字段选项中", node.Title, option))
}
}
}
func jsonStringArrayContains(raw []byte, expected string) bool {
var values []string
if json.Unmarshal(raw, &values) != nil {
return false
}
for _, value := range values {
if value == expected {
return true
}
}
return false
}
func validateCondition(value interface{}, fields map[string]model.ScenarioField, label string, problems *[]string) {
rule, ok := value.(map[string]interface{})
if !ok {
*problems = append(*problems, label+"的条件结构不正确")
return
}
_, hasAll := rule["all"]
_, hasAny := rule["any"]
if hasAll && hasAny {
*problems = append(*problems, label+"不能同时包含全部满足和任一满足")
return
}
for _, group := range []string{"all", "any"} {
if raw, exists := rule[group]; exists {
items, ok := raw.([]interface{})