Flowable 快速入門教程:Flowable 入門開發案例,結合流程設計器詳細講解

前言

本文以一個簡答的 Demo 爲案例,按節點講解,目的是爲了讓剛接觸流程引擎的人能更快的熟悉流程引擎開發,瞭解業務系統與流程引擎結合的思路。

由於是 Demo,接口存在不完善之處,需要自己補充添加。

流程設計器集成

文章:Flowable 快速入門教程:SpringBoot 集成 Flowable + Flowable Modeler 流程配置可視化(超詳細)

整體流程圖

起始與結束節點,就配置了名稱方便查看,沒做額外配置
在這裏插入圖片描述

流程節點說明

第一審覈人節點:實際設置審覈人

在這裏插入圖片描述

配置信息

在這裏插入圖片描述
在這裏插入圖片描述

說明

這裏的分配人爲固定的人,用的使用戶的編號

任務完成時,直接 complete 即可

/**
 * 任務處理
 *
 * @param taskId   任務 Id,來自 ACT_RU_TASK
 * @param assignee 設置審覈人,替換
 * @param map      完成任務需要的條件參數
 * @return
 */
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {
    // 設置審覈人
    taskService.setAssignee(taskId, assignee);

    // 設置任務參數,也可不設置:key value,只是示例
    // 帶 Local 爲局部參數,只適用於本任務,不帶 Local 爲全局任務,可在其他任務調用參數
    taskService.setVariableLocal(taskId, "status", true);

    // 完成任務
    taskService.complete(taskId, map);
    logger.info("任務完成:" + taskId + " " + new Date());
}

第二審覈人:參數設置審覈人

在這裏插入圖片描述

配置信息

在這裏插入圖片描述
在這裏插入圖片描述

說明

變量:reviewer 節點審覈人

說明:通過參數的形式設定分配人,因此在需要在進入任務節點之前把對應參數注入。否則會提示表達式錯誤

疑問說明:如果我在審覈時候才設置審覈人,那我表單展示時如何知道審覈人是誰?

  1. 首先我這裏是第一次審覈時候就放進去的,因爲是 Demo 所有邏輯的嚴謹性上沒考慮那麼全
  2. 關於怎麼知道表單每條數據的審覈人有誰,個人建議自己建表來存關係最爲方便與準確。之後審覈時候審覈人可以在流程開始階段通過監聽器設置進變量或者直接審覈動作之前再設置都可以。

業務系統代碼

// 設置審覈人
feignClientService.setVariable(taskId, "reviewer", "bbb");
// 完成任務,taskId 任務節點 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系統代碼,同上

/**
 * 任務處理
 *
 * @param taskId   任務 Id,來自 ACT_RU_TASK
 * @param assignee 設置審覈人,替換
 * @param map      完成任務需要的條件參數
 * @return
 */
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {
    // 設置審覈人
    taskService.setAssignee(taskId, assignee);

    // 設置任務參數,也可不設置:key value,只是示例
    // 帶 Local 爲局部參數,只適用於本任務,不帶 Local 爲全局任務,可在其他任務調用參數
    taskService.setVariableLocal(taskId, "status", true);

    // 完成任務
    taskService.complete(taskId, map);
    logger.info("任務完成:" + taskId + " " + new Date());
}

第三審覈人:參數分支判斷與實際組配置

在這裏插入圖片描述

配置信息

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

說明

變量:assignee 節點審覈人

說明:分支判斷,如果是 admin 審覈的進入 分支審覈第四審覈人2號 節點,其他情況走 分支審覈第四審覈人1號 節點,默認走 分支審覈第四審覈人1號

組的配置不管是實際還是參數形式其實差不多,因爲都無法讓流程直接拿到審覈人。區別在於實際值方便那些不懂流程的人配置以及我們可以直接獲取組然後查找,而參數我們可以通過變量注入。還是建議自己建表存關係,降低開發難度

業務系統代碼

// 流程完成所需的條件參數
Map<String, Object> map = new HashMap<>();
map.put("assignee", user.getUsername());
// 完成任務,taskId 任務節點 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系統代碼,同上。之後流程引擎 complete 後會自動根據你的參數進行分支判斷

