expression evaluate

This commit is contained in:
Yoan.liu
2025-05-20 18:09:42 +08:00
parent b546cf3ad0
commit 97d692910b
15 changed files with 511 additions and 5 deletions

262
internal/domain/expr.go Normal file
View File

@@ -0,0 +1,262 @@
package domain
import (
"encoding/json"
"fmt"
)
type Value any
type (
ComparisonOperator string
LogicalOperator string
)
const (
GreaterThan ComparisonOperator = ">"
LessThan ComparisonOperator = "<"
GreaterOrEqual ComparisonOperator = ">="
LessOrEqual ComparisonOperator = "<="
Equal ComparisonOperator = "=="
NotEqual ComparisonOperator = "!="
Is ComparisonOperator = "is"
And LogicalOperator = "and"
Or LogicalOperator = "or"
Not LogicalOperator = "not"
)
type Expr interface {
GetType() string
Eval(variables map[string]map[string]any) (any, error)
}
type ConstExpr struct {
Type string `json:"type"`
Value Value `json:"value"`
}
func (c ConstExpr) GetType() string { return c.Type }
type VarExpr struct {
Type string `json:"type"`
Selector WorkflowNodeIOValueSelector `json:"selector"`
}
func (v VarExpr) GetType() string { return v.Type }
func (v VarExpr) Eval(variables map[string]map[string]any) (any, error) {
if v.Selector.Id == "" {
return nil, fmt.Errorf("node id is empty")
}
if v.Selector.Name == "" {
return nil, fmt.Errorf("name is empty")
}
if _, ok := variables[v.Selector.Id]; !ok {
return nil, fmt.Errorf("node %s not found", v.Selector.Id)
}
if _, ok := variables[v.Selector.Id][v.Selector.Name]; !ok {
return nil, fmt.Errorf("variable %s not found in node %s", v.Selector.Name, v.Selector.NodeId)
}
return variables[v.Selector.Id][v.Selector.Name], nil
}
type CompareExpr struct {
Type string `json:"type"` // compare
Op ComparisonOperator `json:"op"`
Left Expr `json:"left"`
Right Expr `json:"right"`
}
func (c CompareExpr) GetType() string { return c.Type }
func (c CompareExpr) Eval(variables map[string]map[string]any) (any, error) {
left, err := c.Left.Eval(variables)
if err != nil {
return nil, err
}
right, err := c.Right.Eval(variables)
if err != nil {
return nil, err
}
switch c.Op {
case GreaterThan:
return left.(float64) > right.(float64), nil
case LessThan:
return left.(float64) < right.(float64), nil
case GreaterOrEqual:
return left.(float64) >= right.(float64), nil
case LessOrEqual:
return left.(float64) <= right.(float64), nil
case Equal:
return left == right, nil
case NotEqual:
return left != right, nil
case Is:
return left == right, nil
default:
return nil, fmt.Errorf("unknown operator: %s", c.Op)
}
}
type LogicalExpr struct {
Type string `json:"type"` // logical
Op LogicalOperator `json:"op"`
Left Expr `json:"left"`
Right Expr `json:"right"`
}
func (l LogicalExpr) GetType() string { return l.Type }
func (l LogicalExpr) Eval(variables map[string]map[string]any) (any, error) {
left, err := l.Left.Eval(variables)
if err != nil {
return nil, err
}
right, err := l.Right.Eval(variables)
if err != nil {
return nil, err
}
switch l.Op {
case And:
return left.(bool) && right.(bool), nil
case Or:
return left.(bool) || right.(bool), nil
default:
return nil, fmt.Errorf("unknown operator: %s", l.Op)
}
}
type NotExpr struct {
Type string `json:"type"` // not
Expr Expr `json:"expr"`
}
func (n NotExpr) GetType() string { return n.Type }
func (n NotExpr) Eval(variables map[string]map[string]any) (any, error) {
inner, err := n.Expr.Eval(variables)
if err != nil {
return nil, err
}
return !inner.(bool), nil
}
type rawExpr struct {
Type string `json:"type"`
}
func MarshalExpr(e Expr) ([]byte, error) {
return json.Marshal(e)
}
func UnmarshalExpr(data []byte) (Expr, error) {
var typ rawExpr
if err := json.Unmarshal(data, &typ); err != nil {
return nil, err
}
switch typ.Type {
case "const":
var e ConstExpr
if err := json.Unmarshal(data, &e); err != nil {
return nil, err
}
return e, nil
case "var":
var e VarExpr
if err := json.Unmarshal(data, &e); err != nil {
return nil, err
}
return e, nil
case "compare":
var e CompareExprRaw
if err := json.Unmarshal(data, &e); err != nil {
return nil, err
}
return e.ToCompareExpr()
case "logical":
var e LogicalExprRaw
if err := json.Unmarshal(data, &e); err != nil {
return nil, err
}
return e.ToLogicalExpr()
case "not":
var e NotExprRaw
if err := json.Unmarshal(data, &e); err != nil {
return nil, err
}
return e.ToNotExpr()
default:
return nil, fmt.Errorf("unknown expr type: %s", typ.Type)
}
}
type CompareExprRaw struct {
Type string `json:"type"`
Op ComparisonOperator `json:"op"`
Left json.RawMessage `json:"left"`
Right json.RawMessage `json:"right"`
}
func (r CompareExprRaw) ToCompareExpr() (CompareExpr, error) {
leftExpr, err := UnmarshalExpr(r.Left)
if err != nil {
return CompareExpr{}, err
}
rightExpr, err := UnmarshalExpr(r.Right)
if err != nil {
return CompareExpr{}, err
}
return CompareExpr{
Type: r.Type,
Op: r.Op,
Left: leftExpr,
Right: rightExpr,
}, nil
}
type LogicalExprRaw struct {
Type string `json:"type"`
Op LogicalOperator `json:"op"`
Left json.RawMessage `json:"left"`
Right json.RawMessage `json:"right"`
}
func (r LogicalExprRaw) ToLogicalExpr() (LogicalExpr, error) {
left, err := UnmarshalExpr(r.Left)
if err != nil {
return LogicalExpr{}, err
}
right, err := UnmarshalExpr(r.Right)
if err != nil {
return LogicalExpr{}, err
}
return LogicalExpr{
Type: r.Type,
Op: r.Op,
Left: left,
Right: right,
}, nil
}
type NotExprRaw struct {
Type string `json:"type"`
Expr json.RawMessage `json:"expr"`
}
func (r NotExprRaw) ToNotExpr() (NotExpr, error) {
inner, err := UnmarshalExpr(r.Expr)
if err != nil {
return NotExpr{}, err
}
return NotExpr{
Type: r.Type,
Expr: inner,
}, nil
}

View File

@@ -81,6 +81,10 @@ type WorkflowNodeConfigForApply struct {
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays,omitempty"` // 证书到期前多少天前跳过续期(零值将使用默认值 30
}
type WorkflowNodeConfigForCondition struct {
Expression Expr `json:"expression"` // 条件表达式
}
type WorkflowNodeConfigForUpload struct {
Certificate string `json:"certificate"`
PrivateKey string `json:"privateKey"`
@@ -104,6 +108,22 @@ type WorkflowNodeConfigForNotify struct {
Message string `json:"message"` // 通知内容
}
func (n *WorkflowNode) GetConfigForCondition() WorkflowNodeConfigForCondition {
raw := maputil.GetString(n.Config, "expression")
if raw == "" {
return WorkflowNodeConfigForCondition{}
}
expr, err := UnmarshalExpr([]byte(raw))
if err != nil {
return WorkflowNodeConfigForCondition{}
}
return WorkflowNodeConfigForCondition{
Expression: expr,
}
}
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
if skipBeforeExpiryDays == 0 {
@@ -171,6 +191,7 @@ type WorkflowNodeIO struct {
type WorkflowNodeIOValueSelector struct {
Id string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}
const WorkflowNodeIONameCertificate string = "certificate"