diff --git a/helper/case.go b/helper/case.go new file mode 100644 index 00000000..d14a57e6 --- /dev/null +++ b/helper/case.go @@ -0,0 +1,49 @@ +package helper + +import ( + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/exception" +) + +// CaseParam 条件参数 +type CaseParam struct { + When []Condition `json:"when"` + Name string `json:"name"` + Process string `json:"process"` + Args []interface{} `json:"args"` +} + +// Case 条件判断 +func Case(params ...CaseParam) interface{} { + for _, param := range params { + if When(param.When) { + return gou.NewProcess(param.Process, param.Args...).Run() + } + } + return nil +} + +// CaseParamOf 读取参数 +func CaseParamOf(v interface{}) CaseParam { + data, err := jsoniter.Marshal(v) + if err != nil { + exception.New("参数错误: %s", 400, err).Throw() + } + res := CaseParam{} + err = jsoniter.Unmarshal(data, &res) + if err != nil { + exception.New("参数错误: %s", 400, err).Throw() + } + return res +} + +// ProcessCase xiang.helper.Case Case条件判断 +func ProcessCase(process *gou.Process) interface{} { + process.ValidateArgNums(1) + params := []CaseParam{} + for _, v := range process.Args { + params = append(params, CaseParamOf(v)) + } + return Case(params...) +} diff --git a/helper/case_test.go b/helper/case_test.go new file mode 100644 index 00000000..bf17c2aa --- /dev/null +++ b/helper/case_test.go @@ -0,0 +1,62 @@ +package helper + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou" +) + +func TestCase(t *testing.T) { + + gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + return process.Args + }) + + case1 := CaseParam{ + When: []Condition{ + {Left: "张三", OP: "=", Right: "李四", Compute: Computes["="]}, + {OR: true, Left: "李四", OP: "=", Right: "李四", Compute: Computes["="]}, + }, + Name: "打印信息", + Process: "xiang.unit.Return", + Args: []interface{}{"world"}, + } + + case2 := CaseParam{ + When: []Condition{ + {Left: "张三", OP: "=", Right: "张三", Compute: Computes["="]}, + }, + Name: "打印信息", + Process: "xiang.unit.Return", + Args: []interface{}{"foo"}, + } + + v := Case(case1, case2).([]interface{}) + assert.Equal(t, "world", v[0]) +} + +func TestProcessCase(t *testing.T) { + + gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + return process.Args + }) + + args := []interface{}{ + map[string]interface{}{ + "when": []map[string]interface{}{{"用户": "张三", "=": "李四"}, {"or": true, "用户": "李四", "=": "李四"}}, + "name": "打印信息", + "process": "xiang.unit.Return", + "args": []interface{}{"world"}, + }, + map[string]interface{}{ + "when": []map[string]interface{}{{"用户": "张三", "=": "张三"}}, + "name": "打印信息", + "process": "xiang.unit.Return", + "args": []interface{}{"foo"}, + }, + } + process := gou.NewProcess("xiang.helper.Case", args...) + res := process.Run().([]interface{}) + assert.Equal(t, "world", res[0]) +} diff --git a/helper/condition.go b/helper/condition.go new file mode 100644 index 00000000..b1a16749 --- /dev/null +++ b/helper/condition.go @@ -0,0 +1,170 @@ +package helper + +import ( + "fmt" + "regexp" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/kun/any" +) + +// ComputeFunc 计算函数 +type ComputeFunc func(interface{}, interface{}) bool + +// Computes 可用计算式 +var Computes = map[string]ComputeFunc{ + "=": func(left interface{}, right interface{}) bool { + return left == right + }, + ">": func(left interface{}, right interface{}) bool { + return any.Of(left).CFloat64() == any.Of(right).CFloat64() + }, + ">=": func(left interface{}, right interface{}) bool { + return any.Of(left).CFloat64() >= any.Of(right).CFloat64() + }, + "<": func(left interface{}, right interface{}) bool { + return any.Of(left).CFloat64() < any.Of(right).CFloat64() + }, + "<=": func(left interface{}, right interface{}) bool { + return any.Of(left).CFloat64() <= any.Of(right).CFloat64() + }, + "!=": func(left interface{}, right interface{}) bool { + return left != right + }, + "hasprefix": func(left interface{}, right interface{}) bool { + return strings.HasPrefix(fmt.Sprintf("%v", left), fmt.Sprintf("%v", right)) + }, + "hassuffix": func(left interface{}, right interface{}) bool { + return strings.HasSuffix(fmt.Sprintf("%v", left), fmt.Sprintf("%v", right)) + }, + "contains": func(left interface{}, right interface{}) bool { + return strings.Contains(fmt.Sprintf("%v", left), fmt.Sprintf("%v", right)) + }, + "match": func(left interface{}, right interface{}) bool { + re := regexp.MustCompile(fmt.Sprintf("%v", right)) + return re.Match([]byte(fmt.Sprintf("%v", left))) + }, + "is": func(left interface{}, right interface{}) bool { + if is, ok := right.(string); ok { + is = strings.ToLower(is) + if is == "null" { + return left == nil + } else if is == "notnull" { + return left != nil + } + } + return false + }, +} + +// Condition 判断条件 +type Condition struct { + Left interface{} `json:"left"` + Right interface{} `json:"right"` + Compute ComputeFunc `json:"-"` + OP string `json:"op"` + OR bool `json:"or"` + Comment string `json:"comment"` +} + +// When 多项条件判断 +func When(conds []Condition) bool { + res := true + for _, cond := range conds { + if cond.OR { + res = res || cond.Exec() + continue + } + res = res && cond.Exec() + } + return res +} + +// Exec 执行条件判断 +func (cond Condition) Exec() bool { + return cond.Compute(cond.Left, cond.Right) +} + +// UnmarshalJSON for json marshalJSON +func (cond *Condition) UnmarshalJSON(data []byte) error { + origin := map[string]interface{}{} + err := jsoniter.Unmarshal(data, &origin) + if err != nil { + return err + } + *cond = ConditionOf(origin) + return nil +} + +// MarshalJSON for json marshalJSON +func (cond Condition) MarshalJSON() ([]byte, error) { + return jsoniter.Marshal(cond.ToMap()) +} + +// ConditionOf 从 map[string]interface{} +func ConditionOf(input map[string]interface{}) Condition { + cond := Condition{} + for k, val := range input { + key := strings.ToLower(k) + // { "=": "foo" } + if compute, has := Computes[key]; has { + cond.Right = val + cond.Compute = compute + cond.OP = k + continue + } + + switch key { + case "left": + cond.Left = val + continue + case "right": + cond.Right = val + continue + case "op": + if val, ok := val.(string); ok { + if compute, has := Computes[val]; has { + cond.Compute = compute + cond.OP = val + } + + } + continue + case "or": + if val, ok := val.(bool); ok { + cond.OR = val + } + continue + case "comment": + if val, ok := val.(string); ok { + cond.Comment = val + } + continue + } + + // { "用户不存在": "bar"}, + cond.Comment = key + cond.Left = val + } + + return cond +} + +// ToMap Condition 转换为 map[string]interface{} +func (cond Condition) ToMap() map[string]interface{} { + res := map[string]interface{}{} + if cond.OP != "" { + res[cond.OP] = cond.Right + } + if cond.Comment != "" { + res[cond.Comment] = cond.Left + } else { + res["left"] = cond.Left + res["right"] = cond.Right + } + if cond.OR { + res["or"] = true + } + return res +} diff --git a/helper/condition_test.go b/helper/condition_test.go new file mode 100644 index 00000000..5d9d459c --- /dev/null +++ b/helper/condition_test.go @@ -0,0 +1,49 @@ +package helper + +import ( + "testing" + + jsoniter "github.com/json-iterator/go" + "github.com/stretchr/testify/assert" +) + +func TestCondition(t *testing.T) { + data := []byte(`{ "用户不存在": "张三", "=": "李四" }`) + cond := Condition{} + err := jsoniter.Unmarshal(data, &cond) + assert.Nil(t, err) + assert.Equal(t, "张三", cond.Left) + assert.Equal(t, "李四", cond.Right) + assert.Equal(t, "=", cond.OP) + assert.Equal(t, "用户不存在", cond.Comment) + assert.Equal(t, false, cond.OR) + assert.False(t, cond.Exec()) + + data = []byte(`{ "用户不存在":"张三", "is":"null" }`) + cond = Condition{} + err = jsoniter.Unmarshal(data, &cond) + assert.Nil(t, err) + assert.Equal(t, "张三", cond.Left) + assert.Equal(t, "null", cond.Right) + assert.Equal(t, "is", cond.OP) + assert.Equal(t, "用户不存在", cond.Comment) + assert.Equal(t, false, cond.OR) + assert.False(t, cond.Exec()) + + data = []byte(`{ "left":"李四", "right":"李四", "op":"=", "or":true }`) + cond = Condition{} + err = jsoniter.Unmarshal(data, &cond) + assert.Nil(t, err) + assert.Equal(t, "李四", cond.Left) + assert.Equal(t, "李四", cond.Right) + assert.Equal(t, "=", cond.OP) + assert.Equal(t, true, cond.OR) + assert.True(t, cond.Exec()) + + data, err = jsoniter.Marshal(cond) + assert.Nil(t, err) + str := string(data) + assert.Contains(t, str, `"=":"李四"`) + assert.Contains(t, str, `"or":true`) + assert.Contains(t, str, `"left":"李四"`) +} diff --git a/helper/if.go b/helper/if.go new file mode 100644 index 00000000..c41a7558 --- /dev/null +++ b/helper/if.go @@ -0,0 +1,26 @@ +package helper + +import "github.com/yaoapp/gou" + +// IF 条件判断 +func IF(param CaseParam, paramElse ...CaseParam) interface{} { + if When(param.When) { + return gou.NewProcess(param.Process, param.Args...).Run() + } else if len(paramElse) > 0 && When(paramElse[0].When) { + return gou.NewProcess(paramElse[0].Process, paramElse[0].Args...).Run() + } + return nil +} + +// ProcessIF xiang.helper.IF IF条件判断 +func ProcessIF(process *gou.Process) interface{} { + process.ValidateArgNums(1) + params := []CaseParam{} + for _, v := range process.Args { + params = append(params, CaseParamOf(v)) + } + if len(params) > 1 { + IF(params[0], params[1]) + } + return IF(params[0]) +} diff --git a/helper/if_test.go b/helper/if_test.go new file mode 100644 index 00000000..2e4be489 --- /dev/null +++ b/helper/if_test.go @@ -0,0 +1,62 @@ +package helper + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou" +) + +func TestIF(t *testing.T) { + + gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + return process.Args + }) + + case1 := CaseParam{ + When: []Condition{ + {Left: "张三", OP: "=", Right: "李四", Compute: Computes["="]}, + {OR: true, Left: "李四", OP: "=", Right: "李四", Compute: Computes["="]}, + }, + Name: "打印信息", + Process: "xiang.unit.Return", + Args: []interface{}{"world"}, + } + + case2 := CaseParam{ + When: []Condition{ + {Left: "张三", OP: "=", Right: "张三", Compute: Computes["="]}, + }, + Name: "打印信息", + Process: "xiang.unit.Return", + Args: []interface{}{"foo"}, + } + + v := IF(case1, case2).([]interface{}) + assert.Equal(t, "world", v[0]) +} + +func TestProcessIF(t *testing.T) { + + gou.RegisterProcessHandler("xiang.unit.return", func(process *gou.Process) interface{} { + return process.Args + }) + + args := []interface{}{ + map[string]interface{}{ + "when": []map[string]interface{}{{"用户": "张三", "=": "李四"}, {"or": true, "用户": "李四", "=": "李四"}}, + "name": "打印信息", + "process": "xiang.unit.Return", + "args": []interface{}{"world"}, + }, + map[string]interface{}{ + "when": []map[string]interface{}{{"用户": "张三", "=": "张三"}}, + "name": "打印信息", + "process": "xiang.unit.Return", + "args": []interface{}{"foo"}, + }, + } + process := gou.NewProcess("xiang.helper.IF", args...) + res := process.Run().([]interface{}) + assert.Equal(t, "world", res[0]) +} diff --git a/helper/process.go b/helper/process.go index c877a123..06e29ff7 100644 --- a/helper/process.go +++ b/helper/process.go @@ -21,6 +21,8 @@ func init() { gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate) gou.RegisterProcessHandler("xiang.helper.For", ProcessFor) gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach) + gou.RegisterProcessHandler("xiang.helper.Case", ProcessCase) + gou.RegisterProcessHandler("xiang.helper.IF", ProcessIF) gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint) }