activiti 中文文档_使用bpmnjs实现activiti的流程设计器

Hello 大家好,我是易样(容易不一样,我们不一样,一天一个样)。

好久没更新文章了,没更新文章的这些时间我都在闭关修炼,努力提升自身技术,毕竟我2020年的flag是成为大牛

今天给大家带来的这篇文章是整理我使用bpmn-js实现activiti流程设计器的经验之谈,bpmn-js的中文文档不多,很多人都不如何入手开发,并且bpmn-js的后端使用的是Camunda,如何使用activiti也是困扰了很多开发。

不要怕,让小姐姐来教教你。

问题

需要前后端分离、觉得activiti的设计器不要用、使用bpmn-js实现设计器,但后端用的是activiti,xml不兼容怎么办?

解决

不止我这一种解决方法,我这里只提供我的解决方法。

1 使用Bpmn-js开发设计器

关于bpmn-js如何使用建议搭建去github上面搜索,这里贴上官网地址:https://github.com/bpmn-io/bpmn-js

官网案例地址:https://github.com/bpmn-io/bpmn-js-examples

笔者开发设计器时参考了霖呆呆的关于bpmn-js从0开发的一系列文章,地址:https://juejin.im/post/5def372af265da33c84a4818

相信大家看完以上我贴的文章,对bpmn-js已经很熟悉了;接下来我来解释一下我的项目:

  • 环境:windows10
  • 开发工具:vscode、IDEA
  • 技术:vue、springboot、element-ui

1.1 自定义右边属性面板

如图,是我完全自定义的属性面板

部分代码如下:

  
">

我是通过propsComponent属性的变化来显示不同事件的属性,比如用户任务的属性、网关的属性

propsComponent属性是通过监听modeler、element来改变值的,代码如下:

addModelerListener() {
              // 监听 modeler        const bpmnjs = this.bpmnModeler        const that = this        // 'shape.removed', 'connect.end', 'connect.move'        const events = ['shape.added', 'shape.move.end', 'shape.removed']        events.forEach(function(event) {
                that.bpmnModeler.on(event, e => {
                  var elementRegistry = bpmnjs.get('elementRegistry')            var shape = e.element ? elementRegistry.get(e.element.id) : e.shape            // console.log(shape)            if (event === 'shape.added') {
                    console.log('新增了shape');              // 展示新增图形的属性              that.key = e.element.id.replace('_label', '');              that.propsComponent = bpmnHelper.getComponentByEleType(shape.type);              that.element = e.element;                          } else if (event === 'shape.move.end') {
                    console.log('移动了shape')              // 展示新增图形的属性              that.key = shape.id;              that.propsComponent = bpmnHelper.getComponentByEleType(shape.type);              that.element = e.shape;            } else if (event === 'shape.removed') {
                    console.log('删除了shape')              // 展示默认的属性              that.propsComponent = 'CommonProps'            }          })        })      },      addEventBusListener() {
              // 监听 element        let that = this        const eventBus = this.bpmnModeler.get('eventBus')        const eventTypes = ['element.click', 'element.changed', 'selection.changed']        eventTypes.forEach(function(eventType) {
                eventBus.on(eventType, function(e) {
                  if (eventType === 'element.changed') {
                    that.elementChanged(e)            } else if (eventType === 'element.click') {
                    console.log('点击了element');              if (!e || e.element.type == 'bpmn:Process') {
                      that.key = '1';                that.propsComponent = 'CommonProps'                that.element = e.element;              } else {
                      // 展示新增图形的属性                that.key = e.element.id;                that.propsComponent = bpmnHelper.getComponentByEleType(e.element.type);                that.element = e.element;              }                          }          })        })      },

由于vue的特殊性,在使用属性组件前,还需要引入组件

components: {
          CommonProps,    ProcessProps,    StartEventProps,    EndEventProps,    IntermediateThrowEventProps,    ExclusiveGatewayProps,    ParallelGatewayProps,    InclusiveGatewayProps,    UserTaskProps,    SequenceFlowProps,    CallActivityProps  },

接下来就是实现各个事件属性的页面了。

完整代码见github:

我特意为你们单独抽离的demo,不要辜负我的良苦用心呀

1.2 适配activiti

由于bpmn-js官方是适配camunda的,所以对activiti存在不兼容的地方,为了让bpmn-js能使用activiti,我们需要在BpmnModeler中扩展activiti代码如下:

import activitiModdleDescriptor from '../js/activiti.json';
this.bpmnModeler = new BpmnModeler({
                container: canvas,          //添加属性面板,添加翻译模块          additionalModules: [              customTranslateModule,              customControlsModule            ],          //模块拓展,拓展activiti的描述          moddleExtensions: {
                    activiti: activitiModdleDescriptor          }        });

关于activiti.json文件,我建议你看自定义元模型示例

部分内容如下:

{
        "name": "Activiti",  "uri": "http://activiti.org/bpmn",  "prefix": "activiti",  "xml": {
          "tagAlias": "lowerCase"  },  "associations": [],  "types": [    {
            "name": "Definitions",      "isAbstract": true,      "extends": [        "bpmn:Definitions"      ],      "properties": [        {
                "name": "diagramRelationId",          "isAttr": true,          "type": "String"        }      ]    }  ],  "emumerations": [ ]}

注意:uri、prefix

1.3 xml

我项目设计器设计的流程xml如下:

                                                                                                                                                                                                                                                                                                                                

节点里的属性大部分都是我自定义的属性

此处省略两千字,由于作者没时间并且已再其他博文中发布且反响很好;请阅读者点击阅读原文阅读,感谢理解。

2 后端activiti实现

具体怎么搭建activiti环境,相信大家都能百度到,我只介绍怎么将bpmn-js和activiti兼容

3.1 解析BPMN文件

如图,展示了一个XML格式的流程文件如何经过几个大的步骤部署到引擎的过程

3.2 先由前端传xml保存到后端开始

http请求将携带主要的两个参数,bpmn_xml和svg_xml

由于activiti保存在数据库中的是json文件,所以我们需要将bpmn_xml文件转换成json

activiti官方提供的转换方法并不能满足我,我自定义了转换方法和解析器,activiti官方也允许你自定义解析器

先上方法:

public static JsonNode converterXmlToJson(String bpmnXml) {
              // 创建转换对象        BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();        // XMLStreamReader读取XML资源        XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();        StringReader stringReader = new StringReader(bpmnXml);        XMLStreamReader xmlStreamReader = null;        try {
                  xmlStreamReader = xmlInputFactory.createXMLStreamReader(stringReader);        } catch (XMLStreamException e) {
                  e.printStackTrace();        }        // UserTaskXMLConverter类是我自定义的        BpmnXMLConverter.addConverter(new UserTaskXMLConverter());        // 把xml转换成BpmnModel对象        BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xmlStreamReader);        // BpmnJsonConverter类是我自定义的        // 创建转换对象        BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();        // 把BpmnModel对象转换成json        JsonNode jsonNodes = bpmnJsonConverter.convertToJson(bpmnModel);        // 返回的json会被保存到数据库中        return jsonNodes;    }

以上代码使用了Activiti的activiti-bpmn-converter模块提供的BpmnModel对象与XML的互转功能,通过创建org.activiti.bpmn.converter.BpmnXMLConverter类对象调用相应的方法即可实现BpmnModel对象与XML之间的转换操作。

首先,自定义类UserTaskXMLConverter是因为我的用户任务事件中有自定义的属性;在将xml转为BpmnModel时,如果是用户任务事件就会走我自定义的UserTaskXMLConverter类

相关代码见github:

然后是将BpmnModel转为json,注意每个bpmnModel.attributes下存方着所有属性

3.3 自定义的BpmnJsonConverter文件

Activiti提供的activiti-json-converter模块中提供了BpmnJsonConverter类,我们对比一下我自定义的和官方的

发现,我们自定义的类中的static中有几个Custom开头的类,见名知义,这些类是关于用户任务、流程、网关的转换类。

问:为何要自定义这些类呢?

答:

1. 因为前端自定义属性(例如:多实例属性、默认流程属性)使用官方的toBpmnModel转换是会丢失自定义属性的,我们自定义类主要是将自定义属性放在attribute中,并且转换多实例属性为Activiti的BPMN规范接受。2. convertElementToJson时加上自定义的属性键值

用户任务自定义属性转换相关代码:

// 多实例类型String multiInstanceType = getPropertyValueAsString(PROPERTY_MULTIINSTANCE_TYPE, elementNode);// 通过权重String multiInstanceCondition = getPropertyValueAsString(PROPERTY_MULTIINSTANCE_CONDITION, elementNode);if (StringUtils.isNotEmpty(multiInstanceType) && !"none".equalsIgnoreCase(multiInstanceType)) {
          String name = getPropertyValueAsString(PROPERTY_NAME, elementNode);    MultiInstanceLoopCharacteristics multiInstanceObject = new MultiInstanceLoopCharacteristics();    if ("sequential".equalsIgnoreCase(multiInstanceType))     {
              multiInstanceObject.setSequential(true);    } else {
              multiInstanceObject.setSequential(false);    }    if (StringUtils.isNotEmpty(multiInstanceCondition)) {
              try {
                  Integer.valueOf(multiInstanceCondition);        } catch (Exception ex) {
                  throw new WorkflowApiException(name + "配置成了会签,但通过权重不是一个整数");        }        multiInstanceObject.setCompletionCondition("${nextTaskEvaluator.isComplete(execution," + multiInstanceCondition + ")}");    } else {
              throw new WorkflowApiException(name + "配置成了会签,但没有配置通过权重");    }}

3.4 Bpmn解析处理器

Activiti支持在解析BPMN资源文件时允许自定义BPMN解析处理器(BpmnParseHandler)参与,可以在开始解析一个元素(Element)或解析完之后调用自定义的BPMN解析处理器,在自定义的解析处理器中,我们可以更改一些BPMN对象的属性。

添加BPMN解析处理器可以在Activiti引擎配置文件中配置属性“preBpmnParseHandlers”和“postBpmnParseHandlers”。下面的代码针对Pre(前置)和Post(后置)类型分别添加了一个解析处理器

上面的代码添加了两种类型的BPMN解析处理器,之所以区分类型是为了更细致地划分处理器类型;Pre类型处理器是总是排在第一位执行,也就是在所有流程文件中定义地元素之前,而Post类型的处理器被放在最后执行,也就是所有流程文件中定义的而元素之后。如果解析处理器有特定的顺序要求,就可以用Pre和Post类型来区分。

小结

总体来说,完整开发下来还是比较费力,需要你对bpmn-js以及activiti有一定的了解并且有一定的耐心。

bpmn-js和activiti也是我慢慢啃下来的,如果感觉文章对你有帮助点关注、点赞、赞赏、关注公众号都不嫌弃。


啦啦啦~~ ,写完了写完了,我又是一个开心的小仙女了。

你可能感兴趣的:(activiti,中文文档,activiti,文档,activiti7流程设计器,activiti中文文档,activiti官方文档)