瘋狂講義Activiti6.X工作流進階與項目實戰,Activiti整合Drools

Activiti與Drools整合

使用Activiti中的業務規則任務(Business Rule Task)可以執行一個或者多個業務規則,當前Activiti只支持Drools。根據流程任務章節可知,每個流程活動都會有自己的行爲,那麼Activiti在實例業務規則任務行爲的時候,只需要使用Drools的API,就可以實現規則文件的加載、事實實例的插入和規則觸發等操作,任務的定義者只需要提供參數、規則和計算結果等信息,就可以在Activiti中調用規則。


業務規則任務詳解

在調用規則前,需要告訴規則引擎加載哪些規則文件,而對於Activiti來說,這些文件都會被看作資源(數據被保存在ACT_GE_BYTEARRAY表中),因此在部署流程資源文件時,就需要提供這些規則文件。當執行流到達業務規則任務時,就會執行業務規則任務的行爲,Activiti中對應的行爲實現類是Busine***uleTaskActivityBehavior,那麼根據本章前面幾節中Drools的API可以知道,這個類的實現應該是創建(獲取緩存中的)KnowledgeBase實例,然後創建一個StatefulKnowledgeSession實例,插入事實實例,最後調用fireAllRules方法觸發規則。Busine***uleTaskActivityBehavior的實現大致如代碼清單14-26所示。


代碼清單14-26:


//創建一個KnowledgeBuilder


KnowledgeBuilder kbuilder = KnowledgeBuilderFactory


.newKnowledgeBuilder();


//添加規則資源到KnowledgeBuilder


kbuilder.add(ResourceFactory.newClassPathResource("rule/MyDrools.drl",


FirstTest.class), ResourceType.DRL);


if (kbuilder.hasErrors()) {


System.out.println(kbuilder.getErrors().toString());


System.exit(0);


}


//獲取知識包集合


Collection<KnowledgePackage> pkgs = kbuilder


.getKnowledgePackages();


//創建KnowledgeBase實例


KnowledgeBase kbase =kbuilder.newKnowledgeBase();①


//將知識包部署到KnowledgeBase中


kbase.addKnowledgePackages(pkgs);


//使用KnowledgeBase創建StatefulKnowledgeSession


StatefulKnowledgeSession ksession = kbase


.newStatefulKnowledgeSession();


//創建事實


Person p1 = newPerson("person 1", 11);


//插入到Working Memory


ksession.insert(p1);


//匹配規則


ksession.fireAllRules();


//關閉當前session的資源


ksession.dispose();


從代碼清單14-26的①開始,將會是Busine***uleTaskActivityBehavior所做的工作,Activiti的實現與代碼清單14-26存在差異,KnowledgeBase實例的創建將由Activiti的其他類完成,包括KnowledgeBuilder的創建、編譯信息輸出等工作,Busine***uleTaskActivityBehavior的實現中,得到KnowledgeBase後,會創建一個StatefulKnowledgeSession,然後根據任務節點的配置,解析爲事實實例,調用StatefulKnowledgeSession的insert方法插入到Working Memory中,最後會觸發全部的規則並關閉資源。需要注意的是,觸發規則時,會讀取任務所配置的規則來添加一個規則攔截器,調用StatefulKnowledgeSession的fireAllRules(AgendaFilter filte)方法來觸發規則,如果在任務中沒有配置使用(或者不使用)的規則,那麼將調用無參數的fireAllRules方法。在接下來的兩個小節,將以一個銷售流程爲基礎,在Activiti中調用規則。


制定銷售單優惠規則

假設當前有一個銷售流程,銷售人員在錄入銷售商品後,系統需要對錄入的商品進行規則處理,例如在單筆消費100元以上打九折、200元以上打八折等優惠策略,都可以在規則文件中定義,然後通過業務規則任務的調用,最後通過一個ServiceTask來輸出計算後的結果。在設定銷售流程前,可以先設計相應的銷售對象。代碼清單14-27爲一個銷售單對象和一個銷售單明細對象。


代碼清單14-27:


codes\14\14.7\drools-sale\src\org\crazyit\activiti\Sale.java,


