1. 程式人生 > 實用技巧 >activiti學習筆記二

activiti學習筆記二

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

刪除流程部署
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這個介面

流程駁回:

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