workflow筆記


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表達式中使用流程變量

 

         網關:

                   排他網關:

                            經過排他網關的分支只有一條。

                   並行網關:

                            經過並行網關分支結點所有分支不管條件是否滿足都 會經過並行網關的匯聚結點。

            並行網關常用於會簽任務(多個用戶共同辦理的任務)

        包含網關:

                            經過包含網關分支結點,只有滿足條件的分支才經過包含網關的匯聚結點。

第六步:流程完成

                   所有結點完成執行完成,流程自動完成。

                   注意:如果使用並行網關或包含網關,必須經過分支的流程全部執行完成才能到達匯聚結點,並行網關或包含風關就結束。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM