mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-30 01:38:43 +08:00 
			
		
		
		
	重新实现后端的 bpm 流程图的高亮接口
This commit is contained in:
		| @@ -0,0 +1,40 @@ | |||||||
|  | package cn.iocoder.yudao.adminserver.modules.bpm.controller.task; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.StrUtil; | ||||||
|  | import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService; | ||||||
|  | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | ||||||
|  | import io.swagger.annotations.Api; | ||||||
|  | import io.swagger.annotations.ApiImplicitParam; | ||||||
|  | import io.swagger.annotations.ApiOperation; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  | import org.springframework.web.bind.annotation.GetMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RequestParam; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import javax.servlet.http.HttpServletResponse; | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | @Api(tags = "流程活动实例") | ||||||
|  | @RestController | ||||||
|  | @RequestMapping("/bpm/activity") | ||||||
|  | @Validated | ||||||
|  | public class BpmActivityController { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private BpmActivityService activityService; | ||||||
|  |  | ||||||
|  |     // TODO 芋艿:注解、权限、validtion | ||||||
|  |  | ||||||
|  |     @ApiOperation(value = "生成指定流程实例的高亮流程图", | ||||||
|  |             notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成") | ||||||
|  |     @GetMapping("/generate-highlight-diagram") | ||||||
|  |     @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class) | ||||||
|  |     public void generateHighlightDiagram(@RequestParam("processInstanceId") String processInstanceId, | ||||||
|  |                                          HttpServletResponse response) throws IOException { | ||||||
|  |         byte[] bytes = activityService.generateHighlightDiagram(processInstanceId); | ||||||
|  |         ServletUtils.writeAttachment(response, StrUtil.format("流程图-{}.svg", processInstanceId), bytes); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -20,7 +20,7 @@ import java.util.List; | |||||||
| import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||||||
| import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; | ||||||
|  |  | ||||||
| @Api(tags = "流程任务") | @Api(tags = "流程任务实例") | ||||||
| @RestController | @RestController | ||||||
| @RequestMapping("/bpm/task") | @RequestMapping("/bpm/task") | ||||||
| @Validated | @Validated | ||||||
| @@ -72,14 +72,4 @@ public class BpmTaskController { | |||||||
|         return success(taskService.getTaskListByProcessInstanceId(processInstanceId)); |         return success(taskService.getTaskListByProcessInstanceId(processInstanceId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 返回高亮的流转图SVG |  | ||||||
|      * @param processInstanceId 流程Id |  | ||||||
|      */ |  | ||||||
|     @GetMapping("/process/highlight-img") |  | ||||||
|     public void getHighlightImg(@RequestParam String processInstanceId, HttpServletResponse response) throws IOException { |  | ||||||
|         FileResp fileResp = taskService.getHighlightImg(processInstanceId); |  | ||||||
|         ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,24 +0,0 @@ | |||||||
| package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task; |  | ||||||
|  |  | ||||||
| import lombok.Data; |  | ||||||
|  |  | ||||||
| // TODO @Li:1)改成 HighlightImgRespVO 吧。2)swagger 注解要补充;3)fileByte => fileContent |  | ||||||
| /** |  | ||||||
|  * 文件输出类 |  | ||||||
|  * |  | ||||||
|  * @author yunlongn |  | ||||||
|  */ |  | ||||||
| @Data |  | ||||||
| public class FileResp { |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 文件名字 |  | ||||||
|      */ |  | ||||||
|     private String fileName; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 文件输出流 |  | ||||||
|      */ |  | ||||||
|     private byte[] fileByte; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -33,7 +33,8 @@ public interface BpmErrorCodeConstants { | |||||||
|     ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); |     ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); | ||||||
|     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); |     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); | ||||||
|     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在"); |     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在"); | ||||||
|     ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003002, "流程定义处于挂起状态"); |     ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态"); | ||||||
|  |     ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在"); | ||||||
|  |  | ||||||
|     // ========== 流程实例 1-009-004-000 ========== |     // ========== 流程实例 1-009-004-000 ========== | ||||||
|     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在"); |     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在"); | ||||||
|   | |||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | package cn.iocoder.yudao.adminserver.modules.bpm.service.task; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * BPM 活动实例 Service 接口 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface BpmActivityService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 生成指定流程实例的高亮流程图,只高亮进行中的任务 | ||||||
|  |      * | ||||||
|  |      * 友情提示,非该方法的注释。如果想实现更高级的高亮流程图(当前节点红色 + 完成节点为绿色),可参考如下内容: | ||||||
|  |      *      博客一:https://blog.csdn.net/qq_40109075/article/details/110939639 | ||||||
|  |      *      博客二:https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-flowable/src/main/java/com/ruoyi/flowable/flow/CustomProcessDiagramGenerator.java | ||||||
|  |      * 这里不实现的原理,需要自定义实现 ProcessDiagramGenerator 和 ProcessDiagramCanvas,代码量有点大 | ||||||
|  |      * | ||||||
|  |      * 如果你想实现高亮已完成的任务,可参考 https://blog.csdn.net/qiuxinfa123/article/details/119579863 博客。不过测试下来,貌似不太对~ | ||||||
|  |      * | ||||||
|  |      * @param processInstanceId 实例Id | ||||||
|  |      * @return 图的字节数组 | ||||||
|  |      */ | ||||||
|  |     byte[] generateHighlightDiagram(String processInstanceId); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -10,7 +10,7 @@ import java.util.List; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 流程任务 Service 接口 |  * 流程任务实例 Service 接口 | ||||||
|  * |  * | ||||||
|  * @author jason |  * @author jason | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
| @@ -32,6 +32,14 @@ public interface BpmTaskService { | |||||||
|      */ |      */ | ||||||
|     List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId); |     List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得流程任务列表 | ||||||
|  |      * | ||||||
|  |      * @param processInstanceId 流程实例的编号 | ||||||
|  |      * @return 流程任务列表 | ||||||
|  |      */ | ||||||
|  |     List<Task> getTasksByProcessInstanceId(String processInstanceId); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获得流程任务列表 |      * 获得流程任务列表 | ||||||
|      * |      * | ||||||
| @@ -101,13 +109,6 @@ public interface BpmTaskService { | |||||||
|      */ |      */ | ||||||
|     void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); |     void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 返回高亮的流转进程 |  | ||||||
|      * @param processInstanceId 实例Id |  | ||||||
|      * @return {@link FileResp} 返回文件 |  | ||||||
|      */ |  | ||||||
|     FileResp getHighlightImg(String processInstanceId); |  | ||||||
|  |  | ||||||
|     // ========== Task 拓展表相关 ========== |     // ========== Task 拓展表相关 ========== | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.io.IoUtil; | ||||||
|  | import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService; | ||||||
|  | import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmActivityService; | ||||||
|  | import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService; | ||||||
|  | import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService; | ||||||
|  | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.activiti.bpmn.model.BpmnModel; | ||||||
|  | import org.activiti.engine.HistoryService; | ||||||
|  | import org.activiti.engine.RepositoryService; | ||||||
|  | import org.activiti.engine.RuntimeService; | ||||||
|  | import org.activiti.engine.history.HistoricProcessInstance; | ||||||
|  | import org.activiti.engine.task.Task; | ||||||
|  | import org.activiti.image.ProcessDiagramGenerator; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS; | ||||||
|  | import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS; | ||||||
|  | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * BPM 活动实例 Service 实现类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | @Slf4j | ||||||
|  | @Validated | ||||||
|  | public class BpmActivityServiceImpl implements BpmActivityService { | ||||||
|  |  | ||||||
|  |     private static final String FONT_NAME = "宋体"; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private ProcessDiagramGenerator processDiagramGenerator; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private BpmProcessInstanceService processInstanceService; | ||||||
|  |     @Resource | ||||||
|  |     private BpmProcessDefinitionService processDefinitionService; | ||||||
|  |     @Resource | ||||||
|  |     private BpmTaskService taskService; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public byte[] generateHighlightDiagram(String processInstanceId) { | ||||||
|  |         // 获得流程实例 | ||||||
|  |         HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); | ||||||
|  |         if (processInstance == null) { | ||||||
|  |             throw exception(PROCESS_INSTANCE_NOT_EXISTS); | ||||||
|  |         } | ||||||
|  |         // 获得流程定义的 BPMN 模型 | ||||||
|  |         BpmnModel bpmnModel = processDefinitionService.getBpmnModel(processInstance.getProcessDefinitionId()); | ||||||
|  |         if (bpmnModel == null) { | ||||||
|  |             throw exception(PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 如果流程已经结束,则无进行中的任务,无法高亮 | ||||||
|  |         // 如果流程未结束,才需要高亮 | ||||||
|  |         List<String> highLightedActivities = Collections.emptyList(); | ||||||
|  |         if (processInstance.getEndTime() == null) { | ||||||
|  |             List<Task> tasks = taskService.getTasksByProcessInstanceId(processInstanceId); | ||||||
|  |             highLightedActivities = CollectionUtils.convertList(tasks, Task::getTaskDefinitionKey); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 生成高亮流程图 | ||||||
|  |         InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, Collections.emptyList(), | ||||||
|  |                 FONT_NAME, FONT_NAME, FONT_NAME); | ||||||
|  |         return IoUtil.readBytes(inputStream); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -54,7 +54,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. | |||||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 流程任务 Service 实现类 |  * 流程任务实例 Service 实现类 | ||||||
|  * |  * | ||||||
|  * @author jason |  * @author jason | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
| @@ -66,14 +66,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|     @Resource |     @Resource | ||||||
|     private TaskService taskService; |     private TaskService taskService; | ||||||
|     @Resource |     @Resource | ||||||
|     private RuntimeService runtimeService; |  | ||||||
|     @Resource |  | ||||||
|     private HistoryService  historyService; |     private HistoryService  historyService; | ||||||
|     @Resource |  | ||||||
|     private RepositoryService repositoryService; |  | ||||||
|  |  | ||||||
|     @Resource |  | ||||||
|     private ProcessDiagramGenerator processDiagramGenerator; |  | ||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private SysUserService userService; |     private SysUserService userService; | ||||||
| @@ -118,6 +111,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap); |         return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Task> getTasksByProcessInstanceId(String processInstanceId) { | ||||||
|  |         if (StrUtil.isEmpty(processInstanceId)) { | ||||||
|  |             return Collections.emptyList(); | ||||||
|  |         } | ||||||
|  |         return taskService.createTaskQuery().processInstanceId(processInstanceId).list(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) { |     public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) { | ||||||
|         if (CollUtil.isEmpty(processInstanceIds)) { |         if (CollUtil.isEmpty(processInstanceIds)) { | ||||||
| @@ -270,129 +271,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
| //        taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment()); | //        taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public FileResp getHighlightImg(String processInstanceId) { |  | ||||||
|         // 查询历史 |  | ||||||
|         //TODO 云扬四海 貌似流程结束后,点击审批进度会报错 |  | ||||||
|         // TODO @Li:一些 historyService 的查询,貌似比较通用,是不是抽一些小方法出来 |  | ||||||
|         HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); |  | ||||||
|         // 如果不存在实例。 说明数据异常 |  | ||||||
|         if (hpi == null) { |  | ||||||
| //            throw exception(PROCESS_INSTANCE_NOT_EXISTS); |  | ||||||
|             throw new RuntimeException("不存在"); |  | ||||||
|         } |  | ||||||
|         // 如果有结束时间 返回model的流程图 |  | ||||||
|         if (!ObjectUtils.isEmpty(hpi.getEndTime())) { |  | ||||||
|             ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(hpi.getProcessDefinitionId()).singleResult(); |  | ||||||
|             String resourceName = Optional.ofNullable(pd.getDiagramResourceName()).orElse(pd.getResourceName()); |  | ||||||
|             BpmnModel bpmnModel = repositoryService.getBpmnModel(pd.getId()); |  | ||||||
|             InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, new ArrayList<>(1), new ArrayList<>(1), |  | ||||||
|                     "宋体", "宋体", "宋体"); |  | ||||||
|             FileResp fileResp = new FileResp(); |  | ||||||
|             fileResp.setFileName( resourceName + ".svg"); |  | ||||||
|             fileResp.setFileByte(IoUtil.readBytes(inputStream)); |  | ||||||
|             return fileResp; |  | ||||||
|         } |  | ||||||
|         // 没有结束时间。说明流程在执行过程中 |  | ||||||
|         // TODO @Li:一些 runtimeService 的查询,貌似比较通用,是不是抽一些小方法出来 |  | ||||||
|         ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); |  | ||||||
|  |  | ||||||
|         List<String> highLightedActivities = new ArrayList<>(); |  | ||||||
|         // 获取所有活动节点 |  | ||||||
|         List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery() |  | ||||||
|                 .processInstanceId(processInstanceId).finished().list(); |  | ||||||
|         finishedInstances.stream().map(HistoricActivityInstance::getActivityId) |  | ||||||
|                 .forEach(highLightedActivities::add); |  | ||||||
|         // 已完成的节点+当前节点 |  | ||||||
|         highLightedActivities.addAll(runtimeService.getActiveActivityIds(processInstanceId)); |  | ||||||
|  |  | ||||||
|         BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); |  | ||||||
|         // 经过的流 |  | ||||||
|         List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, processInstanceId); |  | ||||||
|  |  | ||||||
|         //设置"宋体" |  | ||||||
|         try (InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, highLightedFlowIds, |  | ||||||
|                 "宋体", "宋体", "宋体")){ |  | ||||||
|             FileResp fileResp = new FileResp(); |  | ||||||
|             fileResp.setFileName( hpi.getProcessDefinitionName() + ".svg"); |  | ||||||
|             fileResp.setFileByte(IoUtil.readBytes(inputStream)); |  | ||||||
|             return fileResp; |  | ||||||
|         } catch (IOException e) { |  | ||||||
|             log.error("[getHighlightImg][流程({}) 生成图表失败]", processInstanceId, e); |  | ||||||
|             throw exception(HIGHLIGHT_IMG_ERROR); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // TODO @Li:这个方法的可读性还有一定的优化空间,可以思考下哈。 |  | ||||||
|     /** |  | ||||||
|      * 获取指定 processInstanceId 已经高亮的Flows |  | ||||||
|      * 获取已经流转的线 参考: https://blog.csdn.net/qiuxinfa123/article/details/119579863 |  | ||||||
|      * @param bpmnModel model |  | ||||||
|      * @param processInstanceId 流程实例Id |  | ||||||
|      * @return 获取已经流转的列表 |  | ||||||
|      */ |  | ||||||
|     private List<String> getHighLightedFlows(BpmnModel bpmnModel, String processInstanceId) { |  | ||||||
|         // 获取所有的线条 |  | ||||||
|         List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) |  | ||||||
|                 .orderByHistoricActivityInstanceId().asc().list(); |  | ||||||
|         // 高亮流程已发生流转的线id集合 |  | ||||||
|         List<String> highLightedFlowIds = new ArrayList<>(); |  | ||||||
|         // 全部活动节点 |  | ||||||
|         List<FlowNode> historicActivityNodes = new ArrayList<>(); |  | ||||||
|         // 已完成的历史活动节点 |  | ||||||
|         List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>(); |  | ||||||
|  |  | ||||||
|         for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) { |  | ||||||
|             FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true); |  | ||||||
|             historicActivityNodes.add(flowNode); |  | ||||||
|             // 结束时间不为空,则是已完成节点 |  | ||||||
|             if (historicActivityInstance.getEndTime() != null) { |  | ||||||
|                 finishedActivityInstances.add(historicActivityInstance); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         // 提取活动id 是唯一的。塞入Map |  | ||||||
|         Map<String, HistoricActivityInstance> historicActivityInstanceMap = CollectionUtils.convertMap(historicActivityInstances, HistoricActivityInstance::getActivityId); |  | ||||||
|         // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的 |  | ||||||
|         for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) { |  | ||||||
|             // 获得当前活动对应的节点信息及outgoingFlows信息 |  | ||||||
|             FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true); |  | ||||||
|             List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows(); |  | ||||||
|  |  | ||||||
|             // 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转: |  | ||||||
|             // 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 |  | ||||||
|             // 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转 |  | ||||||
|             if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(currentActivityInstance.getActivityType()) |  | ||||||
|                     || BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE.equals(currentActivityInstance.getActivityType())) { |  | ||||||
|                 // 遍历历史活动节点,找到匹配流程目标节点的 |  | ||||||
|                 for (SequenceFlow sequenceFlow : sequenceFlows) { |  | ||||||
|                     FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true); |  | ||||||
|                     if (historicActivityNodes.contains(targetFlowNode)) { |  | ||||||
|                         highLightedFlowIds.add(targetFlowNode.getId()); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 long earliestStamp = 0L; |  | ||||||
|                 String highLightedFlowId = null; |  | ||||||
|                 // 循环流出的流 |  | ||||||
|                 for (SequenceFlow sequenceFlow : sequenceFlows) { |  | ||||||
|                     HistoricActivityInstance historicActivityInstance = historicActivityInstanceMap.get(sequenceFlow.getTargetRef()); |  | ||||||
|                     if (historicActivityInstance == null) { |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     final long startTime = historicActivityInstance.getStartTime().getTime(); |  | ||||||
|                     // 遍历匹配的集合,取得开始时间最早的一个 |  | ||||||
|                     if (earliestStamp == 0 || earliestStamp >= startTime) { |  | ||||||
|                         highLightedFlowId = sequenceFlow.getId(); |  | ||||||
|                         earliestStamp = startTime; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 highLightedFlowIds.add(highLightedFlowId); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|         return highLightedFlowIds; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private Task getTask(String id) { |     private Task getTask(String id) { | ||||||
|         return taskService.createTaskQuery().taskId(id).singleResult(); |         return taskService.createTaskQuery().taskId(id).singleResult(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | /** | ||||||
|  |  * task 包下,存放的都是 xxx 实例。例如说: | ||||||
|  |  * 1. ProcessInstance 是 ProcessDefinition 创建而来的实例; | ||||||
|  |  * 2. TaskInstance 是 TaskDefinition 创建而来的实例; | ||||||
|  |  * 3. ActivityInstance 是 BPMN 流程图的每个元素创建的实例; | ||||||
|  |  * | ||||||
|  |  * 考虑到 Task 和 Activity 可以比较明确表示名字,所以对应的 Service 就没有使用 Instance 后缀~ | ||||||
|  |  * 嘿嘿,其实也是实现到比较后面的阶段,所以就暂时没去统一和修改了~ | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | package cn.iocoder.yudao.adminserver.modules.bpm.service.task; | ||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV