Activiti7学习(进阶篇)

1. 流程实例

1.1什么是流程实例

流程实例(ProcessInstance)代表流程定义的执行实例

一个流程实例包括了所有的运行节点,我们可以利用这个对象来了解当前流程实例的进度等信息

例如:用户或者程序安装流程定义的内容发起了一个流程,这个就是一个流程实例

Activiti7学习(进阶篇)_第1张图片

1.2 业务管理

流程定义部署在Activiti后,我们就可以在系统中通过Activiti去管理流程的执行,但是如果我们要将我们的 流程实例业务数据 关联,这时我们需要使用到Activiti中预留的 BusinessKey (业务标识) 来关联(就是activiti通过预设定好的流程帮助我们自动管理流程,但是流程实例有时需要和具体的业务挂钩,所以可以使用activiti给我们预留的businessKey)
Activiti7学习(进阶篇)_第2张图片

实现代码

先回顾一下部署流程定义的代码(自己手动的)

@Test
public void test01() {

    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

    Deployment deploy = processEngine.getRepositoryService().createDeployment()
            .addClasspathResource("bpmn/evection.png")
            .addClasspathResource("bpmn/evection.bpmn")
            .name("流程申请单阿")
            .key("evectionah")
            .category("haa")
            .deploy();

    /*
     流程部署的id: 1
     流程部署的name: 流程申请单阿
     流程部署的key: evectionah
     流程部署的category: haa
    */
    log.info("流程部署的id: {}", deploy.getId());
    log.info("流程部署的name: {}", deploy.getName());
    log.info("流程部署的key: {}", deploy.getKey());
    log.info("流程部署的category: {}", deploy.getCategory());

	// 根据流程部署id,去查询刚刚部署的流程定义
    ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery()
            .deploymentId(deploy.getId()).singleResult();

    /*
     流程定义的id: evection2:1:4  - 
     流程定义的key: evection2     - 创建bpmn文件时,点击空白位置时,填入的id
     流程定义的name: 出差申请单1   - 创建bpmn文件时,点击空白位置时,填入的name
    */
    log.info("流程定义的id: {}", processDefinition.getId());
    log.info("流程定义的key: {}", processDefinition.getKey());
    log.info("流程定义的name: {}", processDefinition.getName());

}

开启1个流程实例, 并且指定businessKey(案例中的)

/**
 * 启动流程实例,添加businessKey
 */
@Test
public void test01() {

    // 1.获取ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    
    // 2.获取RuntimeService对象
    RuntimeService runtimeService = processEngine.getRuntimeService();
    
    // 3.启动流程实例
    // (第 1 个参数是:创建bpmn流程图文件时,点击空白位置时,填入的流程定义id)
    // (第 2 个参数是:一般是先把业务数据保存好后, 把业务数据的主键放到这, 将activiti的流程实例与业务关联起来)
    // (这个businessKey保存在act_ru_execution表的BUSINESS_KEY_字段中)
    // (这个businessKey也会保存在act_hi_procinst表的BUSINESS_KEY_字段中)

    ProcessInstance instance = runtimeService.startProcessInstanceByKey("evection", "1001");
    
    // 4.输出processInstance相关属性
    System.out.println("businessKey = " + instance.getBusinessKey());
}

(下图:act_ru_execution表中的BUSINESS_KEY_字段)
Activiti7学习(进阶篇)_第3张图片

1.3 流程实例的挂起和激活

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

全部流程挂起

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

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

/**
 * 全部流程挂起实例与激活
 */
@Test
public void test02() {

    // 1.获取ProcessEngine对象
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

    // 2.获取RepositoryService对象
    RepositoryService repositoryService = engine.getRepositoryService();

    // 3.查询流程定义的对象(根据流程定义key,去查询流程定义对象)
    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
(补充:对某个流程定义挂起后,表act_re_procdef表的SUSPENSION_STATE_字段会修改为2,同时,表act_ru_task表的SUSPENSION_STATE_字段也会修改为2)
Activiti7学习(进阶篇)_第4张图片
然后再去操作对于的流程实例的话(完成该实例的当前任务),会抛如下异常信息

Activiti7学习(进阶篇)_第5张图片

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

Activiti7学习(进阶篇)_第6张图片

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

单个实例挂起

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

/**
  * 单个流程实例挂起与激活
  */
 @Test
 public void test03() {
     
     // 1.获取ProcessEngine对象
     ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
     
     // 2.获取RuntimeService
     RuntimeService runtimeService = engine.getRuntimeService();
     
     // 3.获取流程实例对象(根据流程实例id,去查询指定的流程实例对象)
     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 {
         
         // 激活--》挂起
         // (taskService.createTaskQuery().processInstanceId("10001").active().list(); 查询时, 是不包括挂起的流程实例的)
         // (可以观察到act_ru_task表中SUSPENSION_STATE_字段由1改为了2, 该流程实例对应的任务状态为从激活状态变为了挂起状态)
         runtimeService.suspendProcessInstanceById(id);
         
         System.out.println("流程定义:" + id + ",已挂起");
     }
 }

然后我们可以在数据库中查看到状态的更新
Activiti7学习(进阶篇)_第7张图片

2. 个人任务

2.1 分配任务责任人

2.1.1 固定分配

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

Activiti7学习(进阶篇)_第8张图片
在Properties视图中,填写Assiginee项为任务负责人

2.1.2 表达式分配

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

UEL-value

Activiti7学习(进阶篇)_第9张图片
在assignee中使用流程变量处理
Activiti7学习(进阶篇)_第10张图片
Activiti7学习(进阶篇)_第11张图片
Activiti7学习(进阶篇)_第12张图片
Activiti7学习(进阶篇)_第13张图片
然后我们可以来操作

首先我们需要将定义的流程部署到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表达式对应的赋值信息
Activiti7学习(进阶篇)_第14张图片

UEL-method

Activiti7学习(进阶篇)_第15张图片
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选项
Activiti7学习(进阶篇)_第16张图片

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");
}

Activiti7学习(进阶篇)_第17张图片
Activiti7学习(进阶篇)_第18张图片

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("完成任务");
    }
}

你可能感兴趣的:(记录,学习)