activiti學習筆記二

_ME發表於2020-06-26

 

 上一篇文章大概講了下什麼是流程引擎,為什麼我們要用流程引擎,他的基本原理是啥,以及怎麼進行基本的使用,這篇文章我們再講下其他的一些使用。

 

刪除流程部署
package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;

/**
 *  刪除流程部署
 *      影響了三張表:
 *          act_ge_bytearray
 *          act_re_deployment
 *          act_re_procdef
 *          歷史表資訊會被保留,如果級聯刪除,會把全部記錄刪除
 * */
public class ActivitiDeleteProcessDefinition {

    public static void main(String[] args) {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice例項
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、查詢流程部署
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4.1、刪除流程定義
        // repositoryService.deleteDeployment(processDefinition.getDeploymentId());

        // 4.2、如果還有未結束的流程節點,可以使用級聯刪除(true)
        // deleteDeployment(String deploymentId, boolean cascade)
        repositoryService.deleteDeployment(processDefinition.getDeploymentId(),true);
    }
}

 

刪除流程例項
package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import java.util.List;

/**
 *  刪除流程例項
 *  歷史表資訊會被保留
 * */
public class ActivitiDeleteProcessInstance {

    public static void main(String[] args) {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        RuntimeService runtimeService = processEngine.getRuntimeService();
        TaskService taskService = processEngine.getTaskService();

        Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult();

        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).list();

        for (ProcessInstance processInstance : processInstanceList) {
            runtimeService.deleteProcessInstance(processInstance.getId(),"刪除流程例項");
        }

        System.err.println("ok");
    }
}

 

如果流程例項刪除了,那這次的流程就不用再繼續執行了。act_ru_* 表的相關記錄都刪除

 

掛起/啟用流程定義:

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;

/**
 *  掛起全部流程例項,使其無法啟動新的流程例項,未執行完的流程例項也不允許繼續執行
 *
 * */
public class ActivitiSuspendAllProcessInstance {
    public static void main(String[] args) {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到repositoryService物件
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、查詢流程定義的物件
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4、得到當前流程定義的例項是否都為暫停狀態
        boolean suspended = processDefinition.isSuspended();

        // 5、判斷
        if (suspended) {
            // 5.1、如果是暫停,那就全部啟用
            repositoryService.activateProcessDefinitionById(processDefinition.getId(),true,null);
            System.err.println("流程定義:" + processDefinition.getId() + "啟用");
        }else {
            // 5.2、如果是活動狀態,那就全部掛起
            repositoryService.suspendProcessDefinitionById(processDefinition.getId(),true,null);
            System.err.println("流程定義:" + processDefinition.getId() + "掛起");
        }
    }
}

 

掛起流程定義後,就不能再建立流程例項了,同時未完成的流程例項、流程任務也無法繼續完成。

 

掛起/啟用單個流程例項:

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

/**
 *  掛起指定一個流程例項,使其無法續執行。
 *  給流程定義依舊可以發起新的流程例項,其他流程例項不受影響
 *
 *  和掛起全部區別:
 *      掛起全部是針對流程定義來的,當流程定義被掛起後,該流程定義下的所有流程例項都自動被掛起
 *      掛起一個是針對具體的流程例項來的,只掛起一個流程例項,並不會影響整個流程定義,也不會影響其他流程例項
 * */
public class ActivitiSuspendSingleProcessInstance {
    public static void main(String[] args) {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到runtimeService物件
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、查詢流程例項的物件
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("holiday")
                .singleResult();

        // 4、得到當前流程例項的例項是否都為暫停狀態
        boolean suspended = processInstance.isSuspended();

        // 5、判斷
        if (suspended) {
            // 5.1、如果是暫停,那就全部啟用
            runtimeService.activateProcessInstanceById(processInstance.getId());
            System.err.println("流程例項定義id:" + processInstance.getId() + "啟用");
        }else {
            // 5.2、runtimeService,那就全部掛起
            runtimeService.suspendProcessInstanceById(processInstance.getId());
            System.err.println("流程例項定義id:" + processInstance.getId() + "掛起");
        }
    }
}

 

 

activiti流程繫結業務記錄(BusinessKey):

package activiti03;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

/**
 *  activiti與自定義業務結合。
 *  原理就是在啟動activiti流程例項的時候,傳一個業務流程資料的唯一id進來。
 *  也就是說,activiti只是負責管理流程的推進,業務資料則是由我們自己來進行處理。
 *  兩者通過act_ru_execution表的business_key來進行關聯
 * */
public class ActivitiBusinessKey {
    public static void main(String[] args) {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService物件
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、得到流程例項,需要直到流程定義的key,也就是流程process檔案的Id,可以在bpmn裡面檢視,也可以在資料庫act_re_procdef找到該流程的key
        // startProcessInstanceByKey(String processDefinitionKey, String businessKey)
        // 假設我們的業務請假id為1001
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday","1001");

        // 4、輸出相關資訊
        System.out.println("流程部署id  ===>  "+processInstance.getDeploymentId());
        System.out.println("流程例項id  ===>  "+processInstance.getProcessInstanceId());
        System.out.println("活動id  ===>  "+processInstance.getActivityId());
        System.out.println("business_key  ===>  "+processInstance.getBusinessKey());
    }
}

 

其實也就是在啟動流程例項的時候,就繫結一個我們自己的業務key,這樣以後我們就可以根據這個businessKey來我們自己的表查詢相關業務

 

查詢歷史節點記錄:

package activiti02;

import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;

import java.util.List;

/**
 *  歷史資料查詢
 * */
public class ActivitiHistoryQuery {

    public static void main(String[] args) {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到HistoryService例項
        HistoryService historyService = processEngine.getHistoryService();

        // 3、獲得HistoricActivityInstanceQuery物件,一個查詢器
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();

        // 4、設定條件,並查詢
        HistoricActivityInstanceQuery historicActivity = historicActivityInstanceQuery.processInstanceId("12501");
        List<HistoricActivityInstance> historicActivityInstanceList = historicActivity.orderByHistoricActivityInstanceStartTime()
                .asc().list();

        // 5、輸出流程定義
        for (HistoricActivityInstance processDefinition : historicActivityInstanceList) {
            System.out.println("節點id==》"+processDefinition.getActivityId());
            System.out.println("節點名==》"+processDefinition.getActivityName());
            System.out.println("流程定義id==》"+processDefinition.getProcessDefinitionId());
            System.out.println("流程例項id==》"+processDefinition.getProcessInstanceId());
            System.out.println();
        }
    }
}

 

 

查詢流程定義:

package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;

import java.util.List;

public class ActivitiQueryProcessDefinition {

    public static void main(String[] args) {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice例項
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、獲得ProcessDefinitionQuery物件,一個查詢器
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

        // 4、設定條件,並查詢出當前的所有流程定義
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()      // 設定排序
                .desc().list();

        // 5、輸出流程定義
        for (ProcessDefinition processDefinition : processDefinitionList) {
            System.out.println("流程定義id==》"+processDefinition.getId());
            System.out.println("流程定義key==》"+processDefinition.getKey());
            System.out.println("流程定義名==》"+processDefinition.getName());
            System.out.println("流程定義version==》"+processDefinition.getVersion());
            System.out.println("流程部署id==》"+processDefinition.getDeploymentId());
        }
    }
}

 

 

匯出流程檔案bpmn和圖片:

package activiti02;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.IOUtils;

import java.io.*;

/**
 *  需求:
 *      1、從activiti的act_ge_bytearray讀取兩個資原始檔
 *      2、將這兩個資原始檔放到指定目錄
 *
 *  實現方案:
 *      1、使用activiti自帶的api,呼叫IO流轉換,輸出到磁碟
 *      2、使用jdbc的對blob型別、clob型別資料的讀取,並 呼叫IO流轉換,輸出到磁碟
 *
 * */
