zoukankan      html  css  js  c++  java
  • Activiti6生成实例历史活动追踪图片

      最近要把Activiti6集成到系统中,遇到了生成追踪流程图片的问题,在5.x版本中可以使用历史流程节点查找ActivityImpl,升级到6.0版本后,pvm包被移除,所以要实现新的图片生成办法。本文灵感来自:Activiti6.0.0 跟踪流程执行情况用红色框在流程图上标识路线跟节点

      生成追踪流程图要完成两件事情:已执行的Activity高亮和已流转的Flow高亮。

      以下方法是生成完整的追踪图片:

        public InputStream getResourceDiagramInputStream(String id) {
            try {
                // 获取历史流程实例
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
                            
                // 获取流程中已经执行的节点,按照执行先后顺序排序
                List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceId().asc().list();
                
                // 构造已执行的节点ID集合
                List<String> executedActivityIdList = new ArrayList<String>();
                for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                    executedActivityIdList.add(activityInstance.getActivityId());
                }
                
                // 获取bpmnModel
                BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
                // 获取流程已发生流转的线ID集合
                List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);
                
                // 使用默认配置获得流程图表生成器,并生成追踪图片字符流
                ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
                InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
                return imageStream;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

      以下方法获取所有已流转的线,由于并行网关与包含网关可能存在多个流转,所以要找到全部符合要求的Flow,其它类型的节点只查找历史活动实例中的第一个节点:

        private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
            // 流转线ID集合
            List<String> flowIdList = new ArrayList<String>();
            // 全部活动实例
            List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>();
            // 已完成的历史活动节点
            List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>();
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
                if (historicActivityInstance.getEndTime() != null) {
                    finishedActivityInstanceList.add(historicActivityInstance);
                }
            }
            
            // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
            FlowNode currentFlowNode = null;
            for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
                // 获得当前活动对应的节点信息及outgoingFlows信息
                currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
                List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
                
                /**
                 * 遍历outgoingFlows并找到已流转的
                 * 满足如下条件认为已流转:
                 * 1.当前节点是并行网关或包含网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
                 * 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最近的流转节点视为有效流转
                 */
                FlowNode targetFlowNode = null;
                if (BpmsActivityTypeEnum.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType())
                        || BpmsActivityTypeEnum.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) {
                    // 遍历历史活动节点,找到匹配Flow目标节点的
                    for (SequenceFlow sequenceFlow : sequenceFlowList) {
                        targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                        if (historicFlowNodeList.contains(targetFlowNode)) {
                            flowIdList.add(sequenceFlow.getId());
                        }
                    }
                } else {
                    List<Map<String, String>> tempMapList = new LinkedList<Map<String,String>>();
                    // 遍历历史活动节点,找到匹配Flow目标节点的
                    for (SequenceFlow sequenceFlow : sequenceFlowList) {
                        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                            if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                                tempMapList.add(UtilMisc.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime())));
                            }
                        }
                    }
                    
                    // 遍历匹配的集合,取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String flowId = null;
                    for (Map<String, String> map : tempMapList) {
                        long activityStartTime = Long.valueOf(map.get("activityStartTime"));
                        if (earliestStamp == 0 || earliestStamp >= activityStartTime) {
                            earliestStamp = activityStartTime;
                            flowId = map.get("flowId");
                        }
                    }
                    flowIdList.add(flowId);
                }
            }
            return flowIdList;
        }

      枚举说明:

    public enum BpmsActivityTypeEnum {
        
        START_EVENT("startEvent", "开始事件"),
        END_EVENT("endEvent", "结束事件"),
        USER_TASK("userTask", "用户任务"),
        EXCLUSIVE_GATEWAY("exclusiveGateway", "排他网关"),
        PARALLEL_GATEWAY("parallelGateway", "并行网关"),
        INCLUSIVE_GATEWAY("inclusiveGateway", "包含网关");
        
        private String type;
        private String name;
        
        private BpmsActivityTypeEnum(String type, String name) {
            this.type = type;
            this.name = name;
        }
        
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

      工具方法:

    public class UtilMisc {
        public static <V, V1 extends V, V2 extends V> Map<String, V> toMap(String name1, V1 value1, String name2, V2 value2) {
            return populateMap(new HashMap<String, V>(), name1, value1, name2, value2);
        }
        @SuppressWarnings("unchecked")
        private static <K, V> Map<String, V> populateMap(Map<String, V> map, Object... data) {
            for (int i = 0; i < data.length;) {
                map.put((String) data[i++], (V) data[i++]);
            }
            return map;
        }
    }

      看一个简单的生成效果:

      在按照流程节点类型做区分处理上,考虑的不是特别周全,希望有用到的朋友遇到了问题能够与我分享,一起探讨更好的解决方式~

     
  • 相关阅读:
    puppeteer 离线安装chromium
    如何在Taro项目中使用Iconfont(阿里图标)
    POI3.8内存中限制行数为100问题记录
    centos下puppeteer调用chromium报错,缺少包
    VS Code 简单配置运行Java
    使用VSCode 断点调试 js项目,html页面
    Java--Excel--poi 边框、单元格换行、 背景色、合并单元格相关
    浅析Spring Aware
    Spring MVC 注解
    异常处理
  • 原文地址:https://www.cnblogs.com/leemup/p/activiti6_trace.html
Copyright © 2011-2022 走看看