目錄
接口(一)展示效果:
CustomProcessDiagramGeneratorI
前言:
在這之前小編寫過一篇Activiti流程高亮顯示,參考文章,下面文章中的接口(一)跟我Activit流程高亮顯示裏面的是一個類型。
接口(一)的這個方法存在一個問題,第一個流程實例顏色沒有自定義,顏色默認是紅色,第二個問題是畫流程圖的時候其實我是有個流程實例一個名字的,但是呢,接口(一)並不能將這些文字顯示出來。
暱稱爲斷翅絕塵的博主解決了我的這個問題,他寫的Activit專欄文章質量還是很高的,參照他寫的Activiti實現自定義流程圖做出了我想要的效果,當然嘍,copy過來以後還是有一些問題,一個個的把它解決掉最終也實現了和博主一樣的功能,爲博主打call.
接口(一)代碼:
@RequestMapping("/queryProPlan1")
public void queryProPlan1(HttpServletRequest request, HttpServletResponse response) throws Exception {
String processInstanceId = request.getParameter("ProcessInstanceId");
//獲取歷史流程實例
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//獲取流程圖
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
//高亮環節id集合
List<String> highLightedActivitis = new ArrayList<String>();
//高亮線路id集合
List<String> highLightedFlows = getHighLightedFlows(definitionEntity, highLightedActivitList);
for (HistoricActivityInstance tempActivity : highLightedActivitList) {
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
//中文顯示的是口口口,設置字體就好了
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, highLightedFlows, "", "宋體", "宋體", null, 1.0);
//單獨返回流程圖,不高亮顯示
byte[] b = new byte[1024];
int len;
while ((len = imageStream.read(b, 0, Constant.INT1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
展示效果:
由於當時使用的是框架自帶的方法,執行過的流程是紅色,現在需要做的如何定義這個圖例顏色,以及將圖例上的文字內容顯示在頁面上。比如下面我標註的這個文字,工程,其他,接受駁回這些信息顯示在頁面上。
接口(二)代碼
爲了改進上面這個方法,新增了一些類。
新增類大概有:
@RequestMapping("/queryProPlan")
public void queryProPlan(HttpServletRequest request, HttpServletResponse response) throws Exception {
String processInstanceId = request.getParameter("ProcessInstanceId");
if (StringUtils.isBlank(processInstanceId)) {
logger.error("參數爲空");
}
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity) repositoryService
.getProcessDefinition(processInstance.getProcessDefinitionId());
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
// 高亮環節id集合
List<String> highLightedActivitis = new ArrayList<String>();
// 高亮線路id集合
List<String> highLightedFlows = getHighLightedFlows(definitionEntity, highLightedActivitList);
for (HistoricActivityInstance tempActivity : highLightedActivitList) {
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(processInstance.getId()).list()
.stream().map(e->e.getActivityId()).collect(Collectors.toSet());
CustomProcessDiagramGeneratorI diagramGenerator = (CustomProcessDiagramGeneratorI) processEngineConfiguration
.getProcessDiagramGenerator();
InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,
highLightedFlows, "宋體", "宋體", "宋體", null, 1.0, new Color[] { WorkflowConstants.COLOR_NORMAL, WorkflowConstants.COLOR_CURRENT }, currIds);
// 輸出資源內容到相應對象
byte[] b = new byte[1024];
int len;
while ((len = imageStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
接口(二)展示效果
其中動態流程圖定義的顏色:
接下來我會將代碼都貼出來。
ActivitiConfiguration
package com.bos.activiti;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
/**
* @Author tanghh
* @Date 2020/5/19 9:08
*/
@Configuration
public class ActivitiConfiguration implements ProcessEngineConfigurationConfigurer {
@Autowired
private CustomProcessDiagramGeneratorI customProcessDiagramGeneratorI;
@Override
public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
// TODO Auto-generated method stub
processEngineConfiguration.setDatabaseSchemaUpdate("none");// none true
processEngineConfiguration.setDatabaseType("mysql");
// 流程圖字體
processEngineConfiguration.setActivityFontName("宋體");
processEngineConfiguration.setAnnotationFontName("宋體");
processEngineConfiguration.setLabelFontName("宋體");
processEngineConfiguration.setJpaHandleTransaction(false);
processEngineConfiguration.setJpaCloseEntityManager(false);
processEngineConfiguration.setJobExecutorActivate(false);
processEngineConfiguration.setAsyncExecutorEnabled(false);
//自定義流程圖樣式
processEngineConfiguration.setProcessDiagramGenerator(customProcessDiagramGeneratorI);
}
}
CustomProcessDiagramCanvas
package com.bos.activiti;
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));
/* String baseUrl = Thread.currentThread().getContextClassLoader().getResource("static/img/activiti/").getPath();
SCRIPTTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"scriptTask.png"));
USERTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"userTask.png"));
SERVICETASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"serviceTask.png"));
RECEIVETASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"receiveTask.png"));
SENDTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"sendTask.png"));
MANUALTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"manualTask.png"));
BUSINESS_RULE_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"businessRuleTask.png"));
SHELL_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"shellTask.png"));
CAMEL_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"camelTask.png"));
MULE_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"muleTask.png"));
TIMER_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"timer.png"));
COMPENSATE_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"compensate-throw.png"));
COMPENSATE_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"compensate.png"));
ERROR_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"error-throw.png"));
ERROR_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"error.png"));
MESSAGE_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"message-throw.png"));
MESSAGE_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"message.png"));
SIGNAL_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"signal-throw.png"));
SIGNAL_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"signal.png"));*/
flag = true;
} catch (IOException e) {
flag = false;
LOGGER.warn("Could not load image for process diagram creation: {}", e.getMessage());
}
}
}
CustomProcessDiagramGenerator
package com.bos.activiti;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
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;
}
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]);
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);
}
}
CustomProcessDiagramGeneratorI
package com.bos.activiti;
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;
/**
* 參考文章 https://www.jianshu.com/p/3f976a47114c
* @Author tanghh
* @Date 2020/5/19 9:08
*/
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);
}
ProcessTypeEnum
package com.bos.activiti;
/**
* @Author tanghh
* @Date 2020/5/19 10:42
*/
public enum ProcessTypeEnum {
BUILD_ACCOUNT_APPLY,
CREDIT_LETTER_APPLY,
CREDIT_LETTER_TRANSFER,
CREDIT_LETTER_CASH
}
WorkflowConstants
package com.bos.activiti;
import com.google.common.collect.Lists;
import java.awt.Color;
import java.util.List;
/**
* 變量類
*@author jjd
**/
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;
/** 定義新版業務進度查詢包含的流程類型 **/
public static final List<ProcessTypeEnum> INCLUDE_PROCEE_TYPE = Lists.newArrayList(
ProcessTypeEnum.BUILD_ACCOUNT_APPLY,
ProcessTypeEnum.CREDIT_LETTER_APPLY,
ProcessTypeEnum.CREDIT_LETTER_TRANSFER,
ProcessTypeEnum.CREDIT_LETTER_CASH);
}
本篇文章就到這嘍,如果覺得小編寫的不錯的話,不妨給小編一個小心喔
整個功能涉及的代碼我已貼出,是公司項目所以不能貼出整個項目源碼,如果在使用過程中有什麼問題的話歡迎指出。