51 Star 420 Fork 148

hubert-樂xx / GRule

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
README.md 14.71 KB
一键复制 编辑 原始数据 按行查看 历史
xxb 提交于 2023-08-05 18:44 . fix: mysql

Groovy DSL 动态规则(rule)执行引擎

DSL(特定领域语言): 开发 和 业务 共识的语言. 方便业务表达需求, 方便开发理解业务

一个业务需求(一般程序或一个接口)可以抽象成为: 按一定业务逻辑规则处理数据,然后返回数据

  • 一个人可以用成百上千个属性组成,由这些属性衍生出新的属性(例如,好人/坏人) 返回一个业务结果(0..多个属性值)
  • 一般接口: 查询数据库或接口,对数据进行简单逻辑计算,最后返回一些属性

框架提供一种通用流程: 从多种数据源获取数据转换成属性, 并按业务规则 DSL 执行
框架可以执行成千上万的这种通用程序逻辑, 可以动态修改流程中的每个过程(数据源,属性,规则),即时生效

可用于风控规则, 电商价格规则, 财务计算规则, 工资计算规则等其它业务规则

框架概念

  • 决策: 抽象一个业务需求(一般程序或一个接口)为一条决策,一条决策包含多个业务规则
  • 规则: 框架用自创 DSL规则 语言表达业务逻辑
    规则 {
        规则名 = '芝麻分限制'
        拒绝 { 芝麻分 < 620 }
    }
  • 指标/字段/变量/属性: 规则中的变量 芝麻分
  • 收集器: 指标值来源于数据源配置: 接口,SQL,脚本3种类型

例子 http://grule.cn/ test:test

规则 定义

  • 规则名 和多个 拒绝, 通过, 人工, 操作 组成(按顺序依次执行)
规则 {
    规则名 = '大小比较'
    拒绝 { 年龄 > 40 || 年龄 < 25 }
}
规则 {
    规则名 = '通过规则'
    通过 { 年龄 == 30 }
}
规则 {
    规则名 = '赋值规则'
    操作 { 产品代码 = 'xx' }
}
规则 { // 电商价格规则
    规则名 = '条件赋值规则'
    操作 {
        if (当前时间 > '2021-03-02 00:00:00') {
            单价 = 10
        } 
    }
}
规则 {
    规则名 = '列表判断规则'

    风险地 = ["成都", "巴中"]

    拒绝 { 工作地 in 风险地 }
}
规则 {
    规则名 = '包含规则'
    拒绝 { "$姓名".contains("xx") }
}

策略 定义

  • 策略名 和多个 操作, 条件, 规则, 评分卡, 决策 组成(按顺序依次执行)
  • 条件 函数返回false, 则跳出, 继续执行下一个策略

规则 策略

策略 {
    策略名 = '004244'
    
    操作 { jj_代码 = '004244' }
    条件 { jj_名 }
    
    规则 {
       拒绝 { true }
    }
}

评分卡 策略(专业版)

模型 : 是由多个评分条目组成

  • 普通评分条目: 由 属性名, 值匹配范围, 分值 组成
    • 例: ['年龄', 18..24, 40]
    • 例: ['工作成市', ['成都', '巴中'], 99]
  • 函数分条目: 一个求值函数 组成
    • 例: [{ -> 100}]
  • 变量分条目: 一个数值变量 组成
    • 例: ['芝麻分']
  • 动态分条目: 一个数值变量,及其值计算函数 组成
    • 例: ['逾期次数', {次数 -> -((次数?:0) * 100)}]
  • 模型分计算: 依次遍历每个评分条目, 得到匹配的分值相加
  • 最终评分 = 基础分 + 模型计算得分

赋值变量 : 把评分结果赋值给的变量

策略 {
    策略名 = '测试评分卡'
    
    评分卡 {
       评分卡名 = "测试评分卡"
       基础分 = 600
       模型 = [
           ['年龄', 18..24, 40],
           ['年龄', 25..45, 80],
           ['年龄', 46..60, 50],
           ['性别', 'F', 50],
           ['性别', 'M', 60],
           [{ -> 100}], // 函数分
           ['芝麻分'], //变量值分
           ['逾期次数', {次数 -> -((次数?:0) * 100)}], // 动态分
           ['工作成市', ['成都', '巴中'], 99]
       ]
       赋值变量 = '评分结果'
    }
    
    规则 {
      规则名 = "评分判断"
      拒绝 { 评分结果 < 60 }
    }
}

