1. 程式人生 > >activiti 多例項任務

activiti 多例項任務

1.1.1. 前言

分享牛原創(尊重原創 轉載對的時候第一行請註明,轉載出處來自分享牛http://blog.csdn.net/qq_30739519)

我們在使用activiti 工作流引擎的時候,最常用的肯定是任務節點,因為在OA系統、審批系統、辦公自動化系統中核心的處理就是流程的運轉,在流程運轉的時候,可能我們有這樣的一個需求,在一個任務節點的時候,我們需要多個人對這個節點進行審批,比如實際中這樣一個例子,假如是一個部門的投票,這個部門有5個人,那麼當5個人都投票的時候大概分為如下幾種:

1.部門所有人都去投票,當所有人都投票完成的時候,這個節點結束,流程運轉到下一個節點。(所有的人都需要投票)

2.部門所有人都去投票,

只要有任意2/3的人同意,這個節點結束,流程運轉到下一個節點。(部分人投票只要滿足條件就算完成)

3.部門中有一個部門經理,只要部門經理投票過了,這個節點結束,流程運轉到下一個節點(一票否決權)。

4.部門中根據職位不同,不同的人都不同的權重,當滿足條件的時候,這個節點結束,流程運轉到下一個節點。比如說所有的人員權重加起來是1a0.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 工作流引擎才能使用56等的功能。下面我們將詳細的介紹前四種條件的使用,在掌握基本使用之後,我們在後面的章節中將詳細的介紹,

56這兩種功能以及可能更加複雜的操作。

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  當前還沒有完成的例項 nrnumber單詞縮寫 。

3.loopCounter 已經迴圈的次數。

4.nrOfActiveInstances 已經完成的例項個數。

下面我們結束一個任務看一下,流程走到那個節點了。

1.1.2.1.3. 完成任務

String taskId="45009";

demo.getTaskService().complete(taskId);

接下來看一下資料庫表的變化。

ACT_RU_VARIABLE表的資料有了如下圖所示:

 

上面我們仔細的發現,可以看到

nrOfCompletedInstancesloopCounternrOfActiveInstances都加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. 遺留點

上面的程式已經解決了常用的問題,關於會籤、加簽、減籤、退籤、權重配置、自定義通過條件配置(條件自定義通過)

這些問題,這個章節還沒有具體的實現,關於這些問題,由於本章內容有限,我們就後續章節講解吧。

未完待續...

分享牛,分享、我們是快樂的。