codes\14\14.7\drools-sale\src\org\crazyit\activiti\SaleItem.java


// 銷售單對象


public class Saleimplements Serializable {


 


//銷售單號


private String saleCode;


//銷售日期


private Date date;


//銷售明細


private List<SaleItem> items;


//折扣


private BigDecimaldiscount = new BigDecimal(1);



public Sale(String saleCode, Date date) {


super();


this.saleCode = saleCode;


this.date = date;


this.items = new ArrayList<SaleItem>();


}


//返回日期爲星期幾


public int getDayOfWeek() {


Calendar c = Calendar.getInstance();


c.setTime(this.date);


int dow = c.get(Calendar.DAY_OF_WEEK);


return dow;


}


//返回該銷售單的總金額(優惠前)


public BigDecimal getTotal() {


BigDecimal total = new BigDecimal(0);


for (SaleItem item : this.items) {


BigDecimal itemTotal = item.getPrice().multiply(item.getAmount());


total = total.add(itemTotal);


}


total = total.setScale(2, BigDecimal.ROUND_HALF_UP);


return total;


}



//返回優惠後的總金額


public BigDecimal getDiscountTotal() {


BigDecimal total = getTotal();


total = total.multiply(this.discount).setScale(2, BigDecimal.ROUND_HALF_UP);


return total;


}


public void setDiscount(BigDecimal dicsount) {


this.discount = dicsount.setScale(2, BigDecimal.ROUND_HALF_UP);


}



public BigDecimal getDiscount() {


return this.discount;


}


...省略setter和getter方法


}


// 銷售明細


public class SaleItemimplements Serializable {


 


//商品名稱


private String goodsName;



//商品單價


private BigDecimal price;



//數量


private BigDecimal amount;


 


public SaleItem(String goodsName, BigDecimal price, BigDecimal amount) {


super();


this.goodsName = goodsName;


this.price = price;


this.amount = amount;


}


...省略setter和getter方法


}


代碼清單14-27中的Sale對象,表示在銷售過程中產生的一筆交易,一張銷售單中有多個銷售明細,每個明細表示所銷售的商品信息,包括商品名稱、單價和數量。在代碼清單14-27中,Sale對象提供了getDayOfWeek和getTotal方法,用於返回銷售單日期是星期幾和銷售單總金額,這兩個方法將會被規則的條件所調用,判斷是否符合規則觸發的條件,Sale對象中的getDiscountTotal方法,用於返回優惠後銷售單的總金額,這個方法將會用於顯示結果值。銷售單中有一個discount的屬性,用來標識銷售單的打折情況。


編寫規則文件

設計完事實對象後,就可以制定各種銷售規則,只需要按照具體的業務和Drools的語法來制定規則。假設需要滿足以下的銷售規則:每週六和週日,全部商品打九折;消費滿100打八折,滿200打七折。根據該業務,設定的Drools規則如代碼清單14-28所示。


代碼清單14-28:codes\14\14.7\drools-sale\resource\rule\Sale.drl


package org.crazyit.activiti;


 


import java.util.*;


import java.math.*;


 


// 週六週日打九折


rule "Sat. and Sun. 90%"


no-loop true


lock-on-active true


salience1


when


$s : Sale(getDayOfWeek() == 1 || getDayOfWeek() == 7)


then


$s.setDiscount(new BigDecimal(0.9));


update($s);


end


 


// 100元打八折


rule "100 80%"


no-loop true


lock-on-active true


salience  2


when


$s : Sale(getTotal() >= 100)


then


$s.setDiscount(new BigDecimal(0.8));


update($s);


end


 


// 200元打七折


rule "200 70%"


no-loop true


lock-on-active true


salience3


when


$s : Sale(getTotal() >= 200)


then


$s.setDiscount(new BigDecimal(0.7));


update($s);


end


代碼清單14-28中定義了三個規則,這三個規則都設置了no-loop和lock-on-active屬性爲true,表示一個規則被觸發後,其他規則(包括自身)將不會被再次觸發,三個規則中均設置了規則的優先級,200元打七折的優先級最高,週六週日打九折的規則優先級最低,如果一筆銷售發生在週六日,同時也滿200元的話,這時只會觸發“200元打七折”的業務規則。代碼清單中的三個規則,符合條件後,均會調用Sale的setDiscount方法設置銷售單的折扣屬性。


