組任務及網關
組任務
Candidate-users
候選人
在流程定義中在任務結點的 assignee
固定設置任務負責人,在流程定義時將參與者固定設置在.bpmn
文件中,如果臨時任務負責人變更則需要修改流程定義,系統可擴展性差。
針對這種情況可以給任務設置多個候選人,可以從候選人中選擇參與者來完成任務。
設置任務候選人
在流程圖中任務節點的配置中設置 candidate-users
(候選人),多個候選人之間用逗號分開。
查看bpmn
文件:
我們可以看到部門經理的審覈人已經設置爲 zhangsan,lishi
這樣的一組候選人,可以使用activiti:candiateUsers=”用戶 1,用戶 2,用戶 3”
的這種方式來實現設置一組候選人。
辦理組任務
- 第一步:查詢組任務
指定候選人,查詢該候選人當前的待辦任務。
候選人不能辦理任務。 - 第二步:拾取(
claim
)任務
該組任務的所有候選人都能拾取。將候選人的組任務,變成個人任務。原來候選人就變成了該任務的負責人。- 如果拾取後不想辦理該任務
需要將已經拾取的個人任務歸還到組裏邊,將個人任務變成了組任務。
- 如果拾取後不想辦理該任務
- 第三步:查詢個人任務
查詢方式同個人任務部分,根據 assignee 查詢用戶負責的個人任務。 - 第四步:辦理個人任務
用戶查詢組任務
根據候選人查詢組任務
@Test
public void findGroupTaskList() {
// 流程定義key
String processDefinitionKey = "holiday4";
// 任務候選人
String candidateUser = "zhangsan";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
//查詢組任務
List<Task> list = taskService.createTaskQuery()//
.processDefinitionKey(processDefinitionKey)//
.taskCandidateUser(candidateUser)//根據候選人查詢
.list();
for (Task task : list) {
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());
}
}
用戶拾取組任務
候選人員拾取組任務後該任務變爲自己的個人任務。
@Test
public void claimTask(){
TaskService taskService = processEngine.getTaskService();
//要拾取的任務id
String taskId = "6302";
//任務候選人id
String userId = "lisi";
//拾取任務
//即使該用戶不是候選人也能拾取(建議拾取時校驗是否有資格)
//校驗該用戶有沒有拾取任務的資格
Task task = taskService.createTaskQuery()//
.taskId(taskId)
.taskCandidateUser(userId)//根據候選人查詢
.singleResult();
if(task!=null){
//第一個參數:拾取的任務id
//第二個參數:任務候選人id
taskService.claim(taskId, userId);
System.out.println("任務拾取成功");
}
}
說明:即使該用戶不是候選人也能拾取,建議拾取時校驗是否有資格
組任務拾取後,該任務已有負責人,通過候選人將查詢不到該任務
用戶查詢個人待辦任務
查詢方式同個人任務查詢
@Test
public void findPersonalTaskList() {
// 流程定義key
String processDefinitionKey = "holiday4";
// 任務負責人
String assignee = "zhangsan";
// 創建TaskService
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(processDefinitionKey)
//設置任務執行人
.taskAssignee(assignee)
.list();
for (Task task : list) {
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());
}
}
用戶辦理個人任務
同個人任務辦理
/**完成任務*/
@Test
public void completeTask(){
//任務ID
String taskId = "12304";
processEngine.getTaskService()//
.complete(taskId);
System.out.println("完成任務:"+taskId);
}
說明:建議完成任務前校驗該用戶是否是該任務的負責人
歸還組任務
如果個人不想辦理該組任務,可以歸還組任務,歸還後該用戶不再是該任務的負責人
// 歸還組任務,由個人任務變爲組任務,還可以進行任務交接
@Test
public void setAssigneeToGroupTask() {
// 查詢任務使用TaskService
TaskService taskService = processEngine.getTaskService();
// 當前待辦任務
String taskId = "6004";
// 任務負責人
String userId = "zhangsan2";
// 校驗userId是否是taskId的負責人,如果是負責人纔可以歸還組任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
// 如果設置爲null,歸還組任務,該 任務沒有負責人
taskService.setAssignee(taskId, null);
}
}
說明:建議歸還任務前校驗該用戶是否是該任務的負責人
也可以通過 setAssignee
方法將任務委託給其它用戶負責,注意被委託的用戶可以不是候選人(建議不要這樣使用)
任務交接
任務交接,任務負責人將任務交給其它候選人辦理該任務
@Test
public void setAssigneeToCandidateUser() {
// 查詢任務使用TaskService
TaskService taskService = processEngine.getTaskService();
// 當前待辦任務
String taskId = "6004";
// 任務負責人
String userId = "zhangsan";
// 將此任務交給其它候選人辦理該 任務
String candidateuser = "lisi";
// 校驗userId是否是taskId的負責人,如果是負責人纔可以歸還組任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskAssignee(userId).singleResult();
if (task != null) {
//交接任務
taskService.setAssignee(taskId, candidateuser);
}
}
數據庫表操作
- 任務執行表
SELECT * FROM act_ru_task
記錄當前執行的任務,由於該任務當前是組任務,所有assignee
爲空,當拾取任務後該字段就是拾取用戶的id
- 任務參與者
SELECT * FROM act_ru_identitylink
記錄當前參考任務用戶或組,當前任務如果設置了候選人,會向該表插入候選人記錄,有幾個候選就插入幾個
若不是候選人顯示爲,參與者
於 act_ru_identitylink
對應的還有一張歷史表 act_hi_identitylink
,向 act_ru_identitylink
插入記錄的同時也會向歷史表插入記錄。任務完成
網關
排他網關
排他網關(也叫異或(XOR
)網關,或叫基於數據的排他網關),用來在流程中實現決策。 當流程執行到這個網關,所有分支都會判斷條件是否爲true
,如果爲 true
則執行該分支。
注意,排他網關只會選擇一個爲 true 的分支執行。(即使有兩個分支條件都爲 true,排他網關也會只選擇一條分支去執行)
爲什麼要用排他網關?
不用排他網關也可以實現分支,如下圖:
上圖中,在連線的 condition
條件上設置分支條件。
缺點:
- 如果條件都不滿足,不使用排他網關,流程就結束了(是異常結束)。
- 當
condition
設置爲總經理holiday.num > 3
,人事存檔holiday.num >=
1時且流程變量設置爲holiday.num = 5
,所有條件有成立時,兩個分支會同時執行,流程邏輯出錯。
如果使用排他網關決定分支的走向,如下:
如果從網關出去的線所有條件都不滿足則系統拋出異常。
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.ExclusiveGatewayActivityBehav
ior.leave(ExclusiveGatewayActivityBehavior.java:85)
說明 :經過排他網關必須要有一條且只有一條分支走。且選擇bpmn
文件中Id
較小的路徑執行
所有總經理執行。
最終看到整個執行流程如下:
流程定義
測試
在部門經理審覈後,走排他網關,從排他網關出來的分支有兩條,一條是判斷請假天數是否大於 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(ExclusiveGatewayActivit
yBehavior.java:85)
並行網關
並行網關允許將流程分成多條分支,也可以把多條分支匯聚到一起,並行網關的功能是基於進入和外出順序流的
- fork 分支:
並行後的所有外出順序流,爲每個順序流都創建一個併發分支。 - join 匯聚:
所有到達並行網關,在此等待的進入分支, 直到所有進入順序流的分支都到達以後, 流程就會通過匯聚網關。
注意,如果同一個並行網關有多個進入和多個外出順序流, 它就同時具有分支和匯聚功能。 這時,網關會先匯聚所有進入的順序流,然後再切分成多個並行分支。
與其他網關的主要區別是,並行網關不會解析條件。 即使順序流中定義了條件,也會被忽略。(設置條件失效)
說明:
- 財務結算和入庫是兩個
execution
分支,在act_ru_execution
表有兩條記錄分別是財務會計和行政考勤,act_ru_execution
還有一條記錄表示該流程實例。 - 待財務會計和行政考勤任務全部完成,在匯聚點匯聚,通過
parallelGateway
並行網關。 - 並行網關在業務應用中常用於會籤任務,會籤任務即多個參與者共同辦理的任務。
流程定義
測試
當執行到並行網關數據庫跟蹤如下
-
當前任務表:
SELECT * FROM act_ru_task
上圖中:有兩個(多個)任務當前執行。 -
流程實例執行表:
SELECT * FROM act_ru_execution
上圖中,說明當前流程實例有多個分支(兩個)在運行。 -
對並行任務的執行:
並行任務執行不分前後,由任務的負責人去執行即可。
當完成並任務中一個任務後:已完成的任務在當前任務表act_ru_task
已被刪除。 -
在流程實例執行表:
SELECT * FROM act_ru_execution
有中多個分支存在且有並行網關的匯聚結點。
有並行網關的匯聚結點:說明有一個分支已經到匯聚,等待其它的分支到達。 -
當所有分支任務都完成,都到達匯聚結點後:
流程實例執行表:SELECT * FROM act_ru_execution
執行流程實例不存在,說明流程執行結束
整個流程完成 act_hi_actinst
表中記錄整個過程
包含網關
包含網關可以看做是排他網關和並行網關的結合體。 和排他網關一樣,你可以在外出順序流上定義條件,包含網關會解析它們。 但是主要的區別是包含網關可以選擇多於一條順序流,這和並行
網關一樣。
包含網關的功能是基於進入和外出順序流的。
- 分支:
所有外出順序流的條件都會被解析,結果爲true
的順序流會以並行方式繼續執行, 會爲每個順序流創建一個分支。 - 匯聚:
所有並行分支到達包含網關,會進入等待狀態, 直到每個包含流程token
的進入順序流的分支都到達。 這是與並行網關的最大不同。換句話說,包含網關只會等待被選中執行了的進入順序流。 在匯聚之後,流程會穿過包含網關繼續執行。
流程定義
員工類型:
通過流程變量 userType
來表示,如果等於 1 表示普通員工,如果等於 2 表示領導
注意:通過包含網關的每個分支的連線上設置condition
條件。
測試
如果包含網關設置的條件中,流程變量不存在,報錯;
org.activiti.engine.ActivitiException: Unknown property used in expression: ${userType=='1' ||
userType=='2'}
需要在流程啓動時設置流程變量userType
- 當執行到包含網關:
流程實例執行表:SELECT * FROM act_ru_execution
- 第一條記錄:包含網關分支。
- 後兩條記錄:兩個分支:常規項體檢,抽血化驗
- 當前任務表:
ACT_RU_TASK_
上圖中,常規項體檢,抽血化驗都是當前的任務,在並行執行。
如果有一個分支執行到匯聚:
先走到匯聚結點的分支,要等待其它分支走到匯聚。
等所有分支走到匯聚,包含網關就執行完成。
包含網關執行完成,分支和匯聚就從 act_ru_execution
刪除。
小結:在分支時,需要判斷條件,符合條件的分支,將會執行,符合條件的分支最終才進行匯聚。
總結activiti
開發流程
- 第一步:部署
activiti
的環境。
環境包括:jar
包和數據庫(25 張表)
業務系統通過spring
和activiti
整合進行開發。 - 第二步:使用
activiti
提供流程設計器(和idea
或eclipse
集成的designer
)工具進行流程定義
流程定義生成兩個文件:.bpmn
和.png
(不是必須的)。 - 第三步;將流程定義文件部署到
activiti
的數據庫SELECT * FROM act_re_deployment
#流程定義部署表
一次部署插入一條記錄,記錄流程定義的部署信息SELECT * FROM act_re_procdef
#流程定義表
一次部署流程定義信息,如果一次部署兩個流程定義,插入兩條記錄- 建議:一次部署只部署一個流程定義,這樣
act_re_deployment
和act_re_procdef
一對一關係 - 常用兩個方法:單個文件部署和 zip 文件部署。
建議單個文件部署。
- 第四步: 啓動一個流程實例
業務系統就可以按照流程定義去執行業務流程,執行前需要啓動一個流程實例
根據流程定義來啓動一個流程實例。
指定一個流程定義的key
啓動一個流程實例,activiti
根據key
找最新版本的流程定義。
指定一個流程定義的id
啓動一個流程實例。
啓動一個實例需要指定businesskey
(業務標識),businessKey
是activiti
和業務系統整合時橋樑。
比如:請假流程,businessKey
就是請假單 id。
啓動一個實例還可以指定流程變量,流程變量是全局變量(生命期是整個流程實例,流程實例結束,變量就消失) - 第五步:查詢待辦任務
查詢個人任務:使用taskService
,根據assignee
查詢該用戶當前的待辦任務。
查詢組任務:使用taskService
,根據candidateuser
查詢候選用戶當前的待辦組任務。 - 第六步:辦理任務
辦理個人任務:調用taskService
的complete
方法完成任務。
如果是組任務,需要先拾取任務,調用taskService
的claim
方法拾取任務,拾取任務之後組任務就變成了個人任務(該任務就有負責人)。 - 網關:
- 排他網關:任務執行之後的分支,經過排他網關分支只有一條有效。
- 並行網關:任務執行後,可以多條分支,多條分支總會匯聚,匯聚完成,並行網關結束。
- 包含網關:是排他網關和並行網關結合體。