【Drools】(三)基于业务数据动态调用 DRL 规则文件:详细实现与测试指南

基于业务数据动态调用 DRL 规则文件:详解与实战

在业务规则管理中,DRL 文件(Drools Rule Language 文件)用于定义和执行业务规则。通过动态调用 DRL 文件,我们可以根据不同的业务场景灵活配置和执行定制化的规则,从而提高业务规则配置的灵活性和效率。本文将详细介绍如何基于业务数据动态调用 DRL 规则文件,并提供详细的代码注释和实现步骤,以帮助理解其执行过程。我们还将展示一个测试功能,用于模拟真实场景并验证规则执行的成功与否。

drools介绍,请参考: 探索Drools:Java世界的规则引擎

1. DRT 文件介绍

DRL(Drools Rule Language)文件是用来编写规则的文件格式,基于 DRT 规则模板动态配置生成 DRL 规则文件(更多请看:(一)基于业务需求动态生成 DRT 规则模板:事实与动作定义详解),我们可以根据不同的业务场景创建定制化的规则,提高业务规则配置的灵活性和效率。

下面是: (二)基于业务需求动态生成 DRL 规则文件:事实与动作定义详解

package org.drools

import com.xinyuan.re.utils.DateUtils

declare SuppliersNumberFactPVO
    purchaseMethod: String
    projectStage: String
    suppliersNumber: Integer
end

declare SubmitTaskVerifyMessage
    state: int
    text: String
end

rule "re_openbid_supplier_count_0"
    when 
        $suppliers_number_fact_p_v_o : SuppliersNumberFactPVO(
            ("00380002,00380024,00380020,00380003" == "null" || purchaseMethod memberOf "00380002,00380024,00380020,00380003") &&
            ("1" == "null" || projectStage == 1) &&
            ("3" == "null" || suppliersNumber < 3)
        );
        $submit_task_verify_message : SubmitTaskVerifyMessage();
    then 
        $submit_task_verify_message.setState(2);
        $submit_task_verify_message.setText("招标的项目,投标人数量少于3个不得开标");
end

rule "re_openbid_supplier_count_1"
    when 
        $suppliers_number_fact_p_v_o : SuppliersNumberFactPVO(
            ("00380002,00380003,00380020,00380024" == "null" || purchaseMethod memberOf "00380002,00380003,00380020,00380024") &&
            ("2" == "null" || projectStage == 2) &&
            ("3" == "null" || suppliersNumber < 3)
        );
        $submit_task_verify_message : SubmitTaskVerifyMessage();
    then 
        $submit_task_verify_message.setState(2);
        $submit_task_verify_message.setText("资审阶段的项目,申请人数量少于3个不得开启");
end
2. 规则测试功能

我们为此专门开发了一个测试功能,可以模拟真实的业务场景,并调用规则执行,返回规则执行结果。通过此功能,可以快速验证规则的正确性和业务适用性。

测试功能界面如图所示:
在这里插入图片描述
事实数据如下:
在这里插入图片描述

在测试功能中,事实定义的数据会自动加载出来,用户可以根据实际需求输入或选择相关参数,点击测试按钮后系统会自动调用规则引擎,返回测试结果。

3. 代码实现详解

下面是完整的代码实现,并附上详细注释,帮助理解其生成过程:


/**
* 规则测试
*
* @param reTestPVO 规则测试PVO
* @return 测试结果
*/
@Override
public ReturnValue<Map<String, Object>> reTest(ReTestPVO reTestPVO) {
   RuleEngineContext ruleEngineContext = new RuleEngineContext();
   BeanUtils.copyProperties(reTestPVO, ruleEngineContext);
   return execRule(ruleEngineContext);
}


/**
 * 执行规则
 *
 * @param ruleEngineContext 规则引擎上下文
 * @return 执行结果
 */
