跳至主要內容

Activiti7 进阶笔记

Zenghr大约 10 分钟Activiti7

Activiti7 进阶

提示

涉及 流程定义相关、流程实例相关、任务分配、流程变量、任务候选人、网关等操作

流程定义相关

涉及流程定义信息查询、删除以及对应的资源查询下载等操作

查询流程定义

@Test
public void testDefinitionQuery(){
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取仓库服务
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //获取流程定义集合
    List<ProcessDefinition> processDefinitionList = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("leaveProcess")
            .list();
    //遍历集合
    for (ProcessDefinition definition:processDefinitionList){
        System.out.println("流程定义ID:"+definition.getId());
        System.out.println("流程定义名称:"+definition.getName());
        System.out.println("流程定义key:"+definition.getKey());
        System.out.println("流程定义版本:"+definition.getVersion());
        System.out.println("流程部署ID:"+definition.getDeploymentId());
        System.out.println("====================");
    }
}

删除流程定义

@Test
public void testDeleteDeploy(){
    //流程部署Id
    String deploymentId = "10001";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取仓库服务
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //删除流程定义,如果该流程定义已有流程实例启动则删除时出错
    repositoryService.deleteDeployment(deploymentId);
    //设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
    //repositoryService.deleteDeployment(deploymentId,true);
}

说明:

  1. 如果该流程定义下没有正在运行的流程,则可以用普通删除

  2. 如果该流程定义下存在已经运行的流程,使用普通删除报错,可用级联删除方法将流程及相关记录全部删除。

  3. 项目开发中级联删除操作一般只开放给超级管理员使用.

流程资源下载

我们的流程资源文件已经上传到数据库了,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地

@Test
public void testDownloadResource() throws Exception {
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取仓库服务
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //获取流程定义集合
    List<ProcessDefinition> list = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("leaveProcess")
            .orderByProcessDefinitionVersion() //按照版本排序
            .desc()//降序
            .list();
    //获取最新那个
    ProcessDefinition definition =list.get(0);
    //获取部署ID
    String deploymentId = definition.getDeploymentId();
    //获取bpmn的输入流
    InputStream bpmnInput = repositoryService.getResourceAsStream(
                                        deploymentId,
                                        definition.getResourceName());
    //获取png的输入流
    InputStream pngInput = repositoryService.getResourceAsStream(
                                        deploymentId,
                                        definition.getDiagramResourceName());
    //设置bpmn输入
    FileOutputStream bpmnOutPut = new FileOutputStream("D:/leave.bpmn");
    //设置png输入
    FileOutputStream pngOutPut = new FileOutputStream("D:/leave.png");
    IOUtils.copy(bpmnInput,bpmnOutPut);
    IOUtils.copy(pngInput,pngOutPut);
}

流程实例相关

提示

流程发起之后,目前设定的部门审批人都是李四,李四在审批之前需要看到申请人申请的时间和申请的理由,才能决定是否同意

那么申请人的请假信息【请假时间、请假理由】是如何绑定到流程中的呢?

此时就需要使用到BusinessKey

业务标识(BusinessKey)

  • 启动流程实例时,指定的 businessKey 就会在 act_run_execution 表中存储 businessKey。
  • BusinessKey:业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据。
  • 比如:请假流程启动一个流程实例,就可以将请假单的id作为业务标识存储到Activiti中,将来查询Activiti的流程实例信息就可以获取请假单的id从而关联查询业务系统数据库得到请假单信息,如下图所示👇
hTmP6C
@Test
public void testStartProcess(){
    String businessKey = "8001";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取RuntimeService对象
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 设置对应的参数
    Map<String, Object> variables = new HashMap<String, Object>();
    // 设置部门审核人
    variables.put("deptLeader", "张三");
    //根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的
    //在启动流程的时候将业务key加入进去
    ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("leaveProcess", businessKey, variables);
    //获取流程实例的相关信息
    System.out.println("流程定义的id = " + instance.getProcessDefinitionId());
    System.out.println("流程实例的id = " + instance.getId());
}

获取BusinessKey并关联对应的业务信息

//获取任务集合
List<Task> taskList = taskService.createTaskQuery()
  .processDefinitionKey("leaveProcess")
  .taskAssignee(assignee)
  .list();