/**
 * 任務處理
 *
 * @param taskId   任務 Id,來自 ACT_RU_TASK
 * @param assignee 設置審覈人,替換
 * @param map      完成任務需要的條件參數
 * @return
 */
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {
    // 設置審覈人
    taskService.setAssignee(taskId, assignee);

    // 設置任務參數,也可不設置:key value,只是示例
    // 帶 Local 爲局部參數,只適用於本任務,不帶 Local 爲全局任務,可在其他任務調用參數
    taskService.setVariableLocal(taskId, "status", true);

    // 完成任務
    taskService.complete(taskId, map);
    logger.info("任務完成:" + taskId + " " + new Date());
}

會籤:多人並審與參數設置用戶組

在這裏插入圖片描述

配置信息

在這裏插入圖片描述

說明

變量:

  1. assigneeList:審覈人列表,注意這裏不能用 ${ } 包裹,否則系統拿到的就是對應的 value 值
  2. nrOfInstances:會籤環節實例總數,系統參數,可直接使用
  3. nrOfActiveInstances:還沒有完成的實例數量,系統參數,可直接使用
  4. nrOfCompletedInstances:已經完成的實例的數量,系統參數,可直接使用

多實例類型:

  1. Parallel:並行模式,多人同事審覈
  2. Sequential:串行模式,按順序審覈,順序爲集合順序(暫時沒試驗過)

說明:
assigneeList 這個參數必須在進入會籤任務之前注入,原因是任務在進入會籤任務時就需要根據 assigneeList 來生成 task 任務,因此這個參數必須存在。這裏可以利用監聽器在任務生成之前注入,或者在之前節點就注入。
在這裏插入圖片描述
會籤判斷條件:完成率達到 50% 進入下個節點

業務系統代碼,會籤節點之前
PS:注意集合參數在請求後,流程引擎那不能用 Object 類型來接收,會導致解析時無法轉化爲集合,因此我這裏集合單獨寫了個接口來設置參數

// 設置 assigneeList 參數
// 注意,這個參數是在進入會籤節點之前就設置了
feignClientService.setListVariable(taskId, "assigneeList", assigneeList);

業務系統代碼,會籤節點

// 完成任務,taskId 任務節點 ID
feignClientService.taskByAssignee(taskId, user.getUsername(), map);

流程引擎系統代碼,同上。之後流程引擎 complete 後會自動根據配置的表達式判斷節點是否完成。

/**
 * 任務處理
 *
 * @param taskId   任務 Id,來自 ACT_RU_TASK
 * @param assignee 設置審覈人,替換
 * @param map      完成任務需要的條件參數
 * @return
 */
@RequestMapping(value = "/task", method = RequestMethod.POST)
public void taskByAssignee(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "assignee") String assignee, @RequestBody Map<String, Object> map) {
    // 設置審覈人
    taskService.setAssignee(taskId, assignee);

    // 設置任務參數,也可不設置:key value,只是示例
    // 帶 Local 爲局部參數,只適用於本任務,不帶 Local 爲全局任務,可在其他任務調用參數
    taskService.setVariableLocal(taskId, "status", true);

    // 完成任務
    taskService.complete(taskId, map);
    logger.info("任務完成:" + taskId + " " + new Date());
}

監聽器會籤:結合監聽器實現多人並審

在這裏插入圖片描述

配置信息

在這裏插入圖片描述
在這裏插入圖片描述

說明

變量:okNum 已審覈的次數

說明:ExecutionListener 任務監聽器,每次有人審覈執行任務就把數字 +1,達到 3 個完成節點任務,
監聽器會在 complete 之前執行。

流程引擎系統代碼

/**
 * 會籤監聽器
 * @author: linjinp
 * @create: 2019-11-18 11:43
 **/
@Component
public class MyListener implements ExecutionListener {

    // 頁面配置參數注入
    private FixedValue num;

    @Override
    public void notify(DelegateExecution delegateExecution) {
        // 獲取頁面配置參數的值
        System.out.println(num.getExpressionText());

        // 校驗 okNum 是否已經存在
        if (!delegateExecution.hasVariable("okNum")) {
            delegateExecution.setVariable("okNum", 0);
        }
        // 已審覈次數,審覈一次 +1
        int okNum = (int) delegateExecution.getVariable("okNum") + 1;
        delegateExecution.setVariable("okNum", okNum);
    }
}

示例代碼

項目結構

我這裏業務系統與流程引擎系統是分開的,不處於一個項目,有各自的數據庫

業務系統:用戶數據,表單,權限等,這裏業務系統通過 feign 調用流程引擎的接口

流程引擎系統:流程引擎根據功能對接口進行封裝,通過 Restful 形式的接口對外開放

