`

activiti-跟踪流程图颜色变化之一篇

阅读更多
我喜欢简要文字 主要还是发代码为主了,希望发现问题可以一起探讨!

我用的框架是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<InputStream> 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<InputStream> {
    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<String> taskType = new ArrayList<String>();
    private static List<String> eventType = new ArrayList<String>();
    private static List<String> gatewayType = new ArrayList<String>();
    private static List<String> subProcessType = new ArrayList<String>();
    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<HistoricActivityInstance> 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<FlowNode> 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<GraphicInfo> 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<Artifact> 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<GraphicInfo> 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<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {
        List<Artifact> artifacts = new ArrayList<Artifact>();

        for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
            artifacts.addAll(process.getArtifacts());
        }

        return artifacts;
    }

    protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {
        List<FlowNode> flowNodes = new ArrayList<FlowNode>();

        for (org.activiti.bpmn.model.Process process : bpmnModel.getProcesses()) {
            flowNodes.addAll(gatherAllFlowNodes(process));
        }

        return flowNodes;
    }

    protected static List<FlowNode> gatherAllFlowNodes(
            FlowElementsContainer flowElementsContainer) {
        List<FlowNode> flowNodes = new ArrayList<FlowNode>();

        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<GraphicInfo> 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<Node> getNodes() {
        List<Node> nodes = new ArrayList<Node>();
        visitNode(initial, nodes);

        return nodes;
    }

    public void visitNode(Node node, List<Node> nodes) {
        nodes.add(node);

        for (Edge edge : node.getOutgoingEdges()) {
            Node nextNode = edge.getDest();
            visitNode(nextNode, nodes);
        }
    }

    public List<Edge> getEdges() {
        List<Edge> edges = new ArrayList<Edge>();
        visitEdge(initial, edges);

        return edges;
    }

    public void visitEdge(Node node, List<Edge> 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<HistoricActivityInstance> historicActivityInstances;
    private List<HistoricActivityInstance> visitedHistoricActivityInstances = new ArrayList<HistoricActivityInstance>();
    private Map<String, Node> nodeMap = new HashMap<String, Node>();

    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<String> visitedNodeIds = new HashSet<String>();

    /**
     * 构造方法.
     */
    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<Edge> incomingEdges = new ArrayList<Edge>();

    /**
     * 外出这个节点的所有连线.
     */
    private List<Edge> outgoingEdges = new ArrayList<Edge>();

    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<Edge> getIncomingEdges() {
        return incomingEdges;
    }

    public void setIncomingEdges(List<Edge> incomingEdges) {
        this.incomingEdges = incomingEdges;
    }

    public List<Edge> getOutgoingEdges() {
        return outgoingEdges;
    }

    public void setOutgoingEdges(List<Edge> outgoingEdges) {
        this.outgoingEdges = outgoingEdges;
    }
}

希望对你有所帮助,如果有更好的办法记得分享给大家哦!
分享到:
评论
5 楼 houjiezhuang 2018-08-29  
如果节点名称是汉字,那么节点显示就是空白。这个怎么解决
4 楼 无色的童年 2018-04-04  
无色的童年 写道
scofier 写道
scofier 写道
请问 : workspaceService.executeCommand(cmd) 的实现代码?



已经解决, 没有问题了, 非常感谢


可以说下是怎么解决的么


解决了 是ManagementService
3 楼 无色的童年 2018-04-04  
scofier 写道
scofier 写道
请问 : workspaceService.executeCommand(cmd) 的实现代码?



已经解决, 没有问题了, 非常感谢


可以说下是怎么解决的么
2 楼 scofier 2017-02-04  
scofier 写道
请问 : workspaceService.executeCommand(cmd) 的实现代码?



已经解决, 没有问题了, 非常感谢
1 楼 scofier 2017-02-04  
请问 : workspaceService.executeCommand(cmd) 的实现代码?

相关推荐

    activiti-engine-5.21.0-API文档-中文版.zip

    赠送jar包:activiti-engine-5.21.0.jar; 赠送原API文档:activiti-engine-5.21.0-javadoc.jar; 赠送源代码:activiti-engine-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-engine-5.21.0.pom; 包含...

    activiti-explorer-eclipse项目

    总结来说,activiti-explorer-eclipse项目为Eclipse开发人员提供了一站式的解决方案,以便他们在IDE内实现和管理基于Activiti的工作流程。通过这个项目,开发者不仅可以更高效地设计流程,还可以在本地环境中便捷地...

    activiti-6.0.0.zip

    activiti-6.0.0.zip 工作流官网包 (如果没有积分可以直接Q我:499384808,免费分享) 1. database里放着各种数据库的建表、删表、升级语句数据库包括db2、h2、hsql、mssql、mysql、oracle、postgres 2. libs 相关...

    activiti-json-converter-5.21.0-API文档-中英对照版.zip

    赠送jar包:activiti-json-converter-5.21.0.jar; 赠送原API文档:activiti-json-converter-5.21.0-javadoc.jar; 赠送源代码:activiti-json-converter-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-...

    activiti-5.22.0官方版本 用于activiti-explorer.zip

    在这个特定的版本——activiti-5.22.0,我们关注的是一个用于流程设计的官方工具,即 activiti-explorer。这个工具为用户提供了一个直观的界面,以便于设计、部署和管理业务流程。 首先,让我们深入了解一下 ...

    activiti-app6-汉化.zip

    activiti6的流程设计汉化,解压后将里面的activiti-app放入tomcat中运行就可以了。默认使用的H2,如果要使用mysql等其它数据库需要修改activiti-app\WEB-INF\classes\META-INF\activiti-app包下的activiti-app....

    activiti-5.22.0zip下载

    标题中的 "activiti-5.22.0zip下载" 指的是Activiti的一个特定版本——5.22.0的压缩包文件,通常包含了该版本的所有源码、库文件、文档和示例。 在Activiti 5.22.0中,主要包含以下几个关键知识点: 1. **Activiti...

    activiti-image-generator-5.21.0-API文档-中文版.zip

    赠送jar包:activiti-image-generator-5.21.0.jar; 赠送原API文档:activiti-image-generator-5.21.0-javadoc.jar; 赠送源代码:activiti-image-generator-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-...

    Activiti6-流程跟踪监控图-节点-流程线高亮显示-支持通过、不通过、驳回、退回

    Activiti6-流程跟踪监控图-节点-流程线高亮显示-支持通过、不通过、驳回、退回 支持内容: 已完成节点高亮显示、当前执行中节点红色显示 支持一个节点多条流出线,包括通过、不通过、驳回、退回,按照已执行操作正确...

    activiti-json-converter-5.21.0-API文档-中文版.zip

    赠送jar包:activiti-json-converter-5.21.0.jar; 赠送原API文档:activiti-json-converter-5.21.0-javadoc.jar; 赠送源代码:activiti-json-converter-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-...

    activiti-common-rest-5.21.0-API文档-中英对照版.zip

    赠送jar包:activiti-common-rest-5.21.0.jar; 赠送原API文档:activiti-common-rest-5.21.0-javadoc.jar; 赠送源代码:activiti-common-rest-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-common-rest-...

    activiti-image-generator-5.21.0-API文档-中英对照版.zip

    赠送jar包:activiti-image-generator-5.21.0.jar; 赠送原API文档:activiti-image-generator-5.21.0-javadoc.jar; 赠送源代码:activiti-image-generator-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-...

    Activiti-APP最全汉化包

    Activiti-APP是Activiti工作流引擎的一个组成部分,它提供了用户友好的图形化界面,用于设计、执行和管理业务流程。用户可以通过这个APP来创建流程模型,定义任务流转规则,监控流程实例的状态,并进行相关的操作,...

    activiti-bpmn-model-5.21.0-API文档-中文版.zip

    赠送jar包:activiti-bpmn-model-5.21.0.jar; 赠送原API文档:activiti-bpmn-model-5.21.0-javadoc.jar; 赠送源代码:activiti-bpmn-model-5.21.0-sources.jar; 赠送Maven依赖信息文件:activiti-bpmn-model-...

    activiti-spring-boot-starter-basic-6.0.0适配springboot2.1.2

    activiti-spring-boot-starter-basic-6.0.0适配springboot2.1.2

    activiti-bpmn-converter-5.18.0-sources.jar

    activiti-bpmn-converter-5.18.0-sources.jar

    activiti-app6.0中文版.zip

    总的来说,"activiti-app6.0中文版.zip"是一个方便中文用户使用的完整工作流管理解决方案,它结合了强大的工作流引擎和直观的Web应用,使得流程管理变得简单而高效。无论是小型项目还是大型企业的复杂业务流程,...

    activiti-spring-boot-starter-7.1.0.M1.jar

    activiti-spring-boot-starter-7.1.0.M1

    activiti-app6.0汉化版

    activiti-app6.0汉化版,对界面进行汉化,流程设计器里的英文无法汉化,没有对应的国际化文件

Global site tag (gtag.js) - Google Analytics