Drools规则引擎在支付结算对账中的应用

什么是Drools

Drools是Jboss公司旗下一款开源的规则引擎,是一个基于java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,
以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启机器就可以立即在线上环境生效,有如下特点

1. 完整的实现了Rete算法;
2. 提供了强大的IDE Plugin开发支持;
3. 通过使用其中的DSL(Domain Specific
Language),可以实现用自然语言方式来描述业务规则,使得业务分析人员也可以看懂业务规则代码;
4. 提供了基于WEB的BRMS——Guvnor,Guvnor提供了规则管理的知识库,通过它可以实现规则的版本控制,及规则的在线修改与编译,使得开发人员和系统管理人员可以在线管理业务规则。

Drools 是业务逻辑集成平台,被分为4个项目:

  1. Drools Guvnor (BRMS/BPMS):业务规则管理系统
  2. Drools Expert (rule engine):规则引擎,drools的核心部分
  3. Drools Flow (process/workflow):工作流引擎
  4. Drools Fusion (cep/temporal reasoning):事件处理

工作原理

这里写图片描述

一个简单的使用demo,这里我们主要用到Drools Expert 部分。

业务场景:模拟清算对账
输入数据源:零花钱流水对账数据、快钱渠道对账数据,执行业务规则,输出平账数据、对账差异数据

零花钱流水对账数据实体类

AccountOrder.java

@Data
public class AccountOrder {

    private Long puidOrderId;
    private String puid;
    private String fnPuid;
    private Long orderId;
    private Byte type;
    private Date createTime;
    private Byte busiType;
    private int transType;
    private Long transAmount;
    private Long afterBalance;
    private Long origPuidOrderId;
    private Date expireTime;
    private int orderStatus ; //0 成功
    private int checkStatus;
}

快钱渠道对账数据实体类

BIll99Order.java

@Data
public class BIll99Order {

    private Long outerOrderId;
    private Long orderId;
    private Date createTime;
    private int transType;
    private Long amount;
    private int orderStatus ;
    private int checkStatus; //0 未对平  1 对平
}

1.引入drools依赖

pom.xml

<properties>
     <drools-version>6.4.0.Final</drools-version>
</properties>

<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>${drools-version}</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-internal</artifactId>
    <version>${drools-version}</version>
</dependency>

<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>${drools-version}</version>
</dependency>

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>${drools-version}</version>
</dependency>

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>${drools-version}</version>
</dependency>

2.定义业务规则,可以是.drl文件 也可以是一段符合drools规则的字符串

precheck.drl

package rules;
dialect  "java"
import com.shinyleo.drools.mode.AccountOrder;
import com.shinyleo.drools.mode.BIll99Order;

global java.util.List successCheckList;


//元数据定义
declare SuccessData
   orderId :Long
   checkresult:String
   checkStatus:int
    amount:Long
    status:int
end



rule "precheck"
    salience 100
    when
       $accountOrder : AccountOrder(checkStatus == 0 ,$transAmount:transAmount,$orderId:orderId,$status:orderStatus)
       $bill99Order : BIll99Order(checkStatus == 0,outerOrderId == $orderId,amount == $transAmount,orderStatus == $status)
    then
      System.out.println("-----start rules-----" + $accountOrder.getOrderId());
      $accountOrder.setCheckStatus(1); //标记为对平
      $bill99Order.setCheckStatus(1);
      update($accountOrder);
      update($bill99Order);
      //获取平账数据
      SuccessData successData = new SuccessData();
      successData.setAmount($transAmount);
      successData.setCheckresult("平账");
      successData.setOrderId($orderId);
      //insertLogical(successData);
      insert(successData);
      //返回
      successCheckList.add(successData);
end

rule "successData"
  when
      SuccessData(checkStatus == 1);
  then
     System.out.println("success increase 1 ..." );
end

query "successList"
   // 找出对平的数据
   successData:AccountOrder(checkStatus == 1)
end

