175 lines
4.4 KiB
Go
175 lines
4.4 KiB
Go
package run
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
|
|
)
|
|
|
|
type answerNodeConfig struct {
|
|
FieldKey string `json:"field_key"`
|
|
FieldKeys []string `json:"field_keys"`
|
|
Required bool `json:"required"`
|
|
}
|
|
|
|
func validateNodeAnswers(node model.SOPNode, fields []model.ScenarioField, answers map[string]interface{}) error {
|
|
var config answerNodeConfig
|
|
if len(node.Config) > 0 {
|
|
if err := json.Unmarshal(node.Config, &config); err != nil {
|
|
return fmt.Errorf("当前节点配置不正确")
|
|
}
|
|
}
|
|
fieldMap := make(map[string]model.ScenarioField, len(fields))
|
|
for _, field := range fields {
|
|
fieldMap[field.FieldKey] = field
|
|
}
|
|
expected := map[string]bool{}
|
|
switch node.Type {
|
|
case "question", "choice":
|
|
if config.FieldKey == "" {
|
|
return fmt.Errorf("当前节点没有配置采集字段")
|
|
}
|
|
expected[config.FieldKey] = config.Required
|
|
case "form":
|
|
if len(config.FieldKeys) == 0 {
|
|
return fmt.Errorf("当前表单没有配置采集字段")
|
|
}
|
|
for _, key := range config.FieldKeys {
|
|
expected[key] = false
|
|
}
|
|
default:
|
|
if len(answers) > 0 {
|
|
return fmt.Errorf("当前节点不接受字段回答")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
for key := range answers {
|
|
if _, ok := expected[key]; !ok {
|
|
return fmt.Errorf("字段 %s 不属于当前节点", key)
|
|
}
|
|
}
|
|
for key, nodeRequired := range expected {
|
|
field, exists := fieldMap[key]
|
|
if !exists {
|
|
return fmt.Errorf("字段 %s 不存在", key)
|
|
}
|
|
value, provided := answers[key]
|
|
required := field.Required || nodeRequired
|
|
if !provided || isEmptyValue(value) {
|
|
if required {
|
|
return fmt.Errorf("请填写%s", field.FieldName)
|
|
}
|
|
continue
|
|
}
|
|
if err := validateFieldValue(field, value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateFieldValue(field model.ScenarioField, value interface{}) error {
|
|
switch field.FieldType {
|
|
case "text", "textarea":
|
|
text, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("%s必须是文本", field.FieldName)
|
|
}
|
|
var rules struct {
|
|
MinLength int `json:"min_length"`
|
|
MaxLength int `json:"max_length"`
|
|
}
|
|
_ = json.Unmarshal(field.Validation, &rules)
|
|
length := len([]rune(text))
|
|
if rules.MinLength > 0 && length < rules.MinLength {
|
|
return fmt.Errorf("%s不能少于%d个字符", field.FieldName, rules.MinLength)
|
|
}
|
|
if rules.MaxLength > 0 && length > rules.MaxLength {
|
|
return fmt.Errorf("%s不能超过%d个字符", field.FieldName, rules.MaxLength)
|
|
}
|
|
case "number":
|
|
number, ok := toFloat(value)
|
|
if !ok {
|
|
return fmt.Errorf("%s必须是数字", field.FieldName)
|
|
}
|
|
var rules struct {
|
|
Min *float64 `json:"min"`
|
|
Max *float64 `json:"max"`
|
|
}
|
|
_ = json.Unmarshal(field.Validation, &rules)
|
|
if rules.Min != nil && number < *rules.Min {
|
|
return fmt.Errorf("%s不能小于%v", field.FieldName, *rules.Min)
|
|
}
|
|
if rules.Max != nil && number > *rules.Max {
|
|
return fmt.Errorf("%s不能大于%v", field.FieldName, *rules.Max)
|
|
}
|
|
case "boolean":
|
|
if _, ok := value.(bool); !ok {
|
|
return fmt.Errorf("%s必须选择是或否", field.FieldName)
|
|
}
|
|
case "select":
|
|
selected, ok := value.(string)
|
|
if !ok || !optionAllowed(field.Options, selected) {
|
|
return fmt.Errorf("%s的选项不正确", field.FieldName)
|
|
}
|
|
case "multiselect":
|
|
values, ok := value.([]interface{})
|
|
if !ok {
|
|
return fmt.Errorf("%s必须是多选值", field.FieldName)
|
|
}
|
|
for _, item := range values {
|
|
selected, ok := item.(string)
|
|
if !ok || !optionAllowed(field.Options, selected) {
|
|
return fmt.Errorf("%s包含不正确的选项", field.FieldName)
|
|
}
|
|
}
|
|
case "date":
|
|
text, ok := value.(string)
|
|
if !ok || !validDate(text) {
|
|
return fmt.Errorf("%s的日期格式不正确", field.FieldName)
|
|
}
|
|
default:
|
|
return fmt.Errorf("%s的字段类型不支持", field.FieldName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func optionAllowed(raw []byte, selected string) bool {
|
|
var options []string
|
|
if err := json.Unmarshal(raw, &options); err != nil {
|
|
return false
|
|
}
|
|
for _, option := range options {
|
|
if option == selected {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func validDate(value string) bool {
|
|
for _, layout := range []string{time.RFC3339, "2006-01-02"} {
|
|
if _, err := time.Parse(layout, value); err == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isEmptyValue(value interface{}) bool {
|
|
if value == nil {
|
|
return true
|
|
}
|
|
if text, ok := value.(string); ok {
|
|
return strings.TrimSpace(text) == ""
|
|
}
|
|
if values, ok := value.([]interface{}); ok {
|
|
return len(values) == 0
|
|
}
|
|
return false
|
|
}
|