上一篇文章大概講了下什麼是流程引擎,為什麼我們要用流程引擎,他的基本原理是啥,以及怎麼進行基本的使用,這篇文章我們再講下其他的一些使用。
刪除流程部署
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型別。
注意:
- 如果UEL表示式中流程變數名不存在會報錯
- 如果UEL表示式中流程變數為空null,流程不按UEL表示式執行,直接流程結束
- 如果UEL表示式都不符合條件,流程結束
- 如果任務連線不設定條件,會走flow序號最小的那天=條路徑
- 這種簡單的分支下,如果多條分支的條件都成立時,會多條分支一起走,會出現問題。所以分支一般用閘道器
這種分支方式很簡單,但也有一些限制,如果不是很複雜的流程,可以這樣使用,但還是建議使用閘道器。
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這個介面
流程駁回:
我們的實際中的流程經常會被打回,這種情況下我個建議使用排他閘道器進行分支。當然,網上也有其他的方式進行撤回,但我還是感覺這樣子更簡單點