从Excel中读取数据并自动生成BPMN标准流程图

2022年6月,由于某个项目建设的要求,需要从Excel中读取流程数据并且自动生成遵循BPMN标准的流程图,以用于作业处理,目前支持这些流程图的主流开源框架有Activiti、Flowable、Camunda。由于没有在网上搜索到现成的方案,于是自己通过分析BPMN文件结构,设计了一套实现方案,可能绘制的流程图并不是很完美,但在工作中比较好的实现了预期的效果,这里做个记录以备后续学习改进。

1、原理

以Camunda Modeler绘制的流程图为例,Camunda流程图遵从BPMN2.0标准,流程描述文档为xml格式,打开一个bpmn文件的文本格式,其中的xml结构主要有两部分:process和bpmndi:BPMNDiagram,其中process为各个节点的属性和前后关系。sequenceFlow中有描述sourceRef和targetRef,表示连接线的两端,exclusiveGateway中有描述incoming和outgoing,用于表示流入和流出。
要生成流程图,想办法按格式生成xml文件即可。
以下是一个流程图的样例:
bpmn流程图样例

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0mfbluk" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.9.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.1.0"><bpmn:process id="Process_0hmfwtd" isExecutable="true"><bpmn:startEvent id="StartEvent_1"><bpmn:outgoing>Flow_0w2j9lb</bpmn:outgoing></bpmn:startEvent><bpmn:sequenceFlow id="Flow_0w2j9lb" sourceRef="StartEvent_1" targetRef="Activity_0mwthrf" /><bpmn:sequenceFlow id="Flow_1ez6hz2" sourceRef="Activity_0mwthrf" targetRef="Activity_18ebup0" /><bpmn:userTask id="Activity_0mwthrf" name="节点1"><bpmn:incoming>Flow_0w2j9lb</bpmn:incoming><bpmn:outgoing>Flow_1ez6hz2</bpmn:outgoing></bpmn:userTask><bpmn:userTask id="Activity_18ebup0" name="节点2"><bpmn:incoming>Flow_1ez6hz2</bpmn:incoming><bpmn:outgoing>Flow_1jvovvr</bpmn:outgoing></bpmn:userTask><bpmn:sequenceFlow id="Flow_1jvovvr" sourceRef="Activity_18ebup0" targetRef="Activity_1u98wuw" /><bpmn:userTask id="Activity_1u98wuw" name="节点3"><bpmn:incoming>Flow_1jvovvr</bpmn:incoming><bpmn:outgoing>Flow_1doeb9v</bpmn:outgoing></bpmn:userTask><bpmn:task id="Activity_0my6802" name="节点4"><bpmn:incoming>Flow_1doeb9v</bpmn:incoming><bpmn:outgoing>Flow_0v77htt</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_1doeb9v" sourceRef="Activity_1u98wuw" targetRef="Activity_0my6802" /><bpmn:exclusiveGateway id="Gateway_13kpjty"><bpmn:incoming>Flow_0v77htt</bpmn:incoming><bpmn:outgoing>Flow_0rx278g</bpmn:outgoing><bpmn:outgoing>Flow_0ivytvj</bpmn:outgoing></bpmn:exclusiveGateway><bpmn:sequenceFlow id="Flow_0v77htt" sourceRef="Activity_0my6802" targetRef="Gateway_13kpjty" /><bpmn:sequenceFlow id="Flow_0rx278g" name="分支1" sourceRef="Gateway_13kpjty" targetRef="Activity_0u0ewsu" /><bpmn:userTask id="Activity_0u0ewsu" name="节点5"><bpmn:incoming>Flow_0rx278g</bpmn:incoming><bpmn:outgoing>Flow_0fvhke0</bpmn:outgoing></bpmn:userTask><bpmn:task id="Activity_076z7o7" name="节点6"><bpmn:incoming>Flow_0fvhke0</bpmn:incoming><bpmn:incoming>Flow_1hg2jlc</bpmn:incoming><bpmn:outgoing>Flow_19hca6k</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_0fvhke0" sourceRef="Activity_0u0ewsu" targetRef="Activity_076z7o7" /><bpmn:exclusiveGateway id="Gateway_1meq3pj"><bpmn:incoming>Flow_19hca6k</bpmn:incoming><bpmn:outgoing>Flow_0om5nu7</bpmn:outgoing><bpmn:outgoing>Flow_101go1a</bpmn:outgoing></bpmn:exclusiveGateway><bpmn:sequenceFlow id="Flow_19hca6k" sourceRef="Activity_076z7o7" targetRef="Gateway_1meq3pj" /><bpmn:task id="Activity_0slozxh" name="节点7"><bpmn:incoming>Flow_0om5nu7</bpmn:incoming><bpmn:outgoing>Flow_1hg2jlc</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_0om5nu7" name="分支1" sourceRef="Gateway_1meq3pj" targetRef="Activity_0slozxh" /><bpmn:sequenceFlow id="Flow_1hg2jlc" sourceRef="Activity_0slozxh" targetRef="Activity_076z7o7" /><bpmn:task id="Activity_1qisdls" name="节点8"><bpmn:incoming>Flow_101go1a</bpmn:incoming><bpmn:incoming>Flow_1449zkx</bpmn:incoming><bpmn:outgoing>Flow_02gk645</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_101go1a" name="分支2" sourceRef="Gateway_1meq3pj" targetRef="Activity_1qisdls" /><bpmn:exclusiveGateway id="Gateway_0flehn2"><bpmn:incoming>Flow_02gk645</bpmn:incoming><bpmn:outgoing>Flow_0tdyk61</bpmn:outgoing><bpmn:outgoing>Flow_154cjgf</bpmn:outgoing></bpmn:exclusiveGateway><bpmn:sequenceFlow id="Flow_02gk645" sourceRef="Activity_1qisdls" targetRef="Gateway_0flehn2" /><bpmn:task id="Activity_0un6cyt" name="节点9"><bpmn:incoming>Flow_0tdyk61</bpmn:incoming><bpmn:outgoing>Flow_1449zkx</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_0tdyk61" name="分支1" sourceRef="Gateway_0flehn2" targetRef="Activity_0un6cyt" /><bpmn:task id="Activity_05jsz8l" name="节点10"><bpmn:incoming>Flow_154cjgf</bpmn:incoming><bpmn:outgoing>Flow_15aey6d</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_154cjgf" name="分支2" sourceRef="Gateway_0flehn2" targetRef="Activity_05jsz8l" /><bpmn:task id="Activity_1hwf9iy" name="节点11"><bpmn:incoming>Flow_15aey6d</bpmn:incoming><bpmn:outgoing>Flow_0cwzv60</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_15aey6d" sourceRef="Activity_05jsz8l" targetRef="Activity_1hwf9iy" /><bpmn:task id="Activity_08zhct0" name="节点12"><bpmn:incoming>Flow_0cwzv60</bpmn:incoming><bpmn:outgoing>Flow_0b4ac3v</bpmn:outgoing></bpmn:task><bpmn:sequenceFlow id="Flow_0cwzv60" sourceRef="Activity_1hwf9iy" targetRef="Activity_08zhct0" /><bpmn:exclusiveGateway id="Gateway_1x0x5ad"><bpmn:incoming>Flow_0b4ac3v</bpmn:incoming><bpmn:outgoing>Flow_0dvvwp6</bpmn:outgoing><bpmn:outgoing>Flow_0josnvh</bpmn:outgoing></bpmn:exclusiveGateway><bpmn:sequenceFlow id="Flow_0b4ac3v" sourceRef="Activity_08zhct0" targetRef="Gateway_1x0x5ad" /><bpmn:task id="Activity_1okc7tv" name="节点13"><bpmn:incoming>Flow_0dvvwp6</bpmn:incoming></bpmn:task><bpmn:sequenceFlow id="Flow_0dvvwp6" name="分支1" sourceRef="Gateway_1x0x5ad" targetRef="Activity_1okc7tv" /><bpmn:endEvent id="Event_1vpetqu"><bpmn:incoming>Flow_0josnvh</bpmn:incoming><bpmn:incoming>Flow_0ivytvj</bpmn:incoming></bpmn:endEvent><bpmn:sequenceFlow id="Flow_0josnvh" name="分支2" sourceRef="Gateway_1x0x5ad" targetRef="Event_1vpetqu" /><bpmn:sequenceFlow id="Flow_0ivytvj" name="分支2" sourceRef="Gateway_13kpjty" targetRef="Event_1vpetqu" /><bpmn:sequenceFlow id="Flow_1449zkx" sourceRef="Activity_0un6cyt" targetRef="Activity_1qisdls" /></bpmn:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0hmfwtd"><bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"><dc:Bounds x="179" y="159" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1eaprsv_di" bpmnElement="Activity_0mwthrf"><dc:Bounds x="270" y="137" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_0pu6rnq_di" bpmnElement="Activity_18ebup0"><dc:Bounds x="430" y="137" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1l1fsdt_di" bpmnElement="Activity_1u98wuw"><dc:Bounds x="590" y="137" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_0my6802_di" bpmnElement="Activity_0my6802"><dc:Bounds x="750" y="137" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Gateway_13kpjty_di" bpmnElement="Gateway_13kpjty" isMarkerVisible="true"><dc:Bounds x="915" y="152" width="50" height="50" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1h063h5_di" bpmnElement="Activity_0u0ewsu"><dc:Bounds x="1030" y="137" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_076z7o7_di" bpmnElement="Activity_076z7o7"><dc:Bounds x="1200" y="137" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Gateway_1meq3pj_di" bpmnElement="Gateway_1meq3pj" isMarkerVisible="true"><dc:Bounds x="1375" y="152" width="50" height="50" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_0slozxh_di" bpmnElement="Activity_0slozxh"><dc:Bounds x="1500" y="137" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1qisdls_di" bpmnElement="Activity_1qisdls"><dc:Bounds x="1500" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Gateway_0flehn2_di" bpmnElement="Gateway_0flehn2" isMarkerVisible="true"><dc:Bounds x="1375" y="375" width="50" height="50" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_0un6cyt_di" bpmnElement="Activity_0un6cyt"><dc:Bounds x="1200" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_05jsz8l_di" bpmnElement="Activity_05jsz8l"><dc:Bounds x="890" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1hwf9iy_di" bpmnElement="Activity_1hwf9iy"><dc:Bounds x="750" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_08zhct0_di" bpmnElement="Activity_08zhct0"><dc:Bounds x="590" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Gateway_1x0x5ad_di" bpmnElement="Gateway_1x0x5ad" isMarkerVisible="true"><dc:Bounds x="455" y="375" width="50" height="50" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Activity_1okc7tv_di" bpmnElement="Activity_1okc7tv"><dc:Bounds x="270" y="360" width="100" height="80" /><bpmndi:BPMNLabel /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_1vpetqu_di" bpmnElement="Event_1vpetqu"><dc:Bounds x="179" y="382" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNEdge id="Flow_0w2j9lb_di" bpmnElement="Flow_0w2j9lb"><di:waypoint x="215" y="177" /><di:waypoint x="270" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1ez6hz2_di" bpmnElement="Flow_1ez6hz2"><di:waypoint x="370" y="177" /><di:waypoint x="430" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1jvovvr_di" bpmnElement="Flow_1jvovvr"><di:waypoint x="530" y="177" /><di:waypoint x="590" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1doeb9v_di" bpmnElement="Flow_1doeb9v"><di:waypoint x="690" y="177" /><di:waypoint x="750" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0v77htt_di" bpmnElement="Flow_0v77htt"><di:waypoint x="850" y="177" /><di:waypoint x="915" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0rx278g_di" bpmnElement="Flow_0rx278g"><di:waypoint x="965" y="177" /><di:waypoint x="1030" y="177" /><bpmndi:BPMNLabel><dc:Bounds x="984" y="159" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0fvhke0_di" bpmnElement="Flow_0fvhke0"><di:waypoint x="1130" y="177" /><di:waypoint x="1200" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_19hca6k_di" bpmnElement="Flow_19hca6k"><di:waypoint x="1300" y="177" /><di:waypoint x="1375" y="177" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0om5nu7_di" bpmnElement="Flow_0om5nu7"><di:waypoint x="1425" y="177" /><di:waypoint x="1500" y="177" /><bpmndi:BPMNLabel><dc:Bounds x="1449" y="159" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1hg2jlc_di" bpmnElement="Flow_1hg2jlc"><di:waypoint x="1550" y="137" /><di:waypoint x="1550" y="70" /><di:waypoint x="1250" y="70" /><di:waypoint x="1250" y="137" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_101go1a_di" bpmnElement="Flow_101go1a"><di:waypoint x="1400" y="202" /><di:waypoint x="1400" y="270" /><di:waypoint x="1550" y="270" /><di:waypoint x="1550" y="360" /><bpmndi:BPMNLabel><dc:Bounds x="1461" y="252" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_02gk645_di" bpmnElement="Flow_02gk645"><di:waypoint x="1500" y="400" /><di:waypoint x="1425" y="400" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0tdyk61_di" bpmnElement="Flow_0tdyk61"><di:waypoint x="1375" y="400" /><di:waypoint x="1300" y="400" /><bpmndi:BPMNLabel><dc:Bounds x="1324" y="382" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_154cjgf_di" bpmnElement="Flow_154cjgf"><di:waypoint x="1400" y="425" /><di:waypoint x="1400" y="490" /><di:waypoint x="1030" y="490" /><di:waypoint x="1030" y="400" /><di:waypoint x="990" y="400" /><bpmndi:BPMNLabel><dc:Bounds x="1201" y="472" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_15aey6d_di" bpmnElement="Flow_15aey6d"><di:waypoint x="890" y="400" /><di:waypoint x="850" y="400" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0cwzv60_di" bpmnElement="Flow_0cwzv60"><di:waypoint x="750" y="400" /><di:waypoint x="690" y="400" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0b4ac3v_di" bpmnElement="Flow_0b4ac3v"><di:waypoint x="590" y="400" /><di:waypoint x="505" y="400" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0dvvwp6_di" bpmnElement="Flow_0dvvwp6"><di:waypoint x="455" y="400" /><di:waypoint x="370" y="400" /><bpmndi:BPMNLabel><dc:Bounds x="399" y="382" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0josnvh_di" bpmnElement="Flow_0josnvh"><di:waypoint x="480" y="425" /><di:waypoint x="480" y="480" /><di:waypoint x="197" y="480" /><di:waypoint x="197" y="418" /><bpmndi:BPMNLabel><dc:Bounds x="325" y="462" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0ivytvj_di" bpmnElement="Flow_0ivytvj"><di:waypoint x="940" y="202" /><di:waypoint x="940" y="290" /><di:waypoint x="197" y="290" /><di:waypoint x="197" y="382" /><bpmndi:BPMNLabel><dc:Bounds x="555" y="272" width="28" height="14" /></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1449zkx_di" bpmnElement="Flow_1449zkx"><di:waypoint x="1250" y="360" /><di:waypoint x="1250" y="310" /><di:waypoint x="1550" y="310" /><di:waypoint x="1550" y="360" /></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</bpmn:definitions>

通过观察xml文件,得出几个重要的结论:

  • process节点中描绘的是图形的相对关系,其中sequenceFlow为连接线,sourceRef和targetRef分别表示连接的两端节点编号;
  • bpmndi:BPMNDiagram节点中描绘的是图形的绝对位置,通过坐标的方式定位图形位置。bpmndi:BPMNShape为节点的描述,bpmndi:BPMNEdge为连接线的描述,可以看出这两者共同组成了计算机中典型数据结构:“图”,shape和edge分别对应图的顶点和边,这种带有各种属性,并且可以随意跳转分支的图,可以视为加权网状图;
  • 节点坐标位置为包围节点的外围矩形(示意图中的虚线框)的左上角位置,如下图所示:
    (1) 矩形节点坐标示意图:
    BPMN矩形节点坐标
    (2) 圆形节点坐标示意图:
    BPMN圆形节点坐标
    (3) 网关节点坐标示意图:
    BPMN网关节点坐标

2、方案

数据从Excel读取之后,每一行都对应一个节点,先根据节点顺序排列,一行最多显示10个节点(节点间距默认66,行间距默认300),依次遍历excel数据,达到10个则换行,设置坐标。
记录两份数据,通过map存储:节点坐标信息、sequenceFlow连接线信息,包括连接两端的source、target。
● 根据xml的结构可以看出,process节点中的基本信息可以比较容易的进行拼接,比较有难度的是bpmndi:BPMNDiagram,因为其中涉及到每个节点的坐标、节点间的连接线,还需要考虑到网关的跳转;
● 先根据节点坐标信息初始化每个节点的位置,并设置到“bpmndi:BPMNShape”中;
● 再根据sequenceFlow连接线信息将各个节点根据关系进行连接。连接时需要区分前后相对位置的关系,需要区分如下几种情况:
(1) source和target在一条水平线上(通过判断y坐标的相对位置确定),细分如下3种情况:
A、source的x小于target的x,且间距为正常的间距,则正常的向右连接即可;
BPMN起止节点从左到右直接连接
B、source的x小于target的x,且间距大于正常的间距(大于或等于2倍正常间距),说明中间间隔了一个节点,则需要从上端绕行,以避免连续太过杂乱。具体绕行轨迹为向上、向右、向下,连接source的顶端与target的顶端;
BPMN起止节点向顶端右上方绕行的连线
C、source的x大于target的x,则需要向前绘制连接线,此时为了考虑图形的可读性,需要分向上、向左、向下绕行绘制连接线, 连接source的顶部和target的顶部。

BPMN起止节点向顶端反向连接线
(2) source和target在一条垂直线上(x坐标居中对齐),根据y的大小,分两种情况处理:
A、source.y < target.y, 直接进行向上或者向下的绘制即可,连接source的底部和target的顶部。
BPMN起止节点从上到下的连接线
B、source.y > target.y,向上绘制连接线,连接source的顶部和target的底部。
BPMN起止节点从下到上的连接线
(3) source和target的x、y都不相同,则需要判断source与target的相对位置,参考第(1)种情况里面的判断规则,有4种相对位置:
A、target在source的右上方,则分别向上、向右、向上连接source的顶部和target的底部
BPMN起止节点向顶端上、右、上连线
B、target在source的右下方,则分别向下、向右、向下连接source底部和target的顶部
BPMN起止节点向下、右、下连线
C、target在source的左上方,则分别向上、向左、向下连接source的顶部和target的底部
BPMN起止节点下上、左、上连线
D、target在source的左下方,则分别向下、向左、向下连接source的底部和target的顶部
BPMN起止节点向下、左、下连线
最后还需要根据连接线的source和target,再次编辑各个节点的incoming和outgoing,最终全部添加到xml节点中,即可完成xml的拼装。
以下几种情况需要记录incoming和outgoing:
(1) source.y与target.y相同,source和target在同一个水平线上

A、source.x > target.x,
B、source.x < target.x 且source.x与target.x相距超过2个标准箭头的长度。

(2) source.y与target.y不相同,即source与target不在同一个水平线上

最终生成的xml文件符合BPMN2.0标准,可以使用相关的客户端工具(Camunda Modeler或者Flowable Modeler)进行编辑和查看。

3、实现

核心的处理逻辑在于节点分布和连线算法的实现,提前将节点和他们的位置分布计算出来,并且针对不同的节点相对位置进行连线的位置的计算,由于是实际工作项目,这里就不贴源代码出来了,理解了思路自己实现代码其实并没有太大难度。
从Excel导入有一些开源框架例如easyexcel(底层依赖poi)可以实现,最终绘制图形也是利用一些dom的开源组件例如dom4j写入xml文件即可。
当然,在具体的实现过程中也经历了很多版本的更新,最初的版本比较凌乱,最后的版本已经非常接近设计原图的样式了。

第一版的图形样式,连线有点乱,留个纪念:
第一个版本的自动生成BPMN流程图
第五版的图形样式,基本上和人工绘制的差不多了:
第五个版本的自动生成BPMN流程图

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pg数据库简单操作

postgers数据库简单操作 查看数据库操作 1.首先可以通过psql --version查看你使用pg数据库的版本 我这里是11.5的版本 使用 psql -U postgres 登入数据库 使用 \l命令查看有哪些数据库 \c 数据库名称 选择你要进入的数据库 \d 可以查看此数据库中有哪些表 选择你要查看的表 …

会计专业毕业论文如何选题?

会计专业的同学要打起精神来了&#xff0c;看完这篇文章你将会获得大丰收。 其实在所有人看来&#xff0c;会计专业并不能学&#xff0c;或许我们大家都觉得会计专业只是算算几个数&#xff0c;到月末了就结账就了事了。 但是会计真的这么简单吗&#xff1f;会计学自然不是现…

计算机与财会论文高职,高职院校会计专业模块化教学论文

高职院校会计专业模块化教学论文 摘要:教学模式的设置在人才培养工作中起着重要作用。我国传统的教学模式主要是采用一本教材一讲到底,然后集中安排模拟实训。这种教学模式存在很多弊病,当前突出的表现是在课程体系的设置上仍存在着重理论、轻实训,重模拟实训、轻顶岗实训的现象…

计算机专业的swot分析图,计算机专业学生个人SWOT分析示例.doc

计算机专业学生个人SWOT分析示例.doc 一、个人背景 基本情况xxx&#xff0c;x&#xff0c;1992年出生&#xff0c;xxxx年x月考入xxx大学xxxx专业。二、内外部环境分析1、内部环境分析Strengths* 生活态度比较积极&#xff0c;善于发现事物和环境积极的一面。* 待人真诚&#xf…

【计算机毕业设计】005学生心理咨询评估系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 使用旧方法对学生心理咨询评估信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在学生心理咨询评估信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间…

难得!微软招测试开发了!

大家好&#xff0c;我是韩老师。 已经成功内推 96 人拿到微软 offer&#xff01; 大家应该有了解&#xff0c;微软研发组的正式员工基本都是招的纯软件开发工程师&#xff0c;很少有招测试相关的&#xff01; 但最近&#xff0c;微软招测试啦&#xff01;欢迎找我内推~ 主要是两…

PowerBI微软认证获得记,并没有获得应该有的快乐

小D周末刚考完DA-100的考试&#xff0c;成绩还算可以的吧。本篇向有这个想法的小伙伴分享一下我的考试初衷、备考和考试过程。总体来说收获是有的&#xff0c;但是花700并没有得到等价的快乐。 花700多考试完了你就得到这么个玩样儿 因 今年由于工作上具有挑战的工作项目不多…

微软研究院分享:计算机专业求职的正确姿势

省时查报告-专业、及时、全面的行研报告库 省时查方案-专业、及时、全面的营销策划方案库 【免费下载】2022年8月份热门报告盘点 【高清在线】2022最高分国产电影&#xff1a;《隐入尘烟》为什么说产品能力是一个人的底层能力&#xff1f; 2021-2022元宇宙报告.pdf 某短视频APP…

系统分析师-论文写作-论软件的系统测试及其应用

系统测试及其应用&#xff0c;侧重点在应用&#xff0c;所以找题干中偏重实践的问题&#xff0c;此题为问题3 问题1要点基本可以通用&#xff1b; 问题2需要用自己的进行总结&#xff1b; 问题3中的具体实施过程重点写&#xff0c;应用效果和哪些地方值得改进或提高可以作为结论…

《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息

成书始末 微软公司 的文化&#xff0c;对于我来说是很适合呼吸的空气。我个人的习惯是&#xff0c;无论做任何事&#xff0c;空气如何是重要的。去年12月初我在北京时&#xff0c;机械工业出版社 的陈冀康 编辑面告我有个机会把《How we test software at Microsoft》 译成简体…

微软测试人员简述

虽然微软解散专门的测试部门已经很久&#xff0c;但是其测试工作有增无减&#xff0c;测试人员在其工程能力中扮演了非常重要的作用&#xff0c;虽然有时候看起来他们不像是传统意义上的测试人员。即&#xff0c;不是那种按照需求文档对产品进行验证性操作的人员&#xff0c;他…

对话FreeRTOS创始人

&#xff3b;译者的话&#xff3d;物联网把FreeRTOS推到了风口浪尖&#xff0c;各家MCU芯片公司的开发板、SDK开发套件都移植上了FreeRTOS。著名的智能手表Pebble OS 的内核使用了FreeRTOS&#xff0c;博通的WICED Wi-Fi SDK也推荐使用FreeRTOS。瑞典嵌入式开发工具Atollic的副…

BreederDAO 创始人团队介绍

BreederDAO 团队概述 在我们努力建立我们的平台和发展我们的社区的同时&#xff0c;我们想重点介绍下从第一天开始就一直在幕后努力工作的全球 BreederDAO 核心团队。我们的核心团队带来了各种技能、背景、文化、观点和行业经验&#xff0c;帮助 BreederDAO 应对我们这一路走来…

英语四级考前冲刺技巧

【听力技巧】 35% 1.听什么就选什么&#xff1a;边听边选&#xff0c;四级听力80%都是视听一致。 2.顺序原则&#xff1a;听力题目和文章的考点一般是前后顺序对应的&#xff0c;所以一定要按顺序听&#xff0c;并且选答案。 3.转折和对比原则&#xff1a;注意听表转折和对比的…

英语四级考试在即,这几个超有用的App赶紧用起来,顺利考过四级

相信大家都知道&#xff0c;12月份的英语四级考试已经不剩几天&#xff0c;不知道大家学习得怎么样了。金泰呢小编将来分享几个英语App神器&#xff0c;助大家顺顺考过四级~ 一、网站类 1.沪江网 这是一个非常有名的英语学习网站&#xff0c;不管你是小白还是有一定英语基础的…

英语四级基础

基础笔记&#xff1a; 学习笔记链接&#xff1a;https://www.bilibili.com/video/BV1JJ411C7vw 一&#xff1a;语法结构&#xff1a; 英语句子必须具备主谓结构&#xff0c;并且主语一定是谓语动作的发出者&#xff0c;如果有宾语&#xff0c;则宾语一定是谓语动作的对象或承…

英语4级技巧

真题链接&#xff1a;https://pan.baidu.com/s/1IZ0MmEpkANUm77ZLDmHs1A?pwd1111 提取码&#xff1a;1111 一、听力技巧 听到什么选什么 听发音 4级 90% 6级 60% 1、开头原则(前3句) 2、顺序原则 3、重复原则 1、单词发音/语音现象&#xff08;连读、弱读、失去爆破&#…

英语四级资料【超全】

【超全】英语四级资料&#xff01; 链接&#xff1a;https://pan.baidu.com/s/1eXbGLj5VmCZp8M0aABZdKQ 密码&#xff1a;8kdr

英语四级单词

// UserScript // name 背单词 // description 边上网边刷英语四级单词 // namespace Xiaomaxin // version 1.31 // author Xiaomaxin // include * // require http://cdn.bootcss.…