springboot integrates Activiti-multi-instance task serial parallel configuration

This blog post refers to the following blogger
activiti multi-instance task
[nèng - Activiti6] Getting Started with Activiti6 (7) - Multi-instance task
Activiti actual combat (5): countersign

The difference between multi-instance serial and parallel

  1. Parallel means to proceed at the same time. For example, if a task is assigned to n people for processing, these n people will receive the task at the same time and can process it at the same time without being affected by each of them.
    The node act_ru_task configured with multiple instances will have multiple task data.

  2. Serial means that after the work or task is completed by one person, it is processed by another person until it is fully completed, and each task depends on the completion of the previous task.
    The node act_ru_task configured with multiple instances always has only one piece of data for this node.

One, bpmn parallel configuration

A simple process to configure parallelism in user tasks. (I won’t talk about serial, if you understand parallel, you also understand serial)

bpmn diagram

insert image description here

Workflow instance 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="http://www.activiti.org/processdef">
  <process id="cq" name="产权工作流" isExecutable="true">
    <documentation>产权会签</documentation>
    <startEvent id="sid-start" name="开始会签"/>
    <userTask id="sid-task2" name="村民表决" activiti:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee">
        <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.5 || pass == true}</completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <userTask id="sid-task3" name="镇街审核" activiti:assignee="${assignee}"/>
    <endEvent id="sid-end" name="结束"/>
    <sequenceFlow id="sid-45a0cbe9-6112-4a07-b95c-62545a0171fa" sourceRef="sid-task3" targetRef="sid-end"/>
    <userTask id="sid-task1" name="填写申请资料" activiti:assignee="${assignee}"/>
    <sequenceFlow id="sid-661add14-02b3-4794-b440-edf1798c82a8" sourceRef="sid-start" targetRef="sid-task1"/>
    <sequenceFlow id="sid-19cfbdbf-01f9-4c8d-9614-31300dbe461a" sourceRef="sid-task1" targetRef="sid-task2"/>
    <exclusiveGateway id="sid-gateway"/>
    <sequenceFlow id="sid-c8eeefa1-2f24-4daf-9e15-90d462529992" sourceRef="sid-task2" targetRef="sid-gateway"/>
    <sequenceFlow id="sid-d054bb13-bf87-46e2-9649-5044b9416f6d" sourceRef="sid-gateway" targetRef="sid-task3" name="通过">
      <conditionExpression xsi:type="tFormalExpression">${pass==true}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-92a9d1a2-f73c-46de-b493-44a0b56e3e21" sourceRef="sid-gateway" targetRef="sid-task1" name="拒绝">
      <conditionExpression xsi:type="tFormalExpression">${pass=false}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_cq">
    <bpmndi:BPMNPlane bpmnElement="cq" id="BPMNPlane_cq">
      <bpmndi:BPMNShape id="shape-85388f7c-e845-43d0-b84b-6679cefb3110" bpmnElement="sid-start">
        <omgdc:Bounds x="-110.0" y="-15.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-3fab3d96-c577-44b1-a1b3-f6a6249a14bc" bpmnElement="sid-task2">
        <omgdc:Bounds x="195.0" y="-40.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-08995250-1075-40e2-b92c-a5dd140fbcd5" bpmnElement="sid-task3">
        <omgdc:Bounds x="475.0" y="-40.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-d8775c65-537f-49d1-a05a-b65fef4d4ee8" bpmnElement="sid-end">
        <omgdc:Bounds x="650.0" y="-15.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-2d6eaf90-8343-4483-92c6-716781168133" bpmnElement="sid-45a0cbe9-6112-4a07-b95c-62545a0171fa">
        <omgdi:waypoint x="575.0" y="0.0"/>
        <omgdi:waypoint x="650.0" y="0.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-ed57a314-acc1-410e-8cd0-e6cc1c022668" bpmnElement="sid-task1">
        <omgdc:Bounds x="0.0" y="-40.0" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-62841ec0-6a53-44ff-a21f-529024508c08" bpmnElement="sid-661add14-02b3-4794-b440-edf1798c82a8">
        <omgdi:waypoint x="-80.0" y="0.0"/>
        <omgdi:waypoint x="0.0" y="0.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-92c7b154-d003-40cd-b8b4-cd1a25fbc533" bpmnElement="sid-19cfbdbf-01f9-4c8d-9614-31300dbe461a">
        <omgdi:waypoint x="100.0" y="0.0"/>
        <omgdi:waypoint x="195.0" y="0.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-3a522932-3abf-4cf8-977f-e6dd0a5abdac" bpmnElement="sid-gateway">
        <omgdc:Bounds x="365.0" y="-20.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-f35924a4-0134-4585-8933-81aa7e821c26" bpmnElement="sid-c8eeefa1-2f24-4daf-9e15-90d462529992">
        <omgdi:waypoint x="295.0" y="0.0"/>
        <omgdi:waypoint x="365.0" y="0.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-1ea70a14-127d-4ddd-8339-b389d61b3654" bpmnElement="sid-d054bb13-bf87-46e2-9649-5044b9416f6d">
        <omgdi:waypoint x="405.0" y="0.0"/>
        <omgdi:waypoint x="475.0" y="0.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-3cc368c6-0af2-4635-a4d4-27358568222b" bpmnElement="sid-92a9d1a2-f73c-46de-b493-44a0b56e3e21">
        <omgdi:waypoint x="385.0" y="-20.0"/>
        <omgdi:waypoint x="385.0" y="-127.50001"/>
        <omgdi:waypoint x="55.0" y="-127.500015"/>
        <omgdi:waypoint x="55.0" y="-40.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