query "errorList"
   // 找出还未对平的数据
   errorData:AccountOrder(checkStatus == 0)

end

query "queryOrder" (int $status,Long $orderId)
   // 找出还未对平的数据
   queryOrder:AccountOrder(checkStatus == $status,orderId == $orderId)
end

3.注册规则,kmodule.xml,配置如果是多个规则,则配置如下

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <!--规则1-->
    <kbase name="precheckBase" packages="rules">
        <ksession name="precheck"/>
    </kbase>

    <!--规则2-->
    <kbase name="precheckCmd" packages="rules.cmd">
        <ksession name="cmd"/>
    </kbase>

    <!--规则3-->
    <kbase name="precheckthird" packages="rules.third">
        <ksession name="precheck-third"/>
    </kbase>
</kmodule>

4.定义规则引擎调用接入层

DroolService.java

/**
 * Created by zhugh on 17/1/12.
 */
public interface DroolService {

    /**
     * 根据规则文件激活规则
     * @param ruleName
     * @return
     */
    public KieSession createKieSessionByRule(String ruleName);


    /**
     * 根据规则文件激活规则 并装载数据
     * @param ruleName
     * @return
     */
    public KieSession executeKieSessionByRule(String ruleName, DataBean dataBean, Map<String,Object> globalMap);


    /**
     * 根据规则文件激活规则 并装载数据
     * @param ruleName
     * @return
     */
    public ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String,Object> globalMap,String ... queryName);
    /**
     * 根据自定义内容创建规则
     * @param ruleContent
     * @return
     */
    public KieSession executeKieSessionByRuleContent(String ruleContent);

    /**
     * 根据自定义内容创建规则,并执行规则
     * @param ruleContent
     * @return
     */
    public KieSession executeKieSessionByRuleContent(String ruleContent,Object srcData,Object targetData,Map<String,Object> globalMap);
}

实现类

DroolServiceImpl.java

@Service("droolService")
public class DroolServiceImpl implements DroolService, InitializingBean {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    private  KieContainer kContainer;
    @Override
    public void afterPropertiesSet() throws Exception {
        KieServices ks = KieServices.Factory.get();
        this.kContainer = ks.getKieClasspathContainer();
    }


    @Override
    public KieSession createKieSessionByRule(String ruleName) {
        try{
            return kContainer.newKieSession(ruleName);
        }catch (Exception e){
            throw new RuntimeException(e);
        }

    }

    /**
     * 数据载入
     * @param ruleName
     * @param dataBean
     * @param globalMap
     * @return
     */
    @Override
    public KieSession executeKieSessionByRule(String ruleName, DataBean dataBean, Map<String, Object> globalMap) {

        KieSession kieSession = null;
        try{
            kieSession = kContainer.newKieSession(ruleName);

            List listSrc = (List) dataBean.getSrcData();
            List listTarget = (List) dataBean.getTargetData();
            for(Object srcObj : listSrc){
                kieSession.insert(srcObj);
            }
            for(Object tarObj : listTarget){
                kieSession.insert(tarObj);
            }
            for(Map.Entry<String,Object> entry : globalMap.entrySet()){
                kieSession.setGlobal(entry.getKey(),entry.getValue());
            }
            kieSession.fireAllRules();

        }catch(Exception e){
            logger.error("executeKieSessionByRule.error:{}",e);
            throw new RuntimeException(e);
        }finally {
            if(null  != kieSession){
                kieSession.dispose();
            }
        }
        return kieSession;

    }

    /**
     * 批量数据载入
     * @param ruleName
     * @param dataBean
     * @param globalMap
     * @return
     */

