feat: condition evaluator for rule filtering

This commit is contained in:
2026-06-27 13:15:02 +08:00
parent d7ae840d9a
commit 54af29e4fc
2 changed files with 154 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
package condition
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"aiaa-notification-service/internal/model"
)
// Evaluate checks all conditions against data. Returns true if ALL conditions pass.
// An empty conditions slice always returns true.
func Evaluate(conditions []model.Condition, data map[string]interface{}) bool {
if len(conditions) == 0 {
return true
}
for _, c := range conditions {
if !evaluateOne(c, data) {
return false
}
}
return true
}
func evaluateOne(c model.Condition, data map[string]interface{}) bool {
fieldVal, fieldExists := data[c.Field]
switch c.Op {
case "exists":
return fieldExists
case "not_exists":
return !fieldExists
case "eq":
if !fieldExists {
return false
}
return fmt.Sprintf("%v", fieldVal) == c.Value
case "ne":
if !fieldExists {
return false
}
return fmt.Sprintf("%v", fieldVal) != c.Value
case "contains":
if !fieldExists {
return false
}
return strings.Contains(fmt.Sprintf("%v", fieldVal), c.Value)
case "gt", "gte", "lt", "lte":
if !fieldExists {
return false
}
return compareNumeric(fieldVal, c.Value, c.Op)
default:
return false
}
}
func compareNumeric(fieldVal interface{}, value string, op string) bool {
fv, err := toFloat64(fieldVal)
if err != nil {
return false
}
cv, err := strconv.ParseFloat(value, 64)
if err != nil {
return false
}
switch op {
case "gt":
return fv > cv
case "gte":
return fv >= cv
case "lt":
return fv < cv
case "lte":
return fv <= cv
}
return false
}
func toFloat64(v interface{}) (float64, error) {
switch val := v.(type) {
case float64:
return val, nil
case float32:
return float64(val), nil
case int:
return float64(val), nil
case int64:
return float64(val), nil
case string:
return strconv.ParseFloat(val, 64)
case json.Number:
return val.Float64()
default:
return 0, fmt.Errorf("cannot convert %T to float64", v)
}
}