Activiti入門文檔

Activiti-相關API

本文介紹與Activiti工作流具體操作相關的API。

第一部分 幾個Service相關的API

1.流程定義API--RepostoryService API

activiti.cfg.xml是一個spring依賴注入的xml文件,其具體內容可以參考
Spring集成activiti
測試類

public class ProcessDefinitionTest {
    // activitii.cfg.xml已經在classpath目錄下,直接加載默認的流程引擎即可
    private ProcessEngine processEngine = processEngines.getDefaultProcessEngine();
}

1.1 部署流程定義

和流程部署相關的表

表名中文名解釋
act_re_deployment部署對象信息表存放流程定義的顯示名稱和部署時間,部署一次增加一條記錄,注意:當流程定義的key相同的情況下,使用的是版本升級
act_re_procdef流程定義表存放流程定義的屬性信息,部署每一個新的流程定義都會在這張表中增加一條記錄
act_ge_bytearray資源文件表存儲流程定義相關的部署信息,即流程定義文檔的存放地。每部署一次就會增加兩條記錄,一條是關於bpmn規則文件的,一條是圖片的(如果部署時只指定了bpmn一個文件,activiti會在部署時解析bpmn文件內容自動生成流程圖),兩個文件不是很大,都是以二進制形式存儲在數據庫中。
act_ge_property主鍵生成策略表-

從classpath部署流程

@Test
public void deploymentProcessDefinition_classpath() {
    
    Deployment deployment = processEngine.getRepositoryService() // 獲取流程定義和部署對象相關的Service
            .createDeployment() // 創建一個部署對象
            .name("helloworld入門程序") //添加部署名稱
            .addClasspathResource("diagrams/helloworld.bpmn") // 從classpath的資源中加載,一次只能加載一個文件
            .addClasspathResource("diagrams/helloworld.png") // 從classpath的資源中加載,一次只能加載一個文件
            .deploy(); // 完成部署
            
    System.out.println(deployment.getId()); // 1
    System.out.println(deployment.getName()); // helloworld入門程序
}

從zip部署流程

// 從zip部署流程
// 打包helloworld文件,包含helloworld.bpmn, helloworld.png, 存放到classpath目錄下
@Test
public void deploymentProcessDefinition_zip() {
    InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrams/helloworld.zip")
    ZipInputStream zipInputStream = new ZipInputStream(in);
    
    // 1.部署流程定義-- activitii.cfg.xml已經在classpath目錄下,直接加載默認的流程引擎即可
    ProcessEngine processEngine = processEngines.getDefaultProcessEngine();

    Deployment deployment = processEngine.getRepositoryService() // 獲取流程定義和部署對象相關的Service
            .createDeployment() // 創建一個部署對象
            .name("helloworld入門程序") //添加部署名稱
            .addZipInputStream(zipInputStream) // 指定zip格式的文件完成部署
            .deploy(); // 完成部署
            
    System.out.println(deployment.getId()); // 1
    System.out.println(deployment.getName()); // helloworld入門程序
}

inputStream方式部署

// 通過addInputStream 方式部署流程定義實現--此處從絕對路徑,即classpath下查找bpmn和png資源
@Test
public void deploymentProcessDefinition_inputstream() {
    InputStream bmpnInputstream = this.getClass().getResourceAsStream("/diagrams/processVariables.bpmn");
    InputStream pngInputstream = this.getClass().getResourceAsStream("/diagrams/processVariables.bpmn");
    Deployment deployment = processEngine.getRepositoryService()
            .createDeployment()
            .name("流程定義")
            .addInputStream("processVariables.bpmn", bmpnInputstream)
            .addInputStream("processVariables.png", pngInputstream)
            .deploy();
    System.out.println("DEPLOYMENT_ID: " + deployment.getId());
    System.out.println("DEPLOYMENT_NAME: " + deployment.getName());
}

幾種獲取inputStrem的方式及其異同
當testVariables.bpmn文件存在於 /src/main/java/processDefine/helloworld.bpmn目錄下且下面的代碼在helloworld.bpmn的同目錄下的類中時,有以下幾種獲取InputStream的方式


// 從類加載器中加載input stream
InputStream is = this.getClass().getClassLoader().getResourceAsStream("processDefine/helloworld.bpmn") // 類加載器

// 從當前類的包下找
InputStream is = this.getClass().getResourceAsStream("helloworld.bpmn")

// 從classpath下找
InputStream is = this.getClass().getResourceAsStream("/processDefine/helloworld.bpmn")

1.2 查詢流程定義

查詢流程定義

// 查詢流程定義
@Test
public void findProcessDefinition() {
    List<ProcessDefinition> list = processEngine.getRepositoryService() // 與部署和流程定義相關的service
                .createProcessDefinitionQuery() // 創建一個流程定義查詢
                // 指定查詢條件,where條件
                // .deploymentId(deploymentId) // 使用部署對象ID查詢
                // .processDefinitionId(processDefinitionId) // 使用流程定義Id查詢
                // .processDefinitionKey(processDefinitionKey) // 使用流程定義的key查詢
                // .processDefinitionNameLike(processDefinitionNameLike) // 使用流程定義的名稱模糊查詢
                
                // 排序
                // .orderByProcessDefinitionVersion().asc() // 按照版本的升序排序
                .orderByProcessDefinitionName().desc() // 按照流程定義的名稱降序排序
                
                // 返回的結果集
                .list(); // 返回一個集合列表,封裝流程定義
                // .singleResult(); // 返回唯一結果集
                // .count(); // 返回結果集數量
                // .listPage(firstResult, maxResults); // 分頁查詢
    if (list != null && list.size() > 0) {
        for (ProcessDefinition pd:list) {
            System.out.println("流程定義Id: " + pd.getId()); // 流程定義的key + 版本 + 隨機生成樹
            System.out.println("流程定義名稱: " + pd.getName()); // 對應helloworld.bpmn文件中的name屬性值
            System.out.println("流程定義的key: " + pd.getKey()); // 對應helloworld.bpmn文件中的id屬性值
            System.out.println("流程定義的版本: " + pd.getVersion()); // 流程定義key相同時,版本升級
            System.out.println("資源名稱bpmn文件: " + pd.getResourceName());
            System.out.println("資源名稱png文件: " + pd.getDaigramResourceName());
            System.out.println("部署對象ID: " + pd.DeploymentId());
        }
    }
}

