• Activiti实现自定义流程图颜色-项目使用


    效果图:

    需要用到类如下:

    1、CustomProcessDiagramCanvas类

    package com.ksource.modules.activiti.util.draw;
    
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Paint;
    import java.awt.RenderingHints;
    import java.awt.Stroke;
    import java.awt.font.FontRenderContext;
    import java.awt.font.LineBreakMeasurer;
    import java.awt.font.TextAttribute;
    import java.awt.font.TextLayout;
    import java.awt.geom.Line2D;
    import java.awt.geom.Rectangle2D;
    import java.awt.geom.RoundRectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.text.AttributedCharacterIterator;
    import java.text.AttributedString;
    import javax.imageio.ImageIO;
    import org.activiti.bpmn.model.AssociationDirection;
    import org.activiti.bpmn.model.GraphicInfo;
    import org.activiti.image.exception.ActivitiImageException;
    import org.activiti.image.impl.DefaultProcessDiagramCanvas;
    import org.activiti.image.util.ReflectUtil;
    
    public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
    
        protected static Color LABEL_COLOR = new Color(0, 0, 0);
    
        //font
        protected String activityFontName = "宋体";
        protected String labelFontName = "宋体";
        protected String annotationFontName = "宋体";
    
        private static volatile boolean flag = false;
    
        public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
            super(width, height, minX, minY, imageType);
        }
    
        public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType,
                                          String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
            super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName,
                    customClassLoader);
        }
    
        public void drawHighLight(boolean isStartOrEnd, int x, int y, int width, int height, Color color) {
            Paint originalPaint = g.getPaint();
            Stroke originalStroke = g.getStroke();
    
            g.setPaint(color);
            g.setStroke(MULTI_INSTANCE_STROKE);
            if (isStartOrEnd) {// 开始、结束节点画圆
                g.drawOval(x, y, width, height);
            } else {// 非开始、结束节点画圆角矩形
                RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 5, 5);
                g.draw(rect);
            }
            g.setPaint(originalPaint);
            g.setStroke(originalStroke);
        }
    
        public void drawSequenceflow(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault,
                                     boolean highLighted, double scaleFactor, Color color) {
            drawConnection(xPoints, yPoints, conditional, isDefault, "sequenceFlow", AssociationDirection.ONE, highLighted,
                    scaleFactor, color);
        }
    
        public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault,
                                   String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor,
                                   Color color) {
    
            Paint originalPaint = g.getPaint();
            Stroke originalStroke = g.getStroke();
    
            g.setPaint(CONNECTION_COLOR);
            if (connectionType.equals("association")) {
                g.setStroke(ASSOCIATION_STROKE);
            } else if (highLighted) {
                g.setPaint(color);
                g.setStroke(HIGHLIGHT_FLOW_STROKE);
            }
    
            for (int i = 1; i < xPoints.length; i++) {
                Integer sourceX = xPoints[i - 1];
                Integer sourceY = yPoints[i - 1];
                Integer targetX = xPoints[i];
                Integer targetY = yPoints[i];
                Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);
                g.draw(line);
            }
    
            if (isDefault) {
                Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
                drawDefaultSequenceFlowIndicator(line, scaleFactor);
            }
    
            if (conditional) {
                Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
                drawConditionalSequenceFlowIndicator(line, scaleFactor);
            }
    
            if (associationDirection.equals(AssociationDirection.ONE)
                    || associationDirection.equals(AssociationDirection.BOTH)) {
                Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2],
                        xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);
                drawArrowHead(line, scaleFactor);
            }
            if (associationDirection.equals(AssociationDirection.BOTH)) {
                Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);
                drawArrowHead(line, scaleFactor);
            }
            g.setPaint(originalPaint);
            g.setStroke(originalStroke);
        }
    
        public void drawLabel(boolean highLighted, String text, GraphicInfo graphicInfo, boolean centered) {
            float interline = 1.0f;
    
            // text
            if (text != null && text.length() > 0) {
                Paint originalPaint = g.getPaint();
                Font originalFont = g.getFont();
                if (highLighted) {
                    g.setPaint(WorkflowConstants.COLOR_NORMAL);
                } else {
                    g.setPaint(LABEL_COLOR);
                }
                g.setFont(new Font(labelFontName, Font.BOLD, 10));
    
                int wrapWidth = 100;
                int textY = (int) graphicInfo.getY();
    
                // TODO: use drawMultilineText()
                AttributedString as = new AttributedString(text);
                as.addAttribute(TextAttribute.FOREGROUND, g.getPaint());
                as.addAttribute(TextAttribute.FONT, g.getFont());
                AttributedCharacterIterator aci = as.getIterator();
                FontRenderContext frc = new FontRenderContext(null, true, false);
                LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    
                while (lbm.getPosition() < text.length()) {
                    TextLayout tl = lbm.nextLayout(wrapWidth);
                    textY += tl.getAscent();
                    Rectangle2D bb = tl.getBounds();
                    double tX = graphicInfo.getX();
                    if (centered) {
                        tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2);
                    }
                    tl.draw(g, (float) tX, textY);
                    textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent();
                }
    
                // restore originals
                g.setFont(originalFont);
                g.setPaint(originalPaint);
            }
        }
    
        @Override
        public BufferedImage generateBufferedImage(String imageType) {
            if (closed) {
                throw new ActivitiImageException("ProcessDiagramGenerator already closed");
            }
    
            // Try to remove white space
            minX = (minX <= WorkflowConstants.PROCESS_PADDING) ? WorkflowConstants.PROCESS_PADDING : minX;
            minY = (minY <= WorkflowConstants.PROCESS_PADDING) ? WorkflowConstants.PROCESS_PADDING : minY;
            BufferedImage imageToSerialize = processDiagram;
            if (minX >= 0 && minY >= 0) {
                imageToSerialize = processDiagram.getSubimage(
                        minX - WorkflowConstants.PROCESS_PADDING,
                        minY - WorkflowConstants.PROCESS_PADDING,
                        canvasWidth - minX + WorkflowConstants.PROCESS_PADDING,
                        canvasHeight - minY + WorkflowConstants.PROCESS_PADDING);
            }
            return imageToSerialize;
        }
    
        @Override
        public void initialize(String imageType) {
            this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB);
            this.g = processDiagram.createGraphics();
    
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setPaint(Color.black);
    
            Font font = new Font(activityFontName, Font.BOLD, FONT_SIZE);
            g.setFont(font);
            this.fontMetrics = g.getFontMetrics();
    
            LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);
            ANNOTATION_FONT = new Font(annotationFontName, Font.PLAIN, FONT_SIZE);
            //优化加载速度
            if(flag) {
                return;
            }
            try {
                USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/userTask.png", customClassLoader));
                SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/scriptTask.png", customClassLoader));
                SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/serviceTask.png", customClassLoader));
                RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/receiveTask.png", customClassLoader));
                SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/sendTask.png", customClassLoader));
                MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/manualTask.png", customClassLoader));
                BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/businessRuleTask.png", customClassLoader));
                SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/shellTask.png", customClassLoader));
                CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/camelTask.png", customClassLoader));
                MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/muleTask.png", customClassLoader));
    
                TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/timer.png", customClassLoader));
                COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate-throw.png", customClassLoader));
                COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate.png", customClassLoader));
                ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error-throw.png", customClassLoader));
                ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error.png", customClassLoader));
                MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message-throw.png", customClassLoader));
                MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message.png", customClassLoader));
                SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal-throw.png", customClassLoader));
                SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal.png", customClassLoader));
                flag = true;
            } catch (IOException e) {
                flag = false;
                LOGGER.warn("Could not load image for process diagram creation: {}", e.getMessage());
            }
        }
    
    
    }

    2、CustomProcessDiagramGenerator类

    package com.ksource.modules.activiti.util.draw;
    
    import org.activiti.bpmn.model.Process;
    import org.activiti.bpmn.model.*;
    import org.activiti.image.impl.DefaultProcessDiagramGenerator;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    
    import javax.imageio.ImageIO;
    import javax.imageio.stream.ImageOutputStream;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    
    @Component
    public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator implements CustomProcessDiagramGeneratorI{
        //预初始化流程图绘制,大大提升了系统启动后首次查看流程图的速度
        static {
            new CustomProcessDiagramCanvas(10,10,0,0,"png", "宋体","宋体","宋体",null);
        }
    
        public CustomProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
                                                                 List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName,
                                                                 String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor,
                                                                 Color [] colors, Set<String> currIds) {
    
            if(null == highLightedActivities) {
                highLightedActivities = Collections.<String>emptyList();
            }
            if(null == highLightedFlows) {
                highLightedFlows = Collections.<String>emptyList();
            }
    
            prepareBpmnModel(bpmnModel);
    
            CustomProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    
            // Draw pool shape, if process is participant in collaboration
            for (Pool pool : bpmnModel.getPools()) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
                processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);
            }
    
            // Draw lanes
            for (Process process : bpmnModel.getProcesses()) {
                for (Lane lane : process.getLanes()) {
                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
                    processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);
                }
            }
    
            // Draw activities and their sequence-flows
            for (Process process: bpmnModel.getProcesses()) {
                List<FlowNode> flowNodeList= process.findFlowElementsOfType(FlowNode.class);
                for (FlowNode flowNode : flowNodeList) {
                    drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, colors, currIds);
                }
            }
    
            // Draw artifacts
            for (Process process : bpmnModel.getProcesses()) {
    
                for (Artifact artifact : process.getArtifacts()) {
                    drawArtifact(processDiagramCanvas, bpmnModel, artifact);
                }
    
                List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
                if (subProcesses != null) {
                    for (SubProcess subProcess : subProcesses) {
                        for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
                            drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
                        }
                    }
                }
            }
    
            return processDiagramCanvas;
        }
    
        protected void drawActivity(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode,
                                    List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Color[] colors, Set<String> currIds) {
            ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
            if (drawInstruction != null) {
    
                drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
    
                // Gather info on the multi instance marker
                boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
                if (flowNode instanceof Activity) {
                    Activity activity = (Activity) flowNode;
                    MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
                    if (multiInstanceLoopCharacteristics != null) {
                        multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
                        multiInstanceParallel = !multiInstanceSequential;
                    }
                }
    
                // Gather info on the collapsed marker
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                if (flowNode instanceof SubProcess) {
                    collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
                } else if (flowNode instanceof CallActivity) {
                    collapsed = true;
                }
    
                if (scaleFactor == 1.0) {
                    // Actually draw the markers
                    processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
                            multiInstanceSequential, multiInstanceParallel, collapsed);
                }
    
                // Draw highlighted activities
                if (highLightedActivities.contains(flowNode.getId())) {
                    if(!CollectionUtils.isEmpty(currIds)
                            &&currIds.contains(flowNode.getId())
                            && !(flowNode instanceof Gateway)) {//非结束节点,并且是当前节点
                        drawHighLight((flowNode instanceof StartEvent), processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()), colors[1]);
                    }else {//普通节点
                        drawHighLight((flowNode instanceof StartEvent)||(flowNode instanceof EndEvent),processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()), colors[0]);
                    }
                }
    
            }
    
            // Outgoing transitions of activity
            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                String flowId = sequenceFlow.getId();
                boolean highLighted = (highLightedFlows.contains(flowId));
                String defaultFlow = null;
                if (flowNode instanceof Activity) {
                    defaultFlow = ((Activity) flowNode).getDefaultFlow();
                } else if (flowNode instanceof Gateway) {
                    defaultFlow = ((Gateway) flowNode).getDefaultFlow();
                }
    
                boolean isDefault = false;
                if (defaultFlow != null && defaultFlow.equalsIgnoreCase(flowId)) {
                    isDefault = true;
                }
    //        boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway);
    
                String sourceRef = sequenceFlow.getSourceRef();
                String targetRef = sequenceFlow.getTargetRef();
                FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
                FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(flowId);
                if (graphicInfoList != null && graphicInfoList.size() > 0) {
                    graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
                    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();
    
                    }
                    //画高亮线
                    processDiagramCanvas.drawSequenceflow(xPoints, yPoints, false, isDefault, highLighted, scaleFactor, colors[0]);
    
                    // Draw sequenceflow label
    //          GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(flowId);
    //          if (labelGraphicInfo != null) {
    //            processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
    //          }else {//解决流程图连线名称不显示的BUG
                    GraphicInfo lineCenter = getLineCenter(graphicInfoList);
                    processDiagramCanvas.drawLabel(highLighted, sequenceFlow.getName(), lineCenter, Math.abs(xPoints[1]-xPoints[0]) >= 5);
    //          }
                }
            }
    
            // Nested elements
            if (flowNode instanceof FlowElementsContainer) {
                for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
                    if (nestedFlowElement instanceof FlowNode) {
                        drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
                                highLightedActivities, highLightedFlows, scaleFactor);
                    }
                }
            }
        }
        protected void drawHighLight(boolean isStartOrEnd, CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo, Color color) {
            processDiagramCanvas.drawHighLight(isStartOrEnd, (int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), color);
        }
    
        protected static CustomProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
                                                                             String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
    
            // We need to calculate maximum values to know how big the image will be in its entirety
            double minX = Double.MAX_VALUE;
            double maxX = 0;
            double minY = Double.MAX_VALUE;
            double maxY = 0;
    
            for (Pool pool : bpmnModel.getPools()) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
                minX = graphicInfo.getX();
                maxX = graphicInfo.getX() + graphicInfo.getWidth();
                minY = graphicInfo.getY();
                maxY = 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() > maxX) {
                    maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
                }
                if (flowNodeGraphicInfo.getX() < minX) {
                    minX = flowNodeGraphicInfo.getX();
                }
                // height
                if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
                    maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
                }
                if (flowNodeGraphicInfo.getY() < minY) {
                    minY = flowNodeGraphicInfo.getY();
                }
    
                for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                    List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
                    if (graphicInfoList != null) {
                        for (GraphicInfo graphicInfo : graphicInfoList) {
                            // width
                            if (graphicInfo.getX() > maxX) {
                                maxX = graphicInfo.getX();
                            }
                            if (graphicInfo.getX() < minX) {
                                minX = graphicInfo.getX();
                            }
                            // height
                            if (graphicInfo.getY() > maxY) {
                                maxY = graphicInfo.getY();
                            }
                            if (graphicInfo.getY()< minY) {
                                minY = 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() > maxX) {
                        maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
                    }
                    if (artifactGraphicInfo.getX() < minX) {
                        minX = artifactGraphicInfo.getX();
                    }
                    // height
                    if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
                        maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
                    }
                    if (artifactGraphicInfo.getY() < minY) {
                        minY = artifactGraphicInfo.getY();
                    }
                }
    
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
                if (graphicInfoList != null) {
                    for (GraphicInfo graphicInfo : graphicInfoList) {
                        // width
                        if (graphicInfo.getX() > maxX) {
                            maxX = graphicInfo.getX();
                        }
                        if (graphicInfo.getX() < minX) {
                            minX = graphicInfo.getX();
                        }
                        // height
                        if (graphicInfo.getY() > maxY) {
                            maxY = graphicInfo.getY();
                        }
                        if (graphicInfo.getY()< minY) {
                            minY = graphicInfo.getY();
                        }
                    }
                }
            }
    
            int nrOfLanes = 0;
            for (Process process : bpmnModel.getProcesses()) {
                for (Lane l : process.getLanes()) {
    
                    nrOfLanes++;
    
                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
                    // // width
                    if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
                        maxX = graphicInfo.getX() + graphicInfo.getWidth();
                    }
                    if (graphicInfo.getX() < minX) {
                        minX = graphicInfo.getX();
                    }
                    // height
                    if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
                        maxY = graphicInfo.getY() + graphicInfo.getHeight();
                    }
                    if (graphicInfo.getY() < minY) {
                        minY = graphicInfo.getY();
                    }
                }
            }
    
            // Special case, see https://activiti.atlassian.net/browse/ACT-1431
            if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
                // Nothing to show
                minX = 0;
                minY = 0;
            }
    
            return new CustomProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY,
                    imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
        }
    
        @Override
        public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,
                                           List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName,
                                           ClassLoader customClassLoader, double scaleFactor, Color[] colors, Set<String> currIds) {
            CustomProcessDiagramCanvas customProcessDiagramCanvas = generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
                    activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor,colors, currIds);
            BufferedImage bufferedImage = customProcessDiagramCanvas.generateBufferedImage(imageType);
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            ImageOutputStream imOut;
            try {
                imOut = ImageIO.createImageOutputStream(bs);
                ImageIO.write(bufferedImage, "PNG", imOut);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            InputStream is = new ByteArrayInputStream(bs.toByteArray());
            return is;
        }
        @Override
        public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
            return generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),
                    activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, new Color[] {Color.BLACK, Color.BLACK}, null);
        }
    
    }

    3、CustomProcessDiagramGeneratorI接口

    package com.ksource.modules.activiti.util.draw;
    
    import org.activiti.bpmn.model.BpmnModel;
    import org.activiti.image.ProcessDiagramGenerator;
    
    import java.awt.*;
    import java.io.InputStream;
    import java.util.List;
    import java.util.Set;
    
    public interface CustomProcessDiagramGeneratorI extends ProcessDiagramGenerator {
        InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,
                                    List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName,
                                    ClassLoader customClassLoader, double scaleFactor, Color[] colors, Set<String> currIds);
    }

    4、HighLightedFlows类

    package com.ksource.modules.activiti.util.draw;
    
    import org.activiti.bpmn.model.BpmnModel;
    import org.activiti.bpmn.model.FlowNode;
    import org.activiti.bpmn.model.SequenceFlow;
    import org.activiti.engine.history.HistoricActivityInstance;
    import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
    import org.activiti.engine.impl.pvm.PvmTransition;
    import org.activiti.engine.impl.pvm.process.ActivityImpl;
    import org.apache.commons.collections4.CollectionUtils;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class HighLightedFlows {
    
        public static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
            // 高亮流程已发生流转的线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());
                historicActivityNodes.add(flowNode);
                if (historicActivityInstance.getEndTime() != null) {
                    finishedActivityInstances.add(historicActivityInstance);
                }
            }
    
            FlowNode currentFlowNode = null;
            FlowNode targetFlowNode = null;
            // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
            for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
                // 获得当前活动对应的节点信息及outgoingFlows信息
                currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId());
                List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
    
                /**
                 * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转: 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 
    * 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转
    */ if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) { // 遍历历史活动节点,找到匹配流程目标节点的 for (SequenceFlow sequenceFlow : sequenceFlows) { targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef()); if (historicActivityNodes.contains(targetFlowNode)) { highLightedFlowIds.add(targetFlowNode.getId()); } } } else { List<Map<String, Object>> tempMapList = new ArrayList<>(); for (SequenceFlow sequenceFlow : sequenceFlows) { for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) { if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) { Map<String, Object> map = new HashMap<>(); map.put("highLightedFlowId", sequenceFlow.getId()); map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime()); tempMapList.add(map); } } } if (!CollectionUtils.isEmpty(tempMapList)) { // 遍历匹配的集合,取得开始时间最早的一个 long earliestStamp = 0L; String highLightedFlowId = null; for (Map<String, Object> map : tempMapList) { long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString()); if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) { highLightedFlowId = map.get("highLightedFlowId").toString(); earliestStamp = highLightedFlowStartTime; } } highLightedFlowIds.add(highLightedFlowId); } } } return highLightedFlowIds; } }

    5、WorkflowConstants类

    package com.ksource.modules.activiti.util.draw;
    
    import com.google.common.collect.Lists;
    
    import java.awt.Color;
    import java.util.List;
    
    /**
     * 变量类
     *@author dxy
     **/
    public final class WorkflowConstants {
    
        /**businessKey**/
        public static final String WORKLOW_BUSINESS_KEY = "businessKey";
        /**按钮网关**/
        public static final String WAY_TYPE = "wayType";
        /**按钮网关**/
        public static final String WAY_TYPE_PREFIX = "way_type_";
        /**项目id**/
        public static final String PROJECT_ID = "projectId";
        /**核心企业Id变量**/
        public static final String CORE_ENTERPRISE_ID="coreEnterpriseId";
        /**链属企业Id变量**/
        public static final String CHAIN_ENTERPRISE_ID="chainEnterpriseId";
        /**银行企业Id变量**/
        public static final String BANK_ENTERPRISE_ID="bankEnterpriseId";
        /**保理公司Id变量**/
        public static final String BAOLI_ENTERPRISE_ID="baoliEnterpriseId";
        /**立账开立企业Id变量**/
        public static final String START_ENTERPRISE_ID="startEnterpriseId";
        /**立账合作企业Id变量**/
        public static final String PARTNER_ENTERPRISE_ID="partnerEnterpriseId";
        /**母公司企业id**/
        public static final String PARENT_ENTERPRISE_ID="parentEnterpriseId";
        /**指定签收企业id**/
        public static final String RECEIVE_ENTERPRISE_ID ="receiveEnterpriseId";
        /**转出方企业Id变量**/
        public static final String TRANSFER_ENTERPRISE_ID="transEnterpriseId";
        /**指定签收企业id**/
        public static final String AC_TASK_ID ="acTaskId";
        /**企业所有角色**/
        public static final String ENT_ALL_ROLE ="all";
        /**运营所有角色**/
        public static final String OPER_ALL_ROLE ="oper";
        /**流程定义缓存时间**/
        public static final int PROCESS_DEFINITION_CACHE = 60;
        /**流程实例激活**/
        public static final int PROCESS_INSTANCE_ACTIVE = 1;
    
        /**流程实例挂起**/
        public static final int PROCESS_INSTANCE_SUSPEND = 0;
    
        /**读取图片**/
        public static final String READ_IMAGE = "image";
    
        /**读取xml**/
        public static final String READ_XML = "xml";
    
        /**流程激活**/
        public static final Integer ACTIVE_PROCESSDEFINITION = 1;
    
        /**流程挂起**/
        public static final Integer SUSPEND_PROCESSDEFINITION = 2;
    
        /**流程状态:0-全部,1-正常,2-已挂起**/
        public static final int QUERY_ALL       = 0;
        public static final int QUERY_NORMAL    = 1;
        public static final int QUERY_SUSPENDED = 2;
    
        /**流程实例状态:0-全部,1-正常,2-已删除**/
        public static final int INSTANCE_ALL       = 0;
        public static final int INSTANCE_NOT_DELETED    = 1;
        public static final int INSTANCE_DELETED = 2;
    
        /** 系统管理员ID **/
        public static final String INTERFACE_SYSTEM_ID = "-1";
        /** 系统管理员名称 **/
        public static final String INTERFACE_SYSTEM_NAME = "系统操作";
    
        /** 流程部署类型:1-启动并激活,2-启动即挂起 **/
        public static final int PROCESS_START_ACTIVE = 1;
        public static final int PROCESS_START_SUSPEND = 2;
    
        /** 用于标识流程项目配置信息校验结果:1:新流程,2:新版本, 3:流程类别有误  **/
        public static final int CHECK_NEW_PROCESS = 1;
        public static final int CHECK_NEW_VERSION = 2;
        public static final int CHECK_ERROR_PROCESS_TYPE = 3;
    
        /** 默认网关条件值 **/
        public static final Integer default_GATEWAY_CONDITION_VALUE = 1;
    
        /** 工作流-业务状态表数据类型:1-工作流状态,2-业务状态 **/
        public static final Integer PROCESS_STATUS = 1;
        public static final Integer BIZNESS_STATUS = 2;
    
        /** 新增流程时标识:1-直接保存,2-提示覆盖 **/
        public static final Integer PROCESS_STATUS_SAVE = 1;
        public static final Integer BIZNESS_STATUS_WARN = 2;
    
        /** 模板类型标识:1-新创建或直接导入的模板,2-默认模板生成 **/
        public static final Integer MODEL_TYPE_1 = 1;
        public static final Integer MODEL_TYPE_2 = 2;
    
        /** 查询流程定义标识:1-查询最新版本流程定义,2-查询所有版本 **/
        public static final Integer QUERY_PROCESS_LATEST_VERSION = 1;
        public static final Integer QUERY_PROCESS_ALL_VERSION = 2;
    
        /** 按钮网关 通过1 */
        public static final String WAY_TYPE_PASS = "1";
        /** 按钮网关 驳回或结束0 */
        public static final String WAY_TYPE_REJECT = "0";
    
        /** 按钮网关 退回2 */
        public static final String WAY_TYPE_BACK = "2";
    
        /**任务参数为空**/
        public static final int TASK_CHECK_PARAM_NULL = -1;
        /**任务已办理**/
        public static final int TASK_CHECK_COMPLETED = 1;
        /**无权限办理**/
        public static final int TASK_CHECK_NO_PERMISSIONS= 2;
        /**任务校验通过**/
        public static final int TASK_CHECK_PASS = 0;
        /** 动态流程图颜色定义 **/
        public static final Color COLOR_NORMAL = new Color(0, 205, 0);
        public static final Color COLOR_CURRENT = new Color(255, 0, 0);
    
        /** 定义生成流程图时的边距(像素) **/
        public static final int PROCESS_PADDING = 5;
    }

    6、ActivitiDataSourceConfig配置类中添加CustomProcessDiagramGeneratorI接口

    package com.ksource.config;
    
    import com.ksource.modules.activiti.util.draw.CustomProcessDiagramGeneratorI;
    import org.activiti.engine.ProcessEngineConfiguration;
    import org.activiti.spring.SpringProcessEngineConfiguration;
    import org.activiti.spring.boot.AbstractProcessEngineAutoConfiguration;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    @Configuration
    public class ActivitiDataSourceConfig extends AbstractProcessEngineAutoConfiguration {
        @Resource
        private ActivitiDataSourceProperties activitiDataSourceProperties;
        @Resource
        private CustomProcessDiagramGeneratorI customProcessDiagramGeneratorI;
        /**
         * 直接取AutoConfig生成的数据源
         * */
        @Resource
        private DataSource dataSource;
        /*@Bean
        public DataSource activitiDataSource() {
            DruidDataSource DruiddataSource = new DruidDataSource();
            DruiddataSource.setUrl(activitiDataSourceProperties.getUrl());
            DruiddataSource.setDriverClassName(activitiDataSourceProperties.getDriverClassName());
            DruiddataSource.setPassword(activitiDataSourceProperties.getPassword());
            DruiddataSource.setUsername(activitiDataSourceProperties.getUsername());
            return DruiddataSource;
        }*/
    
        @Bean
        public PlatformTransactionManager transactionManager() {
            //return new DataSourceTransactionManager(activitiDataSource());
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Bean
        public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
            SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
            //configuration.setDataSource(activitiDataSource());
            configuration.setDataSource(dataSource);
            configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
            configuration.setJobExecutorActivate(true);
            configuration.setTransactionManager(transactionManager());
            configuration.setActivityFontName("宋体");
            configuration.setLabelFontName("宋体");
            configuration.setAnnotationFontName("宋体");
            //自定义流程图样式
            configuration.setProcessDiagramGenerator(customProcessDiagramGeneratorI);
            //id生成器
            //configuration.setIdGenerator(new MyUUIDgenerator());
            return configuration;
        }
    
    
    }

    Controller调用方法如下:

    /*获取高亮实时流程图*/
    @RequestMapping(value = "/getHighlightImg/{id}", method = RequestMethod.GET)
    public void getHighlightImg(@PathVariable String id, HttpServletResponse response) throws Exception{
        InputStream inputStream = null;
        ProcessInstance pi = null;
        String picName = "";
        // 查询历史
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
        String processDefinitionId = null;
        if (hpi.getEndTime() != null) {
            // 已经结束流程获取原图
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(hpi.getProcessDefinitionId()).singleResult();
            picName = pd.getDiagramResourceName();
            processDefinitionId = pd.getId();
        } else {
            pi = runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
            picName = pi.getName()+".png";
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
        List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceStartTime().asc().list();
        // 高亮环节id集合
        List<String> highLightedActivities = new ArrayList<String>();
        // 高亮线路id集合
        HighLightedFlows hlf = new HighLightedFlows();
        List<String> highLightedFlows = hlf.getHighLightedFlows(bpmnModel, highLightedActivitList);
       for (HistoricActivityInstance tempActivity : highLightedActivitList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivities.add(activityId);
        }
    
        Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(hpi.getId()).list()
                .stream().map(e->e.getActivityId()).collect(Collectors.toSet());
    
        CustomProcessDiagramGeneratorI diagramGenerator = (CustomProcessDiagramGeneratorI) processEngineConfiguration
                .getProcessDiagramGenerator();
        inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivities,
                highLightedFlows, "宋体", "宋体", "宋体", null, 1.0, new Color[] { WorkflowConstants.COLOR_NORMAL, WorkflowConstants.COLOR_CURRENT }, currIds);
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(picName, "UTF-8"));
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            log.error(e.toString());
            throw new BootException("读取流程图片失败");
        }
    }
  • 相关阅读:
    Android组件化和插件化开发
    开发一流的 Android SDK:Fabric SDK 的创建经验
    关系 和非关系 阻塞非阻塞的区别
    AJAX 中JSON 和JSONP 的区别 以及请求原理
    九马画山数命运, 一身伴君不羡仙! 与代码不离不弃!
    移动端 transitionEnd函数用来检测过渡是否完成
    2017前端该学的知识 分享墨白的文章、大家共勉!
    移动端和pc端事件绑定方式以及取消浏览器默认样式和取消冒泡
    今天要带来的是移动端开发的基础内容
    离线存储
  • 原文地址:https://www.cnblogs.com/henuyuxiang/p/14333538.html
Copyright © 2020-2023  润新知