JBPM+Drools兑现订单流程管理-初探
背景介绍:
电信BOSS系统其中很重要的信息就是订单,所有的业务受理,投诉,建议,咨询,报障信息都是依托订单来实现的,订单贯穿于整个业务的执行流程。订单生命周期描述如下:
1. 通过受理中心生成订单信息。
2. 对订单进行收费。
3. 营业厅班长对订单进行审核。(包括人工审核和自动审核)。审核校验订单的信息,后台的审核包括对订单的流向进行初步分流,比如有些订单是不需要进入后台施工流程的,此时审核就将此单流程定向到待竣工,如果要走后台流程则将订单放入分解队列进行分解。
4. 订单进行分解,将需要发送后台施工的订单进行分解,通过分解来细化订单这里分解遵循一定的分解规则,比如每个产品单分解成一个服务订单由于发送后台施工。通过分解队列获取需要分解的订单,并根据特定规则进行分解。
5. 订单发送,将分解完的订单通过订单发送将订单发送到后台施工系统(在电信里负责后台施工调度的是 服务开通系统),这里的发送需要将分解后的订单,将订单相关的产品信息,产品属性信息,产品地址信息,产品资源信息…..等信息组织成xml信息发送到后台。
6. 后台施工完毕返单,当后台施工完毕后 服务开通系统通过返单操作,通知前台受理系统订单后台施工完毕,综合受理系统将订单迁移到待竣工状态。
7. 订单竣工,当订单下的所有明细都到待竣工状态时通过竣工将订单包含的信息持久化到统一客户资料系统,统一客户资料系统并将数据同步到计费系统。这样前台的数据就都处理完成了,剩下的就是计费去算费用了。
从流程化和规则化可配置化的角度考虑,订单的整个生命周期可以理解为一个工作流程,包含了以下流程节点:
订单受理、收费、订单审核、订单分解、订单发送、返单、订单竣工
* 流程定义:
<?xml version="1.0" encoding="UTF-8"?> <process name="OrderManage" xmlns="http://jbpm.org/4.4/jpdl"> <start g="128,-6,48,48" name="start1"> <transition g="-47,-4" name="to Caculate deciFee" to="DeciFee" /> </start> <java class="com.lht.OrderManagement.FeeManagement" g="264,126,92,52" method="caculateFee" name="CaculateFee" var="retMsg"> <arg> <object expr="#{orderId}" /> </arg> <transition g="-53,-19" name="to OrderAudit" to="OrderAudit" /> </java> <java class="com.lht.OrderManagement.OrderAuditRule" g="142,136,92,52" method="audit" name="OrderAudit"> <arg> <object expr="#{orderId}" /> </arg> <transition g="-85,-6" name="to OrderDispatch" to="OrderDispatch" /> </java> <java class="com.lht.OrderManagement.OrderDispathRule" g="125,217,92,52" method="dispatch" name="OrderDispatch"> <arg> <object expr="#{orderId}" /> </arg> <transition g="-70,-7" name="to OrderSend" to="OrderSend" /> </java> <java class="com.lht.OrderManagement.OrderSendRule" g="147,294,92,52" method="send" name="OrderSend"> <arg> <object expr="#{orderId}" /> </arg> <transition g="-82,-18" name="to OrderArchive" to="OrderArchive" /> </java> <java class="com.lht.OrderManagement.OrderArchiveRule" g="150,369,92,52" method="achive" name="OrderArchive"> <arg> <object expr="#{orderId}" /> </arg> <transition g="-42,-11" name="to OrderEnd" to="OrderEnd" /> </java> <end g="170,458,48,48" name="OrderEnd" /> <end-cancel g="34,15,48,48" name="Cancel" /> <decision name="DeciFee" g="309,56,48,48"> <handler class="com.lht.dao.decisionHandler.OrderFeeDecisionHandler" /> <transition name="to errorDeal" to="ErrorDeal" g="-54,-18"> </transition> <transition name="to CaculateFee" to="CaculateFee" g="-78,-18"></transition> </decision> <java class="com.lht.OrderManagement.ErrorDealRule" g="35,93,92,52" method="errorDeal" name="ErrorDeal"> <transition g="-44,-5" name="to Cancel" to="Cancel" /> </java> </process>
当订单状态不为10(收费环节)时订单直接终止,不走自动流程并返回费用未收取。
* 流程节点执行操作实现
收费环节:
package com.lht.OrderManagement; import com.lht.dao.CustOrderDAO; import com.lht.util.LogUtil; import com.lht.vo.Order; public class FeeManagement { public FeeManagement() { } public String caculateFee(String orderId) { Order order = new CustOrderDAO().queryOrderIngfo(orderId); String ret = "20"; LogUtil.debug("开始计算费用信息 订单编号是:" + order.getOrderId() + ";" + order.getOrderState()); OrderManagementRule rule = new OrderManagementRule(); rule.validateOrderExists("OrderManage.drl", order); return ret; } }
收费环节调用 订单管理规则,执行Drools定义的订单收费规则。
* Drools 实现收费业务规则
** drools 规则定义
当订单编号不为空,同时订单状态为收费环节时则执行收费操作
package com.sample import function com.lht.util.LogUtil.debug; import com.lht.vo.Order; import com.lht.dao.CustOrderDAO; rule "isFeeState" no-loop true activation-group "feeState" when $order:Order(orderId!=null&&orderId!=""&&orderState=="10"); then debug("订单处于收费岗"); CustOrderDAO dao=new CustOrderDAO(); boolean flag=dao.isOrderExists($order.getOrderId()); debug("收费完毕订单状态转移到审核岗"); $order.setMsg("收费成功"); $order.setOrderState("20"); update($order); end rule "isNotFeeState" no-loop true enabled false activation-group "feeState" when $order:Order(orderId!=null&&orderId!=""&&orderState!="10"); then debug("订单不在收费岗,终止流程"); $order.setMsg("订单未收费"); update($order); end
isFeeState 规则执行了以上判断
boolean flag=dao.isOrderExists($order.getOrderId()); 实现了收费操作。
* 订单管理规则类调用drools规则,执行收费操作
package com.lht.OrderManagement; import org.drools.KnowledgeBase; import org.drools.runtime.StatefulKnowledgeSession; import com.lht.util.DroolsUtil; import com.lht.util.LogUtil; import com.lht.vo.Order; public class OrderManagementRule { public OrderManagementRule() { } public String validateOrderExists(String drlFile, Order order) { KnowledgeBase kbase = DroolsUtil.readKnowledgeBase(drlFile); StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); ksession.insert(order); ksession.fireAllRules(); LogUtil.debug("Next OrderState is " + order.getOrderState()); LogUtil.debug("流程处理结果:" + order.getMsg()); return order.getMsg(); } }
package com.lht.util; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderError; import org.drools.builder.KnowledgeBuilderErrors; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; public class DroolsUtil { public static KnowledgeBase readKnowledgeBase(String drlFile) { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource(drlFile), ResourceType.DRL); KnowledgeBuilderErrors errors = kbuilder.getErrors(); if (errors.size() > 0) { for (KnowledgeBuilderError error : errors) { System.err.println(error); } throw new IllegalArgumentException("Could not parse knowledge."); } KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages(kbuilder.getKnowledgePackages()); return kbase; } }
droolsUtil 实现规则文件的执行,创建知识库
* 启动业务流程引擎
package com.lht.test; import java.util.HashMap; import java.util.Map; import org.jbpm.api.Configuration; import org.jbpm.api.ProcessEngine; import org.jbpm.api.ProcessInstance; import com.lht.dao.CustOrderDAO; import com.lht.util.LogUtil; import com.lht.vo.Order; public class ProcessEngineLearn { public static String deployProcess(ProcessEngine pe, String jpdlPath) { return pe.getRepositoryService().createDeployment() .addResourceFromClasspath(jpdlPath).deploy(); } public static void deleteProcessInst(ProcessEngine pe, String processInstId) { pe.getRepositoryService().deleteDeployment(processInstId); } public static ProcessInstance excuteProcess(ProcessEngine pe, String key, Map<String, Object> variables) { return pe.getExecutionService().startProcessInstanceByKey(key, variables); } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { ProcessEngine pe = new Configuration().buildProcessEngine(); deployProcess(pe, "com\\lht\\OrderManagement\\OrderManage.jpdl.xml"); Map<String, Object> variables = new HashMap<String, Object>(); String orderId="1016921081"; Order order = new CustOrderDAO().queryOrderIngfo(orderId); variables.put("orderId", order.getOrderId());// 传入流程的参数 variables.put("orderState", order.getOrderState());// 传入流程的参数 } }
* 执行结果
2010-08-04 17:39:13 开始计算费用信息 订单编号是:1016921081;10 2010-08-04 17:39:18 订单处于收费岗 2010-08-04 17:39:19 收费结果是:true 2010-08-04 17:39:19 收费完毕订单状态转移到审核岗 2010-08-04 17:39:19 Next OrderState is 20 2010-08-04 17:39:19 流程处理结果收费成功 2010-08-04 17:39:20 开始订单审核1016921081 2010-08-04 17:39:21 开始订单分解1016921081 2010-08-04 17:39:22 开始订单发送1016921081 2010-08-04 17:39:23 订单开始竣工1016921081