委托子 决策 策略(专业版)

  • 嵌套其它决策执行(注意不要循环嵌套)
  • 会形成一条单独的决策执行记录(id为父决策的id为前缀)
  • 异步:
    • 不配置默认为false; 为true时, 输出 配置不生效
    • 值为false: 并且 当子决策执行结果为Reject的时,会结束当前决策执行
  • 传参: 共享当前决策数据给子决策使用
  • 输出: 会设置 到当前执行上下文, 可以使用. 没有配置时: 则使用子决策的返回属性作为 输出
策略 {
    策略名 = '测试子决策'
    
    决策 {
        决策id = "cd7049e7e76441fa8315c2e1bd883042"
       // 不配置默认为false, 异步为true时, 输出则不生效
       // 异步 = true
       
       // 左(子决策) = 右(父决策)
       传参 {
          p1 = 1
          p2 = "aa"
          p3 = p3 // p3的值从父决策来
       }
       
       // 左(父决策) = 右(子决策)
       输出 {
          p111 = p1 // p1的值从子决策来
          p222 = p3 // p3的值从子决策来
       }
    }
}

决策定义

  • 决策名, 决策描述 和多个 操作, 策略 组成(按顺序依次执行)
  • 触发当前决策: http://ip:port/decision/{id}/decide
  • 接口返回: 3种结果(Accept, Reject, Review) 和多个返回属性值(由配置指定)
// 返回示例
{
  "decideId": "647f297a2e4540dfa93991b5b6e7b44d",
  "result": "Accept",
  "decisionId": "jj_analyse",
  "status": "0000",
  "desc": null,
  "data": {
    "jj_code": "165525"
   }
}
  • 依次从上往下执行
决策名 = '测试1'
决策描述 = ''

// 返回的调用决策方的结果属性值
// 返回属性 '身份证号码'

// 预操作执行
操作 {}

策略 {
    策略名 = '预处理'
    // 条件 { true }

    规则 {
        规则名 = '年龄限制'
        拒绝 { 年龄 > 40 || 年龄 < 25 }
    }
}

函数定义: 自定义决策函数

决策名 = '测试1'

策略 {
    策略名 = '测试策略'
    规则 {
        规则名 = '使用函数'
        操作 {
            钉钉消息("发个消息") 
        }
    }
}

函数定义("钉钉消息") {String msg -> 
    Utils.http()
        .post("https://oapi.dingtalk.com/robot/send?access_token=7e9d8d97e6b5e76a6a07b0c5d7c31e82f0fbdb8ced1ac23168f9fd5c28c57f1a")
        .jsonBody(JSON.toJSONString(
            [msgtype: 'text', text: [content: "Fund: " + msg], at: ['isAtAll': false]]
        )).debug().execute()
}

Image text

指标/字段/属性

使用 '年龄' 指标

拒绝 { 年龄 > 50 }

指标值获取

Image text

指标列表

Image text

收集器(数据源)

支持从SQL(数据库),http(接口),script(自定义groovy脚本函数)获取数据

收集器执行

Image text

决策执行过程中生产的数据收集

Image text

决策执行

决策执行记录

Image text

决策执行详情

Image text

统计图

决策执行统计

Image text

决策规则统计

Image text

权限

静态权限

  • 权限管理, 登陆, 用户-创建, 决策-创建, 字段-添加, 字段-查看, 字段-更新, 字段-删除, 收集器-添加, 收集器-查看, 收集器-更新, 收集器-删除, 操作历史查看

权限管理: 可修改任何用户的任何权限

用户-创建: 创建者默认拥有被创建的用户的所有动态权限

操作历史查看: 目前支持 决策-查看-{某个决策}, 字段-查看, 收集器-查看

动态资源(用户)权限

  • 被创建用户默认拥有 用户-查看-{自己} 此动态权限
  • 每添加一个用户 就会生成与之对应的以下动态权限

用户-查看-{用户1}: 能搜索到此用户1

用户-删除-{用户1}: 能删除用户1

用户-重置密码-{用户1}: 能为用户1重置密码

用户-继承决策权限-{用户1}: 能自动拥有用户1新创建的决策的所动态有权限

用户-修改权限-{用户1}: 能修改指定用户1权限

  • 如果: 修改用户有 权限管理, 则可修改用户1的所有权限
  • 否则: 能修改用户1的权限只能是, 当前用户所拥有的权限(除 用户-创建)