查詢所有最新的流程定義

// 查詢所有最新版本的流程定義
@Test
public void findLastVersionProcessDefinition() {
    List<ProcessDefinition> list = ProcessEngine processEngine = processEngines.getDefaultProcessEngine()
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc() // 使用流程定義的升序排列
                .list();
    
    Map<String, ProcessDefinition> map = new LinkedHashMap<>();
    if (list != null && list.size() > 0) {
        for (ProcessDefinition pd : list) {
            map.put(pd.getKey(), pd);
        }
    }
    List<PorcessDefinition> pdList = new ArrayList<>(map.values());
    
    if (pdList != null && pdList.size() > 0) {
        for (ProcessDefinition pd:pdList) {
            System.out.println("流程定義Id: " + pd.getId()); // 流程定義的key + 版本 + 隨機生成樹
            System.out.println("流程定義名稱: " + pd.getName()); // 對應helloworld.bpmn文件中的name屬性值
            System.out.println("流程定義的key: " + pd.getKey()); // 對應helloworld.bpmn文件中的id屬性值
            System.out.println("流程定義的版本: " + pd.getVersion()); // 流程定義key相同時,版本升級
            System.out.println("資源名稱bpmn文件: " + pd.getResourceName());
            System.out.println("資源名稱png文件: " + pd.getDaigramResourceName());
            System.out.println("部署對象ID: " + pd.DeploymentId());
        }
    }
}

小結

  1. 流程定義和部署對象相關的service都是RepositoryService
  2. 創建流程定義查詢對象,可以在ProcessDefinitionQuery上設置查詢的相關參數
  3. 調用ProcessDefinitionQuery對象的list方法,執行查詢,獲得符合條件的流程定義列表
  4. 運行結果可以看出,
    Key和Name的值為:bpmn文件process節點的id和name的屬性值
    <process id="LeaveFlow" name="請假流程" isExecutable="true">
  5. key屬性被用來區別不同的流程定義
  6. 帶有特定key的流程定義第一次部署時,版本為1.之後每次部署都會在當前最高版本號上加1
  7. Id的值的生成規則為: {processDefinitionKey}:{processDefinitionVersion}:{generated-id},這裏的generated-id是一個自動生成的唯一的數字
  8. 重複部署一次,deploymentId的值以一定的形式變化,規則act_ge_property表生成

1.3 刪除流程定義

刪除流程定義--根據部署ID刪除

// 刪除流程定義
@Test
public void deleteProcessDefinition() {
    String deploymentId = "1";
    
    // 不帶級聯的刪除
    // 只能刪除沒有啓動的流程,如果流程啓動,就會拋出異常
    // processEngine.getRepositoryService()
    //          .deleteDeployment(deploymentId);
                
    // 級聯刪除
    //  不管流程是否啓動,都可以刪除
    processEngine.getRepositoryService()
                .deleteDeployment(deploymentId, true);
                
    System.out.println("刪除成功!");
}

刪除流程定義--刪除key相同的所有不同版本的流程定義

// 刪除流程定義(刪除key相同的所有不同版本的流程定義)
@Test
public void deleteProcessDefinitionByKey() {
    String processDefinitionKey = "helloworld";
    // 先使用流程定義的key查詢流程定義,查詢出所有的版本
    List<ProcessDefinition> list = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .list();
    // 遍歷刪除 
    if (list != null && list.size() > 0) {
        for (ProcessDefinition pd:list) {
            // 獲取部署ID
            String deploymentId = pd.getDeploymentId();
            processEngine.getRepositoryService()
                    .deleteDeployment(deploymentId, true);
        }
    }
}

小結

  1. 因為刪除的是流程定義,而流程定義的部署是屬於倉庫服務的,所以應該先得到RepositoryService
  2. 如果該流程定義下沒有正在運行的流程,則可以用普通刪除。如果是有關聯的信息,用級聯刪除。項目開發中使用級聯刪除的情況比較多,刪除操作一般只開放給超級管理員使用。

1.4 查看流程圖

// 查看流程圖
@Test
public void viewPic() throws IOException {
    // 將生成的圖片放置到文件夾下
    String deploymentId = "801"; // act_ge_bytearray 表中的deployment_id字段
    
    // 獲取圖片資源名稱
    List<String> list = processEngine.getRepositoryService()
                .getDeploymentResourceNames(deploymentId); // 兩個值,一個是bpmn文件名,一個是png文件名
    String resourceName = "";
    if (list != null && list.size() > 0) {
        for (String name : list) {
            if (name.indexOf(".png") >= 0) {
                resourceName = name;
            }
        }
    }
    
    InputStream in = processEngine.getRepositoryService()
                .getResourceAsStream(deploymentId, resourceName);
    
    // 將圖片生成到D盤的目錄下
    File file = new File("D://" + resourceName);
    // 將輸入流的圖片寫到D盤下
    FileUtils.copyInputStreamToFile(in, file);
}

2.運行時RuntimeService API

