1 概要介紹
1)什么是流程定義 ?
流程定義是按照bpmn2.0標准定義業務流程,將流程定義的文件(.bpmn和.png(不是必須的))部署到activiti中,activiti就可以管理該業務流程。
2)什么是流程實例 ?
參與者(可以用戶,也可以程序)按照流程定義發起一個流程,這個流程就是一個流程實例 。流程定義的內容就是一個靜態文件(.bpmn),流程實例的內容是該 流程的執行過程(動態)。
3)如何啟動一個流程實例 ?
常用方式:啟動一個流程實例時指定一個業務標識。
// 業務標識 ,如果 是采購流程就是采購單id
String businessKey = "001";
// 啟動流程實例時指定業務標識
String processDefinitionKey = "purchasingflow";
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(processDefinitionKey, businessKey);
businessKey:業務標識,作用:通過activiti的api查詢activiti的流程數據時,可以通過businessKey關聯查詢業務系統 的數據,通常業務標識記錄業務系統表的主鍵,比如:如果采購流程,businesskey就是采購單id,如果是請假流程,businessKey就是請假單(請假信息表)的id。
啟動一個流程實例 后,該流程運行到第一個結點,activiti需要給該 任務結點分配任務負責人。
任務分配三種方式:
第一種:采用固定分配方法,設置task結點的assignee(任務負責人)屬性,不常用。
第二種:采用UEL表達式,表達使用流程變量設置任務負責人,在企業開發中常用。
第三種:采用監聽器(自定義監聽器實現 TaskListener接口),可以在監聽器中擴展代碼,在企業開發中常用。
查詢待辦任務:
查詢待辦任務中如果包括 業務系統 數據,如果查詢?
通過Taskid得到任務所屬的流程實例id
通過流程實例id得到流程實例對象ProcessInstance。
從ProcessInstance獲取businessKey
通過businessKey關聯查詢業務數據
4)Activiti開發步驟:
1、 在需求階段,分析出業務流程
2、 設計階段,確定哪些業務流程由activiti管理,對工作流管理的流程進行流程定義
流程定義時和功能設計同步進行:
1》 確定流程啟動所對應的功能
2》 確定流程執行中哪些功能和流程結點對應,哪個功能可以將流程向后推進一步
Activiti開發遵循原則:
1、 角色分工明確,activiti負責流程管理 ,業務系統 負責業務功能。
2、 業務系統 中通常在service層將activiti和控制層、持久層進行隔離(解耦),比如在業務功能中需要查詢activiti的流程數據,需要自定義一個對象存儲activiti的數據。
3、 數據共享問題,在activiti中存儲businesskey(業務標識),通過businesskey查詢業務系統 數據,在業務系統 中存儲activiti的標識(比如在采購單中存儲流程實例 的id),在查詢業務數據時通過此流程實例 id查詢activiti的數據。達到目標:activiti和業務系統 能互相關聯查詢。
辦理任務(完成任務):
需要參數:任務id(activiti的api要求),用戶id(進行權限校驗使用)
在完成任務之前需要校驗該 用戶是否有該 任務的完成權限。
2 流程變量
2.1 什么是流程變量
在activiti在管理流程中,可能需要通過流程變量控制流程的執行。
注意:流程變量只是用於控制流程的執行,而不是存儲業務數據!!
2.2 流程變量作用域
Activiti的流程變量包括 global全局變量和local局部變量。
Activiti常用全局變量進行流程控制,因為local局部變量作用域小一般不用,可以查詢歷史的局部變量。
global全局變量:作用域是整體流程實例,如果流程實例結束,變量無效。
local局部變量: 作用域小,可以是一個任務(task)也可以是一個執行分支(execution)。任務或執行分支結束,local局部變量無效。
通過historyService查詢歷史流程變量值。
不同的流程實例,global全局變量互不影響。
同一個流程實例,無法設置兩個相同名稱 global全局變量,后設置的相同名稱 global全局變量會覆蓋前邊的變量值 。
任務中local變量,不同任務的local變量互不影響。Local變量名稱可以和global全局變量相同。
2.3 流程變量的類型

注意:如果將pojo對象存儲到流程變量中,必須實現序列化接口serializable,為了防止由於新增字段無法反序列化,需要生成serialVersionUID,如下:

注意:由於OrderCustom也繼承了PurBusOrder類,PurBusOrder也必須實現序列化接口,否則 PurBusOrder中的屬性無法反序列化。
總結:
1、如果需要將pojo中的屬性從流程變量獲取(activiti進行反序列化),需要將屬性所在pojo實現serializable接口。
2、需要在pojo中設置serialVersionUID,如果不設置該版本id,日后在pojo中新增字段,如果之前將未新增字段的pojo對象存儲在activiti的流程變量中,如果從流程變量中獲取之前 的pojo對象,將報錯。
2.4 流程變量的使用方法
第一步:設置流程變量
第二步:使用流程變量控制流程的執行
例子:
1) 在task結點的assignee通過UEL表達式使用流程變量
${assignee}:assignee就是一個流程變量名稱
Activiti獲取UEL表達式的值 ,即流程變量assignee的值 ,將assignee的值作為任務的負責人進行任務分配.
2)在連線上的candition條件上通過UEL表達式使用流程變量
${price>=10000}和${price<10000}: price就是一個流程變量名稱,uel表達式結果類型為布爾類型
如果UEL表達式是true,要決定 流程執行走向。
比如:如果采購金額大於10000元由總經理審核,否則由財務直接審核。
2.5 設置全局變量
2.5.1 可以流程啟動時設置流程變量(常用)
由於設置了全局變量,該流程啟動后,在下邊每個結點都可以使用該變量。
在task結點的assignee通過UEL表達式使用流程變量。
代碼:
// 得到runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 根據流程定義的key(標識 )來啟動一個實例,activiti找該key下版本最高的流程定義
// 一般情況下為了方便開發使用該方法啟動一個流程實例。
String processDefinitionKey = "purchasingflow";
// 在流程啟動時設置全局變量,第二個參數variables存儲流程變量
Map<String, Object> variables = new HashMap<String, Object>();
// 流程 變更名稱是assignee,流程變量值是"張三"
variables.put("assignee", "張三");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
2.5.2 在完成任務時設置流程變量(常用)
在完成任務時設置流程變量,在該 任務的后續結點可以使用該 變量,變量的作用域還是整個流程實例 。
//完成任務時設置流程變量
Map<String, Object> variables = new HashMap<String, Object>();
// 流程 變更名稱是assignee,流程變量值是"張三"
variables.put("assignee", "李四");
taskService.complete(taskId, variables);
測試發現,在同一個流程實例下,同名的全局變量,后設置的會覆蓋前邊設置的。
2.5.3 通過當前流程實例 id設置
通過此方法可以在流程實例未結束時任意設置流程變量。
RuntimeService runtimeService = processEngine.getRuntimeService();
//設置單個 變量
//第一個參數:executionId是流程實例 的執行 id,通常使用流程實例 id,必須是當前正在運行的流程實例 id
//第二個參數:變量名
//第三個參數:變量值
runtimeService.setVariable("2001", "price", 10000);
//一次設置多個變量,第二個參數是map
//runtimeService.setVariables(executionId, variables)
注意:第一個參數executionId是流程實例 的執行 id,通常使用流程實例 id,必須是當前正在運行的流程實例 id
獲取變量:
runtimeService.getVariable(executionId, variableName)
2.5.4 通過當前待辦任務id設置
通過此方法可以在流程實例未結束時任意設置流程變量。
TaskService taskService = processEngine.getTaskService();
//設置單個 變量
//第一個參數:taskId是當前待辦任務id,在act_ru_task存在
//第二個參數:變量名
//第三個參數:變量值
//activiti根據 任務id查詢所屬流程實例 id,存儲act_ru_variable表。
taskService.setVariable("2902", "price", 10000);
//一次設置多個變量,第二個參數是map
//taskService.setVariables(taskId, variables)
activiti根據 任務id查詢所屬流程實例 id,將流程變量值存儲act_ru_variable表,不管使用什么任務id,找到流程實例 id相同就會覆蓋原來的變量。
獲取變量方法:
System.out.println(taskService.getVariable("3302", "price"));
3 全局變量測試
什么時候設置流程變量?
采購流程中,在提交采購單時設置流程變量,因為提交采購單后采購單信息不再修改了。
使用完成提交采購單任務時設置流程變量。
// 完成任務時設置流程變量,使用pojo
OrderCustom orderCustom = new OrderCustom();
orderCustom.setPrice(10000f);
Map<String, Object> varaibles = new HashMap<String, Object>();
varaibles.put("order", orderCustom);
taskService.complete(taskId, varaibles);
4 設置局部變量
4.1 在任務完成設置局部變量
在任務完成時設置局部變量,任務完成后,后續結點無法使用局部變量。
// 完成任務時設置流程變量,使用pojo
OrderCustom orderCustom = new OrderCustom();
orderCustom.setPrice(10000f);
Map<String, Object> varaibles = new HashMap<String, Object>();
varaibles.put("order", orderCustom);
//設置局部變量
taskService.setVariablesLocal(taskId, varaibles);
//完成任務
taskService.complete(taskId);
當局部變量消失后,再使用該變量會報錯。
4.2 查詢歷史任務時查詢流程變量
//查詢歷史 任務查詢流程變量
historicTaskInstanceQuery.includeTaskLocalVariables();
//從activiti的流程變量取出pojo對象 ,經過反序列化
OrderCustom orderCustom = (OrderCustom) historicTaskInstance.getTaskLocalVariables().get("order");
System.out.println("歷史 流程變量采購單信息:"+orderCustom.getPrice());
注意:使用流程變量不建議存儲業務數據,因為通過activiti的api查詢流程變量,特別是pojo變量,速度很慢。
流程變量要用於流程控制。
4.3 通過當前任務id設置變量
//通過當前任務id設置局部變量
@Test
public void setVariableByTaskId(){
TaskService taskService = processEngine.getTaskService();
//設置單個 局部變量
taskService.setVariableLocal("5305", "price", 50000);
//一次設置多個局部變量
//taskService.setVariablesLocal(taskId, variables)
}
注意:任務id為當前待辦理的任務id。
4.4 流程變量數據表跟蹤
SELECT * FROM act_ru_variable #當前流程變量表
記錄當前流程實例使用的流程變量
Type_:變量類型
Name_:變量名稱
Execution_id_:流程實例執行id(global和local變量存儲)
Proc_inst_id_:流程實例 id(global和local變量存儲)
Task_id_:流程變量所屬的任務id(local變量存儲)
BYTEARRAY_ID_:如果流程變量為pojo,字段存儲引用act_ge_bytearray表的主鍵,在資源表act_ge_bytearray存儲pojo流程變量的序列化信息。
Long_和text_:根據變量類型存儲變量值
SELECT * FROM act_hi_varinst #歷史 流程變量表
記錄流程執行所創建的所有流程變量
5 案例1
5.1 需求
員工創建采購單,由部門經理審核,部門經理審核通過后一萬元以下由財務直接審核,一萬元以上先由總經理審核,總經理審核通過再由財務審核。

5.2 流程定義
5.3 開發
采購流程中,在提交采購單時設置流程變量,因為提交采購單后采購單信息不再修改了。
使用完成提交采購單任務時設置流程變量。
修改提交 采購單service,在完成任務時設置流程變量。
修改orderService中saveOrderSubmitStauts提交采購單方法:
if (task != null) {
// 說明assignee是該任務的辦理人,有權限完成
OrderCustom orderCustom = new OrderCustom();
// 采購信息獲取
// 根據 任務對象 獲取流程實例 id
String processInstanceId = task.getProcessInstanceId();
// 查詢流程實例 對象
ProcessInstance processInstance = runtimeService
.createProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
// 從流程實例 對象 中獲取businessKey
String businessKey = processInstance.getBusinessKey();
// 根據 businessKey查詢采購單信息
PurBusOrder purBusOrder = purBusOrderMapper.selectByPrimaryKey(businessKey);
BeanUtils.copyProperties(purBusOrder, orderCustom);
//流程變量,值 為orderCustom即采購單信息
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("order", orderCustom);
// 設置流程變量,值 為采購單信息
taskService.complete(taskId,variables);
// System.out.println("完成任務:" + taskId);
}
6 案例2
6.1 需求
在上邊的基礎上添加審核不通過的連線。

需求如下:
在采購系統中實現流程審核不通過分支,功能如下:
1 部門經理審核不通過由員工重新修改采購單
2 總經理審核不通過由員工重新修改采購單再提交
3 財務審核不通過由員工重新審核
6.2 流程定義
6.2.1 對審核不通的由流程發起人再提交采購單
設置流程發起人:
在開始結點,設置initiator(發起人)。

設置創建采購單的任務負責人為:

修改orderService中的saveOrder方法,在啟動流程時設置任務發起人:

注意:
任務發起人設置在啟動流程實例之前設置。
6.2.2 增加審核不通過的連線
部門經理審核 :
審核通過candition:
${order.price>=10000 && firstAudit. status==’1’} 部門經理審核通過且采購金額大於等於10000元由總經理審核
${order.price<10000 && firstAudit. status==’1’}部門經理審核通過且采購金額小於10000元由財務審核
firstAudit和order都是流程變量名稱
審核 不通過candition :
${ firstAudit. status==’0’}:部門經理審核不通過由流程發起人重新修改采購單再提交
總經理審核 :
審核通過candition:${ secondAudit. status==’1’} 總經理審核通過
審核 不通過candition : ${ secondAudit. status==’0’}總經理審核不通過
財務審核 :
審核通過candition:${ thirdAudit. status==’1’} 財務審核通過
審核 不通過candition : ${ third Audit. status==’0’}財務審核不通過
注意:上邊的firstAudit、secondAudit、thirdAudit分別存儲三級審核信息。
6.3 開發
修改orderService中saveOrderSubmitStauts提交采購單方法:
if (task != null) {
// 說明assignee是該任務的辦理人,有權限完成
OrderCustom orderCustom = new OrderCustom();
// 采購信息獲取
// 根據 任務對象 獲取流程實例 id
String processInstanceId = task.getProcessInstanceId();
// 查詢流程實例 對象
ProcessInstance processInstance = runtimeService
.createProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
// 從流程實例 對象 中獲取businessKey
String businessKey = processInstance.getBusinessKey();
// 根據 businessKey查詢采購單信息
PurBusOrder purBusOrder = purBusOrderMapper.selectByPrimaryKey(businessKey);
BeanUtils.copyProperties(purBusOrder, orderCustom);
//流程變量,值 為orderCustom即采購單信息
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("order", orderCustom);
// 設置流程變量,值 為采購單信息
taskService.complete(taskId,variables);
// System.out.println("完成任務:" + taskId);
}
修改 service中采購單審核方法:
設置流程變量:firstAudit、secondAudit、thirdAudit
if (task != null) {
// 說明assignee是該任務的辦理人,有權限完成
Map<String,Object> variables = new HashMap<String,Object>();
//根據 auditType判斷是幾級審核
if(auditType.equals("firstAudit")){
//部門經理審核
variables.put("firstAudit", orderAuditCustom);
}else if(auditType.equals("secondAudit")){
//總經理審核
variables.put("secondAudit", orderAuditCustom);
}else if(auditType.equals("thirdAudit")){
//財務審核
variables.put("thirdAudit", orderAuditCustom);
}
//提交審核時,設置流程變量,變量值就是審核 信息
taskService.complete(taskId,variables);
// System.out.println("完成任務:" + taskId);
}
7 Candidate-user候選人
7.1 什么是候選人
采用固定分配方法給任務指定負責人,如果任務負責人出現變更,需要修改流程定義,就可以采用候選人分配方式,先給任務分配多個候選人,候選人通過拾取組任務進行個人任務辦理。