动态资源(决策)权限

  • 创建者默认自动拥有生成动态权限
  • 每添加一个决策 就会生成与之对应的以下动态权限

决策-查看-{决策1}: 拥有查看决策1的权限

决策-删除-{决策1}: 拥有删除决策1的权限

决策-更新-{决策1}: 拥有更新决策1的权限

用户分类(虚称)

  • 超级用户(系统管理员): 拥有 权限管理 的用户
  • 用户管理员: 拥有 用户-创建 的用户
  • 普通用户: 非 权限管理用户-创建 的用户

Image text

集群(专业版)

  1. 多节点数据一致
  2. 负载均衡
## 应用名,相同应用的实例自动组成集群
sys.name=grule
## 节点与集群中其它节点的数据交换endpoint, 例: :9001 或者 127.0.0.1:9001
remoter.hp=:9001
## 主节点的endpoint, 例: :8001 或者 127.0.0.1:8001
remoter.masterHps=:8001

实践1: 一个域名下有多个grule节点

## 所有节点配置
remoter.hp=:9001
remoter.masterHps=xxx.com:9001

实践2: 多个ip节点grule

## ip1: 视ip2为master节点, 那么ip1会把自己上传到ip2或ip3,并拿到ip2或ip3拥有的所有节点信息
remoter.hp=:9001
remoter.masterHps=[ip2/ip3]:9001
## ip2: 视ip3为master节点, 那么ip2会把自己上传到ip1或ip3,并拿到ip1或ip3拥有的所有节点信息
remoter.hp=:9001
remoter.masterHps=[ip1/ip3]:9001
## ip3: 视ip1为master节点, 那么ip3会把自己上传到ip1或ip2,并拿到ip1或ip2拥有的所有节点信息
remoter.hp=:9001
remoter.masterHps=[ip1/ip2]:9001

专业版

v2.2.0-pro

  • feat: 执行数据清理接口
  • upgrade: tiny:1.2.2, groovy:4.0.6
  • feat: 使用xnet替换集群和http
  • refactor: 用Closure替换GStringTemplate
  • build: java17
  • refactor: 决策记录状态使用枚举
  • refactor: 实体 uuid 换成 nanoid
  • pref: 优化决策接口参数验证
  • fix: this is incompatible with sql_mode=only_full_group_by

v2.1.4-pro

  • feat: 默认随机分配早上一个时间点做数据清理
  • refactor: 数据清理用减小锁范围,保留时间缩小至分钟
  • refactor: 使用tiny:httper替代okhttp
  • upgrade: xhttp:1.1.7

v2.1.3-pro

  • feat: 推送收集结果数据
  • feat: 推送决策结果数据
  • feat: 锁表添加资源id(表示锁住的资源)
  • upgrade: tiny:1.1.8

v2.1.2-pro

  • upgrade: vue, vue-router, element-ui
  • feat: 添加用户名验证规则
  • upgrade: xhttp:1.1.5
  • fix: session失效后 刷新决策结果页面 npe
  • feat: 自动适配多少并发执行决策
  • upgrade: enet:1.1.1

v2.1.1-pro

  • fix: 人工不能返回Review结果
  • feat: web session id 关联 sys.name
  • fix: SQL收集器结果没保存懒计算值的结果
  • fix: 评分卡区分范围比较,枚举对比

v2.1.0-pro

  • feat: 单jar可执行文件
  • upgrade: groovy 3.0.11
  • fix: 创建新决策后没显示创建者

v2.0.2-pro

  • feat: 集群: 可插拨功能
  • feat: 容器部署支持
  • upgrade: 升级基础组件,tiny,enet,remoter,xio,http

使用说明

系统基于java轻量级框架: Tiny

docker run --rm -p 9090:9090 -e "JAVA_TOOL_OPTIONS=-Xms512m -Xmx512m" xnatural/grule-pro:2.2.0_202302031529

环境要求

jdk17

数据库

数据库要求: mysql 8.0.4+

-- 创建数据库
CREATE DATABASE IF NOT EXISTS rule DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# 替换 conf/app.properties 中的 jpa_rule.url 配置项
jpa_rule.url=jdbc:mysql://localhost:3306/rule?useSSL=false&user=root&password=root&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true

开发环境运行

IntelliJ IDEA 运行 Main.groovy 启动

参与贡献

xnatural@msn.cn

Image text

Groovy
1
https://gitee.com/xnat/grule.git
git@gitee.com:xnat/grule.git
xnat
grule
GRule
master

搜索帮助