Multi-instance configuration instructions

<userTask id="sid-task2" name="村民表决" activiti:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee">
        <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.5 || pass == true}</completionCondition>
      </multiInstanceLoopCharacteristics>
</userTask>
  1. isSequential false is parallel, true is serial
  2. completionCondition is the conditional judgment of multiple instances
  3. pass is my custom variable, others are built-in variables

Multi-instance attribute built-in variable description, the variables in the completionCondition element are basically built-in, and you don’t need to pass parameters, except that the pass is customized by me, so you need to pass parameters

1.nrOfActiveInstances The number of instances currently active, that is, the number of instances that have not yet been completed. The number of currently active instances, always 1 for sequential (serial) execution of multi-instances.

2.loopCounter has looped times

3. nrOfInstances total number of instances

4.nrOfCompletedInstances The number of completed instances

So the following code means that more than half of the total number of participants can pass and flow to the next node

<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.5 || pass == true}

Off-topic: loopCardinality loops twice before flowing to the next node, generally it will not be written to death

<multiInstanceLoopCharacteristics isSequential="true">
  <loopCardinality>2</loopCardinality>
</multiInstanceLoopCharacteristics>

I didn't use loopCardinality
insert image description here

2. Process start

1. Deployment process

//使用RepositoryService进行部署
 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
 RepositoryService repositoryService = processEngine.getRepositoryService();
 //使用RepositoryService进行部署
 DeploymentBuilder builder = repositoryService.createDeployment();
 Deployment deployment = builder
                             .addClasspathResource("bpmn/cq/cq.bpmn20.xml")
                             .addClasspathResource("bpmn/cq/cq.png")
                             .name("产权工作流").deploy();


 //输出部署信息
 System.out.println("流程部署id:" + deployment.getId());
 System.out.println("流程部署名称:" + deployment.getName());

After the deployment is successful, you can see the data in the following table, the core is act_re_procdef (deployed process definition

act_ge_bytearray (generic process definition and process resource table)
insert image description here

act_re_deployment (deployment unit information)
insert image description here

act_re_procdef (deployed process definition)
insert image description here

2. Startup process

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();

Map<String, Object> variables = new HashMap<>();
variables.put("assignee","小强");
//根据流程定义Id启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("cq",variables);

//输出实例信息
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动Id:" + processInstance.getActivityId());

3. Dynamic configuration

Parameter Description:

activiti:assignee="${assignee}"
activiti:elementVariable="assignee" #多实例任务依赖上面的配置${
    
    assignee}
activiti:collection="assigneeList" #你需要传参的变量

xm example

<userTask id="sid-task2" name="村民表决" activiti:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="false" activiti:collection="assigneeList" activiti:elementVariable="assignee">
        <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.5 || pass == true}</completionCondition>
      </multiInstanceLoopCharacteristics>
</userTask>

perform tasks

 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        TaskService taskService = processEngine.getTaskService();
        //根据流程key和任务的负责人查询任务并选择其中的一个任务处理,这里用的
        //是singleResult返回一条,真实环境中是通过步骤5中查询出所有的任务,然后在页面上选择一个任务进行处理.
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("cq") //流程Key
                .taskAssignee("小强")  //要查询的负责人
                .singleResult();

        if(task!=null){
    
    
            Map<String, Object> variables = new HashMap<>();
            List<String> list = Arrays.asList("小红", "小张", "小李");
            variables.put("assigneeList", list);
            //完成任务,参数:任务id
            taskService.complete(task.getId(),variables);

            System.out.println("任务执行成功");

        }else {
    
    
            System.out.println("无任务执行");
        }

Because I configured parallel and defined Xiaohong, Xiaozhang, and Xiaoli, so when the flow goes to the multi-instance node, you will see the data of three tasks. If it is serial, you will always see only one data, etc. After he executes, another task data will appear.

insert image description here

When the flow is transferred to a multi-instance node, you can test it yourself. When more than half of the users are executed or pass=true, you will transfer to the next node

        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        TaskService taskService = processEngine.getTaskService();
        //根据流程key和任务的负责人查询任务并选择其中的一个任务处理,这里用的
        //是singleResult返回一条,真实环境中是通过步骤5中查询出所有的任务,然后在页面上选择一个任务进行处理.
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("cq") //流程Key
                .taskAssignee("小张")  //要查询的负责人
                .singleResult();

        if(task!=null){
    
    
            //Map<String, Object> variables = new HashMap<>();

            //variables.put("pass", true);

            //完成任务,参数:任务id
            //taskService.complete(task.getId(),variables);

            taskService.complete(task.getId());

            System.out.println("任务执行成功");

        }else {
    
    
            System.out.println("无任务执行");
        }

Four. Summary

ps: If there are dozens, hundreds or thousands of people who will sign, it is not recommended to use it. Otherwise, it will be difficult to troubleshoot if something goes wrong.
The above program has solved commonly used problems, about countersignature, sign-up, sign-reduction, sign-out, weight configuration, custom pass condition configuration (condition custom pass)

<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.25}</completionCondition> 完成条件的配置。

If you use the serial mode to operate the nrOfActiveInstances variable is always 1, because the +1 operation will only be performed in parallel.

Guess you like

Origin blog.csdn.net/qq407995680/article/details/132597980