實現銷售流程

制定了銷售規則後,就可以在Activiti中設計銷售流程,本例的銷售流程較爲簡單,在銷售員錄入銷售數據後(使用User Task),將數據交給業務規則任務(Business Rule Task)進行處理,最後使用一個簡單的Service Task進行輸出,流程結束,當然,在實際應用的過程中,會有更復雜的後續流程,但並不是本例的重點。本例設計的銷售流程如圖14-3所示,對應的流程文件內容爲代碼清單14-28。




圖14-3銷售流程


代碼清單14-30:codes\14\14.7\drools-sale\resource\bpmn\SaleRule.bpmn


<process id="process1" name="process1">


<startEvent id="startevent1" name="Start"></startEvent>


<busine***uleTask id="busine***uletask1" name="進行優惠策略應用"


activiti:ruleVariablesInput="${sale1}, ${sale2}, ${sale3}, ${sale4}"


activiti:resultVariable="saleResults"></busine***uleTask>


…省略其他元素


</process>


代碼清單14-30的粗體字代碼,使用了busine***uleTask,該任務中會以四個流程參數(sale1到sale4)作爲規則事實,交給規則引擎進行處理,最終返回結果的名稱爲“saleResults”,結果類型是一個集合。本例中的四個Sale流程參數,爲代碼清單14-25中的Sale對象,需要匹配的規則爲代碼清單14-26的規則(週六日打九折、100元以上打八折、200元以上打七折)。爲了讓流程引擎能加載規則文件(drl),需要在資源部署時將規則文件一併部署到流程引擎中,流程的部署以及運行,如代碼清單14-31所示。


代碼清單14-31:codes\14\14.7\drools-sale\src\org\crazyit\activiti\SaleProcess.java


public static void main(String[] args) {


//創建流程引擎


ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();


//得到流程存儲服務組件


RepositoryService repositoryService = engine.getRepositoryService();


//得到運行時服務組件


RuntimeService runtimeService = engine.getRuntimeService();


//得到任務服務組件


TaskService taskService = engine.getTaskService();


//部署流程文件


repositoryService.createDeployment()


.addClasspathResource("rule/Sale.drl")


.addClasspathResource("bpmn/SaleRule.bpmn").deploy();


ProcessInstance pi = runtimeService


.startProcessInstanceByKey("process1");


//創建事實實例,符合週六日打九折條件


Sale s1 = new Sale("001", createDate("2017-07-01"));①


SaleItem s1Item1 = new SaleItem("礦泉水", new BigDecimal(5),


new BigDecimal(4));


s1.addItem(s1Item1);


//滿100打八折


Sale s2 = new Sale("002", createDate("2017-07-03"));②


SaleItem s2Item1 = new SaleItem("爆米花", new BigDecimal(20),


new BigDecimal(5));


s2.addItem(s2Item1);


//滿200打七折


Sale s3 = new Sale("003", createDate("2017-07-03"));③


SaleItem s3Item1 = new SaleItem("可樂一箱", new BigDecimal(70), new BigDecimal(3));


s3.addItem(s3Item1);


//星期天滿200


Sale s4 = new Sale("004", createDate("2017-07-02"));④


SaleItem s4Item1 = new SaleItem("爆米花一箱", new BigDecimal(80), new BigDecimal(3));


s4.addItem(s4Item1);


Map<String, Object> vars = new HashMap<String, Object>();


vars.put("sale1", s1);


vars.put("sale2", s2);


vars.put("sale3", s3);


vars.put("sale4", s4);


//查找任務


Task task = taskService.createTaskQuery().processInstanceId(pi.getId())


.singleResult();


taskService.complete(task.getId(), vars);


}


 


static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");


 


//根據字符串創建日期對象


static Date createDate(String date) {


try {


return sdf.parse(date);


} catch (Exception e) {


throw new RuntimeException("parse date error: " + e.getMessage());


}


}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章