gisk是独立的即插即用的轻量级决策引擎,支持json和yaml格式DSL。支持自定义运算符,自定义函数,自定义动作开放灵活的扩展适用更多的业务场景。
- 基础值
- 变量
- 输入值
- 函数
- 决策(规则)
- 决策集
- 决策流
- 分流决策流
- 赋值决策流
- 评分卡
- 支持数据类型:number, string, bool, array, map
- 支持运算符号(支持自定义,覆盖默认运算符):eq, neq, gt, lt, gte, lte, in, notIn, like, notLike
- 支持函数(支持自定义,函数支持嵌套):内置函数rand、sum
- 支持决策动作(支持自定义):内置动作 赋值,访问url
- 支持串行并行执行
- DSL支持历史版本控制(提供获取接口,实现不同介质DSL储存。内置文件存储)
- DSL支持json和yaml格式
-
环境准备
go version go1.2+
-
安装
go get -u -v gitee.com/sreeb/gisk
- 基础使用
package main
import (
"fmt"
"gitee.com/sreeb/gisk"
)
func main() {
elementType := gisk.RULES //规则
rulesKey := "rules1" //规则唯一key
version := "1" //规则版本
g := gisk.New() //创建gisk实例
g.SetDslFormat(gisk.JSON) //设置dsl格式
err := g.Parse(elementType, rulesKey, version) //解析规则
if err != nil {
//错误处理
}
//获取所有被初始化的变量值
variates := g.GetVariates()
fmt.Println(variates)
}
- 注册dsl获取接口
dsl接口获取器,可以根据提供的类型和key和版本获取dsl字符串。 dsl接口获取器需要实现gisk.DslGetterInterface
接口,返回dsl字符串。
type DslGetterInterface interface {
GetDsl(elementType ElementType, key string, version string) (string, error)
}
示例:
type fileDslGetter struct {
}
func (getter *fileDslGetter) GetDsl(elementType gisk.ElementType, key string, version string) (string, error) {
path := "./dsl/" + elementType + "_" + key + "_" + version + ".json"
bytes, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(bytes), nil
}
func main() {
//创建gisk实例
g := gisk.New()
//设置dsl获取器
g.SetDslGetter(&fileDslGetter{})
//...
}
- 注册比较符
系统实现默认的比较符 eq
, neq
, gt
, lt
, gte
, lte
,in
,notIn
,like
,notLike
如果需要自定义比较符,可以调用RegisterOperation方法。自定义比较符优先级高于系统内置的比较符,可以注册和系统同名比较符实现复写。
package main
import (
"errors"
"gitee.com/sreeb/gisk"
"regexp"
)
func main() {
//自定义正则匹配比较符
gisk.RegisterOperation("reg", func(left gisk.Value, operator gisk.Operator, right gisk.Value) (result bool, err error) {
if left.ValueType != gisk.STRING || right.ValueType != gisk.STRING {
err = errors.New("left and right must be string")
return
}
// 正则表达式匹配
reg := regexp.MustCompile(right.Value.(string))
if reg.MatchString(left.Value.(string)) {
result = true
}
return
})
g := gisk.New()
//...
}
- 注册函数
系统实现默认的函数 rand
,sum
,如果需要自定义函数,可以调用RegisterFunction方法。自定义函数优先级高于系统内置的函数,可以注册和系统同名函数实现复写。*
package main
import (
"gitee.com/sreeb/gisk"
"github.com/gogf/gf/v2/util/gconv"
)
func main() {
//注册求和函数
gisk.RegisterFunc("sum", func(parameters ...gisk.Value) (gisk.Value, error) {
var v float64
for _, parameter := range parameters {
v += gconv.Float64(parameter.Value)
}
return gisk.Value{ValueType: gisk.NUMBER, Value: v}, nil
})
g := gisk.New()
//...
}
基础值包含变量,输入值,函数。在dsl中用字符串表示。基础值
表示法:variate_age_1
解释:variate
表示为变量类型,age
表示变量唯一key(key可以有下划线),1
表示变量版本号。
变量DSL:
{
"key": "age",
"name": "年龄",
"desc": "用户年龄",
"version": "1",
"value_type": "number",
"default": 20,
"is_input": false
}
变量结构体:
type Variate struct {
Key string `json:"key" yaml:"key"` //唯一标识
Name string `json:"name" yaml:"name"` //变量名称(前端页面使用,不涉及逻辑)
Desc string `json:"desc" yaml:"desc"` //描述(前端页面使用,不涉及逻辑)
Version string `json:"version" yaml:"version"` //版本
ValueType ValueType `json:"value_type" yaml:"value_type"` //值类型(number:转成float64, string, bool, array:以,分割转成array, map:json字符串转成map)
Default interface{} `json:"default" yaml:"default"` //默认值
IsInput bool `json:"is_input" yaml:"is_input"` //是否要从输入值中匹配(true时会从输入值中匹配相同key的值进行赋值)
}
表示法:input_1_number
解释:input
表示为输入值类型,1
表示输入值,number
表示输入值数据类型。
输入值没有DSL
表示法:func_rand(input_1_number,func_sum(input_10_number,input_20_string))
解释:函数支持嵌套,函数参数只能是基础值。上述表达式解释为:rand(1,sum(10,20)),rand函数两个参数1和sum函数,sum函数两个参数10和20。 注册函数需要实现 type Func func(parameters ...Value) (Value, error)
类型。
函数无DSL
规则是基于多个比较条件通过逻辑运算符进行组合,最终得到一个布尔值。可以根据最终布尔值进行后续动作执行,赋值,访问url,发送消息,连接数据库等等操作。规则支持括号运算,支持串行并行执行
规则dsl
{
"key": "rule1", //唯一key
"name": "用户筛选", //规则名称
"desc": "用户筛选", //规则描述
"version": "1", //规则版本
"parallel": true, //是否并行执行(并行执行会先并发获取比较条件组所有结果,再用规则表达式进行逻辑运算)
"compares": {
"年龄小于40": {
"left": "variate_年龄_1", //比较条件左边
"operator": "lt", //比较条件运算符
"right": "input_40_number" //比较条件右边
},
"性别女": {
"left": "variate_性别_1",
"operator": "eq",
"right": "input_女_string"
},
"身高大于等于170": {
"left": "variate_身高_1",
"operator": "gte",
"right": "input_170_number"
}
}, //比较条件组
"expression": "年龄小于40 && (性别女 || 身高大于等于170)", //规则表达式
"action_true": [
{
"action_type": "assignment", //赋值操作
"variate": "variate_命中结果_1", //赋值变量
"value": "input_true_bool" //赋值值
},
{
"action_type": "geturl", //访问url
"url": "https://xxx.com" //url
}
], //true执行动作
"action_false": [
{
"action_type": "assignment", //赋值操作
"variate": "variate_命中结果_1", //赋值变量
"value": "input_false_bool" //赋值值
}
] //false执行动作
}
上述规则解释为:如果年龄小于40且性别为女或者身高大于等于170,则命中,否则未命中。命中时执行动作:变量命中结果赋值为true时,访问url。未命中时,赋值变量命中结果为false。
规则结构体:
type Rule struct {
Key string `json:"key" yaml:"key"` //唯一标识
Name string `json:"name" yaml:"name"` //名称
Desc string `json:"desc" yaml:"desc"` //描述
Version string `json:"version" yaml:"version"` //版本
Parallel bool `json:"parallel" yaml:"parallel"` //是否并发执行
Compares map[string]*Compare `json:"compares" yaml:"compares"` //比较
Expression string `json:"expression" yaml:"expression"` //计算公式
ActionTure []RawMessage `json:"action_true" yaml:"action_true"` //命中执行动作
ActionFalse []RawMessage `json:"action_false" yaml:"action_false"` //未命中执行动作
}
type Compare struct {
Left string `json:"left" yaml:"left"`
Operator Operator `json:"operator" yaml:"operator"` // 比较符号(可自定义注册)
Right string `json:"right" yaml:"right"`
}
规则集是多个规则的集合,支持串行并行执行和中断。并行模式下规则的先后顺序和中断不生效
规则集dsl:
{
"key": "ruleset", //唯一key
"name": "决策集1", //规则集名称
"desc": "决策集1", //规则集描述
"version": "1", //规则集版本
"parallel": false, //是否并行执行(串行执行:顺序执行规则,中断后不执行后续规则。 并行执行:并发执行规则,不考虑顺序和中断)
"rules": [
{
"rule_key": "rule1", //规则key
"rule_version": "1", //规则版本
"break_mode": "hit_break" //中断模式:hit_break命中中断,miss_break未命中中断。中断表示不执行后续的规则
},
{
"rule_key": "rule2",
"rule_version": "1",
"break_mode": "miss_break"
},
{
"rule_key": "rule3",
"rule_version": "1",
"break_mode": "miss_break"
}
]//规则集规则
}
规则集结构体:
type Ruleset struct {
Key string `json:"key" yaml:"key"` //唯一标识
Name string `json:"name" yaml:"name"` //名称
Desc string `json:"desc" yaml:"desc"` //描述
Version string `yaml:"version" json:"version"` //版本
Parallel bool `json:"parallel" yaml:"parallel"` //是否并发执行
Rules []*rulesetRule `json:"rules" yaml:"rules"` //规则集规则
}
type rulesetRule struct {
RuleKey string `json:"rule_key" yaml:"rule_key"` //规则key
RuleVersion string `json:"rule_version" yaml:"rule_version"` //规则版本
BreakMode BreakMode `json:"break_mode" yaml:"break_mode"` //中断模式
}
决策流支持普通节点,分流节点和动作节点
普通节点可以执行一个元素,元素可为规则,规则集等
type generalFlowNode struct {
NodeKey string `json:"node_key" yaml:"node_key"` //节点key
NodeType FlowNodeType `json:"node_type" yaml:"node_type"` //节点类型
EleType ElementType `json:"element_type" yaml:"element_type"` //元素类型
EleKey string `json:"element_key" yaml:"element_key"` //元素key
EleVersion string `json:"element_version" yaml:"element_version"` //元素版本
NextNode string `json:"next_node" yaml:"next_node"` //下一个节点
}
分流节点通过比较条件进行分流
type branchFlowNode struct {
NodeKey string `json:"node_key" yaml:"node_key"` //节点key
NodeType FlowNodeType `json:"node_type" yaml:"node_type"` //节点类型
Left string `json:"left" yaml:"left"` //左侧
Branches []struct {
Operator Operator `json:"operator" yaml:"operator"` // 比较符号
Right string `json:"right" yaml:"right"` // 右侧
NextNode string `json:"next_node" yaml:"next_node"` // 下一个节点
} `json:"branches" yaml:"branches"` // 分支
}
动作节点执行动作,公用规则动作,可以自定义
type actionFlowNode struct {
NodeKey string `json:"node_key" yaml:"node_key"` //节点key
NodeType FlowNodeType `json:"node_type" yaml:"node_type"` //节点类型
Actions []RawMessage `json:"actions" yaml:"actions"` //动作
NextNode string `json:"next_node" yaml:"next_node"` //下一个节点
}
复杂的决策流:
{
"key": "flow1",
"name": "普通决策流",
"desc": "普通决策流",
"version": "1",
"nodes": [
{
"node_key": "start",
"node_type": "general_flow_node",
"element_type": "ruleset",
"element_key": "ruleset",
"element_version": "1",
"next_node": "branch_node"
},
{
"node_key": "branch_node",
"node_type": "branch_flow_node",
"left": "func_rand(input_1_number,input_100_number)",
"branches": [
{
"operator": "lte",
"right": "input_20_number",
"next_node": ""
},
{
"operator": "gt",
"right": "input_20_number",
"next_node": ""
}
]
},
{
"node_key": "branch_node",
"node_type": "branch_flow_node",
"left": "func_rand(input_1_number,input_100_number)",
"branches": [
{
"operator": "lte",
"right": "input_20_number",
"next_node": "action_flow_node1"
},
{
"operator": "gt",
"right": "input_20_number",
"next_node": "action_flow_node2"
}
]
},
{
"node_key": "action_flow_node1",
"node_type": "action_flow_node",
"next_node": "",
"actions": [
{
"action_type": "assignment",
"variate": "variate_决策流分流_1",
"value": "input_20%_string"
}
]
},
{
"node_key": "action_flow_node2",
"node_type": "action_flow_node",
"next_node": "",
"actions": [
{
"action_type": "assignment",
"variate": "variate_决策流分流_1",
"value": "input_80%_string"
}
]
}
]
}