流程图高亮跟踪 可自定义渲染图片

news2025/2/27 23:02:30

【流程跟踪】获取流程定义图
获取代码如下:

ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("leave")
        .singleResult();
 String diagramResourceName = processDefinition.getDiagramResourceName();
 InputStream imageStream = repositoryService.getResourceAsStream(
                            processDefinition.getDeploymentId(), diagramResourceName)

1.自动生成流程定义图
如果部署的时候只有xml定义,没有定义图片的话, Activiti流程引擎竟会自动生成一个图像,生成的可能乱码。
乱码问题解决:

@Configuration
public class ActivitiConfig implements ProcessEngineConfigurationConfigurer {
 
    /**
     * 解決工作流生成图片乱码问题
     *
     * @param processEngineConfiguration processEngineConfiguration
     */
    @Override
    public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
        processEngineConfiguration.setActivityFontName("宋体");
        processEngineConfiguration.setAnnotationFontName("宋体");
        processEngineConfiguration.setLabelFontName("宋体");
    }
}

参考: blog.csdn.net/qq_27291799… 关闭自动生成: 如果,因为某种原因,在部署的时候,并不需要或者不必要生成流程定义图片,那么就需要在流程引擎配置的属性中使用isCreateDiagramOnDeploy:

<property name="createDiagramOnDeploy" value="false" />

2.部署的时候提供自定义的图片

repositoryService.createDeployment()
.key("leave")
.name("请假流程")
.addClasspathResource("processes/leave.bpmn20.xml")
.addClasspathResource("processes/leave.png")
  .deploy();
 接下来,可以通过API来获取流程定义图片资源:
  ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                                                         .processDefinitionKey("leave")
                                                         .singleResult();

流程跟踪】获取高亮活动流程图
1.高亮正在进行中的节点
注意设置字体,否则会乱码

ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("leave")
        .singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());// 模型
List<String> highLightedActivities  = runtimeService.getActiveActivityIds("2501");// 高亮节点
List<String> highLightedFlows = new ArrayList<>(); // 高亮连接线
ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
InputStream png = processDiagramGenerator.generateDiagram
                                    (bpmnModel, "png",highLightedActivities,
                                             highLightedFlows, "宋体", "微软雅黑", "黑体", null, 2.0);

2.高亮正在进行中的节点+处理过的连接线
我们来加上连接线的高亮以及所有处理过的节点

List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
    .processInstanceId(processInstanceId)
        .orderByHistoricActivityInstanceId().asc().list();
List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historicActivityInstances);// 获取处理过的连接

/**
 * 获取已经流转的线
 *
 * @param bpmnModel
 * @param historicActivityInstances
 * @return
 */
private 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(), true);
        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(), true);
        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(), true);
                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;
}

3.高亮正在进行中的节点+处理过的连接线+处理过的节点

 // 获取流程中已经执行的节点,按照执行先后顺序排序
		List<HistoricActivityInstance> historicActivityInstances = historyService.
                              createHistoricActivityInstanceQuery()
                                  .processInstanceId(processInstanceId) 
                                  .orderByHistoricActivityInstanceId().asc().list();
		// 高亮已经执行流程节点ID集合
		List<String> highLightedActivitiIds = new ArrayList<>();
		for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
			highLightedActivitiIds.add(historicActivityInstance.getActivityId());
		}               

4.自定义商务范配色
绘图原理是调用下面的方法,扩展一波即可。

ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
processDiagramGenerator.generateDiagram()

核心方法是,DefaultProcessDiagramGenerator.drawActivity 重写它吧

protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, 
    FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor) {

全部代码如下: 涉及到2个类:

LakerProcessDiagramCanvas

继承了DefaultProcessDiagramCanvas

重写定义了这2个值
HIGHLIGHT_COLOR = Color.cyan;// 默认颜色,之前是红色
THICK_TASK_BORDER_STROKE = new BasicStroke(6.0f);// 边框宽度 之前是3.0

public void drawHighLightColor(int x, int y, int width, int height, Color color) {
    Paint originalPaint = g.getPaint();
    Stroke originalStroke = g.getStroke();
 
    g.setPaint(color);
    g.setStroke(THICK_TASK_BORDER_STROKE);
 
    RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
    g.draw(rect);
 
    g.setPaint(originalPaint);
    g.setStroke(originalStroke);
}

LakerProcessDiagramGenerator

继承了DefaultProcessDiagramGenerator

重写了initProcessDiagramCanvas让其返回我们扩展的LakerProcessDiagramCanvas

return new LakerProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY,
        imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);