流程實例對象:ProcessInstance代表流程定義的實例。如范冰冰請了一天的假,她就必須發出一個流程實例的申請。一個流程實例包括了所有的運行節點。我們可以利用這個對象來了解當前流程實例的進度等信息。流程實例就表示一個流程從開始到結束的最大的流程分支,即一個流程中流程實例只有一個。

執行對象:Activiti 用這個對象去描述流程執行的每一個節點。在沒有併發的情況下,Execution就是同 ProcessInstance。流程按照流程定義的規則執行一次的過程,就可以表示執行對象Execution。

流程圖依舊接着第一部分的流程圖(helloworld.bpmn)。
在做後面的啓動流程實例和完成任務的過程中,完成以下查詢。

select * from act_ru_execution   # 正在執行的執行對象列表
select * from act_hi_procinst    # 流程實例的歷史列表,啓動一個流程,產生一個流程實例歷史記錄,直到流程結束也只產生這一筆 記錄
select * from act_ru_task        # 正在執行的任務表(只有節點是UserTask的時候,該表中才會有數據)
select * from act_hi_taskinst    # 任務歷史表(只有節點是UserTask的時候,該表中才會有數據)
select * from act_hi_actinst     # 所有活動節點的歷史表(包括開始節點,任務節點,結束節點等)

測試類

public class ProcessInstanceTest {
    // activitii.cfg.xml已經在classpath目錄下,直接加載默認的流程引擎即可
    private ProcessEngine processEngine = processEngines.getDefaultProcessEngine();
}

2.1 啓動流程實例

// 啓動流程實例
// 默認啓動最新版本的流程
@Test
public void startProcessInstance() {
    String processEngine = "helloworld";
    ProcessInstance pi = processEngine.getRuntimeService()
                .startProcessInstanceByKey(processDefinitionKey) // 使用流程定義的key啓動實例,key對應helloworld.bpmn中Propertes流程的Id屬性值
    System.out.println(pi.getId()); // 流程實例Id   101
    System.out.println(pi.getProcessDefinitionId()); // 流程定義Id:   1:4
}

做一下如下查詢

select * from act_ru_execution
select * from act_hi_procinst

會發現,流程實例和執行對象相同。

2.2 查詢流程狀態

查詢運行時是否存在流程實例來判斷流程是正在進行中還是已結束。

// 查詢流程狀態(判斷流程正在執行,還是結束)
@Test
public void isProcessEnd() {
    String processInstanceId = "1001";
    ProcessInstance pi = processEngine.getRuntimeService() // 正在執行的流程實例和執行對象
                .createProcessInstanceQuery() // 創建流程實例查詢
                .processInstanceId(processInstanceId) // 根據流程實例ID查詢
                .singleResult();
    if (pi == null) {
        System.out.println("流程已結束");
    } else {
        System.out.println("流程沒有結束");
    }
}

3.TaskService API

流程圖接着第一部分的流程圖(helloworld.bpmn)。
測試類接着第二部分的測試類。

3.1 查詢指定人(assignee)的用户的用户任務

// 查詢當前人的個人任務
@Test
public void findMyPersonTask() {
    String assignee = "張三";
    List<Task> list = processEngine.getTaskService() // 與正在執行的任務管理相關的service;
                .createTaskQuery()
                // 查詢條件,where部分
                .taskAssignee(assignee) // 指定個人任務查詢,指定辦理人
                // .taskCandidateUser(candidateUser) // 組任務的辦理人查詢
                // .processDefinitionId(processDefinitionId) // 使用流程定義ID查詢
                // .processInstanceId(processInstanceId) // 使用流程實例ID查詢
                // .executionId(executionId) // 使用執行對象ID查詢
                
                // 排序
                // .orderByTaskDescription().asc() // 
                // .orderByTaskAssignee().asc() // 按處理
                // .orderByTaskCreateTime().asc() // 按照創建時間排序
                
                // 返回結果集
                // .singleResult(); // 範圍唯一結果集
                // .count(); // 返回結果集的數量
                // .listPage(first, max); // 分頁查詢
                .list(); // 返回列表
    if (list != null && list.size() > 0) {
        for (Task task: list) {
            System.out.println("任務Id:" + task.getId());
            System.out.println("任務名稱:" + task.getName());
            System.out.println("任務創建時間:" + task.getCreateTime());
            System.out.println("任務辦理人:" + task.getAssignee());
            System.out.println("流程實例ID:" + task.getProcessInstanceId());
            System.out.println("執行對象ID:" + task.getExecutionId());
            System.out.println("流程定義ID:" + task.getProcessDefinitionId());
            System.out.println("######################################################");
        }
    }
}

3.2 完成任務

// 完成我的任務
@Test
public void completeMyPersonTask() {
    String taskId = "104";
    processEngine.getTaskService()
                .complete(taskId);
    System.out.println("完成任務,任務ID: " + taskId);
}

4.HistoryService API

測試類

public class HistoryQueryTest {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
}

4.1 查詢指定某人的歷史任務

// 根據歷史任務辦理人查詢歷史任務
@Test
public void findHistoryTask() {
    String taskAssignee = "張三";
    List<HistoricTaskInstance> list = processEngine.getHistoryService() // 與歷史數據(歷史表)相關的service
                .createHistoricTaskInstanceQuery()
                .taskAssignee(taskAssignee) // 指定歷史任務的辦理人
                .list();
    if (list != null && list.size() > 0 ) {
        for (HistoricTaskInstance hti: list) {
            System.out.println(hti.getId() + "   " + hti.getName() + "   " + hti.getProcessInstanceId() + "   " + hti.getStartTime() 
                    + "   " + hti.getEndTime() + "   " + hti.getDurationInMillis());
            System.out.println("####################################################");
        }
    }
}

4.2 流程執行完後,可以根據流程實例ID查詢流程歷史