Demo 一共 5 個功能:業務數據列表查詢(這個不涉及流程引擎),審覈列表數據查詢,審覈功能,審覈歷史查詢,流程圖查看流程進度

代碼僅用作入門學習與邏輯參考

流程部署

ACT_DE_MODEL:保存後的流程模型信息
在這裏插入圖片描述

/**
 * 流程部署
 *
 * @param modelId 流程ID,來自 ACT_DE_MODEL
 * @return
 */
@RequestMapping(value = "/deploy/{modelId}", method = RequestMethod.GET)
public void deploy(@PathVariable(value = "modelId") String modelId) {
    // 根據模型 ID 獲取模型
    Model modelData = modelService.getModel(modelId);

    byte[] bytes = modelService.getBpmnXML(modelData);
    if (bytes == null) {
        logger.error("模型數據爲空,請先設計流程併成功保存,再進行發佈");
    }

    BpmnModel model = modelService.getBpmnModel(modelData);
    if (model.getProcesses().size() == 0) {
        logger.error("數據模型不符要求,請至少設計一條主線流程");
    }
    byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
    String processName = modelData.getName() + ".bpmn20.xml";

    // 部署流程
    repositoryService.createDeployment()
            .name(modelData.getName())
            .addBytes(processName, bpmnBytes)
            .deploy();

    logger.info("流程部署成功:" + modelId + " " + new Date());
}

啓動流程

ACT_RE_PROCDEF:流程定義相關信息,流程多次部署後的歷史信息也可以在這張表查看
在這裏插入圖片描述

/**
 * 啓動流程
 *
 * @param deployId 部署的流程 Id,來自 ACT_RE_PROCDEF
 * @param userId   用戶 Id
 * @param dataKey  數據 Key,業務鍵,一般爲表單數據的 ID,僅作爲表單數據與流程實例關聯的依據
 * @return
 */
@RequestMapping(value = "/start/{deployId}/{userId}/{dataKey}", method = RequestMethod.GET)
public void start(@PathVariable(value = "deployId") String deployId, @PathVariable(value = "userId") String userId, @PathVariable(value = "dataKey") String dataKey) {
    // 設置發起人
    identityService.setAuthenticatedUserId(userId);
    // 根據流程 ID 啓動流程
    runtimeService.startProcessInstanceById(deployId, dataKey);
    logger.info("流程啓動成功:" + deployId + " " + new Date());
}

業務數據列表查詢

頁面效果

在這裏插入圖片描述

功能描述

由於不涉及流程引擎,因此就不上代碼了。

由於表單與流程引擎本身是不存在關係的,因此這裏的狀態儲存是直接在業務數據表裏儲存狀態,查詢起來也簡單。

如果需要用到流程引擎中的參數,那就自己查詢就可以了。

審覈列表數據查詢

效果圖

在這裏插入圖片描述

功能描述

查詢當前用戶需要審覈的數據列表

業務系統代碼

整體邏輯:獲取流程中所有審覈人爲當前用戶的流程數據,返回保存在流程中的業務鍵以及其他需要的數據,然後對數據進行組合業務鍵也就是數據 ID 是在流程啓動時候設置到流程中的,詳細看啓動接口。

/**
 * 獲取審覈列表
 * @return
 */
@GetMapping(value = "/getApproveList")
public ErrorMsg getApproveList(HttpServletRequest request) {
    ErrorMsg errorMsg = new ErrorMsg();
    String token = request.getHeader("X-Token");
    User user = (User) redisTemplate.opsForValue().get(token);

    // 獲取用戶擁有的用戶組,admin,aaa,test
    List<Group> hasGroup = userPermissionDao.getUserGroup(user.getUsername());
    // 取出組 ID
    List<String> groups = new ArrayList<>();
    hasGroup.forEach(group -> {
        groups.add(group.getId());
    });

    // 調用流程引擎封裝的接口
    // 根據用戶組獲取需要審覈的數據對應的流程信息
    // 主要爲了滿足我設置的是實際組的情況
    List<Map<String, Object>> idListByGroupMapList = feignClientService.getRuntimeBusinessKeyByGroup(groups);

    // 調用流程引擎封裝的接口
    // 獲取自己發起的正在進行的審覈數據對應的流程信息
    List<Map<String, Object>> idListByUserMapList = feignClientService.getRuntimeBusinessKeyByUser(user.getUsername());
    // 整合兩個 list
    idListByUserMapList.addAll(idListByGroupMapList);

    // 這裏開始對數據進行組合,方便前端展示
    List<String> idList = new ArrayList<>();
    idListByUserMapList.forEach(idListByUserMap -> {
        // businessKey 爲業務鍵,我用來存數據的 ID
        idList.add((String) idListByUserMap.get("businessKey"));
    });
    // 獲取正在審覈的數據
    List<Map<String, Object>> roles = demoService.getRoleListByIdsDemo(idList);
    // 數據整合,將需要在前端展示的流程信息與業務數據信息組合到一起
    roles.forEach(role -> {
        idListByUserMapList.forEach(idListByUserMap -> {
            if (role.get("id").toString().equals(idListByUserMap.get("businessKey").toString())) {
                role.put("taskId", idListByUserMap.get("taskId"));
                role.put("processInstanceName", idListByUserMap.get("processInstanceName"));
                role.put("startTime", idListByUserMap.get("startTime"));
            }
        });
    });
    // 統一返回
    errorMsg.setErrorCode(ErrorCode.SUCCESS);
    errorMsg.setErrorMsg("SUCCESS");
    errorMsg.setRetData(roles);
    return errorMsg;
}

