我喜欢简要文字 主要还是发代码为主了,希望发现问题可以一起探讨!
我用的框架是spring mvc
controller
/**
* <功能简述>流程跟踪图
* <功能详细描述>
* @param processInstanceId 流程实例id
* @param response
* @throws Exception
* @see [类、类#方法、类#成员]
*/
@RequestMapping("graphHistoryProcessInstance")
public void graphHistoryProcessInstance(
@RequestParam("processInstanceId") String processInstanceId,
HttpServletResponse response) throws Exception {
Command cmd = new HistoryProcessInstanceDiagramCmd(
processInstanceId);
InputStream is = workspaceService.executeCommand(
cmd);
response.setContentType("image/png");
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
is.close();
}
HistoryProcessInstanceDiagramCmd.java
package com.plate.bpm.graph;
import java.io.*;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
public class HistoryProcessInstanceDiagramCmd implements Command {
protected String historyProcessInstanceId;
public HistoryProcessInstanceDiagramCmd(String historyProcessInstanceId) {
this.historyProcessInstanceId = historyProcessInstanceId;
}
public InputStream execute(CommandContext commandContext) {
try {
CustomProcessDiagramGenerator customProcessDiagramGenerator = new CustomProcessDiagramGenerator();
return customProcessDiagramGenerator
.generateDiagram(historyProcessInstanceId);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
CustomProcessDiagramGenerator.java
package com.plate.bpm.graph;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.Artifact;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.bpmn.model.Lane;
import org.activiti.bpmn.model.Pool;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.cmd.GetBpmnModelCmd;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.apache.commons.io.FilenameUtils;
/**
* 流程图绘制工具
*/
public class CustomProcessDiagramGenerator {
public static final int OFFSET_SUBPROCESS = 5;
public static final int OFFSET_TASK = 20;
private static List taskType = new ArrayList();
private static List eventType = new ArrayList();
private static List gatewayType = new ArrayList();
private static List subProcessType = new ArrayList();
private static Color RUNNING_COLOR = Color.RED;
private static Color HISTORY_COLOR = Color.GREEN;
private static Color SKIP_COLOR = Color.GRAY;
private static Stroke THICK_BORDER_STROKE = new BasicStroke(3.0f);
private int minX;
private int minY;
public CustomProcessDiagramGenerator() {
init();
}
protected static void init() {
taskType.add(BpmnXMLConstants.ELEMENT_TASK_MANUAL);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_RECEIVE);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SCRIPT);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SEND);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_SERVICE);
taskType.add(BpmnXMLConstants.ELEMENT_TASK_USER);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EXCLUSIVE);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EVENT);
gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL);
eventType.add("intermediateTimer");
eventType.add("intermediateMessageCatch");
eventType.add("intermediateSignalCatch");
eventType.add("intermediateSignalThrow");
eventType.add("messageStartEvent");
eventType.add("startTimerEvent");
eventType.add(BpmnXMLConstants.ELEMENT_ERROR);
eventType.add(BpmnXMLConstants.ELEMENT_EVENT_START);
eventType.add("errorEndEvent");
eventType.add(BpmnXMLConstants.ELEMENT_EVENT_END);
subProcessType.add(BpmnXMLConstants.ELEMENT_SUBPROCESS);
subProcessType.add(BpmnXMLConstants.ELEMENT_CALL_ACTIVITY);
}
public InputStream generateDiagram(String processInstanceId)
throws IOException {
HistoricProcessInstance historicProcessInstance = Context
.getCommandContext().getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(processInstanceId);
String processDefinitionId = historicProcessInstance
.getProcessDefinitionId();
GetBpmnModelCmd getBpmnModelCmd = new GetBpmnModelCmd(
processDefinitionId);
BpmnModel bpmnModel = getBpmnModelCmd.execute(Context
.getCommandContext());
Point point = getMinXAndMinY(bpmnModel);
this.minX = point.x;
this.minY = point.y;
this.minX = (this.minX <= 5) ? 5 : this.minX;
this.minY = (this.minY <= 5) ? 5 : this.minY;
this.minX -= 5;
this.minY -= 5;
ProcessDefinitionEntity definition = new GetDeploymentProcessDefinitionCmd(
processDefinitionId).execute(Context.getCommandContext());
String diagramResourceName = definition.getDiagramResourceName();
String deploymentId = definition.getDeploymentId();
byte[] bytes = Context
.getCommandContext()
.getResourceEntityManager()
.findResourceByDeploymentIdAndResourceName(deploymentId,
diagramResourceName).getBytes();
InputStream originDiagram = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(originDiagram);
HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
historicActivityInstanceQueryImpl.processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceStartTime().asc();
Page page = new Page(0, 100);
List activityInstances = Context
.getCommandContext()
.getHistoricActivityInstanceEntityManager()
.findHistoricActivityInstancesByQueryCriteria(
historicActivityInstanceQueryImpl, page);
this.drawHistoryFlow(image, processInstanceId);
for (HistoricActivityInstance historicActivityInstance : activityInstances) {
String historicActivityId = historicActivityInstance
.getActivityId();
ActivityImpl activity = definition.findActivity(historicActivityId);
if (activity != null) {
if (historicActivityInstance.getEndTime() == null) {
// 节点正在运行中
signRunningNode(image, activity.getX(),
activity.getY(), activity.getWidth(),
activity.getHeight(),
historicActivityInstance.getActivityType());
} else {
String deleteReason = null;
if (historicActivityInstance.getTaskId() != null) {
deleteReason = Context
.getCommandContext()
.getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(
historicActivityInstance.getTaskId())
.getDeleteReason();
}
// 节点已经结束
if ("跳过".equals(deleteReason)) {
signSkipNode(image, activity.getX(),
activity.getY(),
activity.getWidth(), activity.getHeight(),
historicActivityInstance.getActivityType());
} else {
signHistoryNode(image, activity.getX(),
activity.getY() ,
activity.getWidth(), activity.getHeight(),
historicActivityInstance.getActivityType());
}
}
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
String formatName = getDiagramExtension(diagramResourceName);
ImageIO.write(image, formatName, out);
return new ByteArrayInputStream(out.toByteArray());
}
private static String getDiagramExtension(String diagramResourceName) {
return FilenameUtils.getExtension(diagramResourceName);
}
/**
* 标记运行节点
*
* @param image
* 原始图片
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param activityType
* 节点类型
*/
private static void signRunningNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = RUNNING_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
/**
* 标记历史节点
*
* @param image
* 原始图片
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param activityType
* 节点类型
*/
private static void signHistoryNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = HISTORY_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
private static void signSkipNode(BufferedImage image, int x, int y,
int width, int height, String activityType) {
Color nodeColor = SKIP_COLOR;
Graphics2D graphics = image.createGraphics();
try {
drawNodeBorder(x, y, width, height, graphics, nodeColor,
activityType);
} finally {
graphics.dispose();
}
}
/**
* 绘制节点边框
*
* @param x
* 左上角节点坐在X位置
* @param y
* 左上角节点坐在Y位置
* @param width
* 宽
* @param height
* 高
* @param graphics
* 绘图对象
* @param color
* 节点边框颜色
* @param activityType
* 节点类型
*/
protected static void drawNodeBorder(int x, int y, int width, int height,
Graphics2D graphics, Color color, String activityType) {
graphics.setPaint(color);
graphics.setStroke(THICK_BORDER_STROKE);
if (taskType.contains(activityType)) {
drawTask(x, y, width, height, graphics);
} else if (gatewayType.contains(activityType)) {
drawGateway(x, y, width, height, graphics);
} else if (eventType.contains(activityType)) {
drawEvent(x, y, width, height, graphics);
} else if (subProcessType.contains(activityType)) {
drawSubProcess(x, y, width, height, graphics);
}
}
/**
* 绘制任务
*/
protected static void drawTask(int x, int y, int width, int height,
Graphics2D graphics) {
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width,
height, OFFSET_TASK, OFFSET_TASK);
graphics.draw(rect);
}
/**
* 绘制网关
*/
protected static void drawGateway(int x, int y, int width, int height,
Graphics2D graphics) {
Polygon rhombus = new Polygon();
rhombus.addPoint(x, y + (height / 2));
rhombus.addPoint(x + (width / 2), y + height);
rhombus.addPoint(x + width, y + (height / 2));
rhombus.addPoint(x + (width / 2), y);
graphics.draw(rhombus);
}
/**
* 绘制任务
*/
protected static void drawEvent(int x, int y, int width, int height,
Graphics2D graphics) {
Double circle = new Ellipse2D.Double(x, y, width, height);
graphics.draw(circle);
}
/**
* 绘制子流程
*/
protected static void drawSubProcess(int x, int y, int width, int height,
Graphics2D graphics) {
RoundRectangle2D rect = new RoundRectangle2D.Double(x + 1, y + 1,
width - 2, height - 2, OFFSET_SUBPROCESS, OFFSET_SUBPROCESS);
graphics.draw(rect);
}
protected Point getMinXAndMinY(BpmnModel bpmnModel) {
// We need to calculate maximum values to know how big the image will be in its entirety
double theMinX = java.lang.Double.MAX_VALUE;
double theMaxX = 0;
double theMinY = java.lang.Double.MAX_VALUE;
double theMaxY = 0;
for (Pool pool : bpmnModel.getPools()) {
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
theMinX = graphicInfo.getX();
theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
theMinY = graphicInfo.getY();
theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
}
List flowNodes = gatherAllFlowNodes(bpmnModel);
for (FlowNode flowNode : flowNodes) {
GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode
.getId());
// width
if ((flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth()) > theMaxX) {
theMaxX = flowNodeGraphicInfo.getX()
+ flowNodeGraphicInfo.getWidth();
}
if (flowNodeGraphicInfo.getX() < theMinX) {
theMinX = flowNodeGraphicInfo.getX();
}
// height
if ((flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight()) > theMaxY) {
theMaxY = flowNodeGraphicInfo.getY()
+ flowNodeGraphicInfo.getHeight();
}
if (flowNodeGraphicInfo.getY() < theMinY) {
theMinY = flowNodeGraphicInfo.getY();
}
for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(sequenceFlow.getId());
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > theMaxX) {
theMaxX = graphicInfo.getX();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > theMaxY) {
theMaxY = graphicInfo.getY();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
}
List artifacts = gatherAllArtifacts(bpmnModel);
for (Artifact artifact : artifacts) {
GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact
.getId());
if (artifactGraphicInfo != null) {
// width
if ((artifactGraphicInfo.getX() + artifactGraphicInfo
.getWidth()) > theMaxX) {
theMaxX = artifactGraphicInfo.getX()
+ artifactGraphicInfo.getWidth();
}
if (artifactGraphicInfo.getX() < theMinX) {
theMinX = artifactGraphicInfo.getX();
}
// height
if ((artifactGraphicInfo.getY() + artifactGraphicInfo
.getHeight()) > theMaxY) {
theMaxY = artifactGraphicInfo.getY()
+ artifactGraphicInfo.getHeight();
}
if (artifactGraphicInfo.getY() < theMinY) {
theMinY = artifactGraphicInfo.getY();
}
}
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(artifact.getId());
if (graphicInfoList != null) {
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > theMaxX) {
theMaxX = graphicInfo.getX();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > theMaxY) {
theMaxY = graphicInfo.getY();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
}
int nrOfLanes = 0;
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
for (Lane l : process.getLanes()) {
nrOfLanes++;
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
// // width
if ((graphicInfo.getX() + graphicInfo.getWidth()) > theMaxX) {
theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
}
if (graphicInfo.getX() < theMinX) {
theMinX = graphicInfo.getX();
}
// height
if ((graphicInfo.getY() + graphicInfo.getHeight()) > theMaxY) {
theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
}
if (graphicInfo.getY() < theMinY) {
theMinY = graphicInfo.getY();
}
}
}
// Special case, see http://jira.codehaus.org/browse/ACT-1431
if ((flowNodes.size() == 0) && (bpmnModel.getPools().size() == 0)
&& (nrOfLanes == 0)) {
// Nothing to show
theMinX = 0;
theMinY = 0;
}
return new Point((int) theMinX, (int) theMinY);
}
protected static List gatherAllArtifacts(BpmnModel bpmnModel) {
List artifacts = new ArrayList();
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
artifacts.addAll(process.getArtifacts());
}
return artifacts;
}
protected static List gatherAllFlowNodes(BpmnModel bpmnModel) {
List flowNodes = new ArrayList();
for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
flowNodes.addAll(gatherAllFlowNodes(process));
}
return flowNodes;
}
protected static List gatherAllFlowNodes(
FlowElementsContainer flowElementsContainer) {
List flowNodes = new ArrayList();
for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
if (flowElement instanceof FlowNode) {
flowNodes.add((FlowNode) flowElement);
}
if (flowElement instanceof FlowElementsContainer) {
flowNodes
.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
}
}
return flowNodes;
}
public void drawHistoryFlow(BufferedImage image, String processInstanceId) {
HistoricProcessInstance historicProcessInstance = Context
.getCommandContext().getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(processInstanceId);
String processDefinitionId = historicProcessInstance
.getProcessDefinitionId();
Graph graph = new ActivitiHistoryGraphBuilder(processInstanceId)
.build();
for (Edge edge : graph.getEdges()) {
drawSequenceFlow(image, processDefinitionId, edge.getName());
}
}
public void drawSequenceFlow(BufferedImage image,
String processDefinitionId, String sequenceFlowId) {
GetBpmnModelCmd getBpmnModelCmd = new GetBpmnModelCmd(
processDefinitionId);
BpmnModel bpmnModel = getBpmnModelCmd.execute(Context
.getCommandContext());
Graphics2D graphics = image.createGraphics();
graphics.setPaint(HISTORY_COLOR);
graphics.setStroke(new BasicStroke(2f));
try {
List graphicInfoList = bpmnModel
.getFlowLocationGraphicInfo(sequenceFlowId);
int[] xPoints = new int[graphicInfoList.size()];
int[] yPoints = new int[graphicInfoList.size()];
for (int i = 1; i < graphicInfoList.size(); i++) {
GraphicInfo graphicInfo = graphicInfoList.get(i);
GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
if (i == 1) {
xPoints[0] = (int) previousGraphicInfo.getX();
yPoints[0] = (int) previousGraphicInfo.getY();
}
xPoints[i] = (int) graphicInfo.getX();
yPoints[i] = (int) graphicInfo.getY();
}
int radius = 15;
Path2D path = new Path2D.Double();
for (int i = 0; i < xPoints.length; i++) {
Integer anchorX = xPoints[i];
Integer anchorY = yPoints[i];
double targetX = anchorX;
double targetY = anchorY;
double ax = 0;
double ay = 0;
double bx = 0;
double by = 0;
double zx = 0;
double zy = 0;
if ((i > 0) && (i < (xPoints.length - 1))) {
Integer cx = anchorX;
Integer cy = anchorY;
// pivot point of prev line
double lineLengthY = yPoints[i] - yPoints[i - 1];
// pivot point of prev line
double lineLengthX = xPoints[i] - xPoints[i - 1];
double lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
+ Math.pow(lineLengthX, 2));
double dx = (lineLengthX * radius) / lineLength;
double dy = (lineLengthY * radius) / lineLength;
targetX = targetX - dx;
targetY = targetY - dy;
// isDefaultConditionAvailable = isDefault && i == 1 && lineLength > 10;
if ((lineLength < (2 * radius)) && (i > 1)) {
targetX = xPoints[i] - (lineLengthX / 2);
targetY = yPoints[i] - (lineLengthY / 2);
}
// pivot point of next line
lineLengthY = yPoints[i + 1] - yPoints[i];
lineLengthX = xPoints[i + 1] - xPoints[i];
lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
+ Math.pow(lineLengthX, 2));
if (lineLength < radius) {
lineLength = radius;
}
dx = (lineLengthX * radius) / lineLength;
dy = (lineLengthY * radius) / lineLength;
double nextSrcX = xPoints[i] + dx;
double nextSrcY = yPoints[i] + dy;
if ((lineLength < (2 * radius))
&& (i < (xPoints.length - 2))) {
nextSrcX = xPoints[i] + (lineLengthX / 2);
nextSrcY = yPoints[i] + (lineLengthY / 2);
}
double dx0 = (cx - targetX) / 3;
double dy0 = (cy - targetY) / 3;
ax = cx - dx0;
ay = cy - dy0;
double dx1 = (cx - nextSrcX) / 3;
double dy1 = (cy - nextSrcY) / 3;
bx = cx - dx1;
by = cy - dy1;
zx = nextSrcX;
zy = nextSrcY;
}
if (i == 0) {
path.moveTo(targetX, targetY);
} else {
path.lineTo(targetX, targetY);
}
if ((i > 0) && (i < (xPoints.length - 1))) {
// add curve
path.curveTo(ax, ay, bx, by, zx, zy);
}
}
graphics.draw(path);
// draw arrow
Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2],
yPoints[xPoints.length - 2], xPoints[xPoints.length - 1],
yPoints[xPoints.length - 1]);
int ARROW_WIDTH = 5;
int doubleArrowWidth = 2 * ARROW_WIDTH;
Polygon arrowHead = new Polygon();
arrowHead.addPoint(0, 0);
arrowHead.addPoint(-ARROW_WIDTH, -doubleArrowWidth);
arrowHead.addPoint(ARROW_WIDTH, -doubleArrowWidth);
AffineTransform transformation = new AffineTransform();
transformation.setToIdentity();
double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
transformation.translate(line.x2, line.y2);
transformation.rotate((angle - (Math.PI / 2d)));
AffineTransform originalTransformation = graphics.getTransform();
graphics.setTransform(transformation);
graphics.fill(arrowHead);
graphics.setTransform(originalTransformation);
} finally {
graphics.dispose();
}
}
}
Graph.java
package com.plate.bpm.graph;
import java.util.ArrayList;
import java.util.List;
public class Graph {
private Node initial;
public Node getInitial() {
return initial;
}
public void setInitial(Node initial) {
this.initial = initial;
}
public List getNodes() {
List nodes = new ArrayList();
visitNode(initial, nodes);
return nodes;
}
public void visitNode(Node node, List nodes) {
nodes.add(node);
for (Edge edge : node.getOutgoingEdges()) {
Node nextNode = edge.getDest();
visitNode(nextNode, nodes);
}
}
public List getEdges() {
List edges = new ArrayList();
visitEdge(initial, edges);
return edges;
}
public void visitEdge(Node node, List edges) {
for (Edge edge : node.getOutgoingEdges()) {
edges.add(edge);
Node nextNode = edge.getDest();
visitEdge(nextNode, edges);
}
}
public Node findById(String id) {
for (Node node : this.getNodes()) {
if (id.equals(node.getId())) {
return node;
}
}
return null;
}
}
GraphElement.java
package com.plate.bpm.graph;
/**
* 节点和连线的父类.
*/
public class GraphElement {
/**
* 实例id,历史的id.
*/
private String id;
/**
* 节点名称,bpmn图形中的id.
*/
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Edge.java
package com.plate.bpm.graph;
/**
* 连线.
*/
public class Edge extends GraphElement {
/**
* 起点.
*/
private Node src;
/**
* 终点.
*/
private Node dest;
/**
* 循环.
*/
private boolean cycle;
public Node getSrc() {
return src;
}
public void setSrc(Node src) {
this.src = src;
}
public Node getDest() {
return dest;
}
public void setDest(Node dest) {
this.dest = dest;
}
public boolean isCycle() {
return cycle;
}
public void setCycle(boolean cycle) {
this.cycle = cycle;
}
}
ActivitiHistoryGraphBuilder.java
package com.plate.bpm.graph;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 根据历史,生成实时运行阶段的子图.
*/
public class ActivitiHistoryGraphBuilder {
/**
* logger.
*/
private static Logger logger = LoggerFactory
.getLogger(ActivitiHistoryGraphBuilder.class);
private String processInstanceId;
private ProcessDefinitionEntity processDefinitionEntity;
private List historicActivityInstances;
private List visitedHistoricActivityInstances = new ArrayList();
private Map nodeMap = new HashMap();
public ActivitiHistoryGraphBuilder(String processInstanceId) {
this.processInstanceId = processInstanceId;
}
public Graph build() {
this.fetchProcessDefinitionEntity();
this.fetchHistoricActivityInstances();
Graph graph = new Graph();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
Node currentNode = new Node();
currentNode.setId(historicActivityInstance.getId());
currentNode.setName(historicActivityInstance.getActivityId());
currentNode.setType(historicActivityInstance.getActivityType());
currentNode
.setActive(historicActivityInstance.getEndTime() == null);
logger.debug("currentNode : {}:{}", currentNode.getName(),
currentNode.getId());
Edge previousEdge = this.findPreviousEdge(currentNode,
historicActivityInstance.getStartTime().getTime());
if (previousEdge == null) {
if (graph.getInitial() != null) {
throw new IllegalStateException("already set an initial.");
}
graph.setInitial(currentNode);
} else {
logger.debug("previousEdge : {}", previousEdge.getName());
}
nodeMap.put(currentNode.getId(), currentNode);
visitedHistoricActivityInstances.add(historicActivityInstance);
}
if (graph.getInitial() == null) {
throw new IllegalStateException("cannot find initial.");
}
return graph;
}
/**
* 根据流程实例id获取对应的流程定义.
*/
public void fetchProcessDefinitionEntity() {
String processDefinitionId = Context.getCommandContext()
.getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(processInstanceId)
.getProcessDefinitionId();
GetDeploymentProcessDefinitionCmd cmd = new GetDeploymentProcessDefinitionCmd(
processDefinitionId);
processDefinitionEntity = cmd.execute(Context.getCommandContext());
}
public void fetchHistoricActivityInstances() {
HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
// historicActivityInstanceQueryImpl.processInstanceId(processInstanceId)
// .orderByHistoricActivityInstanceStartTime().asc();
// TODO: 如果用了uuid会造成这样排序出问题
// 但是如果用startTime,可能出现因为处理速度太快,时间一样,导致次序颠倒的问题
historicActivityInstanceQueryImpl.processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceId().asc();
Page page = new Page(0, 100);
historicActivityInstances = Context
.getCommandContext()
.getHistoricActivityInstanceEntityManager()
.findHistoricActivityInstancesByQueryCriteria(
historicActivityInstanceQueryImpl, page);
}
/**
* 找到这个节点前面的连线.
*/
public Edge findPreviousEdge(Node currentNode, long currentStartTime) {
String activityId = currentNode.getName();
ActivityImpl activityImpl = processDefinitionEntity
.findActivity(activityId);
HistoricActivityInstance nestestHistoricActivityInstance = null;
String temporaryPvmTransitionId = null;
// 遍历进入当前节点的所有连线
for (PvmTransition pvmTransition : activityImpl
.getIncomingTransitions()) {
PvmActivity source = pvmTransition.getSource();
String previousActivityId = source.getId();
HistoricActivityInstance visitiedHistoryActivityInstance = this
.findVisitedHistoricActivityInstance(previousActivityId);
if (visitiedHistoryActivityInstance == null) {
continue;
}
// 如果上一个节点还未完成,说明不可能是从这个节点过来的,跳过
if (visitiedHistoryActivityInstance.getEndTime() == null) {
continue;
}
logger.debug("current activity start time : {}", new Date(
currentStartTime));
logger.debug("nestest activity end time : {}",
visitiedHistoryActivityInstance.getEndTime());
// 如果当前节点的开始时间,比上一个节点的结束时间要早,跳过
if (currentStartTime < visitiedHistoryActivityInstance.getEndTime()
.getTime()) {
continue;
}
if (nestestHistoricActivityInstance == null) {
nestestHistoricActivityInstance = visitiedHistoryActivityInstance;
temporaryPvmTransitionId = pvmTransition.getId();
} else if ((currentStartTime - nestestHistoricActivityInstance
.getEndTime().getTime()) > (currentStartTime - visitiedHistoryActivityInstance
.getEndTime().getTime())) {
// 寻找离当前节点最近的上一个节点
// 比较上一个节点的endTime与当前节点startTime的差
nestestHistoricActivityInstance = visitiedHistoryActivityInstance;
temporaryPvmTransitionId = pvmTransition.getId();
}
}
// 没找到上一个节点,就返回null
if (nestestHistoricActivityInstance == null) {
return null;
}
Node previousNode = nodeMap
.get(nestestHistoricActivityInstance.getId());
if (previousNode == null) {
return null;
}
logger.debug("previousNode : {}:{}", previousNode.getName(),
previousNode.getId());
Edge edge = new Edge();
edge.setName(temporaryPvmTransitionId);
previousNode.getOutgoingEdges().add(edge);
edge.setSrc(previousNode);
edge.setDest(currentNode);
return edge;
}
public HistoricActivityInstance findVisitedHistoricActivityInstance(
String activityId) {
for (int i = visitedHistoricActivityInstances.size() - 1; i >= 0; i--) {
HistoricActivityInstance historicActivityInstance = visitedHistoricActivityInstances
.get(i);
if (activityId.equals(historicActivityInstance.getActivityId())) {
return historicActivityInstance;
}
}
return null;
}
}
ActivitiGraphBuilder.java
package com.plate.bpm.graph;
import java.util.HashSet;
import java.util.Set;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
/**
* 根据流程定义,构建设计阶段的图.
*/
public class ActivitiGraphBuilder {
/**
* 流程定义id.
*/
private String processDefinitionId;
/**
* 流程定义.
*/
private ProcessDefinitionEntity processDefinitionEntity;
/**
* 已访问的节点id.
*/
private Set visitedNodeIds = new HashSet();
/**
* 构造方法.
*/
public ActivitiGraphBuilder(String processDefinitionId) {
this.processDefinitionId = processDefinitionId;
}
/**
* 构建图.
*/
public Graph build() {
this.fetchProcessDefinitionEntity();
Node initial = visitNode(processDefinitionEntity.getInitial());
Graph graph = new Graph();
graph.setInitial(initial);
return graph;
}
/**
* 获取流程定义.
*/
public void fetchProcessDefinitionEntity() {
GetDeploymentProcessDefinitionCmd cmd = new GetDeploymentProcessDefinitionCmd(
processDefinitionId);
processDefinitionEntity = cmd.execute(Context.getCommandContext());
}
/**
* 遍历.
*/
public Node visitNode(PvmActivity pvmActivity) {
if (visitedNodeIds.contains(pvmActivity.getId())) {
return null;
}
visitedNodeIds.add(pvmActivity.getId());
Node currentNode = new Node();
currentNode.setId(pvmActivity.getId());
currentNode.setName(this.getString(pvmActivity.getProperty("name")));
currentNode.setType(this.getString(pvmActivity.getProperty("type")));
for (PvmTransition pvmTransition : pvmActivity.getOutgoingTransitions()) {
PvmActivity destination = pvmTransition.getDestination();
Node targetNode = this.visitNode(destination);
if (targetNode == null) {
continue;
}
Edge edge = new Edge();
edge.setId(pvmTransition.getId());
edge.setSrc(currentNode);
edge.setDest(targetNode);
currentNode.getOutgoingEdges().add(edge);
}
return currentNode;
}
/**
* 把object转换为string.
*/
public String getString(Object object) {
if (object == null) {
return null;
} else if (object instanceof String) {
return (String) object;
} else {
return object.toString();
}
}
}
Node.java
package com.plate.bpm.graph;
import java.util.ArrayList;
import java.util.List;
/**
* 节点.
*/
public class Node extends GraphElement {
/**
* 类型,比如userTask,startEvent.
*/
private String type;
/**
* 是否还未完成.
*/
private boolean active;
/**
* 进入这个节点的所有连线.
*/
private List incomingEdges = new ArrayList();
/**
* 外出这个节点的所有连线.
*/
private List outgoingEdges = new ArrayList();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public List getIncomingEdges() {
return incomingEdges;
}
public void setIncomingEdges(List incomingEdges) {
this.incomingEdges = incomingEdges;
}
public List getOutgoingEdges() {
return outgoingEdges;
}
public void setOutgoingEdges(List outgoingEdges) {
this.outgoingEdges = outgoingEdges;
}
}
希望对你有所帮助,如果有更好的办法记得分享给大家哦!