任务的多实例和串并行 - AndiHappy/customcamunda GitHub Wiki

  1. 多实例是作为了一个单独的扩展元素来进行说明的。意思也就是说它可以和其他的BPMN组件结合起来使用,能够结合的BPMN组件有:

Service Task
Send Task
User Task
Business Rule Task
Script Task
Receive Task
Manual Task
(Embedded) Sub-Process
Call Activity
Transaction Subprocess

按照最常见的User Task进行说明。

  1. Camunda引擎中支持多实例的行为主要是:MultiInstanceActivityBehavior,以及集成这个类的:SequentialMultiInstanceActivityBehavior和ParallelMultiInstanceActivityBehavior,这两个类的响应是通过多实例扩展元素的属性isSequential="true" 来决定的。

  2. 具体的响应的过程:

MultiInstanceActivityBehavior
public static final String NUMBER_OF_INSTANCES = "nrOfInstances";
public static final String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
public static final String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";

这个是MultiInstanceActivityBehavior定义的三个常量,在流程定义的文件中可以直接的使用。实例个数,未完成实例个数以及已完成实例个数。

SequentialMultiInstanceActivityBehavior
public void complete(ActivityExecution scopeExecution) {
    int loopCounter = getLoopVariable(scopeExecution, LOOP_COUNTER) + 1;
    int nrOfInstances = getLoopVariable(scopeExecution, NUMBER_OF_INSTANCES);
    int nrOfCompletedInstances = getLoopVariable(scopeExecution, NUMBER_OF_COMPLETED_INSTANCES) + 1;

    setLoopVariable(scopeExecution, NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
//完成的条件
    if (loopCounter == nrOfInstances || completionConditionSatisfied(scopeExecution)) {
      leave(scopeExecution);
    }
    else {
      PvmActivity innerActivity = getInnerActivity(scopeExecution.getActivity());
      performInstance(scopeExecution, innerActivity, loopCounter);
    }
  }

从SequentialMultiInstanceActivityBehavior 上面可以看出多实例完成的条件判断,如果全部的实例执行完毕或者定义的完成条件执行完毕,这个实例就算是执行完毕了。

下面是具体的配置可生成的任务了,具体的流程的样子是:

流程定义

userTask中的配置

//顺序的生成三个代办的任务:
 <bpmn:multiInstanceLoopCharacteristics isSequential="true" >
        <bpmn:loopCardinality>3</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>

isSequential为true的情况下,会首先生成一个task,这个task完成以后,再次生成第二个针对同一个activitiId:Task_01 的task,完成以后,再同样的生成第三个。

16:10:58.704 INFO CamundaBaseTest [170] : task:id:null,activitiId:EndEvent_0a13dh0,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.704 INFO CamundaBaseTest [170] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:30 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5513,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:40 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5602,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:49 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5702,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:null,activitiId:Task_02,activityName:task2,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
//顺序的生成三个代办的任务:
 <bpmn:multiInstanceLoopCharacteristics isSequential=“false" >
        <bpmn:loopCardinality>3</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>

isSequential为false的情况下,会同时生成单个task,这三个task的审批顺序就没有前后的关系,知道三个人同时审批过了,才会进入下一级的审批

16:12:58.074 INFO CamundaBaseTest [170] : ------------------------------------------------------
16:13:00.947 INFO CamundaBaseTest [172] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:12:43 CST 2018
16:13:00.947 INFO CamundaBaseTest [172] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:null
16:13:00.947 INFO CamundaBaseTest [172] : task:id:5918,activitiId:Task_01,activityName:task1,assignee:role,endTime:null
16:13:00.948 INFO CamundaBaseTest [172] : task:id:5920,activitiId:Task_01,activityName:task1,assignee:role,endTime:null
16:13:00.948 INFO CamundaBaseTest [172] : task:id:5922,activitiId:Task_01,activityName:task1,assignee:role,endTime:null

下面的这个是并发的配置以及生成的task的样式:

  <bpmn:multiInstanceLoopCharacteristics isSequential="false">
    <bpmn:loopDataInputRef>assigneeList</bpmn:loopDataInputRef>
    <bpmn:inputDataItem name="assignee" />
  </bpmn:multiInstanceLoopCharacteristics>

并发实例的响应行为对应的逻辑:

public class ParallelMultiInstanceActivityBehavior extends MultiInstanceActivityBehavior implements MigrationObserverBehavior {
  @Override
  protected void createInstances(ActivityExecution execution, int nrOfInstances) throws Exception {
    PvmActivity innerActivity = getInnerActivity(execution.getActivity());
    // initialize the scope and create the desired number of child executions
    prepareScopeExecution(execution, nrOfInstances);
    List<ActivityExecution> concurrentExecutions = new ArrayList<ActivityExecution>();
    for (int i = 0; i < nrOfInstances; i++) {
      concurrentExecutions.add(createConcurrentExecution(execution));
    }
    // start the concurrent child executions
    // start executions in reverse order (order will be reversed again in command context with the effect that they are
    // actually be started in correct order :) )
    for (int i = (nrOfInstances - 1); i >= 0; i--) {
      ActivityExecution activityExecution = concurrentExecutions.get(i);
      //这个i保证在生成对应任务的时候,是轮流生成的,一个一个的带进去的
      performInstance(activityExecution, innerActivity, i);
    }
  }

//这里面的collectionElementVariable,是通过:inputDataItem 设置的,一个里面只能设置一个字段值 生成的任务形式是:

16:55:09.351 INFO CamundaBaseTest [187] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:39:12 CST 2018
16:55:09.352 INFO CamundaBaseTest [187] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:null
16:55:09.352 INFO CamundaBaseTest [187] : task:id:6424,activitiId:Task_01,activityName:My Task,assignee:100,endTime:null
16:55:09.353 INFO CamundaBaseTest [187] : task:id:6426,activitiId:Task_01,activityName:My Task,assignee:200,endTime:null
16:55:09.353 INFO CamundaBaseTest [187] : task:id:6428,activitiId:Task_01,activityName:My Task,assignee:300,endTime:null

下面的这种是对上面设置的一种优化:

<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="true"
     camunda:collection="${myService.resolveUsersForTask()}" camunda:elementVariable="assignee" >
  </multiInstanceLoopCharacteristics>
</userTask>

只不过使用的是myService.resolveUsersForTask() 服务调用的方式,另外配置方式,简单了一点。
在下面的这种模式,就是针对完成条件,进行的限制,这种条件就是完成率达到0.6,也就是百分之60的时候,就能够直接运行到下一个审批节点 适合做投票的流程操作

<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false"
     camunda:collection="assigneeList" camunda:elementVariable="assignee" >
    <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
  </multiInstanceLoopCharacteristics>
</userTask>
⚠️ **GitHub.com Fallback** ⚠️