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 節點審覈人
說明:通過參數的形式設定分配人,因此在需要在進入任務節點之前把對應參數注入。否則會提示表達式錯誤。
疑問說明:如果我在審覈時候才設置審覈人,那我表單展示時如何知道審覈人是誰?
- 首先我這裏是第一次審覈時候就放進去的,因爲是 Demo 所有邏輯的嚴謹性上沒考慮那麼全
- 關於怎麼知道表單每條數據的審覈人有誰,個人建議自己建表來存關係最爲方便與準確。之後審覈時候審覈人可以在流程開始階段通過
監聽器
設置進變量或者直接審覈動作之前再設置都可以。
業務系統代碼
// 設置審覈人
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());
}
會籤:多人並審與參數設置用戶組
配置信息
說明
變量:
- assigneeList:審覈人列表,注意這裏不能用 ${ } 包裹,否則系統拿到的就是對應的 value 值
- nrOfInstances:會籤環節實例總數,系統參數,可直接使用
- nrOfActiveInstances:還沒有完成的實例數量,系統參數,可直接使用
- nrOfCompletedInstances:已經完成的實例的數量,系統參數,可直接使用
多實例類型:
- Parallel:並行模式,多人同事審覈
- 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 包裏