效果图
代码
1.bpmn文件ExpenseProcess.bpmn20.xml(通过可视化工具生成【非常熟悉也可以手动编写】)
<?xml version="1.0" encoding="UTF-8"?><definitions xmlns="/spec/BPMN/0524/MODEL" xmlns:xsi="/2001/XMLSchema-instance"xmlns:flowable="/bpmn" xmlns:bpmndi="/spec/BPMN/0524/DI"xmlns:omgdc="/spec/DD/0524/DC" xmlns:omgdi="/spec/DD/0524/DI"typeLanguage="/2001/XMLSchema" expressionLanguage="/1999/XPath"targetNamespace="/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 /11/11.*/public class DefaultProcessDiagramCanvasExt extends DefaultProcessDiagramCanvas {//定义走过流程连线颜色为红色protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN;//设置未走过流程的连接线颜色protected static Color CONNECTION_COLOR = Color.BLACK;//设置flows连接线字体颜色redprotected 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);}/*** 重写绘制连线的方式,设置绘制颜色*/@Overridepublic 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);}//设置字体大小图标颜色@Overridepublic 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());}}//设置连接线字体@Overridepublic void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) {float interline = 1.0f;// textif (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 originalsg.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 relativeg.setPaint(TASK_BOX_COLOR);int arcR = 6;if (thickBorder)arcR = 3;// shapeRoundRectangle2D 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);// textif (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 imageint 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 /11/11.*/public class DefaultProcessDiagramGeneratorExt extends DefaultProcessDiagramGenerator {@Overrideprotected 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 markerboolean 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 markerGraphicInfo 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 markersprocessDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),multiInstanceSequential, multiInstanceParallel, collapsed);}// Draw highlighted activitiesif (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 activityfor (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 labelGraphicInfo 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 elementsif (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 /11/7*/@Configurationpublic class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {@Overridepublic void configure(SpringProcessEngineConfiguration engineConfiguration) {engineConfiguration.setActivityFontName("宋体");engineConfiguration.setLabelFontName("宋体");engineConfiguration.setAnnotationFontName("宋体");}}
(4)提交请假(简单演示提交后就全部自动审批完成)
/*** 提交请假** @param processId 任务ID*/@RequestMapping(value = "submit")@ResponseBodypublic 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);plete(task.getId());//老板或经理审批根据请假天数Task task2= taskService.createTaskQuery().processInstanceId(pi.getProcessInstanceId()).singleResult();if("boss".equals(task2.getAssignee())){taskService.setVariable(task2.getId(),"outcome","驳回");plete(task2.getId());}else{taskService.setVariable(task2.getId(),"outcome","通过");plete(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;@SpringBootApplicationpublic class FlowableApplication {public static void main(String[] args) {SpringApplication.run(FlowableApplication.class, args);}@Beanpublic 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="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /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张表格