Falando sobre a aplicação do mecanismo de regras no front-end

-2021- copiar copy.jpg

1. Introdução

      O mecanismo de regras geralmente é executado como um serviço independente em um determinado volume de produtos e toma decisões de negócios razoáveis ​​ao receber dados válidos. Sob a iteração perene de projetos front-end, o legado de alguns módulos de negócios importantes ou frequentemente expandidos devido a problemas objetivos, como muito tempo, poucas anotações e dificuldade de leitura, causarão certos problemas em iterações posteriores e, inevitavelmente, causarão testes adicionais . pressão. Portanto, um mecanismo de regras leve que possa ser executado no lado do navegador eliminará completamente a existência de tais problemas.

2. Um estudo preliminar do mecanismo de regras

      O mecanismo de regras adequado para o navegador foi implementado na comunidade de código aberto. Aqui json-rules-engineentraremos no mundo dos mecanismos de regras através de projetos de código aberto~

json-rules-engine é um mecanismo de regras leve e poderoso. As regras são compostas de estruturas json simples, tornando-as legíveis e fáceis de persistir.

json-rules-engineAs regras são combinadas pelo formato de dados JSON, que é conveniente tanto para a leitura das regras quanto para o armazenamento persistente das regras, e o peso leve também é uma de suas características~

2.1 Faça as regras do jogo:

Ao formular as regras de um evento esportivo, é acordado que:

  1. Quando o jogo atingir 40 minutos, se os corredores cometerem 5 ou mais faltas, serão expulsos;
  2. Quando o jogo atingir 48 minutos, se o atleta cometer 6 ou mais faltas, será expulso;

2.2 Construir um mecanismo de regras:

2.2.1 Crie um mecanismo:

A criação do mecanismo é concluída simplesmente importando e instanciando o objeto do mecanismo como um módulo:

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

2.2.2 Adicionar regras:

eventAs informações que são acionadas quando a regra é atingida são definidas pelo       objeto conditionse a regra específica é definida pelo objeto. Cada regra contém pelo menos três partes: fact, operatore valuetrês partes, respectivamente definindo o nome do fato, o operador e o limite ou intervalo da regra, regras e regras atuais Use anypara representar o OR lógico e use allpara representar o relacionamento AND lógico, que constitui as regras específicas deste 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 });
复制代码

下图是这个赛事规则的可视化规则示意图,在文末将会讲解如何利用可视化工具来高效阅读所定义的规则~
imagem.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 个属性均符合条件后才允许通过,这个时候对应的菜单才允许被显示:
imagem.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 规则示意图:

Quando houver mais regras, a leitura de código puro também se tornará mais trabalhosa, então, neste momento, você pode usar ferramentas visuais para ler as imagens. As imagens a seguir são as descrições das regras de caso acima:
regra1-1.pngregra1-2.pngregra1-3.png

4. Esquema de visualização de regras

A premissa da visualização de regras é converter as regras de mecanismo escritas em objetos JSON e realizar a visualização de regras carregando os objetos JSON.

4.1 Processamento de regras JSON:

json-rules-engineO Ruleobjeto fornece toJSON()funções e escrevemos diretamente um script para obter os dados de uma regra 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);
复制代码

Após obter os dados da regra, é necessário fazer o processamento simples dos dados, namenomeando o nome da regra para distingui-la, decisionsinserir os dados da regra obtida em:

{
  "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 Visualização de regras:

      json-rule-editorProjetos de código aberto podem concluir rapidamente a visualização das regras do mecanismo. json-rule-editor é a página online da implantação do projeto. Ao selecionar a pasta de regras, fazer upload do arquivo de regras e atualizar, você pode ver a seguinte visualização rulena guia direita após selecionar a regra . Decisionsrule:
imagem.png

5. Resumo

      O uso de json-rules-enginemecanismos de regras de código aberto pode realizar o processamento de otimização em lógica complexa e, com soluções visuais, é mais conveniente ler as regras do mecanismo. Os casos acima são apenas para json-rules-engineaplicações superficiais, e funções de motor mais complexas precisam ser exploradas posteriormente. Se você estiver interessado, vamos trabalhar juntos~

Guess you like

Origin juejin.im/post/7158267715789488164