本篇內容:實時檢視當前流程進度。
HolidayService類中新增兩個方法:/** * <p>查詢歷史資訊</p> * <p>時間:2021年1月24日-上午10:42:02</p> * @author baba * @param instanceId 例項ID * @return HistoricProcessInstance */ public HistoricProcessInstance queryHistory(String instanceId) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult() ; } /** * <p>根據例項ID獲取歷史活動示例查詢器</p> * <p>時間:2021年1月24日-上午10:45:46</p> * @author baba * @param instanceId 例項ID * @return HistoricActivityInstanceQuery */ public HistoricActivityInstanceQuery getHistoryActivity(String instanceId) { return historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId) ; }
配置流程圖生成Bean@Configurationpublic class ActivitiConfig { @Bean @ConditionalOnMissingBean public ProcessDiagramGenerator processDiagramGenerator() { return new DefaultProcessDiagramGenerator() ; } }
獲取各個節點資訊(執行過的)
public class ActivitiUtils { /** * <p> * 獲取流程走過的線 * </p> * <p>時間:2021年1月24日-上午10:52:01</p> * @author baba * @param bpmnModel 流程物件模型 * @param processDefinitionEntity 流程定義物件 * @param historicActivityInstances 歷史流程已經執行的節點,並已經按執行的先後順序排序 * @return List<String> 流程走過的線 */ public static List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) { // 用以儲存高亮的線flowId List<String> highFlows = new ArrayList<String>(); if(historicActivityInstances == null || historicActivityInstances.size() == 0) return highFlows; // 遍歷歷史節點 for (int i = 0; i < historicActivityInstances.size() - 1; i++) { // 取出已執行的節點 HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i); // 用以儲存後續開始時間相同的節點 List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>(); // 獲取下一個節點(用於連線) FlowNode sameActivityImpl = getNextFlowNode(bpmnModel, historicActivityInstances, i, activityImpl_); // 將後面第一個節點放在時間相同節點的集合裡 if(sameActivityImpl != null) sameStartTimeNodes.add(sameActivityImpl); // 迴圈後面節點,看是否有與此後繼節點開始時間相同的節點,有則新增到後繼節點集合 for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) { HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 後續第一個節點 HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 後續第二個節點 if (activityImpl1.getStartTime().getTime() != activityImpl2.getStartTime().getTime()) break; // 如果第一個節點和第二個節點開始時間相同儲存 FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId()); sameStartTimeNodes.add(sameActivityImpl2); } // 得到節點定義的詳細資訊 FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId()); // 取出節點的所有出去的線,對所有的線進行遍歷 List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows(); for (SequenceFlow pvmTransition : pvmTransitions) { // 獲取節點 FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef()); // 不是後繼節點 if(!sameStartTimeNodes.contains(pvmActivityImpl)) continue; // 如果取出的線的目標節點存在時間相同的節點裡,儲存該線的id,進行高亮顯示 highFlows.add(pvmTransition.getId()); } } //返回高亮的線 return highFlows; } /** * <p> * 獲取下一個節點資訊 * </p> * <p>時間:2021年1月24日-上午10:52:46</p> * @author baba * @param bpmnModel 流程模型 * @param historicActivityInstances 歷史節點 * @param i 當前已經遍歷到的歷史節點索引(找下一個節點從此節點後) * @param activityImpl_ 當前遍歷到的歷史節點例項 * @return FlowNode 下一個節點資訊 */ private static FlowNode getNextFlowNode(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances, int i, HistoricActivityInstance activityImpl_) { // 儲存後一個節點 FlowNode sameActivityImpl = null; // 如果當前節點不是使用者任務節點,則取排序的下一個節點為後續節點 if(!"userTask".equals(activityImpl_.getActivityType())) { // 是最後一個節點,沒有下一個節點 if(i == historicActivityInstances.size()) return sameActivityImpl; // 不是最後一個節點,取下一個節點為後繼節點 sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i + 1).getActivityId());// 找到緊跟在後面的一個節點 // 返回 return sameActivityImpl; } // 遍歷後續節點,獲取當前節點後續節點 for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) { // 後續節點 HistoricActivityInstance activityImp2_ = historicActivityInstances.get(k); // 都是userTask,且主節點與後續節點的開始時間相同,說明不是真實的後繼節點 if("userTask".equals(activityImp2_.getActivityType()) && activityImpl_.getStartTime().getTime() == activityImp2_.getStartTime().getTime()) continue; // 找到緊跟在後面的一個節點 sameActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId()); break; } return sameActivityImpl; }}
測試Controller@RestController@RequestMapping("/view")public class ProcessViewController { private static Logger logger = LoggerFactory.getLogger(ProcessViewController.class) ; @Resource private HolidayService holidayService ; @Resource private RepositoryService repositoryService ; @Resource private ProcessDiagramGenerator processDiagramGenerator ; @ResponseBody @GetMapping("/image") public void showImg(String instanceId, HttpServletResponse response) throws Exception { response.setContentType("text/html;charset=utf-8") ; if (StringUtils.isEmpty(instanceId)) { PrintWriter out = response.getWriter() ; out.write("error") ; out.close() ; return ; } // 獲取流程例項 HistoricProcessInstance processInstance = holidayService.queryHistory(instanceId) ; if(processInstance == null) { logger.error("流程例項ID:{}沒查詢到流程例項!", instanceId); PrintWriter out = response.getWriter() ; out.write("error instance not exists") ; out.close() ; return; } // 根據流程物件獲取流程物件模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); // 構造歷史流程查詢 HistoricActivityInstanceQuery historyInstanceQuery = holidayService.getHistoryActivity(instanceId) ; // 檢視已執行的節點集合,獲取流程歷史中已執行節點,並按照節點在流程中執行先後順序排序 List<HistoricActivityInstance> historicActivityInstanceList = historyInstanceQuery.orderByHistoricActivityInstanceStartTime().asc().list(); if(historicActivityInstanceList == null || historicActivityInstanceList.isEmpty()) { logger.error("流程例項ID: {}, 沒有歷史節點資訊!", instanceId) ; outputImg(response, bpmnModel, null, null) ; return ; } // 已執行的節點ID集合(將historicActivityInstanceList中元素的activityId欄位取出封裝到executedActivityIdList) List<String> executedActivityIdList = historicActivityInstanceList.stream().map(item -> item.getActivityId()).collect(Collectors.toList()); // 獲取流程定義 ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processInstance.getProcessDefinitionId()); List<String> flowIds = ActivitiUtils.getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList); // 輸出影象,並設定高亮 outputImg(response, bpmnModel, flowIds, executedActivityIdList); } /** * <p> * 輸出影象 * </p> * <p>時間:2021年1月24日-上午10:49:42</p> * @author baba * @param response * @param bpmnModel 影象物件 * @param flowIds 已執行的線集合 * @param executedActivityIdList 已執行的節點ID集合 * @return void */ private void outputImg(HttpServletResponse response, BpmnModel bpmnModel, List<String> flowIds, List<String> executedActivityIdList) { InputStream imageStream = null; try { imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋體", "微軟雅黑", "黑體", true, "png"); // 輸出資源內容到相應物件 byte[] b = new byte[10 * 1024] ; int len = -1 ; while ((len = imageStream.read(b)) != -1) { response.getOutputStream().write(b, 0, len); } response.getOutputStream().flush(); }catch(Exception e) { logger.error("流程圖輸出異常!", e); } finally { if (imageStream != null) { try { imageStream.close() ; } catch (IOException e) { e.printStackTrace(); } } } } }
測試:
生成一個新的請假流程呼叫介面:/holidays/start?processDefinitionId=holiday:1:0a012ce6-5df2-11eb-afe9-00d861e5b732&userId=10000檢視當前使用者的任務列表(找出剛啟動流程的例項id)呼叫介面:/holidays/tasks?userId=10000檢視當前流程圖呼叫介面:/view/image?instanceId=341ee217-5df2-11eb-afe9-00d861e5b732繼續執行下一步流程呼叫介面:/holidays/apply?days=3&mgr=10001&explain=生病&instanceId=341ee217-5df2-11eb-afe9-00d861e5b732檢視當前流程圖呼叫介面:/view/image?instanceId=341ee217-5df2-11eb-afe9-00d861e5b732到此完畢!!!
最新評論