流程執行完後,歷史流程實例的endTime,durationInMillis將會有數據,在執行完之前,endTime,durationInMillis為空。

// 根據流程實例ID查詢歷史流程實例
@Test
public void findHistoryProcessInstance() {
    String processInstanceId = "1001";
    HistoricProcessInstance hpi = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery() // 歷史流程實例查詢
                .processInstanceId(processInstanceId) // 使用流程實例ID查詢
                .singleResult();
    System.out.println(hpi.getId() + ", " + 
            hpi.getProcessDefinitionId() + ", " + 
            hpi.getStartTime() + ", " + 
            hpi.getEndTime() + ", " + 
            hpi.getDurationInMillis());
}

4.3 根據流程實例ID查詢歷史流程實例

@Test
public void findHistoryProcessInstanceHistory() {
    String processInstanceId = "";
    HistoryProcessInstance hpi = processEngine.getHistoryService()
                .createHisotricProcessInstanceQuery()
                .processInstanceId(processInstanceId()
                .singleResult();
}

4.4 根據流程實例ID查詢歷史活動

@Test
public void findHisotryActiviti() {
    String processInstanceId=""
    List<HistoricActivityInstance> list = processEngine.getHistoryService();
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId())
                    .orderByHistoricActivityInstanceStartTime().asc()
                    .list();
    // 遍歷並打印:活動類型,開始時間,結束時間,活動耗時
    // sysout: list-> hai: activityType, startTime, endTime, durationInMillis
}

4.5 根據流程實例ID查詢歷史任務

@Test
public void findHistoricTask() {
    String processInstanceId=""
    List<HistoricTaskInstance> list = processEngine.getHistoryService();
                    .createHistoricTaskInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricTaskInstanceStartTime().asc()
                    .list();
    // 遍歷並打印:任務ID,任務名稱,流程實例ID,開始時間,結束時間,任務完成耗時
    // sysout: list-> hai: id, name, processInstanceId, startTime, endTime, durationInMillis
}

4.6 根據流程實例ID查詢歷史變量

@Test
public void findHistoryVariables() {
    String processInstanceId=""
    List<HistoricVariableInstance> list = processEngine.getHistoryService();
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricTaskInstanceStartTime().asc()
                    .list();
    // 遍歷並打印:流程變量ID,流程實例ID,變量名,變量類型名,變量值
    // sysout: list-> hai: id, processInstanceId, variableName, variableTypeName, value
}

第二部分 核心知識點

1.流程變量

流程變量的作用:

  1. 用來傳遞業務參數
  2. 指定連線完成任務(同意和拒絕)
  3. 動態指定任務的辦理人

和流程變量相關的表:

column中文名
act_ru_variable正在執行的流程變量表
act_hi_varinst歷史的流程變量表

測試類定義如下

public class ProcessVariablesTest {
    ProcessEngine processEngine = ProccessEngines.getDefaultProcessEngine();
    // 部署流程定義,此處建議採用inputStream方式部署,參考流程定義部分代碼
    // 啓動流程代碼
    
}

processVariables.bpmn
請假流程-processVariables.bpmn

流程變量支持的數據類型

string
long
double
serializable
binary
date
short
integer

1.1 在任務中設置流程變量--taskService

// 設置流程變量
@Test
public void setVariables() {
    // 與任務相關的,正在執行的
    TaskService taskService = processEngine.getTaskService();
    String taskId = "1504";
    // 設置流程變量,使用基本數據類型
    taskService.setVariableLocal(taskId, "請假天數", 3); // 與任務ID綁定,當任務流轉到下一個任務時,將不能再查詢到這個參數
    taskService.setVariable(taskId, "請假日期", new Date());
    taskService.setVariable(taskId, "請假原因", "回家探親");
    
    System.out.println("設置流程變量成功!");
}

1.2 獲取流程變量

// 獲取流程變量
@Test
public void getVariables() {
    // 與任務相關的,正在執行的
    TaskService taskService = processEngine.getTaskService();
    String taskId = "1504";
    Integer days = (Integer)taskService.getVariable(taskId, "請假天數");
    Date date = (Date)taskService.getVariable(taskId, "請假日期");
    String reason = (String)taskService.getVariable(taskId, "請假原因");
    System.out.println("請假天數:" + days + ", 請假日期: " + date + ", 請假原因: " + reason);
}

1.3 其它幾種設置流程變量的方式

用HashMap同時設置多個參數--runtimeService和taskService均可設置流程變量

runtimeService.setVariables(executionId, variables) // variables: Map集合,表示使用執行對象ID和Map集合設置流程變量的名稱,map集合的value就是變量的值

taskService.setVariable(taskId, variab****les) // variables: Map集合

啓動流程實例時設置流程變量

runtimeService.startProcessInstanceByKey(processDefinitionKey, variables) // 表示啓動流程實例的時候可以設置map參數

完成任務時設置流程變量

taskService.complete(taskId, variables) // 表示完成實例的時候可以設置map參數

設置流程變量僅在當前任務有效--Local

runtimeService.setVariableLocal(executionId, variableName, value)
taskService.setVariableLocal(executionId, variableName, value)

1.4 其它幾種獲取流程變量的方式

根據variableName獲取值--runtimeService和taskService均可獲取流程變量

runtimeService.getVariable(executionId, variableName) // 使用執行對象Id,和流程變量的名稱,獲取流程變量的值
taskService.getVariable(taskId, variableName) // 使用任務Id,和流程變量的名稱,獲取流程變量的值

根據執行對象ID或taskId獲取鍵值的HashMap集合(全部的)

runtimeService.getVariables(executionId) // 使用執行對象Id,將流程變量放置到Map集合中
taskService.getVariables(taskId) // 使用任務Id,將流程變量放置到Map集合中

傳入指定的variableNames(HashMap)獲取鍵值的HashMap集合(指定name的)

runtimeService.getVariables(executionId, variableNames) // 使用執行對象ID獲取流程變量的值,通過設置流程變量的名稱存放到一個集合中,獲取指定流程變量名稱的流程變量值的集合
taskService.getVariables(taskId, variableNames) // 使用任務Id獲取流程變量的值,通過設置流程變量的名稱存放到一個集合中,獲取指定流程變量名稱的流程變量值的集合

1.5 彙總

// 模擬設置和獲取流程變量的場景
@Test
public void setAndGetVariables() {
    // 與流程實例,執行對象相關的
    RuntimeService runtimeService = processEngine.getRuntimeService()

    // 與任務相關的,正在執行的
    TaskService taskService = processEngine.getTaskService();

    // runtimeService.setVariable(executionId, variableName, value) // 表示使用執行對象ID,和流程變量的名稱,設置流程變量的值(一次只能設置一個值)
    // runtimeService.setVariables(executionId, variables) // variables: Map集合,表示使用執行對象ID和Map集合設置流程變量的名稱,map集合的value就是變量的值

    // taskService.setVariable(taskId, variableName, value) 
    // taskService.setVariable(taskId, variables) // variables: Map集合

    // runtimeService.startProcessInstanceByKey(processDefinitionKey, variables) // 表示啓動流程實例的時候可以設置map參數

    // taskService.complete(taskId, variables) // 表示完成實例的時候可以設置map參數
    
    // 獲取流程變量
    // runtimeService.getVariable(executionId, variableName) // 使用執行對象Id,和流程變量的名稱,獲取流程變量的值
    // runtimeService.getVariables(executionId) // 使用執行對象Id,將流程變量放置到Map集合中
    // runtimeService.getVariables(executionId, variableNames) // 使用執行對象ID獲取流程變量的值,通過設置流程變量的名稱存放到一個集合中,獲取指定流程變量名稱的流程變量值的集合

    // taskService.getVariable(taskId, variableName) // 使用任務Id,和流程變量的名稱,獲取流程變量的值
    // taskService.getVariables(taskId) // 使用任務Id,將流程變量放置到Map集合中
    // taskService.getVariables(taskId, variableNames) // 使用任務Id獲取流程變量的值,通過設置流程變量的名稱存放到一個集合中,獲取指定流程變量名稱的流程變量值的集合
}

1.6 傳參-javabean

定義Person類

public class Person implements Serializable{
    private Integer id;
    
    private String name;
    // getter ... setter ...
}

設置流程變量

Person p = new Person();
p.setId(10);
p.setName("翠花");
taskService.setVariable(taskId, "人員信息", p);

值得注意的是,serializable類型的參數值是二進制類型的,會存放到 act_ge_bytearray 表中,當一個javabean(實現序列化)放置到流程變量中,要求javabean的屬性不能再發生變化,如果發生變化,再獲取的時候,拋出異常(假定你在Person中再加一個education屬性,那麼你再獲取先前set的值,則取不出來了),要解決這個問題,需要給Person類添加序列化ID。

publlic static final long serialVersionUID=253678908763728903L;

獲取person

@Test
public void getVariables() {
    Person p = (Person) taskService.getVariable(taskId, "人員信息");
    System.out.println("person.id = " + p.getId() + ", person.name = " +  p.getName());
}

1.7 查詢歷史的流程變量

@Test
public void findHistoryProcessVariables() {
    List<HistoricVariableInstancce> list = processEngine.getHistoryService()
                .createHistoricVariableInstanceQuery()
                .variableName("請假天數")
                .list();
    if(list != null and list.size > 0) {
        for (HistoricVariableInstancce hvi: list) {
            System.out.println("id = " + hvi.getId() 
                    + ", processInstanceId = " + hvi.getProcessInstanceId()
                    + ", variableName = " + hvi.getVariableName()
                    + ", variableValue = " + hvi.getVariableValue());
            System.out.println("######################");
        }
    }
}

2.連線

流程圖
1512377811782.jpg

測試類

public class SequenceFlowTest {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 部署流程
    // 啓動流程實例
}

參數傳入“不重要”,走連線a

// 完成個人任務
@Test
public void completePersonProcess() {
    String taskId = "";
    Map<String, Object> variables = new HashMap<>();
    variables.put("message", "不重要");
    proccessEngine.getTaskService
            .complete(taskId, variables);
    System.out.println("taskId = " + taskId);
}

參數傳入“重要”,走連線b

// 完成個人任務2 -- 會執行到總經理再結束
@Test
public void completePersonProcess() {
    String taskId = "";
    Map<String, Object> variables = new HashMap<>();
    variables.put("message", "重要");
    proccessEngine.getTaskService
            .complete(taskId, variables);
    System.out.println("taskId = " + taskId);
}

3.排他網關

流程圖
1512379519961.png

測試類

public class ExclusiveGateWayTest {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 部署流程
    // 啓動流程實例
}

完成任務--money參數分別傳入800,1200,300

    // 完成任務
    completeMyPersonTask() {
        String taskId = "";
        Map<String, Object> variables = new HashMap<>();
        variables.put("money", 800); // 800 -> 部門經理射頻
                                    // 1200 -> 總經理
                                    // 300  -> 財務
        proccessEngine.getTaskService
                .complete(taskId, variables);
        sysout: taskId
    }

小結

  1. 一個排他網關對應一個以上的順序流
  2. 由排他網關流出的順序流都有個conditionExpression 元素,在內部維護返回boolean類型的決策結果。
  3. 決策網關只會返回一條結果,當流程執行到排他網關時,流程引擎會自動檢索網關出口,從上到下檢索如果發現第一條決策結果為true或者沒有設置條件的(默認為成立),則流出。
  4. 如果沒有任何一個出口符合條件,則拋出異常。
  5. 如果流程變量,設置連線的條件,並按照連線的條件執行工作流,如果沒有符合的條件,則以默認的連線(Default Flow)離開。

4.並行網關

並行網關可以表示分支和聚合 -- 流程實例和執行對象將不再是同一個
流程圖
1512457696270.png

測試類

public class ParallelGateWayTest {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 部署流程
    // 啓動流程實例
}

依次完成任務

// 完成任務
@Test
public void completeMyPersonTask() {
    String taskId = "";
    proccessEngine.getTaskService()
            .complete(taskId);
    sysout: taskId
}

流程執行過程

當啓動流程實例時

select * from act_ru_execution
3條記錄,一個流程實例,兩個執行對象

act_hi_procinst  流程實例的歷史表
1條記錄

act_ru_task    正在執行的任務
2個,付款和發貨

act_hi_taskinst   歷史任務表
2個,付款和發貨

act_hi_actinst  歷史活動
4個,付款,發貨,開始和並行網關

當所有任務完成後

select * from act_ru_execution
0

act_hi_procinst  流程實例的歷史表
1條記錄

act_ru_task    正在執行的任務
0

act_hi_taskinst   歷史任務表
4個,付款,發貨,收貨,收款

act_hi_actinst  歷史活動
9個,付款,發貨,收貨,收款,開始,結束,並行網關1(分支),並行網關2-商家(聚合),並行網關2-廠家(聚合)

小結

  1. 一個流程中流程實例只有1個,執行對象有多個
  2. 並行網關的功能是基於進入和外出的順序流的:
    分支:並行後的所有外出順序流,為每個順序流都創建一個併發分支
    聚合:所有到達並行網關,在此等待的進入分支,直到所有進入順序流的分支都到達以後,流程就會通過匯聚網關
  3. 並行網關的進入和外出都是使用相同節點標識
  4. 如果同一個並行網關有多個進入和多個外出順序流,它就同時具有分支和匯聚功能。這時,網關會先匯聚所有進入的順序流,然後再切分成多個並行分支。
  5. 並行網關並不會解析條件。即使順序流中定義了條件,也會被忽略。
  6. 並行網關不需要是“平衡的”(比如,對應並行網關的進入和外出節點數目不一定相等)
    圖-並行網關進入和外出節點數並不一定相等

5.開始活動

流程圖
1512458346366.png
開始活動節點流程圖就是從開始節點直接到結束節點,基本上沒多大意義。

代碼

1512458262248.png

小結

  1. 結束節點沒有出口
  2. 其它節點有一個或多個出口
    如果有一個出口,則代表是一個單線流程
    如果有多個出口,則代表是開啓併發流程

6.接收任務活動

概念

接收任務是一個簡單任務,它會等待對應消息的到達。當前,官方只實現了這個任務的java語義。當流程達到接收任務,流程狀態會保存到數據庫中。

在任務創建後,意味着流程會進入等待狀態直到引擎接收了一個特定的消息,這會觸發流程穿過接收任務繼續執行。

流程圖

1512460362135.png

代碼

// 啓動流程實例推斷流程是否結束 + 查詢歷史
@Test
public void startProcessInstance() {
    // 流程定義的key
    String processDefinitionKey = "receiveTask";
    ProcessInstance pi = processEngine.getRuntimeService() //  與正在執行的流程實例和執行對象相關的 Service
                .startProcessInstanceByKey(processDefinitionKey); // 使用 流程定義的key啓動流程實例,key對應helloworld.bpmn文件中id屬性的值
    System.out.println("流程實例ID:" + pi.getId());
    System.out.println("流程實例ID:" + pi.getId());
    
    // 查詢執行對象ID
    Execution execution1 = processEngine.getRuntimeService()
                .createExecutionQuery() // 創建執行對象查詢
                .processInstanceId(pi.getId()) // 使用流程實例ID查詢
                .activityId("receivetask1") // 當前活動的id,對應receiveTask.bpmn文件中的活動節點id的屬性值
                .singleResult();
    // 使用流程變量設置當日銷售額,用來傳遞業務參數
    processEngine.getRuntimeService()
                .setVariable(execution1.getId(), "彙總當日銷售額", 21000);
                
    // 向後執行一步,如果流程處於等待狀態,使得流程繼續執行
    processEngine.getRuntimeService()
                .signal(execution1.getId());
    // 查詢執行對象ID
    Execution execution2 = processEngine.getRuntimeService()
                .createExecutionQuery() // 創建執行對象查詢
                .processInstanceId(pi.getId()) // 使用流程實例ID查詢
                .activityId("receivetask2") // 當前活動的id,對應receiveTask.bpmn文件中的活動節點id的屬性值
                .singleResult();
    // 向後執行一步,如果流程處於等待狀態,使得流程繼續執行
    processEngine.getRuntimeService()
                .signal(execution2.getId());
    // 執行完後,流程結束
}

小結:

  1. 當前任務(一般指機器自動完成,但需要耗費一定時間的工作)完成後,向後推移流程,可以調用runtimeService.signal(executionId),傳遞接收執行對象的ID
  2. receiveTask在act_ru_task表中是不會產生數據的

7.任務分配

任務分配包括個人任務分配和組任務分配,兩者均存在3種任務分配方式:

  • 直接指定辦理人--先前的任務分配均是此種,指定具體的人來辦理;
  • 使用流程變量動態分配;
  • 實現 org.activiti.engine.delegate.TaskListener 接口動態分配任務。

7.1 個人任務分配

Ⅰ 直接指定任務辦理人

略,前面幾節講的都是直接在流程定義中指定Assignee來指定任務辦理人。
1512461235390.jpg

