- 效果图
- 代码
1.bpmn文件ExpenseProcess.bpmn20.xml(通过可视化工具生成【非常熟悉也可以手动编写】)
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="Expense" name="ExpenseProcess" isExecutable="true">
<documentation>请假申请</documentation>
<startEvent id="start" name="开始"></startEvent>
<sequenceFlow id="flow1" sourceRef="start" targetRef="fillTask"/>
<userTask id="fillTask" name="请假申请" flowable:assignee="${taskUser}"/>
<exclusiveGateway id="judgeTask"></exclusiveGateway>
<sequenceFlow id="flow2" sourceRef="fillTask" targetRef="judgeTask"/>
<sequenceFlow id="judgeMore" name="大于3天" sourceRef="judgeTask" targetRef="bossTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day > 3}]]></conditionExpression>
</sequenceFlow>
<userTask id="bossTask" name="老板审批" flowable:assignee="${user2}"/>
<sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="fillTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="judgeLess" name="小于等于3天" sourceRef="judgeTask" targetRef="directorTak">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day <= 3}]]></conditionExpression>
</sequenceFlow>
<userTask id="directorTak" name="经理审批" flowable:assignee="${user1}"/>
<sequenceFlow id="directorNotPassFlow" name="驳回" sourceRef="directorTak" targetRef="fillTask">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="directorPassFlow" name="通过" sourceRef="directorTak" targetRef="end">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
</sequenceFlow>
<endEvent id="end" name="结束"></endEvent>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_Expense">
<bpmndi:BPMNPlane bpmnElement="Expense" id="BPMNPlane_Expense">
<bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
<omgdc:Bounds height="30.0" width="30.0" x="85.0" y="85.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="fillTask" id="BPMNShape_fillTask">
<omgdc:Bounds height="80.0" width="100.0" x="205.0" y="60.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
<omgdc:Bounds height="40.0" width="40.0" x="385.0" y="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="directorTak" id="BPMNShape_directorTak">
<omgdc:Bounds height="80.0" width="100.0" x="535.0" y="60.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
<omgdc:Bounds height="80.0" width="100.0" x="355.0" y="205.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
<omgdc:Bounds height="28.0" width="28.0" x="571.0" y="231.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="115.0" y="100.0"/>
<omgdi:waypoint x="205.0" y="100.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="305.0" y="100.16611295681062"/>
<omgdi:waypoint x="385.4333333333333" y="100.43333333333334"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
<omgdi:waypoint x="424.5530726256983" y="100.44692737430168"/>
<omgdi:waypoint x="535.0" y="100.1392757660167"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="directorNotPassFlow" id="BPMNEdge_directorNotPassFlow">
<omgdi:waypoint x="585.0" y="110.0"></omgdi:waypoint>
<omgdi:waypoint x="585.0" y="37.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="37.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="110.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
<omgdi:waypoint x="455.0" y="245.0"/>
<omgdi:waypoint x="571.0" y="245.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
<omgdi:waypoint x="405.4340277777778" y="119.56597222222223"/>
<omgdi:waypoint x="405.1384083044983" y="205.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="directorPassFlow" id="BPMNEdge_directorPassFlow">
<omgdi:waypoint x="585.0" y="140.0"/>
<omgdi:waypoint x="585.0" y="231.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
<omgdi:waypoint x="355.0" y="245.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="245.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="140.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
2.自定义已经走过的流程高亮连接线和高亮图标
(1)重写DefaultProcessDiagramCanvas方法
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.bpmn.model.GraphicInfo;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.util.ReflectUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
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;
/**
* Created by x_zenglengceng on 2019/11/11.
*/
public class DefaultProcessDiagramCanvasExt extends DefaultProcessDiagramCanvas {
//定义走过流程连线颜色为红色
protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN;
//设置未走过流程的连接线颜色
protected static Color CONNECTION_COLOR = Color.BLACK;
//设置flows连接线字体颜色red
protected static Color LABEL_COLOR = new Color(0, 0, 0);
//高亮显示task框颜色
protected static Color HIGHLIGHT_COLOR = Color.GREEN;
protected static Color HIGHLIGHT_COLOR1 = Color.RED;
public DefaultProcessDiagramCanvasExt(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);
this.initialize(imageType);
}
/**
* 重写绘制连线的方式,设置绘制颜色
*/
@Override
public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
Paint originalPaint = this.g.getPaint();
Stroke originalStroke = this.g.getStroke();
this.g.setPaint(CONNECTION_COLOR);
if (connectionType.equals("association")) {
this.g.setStroke(ASSOCIATION_STROKE);
} else if (highLighted) {
this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR);
this.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];
java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double)sourceX, (double)sourceY, (double)targetX, (double)targetY);
this.g.draw(line);
}
java.awt.geom.Line2D.Double line;
if (isDefault) {
line = new java.awt.geom.Line2D.Double((double)xPoints[0], (double)yPoints[0], (double)xPoints[1], (double)yPoints[1]);
this.drawDefaultSequenceFlowIndicator(line, scaleFactor);
}
if (conditional) {
line = new java.awt.geom.Line2D.Double((double)xPoints[0], (double)yPoints[0], (double)xPoints[1], (double)yPoints[1]);
this.drawConditionalSequenceFlowIndicator(line, scaleFactor);
}
if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) {
line = new java.awt.geom.Line2D.Double((double)xPoints[xPoints.length - 2], (double)yPoints[xPoints.length - 2], (double)xPoints[xPoints.length - 1], (double)yPoints[xPoints.length - 1]);
this.drawArrowHead(line, scaleFactor);
}
if (associationDirection.equals(AssociationDirection.BOTH)) {
line = new java.awt.geom.Line2D.Double((double)xPoints[1], (double)yPoints[1], (double)xPoints[0], (double)yPoints[0]);
this.drawArrowHead(line, scaleFactor);
}
this.g.setPaint(originalPaint);
this.g.setStroke(originalStroke);
}
//设置字体大小图标颜色
@Override
public void initialize(String imageType) {
if("png".equalsIgnoreCase(imageType)) {
this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2);
} else {
this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1);
}
this.g = this.processDiagram.createGraphics();
if(!"png".equalsIgnoreCase(imageType)) {
this.g.setBackground(new Color(255, 255, 255, 0));
this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
}
this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//修改图标颜色,修改图标字体大小
this.g.setPaint(Color.black);
Font font = new Font(this.activityFontName, 10, 14);
this.g.setFont(font);
this.fontMetrics = this.g.getFontMetrics();
//修改连接线字体大小
LABEL_FONT = new Font(this.labelFontName, 10, 15);
ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11);
try {
USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader));
SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader));
SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader));
RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader));
SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader));
MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader));
BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader));
SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader));
DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader));
CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader));
MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader));
HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader));
TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader));
COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader));
COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader));
ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader));
ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader));
MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader));
MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader));
SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader));
SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader));
} catch (IOException var4) {
LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage());
}
}
//设置连接线字体
@Override
public void drawLabel(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();
g.setPaint(LABEL_COLOR);
g.setFont(LABEL_FONT);
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);
}
}
//高亮显示task框完成的
public void drawHighLight(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(HIGHLIGHT_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);
}
//自定义task框当前的位置
public void drawHighLight1(int x, int y, int width, int height) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(HIGHLIGHT_COLOR1);
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);
}
//task框自定义文字
protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) {
// if("请假申请".equals(name)){
// name=name+" (小强)";
// }
// if("老板审批".equals(name)){
// name=name+" (BOSS)";
// }
// if("经理审批".equals(name)){
// name=name+" (MG)";
// }
Paint originalPaint = g.getPaint();
int x = (int) graphicInfo.getX();
int y = (int) graphicInfo.getY();
int width = (int) graphicInfo.getWidth();
int height = (int) graphicInfo.getHeight();
// Create a new gradient paint for every task box, gradient depends on x and y and is not relative
g.setPaint(TASK_BOX_COLOR);
int arcR = 6;
if (thickBorder)
arcR = 3;
// shape
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR);
g.fill(rect);
g.setPaint(TASK_BORDER_COLOR);
if (thickBorder) {
Stroke originalStroke = g.getStroke();
g.setStroke(THICK_TASK_BORDER_STROKE);
g.draw(rect);
g.setStroke(originalStroke);
} else {
g.draw(rect);
}
g.setPaint(originalPaint);
// text
if (scaleFactor == 1.0 && name != null && name.length() > 0) {
int boxWidth = width - (2 * TEXT_PADDING);
int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2;
int boxX = x + width / 2 - boxWidth / 2;
int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2;
drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight);
}
}
protected static Color EVENT_COLOR = new Color(255, 255, 255);
//重写开始事件
public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) {
Paint originalPaint = g.getPaint();
g.setPaint(EVENT_COLOR);
Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
graphicInfo.getWidth(), graphicInfo.getHeight());
g.fill(circle);
g.setPaint(EVENT_BORDER_COLOR);
g.draw(circle);
g.setPaint(originalPaint);
if (image != null) {
// calculate coordinates to center image
int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor)));
int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor)));
g.drawImage(image, imageX, imageY,
(int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null);
}
}
//重写结束事件
public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(EVENT_COLOR);
Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
graphicInfo.getWidth(), graphicInfo.getHeight());
g.fill(circle);
g.setPaint(EVENT_BORDER_COLOR);
if (scaleFactor == 1.0) {
g.setStroke(END_EVENT_STROKE);
} else {
g.setStroke(new BasicStroke(2.0f));
}
g.draw(circle);
g.setStroke(originalStroke);
g.setPaint(originalPaint);
}
}
(2)重写DefaultProcessDiagramGenerator方法
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by x_zenglengceng on 2019/11/11.
*/
public class DefaultProcessDiagramGeneratorExt extends DefaultProcessDiagramGenerator {
@Override
protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
this.prepareBpmnModel(bpmnModel);
DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
Iterator var13 = bpmnModel.getPools().iterator();
while(var13.hasNext()) {
Pool process = (Pool)var13.next();
GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId());
processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor);
}
var13 = bpmnModel.getProcesses().iterator();
Process process1;
Iterator subProcesses1;
while(var13.hasNext()) {
process1 = (Process)var13.next();
subProcesses1 = process1.getLanes().iterator();
while(subProcesses1.hasNext()) {
Lane artifact = (Lane)subProcesses1.next();
GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId());
processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor);
}
}
var13 = bpmnModel.getProcesses().iterator();
while(var13.hasNext()) {
process1 = (Process)var13.next();
subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator();
while(subProcesses1.hasNext()) {
FlowNode artifact1 = (FlowNode)subProcesses1.next();
if(!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) {
this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI));
}
}
}
var13 = bpmnModel.getProcesses().iterator();
label75:
while(true) {
List subProcesses2;
do {
if(!var13.hasNext()) {
return processDiagramCanvas;
}
process1 = (Process)var13.next();
subProcesses1 = process1.getArtifacts().iterator();
while(subProcesses1.hasNext()) {
Artifact artifact2 = (Artifact)subProcesses1.next();
this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2);
}
subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true);
} while(subProcesses2 == null);
Iterator artifact3 = subProcesses2.iterator();
while(true) {
GraphicInfo graphicInfo;
SubProcess subProcess1;
do {
do {
if(!artifact3.hasNext()) {
continue label75;
}
subProcess1 = (SubProcess)artifact3.next();
graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId());
} while(graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue());
} while(this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel));
Iterator var19 = subProcess1.getArtifacts().iterator();
while(var19.hasNext()) {
Artifact subProcessArtifact = (Artifact)var19.next();
this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
}
}
}
}
protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
double minX = 1.7976931348623157E308D;
double maxX = 0.0D;
double minY = 1.7976931348623157E308D;
double maxY = 0.0D;
GraphicInfo nrOfLanes;
for(Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) {
Pool artifacts = (Pool)flowNodes.next();
nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId());
minX = nrOfLanes.getX();
maxX = nrOfLanes.getX() + nrOfLanes.getWidth();
minY = nrOfLanes.getY();
}
List var23 = gatherAllFlowNodes(bpmnModel);
Iterator var24 = var23.iterator();
label155:
while(var24.hasNext()) {
FlowNode var26 = (FlowNode)var24.next();
GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId());
if(artifact.getX() + artifact.getWidth() > maxX) {
maxX = artifact.getX() + artifact.getWidth();
}
if(artifact.getX() < minX) {
minX = artifact.getX();
}
if(artifact.getY() + artifact.getHeight() > maxY) {
maxY = artifact.getY() + artifact.getHeight();
}
if(artifact.getY() < minY) {
minY = artifact.getY();
}
Iterator process = var26.getOutgoingFlows().iterator();
while(true) {
List l;
do {
if(!process.hasNext()) {
continue label155;
}
SequenceFlow graphicInfoList = (SequenceFlow)process.next();
l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId());
} while(l == null);
Iterator graphicInfo = l.iterator();
while(graphicInfo.hasNext()) {
GraphicInfo graphicInfo1 = (GraphicInfo)graphicInfo.next();
if(graphicInfo1.getX() > maxX) {
maxX = graphicInfo1.getX();
}
if(graphicInfo1.getX() < minX) {
minX = graphicInfo1.getX();
}
if(graphicInfo1.getY() > maxY) {
maxY = graphicInfo1.getY();
}
if(graphicInfo1.getY() < minY) {
minY = graphicInfo1.getY();
}
}
}
}
List var25 = gatherAllArtifacts(bpmnModel);
Iterator var27 = var25.iterator();
GraphicInfo var37;
while(var27.hasNext()) {
Artifact var29 = (Artifact)var27.next();
GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId());
if(var31 != null) {
if(var31.getX() + var31.getWidth() > maxX) {
maxX = var31.getX() + var31.getWidth();
}
if(var31.getX() < minX) {
minX = var31.getX();
}
if(var31.getY() + var31.getHeight() > maxY) {
maxY = var31.getY() + var31.getHeight();
}
if(var31.getY() < minY) {
minY = var31.getY();
}
}
List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId());
if(var33 != null) {
Iterator var35 = var33.iterator();
while(var35.hasNext()) {
var37 = (GraphicInfo)var35.next();
if(var37.getX() > maxX) {
maxX = var37.getX();
}
if(var37.getX() < minX) {
minX = var37.getX();
}
if(var37.getY() > maxY) {
maxY = var37.getY();
}
if(var37.getY() < minY) {
minY = var37.getY();
}
}
}
}
int var28 = 0;
Iterator var30 = bpmnModel.getProcesses().iterator();
while(var30.hasNext()) {
Process var32 = (Process)var30.next();
Iterator var34 = var32.getLanes().iterator();
while(var34.hasNext()) {
Lane var36 = (Lane)var34.next();
++var28;
var37 = bpmnModel.getGraphicInfo(var36.getId());
if(var37.getX() + var37.getWidth() > maxX) {
maxX = var37.getX() + var37.getWidth();
}
if(var37.getX() < minX) {
minX = var37.getX();
}
if(var37.getY() + var37.getHeight() > maxY) {
maxY = var37.getY() + var37.getHeight();
}
if(var37.getY() < minY) {
minY = var37.getY();
}
}
}
if(var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) {
minX = 0.0D;
minY = 0.0D;
}
return new DefaultProcessDiagramCanvasExt((int)maxX + 10, (int)maxY + 10, (int)minX, (int)minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
}
private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
private static void drawHighLigh1(DefaultProcessDiagramCanvasExt processDiagramCanvas, GraphicInfo graphicInfo) {
processDiagramCanvas.drawHighLight1((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
FlowNode flowNode, java.util.List<String> highLightedActivities, java.util.List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI ) {
DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
if (drawInstruction != null) {
drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
// Gather info on the multi instance marker
boolean multiInstanceSequential = false;
boolean multiInstanceParallel = false;
boolean 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(highLightedActivities.get(highLightedActivities.size()-1).equals(flowNode.getId())
&&!flowNode.getId().equals("endenv")){
//更改源码没有走的红色表示
drawHighLigh1((DefaultProcessDiagramCanvasExt)processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
}else{
drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
}
}
}
// 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);
java.util.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);
}else {
if (drawSequenceFlowNameWithNoLabelDI) {
GraphicInfo lineCenter = getLineCenter(graphicInfoList);
processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
}
}
}
}
// Nested elements
if (flowNode instanceof FlowElementsContainer) {
for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) {
drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
highLightedActivities, highLightedFlows, scaleFactor,drawSequenceFlowNameWithNoLabelDI);
}
}
}
}
}
(3)解决图片文字乱码问题
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
* 为解决flowable图片中的中文乱码
*
* @author zlc
* @date 2019/11/7
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}
(4)提交请假(简单演示提交后就全部自动审批完成)
/**
* 提交请假
*
* @param processId 任务ID
*/
@RequestMapping(value = "submit")
@ResponseBody
public String submit(String taskUser,Integer day){
List<Task> tasks = taskService.createTaskQuery().taskAssignee(taskUser).orderByTaskCreateTime().desc().list();
if(day==null||"".equals(day)){
return "请写入请假天数.........";
}
// if(tasks!=null&&tasks.size()!=0){
// return "任务已经提交,正在处理中.........";
// }
//启动流程
HashMap<String, Object> map = new HashMap<>();
map.put("taskUser", taskUser);
map.put("user1", "mg");
map.put("user2", "boss");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("Expense", map);
//过排他网关
Task task = taskService.createTaskQuery().processInstanceId(pi.getProcessInstanceId()).singleResult();
taskService.setVariable(task.getId(),"day",day);
taskService.complete(task.getId());
//老板或经理审批根据请假天数
Task task2= taskService.createTaskQuery().processInstanceId(pi.getProcessInstanceId()).singleResult();
if("boss".equals(task2.getAssignee())){
taskService.setVariable(task2.getId(),"outcome","驳回");
taskService.complete(task2.getId());
}else{
taskService.setVariable(task2.getId(),"outcome","通过");
taskService.complete(task2.getId());
}
return "{进程实例id:"+"\""+pi.getId()+"\""+","+"请假申请人:"+"\""+taskUser+"\""+","+"请假天数:"+"\""+day+"\""+"}";
}
(5)生成图片的方法
/**
* @param processId 提交流程的进程实例id
*/
@RequestMapping(value = "processImage")
public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) {
/**
* 获得当前活动的节点
*/
String processDefinitionId = "";
if (this.isFinished(processId)) {// 如果流程已经结束,则得到结束节点
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId=pi.getProcessDefinitionId();
} else {// 如果流程没有结束,则取当前活动节点
// 根据流程实例ID获得当前处于活动状态的ActivityId合集
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId=pi.getProcessDefinitionId();
}
List<String> highLightedActivitis = new ArrayList<String>();
/**
* 获得活动的节点
*/
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
//高亮节点
for(HistoricActivityInstance tempActivity : highLightedActivitList){
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
List<String> highLightedflows = new ArrayList<>();
//高亮线
for(HistoricActivityInstance tempActivity : highLightedActivitList){
if("sequenceFlow".equals(tempActivity.getActivityType())){
highLightedflows.add(tempActivity.getActivityId());
}
}
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
// ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
//获取自定义图片生成器
ProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGeneratorExt();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, highLightedflows, engconf.getActivityFontName(),
engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} catch (IOException e) {
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
}
(6)springboot启动
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
@SpringBootApplication
public class FlowableApplication {
public static void main(String[] args) {
SpringApplication.run(FlowableApplication.class, args);
}
@Bean
public InitializingBean initializingBean(RepositoryService repositoryService) {
return () -> {
List<Deployment> list = repositoryService.createDeploymentQuery().list();
System.err.println("Deployment size: " + list.size());// where '1' is expected
};
}
}
- pom.xml需要的依赖(本文使用oracle数据库)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.phfund</groupId>
<artifactId>flowable</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>flowable</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--flowable工作流依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- oracle驱动 -->
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
<scope>runtime</scope>
</dependency>
<!-- 加密 注意版本-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
flowable6.4.1初始化生成70张表格