springboot整合Activiti-多实例任务串行并行配置

该博文参考了以下博主
activiti 多实例任务
【弄nèng - Activiti6】Activiti6入门篇(七)—— 多实例任务
Activiti实战(五):会签

多实例串行和并行区别

  1. 并行代表同时进行,如把任务分给n个人来处理,这n个人同时会收到任务,并且可以同时处理,不受各自的影响。
    配置了多实例的节点act_ru_task会有多条任务数据。

  2. 串行代表工作或任务由一个人完成后,再由另一个人去处理,直至全部完成,每个任务依赖于前一个任务完成。
    配置了多实例的节点act_ru_task永远只有一条该节点的数据。

一、bpmn并行配置

一个简单的流程,在用户任务里面配置并行。(串行就不说了,理解并行,也就懂串行)

bpmn图

在这里插入图片描述

工作流实例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>

多实例配置说明

<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为并行,true为串行
  2. completionCondition 为多实例的条件判断
  3. pass为我自定义的变量,其他都是内置变量

多实例属性内置变量说明 ,completionCondition 元素里面变量基本都是内置的,不用你传参,除了pass是我自定义的,才需要传参

1.nrOfActiveInstances 当前活动的实例数量,即还没有完成的实例数量。当前活动的实例数,对于顺序(串行)执行的多实例,值一直为1。

2.loopCounter 已经循环的次数

3.nrOfInstances 总共的实例数

4.nrOfCompletedInstances 已经完成的实例数量

所以下面这段代码的意思是参与人超过总人数的一半就可以通过,流转到下一个节点

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

题外:loopCardinality是循环2次才流转到下一个节点,一般不会写死

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

我没用loopCardinality
在这里插入图片描述

二、流程启动

1、部署流程

//使用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());

部署成功后,就看到以下表有数据了,核心是act_re_procdef(已部署的流程定义

act_ge_bytearray(通用的流程定义和流程资源表)
在这里插入图片描述

act_re_deployment(部署单元信息)
在这里插入图片描述

act_re_procdef(已部署的流程定义)
在这里插入图片描述

2、启动流程

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());

三、动态配置

参数说明:

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

xm示例

<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>

执行任务

 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("无任务执行");
        }

因为我配置的是并行,并且定义了小红、小张和小李,所以流转到多实例这个节点,你会看到三条任务的数据,如果是串行,永远只会看到一条数据,等他执行完,才会出现另一条任务数据。

在这里插入图片描述

流转到多实例节点的时候,你自己测试下,当执行用户超过一半或者传参pass=true,你就会流转到下一个节点了

        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("无任务执行");
        }

四、总结

ps:如果会签的人几十、上百或几千,就不建议用了,不然出了问题很难排查,还不如自己业务处理,再传参通过还是不通过。
上面的程序已经解决了常用的问题,关于会签、加签、减签、退签、权重配置、自定义通过条件配置(条件自定义通过)

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

如果使用串行方式操作nrOfActiveInstances 变量始终是1,因为并行的时候才会去+1操作。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值