流程引擎功能封裝

根據組獲取任務數據

/**
 * 獲取組,獲取需要審覈的業務鍵 business_key 列表
 *
 * @param groupIds 組 Id
 * @return
 */
@RequestMapping(value = "/getRuntimeBusinessKeyByGroup", method = RequestMethod.POST)
public List<Map<String, Object>> getRuntimeBusinessKeyByGroup(@RequestBody List<String> groupIds) {
    List<Map<String, Object>> idList = new ArrayList<>();
    // 判斷是否有組信息
    if (groupIds != null && groupIds.size() > 0) {
        // 根據發起人獲取正在執行的任務列表
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();
        tasks.forEach(task -> {
            Map<String, Object> data = new HashMap<>();
            // 根據任務獲取流程實例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            // 獲取流程實例中的業務鍵
            data.put("businessKey", processInstance.getBusinessKey());
            // 獲取任務 Id
            data.put("taskId", task.getId());
            // 流程定義名稱
            data.put("processInstanceName", processInstance.getProcessDefinitionName());
            // 流程開始時間
            data.put("startTime", processInstance.getStartTime());
            idList.add(data);
        });
    }
    return idList;
}

根據審覈人獲取任務數據

/**
 * 根據用戶,獲取需要審覈的業務鍵 business_key 列表
 *
 * @param userId 用戶 Id
 * @return
 */
@RequestMapping(value = "/getRuntimeBusinessKeyByUser/{userId}", method = RequestMethod.GET)
public List<Map<String, Object>> getRuntimeBusinessKeyByUser(@PathVariable(value = "userId") String userId) {
    List<Map<String, Object>> idList = new ArrayList<>();
    // 根據用戶獲取正在進行的任務
    List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();
    tasks.forEach(task -> {
        Map<String, Object> data = new HashMap<>();
        // 根據任務獲取流程實例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        // 獲取流程實例中的業務鍵
        data.put("businessKey", processInstance.getBusinessKey());
        // 獲取任務 Id
        data.put("taskId", task.getId());
        // 流程定義名稱
        data.put("processInstanceName", processInstance.getProcessDefinitionName());
        // 流程開始時間
        data.put("startTime", processInstance.getStartTime());
        idList.add(data);
    });
    return idList;
}

審覈功能

功能描述

點擊按鈕,進行審覈

業務系統代碼

整體邏輯:獲取任務 Id,完成任務,對需要的參數進行設置
PS:關於用戶組,審覈人的參數設置建議用監聽器之類的來實現,這裏爲了方便直接在審覈裏設置了

/**
* 進行審覈
 * @param request
 * @param taskId 任務節點 Id
 * @return
 */
