Hablando de la aplicación del motor de reglas en el front-end

-2021- copia copia.jpg

1. Introducción

      El motor de reglas a menudo se ejecuta como un servicio independiente en un cierto volumen de productos y toma decisiones comerciales razonables al recibir datos válidos. Bajo la iteración perenne de proyectos front-end, el legado de algunos módulos comerciales importantes o ampliados con frecuencia debido a problemas objetivos como mucho tiempo, pocas anotaciones y dificultad en la lectura causará ciertos problemas en iteraciones posteriores e inevitablemente provocará pruebas adicionales. presión Por lo tanto, un motor de reglas liviano que pueda ejecutarse en el lado del navegador eliminará por completo la existencia de tales problemas.

2. Un estudio preliminar del motor de reglas

      El motor de reglas adecuado para el navegador se ha implementado en la comunidad de código abierto. Aquí entraremos json-rules-engineen el mundo de los motores de reglas a través de proyectos de código abierto ~

json-rules-engine es un motor de reglas ligero y potente. Las reglas se componen de estructuras json simples, lo que las hace legibles para los humanos y fáciles de conservar.

json-rules-engineLas reglas se combinan con el formato de datos JSON, que es conveniente tanto para la lectura de las reglas como para el almacenamiento persistente de las reglas, y el peso ligero también es una de sus características ~

2.1 Hacer las reglas del juego:

Al formular las reglas de un evento deportivo, se acuerda que:

  1. Cuando el juego llegue a los 40 minutos, si los corredores cometen 5 o más faltas, serán expulsados;
  2. Cuando el juego llegue a los 48 minutos, si el atleta comete 6 o más faltas, será expulsado;

2.2 Cree un motor de reglas:

2.2.1 Crear un motor:

La creación del motor se completa simplemente importando e instanciando el objeto del motor como un módulo:

let { Engine } = require('json-rules-engine');
let engine = new Engine();
复制代码

2.2.2 Agregar reglas:

eventLa información que se activa cuando se aplica la regla está definida por el       objeto conditionsy la regla específica está definida por el objeto Cada regla contiene al menos tres partes: fact, operatory valuetres partes, que definen respectivamente el nombre del hecho, el operador y el umbral o rango de la regla actual, reglas y reglas Use anypara representar el OR lógico y use allpara representar la relación AND lógica, que constituye las reglas específicas de este evento:

let event = {
  type: 'fouledOut',
  params: {
    message: 'Player has fouled out!'
  }
};
let conditions = {
  any: [
        {
            all: [
                { fact: 'gameDuration', operator: 'equal', value: 40 },
                { fact: 'personalFoulCount', operator: 'greaterThanInclusive', value: 5 },
            ]
        }, {
            all: [
                { fact: 'gameDuration', operator: 'equal', value: 48 },
                { fact: 'personalFoulCount', operator: 'greaterThanInclusive', value: 6 },
            ]
        }
    ]
};
engine.addRule({ conditions, event });
复制代码

下图是这个赛事规则的可视化规则示意图,在文末将会讲解如何利用可视化工具来高效阅读所定义的规则~
imagen.png

2.2.3 定义Facts:

这里使用对象来表示一个Fact,让它进入到规则引擎并经过每一条规则来判断是否允许通过:

let facts = {
    personalFoulCount: 6,
    gameDuration: 40,
}
复制代码

2.2.4 运行引擎:

通过引擎校验后将在控制台输出Player has fouled out!的信息,说明这个运动员已经命中了被罚出场的规则~

const { events } = await engine.run(facts);
events.map(event => console.log(event.params.message));
复制代码

3. 线上项目分析应用

3.1 场景 1&多属性控制菜单:

      在线上项目的某一个菜单处出现了同时由 8 个属性共同控制一个菜单的显示与隐藏,猜想在最初也只是仅包含一个用户权限和数据状态的控制吧,但随着业务的持续变动导致这块的控制属性持续增加,也是在最近的迭代中又为这个菜单补充了一个属性的控制,其实这些属性中也不全是 Boolean 类型的判断,下面通过对比源代码和应用规则引擎后的代码来看一下:

/**
  	源代码:
   
    // item.error_type !== 53
    // !item.not_foreign_nationality_settle &&
    // !item.not_hongkong_macao_taiwan_settle && 
    // !item.is_excess_single && 
    // isSuperAdmin!= 'groupmanager' && 
    // !isMissingPayMode(item) && 
    // (item.status === 0 || item.status === 4 || item.status === 9) && 
    // showCreateBillBtn(item) && // 忽略,因函数逻辑不易拆解
    // !item.bankerr"
 */

// 引擎规则:
let conditions = {
    all: [
        // 非此状态的允许通过
        { fact: 'error_type', operator: 'notEqual', value: 53 },
        // 非组织管理员允许通过
        { fact: 'isSuperAdmin', operator: 'notEqual', value: 'groupmanager' },
        // 状态符合:0 4 9 允许通过
        { fact: 'status', operator: 'in', value: [0, 4, 9] },
        // 非外籍人员允许通过
        { fact: 'isForeignNationality', operator: 'equal', value: false },
        // 非港澳台人员允许通过
        { fact: 'isHongkongMacaoTaiwanRegion', operator: 'equal', value: false },
        // 未超出单笔限额允许通过
        { fact: 'isExcessSingle', operator: 'equal', value: false },
        // 支付方式未丢失用户允许通过
        { fact: 'isMissingPayMode', operator: 'equal', value: false },
        // 银行卡号未丢失时允许通过(当支付方式为银行卡且卡号丢失时此节点为true)
        { fact: 'isMissingBankCardNumber', operator: 'equal', value: false },
    ]
}

let event = {
    type: 'showAllowSettlement',
    params: {
        message: 'You can display the settlement menu.!'
    }
}
engine.addRule({ conditions, event });
复制代码

下图是上述规则的可视化示意图,通过图所示,当着 8 个属性均符合条件后才允许通过,这个时候对应的菜单才允许被显示:
imagen.png

3.2 场景 2&多属性多分支控制菜单:

上面的场景 1 你可能看不出来规则引擎带来的便利,可能觉得原来的代码看起来也说的过去,那接着看这个包含多个分支的控制案例。
商户开票的功能设计由于不同的开票方式等其他的一系列因素导致这块有 4 个比较大的分支处理,这里我拆分出其中的一个分支来利用规则引擎简单的实现一下:

3.2.1 源项目逻辑分析:

下面是摘自源项目的部分逻辑控制,其中部分的属性和函数背后还有很多的逻辑处理,对于代码的阅读和功能的测试都会造成困扰:

申请开票方式1:
控制菜单显示:
  !permissionSwitch() 
  && ((isSuperAdmin && projectListCode && is_allow_apply_invoice) 
  || (projectListCode == '' && is_allow_apply_invoice))
控制菜单触发:
  (settlement_business_scenario_switch 
      && invoiceDealObj.isShowHistory 
      && this.invoiceDealObj.historyCanInvoice !== 0) 
  || (invoiceDealObj.isShowHistory 
      && invoiceDealObj.merchantHistoryCanInvoice !== 0)
复制代码

3.2.2 引擎规则编写:

通过源代码分析控制此菜单的数据达 10 个,通过逻辑与和逻辑或共同控制着 16 条规则的运行:

let conditions = {
    all: [
        { fact: 'invoice_way', operator: 'notEqual', value: 1 },
        {
            any: [
                {
                    all: [
                        {
                            any: [
                                { fact: 'isSuperAdmin', operator: 'equal', value: 'superadmin' },
                                { fact: 'isSuperAdmin', operator: 'equal', value: 'groupmanager' },
                                {
                                    all: [
                                        { fact: 'isSuperAdmin', operator: 'equal', value: 'projectmanager' },
                                        { fact: 'project_invoice_switch', operator: 'equal', value: true },
                                    ]
                                },
                                {
                                    all: [
                                        { fact: 'isSuperAdmin', operator: 'equal', value: 'financial' },
                                        { fact: 'hasCreatePower', operator: 'equal', value: 1 },
                                    ]
                                },
                            ]
                        },
                        { fact: 'projectListCode', operator: 'notEqual', value: '' },
                        { fact: 'is_allow_apply_invoice', operator: 'equal', value: true },
                    ]
                },
                {
                    all: [
                        { fact: 'projectListCode', operator: 'equal', value: '' },
                        { fact: 'is_allow_apply_invoice', operator: 'equal', value: true },
                    ]
                },
            ]
        },
        {
            any: [
                {
                    all: [
                        { fact: 'settlement_business_scenario_switch', operator: 'equal', value: true },
                        { fact: 'isShowHistory', operator: 'equal', value: true, },
                        { fact: 'historyCanInvoice', operator: 'notEqual', value: 0, },
                    ]
                },
                {
                    all: [
                        { fact: 'isShowHistory', operator: 'equal', value: true },
                        { fact: 'merchantHistoryCanInvoice', operator: 'notEqual', value: 0 }
                    ]
                }
            ]
        }
    ]
}

let event = {
    type: 'allowInvoicing',
    params: {
        message: 'Invoicing method 1 allowed!'
    }
}
复制代码

3.2.3 规则示意图:

Cuando haya más reglas, la lectura del código puro también se volverá más laboriosa, por lo que en este momento puede usar herramientas visuales para leer las imágenes. Las siguientes imágenes son las descripciones de las reglas de casos anteriores:
regla1-1.pngregla1-2.pngregla1-3.png

4. Esquema de visualización de reglas

La premisa de la visualización de reglas es convertir las reglas escritas del motor en objetos JSON y realizar la visualización de reglas cargando los objetos JSON.

4.1 Procesamiento JSON de reglas:

json-rules-engineEl Ruleobjeto proporciona toJSON()funciones, y escribimos directamente un script para obtener datos de una regla jsonizada:

const { Rule } = require("json-rules-engine");
const json = new Rule({
  conditions: {
    any: [
      {
        all: [
          { fact: "gameDuration", operator: "equal", value: 40 },
          {
            fact: "personalFoulCount",
            operator: "greaterThanInclusive",
            value: 5,
          },
        ],
      },
      {
        all: [
          { fact: "gameDuration", operator: "equal", value: 48 },
          {
            fact: "personalFoulCount",
            operator: "greaterThanInclusive",
            value: 6,
          },
        ],
      },
    ],
  },
  event: {
    type: "fouledOut",
    params: {
      message: "Player has fouled out!",
    },
  },
}).toJSON();
console.log(json);
复制代码

Después de obtener los datos de la regla, es necesario hacer un procesamiento simple de los datos, namenombrando el nombre de la regla para distinguirla, decisionsinserte los datos de la regla obtenidos en:

{
  "name": "rule",
  "decisions": [
    {
      "conditions": {
        "priority": 1,
        "any": [
          {
            "priority": 1,
            "all": [
              {
                "operator": "equal",
                "value": 40,
                "fact": "gameDuration"
              },
              {
                "operator": "greaterThanInclusive",
                "value": 5,
                "fact": "personalFoulCount"
              }
            ]
          },
          {
            "priority": 1,
            "all": [
              {
                "operator": "equal",
                "value": 48,
                "fact": "gameDuration"
              },
              {
                "operator": "greaterThanInclusive",
                "value": 6,
                "fact": "personalFoulCount"
              }
            ]
          }
        ]
      },
      "priority": 1,
      "event": {
        "type": "fouledOut",
        "params": {
          "message": "Player has fouled out!"
        }
      }
    }
  ]
}
复制代码

4.2 Visualización de reglas:

      json-rule-editorLos proyectos de código abierto pueden completar rápidamente la visualización de las reglas del motor. json-rule-editor es la página en línea de la implementación del proyecto. Al seleccionar la carpeta de la regla, cargar el archivo de la regla y actualizar, puede ver la siguiente visualización ruleen la pestaña derecha después de seleccionar la regla Decisions.regla:
imagen.png

5. Resumen

      El uso de json-rules-enginemotores de reglas de código abierto puede realizar un procesamiento de optimización en una lógica compleja y, con soluciones visuales, es más conveniente leer las reglas del motor. Los casos anteriores son solo para json-rules-engineaplicaciones poco profundas, y las funciones más complejas del motor deben explorarse más adelante. Si está interesado, trabajemos juntos ~

Supongo que te gusta

Origin juejin.im/post/7158267715789488164
Recomendado
Clasificación