Ⅱ 使用流程變量動態分配任務辦理人

如下圖所示,在任務的Properties中指定Assignee為一個變量${userId}
1512461348570.jpg

我們可以在流程啓動時,指定流程變量userId

Map<String, Object> variables = new HashMap<>();
variables.put("userId", "周芷若");
processEngine.getRuntimeService().startProcessInstanceByKey(processInstanceKey, variables);

當然,你也可以在其它地方指定流程變量userId,譬如,本次任務完成時,指定下一個任務的辦理人,在某個條件下通過taskService.setVariable(taskId, "userId", "王五");來指定任務辦理人。

Ⅲ 實現 org.activiti.engine.delegate.TaskListener 接口動態分配任務。

先設置Main Config下的Assignee的值為空

在Listener下,指定類 TaskListenerImpl ,實現接口org.activiti.engine.delegate.TaskListener

1512461966404.jpg

New的內容如下:

Event=create
Type=java class
點擊Select Class去選擇TaskListenerImpl

TaskListenerImpl 類定義如下

public class TaskListenerImpl implements TaskListener {

    public void notify(DeletegateTask delegateTask) {
        // 指定個人任務的辦理人,也可以指定組任務的辦理人
        // 個人任務:通過類去查詢數據庫,將下一個任務的辦理人查詢獲取,然後通過 setAssignee() 方法指定任務的辦理人
        delegateTask.setAssignee("滅絕師太");
    }
}

在啓動流程實例時,會執行回調函數。

當然,你也可以在指定了任務辦理人後再重新分配任務辦理人(認領任務)。

String taskId = "5804";
String userId = "張翠山";
processEngine.getTaskService()
        setAssignee(taskId, userId);

小結

個人任務及三種分配方式:

  1. 在taskProcess.bpmn中直接寫assignee="張三丰"
  2. 在taskProcess.bpmn中寫 assignee="#{userId}",變量的值是String的。使用流程變量指定辦理人。
  3. 使用TaskListener接口,要使類實現該接口,在實現方法中調用 delegateTask.setAssignee(assignee) 來指定個人任務的辦理人。

最後,還可以通過任務ID和辦理人重新指定辦理人:

processEngine.getTaskService().setAssignee(taskId, userId);

7.2 組任務分配

流程圖

1512526766605.png

和組任務相關的表
a.任務表(個人任務,組任務)

select * from act_ru_identitylink 
-- 其中字段 TYPE的值有如下兩個:
      participant // 參與者
      candidate // 候選者

b.歷史任務辦理人表

select * from act_hi_identitylink 
該表中,個人任務的TYPE_都為participant(參與者)
每一個組任務用户的TYPE_包含participant和candidate兩條數據

做如下操作

1.部署流程
2.啓動流程實例

Ⅰ 直接指定任務辦理人

查詢我的組任務

// 查詢我的組任務
findMyGroupTask() {
    String candidateUser = "小A";
    List<Task> list = processEngine.getTaskService
        .createTaskQuery
        .taskCandidateUser(candidateUser)
        .orderByTaskCreateTime().asc()
        .list()
}

查詢正在執行的任務辦理人表

// 查詢正在執行的任務辦理人表
findRunPersonTask() {
    String taskId = "";
    List<IdenityLink> list = processEngine.getTaskService
                .getIdentityLinksForTask(taskId)
    if list != null && list.size > 0
        for tl:list
            sysout: tl -> taskId, type, processInstanceId, userId // 小C, 小D, 小B, 小A
}

查詢歷史任務的辦理人表

// 查詢歷史任務的辦理人表
findHistoryPersonTask() {
    String processInstanceId = "";
    List<HistoricIdentityLink> list = processEngine.getHistoryService
                .getHistoricIdentityLinksForProcessInstance(processInstanceId)
    if list != null && list.size > 0
        for hil:list
            sysout: tl -> taskId, type, processInstanceId, userId 
}

拾取任務,將組任務分配給個人任務,指定任務的辦理人字段

// 拾取任務,將組任務分配給個人任務,指定任務的辦理人字段
claim() {
    // 將組任務分配給個人任務
    String taskId = "";
    // 分配的個人任務(可以是組任務中的成員,也可以是非組任務的成員)
    String userId = "小C" // 後再改為大F可以測試非組任務成員,指定了大F之後,再用小A查它的組任務結果為空,可以通過下面的方法改回到組任務
    proccessEngine.getTaskService
                .claim(taskId, userId);
}

將個人任務回退到組任務,前提,之前一定是個組任務

// 將個人任務回退到組任務,前提,之前一定是個組任務
setAssignee() {
    String taskId ="";
    processEngine.getTaskService
                .setAssignee(taskId, null);
}

向組任務中添加成員

// 向組任務中添加成員
addGroupUser() {
    String taskId = "";
    String userId = "大H";
    processEngine.getTaskService
                .addCadidateUser(taskId, userId);
}

從組任務中刪除成員

// 從組任務中刪除成員
deleteGroupUser() {
    String taskId = "";
    String userId = "小B";
    processEngine.getTaskService
                .deleteCandidateUser(taskId, userId); // 刪除後查詢小B的組任務,結果為空。但刪除僅刪除了小B的候選者數據,並沒有刪除參與者數據。
}

説明:

  1. 小A,小B,小C,小D是組任務的辦理人
  2. 這樣分配組任務的辦理人不夠靈活,因為項目開發中任務的辦理人不要放置XML文件中
  3. act_ru_identitylink表存放任務的辦理人,包括個人任務和組任務,表示正在執行的任務。
  4. act_hi_identitylink表存放任務的辦理人,包括個人任務和組任務,表示歷史任務

