一、Exclusive Gateway
Exclusive Gateway(也稱為XOR網關或更多技術基於數據的排他網關)經常用做決定流程的流轉方向。當流程到達該網關的時候,所有的流出序列流到按照已定義好的順序依次執行。當序列流條件的求值結果為true(或沒有條件集的時候,在概念上有定義一個“true”定義序列流),就會選擇該序列繼續的處理。Exclusive Gateway的圖標就是菱形里面有一個X符合,如下所示:
XML的代碼為:
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" /> <sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1"> <conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2"> <conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3"> <conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression> </sequenceFlow>
二、示例
本例子以請假流程為例,流程設計圖如下圖所示:
當員工提出的請假時間大於2天的時候由總經理審批,當請假時間不大於2天的時候由經理審批。
1、leave.bpmn內容如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> 3 <process id="leave" name="請假流程圖" isExecutable="true"> 4 <documentation>請假流程圖</documentation> 5 <startEvent id="startevent1" name="Start"></startEvent> 6 <endEvent id="endevent1" name="End"></endEvent> 7 <!-- ${employeeId} 中的employeeId 在啟動流程實例的時候,就設置employeeId變量。再根據employeeId獲取員工請假任務,最后設置請假時間進行提交--> 8 <userTask id="usertask1" name="員工請假" activiti:assignee="${employeeId}"></userTask> 9 <userTask id="usertask2" name="經理審批" activiti:candidateGroups="manager"></userTask> 10 <userTask id="usertask3" name="總經理審批" activiti:candidateGroups="boss"></userTask> 11 <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> 12 <sequenceFlow id="flow2" name="${flag == true}" sourceRef="usertask2" targetRef="endevent1"> 13 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == true}]]></conditionExpression> 14 </sequenceFlow> 15 <sequenceFlow id="flow3" name="${flag == true}" sourceRef="usertask3" targetRef="endevent1"> 16 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == true}]]></conditionExpression> 17 </sequenceFlow> 18 <sequenceFlow id="flow4" name="${flag == false}" sourceRef="usertask2" targetRef="usertask1"> 19 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == false}]]></conditionExpression> 20 </sequenceFlow> 21 <sequenceFlow id="flow5" name="${flag == false}" sourceRef="usertask3" targetRef="usertask1"> 22 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${flag == false}]]></conditionExpression> 23 </sequenceFlow> 24 <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway> 25 <sequenceFlow id="flow6" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow> 26 <sequenceFlow id="flow7" name="${day <= 2}" sourceRef="exclusivegateway1" targetRef="usertask2"> 27 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day <= 2}]]></conditionExpression> 28 </sequenceFlow> 29 <sequenceFlow id="flow8" name="${day > 2}" sourceRef="exclusivegateway1" targetRef="usertask3"> 30 <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day > 2}]]></conditionExpression> 31 </sequenceFlow> 32 </process> 33 <bpmndi:BPMNDiagram id="BPMNDiagram_leave"> 34 <bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave"> 35 <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> 36 <omgdc:Bounds height="35.0" width="35.0" x="355.0" y="48.0"></omgdc:Bounds> 37 </bpmndi:BPMNShape> 38 <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> 39 <omgdc:Bounds height="35.0" width="35.0" x="355.0" y="362.0"></omgdc:Bounds> 40 </bpmndi:BPMNShape> 41 <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> 42 <omgdc:Bounds height="55.0" width="105.0" x="320.0" y="112.0"></omgdc:Bounds> 43 </bpmndi:BPMNShape> 44 <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> 45 <omgdc:Bounds height="55.0" width="105.0" x="170.0" y="240.0"></omgdc:Bounds> 46 </bpmndi:BPMNShape> 47 <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> 48 <omgdc:Bounds height="55.0" width="105.0" x="480.0" y="242.0"></omgdc:Bounds> 49 </bpmndi:BPMNShape> 50 <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1"> 51 <omgdc:Bounds height="40.0" width="40.0" x="352.0" y="247.0"></omgdc:Bounds> 52 </bpmndi:BPMNShape> 53 <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> 54 <omgdi:waypoint x="372.0" y="83.0"></omgdi:waypoint> 55 <omgdi:waypoint x="372.0" y="112.0"></omgdi:waypoint> 56 </bpmndi:BPMNEdge> 57 <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> 58 <omgdi:waypoint x="222.0" y="295.0"></omgdi:waypoint> 59 <omgdi:waypoint x="222.0" y="379.0"></omgdi:waypoint> 60 <omgdi:waypoint x="355.0" y="379.0"></omgdi:waypoint> 61 <bpmndi:BPMNLabel> 62 <omgdc:Bounds height="14.0" width="100.0" x="183.0" y="309.0"></omgdc:Bounds> 63 </bpmndi:BPMNLabel> 64 </bpmndi:BPMNEdge> 65 <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> 66 <omgdi:waypoint x="532.0" y="297.0"></omgdi:waypoint> 67 <omgdi:waypoint x="532.0" y="379.0"></omgdi:waypoint> 68 <omgdi:waypoint x="390.0" y="379.0"></omgdi:waypoint> 69 <bpmndi:BPMNLabel> 70 <omgdc:Bounds height="14.0" width="100.0" x="483.0" y="309.0"></omgdc:Bounds> 71 </bpmndi:BPMNLabel> 72 </bpmndi:BPMNEdge> 73 <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> 74 <omgdi:waypoint x="222.0" y="240.0"></omgdi:waypoint> 75 <omgdi:waypoint x="222.0" y="139.0"></omgdi:waypoint> 76 <omgdi:waypoint x="320.0" y="139.0"></omgdi:waypoint> 77 <bpmndi:BPMNLabel> 78 <omgdc:Bounds height="14.0" width="100.0" x="170.0" y="191.0"></omgdc:Bounds> 79 </bpmndi:BPMNLabel> 80 </bpmndi:BPMNEdge> 81 <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"> 82 <omgdi:waypoint x="532.0" y="242.0"></omgdi:waypoint> 83 <omgdi:waypoint x="532.0" y="139.0"></omgdi:waypoint> 84 <omgdi:waypoint x="425.0" y="139.0"></omgdi:waypoint> 85 <bpmndi:BPMNLabel> 86 <omgdc:Bounds height="14.0" width="100.0" x="485.0" y="191.0"></omgdc:Bounds> 87 </bpmndi:BPMNLabel> 88 </bpmndi:BPMNEdge> 89 <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6"> 90 <omgdi:waypoint x="372.0" y="167.0"></omgdi:waypoint> 91 <omgdi:waypoint x="372.0" y="247.0"></omgdi:waypoint> 92 </bpmndi:BPMNEdge> 93 <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"> 94 <omgdi:waypoint x="352.0" y="267.0"></omgdi:waypoint> 95 <omgdi:waypoint x="275.0" y="267.0"></omgdi:waypoint> 96 <bpmndi:BPMNLabel> 97 <omgdc:Bounds height="14.0" width="100.0" x="283.0" y="247.0"></omgdc:Bounds> 98 </bpmndi:BPMNLabel> 99 </bpmndi:BPMNEdge> 100 <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8"> 101 <omgdi:waypoint x="392.0" y="267.0"></omgdi:waypoint> 102 <omgdi:waypoint x="480.0" y="269.0"></omgdi:waypoint> 103 <bpmndi:BPMNLabel> 104 <omgdc:Bounds height="14.0" width="100.0" x="400.0" y="247.0"></omgdc:Bounds> 105 </bpmndi:BPMNLabel> 106 </bpmndi:BPMNEdge> 107 </bpmndi:BPMNPlane> 108 </bpmndi:BPMNDiagram> 109 </definitions>
2、測試類LeaveTest代碼如下:
1 package com.shh.test; 2 3 import java.util.HashMap; 4 import java.util.List; 5 import java.util.Map; 6 7 import org.activiti.engine.HistoryService; 8 import org.activiti.engine.ProcessEngine; 9 import org.activiti.engine.ProcessEngineConfiguration; 10 import org.activiti.engine.RepositoryService; 11 import org.activiti.engine.RuntimeService; 12 import org.activiti.engine.TaskService; 13 import org.activiti.engine.history.HistoricProcessInstanceQuery; 14 import org.activiti.engine.task.Task; 15 import org.junit.Before; 16 import org.junit.Test; 17 /** 18 * 請假流程 19 */ 20 public class LeaveTest { 21 ProcessEngine processEngine = null; 22 RepositoryService repositoryService = null; 23 RuntimeService runtimeService = null; 24 TaskService taskService = null; 25 26 /** 27 * 加載配置文件 28 */ 29 @Before 30 public void init() { 31 processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml") 32 .buildProcessEngine(); 33 repositoryService = processEngine.getRepositoryService(); 34 runtimeService = processEngine.getRuntimeService(); 35 taskService = processEngine.getTaskService(); 36 } 37 38 /** 39 * 部署流程定義 40 */ 41 @Test 42 public void deploy(){ 43 repositoryService.createDeployment().addClasspathResource("diagrams/leave.bpmn").deploy(); 44 System.out.println("***************部署流程定義完成***************"); 45 } 46 47 /** 48 * 啟動一個請假流程 49 */ 50 @Test 51 public void start() { 52 Map<String, Object> variables = new HashMap<String, Object>(); 53 variables.put("employeeId", "Kermit"); //請假人 54 String processId = runtimeService.startProcessInstanceByKey("leave", variables).getId(); 55 System.out.println("***************啟動一個請假流程完成***************" + processId); 56 } 57 58 /** 59 * 提交請假申請 60 */ 61 @Test 62 public void apply(){ 63 System.out.println("***************提交請假申請開始***************"); 64 List<Task> tasks = taskService.createTaskQuery().taskAssignee("Kermit").list(); 65 System.out.println(tasks.size()); 66 for (Task task : tasks) { 67 System.out.println("Kermit的任務taskname:" + task.getName() + ",id:" + task.getId()); 68 taskService.setVariable(task.getId(), "day", 4);//設置請假天數 69 taskService.complete(task.getId());//完成任務 70 System.out.println("請假4 天"); 71 } 72 System.out.println("***************提交請假申請完成***************"); 73 } 74 75 @Test 76 public void step2manager() { 77 System.out.println("***************經理組審批流程開始***************"); 78 List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("manager").list();// 經理組的任務 79 System.out.println("經理組的任務:" + tasks.size()); 80 for (Task task : tasks) { 81 System.out.println("經理組的任務taskname:" + task.getName() + ",id:" + task.getId()); 82 taskService.claim(task.getId(), "李四");//申領任務 83 taskService.setVariable(task.getId(), "flag", false);//true批准,false不批准 84 Object applyUser = taskService.getVariable(task.getId(), "employeeId"); 85 Object day = taskService.getVariable(task.getId(), "day"); 86 System.out.println(String.format("%s請假%s天", applyUser, day)); 87 taskService.complete(task.getId());//完成任務 88 } 89 System.out.println("***************經理組審批流程結束***************"); 90 } 91 92 @Test 93 public void step2Boss() { 94 System.out.println("***************總經理組審批流程開始***************"); 95 List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("boss").list();// 總經理組的任務 96 System.out.println("總經理組的任務:" + tasks.size()); 97 for (Task task : tasks) { 98 System.out.println("manager的任務taskname:" + task.getName() + ",id:" + task.getId()); 99 taskService.claim(task.getId(), "王五");//申領任務 100 taskService.setVariable(task.getId(), "flag", true);//true批准,false不批准 101 Object applyUser = taskService.getVariable(task.getId(), "employeeId"); 102 Object day = taskService.getVariable(task.getId(), "day"); 103 System.out.println(String.format("%s請假%s天", applyUser, day)); 104 taskService.complete(task.getId());//完成任務 105 } 106 System.out.println("***************總經理組審批流程結束***************"); 107 } 108 }
依次運行deploy()、start()、apply()方法之后,因為申請的請假天數為4天,所以流程已經到了總經理角色,此時運行step2Boss()方法,一個完整的請假流程就已經完成。運行結果如下圖所示:
也可對代碼進行修改,總經理不批准
taskService.setVariable(task.getId(), "flag", false);
流程就回到員工那里,員工繼續申請,設置天數為2
taskService.setVariable(task.getId(), "day", 4);//設置請假天數
此時流程到達經理角色,經理批准,流程也就結束
taskService.setVariable(task.getId(), "flag", true);