1. 程式人生 > >【Activiti工作流】10.排他閘道器

【Activiti工作流】10.排他閘道器

很久沒有更新Activiti的文章,有朋友想看之後的技術總結,我這裡就順著上一次的記憶繼續寫,把這個系列完結了。

上一次介紹了Activiti中的其中一種模式“連線”,該模式即是一件任務可能要分多種情況,有些情況需要走一個複雜流程,有些情況需要走簡單流程,就如同一條河的分支一樣。

而本次介紹的是Activiti的另一種模式“排他閘道器(ExclusiveGateWay)”。
我們開啟Eclipse中的某個bpmn的流程圖檢視,在左側的編輯框中就可以看到有一個“GateWay”的選項,其中有“ExclusiveGateWay”和“ParallelGateWay”兩個選項,其中“ExclusiveGateWay”就是我們要講的排他閘道器:

什麼是閘道器(GateWay)?其實閘道器說白了就是事件流到某一個核心節點,該節點需要做一個判斷,如果判斷符合某一個邏輯,那麼事件就流到合適的路徑上去,進行了分支。而做判斷的節點就是所謂的閘道器。

為了讓大家更加清晰的理解排他閘道器,我們利用之前的工程做一個排他閘道器的樣例。
我們要做一個類似“費用報銷申請”的流程圖,申請人進行費用報銷申請,然後利用閘道器來區分費用的大小。要求有達成如下條件進行不同的分支操作:
(1)如果報銷金額大於500,小於等於1000,則任務流轉至部門經理審批;
(2)如果報銷金額小於等於500,預設流轉至財務處審批;
(3)如果報銷金額大於1000,則任務流轉至總經理審批;

下面我們來利用排他閘道器來實現這個例項。
首先在測試工程“”中建立一個新的包“”來放置本次測試樣例的程式碼:

然後在該包下新建一個bpmn的流程圖:

進入編輯介面後,先建立一個StartEvent節點和一個連線以及一個UserTask:

其這裡的UserTask是報銷人的申請任務。該UserTask的Properties引數如下:


然後在下面建立3個任務,分別是排他閘道器根據情況進行分支後的任務:

三個任務的Properties引數如下:







然後我們在申請任務和下面三個任務之間新增一個排他閘道器:

然後三個任務最終流向結束節點:


然後最重要的幾步來了,首先定義排他閘道器下面的三條線的Name和相關message判斷資訊:
總經理審批的流線:



部門經理審批的流線:




對於財務的線,我們沒有必要為其裝置“${message<=500}”的條件,直接將其設定為排他閘道器的預設Task(將指向該Task的線的id給排他閘道器的Defalut flow屬性),即是不滿足其它兩個條件時,預設走該Task:



到此我們的排他閘道器的流程圖就畫好了,Ctrl+S儲存流程圖,可以看到在相應的包下生成了一個png的流程圖片:


然後我們建立名為“ExclusiveGateWayTest”的測試類進行測試,先編寫一個部署流程的部署方法:
package cn.com.eclusiveGateWay;
import java.io.InputStream;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.junit.Test;

public class ExclusiveGateWayTest {//獲取流程引擎物件
	//getDefaultProcessEngine方法內部會自動讀取名為activiti.cfg.xml檔案的配置資訊
	ProcessEngine processEngine=ProcessEngines.getDefaultProcessEngine();

	/**部署流程定義*/
	@Test
	public void deploymentProcessDefinition_inputStream(){
		//獲得上傳檔案的輸入流
		InputStream inputStreamBpmn=this.getClass().getResourceAsStream("exclusiveGateWayFlow.bpmn");
		InputStream inputStreamPng=this.getClass().getResourceAsStream("exclusiveGateWayFlow.png");
		//獲取倉庫服務,從類路徑下完成部署
		RepositoryService repositoryService=processEngine.getRepositoryService();
		DeploymentBuilder deploymentBuilder=repositoryService.createDeployment();//建立一個部署物件
		deploymentBuilder.name("排他閘道器");//新增部署的名稱
		deploymentBuilder.addInputStream("exclusiveGateWayFlow.bpmn", inputStreamBpmn);
		deploymentBuilder.addInputStream("exclusiveGateWayFlow.png", inputStreamPng);
		Deployment deployment=deploymentBuilder.deploy();//完成部署
		
		//列印我們的流程資訊
		System.out.println("部署Id:"+deployment.getId());
		System.out.println("部署名稱Name:"+deployment.getName());
	}
}
執行該部署方法,可以看到控制檯輸出了部署資訊:


