2021全網最全Activiti7教程04(Activiti7進階篇-歡迎收藏)


  本篇重點給大家介紹下Activiti中的高級內容,比如流程實例,個人任務,流程變量,組任務及網關。

全網最詳細Activiti系列文章,強烈建議收藏加關注哦!

Activiti進階篇

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

  事件網關允許根據事件判斷流向。網關的每個外出順序流都要連接到一個中間捕獲事件。 當流程到達一個基於事件網關,網關會進入等待狀態:會暫停執行。與此同時,會為每個外出順序流創建相對的事件訂閱。

  事件網關的外出順序流和普通順序流不同,這些順序流不會真的"執行", 相反它們讓流程引擎去決定執行到事件網關的流程需要訂閱哪些事件。 要考慮以下條件:

  1. 事件網關必須有兩條或以上外出順序流;
  2. 事件網關后,只能使用intermediateCatchEvent類型(activiti不支持基於事件網關后連接ReceiveTask)
  3. 連接到事件網關的中間捕獲事件必須只有一個入口順序流。

5.4.1流程定義

事件網關圖標,紅框內

在這里插入圖片描述

intermediateCatchEvent:
在這里插入圖片描述

intermediateCatchEvent支持的事件類型:
Message Event: 消息事件
Singal Event: 信號事件
Timer Event: 定時事件
在這里插入圖片描述

使用事件網關定義流程:
在這里插入圖片描述

~好了進階篇的內容就給大家介紹到這兒了,如果感覺有幫助歡迎點贊關注加收藏哦 V_V


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM