【2023最新Activiti7全网最全】4、Activiti7进阶篇

2023-05-16

1.流程实例

1.1 什么是流程实例

流程实例(ProcessInstance)代表流程定义的执行实例,一个流程实例包括了所有的运行节点,我们可以利用这个对象来了解当前流程实例的进度等信息
  例如:用户或者程序安装流程定义的内容发起了一个流程,这个就是一个流程实例 

1.2 业务管理

  流程定义部署在Activiti后,我们就可以在系统中通过Activiti去管理流程的执行,但是如果我们要将我们的流程实例和业务数据关联,这时我们需要使用到Activiti中预留的BusinessKey(业务标识)来关联

 

 实现代码:

    /**
     * 启动流程实例,添加businessKey
     */
    @Test
    public void test01(){
        // 1.获取ProcessEngine对象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 3.启动流程实例
        ProcessInstance instance = runtimeService
                .startProcessInstanceByKey("evection", "1001");
        // 4.输出processInstance相关属性
        System.out.println("businessKey = "+instance.getBusinessKey());
    }

1.3 流程实例的挂起和激活

  在实际场景中可能由于流程变更需要将当前运行的流程暂停而不是删除,流程暂停后将不能继续执行。

1.3.1 全部流程挂起

操作流程的定义为挂起状态,该流程定义下边所有的流程实例全部暂停。

流程定义为挂起状态,该流程定义将不允许启动新的流程实例,同时该流程定义下的所有的流程实例都将全部挂起暂停执行。

    /**
     * 全部流程挂起实例与激活
     */
    @Test
    public void test02(){
       // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService对象
        RepositoryService repositoryService = engine.getRepositoryService();
        // 3.查询流程定义的对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("evection")
                .singleResult();
        // 4.获取当前流程定义的状态
        boolean suspended = processDefinition.isSuspended();
        String id = processDefinition.getId();
        // 5.如果挂起就激活,如果激活就挂起
        if(suspended){
            // 表示当前定义的流程状态是 挂起的
            repositoryService.activateProcessDefinitionById(
                    id // 流程定义的id
            ,true // 是否激活
            ,null // 激活时间
            );
            System.out.println("流程定义:" + id + ",已激活");
        }else{
            // 非挂起状态,激活状态 那么需要挂起流程定义
            repositoryService.suspendProcessDefinitionById(
                    id // 流程id
                    ,true // 是否挂起
                    ,null // 挂起时间
            );
            System.out.println("流程定义:" + id + ",已挂起");
        }
    }

挂起流程定义后,对于的实例对象中的状态会修改为2

 然后再去操作对于的流程实例会抛异常信息

 我们再将挂起的流程转变为激活状态,对于的状态值会从2更新为1

 然后就是业务流程可以正常处理了

1.3.2 单个实例挂起 

操作流程实例对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续执行,当前流程定义的其他流程实例是不受干扰的。完成该流程实例的当前任务会抛异常 

    /**
     * 单个流程实例挂起与激活
     */
    @Test
    public void test03(){
        // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RuntimeService
        RuntimeService runtimeService = engine.getRuntimeService();
        // 3.获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId("25001")
                .singleResult();
        // 4.获取相关的状态操作
        boolean suspended = processInstance.isSuspended();
        String id = processInstance.getId();
        if(suspended){
            // 挂起--》激活
            runtimeService.activateProcessInstanceById(id);
            System.out.println("流程定义:" + id + ",已激活");
        }else{
            // 激活--》挂起
            runtimeService.suspendProcessInstanceById(id);
            System.out.println("流程定义:" + id + ",已挂起");
        }

    }

 然后我们可以在数据库中查看到状态的更新

 2. 个人任务

2.1 分配任务责任人

2.1.1 固定分配

 在进行业务流程建模的时候指定固定的任务负责人:

 在Properties视图中,填写Assiginee项为任务负责人

 2.1.2 表达式分配

在Activiti中支持使用UEL表达式,UEL表达式是Java EE6 规范的一部分, UEL(Unified Expression Language) 即 统一表达式语音, Activiti支持两种UEL表达式: UEL-value 和UEL-method 

UEL-value 

 在assignee中使用流程变量处理

 

 

 

