Activiti6+SpringBoot---会签功能学习-1

1. 会签说明

会签,是指多个人员针对同一个事务进行协商处理,共同签署决定一件事情。
在工作流中会签,是指多个人员在同一个环节进行处理,同一环节的有多个处理人并行处理,按照配置规则,固定比例的人员办理完成后即可继续扭转至下一环节。

2. 多实例

多实例节点是在业务流程中定义重复环节的一个方法。 从开发角度讲,多实例和循环是一样的: 它可以根据给定的集合,为每个元素执行一个环节甚至一个完整的子流程, 既可以顺序依次执行也可以并发同步执行。

多实例是在一个普通的节点上添加了额外的属性定义 (所以叫做’多实例特性’),这样运行时节点就会执行多次。 下面的节点都可以成为一个多实例节点:

User Task

Script Task

Java Service Task

Web Service Task

Business Rule Task

Email Task

Manual Task

Receive Task

(Embedded) Sub-Process

Call Activity

根据规范的要求,每个上级流程为每个实例创建分支时都要提供如下变量:

nrOfInstances:实例总数

nrOfActiveInstances:当前活动的,比如,还没完成的,实例数量。 对于顺序执行的多实例,值一直为1。

nrOfCompletedInstances:已经完成实例的数目。

可以通过execution.getVariable(x)方法获得这些变量。

另外,每个创建的分支都会有分支级别的本地变量(比如,其他实例不可见, 不会保存到流程实例级别):

loopCounter:表示特定实例的在循环的索引值。可以使用activiti的elementIndexVariable属性修改loopCounter的变量名。
图形标记
如果节点是多实例的,会在节点底部显示三条短线。 三条竖线表示实例会并行执行。 三条横线表示顺序执行。
在这里插入图片描述
Xml内容
要把一个节点设置为多实例,节点xml元素必须设置一个multiInstanceLoopCharacteristics子元素。

<multiInstanceLoopCharacteristics isSequential="false|true">
 ...
</multiInstanceLoopCharacteristics>

isSequential属性表示节点是进行 顺序执行还是并行执行。

实例的数量会在进入节点时计算一次。 有一些方法配置它。一种方法是使用loopCardinality子元素直接指定一个数字。

<multiInstanceLoopCharacteristics isSequential="false|true">
  <loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>

也可以使用结果为整数的表达式:

<multiInstanceLoopCharacteristics isSequential="false|true">
  <loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>

另一个定义实例数目的方法是,通过loopDataInputRef子元素,设置一个类型为集合的流程变量名。 对于集合中的每个元素,都会创建一个实例。 也可以通过inputDataItem子元素指定集合。 下面的代码演示了这些配置:

<userTask id="miTasks" name="My Task ${loopCounter}" activiti:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false">
    <loopDataInputRef>assigneeList</loopDataInputRef>
    <inputDataItem name="assignee" />
  </multiInstanceLoopCharacteristics>
</userTask>

假设assigneeList变量包含这些值[kermit, gonzo, foziee]。 在上面代码中,三个用户任务会同时创建。每个分支都会拥有一个用名为assignee的流程变量, 这个变量会包含集合中的对应元素,在例子中会用来设置用户任务的分配者。

loopDataInputRef和inputDataItem的缺点是1)名字不好记, 2)根据BPMN 2.0格式定义,它们不能包含表达式。activiti通过在 multiInstanceCharacteristics中设置 collection和 elementVariable属性解决了这个问题:

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

多实例节点在所有实例都完成时才会结束。也可以指定一个表达式在每个实例结束时执行。 如果表达式返回true,所有其他的实例都会销毁,多实例节点也会结束,流程会继续执行。 这个表达式必须定义在completionCondition子元素中。

<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false"
     activiti:collection="assigneeList" activiti:elementVariable="assignee" >
    <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
  </multiInstanceLoopCharacteristics>
</userTask>

在这里例子中,会为assigneeList集合的每个元素创建一个并行的实例。 当60%的任务完成时,其他任务就会删除,流程继续执行。
以上内容为Activiti用户手册的内容,下面实现一个简单地流程。

3.流程实现

3.1 设计流程图

第一张为:${nrOfCompletedInstances/nrOfInstances >= 1 },表示需要所有会签任务全部完成才会往下执行。
在这里插入图片描述
Bpmn文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1597109014081" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="jointlySign" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="开始"/>
    <userTask activiti:assignee="${assignee}" activiti:candidateGroups="生产部领导" activiti:exclusive="true" id="_3" name="生产部领导会签">
      <multiInstanceLoopCharacteristics activiti:collection="${assignees}" activiti:elementVariable="assignee" isSequential="false">
        <completionCondition><![CDATA[${
    
    nrOfCompletedInstances/nrOfInstances>1}]]></completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <endEvent id="_4" name="结束"/>
    <sequenceFlow id="_5" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_6" sourceRef="_3" targetRef="_4"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#000000;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="jointlySign">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="45.0" y="290.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="55.0" width="85.0" x="260.0" y="285.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="32.0" width="32.0" x="560.0" y="295.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="77.0" y="306.0"/>
        <omgdi:waypoint x="260.0" y="312.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="345.0" y="312.5"/>
        <omgdi:waypoint x="560.0" y="311.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

测试类

package com.yb;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/5
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiJpaTest04 {
    
    

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private HttpServletRequest request;
    /**
     * 部署流程,测试会签功能
     */
    @Test
    public void repositoryDeploy(){
    
    
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("processes/activiti_jointlySign.bpmn")
                .addClasspathResource("processes/activiti_jointlySign.png")
                .name("测试会签功能-1")
                .deploy();
        System.out.println("部署ID:"+deploy.getId());
        System.out.println("部署名称"+deploy.getName());
    }

    /**
     * 发布流程
     */
    @Test
    public void runtimeRelease(){
    
    
        ArrayList<String> list = new ArrayList<>();
        list.add("tom");
        list.add("jack");
        list.add("mary");
        HashMap<String, Object> map = new HashMap<>();
        map.put("assignees",list);
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("jointlySign",map);
        System.out.println("流程实例ID:"+pi.getId());
        System.out.println("流程定义ID:"+pi.getProcessDefinitionId());
    }

    /**
     * 查询及完成任务-需要所有会签全部完成
     */
    @Test
    public void taskQueryComplete(){
    
    
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("tom")
                .list();
        for (Task task : list) {
    
    
            System.out.println("--------------------------------------------");
            System.out.println("任务ID:" + task.getId());
            System.out.println("任务名称:" + task.getName());
            System.out.println("任务创建时间:" + task.getCreateTime());
            System.out.println("任务委派人:" + task.getAssignee());
            System.out.println("流程实例ID:" + task.getProcessInstanceId());
            taskService.complete(task.getId());
        }
    }
}

数据库历史记录:
需要三个人全部完成才能结束,通过结束时间可以看出这三个人都是必须完成任务的。
在这里插入图片描述

第二张为:${nrOfCompletedInstances/nrOfInstances >= 0.6},表示需要两个会签任务完成就会往下执行。
在这里插入图片描述
Bpmn文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1597111769369" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="jointlySign2" isClosed="false" isExecutable="true" processType="None">
    <startEvent id="_2" name="开始"/>
    <userTask activiti:assignee="${assignee}" activiti:candidateGroups="生产部领导" activiti:exclusive="true" id="_3" name="生产部领导会签">
      <multiInstanceLoopCharacteristics activiti:collection="${assignees}" activiti:elementVariable="assignee" isSequential="false">
        <completionCondition><![CDATA[${
    
    nrOfCompletedInstances/nrOfInstances>0.6}]]></completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <endEvent id="_4" name="结束"/>
    <sequenceFlow id="_5" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_6" sourceRef="_3" targetRef="_4"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#000000;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="jointlySign2">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="400.0" y="15.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="55.0" width="85.0" x="375.0" y="170.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="32.0" width="32.0" x="405.0" y="345.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="416.0" y="47.0"/>
        <omgdi:waypoint x="416.0" y="170.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="421.0" y="225.0"/>
        <omgdi:waypoint x="421.0" y="345.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

测试类

package com.yb;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author [email protected]
 * @version 1.0
 * @date 2020/8/5
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiJpaTest05 {
    
    

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private HttpServletRequest request;
    /**
     * 部署流程,测试会签功能
     */
    @Test
    public void repositoryDeploy(){
    
    
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("processes/activiti_jointlySign2.bpmn")
                .addClasspathResource("processes/activiti_jointlySign2.png")
                .name("测试会签功能-2")
                .deploy();
        System.out.println("部署ID:"+deploy.getId());
        System.out.println("部署名称"+deploy.getName());
    }

    /**
     * 发布流程
     */
    @Test
    public void runtimeRelease(){
    
    
        ArrayList<String> list = new ArrayList<>();
        list.add("tom");
        list.add("jack");
        list.add("mary");
        HashMap<String, Object> map = new HashMap<>();
        map.put("assignees",list);
        ProcessInstance pi = runtimeService.startProcessInstanceByKey("jointlySign2",map);
        System.out.println("流程实例ID:"+pi.getId());
        System.out.println("流程定义ID:"+pi.getProcessDefinitionId());
    }

    /**
     * 查询及完成任务
     */
    @Test
    public void taskQueryComplete(){
    
    
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("tom")
                .list();
        for (Task task : list) {
    
    
            System.out.println("--------------------------------------------");
            System.out.println("任务ID:" + task.getId());
            System.out.println("任务名称:" + task.getName());
            System.out.println("任务创建时间:" + task.getCreateTime());
            System.out.println("任务委派人:" + task.getAssignee());
            System.out.println("流程实例ID:" + task.getProcessInstanceId());
            taskService.complete(task.getId());
        }
    }
}

数据库历史记录:
需要二个人完成就可以结束,通过结束时间可以看出这有两个人的结束时间是相同的,说明只要有两个人完成任务就可以结束流程了。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Lv_vI/article/details/107927659