//遍历任务列表
for(Task task:taskList){
  System.out.println("流程定义id = " + task.getProcessDefinitionId());
  System.out.println("流程实例id = " + task.getProcessInstanceId());
  System.out.println("任务id = " + task.getId());
  System.out.println("任务名称 = " + task.getName());
  //根据任务上的流程实例Id查询出对应的流程实例对象,从流程实例对象中获取BusinessKey
  ProcessInstance instance = runtimeService
    .createProcessInstanceQuery()
    .processInstanceId(task.getProcessInstanceId())
    .singleResult();
  System.out.println("业务key:"+instance.getBusinessKey());
  System.out.println("===================");
}

流程挂起、激活

@Test
public void testSuspendAllProcessInstance(){
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //获取流程定义对象
    ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("leaveProcess")
            .singleResult();
    boolean suspended = processDefinition.isSuspended();
    //输出流程定义状态
    System.out.println("流程定义状态:"+(suspended ?"已挂起":"已激活"));
    String processDefinitionId = processDefinition.getId();
    if(suspended){
        //如果是挂起,可以执行激活操作 ,参数1 :流程定义id ,参数2:是否激活流程实例,参数3:激活时间
        repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
        System.out.println("流程ID:"+processDefinitionId+",已激活");
    }else{
        //如果是激活,可以执行挂起操作 ,参数1 :流程定义id ,参数2:是否暂停流程实例,参数3:激活时间
        repositoryService.suspendProcessDefinitionById(processDefinitionId,true,null);
        System.out.println("流程ID:"+processDefinitionId+",已挂起");
    }
}
  • 操作流程定义为挂起状态,该操作定义下面的所有的流程实例将全部暂停
  • 流程定义为挂起状态,该流程定义下将不允许启动新的流程实例,同时该流程定义下的所有流程实例将全部挂起暂停执行

流程实例挂起、激活

@Test
public void testSuspendSingleProcessInstance(){
    //流程实例Id
    String processInstanceId = "2501";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取RepositoryService
    RuntimeService runtimeService = processEngine.getRuntimeService();
    //根据流程实例Id获取流程实例对象
    ProcessInstance processInstance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
    //状态
    boolean suspended = processInstance.isSuspended();
    System.out.println("流程实例ID:"+processInstanceId+",状态:"+ (suspended?"已挂起":"已激活"));
    if(suspended){
        runtimeService.activateProcessInstanceById(processInstanceId);
        System.out.println("流程实例ID:"+processInstanceId+",状态修改为已激活");
    }else{
        runtimeService.suspendProcessInstanceById(processInstanceId);
        System.out.println("流程实例ID:"+processInstanceId+",状态修改为已挂起");
    }
}

任务分配负责人

固定分配

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

ASn38W
ASn38W

UEL 表达式分配

Activiti 使用 UEL 表达式, UEL 是 java EE6 规范的一部分, UEL(Unified Expression Language)即 统一表达式语言

posPxS
posPxS

监听器分配(使用麻烦,极少使用)

  • 任务监听器是发生对应的任务相关事件时执行自定义的Java逻辑或表达式
  • 任务相关事件包括:
    • Event:
      • Create:任务创建后触发。
      • Assignment:任务分配后触发。
      • Delete:任务完成后触发。
      • All:所有事件发生都触发。

1. 自定义一个任务监听器类,然后此类必须实现org.activiti.engine.delegate.TaskListener接口

public class MyTaskListener implements TaskListener {
  @Override
  public void notify(DelegateTask delegateTask) {
    //这里指定任务负责人
    if (delegateTask.getName().equals("部门经理审批")) {
      delegateTask.setAssignee("赵六");
    } else if (delegateTask.getName().equals("部门经理审批")) {
      delegateTask.setAssignee("孙七");
    }
  }
}

2. 在bpmn文件中配置监听器

upZLYx
upZLYx

流程变量

提示

流程变量在Activiti中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和Activiti结合时少不了流程变量,流程变量就是Activiti在管理工作流时根据管理需要而设置的变量