重写了drawActivity,最后一个节点自定义颜色显示

// 画高亮的节点 TODO
if (highLightedActivities.contains(flowNode.getId())) {
    if (highLightedActivities.get(highLightedActivities.size() - 1).equalsIgnoreCase(flowNode.getId())) {
 
        LakerProcessDiagramCanvas lakerProcessDiagramCanvas = ((LakerProcessDiagramCanvas) processDiagramCanvas);
        lakerProcessDiagramCanvas.drawHighLightColor((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), Color.YELLOW);
    } else {
        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }
}

全部源码

继承DefaultProcessDiagramCanvas

package com.laker.workflow.controller;
 
import org.activiti.image.impl.DefaultProcessDiagramCanvas;
 
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
 
public class LakerProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
    public LakerProcessDiagramCanvas(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);
        HIGHLIGHT_COLOR = Color.cyan;
        THICK_TASK_BORDER_STROKE = new BasicStroke(6.0f);
    }
 
    public LakerProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
        super(width, height, minX, minY, imageType);
        HIGHLIGHT_COLOR = Color.cyan;
        THICK_TASK_BORDER_STROKE = new BasicStroke(6.0f);
    }
 
    public void drawHighLightColor(int x, int y, int width, int height, Color color) {
        Paint originalPaint = g.getPaint();
        Stroke originalStroke = g.getStroke();
 
        g.setPaint(color);
        g.setStroke(THICK_TASK_BORDER_STROKE);
 
        RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
        g.draw(rect);
 
        g.setPaint(originalPaint);
        g.setStroke(originalStroke);
    }
 
}

继承DefaultProcessDiagramGenerator

package com.laker.workflow.controller;
 
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.image.impl.DefaultProcessDiagramCanvas;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
 
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
 
public class LakerProcessDiagramGenerator extends DefaultProcessDiagramGenerator {
    private BufferedImage processDiagram;
 
    @Override
    protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
                                                                 List<String> highLightedActivities, List<String> highLightedFlows,
                                                                 String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
 
        {
 
            prepareBpmnModel(bpmnModel);
 
            DefaultProcessDiagramCanvas 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 (FlowNode flowNode : bpmnModel.getProcesses().get(0).findFlowElementsOfType(FlowNode.class)) {
                drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor);
            }
 
            for (Process process : bpmnModel.getProcesses()) {
                for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
                    drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor);
                }
            }
 
            // 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;
        }
    }
 
    @Override
    protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
                                FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor) {
 
        {
 
            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);
                }
 
                // 画高亮的节点 TODO
                if (highLightedActivities.contains(flowNode.getId())) {
                    if (highLightedActivities.get(highLightedActivities.size() - 1).equalsIgnoreCase(flowNode.getId())) {
 
                        LakerProcessDiagramCanvas lakerProcessDiagramCanvas = ((LakerProcessDiagramCanvas) processDiagramCanvas);
                        lakerProcessDiagramCanvas.drawHighLightColor((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), Color.YELLOW);
                    } else {
                        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
                    }
 
 
                }
 
            }
 
            // Outgoing transitions of activity
            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
                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(sequenceFlow.getId())) {
                    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(sequenceFlow.getId());
                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, drawConditionalIndicator, isDefault, highLighted, scaleFactor);
 
                    // Draw sequenceflow label
                    GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
                    if (labelGraphicInfo != null) {
                        processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
                    }
                }
            }
 
            // 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 static DefaultProcessDiagramCanvas 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 LakerProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY,
                imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    }
 
}

参考:blog.csdn.net/u010740917/…

【流程跟踪】流程的任务流转路径

List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
        .processInstanceId("2515")
        .orderByHistoricTaskInstanceEndTime().asc().list();

SELECT DISTINCT RES.*
FROM ACT_HI_TASKINST RES
WHERE RES.PROC_INST_ID_ = '2515'
ORDER BY  RES.END_TIME_ ASC LIMIT '2147483647' OFFSET '0'

【流程跟踪】设置流程标题
例如流程标题为“张三的请假单”

runtimeService.setProcessInstanceName(processInstance.getId(),"张三的请假条");
查询的时候
runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1585176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

恒流电源驱动惠斯通电桥输出电压计算

笔记 并联分流/串联分压贯穿始终 先通过式子计算每条路流经的电流大小 &#xff08;1-(支路电阻)/(所有电阻) &#xff09;* 电流源I 每条之路电流大小知道了。就可以知道每条路中间点输出 支路电流*电阻。 输出即&#xff1a;中间点输出电位差。同理可以通过阻值变化推…

力扣HOT100 - 240. 搜索二维矩阵 II

解题思路&#xff1a; 从左下角开始&#xff0c;根据条件删除行和列。 class Solution {public boolean searchMatrix(int[][] matrix, int target) {int row matrix.length - 1;int col matrix[0].length - 1;int l 0;while (row > 0 && l < col) {if (targ…

深入理解java虚拟机-类的生命周期-初始化阶段

针对P2的字节码解释&#xff1a; 操作数栈里面放1&#xff0c;然后对Demo1.value的static变量进行赋值操作&#xff0c;从栈里面拿1赋值给Demo1.value的static变量 P3的情况由于源码是把 public static int value 1; static {value 2; }颠倒为&#xff1a; static {value …

2024电力、电网与智能应用技术国际学术会议(ICPGIAT2024)

2024电力、电网与智能应用技术国际学术会议(ICPGIAT2024) 会议简介 2024年国际电力、电网和智能应用技术大会&#xff08;ICPGIAT 2024&#xff09;将在中国杭州举行&#xff0c;就“电力系统”、“智能控制”和“智能应用技术”等研究主题进行讨论和交流。会议围绕智能系统…

第十二届蓝桥杯大赛软件赛省赛Java 大学 B 组题解