給任務分配候選人,如果分配多個候選人中間使用半角逗號分隔。
7.2 什么是組任務
多個候選人有資格完成該 任務,這個任務叫做組任務。
組任務具備條件:
任務沒有設置assignee任務負責人
任務具有候選人
7.3 候選人辦理任務過程
第一步:給任務設置候選人(多個, 中間使用半角逗號分隔)
候選人是無法辦理任務
第二步:候選人查詢組任務
使用taskService查詢,指定candidate候選人。
第三步:候選人拾取(claim)組任務
候選人拾取組任務后,該 候選人變為任務的負責人,該任務變為個人任務
如果候選人拾取組任務后,不想辦理該 任務,可以將個人任務歸還,該個人任務變為組任務
第四步:查詢待辦個人任務
第五步:辦理任務
第六步:流程結束
7.4 Candidate-user辦理任務api
7.4.1 候選人查詢組任務
使用taskService指定candidateUser候選人查詢組任務。
//任務查詢對象
TaskQuery taskQuery = taskService.createTaskQuery();
//候選人
String candidateUser = "zhangsan";
taskQuery.taskCandidateUser(candidateUser);
//流程定義key
String processDefinitionKey = "purchasingflow";
taskQuery.processDefinitionKey(processDefinitionKey);
List<Task> list = taskQuery.list();
注意:查詢組任務,必須指定 candidateUser候選人,查詢該候選人有資格辦理的組任務。
7.4.2 拾取組任務
通過taskService,指定任務id和候選人拾取任務:
TaskService taskService = processEngine.getTaskService();
//組任務id
String taskId = "5604";
//任務候選人,claim拾取后該 候選人變為任務負責人
String userId = "zhangsan";
//任務拾取
taskService.claim(taskId, userId);
注意:如果拾取人不是該任務的候選人也可以拾取成功,在拾取之前需要校驗,該 候選人是否有資格拾取該 任務.
// 組任務id
String taskId = "6004";
// 任務候選人,claim拾取后該 候選人變為任務負責人
String candidateUser = "zhangsan4";
//根據候選人和組任務id查詢,如果有記錄說明該 候選人有資格拾取該 任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskCandidateUser(candidateUser).singleResult();
if(task!=null){
// 任務拾取
taskService.claim(taskId, candidateUser);
System.out.println("任務拾取成功");
}
7.4.3 組任務歸還
// 歸還組任務,由個人任務變為組任務,還可以進行任務交接
@Test
public void setAssignee() {
// 查詢任務使用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);
}
}
7.4.4 任務交接
任務負責人也可以將任務交給其它候選人辦理該任務
代碼如下:
@Test
public void setAssigneeToCandidateUser() {
// 查詢任務使用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) {
// 將此任務交給其它候選人辦理該 任務
String candidateuser = "zhangsan";
// 根據候選人和組任務id查詢,如果有記錄說明該 候選人有資格拾取該 任務
Task task2 = taskService.createTaskQuery().taskId(taskId)
.taskCandidateUser(candidateuser).singleResult();
if (task2 != null) {
// 才可以交接
taskService.setAssignee(taskId, candidateuser);
}
}
}
8 Candidate-group候選組
8.1 什么候選組
即使給任務指定了多個候選人,多個候選人都有辦理任務資格,但是候選的人數有限,無法動態擴展,如果需要添加或刪除候選,需要修改流程定義 文件,不利於系統 擴展。
采用候選組方式解決上邊的問題。
給任務設置候選組,在組中有多個用戶並且可以動態擴展用戶,組中的用戶都是候選人,候選人先拾取組任務,將組任務變為自己的個人任務,進行個人任務辦理。
8.2 候選組辦理任務過程
第一步:Activiti會自動從候選組中找用戶,將這些用戶作為該 任務的候選人。
下邊的流程同候選人辦理任務過程!!
第二步:給任務設置候選人(多個, 中間使用半角逗號分隔)
候選人是無法辦理任務
第三步:候選人查詢組任務
使用taskService查詢,指定candidate候選人。
第四步:候選人拾取(claim)組任務
候選人拾取組任務后,該 候選人變為任務的負責人,該任務變為個人任務
如果候選人拾取組任務后,不想辦理該 任務,可以將個人任務歸還,該個人任務變為組任務
第五步:查詢待辦個人任務
第六步:辦理任務
第七步:流程結束
8.3 設置候選組

8.4 設置組和用戶信息
Activiti中采用以下表記錄組信息、用戶信息、組和用戶關系 信息
SELECT * FROM act_id_group #組信息
SELECT * FROM act_id_user #用戶信息
SELECT * FROM act_id_membership #組和用戶關系信息
8.4.1 Api設置方法
以下設置的信息和業務系統的用戶信息、角色信息保存一致。
先設置組信息

再設置用戶信息

再設置組和用戶關系信息
代碼如下:
//設置組和用戶信息
@Test
public void setUserGroup(){
IdentityService identityService = processEngine.getIdentityService();
//設置組信息
//添加之前應該校驗組信息是否存在,不存在再進行添加
if(identityService.createGroupQuery().groupId("10").singleResult()==null){
//添加新組
GroupEntity groupEntity = new GroupEntity();
groupEntity.setId("10");
groupEntity.setName("員工");
identityService.saveGroup(groupEntity);
}
if(identityService.createGroupQuery().groupId("11").singleResult()==null){
//添加新組
GroupEntity groupEntity = new GroupEntity();
groupEntity.setId("11");
groupEntity.setName("部門經理");
identityService.saveGroup(groupEntity);
}
if(identityService.createGroupQuery().groupId("12").singleResult()==null){
//添加新組
GroupEntity groupEntity = new GroupEntity();
groupEntity.setId("12");
groupEntity.setName("總經理");
identityService.saveGroup(groupEntity);
}
if(identityService.createGroupQuery().groupId("13").singleResult()==null){
//添加新組
GroupEntity groupEntity = new GroupEntity();
groupEntity.setId("13");
groupEntity.setName("財務");
identityService.saveGroup(groupEntity);
}
//設置用戶信息
//添加之前應該校驗用戶信息是否存在,不存在再進行添加
if(identityService.createUserQuery().userId("zhangsan").singleResult()==null){
//添加新用戶
UserEntity userEntity = new UserEntity();
userEntity.setId("zhangsan");
userEntity.setFirstName("張三");
identityService.saveUser(userEntity);
}
if(identityService.createUserQuery().userId("lisi").singleResult()==null){
//添加新用戶
UserEntity userEntity = new UserEntity();
userEntity.setId("lisi");
userEntity.setFirstName("李四");
identityService.saveUser(userEntity);
}
if(identityService.createUserQuery().userId("wangwu").singleResult()==null){
//添加新用戶
UserEntity userEntity = new UserEntity();
userEntity.setId("wangwu");
userEntity.setFirstName("王五");
identityService.saveUser(userEntity);
}
if(identityService.createUserQuery().userId("zhaoliu").singleResult()==null){
//添加新用戶
UserEntity userEntity = new UserEntity();
userEntity.setId("zhaoliu");
userEntity.setFirstName("趙六");
identityService.saveUser(userEntity);
}
//設置用戶和組的關系信息
//采用先刪除再添加
identityService.deleteMembership("zhangsan", "10");
identityService.createMembership("zhangsan", "10");
identityService.deleteMembership("lisi", "11");
identityService.createMembership("lisi", "11");
identityService.deleteMembership("wangwu", "12");
identityService.createMembership("wangwu", "12");
identityService.deleteMembership("zhaoliu", "13");
identityService.createMembership("zhaoliu", "13");
}
8.4.2 與業務系統同步方法(常用)
方法1 :
數據庫觸發器方法
企業中在進行數據同步的常用方法,一般在一個數據庫中采用此方法。
業務系統 用戶表----》activiti的act_id_user
在業務系統 用戶表添加觸發器:新增、刪除、修改
注意:如果act_id_user有外鍵關系,需要先刪除依賴關系。
業務系統 角色表----》activiti的act_id_group
業務系統 角色和用戶關系表—》activiti的act_id_membership
方法2 :
采用即時觸發java程序。
用戶角色同步:
在操作業務系統 用戶角色表時執行以下操作:
業務系統添加角色-àactiviti添加角色
業務系統修改角色àactiviti修改角色
業務系統刪除角色àactiviti刪除角色,刪除之前將用戶角色關系表先刪除(根據角色刪除)
用戶信息同步
在操作業務系統 用戶表時執行以下操作
業務系統添加用戶----》activiti添加用戶,添加用戶與角色關系表
業務系統修改用戶---》activiti修改用戶,先刪除原來用戶與角色關系表,再添加用戶與角色關系
業務系統刪除用戶—》activiti刪除用戶,刪除之前將用戶角色關系表刪除(根據用戶刪除)
8.5 組任務辦理過程api
8.5.1 設置組和用戶信息
參考上邊設置組和用戶api
正式開發時,需要將業務系統 用戶和角色信息同步到activiti中。
8.5.2 候選人查詢組任務
參考candidate-user的api
注意:
在activiti的用戶、組、用戶和組關系表中隨時添加數據,不受流程啟動先后順序影響。
9 網關
9.1 排他網關
9.1.1 什么排他網關
排他網關用於決策,選擇分支執行流程,分支上需要設置condition條件,如果分支的條件結果為true,那么該分支會通過排他網關。排他網關只會選擇一條分支去執行。
9.1.2 定義方法
圖標:

流程定義:

9.1.3 排他網關測試
第一步:流程定義部署
第二步:啟動流程實例
設置price流程變量值,因為price 在排他網關的兩分支使用
第三步:查詢待辦任務
也可以在部門經理審核后設置price流程變量值,因為price 在排他網關的兩分支使用
第四步:辦理任務
如果分支上的條件都不滿足,沒有一條線經過排他網關,activiti會拋出異常:
org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process at org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior.leave(ExclusiveGatewayActivityBehavior.java:85)
如果多條分支都滿足,只會有一條線經過排他網關。
上邊兩種情況必須在開發避免!!!
9.2 並行網關
9.2.1 什么並行網關
並行網關(parallelGateway),包括分支和匯聚兩個結點,所有的分支不判斷條件都經過分支結點,所有經過分支結點的分支都要進行匯聚,所有的分支全部執行完成,並行網關執行完成。
Fork(分支)
所有的分支不判斷條件都經過分支結點
Join(匯聚)
所有經過分支結點的分支都要進行匯聚
分支的數量等於匯聚數量!
9.2.2 流程定義
圖標:


注意:經過並行網關的分支結點,不需要設置condition條件。
9.2.3 並行網關測試
當流程執行到並行的分支結點時,
向act_ru_execution #流程實例執行表執行並行分支(結算,入庫)