比如在请假流程流转时如果 请假天数>3 天则有总经理审批,否则由人事直接审批,请假天数就可以设置流程变量,在流程流转时使用

流程变量类型

JdYxvc
JdYxvc

注意:如果将POJO存储到流程变量中,必须实现序列化接口Serializable,为了防止由于新增字段无法反序列化

流程变量的作用域

流程变量的作用域范围可以是一个流程实例(ProcessInstance)、一个任务(Task)或一个执行实例(Execution)

  • global变量 : 流程变量的作用域范围的默认值是流程实例,作用域范围最大。
  • local变量 : 流程变量的作用域范围如果仅仅针对一个任务或一个执行实例,那么作用域范围没有流程实例大

实际开发中一般不用local变量,了解即可

流程变量的使用方法

  • 在属性上使用UEL表达式

    可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。

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

  • 在连线上使用UEL表达式

    可以在连线上设置UEL表达式,决定流程走向。

    比如:${price<10000} price就是一个流程变量名称,uel表达式结果类型为布尔类型。

    如果UEL表达式是true,要决定 流程执行走向

使用global变量控制流程

在连线处添加判断条件

eI0vjg
eI0vjg
@Test
public void testStartProcess(){
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取RuntimeService对象
    RuntimeService runtimeService = processEngine.getRuntimeService();
    Map<String,Object> variables = new HashMap<String,Object>();
    variables.put("day",2);
    //根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的
    ProcessInstance instance = runtimeService.startProcessInstanceByKey("leaveVariablesProcess",variables);
    //获取流程实例的相关信息
    System.out.println("流程定义的id = " + instance.getProcessDefinitionId());
    System.out.println("流程实例的id = " + instance.getId());
}

执行task 设置流程变量

taskService.setVariable(taskId,variableName,variableValue)

  • taskId - 任务 ID
  • variableName - 变量名称
  • variableValue - 变量实际的值

注意

1.如果UEL表达式中流程变量名不存在则报错。
2.如果如果UEL表达式都不符合条件,流程报错。
3.如果连接不设置条件/条件都满足,每个连线都会走

任务候选人

在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。

针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务

设置任务候选人

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

YywESD
YywESD

使用 Api 设置候选人

taskService.addCandidateUser(taskId,candidateUser)

  • taskId: 任务 Id
  • candidateUser: 候选人

查询候选人任务

//查询候选任务
@Test
public void testSelectCandidateTaskList(){
    //任务负责人
    String candidateUser = "李四";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取TaskService
    TaskService taskService = processEngine.getTaskService();
    //获取任务集合
    List<Task> taskList = taskService.createTaskQuery()
            .processDefinitionKey("leaveCandidateProcess")
            .taskCandidateUser(candidateUser)
            .list();
    //遍历任务列表
    for(Task task:taskList){
        System.out.println("流程定义id = " + task.getProcessDefinitionId());
        System.out.println("流程实例id = " + task.getProcessInstanceId());
        System.out.println("任务id = " + task.getId());
        System.out.println("任务名称 = " + task.getName());
    }
}

注意

注意:在 SpringBoot 集成 Activiti7 中,使用该 Api 查询候选人时会抛出异常,异常详细以及解决方案请看 Activiti7 填坑笔记

领取、完成候选人任务

如果候选任务没有进行领取就直接完成的话,那么在历史记录中就不会记录是哪个用户执行了这个任务

所以对于这种候选人的任务,我们需要先领取再完成

@Test
public void testClaimTask(){
    //任务ID
    String taskId = "2505";
    String assignee = "张三";
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取TaskService
    TaskService taskService = processEngine.getTaskService();
    //领取任务
    taskService.claim(taskId,assignee);
    // 完成任务
  	taskService.complete(taskId);
}

网关相关

排他网关

排他网关(ExclusiveGateway)(异或网关或基于数据的排他网关),用来在流程中实现决策。当流程执行到这个网关的时候,所有分支都会判断条件是否为true,如果为true则执行该分支

fdgdQz
fdgdQz

并行网关

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

并行网关不会解析条件。即使顺序流中定义了条件,也会被忽略

J4KD0z
J4KD0z

包含网关

包含网关可以看做是排他网关和并行网关的结合体

bVI3wP
bVI3wP