public class DownloadBPMNFile {
    public static void main(String[] args) throws IOException {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice例項
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、得到查詢器ProcessDefinitionQuery物件
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

        // 4、設定查詢條件
        processDefinitionQuery.processDefinitionKey("holiday");

        // 5、執行查詢操作,得到想要的流程定義
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();

        // 6、通過流程定義資訊,得到部署id
        String deploymentId = processDefinition.getDeploymentId();

        // 7、通過 RepositoryService 的方法,實現讀取圖片資訊以及bpmn檔案資訊(輸入流)
        // getResourceAsStream(String deploymentId, String resourceName)
        // processDefinition.getDiagramResourceName():獲取流程圖片名字
        InputStream pngStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
        // processDefinition.getResourceName():獲取流程bpmn檔名字
        InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId,processDefinition.getResourceName());

        // 8、構建輸出流
        OutputStream pngOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getDiagramResourceName());
        OutputStream bpmnOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getResourceName());

        // 9、轉換輸入流並儲存檔案
        IOUtils.copy(pngStream,pngOutputStream);
        IOUtils.copy(bpmnStream,bpmnOutputStream);

        // 10、關閉流
        pngOutputStream.close();
        bpmnOutputStream.close();
        pngStream.close();
        pngStream.close();
    }
}

 

 

流程變數

package activiti04;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;

import java.io.Serializable;
import java.util.HashMap;

/**
 *  啟動流程例項,流程節點參與人使用UEL表示式 ${變數名}  ${變數名.屬性名}   ${變數名.方法名()}
 * */
public class ActivitiStartInstanceUEL {
    public static void main(String[] args) {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService物件
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、設定assignee的值,   流程節點的參與人可以動態選擇
        HashMap<String, Object> map = new HashMap<>();
        map.put("assignee","zhaoliu");
        map.put("user",new User("蘇七","666","suqi"));

        // 4、啟動流程例項,同時傳入流程定義的引數值
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday3", map);

        // 5、輸出相關資訊
        System.out.println(processInstance.getProcessDefinitionName());
    }
}

@Getter
@Setter
@AllArgsConstructor
class User implements Serializable {
    private String name;
    private String id;
    private String assignee;

    public String getUserAssignee(){
        return id + name + assignee;
    }
}

 

流程變數是指在整個流程例項中使用的變數,activiti支援UEL表示式作為流程變數。

UEL表示式有三種寫法: ${變數名}       ${變數名.屬性名}        ${變數名.方法名()}

另外,流程變數分成全域性流程變數和區域性流程變數。

流程變數分為:

  • 全域性global變數,所有節點都可以用。
  • 任務節點local變數,僅當前節點可用

全域性的流程變數可以通過流程例項來新增,也可以通過任務節點來新增
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday4", map);
  runtimeService.setVariable(String processInstanceId, String variableName, Object value);
  runtimeService.setVariables( String processInstanceId, Map<String, ? extends Object> variables);

  taskService.complete(String taskId, Map<String, Object> variables)
  taskService.setVariables(String taskId, Map<String, ? extends Object> variables)
  taskService.setVariable(String taskId, String variableName, Object value)

 

區域性流程變數:
  只能在當前任務節點使用,一旦當前任務節點結束,這個流程變數就會被刪除。
  但是歷史表裡面依舊還是會儲存我們的區域性流程變數,依舊還是可以獲取到。只是當前正在執行的任務不能直接獲取區域性流程變數了而已,曲線獲取還是可以的。

區域性流程變數的設定:
  runtimeService.setVariableLocal(String processInstanceId, String variableName, Object value);
  runtimeService.setVariablesLocal( String processInstanceId, Map<String, ? extends Object> variables);

  taskService.setVariablesLocal(String taskId, Map<String, ? extends Object> variables)
  taskService.setVariableLocal(String taskId, String variableName, Object value)

注意:如果區域性變數是加在流程例項上,那依舊還是把所有任務節點共享

 

下面給一個使用流程變數來確定動態指定任務責任人的例子:

 

 

 

 

 

 

 

 

 

 

 

 流程變數的使用大致就是這樣子。

 我們可以在資料庫act_ru_variable、act_hi_varinst、act_ge_bytearray找到相關資訊

 

 

 

 

 

 

任務監聽器:

 

 

 

 

 

 

 

 

 

任務監聽器是在發生對應的任務相關事件時執行的自定義java邏輯或表示式。

有四種型別的任務監聽器,好像只是用在task 節點上才有用:

  • create:任務建立後觸發
  • assignment:任務分配後觸發
  • complete:任務完成後觸發
  • all:所有任務事件都會觸發


監聽器的執行者有四種:

  • java類:必須實現org.activiti.engine.delegate.TaskListener這個介面
  • Expression 表示式:
  • Delegate Expression 表示式:
  • script指令碼

 

 

 我們可以在監聽器裡面改變任務處理人,也可以進行其他操作

 

 

動態選擇任務節點的處理人:

參考前面流程變數時最後舉得那個例子

 

簡單的任務分支:

 

 

 

 

 

 

需要用到流程變數,流程變數可以是UEL表示式,但條件必須是boolean型別。


注意:

  1. 如果UEL表示式中流程變數名不存在會報錯
  2. 如果UEL表示式中流程變數為空null,流程不按UEL表示式執行,直接流程結束
  3. 如果UEL表示式都不符合條件,流程結束
  4. 如果任務連線不設定條件,會走flow序號最小的那天=條路徑
  5. 這種簡單的分支下,如果多條分支的條件都成立時,會多條分支一起走,會出現問題。所以分支一般用閘道器

 

這種分支方式很簡單,但也有一些限制,如果不是很複雜的流程,可以這樣使用,但還是建議使用閘道器。

 

 

 flow序號其實就是線的id

 

 

任務候選人/任務候選組:

 

我們之前指定任務都是使用assignee來指定具體哪一個人來執行任務,但是如果這個任務可以有多個執行人,也就是說張三、李四、王五、趙六四個人都可以執行同一個任務,那麼就不能通過assignee來指定了。

此時就可以用candidate user(候選人)來指定一堆執行人,將來這一堆執行人都可以看到這個任務,但是隻能有一個人將這個任務領取並執行。多個候選人之間通過逗號隔開

另外也有一個candidate groups(候選組)來指定一個人員組,在這個組裡的人都是候選人,就不用一個個人來指定了。候選組其實也就是一個角色


組任務的辦理流程:

  1、指定候選人,或指定候選人組部署流程

  2、建立流程例項
  3、候選人查詢當前待辦任務
  4、候選人領取任務,將組任務變成個人任務
    ***如果該候選人領取任務後又不想處理了,可以將任務歸還,個人任務變成組任務
  5、根據assignee來查詢自己的任務
  6、處理任務

taskService.claim(task.getId(),任務負責人);領取任務
taskService.setAssignee(task.getId(),null);放棄任務
taskService.setAssignee(task.getId(),任務負責人);委派任務


在我們正常的業務中,

assignee應該是我們的系統使用者標識,可以是使用者id,也可以是使用者名稱,反正能唯一區分就行。其意思就是這個任務指定某個人完成,一般都是使用流程變數,動態決定誰可以做。例如我們在用釘釘發起審批時,有時就可以指定給哪個領導審。

candidate user(候選人)應該是多個系統使用者標識,其意思就是這個任務可以是這堆人中的某一個來完成。

candidate groups(候選組)應該是使用者的角色,在正常的系統中,使用角色將使用者進行分組。而我們的實際生活中也是這樣,有領導角色身份的人,才能進行最後的審批決定。

package activiti04.group;

import activiti01.ActivitiDeployment;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 *  組任務
 * */
public class ActivitiTaskGroup {
    public static void main(String[] args) {
        ActivitiTaskGroup activitiTaskGroup = new ActivitiTaskGroup();
        activitiTaskGroup.giveUpTask();
    }