@Override
public ReturnValue<Map<String, Object>> execRule(RuleEngineContext ruleEngineContext) {
    log.info("---------------执行规则开始---------------");

    // 获取规则定义
    GetReDefine getReDefine = reEngineService.getReDefine(ruleEngineContext.getRuleCode());
    Map<String, Object> actionMap = new HashMap<>();

    // 检查规则文件是否存在
    if (StringUtils.isNotBlank(getReDefine.getDrl_rule_file())) {
        // 获取事实定义
        GetReFactDefine getReFactDefine = reEngineService.getReFactDefine(getReDefine.getId());

        // 获取动作定义
        GetReActionDefine getReActionDefine = reEngineService.getReActionDefine(getReDefine.getId());

        // 使用 KieHelper 构建规则引擎
        KieHelper helper = new KieHelper();
        GetBigColumnRVO getBigColumnRVO = bigColumnFacade.getBigColumn(getReDefine.getDrl_rule_file());
        helper.addContent(getBigColumnRVO.getZdz(), ResourceType.DRL);
        log.info("执行规则,drl文件:{}", getBigColumnRVO.getZdz());

        // 构建 KieBase
        KieBase kieBase = helper.build();

        // 获取事实类型和动作类型
        FactType factType = kieBase.getFactType(Constants_public.ORG_DRT_PACKAGE_NAME, getReFactDefine.getClass_name());
        FactType actionType = kieBase.getFactType(Constants_public.ORG_DRT_PACKAGE_NAME, getReActionDefine.getClass_name());
        log.info("执行规则,事实定义,所属服务:{}", getReFactDefine.getOwn_service());

        Map<String, Object> bizData;

        // 如果业务数据为空,则根据业务编号获取业务数据
        if (ruleEngineContext.getBizData().isEmpty()) {
            ReturnValue<Map<String, Object>> returnValue = reBizFacade.getBizData(
                getReFactDefine.getOwn_service(),
                RuleEngineContext.builder()
                    .bizId(ruleEngineContext.getBizId())
                    .implClass(getReFactDefine.getFact_loader())
                    .build()
            );
            bizData = returnValue.getData();
        } else {
            // 使用上下文中的业务数据
            bizData = ruleEngineContext.getBizData();

            // 将数值类型的字符串转换为相应的数值类型
            List<ConditionDefine> conditionDefineList = getReFactDefine.getConditionDefineList();
            Map<String, Integer> conditionMap = conditionDefineList.stream()
                .collect(Collectors.toMap(ConditionDefine::getFieldCode, ConditionDefine::getFieldType));
            
            bizData.forEach((key, val) -> {
                if (val != null && Objects.equals(conditionMap.get(key), Constants_re.FACT_PROP_TYPE_NUMBER)) {
                    if (ConvertUtil.createString(val).length() >= 8) {
                        bizData.put(key, ConvertUtil.createLong(val));
                    } else {
                        bizData.put(key, ConvertUtil.createInteger(val));
                    }
                }
            });
        }
        log.info("执行规则,业务数据:{}", bizData);

        Object factInstance;
        Object actionInstance;
        try {
            // 创建事实实例
            factInstance = factType.newInstance();
            factType.setFromMap(factInstance, bizData);

            // 创建动作实例
            actionInstance = actionType.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        // 创建 KieSession
        KieSession kieSession = kieBase.newKieSession();
        kieSession.insert(factInstance);
        kieSession.insert(actionInstance);

        // 执行所有规则
        kieSession.fireAllRules();

        // 释放 KieSession
        kieSession.dispose();

        // 获取动作执行后的结果
        actionMap = actionType.getAsMap(actionInstance);
        log.info("执行规则,返回数据:{}", actionMap);
    }

    log.info("---------------执行规则结束---------------");

    ReturnValue<Map<String, Object>> returnValue = ReturnValue.newSuccessInstance();
    returnValue.setData(actionMap);
    return returnValue;
}

下面是对上述代码的详细说明:

该方法 execRule 主要用于执行规则引擎中的规则,并返回相应的结果数据。方法接受一个 RuleEngineContext 对象作为参数,并返回一个包含 Map<String, Object> 类型数据的 ReturnValue 对象。首先,记录日志以标明规则执行的开始。接着,通过 reEngineService 获取规则定义对象 GetReDefine,并初始化一个空的 actionMap。在确认规则定义对象的 drl_rule_file 字段不为空后,方法继续执行。随后,通过 reEngineService 分别获取事实定义和动作定义。接下来,使用 KieHelper 构建规则引擎,并加载 DRL 文件内容。使用 KieHelper 构建 KieBase 后,获取事实类型和动作类型,并记录日志以显示事实定义的相关信息。

如果 ruleEngineContext 中没有业务数据,则通过 reBizFacade 根据业务编号获取业务数据;否则,使用上下文中的业务数据,并将数值类型的字符串转换为相应的数值类型。接下来,创建事实实例和动作实例,并使用 KieSession 插入实例,执行所有规则后释放 KieSession。最终,将动作执行后的结果放入 actionMap,记录日志以显示返回的数据,并返回包含 actionMapReturnValue 对象。最后,记录日志以标明规则执行的结束。

ReTestPVO 如下:

/**
 * 规则测试PVO
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ReTestPVO extends ParamVO {

    /**
     * 规则标识
     */
    private String ruleCode;

    /**
     * 业务数据
     */
    private Map<String, Object> bizData;

}
4. 测试功能说明

为了验证规则文件的正确性,我们实现了一个专门的测试功能。用户可以通过界面输入相关参数,模拟真实业务场景,测试规则的执行情况。

该测试功能的使用步骤如下:

  1. 选择步骤:从下拉列表中选择需要测试的业务步骤。业务步骤可能包括不同的项目阶段,例如招标阶段或资审阶段。选择合适的步骤有助于精确模拟特定场景下的规则执行。

  2. 选择时间类型:从下拉列表中选择时间类型。时间类型决定了系统将如何解读和应用输入的时间参数,例如投标截止时间或当前时间。

  3. 输入当前时间和投标截止时间:在相应的输入框中填写当前时间和投标截止时间。这些时间参数是规则执行的关键因素,确保输入的时间准确无误。

  4. 选择采购方式:从下拉列表中选择采购方式。不同的采购方式可能对应不同的规则和条件,因此选择正确的采购方式是确保规则测试准确性的必要步骤。

  5. 点击“测试”按钮:在填写完所有必要参数后,点击“测试”按钮。系统将调用规则引擎执行预设的规则,并在“测试结果”中显示规则执行的结果。

  6. 查看测试结果:测试结果会显示在“测试结果”区域。根据结果,用户可以评估规则的执行情况,确保规则在不同业务场景下的正确性和适用性。

通过此测试功能,用户能够快速验证不同业务场景下规则的执行效果,确保规则的正确性和适用性。这不仅提高了规则配置的效率,也减少了在实际业务过程中可能出现的错误,提升了整体系统的可靠性。

总结

本文详细介绍了如何基于业务数据动态调用 DRL 规则文件,并通过实际代码示例和详细注释,阐述了整个执行过程。我们还展示了一个专门的测试功能,帮助模拟和验证规则执行的效果。通过这种方式,可以实现业务规则的灵活配置和高效执行,提升业务系统的自动化和智能化水平。

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微笑听雨。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值