1、ASC public class Main {public static void main(String[] args) {System.out.println(

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理 一、前言 以下软件操作教程以&#xff0c;佳易王药店药品进销存管理软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 软件可以对药品有效期进行管理查询&#xff0c;可以…

AI大模型探索之路-应用篇8:Langchain框架LangServe模块-专注于AI模型的部署

目录 前言 一、概述 二、功能特性 三、REST API 开发 四、Postman调用测试 五、Client调用测试 总结 前言 随着AI大语言模型&#xff08;LLM&#xff09;的技术的不断演进&#xff0c;AI应用的开发和部署变得越来越复杂。在这样的背景下&#xff0c;LangServe应运而生—…

java的aop实现方式

java的aop可以通过三种方式来实现 1.使用ajc编译的插件来完成增强 这种方法会直接修改编译完成的class文件&#xff0c;不需要依赖springboot 2.动态代理proxy 2.1cglib 可以代理接口&#xff0c;方法等&#xff0c;这种方式代理类是目标类的子类&#xff0c;要求目标类不…

技术图纸受控下发软件,知名的技术图纸管理软件

技术图纸受控下发软件是一种专门用于管理和控制技术图纸下发的工具。这种软件的主要目的是确保技术图纸的安全、准确和受控的下发&#xff0c;同时提高图纸管理的效率和便利性。 以下是技术图纸受控下发软件的一些主要功能和特点&#xff1a; 权限管理&#xff1a;软件能够设置…

python基础——MySQL

文章目录 一、引入pymysql二、使用三、执行非查询性质的SQL语句四、执行查询性质的SQL语句五、数据插入 一、引入pymysql 除了使用图形化工具以外&#xff0c;我们也可以使用编程语言来执行SQL从而操作数据库。 在Python中&#xff0c;使用第三方库:pymysql来完成对MySQL数据库…

为什么每个人都需要了解这些数据加密技术?

在数字时代&#xff0c;数据加密技术不仅对保护企业的商业秘密至关重要&#xff0c;也是个人隐私安全的重要屏障。随着技术的进步和网络犯罪的增加&#xff0c;数据加密已经成为了信息安全领域的一个热点议题。以下是探讨为什么每个人都需要了解这些数据加密技术的几个主要原因…

Python 读写T5557低频RFID,将T5557卡制做成ID、HID卡

本示例使用的发卡器&#xff1a; T5557 T5567 T5577低频RFID读写器 EM4100 HID卡复制器 酒店门卡-淘宝网 (taobao.com) from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget,QMessageBox,QCheckBox,QLineEdit from…

基于ARM内核的智能手环(day7)

RTC&#xff08;实时时钟&#xff09; 什么是RTC&#xff1f; RTC是指实时时钟&#xff08;Real-Time Clock&#xff09;&#xff0c;是一种能够持续跟踪时间的计时器&#xff0c;即使在设备断电的情况下也能保持时间的准确性。它通常用于需要准确时间记录的应用&#xff0c;…

遥感降水、气温数据的处理与可视化、ERA5大气再分析数据的统计分析、干旱监测及SCI论文写作技巧

深度探讨人工智能在大气科学中的应用&#xff0c;特别是如何结合最新AI模型与Python技术处理和分析气候数据。课程介绍包括GPT-4等先进AI工具&#xff0c;旨在帮助学员掌握这些工具的功能及应用范围。内容覆盖使用GPT处理数据、生成论文摘要、文献综述、技术方法分析等实战案例…

Oracle数据库imp文件导入失败提示:“不是有效的导出文件, 标头验证失败”解决方法

导入数据库时&#xff0c;直接提示不是有效的导出文件&#xff0c;标头验证失败 原因&#xff1a;这是因为导出的imp文件和你当前导入的数据库版本不一致造成的&#xff0c;例如&#xff1a;导出文件版本号12.0.1 导入数据库的版本号11.0.2&#xff0c;会报这个错误。 解决办法…

[C++]map set

一、set 1、概念 set是按照一定次序存储元素的容器在set中&#xff0c;元素的value也标识它(value就是key&#xff0c;类型为T)&#xff0c;并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const)&#xff0c;但是可以从容器中插入或删除它们。在内部&…

泰迪智能科技高职人工智能专业人才培养方案

人工智能行业近年来得到了快速发展&#xff0c;全球科技公司都在竞相投入人工智能的研发&#xff0c;从硅谷到北京&#xff0c;都在人工智能上取得了显著的进步。人工智能已经从学术研究转变为影响制造业、医疗保健、交通运输和零售等多个行业的关键因素。我国政策的积极推动下…

备战2024年中学生古诗文大会(初中组):单选题真题和独家解析

我们继续来做中学生古诗文大会&#xff08;初中组&#xff09;&#xff0c;即我们常说的初中生古诗文大会的部分真题&#xff0c;从中了解初中生古诗文大会的考察内容和形式&#xff0c;并且提供了我独家的题目解析和答案&#xff0c;供初中的同学们学习和参考。 初中生古诗文…

C++常用技巧与算法总结(简洁)

前言警告&#xff1a; 1、注意数据的边界&#xff0c;数组不能越界&#xff01; 2、 时间复杂度 3、开long long 4、注意输出的四舍五入 printf("%d %.0f\n",k,sum*1.0/k);//double类型输出会自动四舍五入&#xff1b; 5、让Dev C支持C11 先在dev的【工具】里找…

从零到部署指南:Ubuntu上安装Boost和Crow库

1.安装boost 在安装Crow之前&#xff0c;需要确保您的系统中已经安装了Boost库。以下是Boost库安装步骤&#xff1a; 首先&#xff0c;从Boost官方网站或通过特定的链接下载Boost的源码&#xff0c;boost源码具体可参看这个链接&#xff1a; https://blog.csdn.net/duan199201…