    /**
     * 放棄任務。
     * 原理:
     *      直接將task的assignee設定成null,讓候選人重新搶任務。
     *      也可以將任務指定給某個具體的使用者,相當於委派給別人執行
     *  注意:
     *      如果這個任務不是指定候選組的,那麼當這個人放棄後,將沒有人可以再領取這個任務
     */
    @Test
    public void giveUpTask() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();

        // 3、查詢使用者的組任務
        String userId = "hongcheng";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskAssignee(userId).list();

        // 4、放棄任務
        for (Task task : taskList) {
            System.err.println("流程例項id ==> " + task.getProcessInstanceId());
            System.err.println("任務定義key ==> " + task.getTaskDefinitionKey());
            System.err.println("任務id ==> " + task.getId());
            System.err.println("任務處理人 ==> " + task.getAssignee());
            System.err.println("任務名 ==> " + task.getName());
            if (userId.equals(task.getAssignee())) {
                // 放棄任務
                taskService.setAssignee(task.getId(), null);
                // 任務委派
//                taskService.setAssignee(task.getId(),"hongcheng");
            }
            System.err.println("任務放棄成功 ==> " + userId);
        }
    }



    /**
     *  認領組任務,claim也可以說是對外宣稱任務
     * */
    @Test
    public void claimGroupTaskCandidateUserGroup() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();

        // 3、查詢使用者的組任務
        String userId = "hongcheng";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateGroup("leader").list();

        // 4、認領任務
        for (Task task : taskList) {
            System.err.println("流程例項id ==> "+task.getProcessInstanceId());
            System.err.println("任務定義key ==> "+task.getTaskDefinitionKey());
            System.err.println("任務id ==> "+task.getId());
            System.err.println("任務處理人 ==> "+task.getAssignee());
            System.err.println("任務名 ==> "+task.getName());
            // 認領任務
            taskService.claim(task.getId(),userId);
            System.err.println("任務認領成功 ==> "+userId);

        }
    }


    /**
     *  查詢組任務,也就是候選人有自己的任務,此時該組任務並沒有真正確定誰是任務的最終負責人assignee
     *  按候選人組進行查詢
     * */
    @Test
    public void queryGroupTaskCandidateUserGroup() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();

        // 3、查詢使用者的組任務
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateGroup("leader").list();

        // 5、輸出
        for (Task task : taskList) {
            System.err.println("流程例項id ==> "+task.getProcessInstanceId());
            System.err.println("任務定義key ==> "+task.getTaskDefinitionKey());
            System.err.println("任務id ==> "+task.getId());
            System.err.println("任務處理人 ==> "+task.getAssignee());
            System.err.println("任務名 ==> "+task.getName());
        }
    }


    /**
     *  認領組任務,claim也可以說是對外宣稱任務
     * */
    @Test
    public void claimGroupTaskCandidateUser() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();

        // 3、查詢使用者的組任務
        String userId = "wangwu";
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateUser(userId).list();

        // 4、認領任務
        for (Task task : taskList) {
            System.err.println("流程例項id ==> "+task.getProcessInstanceId());
            System.err.println("任務定義key ==> "+task.getTaskDefinitionKey());
            System.err.println("任務id ==> "+task.getId());
            System.err.println("任務處理人 ==> "+task.getAssignee());
            System.err.println("任務名 ==> "+task.getName());
            // 認領任務
            taskService.claim(task.getId(),userId);
            System.err.println("任務認領成功 ==> "+userId);

        }
    }





    /**
     *  查詢組任務,也就是候選人有自己的任務,此時該組任務並沒有真正確定誰是任務的最終負責人assignee
     *  按候選人列表進行查詢
     * */
    @Test
    public void queryGroupTaskCandidateUser() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();

        // 3、查詢使用者的組任務
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup")
                .taskCandidateUser("lisi").list();

        // 5、輸出
        for (Task task : taskList) {
            System.err.println("流程例項id ==> "+task.getProcessInstanceId());
            System.err.println("任務定義key ==> "+task.getTaskDefinitionKey());
            System.err.println("任務id ==> "+task.getId());
            System.err.println("任務處理人 ==> "+task.getAssignee());
            System.err.println("任務名 ==> "+task.getName());
        }
    }



    /**
     *  完成任務
     * */
    @Test
    public void complete() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();
        // 3、結合任務查詢,將查詢到的任務進行處理
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey("holidayGroup")
                .taskAssignee("zhangsan")
                .list();
        // 4、完成任務
        for (Task task : taskList) {
            taskService.complete(task.getId());
            System.err.println(task.getName());
        }
    }

    /**
     *  查詢自己的個人任務
     * */
    @Test
    public void taskQuery() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 2、得到TaskService物件
        TaskService taskService = processEngine.getTaskService();
        // 3、用流程定義的key和負責人assignee來實現當前使用者的任務列表查詢
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey("holidayGroup").taskAssignee("zhangsan")
                .list();
        // 4、任務列表查詢
        for (Task task : taskList) {
            System.err.println("流程例項id ==> "+task.getProcessInstanceId());
            System.err.println("任務定義key ==> "+task.getTaskDefinitionKey());
            System.err.println("任務id ==> "+task.getId());
            System.err.println("任務處理人 ==> "+task.getAssignee());
            System.err.println("任務名 ==> "+task.getName());
        }
    }

    /**
     *  啟動流程
     * */
    @Test
    public void startInstance() {
        // 1、得到processEngine物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到RuntimeService物件
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 3、設定assignee的值,   流程節點的參與人可以動態選擇
        HashMap<String, Object> map = new HashMap<>();

        // 4、啟動流程例項,同時傳入流程定義的引數值
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayGroup", map);

        // 5、輸出相關資訊
        System.out.println(processInstance.getProcessDefinitionName());
        System.out.println(processInstance.getId());
    }


    /**
     *  部署流程
     * */
    @Test
    public void deployment() {
        // 1、建立ProcessEngine流程引擎物件
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 2、得到Repositoryervice例項
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 3、獲取壓縮檔案
        InputStream inputStream = ActivitiDeployment.class.getClassLoader().getResourceAsStream("bpmn/group/holidayGroup.zip");

        // 4、建立一個ZipInputStream流
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        // 3、進行部署,bpmn檔案是一定要的,圖片檔案可以沒有,流程key相同的話,會使用最新部署的流程定義
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .name("請假申請流程候選人")
                .deploy();

        // 4、輸出部署的資訊
        System.out.println(deployment.getName());
        System.out.println(deployment.getId());
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd
="http://www.w3.org/2001/XMLSchema"
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"
typeLanguage
="http://www.w3.org/2001/XMLSchema"
expressionLanguage
="http://www.w3.org/1999/XPath"
targetNamespace
="http://www.activiti.org/test"> <process id="holidayGroup" name="候選人流程" isExecutable="true"> <startEvent id="startevent1" name="Start"></startEvent> <userTask id="usertask1" name="填寫請假申請" activiti:assignee="zhangsan"></userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> <userTask id="usertask2" name="部門經理稽核" activiti:candidateUsers="lisi,wangwu"></userTask> <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> <userTask id="usertask3" name="總經理稽核" activiti:candidateGroups="leader"></userTask> <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_holidayGroup"> <bpmndi:BPMNPlane bpmnElement="holidayGroup" id="BPMNPlane_holidayGroup"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="110.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> <omgdc:Bounds height="55.0" width="105.0" x="190.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="340.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="145.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="190.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="295.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="340.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="445.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="490.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="595.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>

 

 

閘道器:

閘道器其實就是控制流程進行分支、匯聚的一個節點

activiti裡面閘道器有4種:

  • 排他閘道器
  • 並行閘道器
  • 包含閘道器
  • 事件閘道器

和簡單任務分支相比就是多個了閘道器節點,其他東西不變。

 

排他閘道器:

  • 只會執行一條路徑,如果有多條路徑可以走,那麼預設會走下一個節點的id比較小的那條路徑。可以防止多條分支同時成立,同時走了多條分支。
  • 如果沒有一個分支可以走,那就會報錯。
  • 必須保證有一條路徑可以走

並行閘道器:

  • 可以作為多條分支匯聚(只有所有匯聚分支都完成,才會進去下一步)。
  • 也可以作為多條分支分散,分散分支時沒有條件的,即便你設定了條件,也會被忽略掉,他肯定是全部分支都要執行。

 

 

 

包含閘道器:

  • 同時擁有排他閘道器和並行閘道器的特性。也就是說根據條件分散執行條件成立的一個或多個分支,也可以等待一個或多個分支匯聚。

針對上圖,當userType==1時,會同時走常規專案和抽血化驗這兩條路徑,如果userType==2,會走三條路徑

 

任務定時提醒:

我們實際的流程中,經常會有人一直不審批,所以就需要有一個提醒機制

這個時候我們就需要用到邊界事件。有3篇部落格介紹的挺不錯的,有興趣的自己去看:

https://blog.csdn.net/qq_33333654/article/details/101374202

https://blog.csdn.net/qq_33333654/article/details/101373157

https://www.cnblogs.com/dengjiahai/p/6942310.html

iso 8601格式解析

R2/2015-06-04T19:25:16.828696-07:00/P1DT10S

上面的字串通過"/"分為了三部分即:

重複次數/開始時間/執行間隔

重複次數

  • R - 將永遠重複
  • R1 - 將重複一次
  • R231 - 將重複231次。

開始時間

任務第一次執行的時間。如果開始日期時間已經過去,將返回一個錯誤。

其中"T"用來分割日期和時間,時間後面跟著的"-07:00"表示西七區,注意"-"是連字元,不是減號。

時區預設是0時區,可以用"Z"表示,也可以不寫。

對於我國,要使用"+08:00",表示東八區。
上面的字串表示 2015年6月4日,19點25分16秒828696納秒,西七區。

執行間隔

執行間隔以"P"開始,和上面一樣也是用"T"分割日期和時間,如P1Y2M10DT2H30M15S

  • P 開始標記
  • 1Y - 一年
  • 2M - 兩個月
  • 10D - 十天
  • T - 時間和日期分的割標記
  • 2H - 兩個小時
  • 30M - 三十分鐘
  • 15S 十五秒鐘

例子,注意如果沒有年月日,"T"也不能省略

  • P1DT1M - 一天一分鐘執行一次
  • P1W - 一週執行一次
  • PT1H - 一小時執行一次
  • PT10S - 十秒執行一次

Time duration:延遲時間,使用“執行間隔”的格式,例如:PT1M30S,一分30秒後執行。

Time date:可以是java.util.Date的變數,也可以是符合ISO 8601 的一個時間點字串

Time cycle:格式:“迴圈次數/執行間隔”,例如:R3/P1DT3H,迴圈執行3次,每次間隔1天3小時。這裡也可以使用cron表示式。

cancel activiti:是否取消當前節點任務,true表示只要超時了,就取消這個當前節點任務。

 

原理:activiti會在act_ru_timer_job表增加一條記錄,到指定的時間時,會讀取這條記錄,同時判斷這個任務完成沒有,如果沒有完成,就會執行TimerBoundaryEvent連著的下一個任務。如果制定了要取消當前任務,就會把當前任務取消。

 

 注意:想要啟動定時器,加上這段配置
 

  注意:想要啟動定時器,加上這段配置

 

 

Service Task服務任務:

之前我們都是使用user task,那這兩者有啥區別呢?

user task是我們的使用者自定義任務,需要我們自己來手動完成

但是service task是服務任務,只要進入了這個任務,系統就會自動執行,不需要我們手動執行。

我們上個例子就用了service task來做迴圈提醒

 

 

 

 需要注意的是,你得實現

org.activiti.engine.delegate.JavaDelegate這個介面

 

流程駁回:

我們的實際中的流程經常會被打回,這種情況下我個建議使用排他閘道器進行分支。當然,網上也有其他的方式進行撤回,但我還是感覺這樣子更簡單點

 

相關文章