最近要把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; } }
看一个简单的生成效果:
在按照流程节点类型做区分处理上,考虑的不是特别周全,希望有用到的朋友遇到了问题能够与我分享,一起探讨更好的解决方式~