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

@@ -0,0 +1,113 @@
package main
import (
"encoding/json"
"testing"
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
"git.iwork-ai.com/xdc/iqudo-top1/internal/sop"
"gorm.io/datatypes"
)
func TestPetDoctorSOPPassesPublishValidation(t *testing.T) {
fields := petFieldModels()
knowledgeIDs := map[string]uint64{
"线上问诊边界": 1,
"急症红旗征象": 2,
"问诊用药安全原则": 3,
"观察与复诊提示": 4,
}
nodes := petNodeModels(petNodes(knowledgeIDs))
edges := petEdgeModels(petEdges())
if len(fields) != 47 || len(nodes) != 21 || len(edges) != 21 {
t.Fatalf("unexpected definition size: fields=%d nodes=%d edges=%d", len(fields), len(nodes), len(edges))
}
problems := sop.ValidateForPublish("start", nodes, edges, sop.ValidationContext{
Fields: fields,
PublishedKnowledgeCardIDs: map[uint64]bool{
1: true,
2: true,
3: true,
4: true,
},
})
if len(problems) > 0 {
t.Fatalf("pet doctor SOP should be publishable, got: %v", problems)
}
}
func TestPetDoctorSOPHasBothRiskEscalations(t *testing.T) {
nodes := petNodes(map[string]uint64{
"线上问诊边界": 1,
"急症红旗征象": 2,
"问诊用药安全原则": 3,
"观察与复诊提示": 4,
})
escalations := map[string]bool{}
for _, node := range nodes {
if node.Type == "escalate" {
escalations[node.Key] = true
}
}
for _, key := range []string{"emergency_escalate", "medication_escalate"} {
if !escalations[key] {
t.Fatalf("missing risk escalation node %s", key)
}
}
}
func TestSameJSONIgnoresObjectOrderAndWhitespace(t *testing.T) {
left := []byte(`{"standard_copy":"安全提示","risk_note":"立即就医"}`)
right := []byte(`{ "risk_note": "立即就医", "standard_copy": "安全提示" }`)
if !sameJSON(left, right) {
t.Fatal("equivalent JSON objects should match")
}
}
func petFieldModels() []model.ScenarioField {
definitions := petFieldDefinitions()
fields := make([]model.ScenarioField, 0, len(definitions))
for _, definition := range definitions {
options, _ := json.Marshal(definition.Options)
validation, _ := json.Marshal(definition.Validation)
fields = append(fields, model.ScenarioField{
FieldKey: definition.Key,
FieldName: definition.Name,
FieldType: definition.Type,
Required: definition.Required,
Options: datatypes.JSON(options),
Validation: datatypes.JSON(validation),
})
}
return fields
}
func petNodeModels(definitions []nodeDefinition) []model.SOPNode {
nodes := make([]model.SOPNode, 0, len(definitions))
for _, definition := range definitions {
config, _ := json.Marshal(definition.Config)
nodes = append(nodes, model.SOPNode{
NodeKey: definition.Key,
Type: definition.Type,
Title: definition.Title,
Content: definition.Content,
Config: datatypes.JSON(config),
})
}
return nodes
}
func petEdgeModels(definitions []edgeDefinition) []model.SOPEdge {
edges := make([]model.SOPEdge, 0, len(definitions))
for _, definition := range definitions {
condition, _ := json.Marshal(definition.Condition)
edges = append(edges, model.SOPEdge{
SourceNodeKey: definition.Source,
TargetNodeKey: definition.Target,
Condition: datatypes.JSON(condition),
Priority: definition.Priority,
})
}
return edges
}