    @Override
    public  ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String, Object> globalMap,String ... queryName) {
        KieSession kieSession = null;
        ExecutionResults executionResults = null;
        try{
            kieSession = kContainer.newKieSession(ruleName);
            List<Command> cmdList = new ArrayList<>();
            Command srcCmd = CommandFactory.newInsertElements((Collection) dataBean.getSrcData());
            cmdList.add(srcCmd);
            Command tarCmd = CommandFactory.newInsertElements((Collection) dataBean.getTargetData());
            cmdList.add(tarCmd);
            //全局变量装载
            for(Map.Entry<String,Object> entry : globalMap.entrySet()){
                Command globalCmd = CommandFactory.newSetGlobal(entry.getKey(), entry.getValue(),true);
                cmdList.add(globalCmd);
            }
            //查询装载
            for(String query_Name : queryName){
                cmdList.add(CommandFactory.newQuery(query_Name,query_Name));
            }
            Command fireCmd = CommandFactory.newFireAllRules();
            cmdList.add(fireCmd);
            BatchExecutionCommand command = CommandFactory.newBatchExecution(cmdList);
            executionResults = kieSession.execute(command);

        }catch(Exception e){
            logger.error("executeBatchCmd.error:{}",e);
            throw new RuntimeException(e);
        }finally {
            if(null != kieSession){
                kieSession.dispose();
            }
        }
        return executionResults;
    }


/*
    @Override
    public  ExecutionResults executeBatchCmd(String ruleName, DataBean dataBean, Map<String, Object> globalMap,String ... queryName) {
        KieSession kieSession = null;
        ExecutionResults executionResults = null;
        try{
            kieSession = kContainer.newKieSession(ruleName);
            List<Command> list = new ArrayList<>();
            Command srcCmd = CommandFactory.newInsertElements((Collection) dataBean.getSrcData());
            list.add(srcCmd);
            Command tarCmd = CommandFactory.newInsertElements((Collection) dataBean.getTargetData());
            list.add(tarCmd);
            //全局变量装载
            for(Map.Entry<String,Object> entry : globalMap.entrySet()){
                Command globalCmd = CommandFactory.newSetGlobal(entry.getKey(), entry.getValue(),true);
                list.add(globalCmd);
            }
            //查询装载
            for(String query_Name : queryName){
                list.add(CommandFactory.newQuery(query_Name,query_Name));
            }
            BatchExecutionCommand command = CommandFactory.newBatchExecution(list);
            executionResults = kieSession.execute(command);

        }catch(Exception e){
            logger.error("executeBatchCmd.error:{}",e);
            throw new RuntimeException(e);
        }finally {
            if(null != kieSession){
                kieSession.dispose();
            }
        }
        return executionResults;
    }
*/

    @Override
    public KieSession executeKieSessionByRuleContent(String ruleContent) {
        StatefulKnowledgeSession ksession = null;
        try {
            KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            kBuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()), ResourceType.DRL);
            if(kBuilder.hasErrors()){
                logger.error("DroolServiceImpl.getKieSessionByRuleContent.error:{}",kBuilder.getErrors().toString());
                throw new RuntimeException( kBuilder.getErrors().toString() );
            }
            KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
            knowledgeBase.addKnowledgePackages(kBuilder.getKnowledgePackages());
            ksession = knowledgeBase.newStatefulKnowledgeSession();

        }catch (Exception e){
            logger.error("executeKieSessionByRuleContent.error:{}",e);
            throw new RuntimeException(e);
        }finally {
            if(null != ksession){
                ksession.dispose();
            }
        }
        return ksession;

    }

    @Override
    public KieSession executeKieSessionByRuleContent(String ruleContent, Object srcData, Object targetData,Map<String,Object> globalMap) {
        StatefulKnowledgeSession ksession = null;
        try {
            KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            kBuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()), ResourceType.DRL);
            if (kBuilder.hasErrors()) {
                logger.error("DroolServiceImpl.getKieSessionByRuleContent.error:{}", kBuilder.getErrors().toString());
                throw new RuntimeException(kBuilder.getErrors().toString());
            }
            KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
            knowledgeBase.addKnowledgePackages(kBuilder.getKnowledgePackages());
            ksession = knowledgeBase.newStatefulKnowledgeSession();
            ksession.insert(srcData);
            ksession.insert(targetData);
            for (Map.Entry<String, Object> entry : globalMap.entrySet()) {
                ksession.setGlobal(entry.getKey(), entry.getValue());
            }
            ksession.fireAllRules();
        }catch (Exception e){
            logger.error("executeKieSessionByRuleContent.error:{}",e);
            throw new RuntimeException(e);
        }finally {
            if(null != ksession){
                ksession.dispose();
            }
        }
        return ksession;
    }
}

5.执行业务规则

@Test
public void droolsTest(){
    List<AccountOrder> srcDataList = getSrcList();
    List<BIll99Order> targetList = getTargetList();
    Map<String ,Object> globalMap = new HashMap<>();
    globalMap.put("successCheckList",new ArrayList());

    DataBean<List<AccountOrder>,List<BIll99Order>> dataBean = new DataBean(srcDataList,targetList);
    KieSession kieSession = droolService.executeKieSessionByRule("precheck",dataBean,globalMap);

    List successlist = (List) kieSession.getGlobal("successCheckList");
    System.out.println("global successlist :" + successlist.size());
    //获取对平数据
    QueryResults querySuccessList = kieSession.getQueryResults("successList");
    System.out.println("对平数据条数:" + querySuccessList.size());
    for(QueryResultsRow qr : querySuccessList){
        AccountOrder order = (AccountOrder) qr.get("successData");
        System.out.println("对平数据明细:" + order.getOrderId());
    }

    //获取未对平数据
    QueryResults queryErrors = kieSession.getQueryResults("errorList");
    System.out.println("未对平数据条数:" + queryErrors.size());

    for(QueryResultsRow qr : queryErrors){
        AccountOrder order = (AccountOrder) qr.get("errorData");
        System.out.println("未对平数据明细:" + order.getOrderId());
    }

    //查找数据
    QueryResults successData = kieSession.getQueryResults("queryOrder", new Object[]{new Integer(1),1000112345L});
    for(QueryResultsRow qr : successData){
        AccountOrder order = (AccountOrder)qr.get("queryOrder"); //打印查询结果
        System.out.println("查找单号为1000112345的对账结果结果 :" + order.getOrderId() + ":" + order.getCheckStatus());
    }
    kieSession.dispose();

}

//构造数据源,实际数据源从对账文件中录入
   private List<AccountOrder> getSrcList(){
        List<AccountOrder> srcDataList = new ArrayList<>();
        AccountOrder accountOrder = new AccountOrder();
        accountOrder.setOrderId(123L);
        accountOrder.setTransAmount(500L);
        accountOrder.setOrderStatus(1);
        accountOrder.setCheckStatus(0);

        AccountOrder accountOrder1 = new AccountOrder();
        accountOrder1.setOrderId(456L);
        accountOrder1.setTransAmount(600L);
        accountOrder1.setOrderStatus(1);
        accountOrder1.setCheckStatus(0);

        srcDataList.add(accountOrder);
        srcDataList.add(accountOrder1);
        return srcDataList;
    }


    private  List<BIll99Order> getTargetList(){
        List<BIll99Order> targetList = new ArrayList<>();
        BIll99Order bIll99Order = new BIll99Order();
        bIll99Order.setOuterOrderId(123L);
        bIll99Order.setAmount(500L);
        bIll99Order.setOrderStatus(1);
        bIll99Order.setCheckStatus(0);

        BIll99Order bIll99Order1 = new BIll99Order();
        bIll99Order1.setOuterOrderId(456L);
        bIll99Order1.setAmount(600L);
        bIll99Order1.setOrderStatus(3);
        bIll99Order1.setCheckStatus(0);

        targetList.add(bIll99Order);
        targetList.add(bIll99Order1);
        return targetList;
    }

6.输出结果

-----start rules-----123
global successlist :1
对平数据条数:1
对平数据明细:123
未对平数据条数:1
未对平数据明细:456

7.工程结构
这里写图片描述

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值