+ IF & Case Process

This commit is contained in:
Max 2021-11-22 22:26:46 +08:00
parent 2c597509c7
commit c0df8b4b12
7 changed files with 420 additions and 0 deletions

49
helper/case.go Normal file
View file

@ -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...)
}

62
helper/case_test.go Normal file
View file

@ -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])
}

170
helper/condition.go Normal file
View file

@ -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
}

49
helper/condition_test.go Normal file
View file

@ -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":"李四"`)
}

26
helper/if.go Normal file
View file

@ -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])
}

62
helper/if_test.go Normal file
View file

@ -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])
}

View file

@ -21,6 +21,8 @@ func init() {
gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate) gou.RegisterProcessHandler("xiang.helper.JwtValidate", ProcessJwtValidate)
gou.RegisterProcessHandler("xiang.helper.For", ProcessFor) gou.RegisterProcessHandler("xiang.helper.For", ProcessFor)
gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach) gou.RegisterProcessHandler("xiang.helper.Each", ProcessEach)
gou.RegisterProcessHandler("xiang.helper.Case", ProcessCase)
gou.RegisterProcessHandler("xiang.helper.IF", ProcessIF)
gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint) gou.RegisterProcessHandler("xiang.helper.Print", ProcessPrint)
} }