基于业务数据动态调用 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
,记录日志以显示返回的数据,并返回包含 actionMap
的 ReturnValue
对象。最后,记录日志以标明规则执行的结束。
ReTestPVO 如下:
/**
* 规则测试PVO
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class ReTestPVO extends ParamVO {
/**
* 规则标识
*/
private String ruleCode;
/**
* 业务数据
*/
private Map<String, Object> bizData;
}
4. 测试功能说明
为了验证规则文件的正确性,我们实现了一个专门的测试功能。用户可以通过界面输入相关参数,模拟真实业务场景,测试规则的执行情况。
该测试功能的使用步骤如下:
-
选择步骤:从下拉列表中选择需要测试的业务步骤。业务步骤可能包括不同的项目阶段,例如招标阶段或资审阶段。选择合适的步骤有助于精确模拟特定场景下的规则执行。
-
选择时间类型:从下拉列表中选择时间类型。时间类型决定了系统将如何解读和应用输入的时间参数,例如投标截止时间或当前时间。
-
输入当前时间和投标截止时间:在相应的输入框中填写当前时间和投标截止时间。这些时间参数是规则执行的关键因素,确保输入的时间准确无误。
-
选择采购方式:从下拉列表中选择采购方式。不同的采购方式可能对应不同的规则和条件,因此选择正确的采购方式是确保规则测试准确性的必要步骤。
-
点击“测试”按钮:在填写完所有必要参数后,点击“测试”按钮。系统将调用规则引擎执行预设的规则,并在“测试结果”中显示规则执行的结果。
-
查看测试结果:测试结果会显示在“测试结果”区域。根据结果,用户可以评估规则的执行情况,确保规则在不同业务场景下的正确性和适用性。
通过此测试功能,用户能够快速验证不同业务场景下规则的执行效果,确保规则的正确性和适用性。这不仅提高了规则配置的效率,也减少了在实际业务过程中可能出现的错误,提升了整体系统的可靠性。
总结
本文详细介绍了如何基于业务数据动态调用 DRL 规则文件,并通过实际代码示例和详细注释,阐述了整个执行过程。我们还展示了一个专门的测试功能,帮助模拟和验证规则执行的效果。通过这种方式,可以实现业务规则的灵活配置和高效执行,提升业务系统的自动化和智能化水平。