然後在資料庫中,可以看到act_re_deployment部署物件表、act_re_procdef流程定義表以及act_ge_bytearray資原始檔表中都生成了該次部署的流程定義資訊:




然後在ExclusiveGateWayTest中編寫啟動例項方法:
/**啟動流程引擎*/
@Test
public void startProcessInstance(){
	//獲取流程啟動Service
	RuntimeService runtimeService=processEngine.getRuntimeService();
	//使用流程定義的key,key對應bpmn檔案對應的id,
	//(也是act_re_procdef表中對應的KEY_欄位),預設是按照最新版本啟動
	String processDefinitionkey="myProcess";//流程定義的key就是myProcess
	//獲取流程例項物件
	ProcessInstance processInstance=runtimeService.startProcessInstanceByKey(processDefinitionkey);
	System.out.println("流程例項ID:"+processInstance.getId());//流程例項ID
	System.out.println("流程定義ID:"+processInstance.getProcessDefinitionId());//流程定義ID
}

執行測試方法,啟動該流程:

在資料庫中的act_run_task表中有一個正在執行的任務:


那麼我們編寫查詢“姜曉巨集”流程任務的方法,檢視姜曉巨集當前的待辦任務:
/**查詢當前的個人任務(實際就是查詢act_ru_task表)*/
@Test
public void findMyPersonalTask(){
	String assignee="姜曉巨集";
	//獲取事務Service
	TaskService taskService=processEngine.getTaskService();
	List<Task> taskList=taskService.createTaskQuery()//建立任務查詢物件
			   .taskAssignee(assignee)//指定個人任務查詢,指定辦理人
			   .list();//獲取該辦理人下的事務列表
	
	if(taskList!=null&&taskList.size()>0){
		for(Task task:taskList){
			System.out.println("任務ID:"+task.getId());
			System.out.println("任務名稱:"+task.getName());
			System.out.println("任務的建立時間:"+task.getCreateTime());
			System.out.println("任務辦理人:"+task.getAssignee());
			System.out.println("流程例項ID:"+task.getProcessInstanceId());
			System.out.println("執行物件ID:"+task.getExecutionId());
			System.out.println("流程定義ID:"+task.getProcessDefinitionId());
			System.out.println("#############################################");
		}
	}
}
查詢結果:


可以看到他的任務ID為2304,下面編寫完成任務的測試方法,定義流程變數的名稱為message,值為450:
/**完成我的任務*/
@Test
public void completeMyPersonalTask(){
	String taskId="2304";//上一次我們查詢的任務ID
	//完成任務的同時,設定流程變數,使用流程變數用來制定完成任務後,下一個連線,
	//對應exclusiveGateWayFlow.bpmn檔案中${message==450}
	Map<String,Object> variables=new HashMap<String,Object>();
	variables.put("message", 450);
	TaskService taskService=processEngine.getTaskService();
	taskService.complete(taskId,variables);//完成taskId對應的任務,並附帶流程變數
	System.out.println("完成ID為"+taskId+"的任務");
}
完成結果:


按照流程圖來說,節點應該流向預設的“財務”處,即是“張麗”來辦理該任務,所以我們查詢張麗的代辦任務:


然後執行張麗的任務:

最後任務完成:

歷史的任務節點:

流程中出現的流程變數:


然後重新啟動一個流程,將message設定為700:



此時流程流向了部門經理:

執行2704任務後流程結束:


最後再重啟一個流程,將message設定為1300:



此時流程流向了總經理:

執行3004任務後流程結束:

總結:
1)一個排他閘道器對應一個以上的順序流。

2)由排他閘道器流出的順序流都有一個conditionExpression元素,在內部維護返回boolean型別的決策結果。

3)決策閘道器只會返回一條結果。當流程執行到排他閘道器時,流程引擎會自動檢索網關出口,從上到下檢索,如果發現第一條決策結果為true或者沒有設定條件的(預設成立),則流出。

4)如果沒有任何一個出口符合條件,則丟擲異常。

5)使用流程變數,設定連線的條件,並按照連線的條件執行工作流,如果沒有符合的條件,則執行預設的連線。