然后我们可以来操作

  首先我们需要将定义的流程部署到Activiti数据库中

    /**
     * 先将新定义的流程部署到Activiti中数据库中
     */
    @Test
    public void test01(){
        // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService service = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署操作
        Deployment deploy = service.createDeployment()
                .addClasspathResource("bpmn/evection-uel.bpmn") // 添加bpmn资源
                .addClasspathResource("bpmn/evection-uel.png") // 添加png资源
                .name("出差申请流程-UEL")
                .deploy();// 部署流程
        // 4.输出流程部署的信息
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

部署成功后我们需要启动一个新的流程实例,然后在流程实例创建的其实关联UEL表达式

    /**
     * 创建一个流程实例
     *    给流程定义中的 UEL表达式赋值
     */
    @Test
    public void test02(){
      // 获取流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 获取RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 设置 assignee 的取值,
        Map<String,Object> map = new HashMap<>();
        map.put("assignee0","张三");
        map.put("assignee1","李四");
        map.put("assignee2","王五");
        map.put("assignee3","赵财务");
        // 创建流程实例
        runtimeService.startProcessInstanceByKey("evection-uel",map);
    }

启动成功后我们在 act_ru_variable中可以看到UEL表达式对应的赋值信息

 UEL-method

 userBean 是 spring 容器中的一个 bean,表示调用该 bean 的 getUserId()方法。

UEL-method 与 UEL-value 结合 

再比如:
${ldapService.findManagerForEmployee(emp)}
ldapService 是 spring 容器的一个 bean,findManagerForEmployee 是该 bean 的一个方法,emp 是 activiti
流程变量, emp 作为参数传到 ldapService.findManagerForEmployee 方法中。
其它

表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。
  如下:
  ${order.price > 100 && order.price < 250}

 2.1.3 监听器分配

可以使用监听器来完成很多Activiti的流程业务。我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定assignee 

Event选项 

 create:任务创建后触发
assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件都触发

自定义的监听器 

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

public class MyTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        if("创建请假单".equals(delegateTask.getName())
        && "create".equals(delegateTask.getEventName())){
            // 指定任务的负责人
            delegateTask.setAssignee("张三-Listener");
        }

    }
}

测试代码

/**
 * 先将新定义的流程部署到Activiti中数据库中
 */
@Test
public void test01(){
    // 1.获取ProcessEngine对象
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取RepositoryService进行部署操作
    RepositoryService service = engine.getRepositoryService();
    // 3.使用RepositoryService进行部署操作
    Deployment deploy = service.createDeployment()
            .addClasspathResource("bpmn/evection-listener.bpmn") // 添加bpmn资源
            .addClasspathResource("bpmn/evection-listener.png") // 添加png资源
            .name("出差申请流程-UEL")
            .deploy();// 部署流程
    // 4.输出流程部署的信息
    System.out.println("流程部署的id:" + deploy.getId());
    System.out.println("流程部署的名称:" + deploy.getName());
}

/**
 * 创建一个流程实例
 *    给流程定义中的 UEL表达式赋值
 */
@Test
public void test02(){
  // 获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取RuntimeService对象
    RuntimeService runtimeService = processEngine.getRuntimeService();

    // 创建流程实例
    runtimeService.startProcessInstanceByKey("evection-listener");
}

2.2 查询任务 

查询任务负责人的待办任务

代码如下:

// 查询当前个人待执行的任务
@Test
public void findPersonalTaskList() {
    // 流程定义key
    String processDefinitionKey = "myEvection1";
    // 任务负责人
    String assignee = "张三";
    // 获取TaskService
    TaskService taskService = processEngine.getTaskService();
    List<Task> taskList = taskService.createTaskQuery()
    	.processDefinitionKey(processDefinitionKey)
    	.includeProcessVariables()
        .taskAssignee(assignee)
        .list();
    for (Task task : taskList) {
        System.out.println("----------------------------");
        System.out.println("流程实例id: " + task.getProcessInstanceId());
        System.out.println("任务id: " + task.getId());
        System.out.println("任务负责人: " + task.getAssignee());
        System.out.println("任务名称: " + task.getName());
    }
}

关联 businessKey

需求:
  在 activiti 实际应用时,查询待办任务可能要显示出业务系统的一些相关信息。

比如:查询待审批出差任务列表需要将出差单的日期、 出差天数等信息显示出来。

  出差天数等信息在业务系统中存在,而并没有在 activiti 数据库中存在,所以是无法通过 activiti 的 api 查询到出差天数等信息。
实现:
  在查询待办任务时,通过 businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。
 

@Test
    public void findProcessInstance(){
//        获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        获取TaskService
        TaskService taskService = processEngine.getTaskService();
//        获取RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
//        查询流程定义的对象
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("myEvection1")
                .taskAssignee("张三")
                .singleResult();
//        使用task对象获取实例id
        String processInstanceId = task.getProcessInstanceId();
//          使用实例id,获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
//        使用processInstance,得到 businessKey
        String businessKey = processInstance.getBusinessKey();

        System.out.println("businessKey=="+businessKey);

    }

 2.3 办理任务

注意:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限 。

/**
     * 完成任务,判断当前用户是否有权限
     */
    @Test
    public void completTask() {
        //任务id
        String taskId = "15005";
//        任务负责人
        String assingee = "张三";
        //获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 创建TaskService
        TaskService taskService = processEngine.getTaskService();
//        完成任务前,需要校验该负责人可以完成当前任务
//        校验方法:
//        根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(assingee)
                .singleResult();
        if(task != null){
            taskService.complete(taskId);
            System.out.println("完成任务");
        }
    }

3.流程变量
3.1、什么是流程变量
  流程变量在 activiti 中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 activiti结合时少不了流程变量,流程变量就是 activiti 在管理工作流时根据管理需要而设置的变量。比如:在出差申请流程流转时如果出差天数大于 3 天则由总经理审核,否则由人事直接审核, 出差天数就可以设置为流程变量,在流程流转时使用。

注意:虽然流程变量中可以存储业务数据可以通过activiti的api查询流程变量从而实现 查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。

3.2、流程变量类型
  如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,需要生成 serialVersionUID。

3.3、流程变量作用域
  流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例
(execution)

3.3.1、globa变量
  流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量

注意:

如: Global变量:userId(变量名)、zhangsan(变量值)

  global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。

3.3.2、local变量
  任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。

3.4、流程变量的使用方法
3.4.1、在属性上使用UEL表达式
  可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。

Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配

3.4.2、在连线上使用UEL表达式
  可以在连线上设置UEL表达式,决定流程走向。比如:${price<10000} 。price就是一个流程变量名称,uel表达式结果类型为布尔类型。如果UEL表达式是true,要决定 流程执行走向。

3.5 流程变量使用
3.5.1 需求
​   员工创建出差申请单,由部门经理审核,部门经理申请通过后3天以下由财务直接申批,3天以上先由总经理审批,总经理审批通过后再由财务审批。

3.5.2 流程定义

  先通过UEL-value来设置负责人

 

然后在分支线上来设置条件 

 那么还可以通过对象参数命名,比如 evection.num:

 

 另一根线对应的设置

 

然后可以将相关的资源文件拷贝到项目中,

3.5.3 使用Global变量

  接下来使用Global变量控制流程

3.5.3.1 POJO创建

  首先创建POJO对象

/**
 * 出差申请的POJO对象
 */
@Data
public class Evection {

    private long id;

    private String evectionName;


    /**
     * 出差的天数
     */
    private double num;

    private Date beginDate;

    private Date endDate;

    private String destination;

    private String reson;
}

 3.5.3.2 流程的部署

    /**
     * 部署流程
     */
    @Test
    public void test01(){
        // 1.获取ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        // 2.获取RepositoryService进行部署操作
        RepositoryService service = engine.getRepositoryService();
        // 3.使用RepositoryService进行部署操作
        Deployment deploy = service.createDeployment()
                .addClasspathResource("bpmn/evection-variable.bpmn") // 添加bpmn资源
                .addClasspathResource("bpmn/evection-variable.png") // 添加png资源
                .name("出差申请流程-流程变量")
                .deploy();// 部署流程
        // 4.输出流程部署的信息
        System.out.println("流程部署的id:" + deploy.getId());
        System.out.println("流程部署的名称:" + deploy.getName());
    }

3.5.3.3 设置流程变量

a.启动时设置流程变量

在启动流程时设置流程变量,变量的作用域是整个流程实例。 

    /**
     * 启动流程实例,设置流程变量
     */
    @Test
    public void test02(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = engine.getRuntimeService();
        // 流程定义key
        String key = "evection-variable";
        // 创建变量集合
        Map<String,Object> variables = new HashMap<>();
        // 创建出差对象 POJO
        Evection evection = new Evection();
        // 设置出差天数
        evection.setNum(4d);
        // 定义流程变量到集合中
        variables.put("evection",evection);
        // 设置assignee的取值
        variables.put("assignee0","张三1");
        variables.put("assignee1","李四1");
        variables.put("assignee2","王五1");
        variables.put("assignee3","赵财务1");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
        // 输出信息
        System.out.println("获取流程实例名称:"+processInstance.getName());
        System.out.println("流程定义ID:" + processInstance.getProcessDefinitionId());
    }

完成任务

   /**
     * 完成任务
     */
    @Test
    public void test03(){
        String key = "evection-variable";
        String assignee = "李四1";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();
        if(task != null){
            taskService.complete(task.getId());
            System.out.println("任务执行完成...");
        }
    }

通过startProcessInstanceByKey方法设置流程变量的作用域是一个流程实例,流程变量使用Map存储,同一个流程实例map中的key相同,后者会覆盖前者

b.任务办理时设置
  在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。

  这里需要在创建出差单任务完成时设置流程变量
 

    /**
     * 启动流程实例,设置流程变量
     */
    @Test
    public void test02(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = engine.getRuntimeService();
        // 流程定义key
        String key = "evection-variable";
        // 创建变量集合
        Map<String,Object> variables = new HashMap<>();
        
        // 设置assignee的取值
        variables.put("assignee0","张三1");
        variables.put("assignee1","李四1");
        variables.put("assignee2","王五1");
        variables.put("assignee3","赵财务1");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
        // 输出信息
        System.out.println("获取流程实例名称:"+processInstance.getName());
        System.out.println("流程定义ID:" + processInstance.getProcessDefinitionId());
    }

    /**
     * 完成任务
     */
    @Test
    public void test03(){
        String key = "evection-variable";
        String assignee = "李四1";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskAssignee(assignee)
                .singleResult();

        Map<String,Object> variables = new HashMap<>();
        // 创建出差对象 POJO
        Evection evection = new Evection();
        // 设置出差天数
        evection.setNum(4d);
        // 定义流程变量到集合中
        variables.put("evection",evection);

        if(task != null){
            taskService.complete(task.getId(),variables);
            System.out.println("任务执行完成...");
        }
    }

说明:
通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。
任务办理时也是通过map<key,value>设置流程变量,一次可以设置多个变量。

c.当前流程实例设置

  通过流程实例id设置全局变量,该流程实例必须未执行完成。

    @Test
    public void setGlobalVariableByExecutionId(){
//    当前流程实例执行 id,通常设置为当前执行的流程实例
        String executionId="2601";
//     获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        获取RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
//        创建出差pojo对象
        Evection evection = new Evection();
//        设置天数
        evection.setNum(3d);
//      通过流程实例 id设置流程变量
        runtimeService.setVariable(executionId, "evection", evection);
//      一次设置多个值
//      runtimeService.setVariables(executionId, variables)
    }

注意:
  executionId必须当前未结束 流程实例的执行id,通常此id设置流程实例 的id。也可以通runtimeService.getVariable()获取流程变量。

d.当前任务设置

@Test
	public void setGlobalVariableByTaskId(){
		
		//当前待办任务id
		String taskId="1404";
//     获取processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
		TaskService taskService = processEngine.getTaskService();
		Evection evection = new Evection();
		evection.setNum(3);
		//通过任务设置流程变量
		taskService.setVariable(taskId, "evection", evection);
		//一次设置多个值 
		//taskService.setVariables(taskId, variables)
	}

 注意:
  任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,会报错也可以通过taskService.getVariable()获取流程变量。

3.5.4 设置local流程变量
3.5.4.1、任务办理时设置
  任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。
 

/*
*处理任务时设置local流程变量
*/
@Test
public void completTask() {
   //任务id
   String taskId = "1404";
//  获取processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
//  定义流程变量
   Map<String, Object> variables = new HashMap<String, Object>();
   Evection evection = new Evection ();
   evection.setNum(3d);
// 定义流程变量
   Map<String, Object> variables = new HashMap<String, Object>();
//  变量名是holiday,变量值是holiday对象
    variables.put("evection", evection);
//  设置local变量,作用域为该任务
    taskService.setVariablesLocal(taskId, variables);
//  完成任务
   taskService.complete(taskId);
}

说明:
  设置作用域为任务的local变量,每个任务可以设置同名的变量,互不影响。

3.5.4.2、通过当前任务设置

@Test
public void setLocalVariableByTaskId(){
//   当前待办任务id
    String taskId="1404";
//  获取processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
    Evection evection = new Evection ();
    evection.setNum(3d);
//  通过任务设置流程变量
    taskService.setVariableLocal(taskId, "evection", evection);
//  一次设置多个值 
    //taskService.setVariablesLocal(taskId, variables)
}

注意:
  任务id必须是当前待办任务id,act_ru_task中存在。

3.5.4.3、 Local变量测试1
  如果上边例子中设置global变量改为设置local变量是否可行?为什么?
  Local变量在任务结束后无法在当前流程实例执行中使用,如果后续的流程执行需要用到此变量则会报错。

3.5.4.4、 Local变量测试2
  在部门经理审核、总经理审核、财务审核时设置local变量,可通过historyService查询每个历史任务时将流程变量的值也查询出来。

代码如下:
 

// 创建历史任务查询对象
      HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
      // 查询结果包括 local变量
      historicTaskInstanceQuery.includeTaskLocalVariables();
for (HistoricTaskInstance historicTaskInstance : list) {
         System.out.println("==============================");
         System.out.println("任务id:" + historicTaskInstance.getId());
         System.out.println("任务名称:" + historicTaskInstance.getName());
         System.out.println("任务负责人:" + historicTaskInstance.getAssignee());
     System.out.println("任务local变量:"+ historicTaskInstance.getTaskLocalVariables());

}

注意:查询历史流程变量,特别是查询pojo变量需要经过反序列化,不推荐使用。

4.组任务
4.1、需求

  在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。
  针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。

4.2、设置任务候选人
  在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。

 查看bpmn文件

<userTask activiti:candidateUsers="lisi,wangwu" activiti:exclusive="true" id="_3" name="经理审批"/>

我们可以看到部门经理的审核人已经设置为 lisi,wangwu 这样的一组候选人,可以使用activiti:candiateUsers=”用户 1,用户 2,用户 3”的这种方式来实现设置一组候选人

4.3、组任务
4.3.1、组任务办理流程

a、查询组任务
  指定候选人,查询该候选人当前的待办任务。候选人不能立即办理任务。

b、拾取(claim)任务
  该组任务的所有候选人都能拾取。将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。如果拾取后不想办理该任务?需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。

c、查询个人任务
  查询方式同个人任务部分,根据assignee查询用户负责的个人任务。

d、办理个人任务
4.3.2、 查询组任务

   根据候选人查询组任务

    /**
     * 查询组任务
     */
    @Test
    public void test03(){
        String key = "evection1";
        String candidateUser = "lisi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateUser(candidateUser)
                .list();
        for (Task task : list) {
            System.out.println("流程实例Id:" + task.getProcessInstanceId());
            System.out.println("任务ID:" + task.getId());
            System.out.println("负责人:" + task.getAssignee());
            System.out.println("任务名称:" + task.getName());
        }
    }

4.3.3 、 拾取组任务

   候选人员拾取组任务后该任务变为自己的个人任务。

    /**
     * 候选人 拾取任务
     */
    @Test
    public void test04(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "72505";
        // 候选人
        String userId = "lisi";
        // 拾取任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(userId) // 根据候选人查询
                .singleResult();
        if(task != null){
            // 可以拾取任务
            taskService.claim(taskId,userId);
            System.out.println("拾取成功");
        }
    }

4.3.4、 查询个人待办任务

  查询方式同个人任务查询

    @Test
    public void test03(){
        String key = "evection1";
        String candidateUser = "lisi";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                //.taskCandidateUser(candidateUser)
                //.taskCandidateOrAssigned(candidateUser)
                .taskAssignee(candidateUser)
                .list();
        for (Task task : list) {
            System.out.println("流程实例Id:" + task.getProcessInstanceId());
            System.out.println("任务ID:" + task.getId());
            System.out.println("负责人:" + task.getAssignee());
            System.out.println("任务名称:" + task.getName());
        }
    }

4.3.5、 办理个人任务

  同个人任务办理

    /**
     * 完成个人任务
     */
    @Test
    public void test05(){
        String  taskId = "72505";
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        taskService.complete(taskId);
        System.out.println("完成任务:" + taskId);
    }

4.3.6、 归还组任务

   如果个人不想办理该组任务,可以归还组任务,归还后该用户不再是该任务的负责人

   /**
     * 归还任务
     */
    @Test
    public void test06(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "75002";
        String userId= "zhangsan";
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(userId)
                .singleResult();
        if(task != null){
            // 如果设置为null,归还组任务,任务没有负责人
            taskService.setAssignee(taskId,null);
        }
    }

4,3,7 任务交接

  任务负责人将任务交给其他负责人来处理

    /**
     * 任务交接
     */
    @Test
    public void test07(){
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = engine.getTaskService();
        String taskId = "75002";
        String userId= "zhangsan";
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(userId)
                .singleResult();
        if(task != null){
            // 设置该任务的新的负责人
            taskService.setAssignee(taskId,"赵六");
        }
    }

4.3.8、 数据库表操作

  查询当前任务执行表

SELECT * FROM act_ru_task 

 任务执行表,记录当前执行的任务,由于该任务当前是组任务,所有assignee为空,当拾取任务后该字段就是拾取用户的id,查询任务参与者

SELECT * FROM act_ru_identitylink

任务参与者,记录当前参考任务用户或组,当前任务如果设置了候选人,会向该表插入候选人记录,有几个候选就插入几个与act_ru_identitylink对应的还有一张历史表act_hi_identitylink,向act_ru_identitylink插入记录的同时也会向历史表插入记录。任务完成

5.网关
  网关用来控制流程的流向

5.1 排他网关ExclusiveGateway
5.1.1 什么是排他网关:

  排他网关,用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支,

注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。

为什么要用排他网关?

  不用排他网关也可以实现分支,如:在连线的condition条件上设置分支条件。在连线设置condition条件的缺点:如果条件都不满足,流程就结束了(是异常结束)。如果 使用排他网关决定分支的走向,如下:

 如果从网关出去的线所有条件都不满足则系统抛出异常。

org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process
   at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)

5.1.2 流程定义

  排他网关图标,红框内:
在这里插入图片描述

5.1.3 测试

  在部门经理审核后,走排他网关,从排他网关出来的分支有两条,一条是判断出差天数是否大于3天,另一条是判断出差天数是否小于等于3天。设置分支条件时,如果所有分支条件都不是true,报错:

org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process

       at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)

5.2 并行网关ParallelGateway

5.2.1 什么是并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:

l fork分支:

  并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。

l join汇聚:

  所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。

 

 例子:

说明:

  技术经理和项目经理是两个execution分支,在act_ru_execution表有两条记录分别是技术经理和项目经理,act_ru_execution还有一条记录表示该流程实例。待技术经理和项目经理任务全部完成,在汇聚点汇聚,通过parallelGateway并行网关。并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。

5.2.2 流程定义
  并行网关图标,红框内:

在这里插入图片描述
5.2.3 测试
  当执行到并行网关数据库跟踪如下:当前任务表:SELECT * FROM act_ru_task

在这里插入图片描述
上图中:有两个任务当前执行。查询流程实例执行表:SELECT * FROM act_ru_execution

在这里插入图片描述

 上图中,说明当前流程实例有多个分支(两个)在运行。

对并行任务的执行:
并行任务执行不分前后,由任务的负责人去执行即可。
执行技术经理任务后,查询当前任务表 SELECT * FROM act_ru_task

在这里插入图片描述

已完成的技术经理任务在当前任务表act_ru_task_已被删除。
在流程实例执行表:SELECT * FROM act_ru_execution有中多个分支存在且有并行网关的汇聚结点。

在这里插入图片描述
有并行网关的汇聚结点:说明有一个分支已经到汇聚,等待其它的分支到达。
当所有分支任务都完成,都到达汇聚结点后:
流程实例执行表:SELECT * FROM act_ru_execution,执行流程实例已经变为总经理审批,说明流程执行已经通过并行网关

在这里插入图片描述

 总结:所有分支到达汇聚结点,并行网关执行完成。

5.3 包含网关InclusiveGateway

5.3.1 什么是包含网关

包含网关可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。
  包含网关的功能是基于进入和外出顺序流的:

l 分支:

  所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。

l 汇聚:

  所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。

5.3.2 流程定义:
  出差申请大于等于3天需要由项目经理审批,小于3天由技术经理审批,出差申请必须经过人事经理审批。
包含网关图标,红框内:

在这里插入图片描述

 定义流程:

 注意:通过包含网关的每个分支的连线上设置condition条件。

5.3.3 测试
  如果包含网关设置的条件中,流程变量不存在,报错;
 

org.activiti.engine.ActivitiException: Unknown property used in expression: ${evection.num>=3}

 需要在流程启动时设置流程变量evection.num。

1)、当流程执行到第一个包含网关后,会根据条件判断,当前要走哪几个分支:

  流程实例执行表:SELECT * FROM act_ru_execution

在这里插入图片描述
第一条记录:包含网关分支。

 后两条记录代表两个要执行的分支:
ACT_ID = “_13” 代表 项目经理神品
ACT_ID = “_5” 代表 人事经理审批
当前任务表:ACT_RU_TASK

在这里插入图片描述

 上图中,项目经理审批、人事经理审批 都是当前的任务,在并行执行。
如果有一个分支执行先走到汇聚结点的分支,要等待其它执行分支走到汇聚。

2)、先执行项目经理审批,然后查询当前任务表:ACT_RU_TASK

在这里插入图片描述

 当前任务还有人事经理审批需要处理。
流程实例执行表:SELECT * FROM act_ru_execution

  发现人事经理的分支还存在,而项目经理分支已经走到ACT_ID = _18的节点。而ACT_ID=__18就是第二个包含网关这时,因为有2个分支要执行,包含网关会等所有分支走到汇聚才能执行完成。

3)、执行人事经理审批
然后查询当前任务表:ACT_RU_TASK

在这里插入图片描述
  当前任务表已经不是人事经理审批了,说明人事经理审批已经完成。 流程实例执行表:SELECT * FROM act_ru_execution

在这里插入图片描述

包含网关执行完成,分支和汇聚就从act_ru_execution删除。

小结:在分支时,需要判断条件,符合条件的分支,将会执行,符合条件的分支最终才进行汇聚。
5.4 事件网关EventGateway

事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。

  事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行", 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件:

事件网关必须有两条或以上外出顺序流;
事件网关后,只能使用intermediateCatchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)
连接到事件网关的中间捕获事件必须只有一个入口顺序流。
5.4.1流程定义

事件网关图标,红框内 

在这里插入图片描述

 intermediateCatchEvent:

 intermediateCatchEvent支持的事件类型:
Message Event: 消息事件
Singal Event: 信号事件
Timer Event: 定时事件

使用事件网关定义流程: 