Execution表中8501的記錄數等於分支數+1
只有一條記錄的流程實例 id和流程實例執行id相等的,這一條為流程執行主線。
向當前任務表中插入兩條記錄(結算、入庫)
9.3 包含網關
9.3.1 什么是包含網關
包含網關是排他網關和並行網關的結合體。
包含網關(IncluesiveGateway),包括分支和匯聚兩個結點,經過分支結點需要判斷條件,滿足條件經過分支結點,所有經過分支結點的線邊最終會進行匯聚。
Fork(分支)
所有的分支需要判斷條件,滿足條件的經過分支結點
Join(匯聚)
所有滿足條件的分支都要進行匯聚

10 案例
10.1 需求
將采購流程改為組任務(使用候選組)實現
在采購流程中實現排他網關
在采購流程中實現並行網關

需求描述:
員工創建采購單
經過部門經理審核
審核通過:
部門經理審核通過,如果采購金額大於等於1萬元,由總經理審核
部門經理審核通過,如果采購金額小於1萬元,由財務審核
審核不通過:
部門經理審核不通過,由員工重新修改采購單進行提交
總經理審核
總經理審核通過由財務審核通過
總經理審核不通過,由員工重新修改采購單進行提交
財務審核
財務審核通過並行執行財務結算和入庫
財務審核不通過,由員工重新修改采購單進行提交
財務結算和入庫兩個操作可以並行執行。
10.2 流程定義
部門經理審核通過后,通過排他網關決定走總經理審核還是財務審核。
財務審核通過后,經過並行網關,財務結算和入庫並行執行。
審核分支設置condition 條件:
部門經理審核 :
審核通過candition:
${order.price>=10000 && firstAudit. status==’1’} 部門經理審核通過且采購金額大於等於10000元由總經理審核
${order.price<10000 && firstAudit. status==’1’}部門經理審核通過且采購金額小於10000元由財務審核
firstAudit和order都是流程變量名稱
審核 不通過candition :
${ firstAudit. status==’0’}:部門經理審核不通過由流程發起人重新修改采購單再提交
總經理審核 :
審核通過candition:${ secondAudit. status==’1’} 總經理審核通過
審核 不通過candition : ${ secondAudit. status==’0’}總經理審核不通過
財務審核 :
審核通過candition:${ thirdAudit. status==’1’} 財務審核通過
審核 不通過candition : ${ third Audit. status==’0’}財務審核不通過
10.3 分析
10.3.1 需要開發結算和入庫功能
真正的結算和入庫很復雜,確定一個功能將流程向后推進一步。
為了教學方便,開發結算和入庫功能實現activiti任務完成。
10.3.2 候選人查詢組任務
由於任務結點改變 候選組方式分配任務,實現候選人查詢組任務。
實現 方法:
調用taskService,指定candidateUser候選人查詢組任務。
10.4 開發
接口功能:候選人查詢組任務
接口參數:候選人candidateUserId,(實際開發需要查詢條件)
接口實現 :
調用taskService,指定candidateUser候選人查詢組任務
public List<OrderCustom> findOrderGroupTaskList(String userId)
throws Exception {
// 任務查詢對象
TaskQuery taskQuery = taskService.createTaskQuery();
// 候選人,在act_id_user表中存在,從act_id_membership通過group_id_查詢出用戶
String candidateUser = userId;
// 指定候選人
taskQuery.taskCandidateUser(candidateUser);
// 流程定義key
String processDefinitionKey = ResourcesUtil.getValue(
"diagram.purchasingflow", "purchasingProcessDefinitionKey");
taskQuery.processDefinitionKey(processDefinitionKey);
List<Task> list = taskQuery.list();
List<OrderCustom> orderList = new ArrayList<OrderCustom>();
for (Task task : list) {
OrderCustom orderCustom = new OrderCustom();
//下邊的代碼同采購單處理列表代碼...
// 流程實例id
String processInstanceId = task.getProcessInstanceId();
// 根據流程實例id找到流程實例對象
ProcessInstance processInstance = runtimeService
.createProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
// 從流程實例對象中獲取businessKey
String businessKey = processInstance.getBusinessKey();
// 根據businessKey查詢業務系統
// 采購單id
String orderId = businessKey;
PurBusOrder purBusOrder = purBusOrderMapper
.selectByPrimaryKey(orderId);
// 獲取采購單名稱、采購金額等采購單信息
// 將purBusOrder內容拷貝到orderCustom
BeanUtils.copyProperties(purBusOrder, orderCustom);
// 下邊向orderCustom開始設置任務信息
// 任務id、任務標識 、任務名稱
// 任務id
orderCustom.setTaskId(task.getId());
// 任務標識
orderCustom.setTaskDefinitionKey(task.getTaskDefinitionKey());
// 任務名稱
orderCustom.setTaskName(task.getName());
orderList.add(orderCustom);
}
return orderList;
}
查詢組任務方法:
代碼基本上同采購單處理列表的代碼:
// 采購單組任務列表
@RequestMapping("/orderGroupTaskList")
public String orderGroupTaskList(HttpSession session, Model model)
throws Exception {
// 當前登陸用戶
ActiveUser activeuser = UserUtil.getUserFromSession(session);
// 用戶id
String userId = activeuser.getUserid();
List<OrderCustom> list = orderService.findOrderGroupTaskList(userId);
model.addAttribute("list", list);
return "order/orderGroupTaskList";
}
接口功能:拾取組任務
接口參數:taskId任務id,candidateUserId候選人
接口實現 :
調用taskService,指定組任務id和candidateUserId候選人。
拾取任務之前需要校驗候選人是否資格拾取該組任務。
@Override
public void saveClaimTask(String taskId, String candidateUserId)
throws Exception {
// 根據候選人和組任務id查詢,如果有記錄說明該 候選人有資格拾取該 任務
Task task = taskService.createTaskQuery().taskId(taskId)
.taskCandidateUser(candidateUserId).singleResult();
if (task != null) {
// 任務拾取
taskService.claim(taskId, candidateUserId);
System.out.println("任務拾取成功");
}
}
拾取組任務方法:
需要從頁面傳入taskId組任務id.
// 拾取組任務
@RequestMapping("/claimTask")
public String claimTask(HttpSession session, String taskId)
throws Exception {
// 當前登陸用戶
ActiveUser activeuser = UserUtil.getUserFromSession(session);
// 用戶id
String userId = activeuser.getUserid();
orderService.saveClaimTask(taskId, userId);
//返回采購單組任務列表
return "redirect:orderGroupTaskList.action";
}
修改組任務列表頁面,添加“拾取組任務”連接:
1.2 測試
測試注意點:
1、組任務(使用候選組)
准備組和用戶的數據,調用activiti的api設置組、用戶、組和用戶關系信息
正式開發,將業務系統 用戶角色數據實時同步到activiti中。
2、測試組任務辦理流程
a>查詢組任務功能
b>拾取組任務功能
3、測試排他網關
修改原來的功能是否存在bug。
4、測試並行網關
結算功能和入庫功能是否並行執行.
11 總結
什么是工作流?
工作流是通過計算機自動管理業務流程,實現多個參與者按照預定義的流程自動執行業務流程。
什么是activiti?
Activiti是一個工作流的引擎(框架,jar、組件),對業務流程的自動化管理。Activiti按照bpmn2.0標准進行流程定義,按照定義流程(bpmn文件) 去自動執行業務流程。
第一步:線下進行流程定義
Activiti按照bpmn2.0標准進行流程定義,定義文件包括 .bpmn和.png,其中.bpmn是必須的文件。
第二步:進行流程定義部署
將線下流程定義文件.bpmn部署到activiti的數據庫中。這樣activiti方可按照定義流程(bpmn文件) 去自動執行業務流程。
兩種方法部署方法:
1、 單個文件部署方法
2、 Zip包部署方法
建議使用單個文件 部署,建議一次部署只部署一個流程定義 。
第三步:啟動一個流程 實例
流程定義和流程實例 的區別:
流程定義 是一個靜態文件(.bpmn),流程實例是動態的流程執行過程。如果要activiti去管理業務流程的執行,需要首先發起一個流程實例 。
第四步:查詢待辦任務
個人任務:
通過固定設置方法或UEL表達式設置方法設置task結點的assignee屬性。
組任務:
通過設置多個候選人或多個候選組的方法。
組任務常用,組任務中以candidate-group候選組方式最常用。
組任務辦理過程:
通過候選人查詢任務
拾取任務
同個人任務辦理流程。
第五步:辦理任務
流程的執行可能需要通過流程變量決定執行分支。
流程變量:
常用全局變量
需要在啟動流程實例 或完成任務時設置流程變量
在UEL表達式中使用流程變量
網關:
排他網關:
經過排他網關的分支只有一條。
並行網關:
經過並行網關分支結點所有分支不管條件是否滿足都 會經過並行網關的匯聚結點。
並行網關常用於會簽任務(多個用戶共同辦理的任務)
包含網關:
經過包含網關分支結點,只有滿足條件的分支才經過包含網關的匯聚結點。
第六步:流程完成
所有結點完成執行完成,流程自動完成。
注意:如果使用並行網關或包含網關,必須經過分支的流程全部執行完成才能到達匯聚結點,並行網關或包含風關就結束。