@GetMapping(value = "/task/{taskId}/{dataId}")
public ErrorMsg taskByAssignee(HttpServletRequest request, @PathVariable(value = "taskId") String taskId, @PathVariable(value = "dataId") String dataId) {
    ErrorMsg errorMsg = new ErrorMsg();
    String token = request.getHeader("X-Token");

    User user = (User) redisTemplate.opsForValue().get(token);

    // 會籤情況需要用戶列表數據
    List<String> assigneeList = userGroupPermissionDao.getIdByGroup("ce537a73-dbc2-4d2f-ac5f-2cdc208a20e0");
    
    // 設置會籤所需的用戶列表數據
    // 會籤與監聽器會籤節點用戶組參數
    // 注意這裏單獨封裝了個方法,集合不能用 Object 接收,否則流程引擎解析會失敗
    feignClientService.setListVariable(taskId, "assigneeList", assigneeList);
    // 多實例基數,沒用到
    feignClientService.setVariable(taskId, "cycleNum", assigneeList.size());
    // 第二審覈人節點,審覈人蔘數
    feignClientService.setVariable(taskId, "reviewer", "bbb");

    // 流程完成所需的條件參數
    // 主要用於第三審覈人節點,根據審覈人進行分支判斷依據
    Map<String, Object> map = new HashMap<>();
    map.put("assignee", user.getUsername());

    // 根據任務節點 Id,獲取流程實例 Id
    String processInstanceId = feignClientService.getTaskInfo(taskId);
    // 完成任務,taskId 任務節點 ID
    feignClientService.taskByAssignee(taskId, user.getUsername(), map);
    // 通過流程實例 Id,判斷流程是否結束
    boolean isFinish = feignClientService.checkProcessInstanceFinish(processInstanceId);
    if (isFinish) {
        // 更新審覈狀態
        Role role = new Role();
        role.setId(Integer.valueOf(dataId));
        role.setStatus(2);
        demoService.updateRoleDemo(role);
    }
    errorMsg.setErrorCode(ErrorCode.SUCCESS);
    errorMsg.setErrorMsg("SUCCESS");
    return errorMsg;
}

流程引擎功能封裝

參數設置接口

/**
 * 設置任務參數
 *
 * @param taskId 任務ID
 * @param key 鍵
 * @param value 值
 * @return
 */
@RequestMapping(value = "/setVariable", method = RequestMethod.POST)
public void setVariable(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "key") String key, @RequestParam(value = "value") Object value) {
    String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
    runtimeService.setVariable(processInstanceId, key, value);
}

/**
 * 設置任務參數,List 使用
 *
 * @param taskId 任務ID
 * @param key 鍵
 * @param value 值
 * @return
 */
@RequestMapping(value = "/setListVariable", method = RequestMethod.POST)
public void setListVariable(@RequestParam(value = "taskId") String taskId, @RequestParam(value = "key") String key, @RequestParam(value = "value") List<String> value) {
    String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
    runtimeService.setVariable(processInstanceId, key, value);
}

根據任務節點獲取流程實例 Id

/**
 * 根據任務節點獲取流程實例 Id
 *
 * @param taskId 任務節點 Id
 * @return
 */
@RequestMapping(value = "/getTaskInfo/{taskId}", method = RequestMethod.GET)
public String getTaskInfo(@PathVariable(value = "taskId") String taskId) {
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    return task.getProcessInstanceId();
}

通過流程實例 Id,判斷流程是否結束

/**
 * 通過流程實例 Id,判斷流程是否結束
 *
 * @param processInstanceId 流程實例 Id
 * @return true 結束,false 未結束
 */
@RequestMapping(value = "/checkProcessInstanceFinish/{processInstanceId}", method = RequestMethod.GET)
public boolean checkProcessInstanceFinish(@PathVariable(value = "processInstanceId") String processInstanceId) {
    boolean isFinish = false;
    // 根據流程 ID 獲取未完成的流程中是否存在此流程
    long count = historyService.createHistoricProcessInstanceQuery().unfinished().processInstanceId(processInstanceId).count();
    // 不存在說明沒有結束
    if (count == 0) {
        isFinish = true;
    }
    return isFinish;
}

審覈歷史查詢

效果圖

在這裏插入圖片描述

功能描述

根據審覈人字段,獲取已完成的任務中,審覈人爲當前用戶的數據

PS:組審覈情況,在審覈時,都直接設置了審覈人,因此可以直接獲取到

業務系統代碼

/**
 * 獲取審覈歷史記錄
 * @return
 */
@GetMapping(value = "/getApproveHistory")
public ErrorMsg getApproveHistory(HttpServletRequest request) {
    ErrorMsg errorMsg = new ErrorMsg();
    String token = request.getHeader("X-Token");
    User user = (User) redisTemplate.opsForValue().get(token);

    // 調用流程引擎接口,獲取審覈人爲當前用戶的已完成的任務
    List<Map<String, Object>> historys = feignClientService.getHistoryByUser(user.getUsername());
    errorMsg.setErrorCode(ErrorCode.SUCCESS);
    errorMsg.setErrorMsg("SUCCESS");
    errorMsg.setRetData(historys);
    return errorMsg;
}