进阶篇的内容就给大家介绍到这儿了。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【2023最新Activiti7全网最全】4、Activiti7进阶篇 的相关文章

  • lin通信过程的理解

    uart接收和发送消息时 xff0c 都会产生中断 xff0c 即产生中断信号 xff0c 运行一次中断服务函数 中断函数中根据寄存器的标志位 xff0c 首先判断是否收到头部报文 xff0c 再 根据PID判断是lin传送还是lin接受
  • ESKF(error-state Kalman Filter)总结

    写在前面 前段时间看了一下ESKF的相关知识 xff0c VIO中 xff08 VINS xff09 用的还是挺多的 xff0c 特此总结 先验知识 四元数 四元数的基本表示形式 xff0c 这个还是蛮重要的 xff0c 主要有以下几种表示
  • AI-IMU Dead-Reckoning论文总结

    AI IMU Dead Reckoning Martin 论文出处 论文 xff1a https arxiv org abs 1904 06064 代码 xff1a https github com mbrossar ai imu dr 整
  • Unity VR游戏开发干货教程:优化VR体验

    简介 对于VR应用来说 xff0c 如果想要让用户获得好的用户体验 xff0c 特别是免除恶心眩晕的困扰 xff0c 在VR开发中进行优化是必不可少的 xff0c 惟其如此才能达到我们期望的游戏运行帧速 和其它平台上的开发不同 xff0c
  • kali-linux docker桌面版部署

    Dockerfile 构建镜像参考 kali desktop kali docker 桌面版Dockerfile 部署方式 docker run privileged name kali desktop p 5901 5901 p 6080
  • C语言isalpha函数介绍、示例和实现

    文章目录 1 包含头文件2 函数声明3 功能说明4 示例5 自定义的isalpha函数的实现方法6 获取视频教程7 版权声明 isalpha函数用于判断字符是否为字母 xff08 a z和A Z xff09 在本文中 xff0c 我们先来介
  • C语言isalnum函数介绍、示例和实现

    文章目录 1 包含头文件2 函数声明3 功能说明4 示例5 自定义的isalnum函数的实现方法6 获取视频教程7 版权声明 C语言isalnum函数用于判断字符是否为字母 xff08 a z和A Z xff09 或数字 xff08 0 9
  • 企业对C/C++程序员的技能要求

    一个人应该具备对事物的思考能力 xff0c 否则容易被忽悠 对大部分未入门或刚入门的菜鸟来说 xff0c 很难搞明白C语言能做什么和C程序员在做什么这两个问题 如果你打算种菜 xff0c 必须先了解行情 xff08 包括销量和价钱 xff0
  • HBase详解(对hbase集群搭建、读写流程、hbase的javaApi等细致入微的讲解与保姆级的图解)

    学HBase的意义是什么 我本想用MySQL来与HBase作比较 xff0c 但发现他们两者毫无可比性 xff0c 因为两者运用领域不同 xff0c 各自有各自的优点 xff0c 就好比爬山穿登山鞋 xff0c 潜水穿脚蹼一般 一门技术的兴
  • 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“0”不匹配值“2” 原因

    使用VS2012版本引用外部的lib进行编译时候提示 xff1a 1 gt libprotobuf lib common obj error LNK2038 检测到 ITERATOR DEBUG LEVEL 的不匹配项 值 0 不匹配值 2
  • 写给大侄女

    老姑从你上高中开始 xff0c 就想写点东西给大侄女看 xff0c 不过老姑理科出身 xff0c 文笔比较差 不知道该不该提你在学校看手机的事情 xff0c 老姑没有责备你的意思 xff0c 只是和你探讨一下 xff0c 毕竟谁没有年轻的时
  • centos安装lspci工具

    背景 由于centos6 3迷你安装版上没有带lspci工具 在定制内核时 无法用此工具查询硬件相关信息 具体步骤如下 1 下载 pci包 xff1a http www kernel org pub software utils pciut
  • 软件性能测试方法论

    软件性能测试过程详解与案例分析 xff08 段念 编著 xff09 学习笔记三 1 SEI负载测试计划过程 SEI load Testing Planning Process是一个关注于负载测试计划的方法 xff0c 其目标是产生 清晰 易
  • 2011年总结&2012年展望

    再有几天就2012了 xff0c 工作三年多了 xff0c 之前也没写过总结 xff0c 感觉总结这东西写写也挺好的 xff0c 可以思考下过去和未来 xff0c 留个底 xff0c 可以作为日后的对比 xff1b 总结我的2011年 xf
  • Linux查看端口使用状态、关闭端口方法

    前提 xff1a 首先你必须知道 xff0c 端口不是独立存在的 xff0c 它是依附于进程的 某个进程开启 xff0c 那么它对应的端口就开启了 xff0c 进程关闭 xff0c 则该端口也就关闭了 下次若某个进程再次开启 xff0c 则
  • 查找列表中某个值的位置(python)

    p 61 list index value list为列表的名字 value为查找的值 p为value在list的位置 以下内容引自 xff1a http www linuxidc com Linux 2012 01 51638 htm P
  • python 等待一定时间后继续执行其后的程序

    简单示例 xff1a import time print 39 11 39 time sleep 10 print 39 22 39 先打印11 xff0c 等待10秒后 xff0c 打印22
  • Linux下用于查看系统当前登录用户信息的4种方法

    作为系统管理员 xff0c 你可能经常会 xff08 在某个时候 xff09 需要查看系统中有哪些用户正在活动 有些时候 xff0c 你甚至需要知道他 xff08 她 xff09 们正在做什么 本文为我们总结了4种查看系统用户信息 xff0
  • TCP:三次握手,URG、ACK、PSH、RST、SYN、FIN 含义

    TCP SYN ACK FIN RST PSH URG简析 三次握手Three way Handshake 一个虚拟连接的建立是通过三次握手来实现的 1 B gt SYN gt A 假如服务器A和客户机B通讯 当A要和B通信时 xff0c
  • [转载]一次 JMeter 脚本请求错误 HTTP Status 415 的解决笔记

    录制好脚本以后 xff0c 使用 JMeter 打开 xff0c 直接运行测试 xff0c 发现有个 Ajax 提交表单的时候出错了 服务器返回信息如下 xff1a HTTP Status 415 type Status report me

随机推荐