Files
iqudo-top1/internal/run/engine.go
2026-08-06 21:52:03 +08:00

130 lines
3.1 KiB
Go

package run
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
func matchCondition(raw json.RawMessage, answers map[string]interface{}) (bool, error) {
if len(raw) == 0 || string(raw) == "{}" || string(raw) == "null" {
return true, nil
}
var rule map[string]interface{}
if err := json.Unmarshal(raw, &rule); err != nil {
return false, err
}
if all, ok := rule["all"].([]interface{}); ok {
for _, item := range all {
object, ok := item.(map[string]interface{})
if !ok {
return false, fmt.Errorf("invalid all condition")
}
matched, err := matchRule(object, answers)
if err != nil || !matched {
return false, err
}
}
return true, nil
}
if any, ok := rule["any"].([]interface{}); ok {
for _, item := range any {
object, ok := item.(map[string]interface{})
if !ok {
continue
}
matched, err := matchRule(object, answers)
if err != nil {
return false, err
}
if matched {
return true, nil
}
}
return false, nil
}
return matchRule(rule, answers)
}
func matchRule(rule map[string]interface{}, answers map[string]interface{}) (bool, error) {
field, _ := rule["field"].(string)
operator, _ := rule["operator"].(string)
if field == "" || operator == "" {
return false, fmt.Errorf("condition field and operator are required")
}
actual, exists := answers[field]
expected := rule["value"]
switch operator {
case "exists":
return exists && actual != nil && fmt.Sprint(actual) != "", nil
case "not_exists":
return !exists || actual == nil || fmt.Sprint(actual) == "", nil
case "equals":
return reflect.DeepEqual(normalizeValue(actual), normalizeValue(expected)), nil
case "not_equals":
return !reflect.DeepEqual(normalizeValue(actual), normalizeValue(expected)), nil
case "contains":
return strings.Contains(strings.ToLower(fmt.Sprint(actual)), strings.ToLower(fmt.Sprint(expected))), nil
case "greater_than", "less_than":
left, leftOK := toFloat(actual)
right, rightOK := toFloat(expected)
if !leftOK || !rightOK {
return false, nil
}
if operator == "greater_than" {
return left > right, nil
}
return left < right, nil
case "in":
values, ok := expected.([]interface{})
if !ok {
return false, nil
}
for _, value := range values {
if reflect.DeepEqual(normalizeValue(actual), normalizeValue(value)) {
return true, nil
}
}
return false, nil
default:
return false, fmt.Errorf("unsupported operator: %s", operator)
}
}
func normalizeValue(value interface{}) interface{} {
switch typed := value.(type) {
case json.Number:
if number, err := typed.Float64(); err == nil {
return number
}
case int:
return float64(typed)
case int64:
return float64(typed)
case uint64:
return float64(typed)
}
return value
}
func toFloat(value interface{}) (float64, bool) {
switch typed := value.(type) {
case float64:
return typed, true
case float32:
return float64(typed), true
case int:
return float64(typed), true
case int64:
return float64(typed), true
case uint64:
return float64(typed), true
case json.Number:
result, err := typed.Float64()
return result, err == nil
default:
return 0, false
}
}