流程引擎功能封裝

/**
 * 獲取用戶審覈歷史
 *
 * @param userId 發起人 Id
 * @return
 */
@RequestMapping(value = "/getHistoryByUser/{userId}", method = RequestMethod.GET)
public List<Map<String, Object>> getHistoryByUser(@PathVariable(value = "userId") String userId) {
    List<Map<String, Object>> historyList = new ArrayList<>();
    // 根據用戶,查詢任務實例歷史
    List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
    list.forEach(historicTaskInstance -> {
        // 歷史流程實例
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
        // 獲取需要的歷史數據
        Map<String, Object> historyInfo = new HashMap<>();
        historyInfo.put("assignee", historicTaskInstance.getAssignee());
        // 節點名稱
        historyInfo.put("nodeName", historicTaskInstance.getName());
        // 流程開始時間
        historyInfo.put("startTime", historicTaskInstance.getCreateTime());
        // 節點操作時間(本流程節點結束時間)
        historyInfo.put("endTime", historicTaskInstance.getEndTime());
        // 流程定義名稱
        historyInfo.put("processName", hpi.getProcessDefinitionName());
        // 流程實例 ID
        historyInfo.put("processInstanceId", historicTaskInstance.getProcessInstanceId());
        // 業務鍵
        historyInfo.put("businessKey", hpi.getBusinessKey());
        historyList.add(historyInfo);
    });
    return historyList;
}

流程圖查看流程進度

效果圖

在這裏插入圖片描述

功能描述

這裏直接採用流輸出直接在頁面顯示,因此只能前端直接調用流程引擎的接口

如果想要更完善的顯示流程,建議自己解析 Json,自己前端用組件生成流程圖

流程引擎功能封裝

根據任務 ID 獲取任務進度流程圖

/**
 * 根據任務 ID 獲取任務進度流程圖
 *
 * @param taskId 任務節點 Id
 * @return
 */
@RequestMapping(value = "/getTaskProcessDiagram/{taskId}", method = RequestMethod.GET)
public void getTaskProcessDiagram(@PathVariable(value = "taskId") String taskId, HttpServletResponse httpServletResponse) {

    // 根據任務 ID 獲取流程實例 ID
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    String processInstanceId = task.getProcessInstanceId();

    // 根據流程實例獲取流程圖
    // 流程定義 ID
    String processDefinitionId;

    // 查看完成的進程中是否存在此進程
    long count = historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count();
    if (count > 0) {
        // 如果流程已經結束,則得到結束節點
        HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();

        processDefinitionId = pi.getProcessDefinitionId();
    } else {// 如果流程沒有結束,則取當前活動節點
        // 根據流程實例ID獲得當前處於活動狀態的ActivityId合集
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        processDefinitionId = pi.getProcessDefinitionId();
    }
    List<String> highLightedActivitis = new ArrayList<>();

    // 獲得活動的節點
    List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();

    for (HistoricActivityInstance tempActivity : highLightedActivitList) {
        String activityId = tempActivity.getActivityId();
        highLightedActivitis.add(activityId);
    }

    List<String> flows = new ArrayList<>();
    //獲取流程圖
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
    ProcessEngineConfiguration processEngineConfig = processEngine.getProcessEngineConfiguration();

    ProcessDiagramGenerator diagramGenerator = processEngineConfig.getProcessDiagramGenerator();
    InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows, processEngineConfig.getActivityFontName(),
            processEngineConfig.getLabelFontName(), processEngineConfig.getAnnotationFontName(), processEngineConfig.getClassLoader(), 1.0, true);

    OutputStream out = null;
    byte[] buf = new byte[1024];
    int legth;
    try {
        out = httpServletResponse.getOutputStream();
        while ((legth = in.read(buf)) != -1) {
            out.write(buf, 0, legth);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        IOUtils.closeQuietly(out);
        IOUtils.closeQuietly(in);
    }
}

結尾

文章如果存在什麼問題,請及時留言反饋

集成後的代碼:https://gitee.com/linjinp-spring-cloud/linjinp-spring-cloud
Flowable 封裝接口在 flowable-demo 模塊下的 flowable.api 包裏
Flowable 業務調用示例在 service-b 模塊下的 flowableDemo 包裏

發佈了103 篇原創文章 · 獲贊 387 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章