feat: simplify SOP to immediate-effect configuration

This commit is contained in:
Eric 1549169735@qq.com
2026-08-18 16:27:02 +08:00
parent c5ab886b70
commit 8de48fb05e
150 changed files with 6764 additions and 1626 deletions

View File

@@ -10,10 +10,37 @@ import (
)
type answerNodeConfig struct {
FieldKey string `json:"field_key"`
FieldKeys []string `json:"field_keys"`
Required bool `json:"required"`
Options []string `json:"options"`
FieldKey string `json:"field_key"`
FieldKeys []string `json:"field_keys"`
RequiredFieldKeys []string `json:"required_field_keys"`
Required bool `json:"required"`
Options []string `json:"options"`
}
// validateInitialAnswers accepts a partial, externally supplied set of values
// when an execution starts. Required fields are still enforced by their
// collection nodes, so integrations can supply only the data they possess.
func validateInitialAnswers(fields []model.ScenarioField, answers map[string]interface{}) error {
fieldMap := make(map[string]model.ScenarioField, len(fields))
for _, field := range fields {
fieldMap[field.FieldKey] = field
if field.Required && field.SourcePath != "" && isEmptyValue(answers[field.FieldKey]) {
return fmt.Errorf("缺少必填输入%s", field.FieldName)
}
}
for key, value := range answers {
field, exists := fieldMap[key]
if !exists {
return fmt.Errorf("传入字段 %s 不存在", key)
}
if isEmptyValue(value) {
continue
}
if err := validateFieldValue(field, value); err != nil {
return err
}
}
return nil
}
func validateNodeAnswers(node model.SOPNode, fields []model.ScenarioField, answers map[string]interface{}) error {
@@ -39,9 +66,12 @@ func validateNodeAnswers(node model.SOPNode, fields []model.ScenarioField, answe
return fmt.Errorf("当前表单没有配置采集字段")
}
for _, key := range config.FieldKeys {
expected[key] = false
expected[key] = containsString(config.RequiredFieldKeys, key)
}
default:
if node.Type == "knowledge" {
return validateKnowledgeNodeAnswers(node, fieldMap, answers)
}
if len(answers) > 0 {
return fmt.Errorf("当前节点不接受字段回答")
}
@@ -76,6 +106,87 @@ func validateNodeAnswers(node model.SOPNode, fields []model.ScenarioField, answe
return nil
}
func validateKnowledgeNodeAnswers(node model.SOPNode, fields map[string]model.ScenarioField, answers map[string]interface{}) error {
var config knowledgeNodeConfig
if json.Unmarshal(node.Config, &config) != nil || config.KnowledgeCollection == nil {
if len(answers) > 0 {
return fmt.Errorf("当前节点不接受字段回答")
}
return nil
}
expected := map[string]bool{}
for _, key := range config.KnowledgeCollection.ContextFieldKeys {
expected[key] = fields[key].Required
}
for _, step := range config.KnowledgeCollection.Steps {
expected[step.FieldKey] = step.Required
}
for key := range answers {
if _, ok := expected[key]; !ok {
return fmt.Errorf("字段 %s 不属于当前节点", key)
}
}
for key, required := range expected {
value := answers[key]
if isEmptyValue(value) {
if required {
return fmt.Errorf("请填写%s", knowledgeAnswerName(config, fields, key))
}
continue
}
if field, ok := fields[key]; ok && !isKnowledgeStep(config, key) {
if err := validateFieldValue(field, value); err != nil {
return err
}
}
if isKnowledgeStep(config, key) {
if _, ok := stringValues(value); !ok {
return fmt.Errorf("%s必须选择有效选项", knowledgeAnswerName(config, fields, key))
}
}
}
return nil
}
func knowledgeAnswerName(config knowledgeNodeConfig, fields map[string]model.ScenarioField, key string) string {
if field, ok := fields[key]; ok {
return field.FieldName
}
for _, step := range config.KnowledgeCollection.Steps {
if step.FieldKey == key {
return step.Name
}
}
return key
}
func isKnowledgeStep(config knowledgeNodeConfig, key string) bool {
for _, step := range config.KnowledgeCollection.Steps {
if step.FieldKey == key {
return true
}
}
return false
}
func stringValues(value interface{}) ([]string, bool) {
switch typed := value.(type) {
case string:
return []string{typed}, typed != ""
case []interface{}:
result := make([]string, 0, len(typed))
for _, item := range typed {
text, ok := item.(string)
if !ok || text == "" {
return nil, false
}
result = append(result, text)
}
return result, true
case []string:
return typed, true
}
return nil, false
}
func stringAllowed(options []string, selected string) bool {
for _, option := range options {
if option == selected {
@@ -140,6 +251,15 @@ func validateFieldValue(field model.ScenarioField, value interface{}) error {
return fmt.Errorf("%s包含不正确的选项", field.FieldName)
}
}
case "array":
switch values := value.(type) {
case []interface{}:
_ = values
case []string:
_ = values
default:
return fmt.Errorf("%s必须是数组", field.FieldName)
}
case "date":
text, ok := value.(string)
if !ok || !validDate(text) {