activiti 多例項任務
1.1.1. 前言
分享牛原創(尊重原創 轉載對的時候第一行請註明,轉載出處來自分享牛http://blog.csdn.net/qq_30739519)
我們在使用activiti 工作流引擎的時候,最常用的肯定是任務節點,因為在OA系統、審批系統、辦公自動化系統中核心的處理就是流程的運轉,在流程運轉的時候,可能我們有這樣的一個需求,在一個任務節點的時候,我們需要多個人對這個節點進行審批,比如實際中這樣一個例子,假如是一個部門的投票,這個部門有5個人,那麼當5個人都投票的時候大概分為如下幾種:
1.部門所有人都去投票,當所有人都投票完成的時候,這個節點結束,流程運轉到下一個節點。(所有的人都需要投票)
2.部門所有人都去投票,
3.部門中有一個部門經理,只要部門經理投票過了,這個節點結束,流程運轉到下一個節點(一票否決權)。
4.部門中根據職位不同,不同的人都不同的權重,當滿足條件的時候,這個節點結束,流程運轉到下一個節點。比如說所有的人員權重加起來是1,a有0.2的權重,其他的四個人分別是0.1的權重,我們可以配置權重達到0.3就可以走向下一個節點,換言之a的權重是其他人的2倍,那就是a的投票相當於2個人投票。這種需求還是很常見的。
5.部門所有人都去投票,a投票結束到b,b開始投票結束到c,一直如此,序列執行。最終到最後一個人再統計結果,決定流程的運轉。
上面的五種情況,我們可以提取出來一些資訊,我們的activiti 工作流引擎,必須支援如下功能,才能滿足上面的需求:
1.任務節點可以配置自定義滿足條件。
2.任務節點必須支援序列、並行。
3.任務節點必須支援可以指定候選人或者候選組。
4.任務節點必須支援可以迴圈的次數。
5.任務節點必須支援可以自定義權重。
6.任務節點必須支援加簽、減籤。(就是動態的修改任務節點的處理人)
因為實際上的需求可能比上面的幾種情況更加的複雜,上面的6個滿足條件,工作流支援前4個,後面的2個條件是不支援的,所以我們必須要擴充套件activiti 工作流引擎才能使用5、6等的功能。下面我們將詳細的介紹前四種條件的使用,在掌握基本使用之後,我們在後面的章節中將詳細的介紹,
1.1.2. 序列、並行配置
為了演示如何使用,我們採用由淺入深的使用,結合流程圖、流程定義xml、以及程式碼和資料庫的變化來闡釋每一個配置的使用以及含義。
流程的詳細定義如下圖所示:
流程的詳細定義xml如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<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="daling">
<process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="多例項任務" activiti:candidateUsers="shareniu1,shareniu2,shareniu3,shareniu4">
<multiInstanceLoopCharacteristics isSequential="true">
<loopCardinality>2</loopCardinality>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_daling">
<bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="180.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="370.0" y="90.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="650.0" y="130.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="215.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="422.0" y="145.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="475.0" y="117.0"></omgdi:waypoint>
<omgdi:waypoint x="667.0" y="130.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
xml配置檔案的部分含義如下:
1.activiti:candidateUsers="shareniu1,shareniu2,shareniu3,shareniu4" 這個節點可以4個人稽核。
2.<loopCardinality>2</loopCardinality> 迴圈2次結束。
3.<multiInstanceLoopCharacteristics isSequential="true"> 序列並行的配置。
1.1.2.1. 序列的配置
修改<multiInstanceLoopCharacteristics isSequential="true"> 中的isSequential為true是序列,isSequential為false是並行。我們測試序列。下面的程式碼展示啟動流程因為是以一個節點所以部署啟動後,直接進入多例項任務。
1.1.2.1.1. 流程的部署
Deployment deploy = repositoryService2.createDeployment()
.category(category).addClasspathResource("demo1.bpmn").deploy();
1.1.2.1.2. 流程的啟動
/**
*
* @param runtimeService
* @param key
* act_re_procdef表的Id
* @param businessKey
* 業務單號
* @return
*/
public ProcessInstance startProcessInstanceByKey(
RuntimeService runtimeService, String key, String businessKey){
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(key, businessKey);
returnprocessInstance;
}
我們按照上面的步驟啟動一個流程看一下資料庫的變化。
ACT_RU_TASK表的資料有了如下圖所示:
ACT_RU_IDENTITYLINK表的資料有了如下圖所示:
ACT_RU_IDENTITYLINK許可權表中,確實把我們設定的人員資訊設定進去了,shareniu1,shareniu2,shareniu3,shareniu4現在就有代辦資訊了。
ACT_RU_VARIABLE表的資料有了如下圖所示:
上面重要的變數需要解釋一下,要不然還真不好理解,多工是怎麼運轉的。
1.nrOfInstances 例項總數。
2.nrOfCompletedInstances 當前還沒有完成的例項 nr是number單詞縮寫 。
3.loopCounter 已經迴圈的次數。
4.nrOfActiveInstances 已經完成的例項個數。
下面我們結束一個任務看一下,流程走到那個節點了。
1.1.2.1.3. 完成任務
String taskId="45009";
demo.getTaskService().complete(taskId);
接下來看一下資料庫表的變化。
ACT_RU_VARIABLE表的資料有了如下圖所示:
上面我們仔細的發現,可以看到
nrOfCompletedInstances、loopCounter、nrOfActiveInstances都加1了,確實多工就是參考這幾個值的變化進行判斷的。
因為我們設定了迴圈2次,所以我們看看ACT_RU_IDENTITYLINK還有一個任務,因為我們是並行處理的。
所以我們在結束新的任務看一下流程是不是真的結束了,如果結束了,那麼我們迴圈次數的配置就是正確的。
1.1.2.1.4. 完成任務
String taskId="47502";
demo.getTaskService().complete(taskId);
下面看一下ACT_RU_TASK,裡面沒有任務資訊了,所以側面證明迴圈次數的配置就是正確的。
接下來我們測試並行任務。除了isSequential="false",其他的配置是一樣的。
1.1.2.2. 並行的配置測試
除了isSequential="false",其他的配置跟上面的序列是一樣一樣的。
重新部署測試,部署後我們啟動一個新的流程測試。
ACT_RU_TASK表如下:
一次性的有2個任務需要處理,因為我們迴圈的是2次,所以直接就是2個。
ok序列、並行就講解到這裡。
1.1.3. 序列、並行總結
我們配置的是迴圈2次,看以看到不管是並行還是序列,兩個代辦任務結束之後,流程直接跳轉到下一個狀態,但是
我們並沒有配置結束條件,所以上面的例子,也可以看出來,如果不配置預設的通過條件,則預設條件是1,後面的原始碼章節會給大家說明這一點的。
1.1.4. 通過條件的配置
在上面的序列、並行例項中,我們沒有設定通過條件,但是程式按照配置的迴圈的次數,然後跳轉到了下一個狀態,可以側面印證,如果不配置通過條件則預設值就是1.
下面的程式碼詳細的介紹通過條件的配置,具體的配置程式碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<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="daling">
<process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="多例項任務" activiti:candidateUsers="shareniu1,shareniu2,shareniu3,shareniu4">
<multiInstanceLoopCharacteristics isSequential="false">
<loopCardinality>4</loopCardinality>
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_daling">
<bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="180.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="371.0" y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="660.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="215.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="371.0" y="167.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="476.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="660.0" y="167.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
1.1.4.1. 配置描述
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition>
nrOfCompletedInstances、nrOfInstances 變數描述上面已經描述了,我們這裡設定的條件是大於1/4的人完成任務,任務就結束了。下面我們程式碼部署流程,啟動流程後進行測試:
ACT_RU_TASK表如下:
我們隨便結束一個任務,看一下ACT_RU_TASK表變化。
String taskId="60038";
demo.getTaskService().complete(taskId);
執行上面的程式碼,我們很神奇的發現,ACT_RU_TASK表中的其他任務沒有了,因為我們配置了4個人,通過條件是1/4,所以任意一個人結束了,流程就結束了。這裡我們測試的是並行,序列也是一樣的,讀者可以自行測試驗證。
1.1.5. 動態的配置
上面的幾種方式,我們定義xml的時候,迴圈的次數是固定寫在xml中的,也就是說我們配置的是迴圈2次,那麼所有的流程例項都是迴圈2次,這樣就不靈活了,程式當然是靈活了比較好,所以在實際開發中,我們可以使用下面的這種方式操作,使程式更加的靈活。
程式的xml配置如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<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="daling">
<process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="多例項任務" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee">
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
<userTask id="usertask2" name="User Task"></userTask>
<sequenceFlow id="flow3" sourceRef="startevent1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask2" targetRef="usertask1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_daling">
<bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="180.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="371.0" y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="660.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="330.0" y="50.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="476.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="660.0" y="167.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="197.0" y="150.0"></omgdi:waypoint>
<omgdi:waypoint x="382.0" y="105.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="382.0" y="105.0"></omgdi:waypoint>
<omgdi:waypoint x="423.0" y="140.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
動態配置如下所示:
<userTask id="usertask1" name="多例項任務" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee">
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
引數說明:
activiti:assignee="${assignee}"
activiti:elementVariable="assignee" 多例項任務依賴上面的配置${assignee}
activiti:collection="assigneeList"
三個引數結合決定了,當前節點的處理人來自assigneeList集合,注意這裡是集合資訊而不是字串,所以程式的執行時候變數的賦值,如下所示:
String[]v={"shareniu1","shareniu2","shareniu3","shareniu4"};
vars.put("assigneeList", Arrays.asList(v));
String taskId="97515";
demo.getTaskService().complete(taskId,vars);
ok了,測試一下,確實程式如預期的所示,大功告成。
1.1.6. 總結
引數的使用總結
4.activiti:candidateUsers="shareniu1,shareniu2,shareniu3,shareniu4" 這個節點可以4個人稽核。
5.<loopCardinality>2</loopCardinality> 迴圈2次結束。
6.<multiInstanceLoopCharacteristics isSequential="true"> 序列並行的配置。
7.
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition> 完成條件的配置。
這裡我們還可以得出一個結論:
如果使用序列方式操作nrOfActiveInstances 變數始終是1,因為並行的時候才會去+1操作。
1.1.7. 遺留點
上面的程式已經解決了常用的問題,關於會籤、加簽、減籤、退籤、權重配置、自定義通過條件配置(條件自定義通過)
這些問題,這個章節還沒有具體的實現,關於這些問題,由於本章內容有限,我們就後續章節講解吧。
未完待續...
分享牛,分享、我們是快樂的。