區別在於:
如果是個人任務TYPE的類型表示participant(參與者)
如果是組任務TYPE的類型表示candidate(候選者)和participant(參與者)

Ⅱ 通過流程變量指定組任務成員

修改bpmn文件,指定Candidate users

Main Config/Candidate users: #{userIDs}

啓動流程實例時指定組成員

// 啓動流程實例時指定組成員
startProcessInstance
    variables.put("userIDs", "大大,中中,小小");

之後完成下面的步驟

  1. 啓動完後,查詢大大的組任務
  2. 指定大大拾取claim任務
  3. 拾取後,正在執行的任務表中,就有大大了
  4. 完成任務
  5. 查詢歷史任務表就能查詢到大大完成了任務

Ⅲ 使用TaskListener來分配組任務

在bpmn文件中指定Listener

Listeners
    create class TaskListenerImpl implements TaskListener

編寫TaskListenerImpl 類

public class TaskListenerImpl implements TaskListener {

    public void notify(DeletegateTask delegateTask) {
        delegateTask.addCadidateUser("郭靖")
        delegateTask.addCadidateUser("黃蓉")
        // delegateTask.addCadidateUsers(Collection)
    }
}

後續完成下面的步驟

  1. 部署流程
  2. 啓動流程
  3. 查詢表:run任務表,act_run_execution表,act_ru_identitylink表
  4. 用郭靖查組任務
  5. 用郭靖拾取claim任務
  6. 查詢執行任務表中,郭靖在表中
  7. 查詢郭靖的個人任務
  8. 完成個人任務
  9. 查詢表,act_hi_identitylink表,查詢歷史任務表

説明:

  1. 在類中使用delegateTask.addCandidateUser(userId); 的方式分配組任務的處理人,此時郭靖和黃蓉是下一個任務的辦理人。
  2. 通過processEngine.getTaskService().claim(taskId, userId); 將組任務分配給個人任務,也叫認領任務,即指定某個人去辦理這個任務,此時由郭靖辦理任務。

    注意:認領任務的時候,可以是組任務成員中的人,也可以不是組任務成員中的人,此時通過TYPE的類型participant來指定任務的辦理人

  3. addCandidateUser()即向組任務添加成員,deleteCandidateUser()即刪除組任務的成員
  4. 在開發中,可以將每一個人物的辦理人規定好,例如張三的領導是李四和王五,這樣張三提交任務,由李四或者王五去查詢組任務,可以看到對應張三的申請李四或王五在通過認領任務(claim)的方式,由某個人去完成這個任務。

小結:

組任務及三種分配方式:

  1. 在taskProcess.bpmn中直接寫candidate-users="小A,小B,小C,小D"
  2. 在在taskProcess.bpmn中寫candidate-users="#{userIDs}",變量的值要是String的。
    使用流程變量指定辦理人的代碼如下
Map<String, Object> variables = new HashMap<>();
variables.put("userIDs", "大大,中中,小小");
  1. 使用TaskListener接口,使用類實現該接口,在類中定義:
// 添加組任務的用户
delegateTask.addCandidateUser(userId1);
delegateTask.addCandidateUser(userId2);

組任務分配給個人任務

processEngine.getTaskService().claim(taskId, userId);

個人任務分配給組任務:

processEngine.getTaskService().setAssignee(taskId, null);

向組任務添加成員:

processEngine.getTaskService().addCandidateUser(taskId, userId)

向組任務刪除人員:

processEngine.getTaskService().deleteCandidateUser(taskId, userId)

個人任務和組任務存放辦理人對應的表:

- act_ru_identitylink 表存放任務的辦理人,包括個人任務和組任務表,表示正在執行的任務
- act_hi_identitylink 表存放任務的辦理人,包括個人任務和組任務表,表示歷史任務

區別:
- 如果是個人任務TYPE的類型表示participant(參與者)
- 如果是組任務TYPE的類型表示candidate(候選者)和participant(參與者)

8. 工作流定義的角色組(瞭解)

流程圖
1512528836182.png

注:

  • bpmn文件中指定角色組
  • Main Config/Candidate groups 指定角色組:部門經理

在deploymentprocessDefinition方法的最後

// 添加用户角色組
IdentityService identityService = processEngine.getIdentityService()
// 創建角色
// GroupEntity是Group接口的子類
identityService.saveGroup(new GroupEntity("總經理"))
identityService.saveGroup(new GroupEntity("部門經理"))
// 創建用户
// UserEntity是User接口的子類
identityService.saveUser(new UserEntity("張三")/*.set  去嘗試一下這個操作*/);
identityService.saveUser(new UserEntity("李四"));
identityService.saveUser(new UserEntity("王五"));
// 建立用户和角色的關聯關係
identityService.createMembership("張三", "部門經理");
identityService.createMembership("李四", "部門經理");
identityService.createMembership("王五", "總經理經理");

sysout: 添加組織機構成功!

  • 部署流程
  • 查詢表
select * from act_id_group // 工作流提供的角色表
select * from act_id_user // 用户表
select * from act_id_membership // 用户角色關聯表
  • 啓動流程實例
  • 查詢act_ru_identitylink表,可以得知“審批”任務是一個組任務,其TYPE_字段值為candidate,其GROUP_ID的值為部門經理

  • 查詢張三的組任務,可以查詢到張三擁有“審批”這個組任務。
  • 查詢李四的組任務,可以查詢到李四擁有“審批”這個組任務。
  • 試一下王五呢,(已知:Main Config配置了角色)

  • 拾取任務給張三
  • 查詢act_ru_task,就可以看到assignee就有值了,其值為張三

  • 完成任務

【小主,覺得有用打賞點哦!多多少少沒關係,一分也是對我的支持和鼓勵。謝謝!】

轉載於:https://www.cnblogs.com/iuie/p/8006068.html