bpmn-js 流程设计器 与 flowable/activiti 拓展的可行性研究
bpmn-js 流程设计器 与 flowable/activiti 拓展的可行性研究前因先上效果图涉及技术引用前因最近在准备开源一款流程引擎项目,主要包含 流程设计器 表单设计器 流程引擎,碰见了一个问题 开发过程中 经常需要拓展节点或节点元素,因为bpmn规范可能不满足实际项目需求。记录一下 解决思路。先上效果图涉及技术前端使用 bpmn.js拓展 flowable.json或者 activit
·
前因
最近在准备开源一款流程引擎项目,主要包含 流程设计器 表单设计器 流程引擎,碰见了一个问题 开发过程中 经常需要拓展节点或节点元素,因为bpmn规范可能不满足实际项目需求。记录一下 解决思路。
先上效果图
涉及技术
前端使用 bpmn.js
- 拓展 flowable.json或者 activiti.json,新增我们 拓展的节点及元素。
{
"name": "Flowable",
"uri": "http://flowable.org/bpmn",
"prefix": "flowable",
"xml": {
"tagAlias": "lowerCase"
},
"associations": [],
"types": [
{
// 拓展节点名称
"name": "CustomProperties",
"superClass": [
"Element"
],
"meta": {
// * 表示所有bpmn节点都可继承该属性
"allowedIn": [
"*"
]
},
"properties": [
// 拓展属性
{
"name": "values",
"type": "CustomProperty",
"isMany": true
},
{
"name": "userIdList",
"isAttr": true,
"type": "String"
},
{
"name": "userNameList",
"isAttr": true,
"type": "String"
},
{
"name": "assigneeField",
"isAttr": true,
"type": "String"
},
{
"name": "handlerStrategy",
"isAttr": true,
"type": "String"
},
{
"name": "roleGroupCode",
"isAttr": true,
"type": "String"
},
{
"name": "roleCode",
"isAttr": true,
"type": "String"
},
{
"name": "findUserType",
"isAttr": true,
"type": "String"
},
{
"name": "combineType",
"isAttr": true,
"type": "String"
},
{
"name": "relationNodeId",
"isAttr": true,
"type": "String"
},
{
"name": "actionList",
"isAttr": true,
"type": "String"
},
{
"name": "taskType",
"isAttr": true,
"type": "String"
},
{
"name": "nodeType",
"isAttr": true,
"type": "String"
},
{
"name": "isSequential",
"isAttr": true,
"type": "String"
},
{
"name": "proportion",
"isAttr": true,
"type": "String"
},
{
"name": "expression",
"isAttr": true,
"type": "String"
},
{
"name": "skipExpression",
"isAttr": true,
"type": "String"
},{
"name": "formName",
"isAttr": true,
"type": "String"
},
{
"name": "selectFormKey",
"isAttr": true,
"type": "String"
},
{
"name": "selectPath",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "CustomProperty",
"superClass": [
"Element"
],
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true
},
{
"name": "name",
"type": "String",
"isAttr": true
},
{
"name": "value",
"type": "String",
"isAttr": true
}
]
},
- 通过bpmn.js 新增或者修改对应节点元素XML
// 先判断当前element 是否包含 flowable:CustomProperties ,如果包含 则找出来更新对应属性,如果不存在,则创建后, 在更新整个 extensionElements 即可
createOrUpdateCustomProperties(property, value) {
const that = this
const bpmnModeler = that.bpmnModeler()
const bpmnFactory = bpmnModeler.get('bpmnFactory')
let extensionElements = bpmnHelper.getPropertie(that.element, 'extensionElements')
if (!extensionElements) {
extensionElements = elementHelper.createElement('bpmn:ExtensionElements', null, this.element, bpmnFactory)
}
const length = extensionElements.get('values').length
let customProperties
let customPropertiesIndex = -1
for (let i = 0; i < length; i++) {
if (extensionElements.get('values')[i] && extensionElements.get('values')[i].$type === 'flowable:CustomProperties') {
customProperties = extensionElements.get('values')[i]
customPropertiesIndex = i
}
}
if (!customProperties) {
customProperties = elementHelper.createElement('flowable:CustomProperties', null, this.element, bpmnFactory)
}
const data = {}
data[property] = value
customProperties[property] = value
if (customPropertiesIndex > -1) {
extensionElements.get('values')[customPropertiesIndex] = customProperties
} else {
extensionElements.get('values').push(customProperties)
}
const modeling = bpmnModeler.get('modeling')
// 更新
modeling.updateProperties(this.element, {
extensionElements: extensionElements
})
}
elementHelper 如下:
'use strict'
var ElementHelper = {}
module.exports = ElementHelper
/**
* Creates a new element and set the parent to it
*
* @method ElementHelper#createElement
*
* @param {String} elementType of the new element
* @param {Object} properties of the new element in key-value pairs
* @param {moddle.object} parent of the new element
* @param {BpmnFactory} factory which creates the new element
*
* @returns {djs.model.Base} element which is created
*/
ElementHelper.createElement = function(elementType, properties, parent, factory) {
var element = factory.create(elementType, properties)
element.$parent = parent
return element
}
- 前端将这个xml 文件 传递给后端,后端可以通过如下代码解析:
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
byte[] bytes = processXml.getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader in = null;
try {
in = new InputStreamReader(inputStream, "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(in);
BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xtr);
// 注意 这个 bpmnModel 已经包含了我们刚才定义的属性
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (XMLStreamException e) {
e.printStackTrace()
}
- 后台解析结果,即可拿到对应拓展属性与业务对接
附上后台读取Utils 方法
public static final String CUSTOME_EXTENSIONELEMENT = "customProperties";
/**
* 功能描述: 从 flowElement 获取 指定名称的 拓展元素
*
*
* @param flowElement 元素
* @param extensionElementName 拓展元素名称
* @return : org.flowable.bpmn.model.ExtensionElement
* @author : zhoulin.zhu
* @date : 2020/6/19 18:28
*/
public static ExtensionElement getExtensionElementFromFlowElementByName(FlowElement flowElement, String extensionElementName) {
if (flowElement == null) {
return null;
}
if (StringUtils.isEmpty(extensionElementName)) {
extensionElementName = CUSTOME_EXTENSIONELEMENT;
}
Map<String, List<ExtensionElement>> extensionElements = flowElement.getExtensionElements();
for (Map.Entry<String, List<ExtensionElement>> stringEntry : extensionElements.entrySet()) {
if (stringEntry.getKey().equals(extensionElementName)) {
for (ExtensionElement extensionElement : stringEntry.getValue()) {
if (extensionElement.getName().equals(extensionElementName)) {
return extensionElement;
}
}
}
}
return null;
}
/**
* 功能描述: 从拓展元素 获取 拓展 属性值
/**
* 功能描述: 从拓展元素 获取 拓展 属性值
*
*
* @param extensionElement 拓展元素
* @param attributesName 属性名称
* @return : java.lang.String
* @author : zhoulin.zhu
* @date : 2020/6/19 18:30
*/
public static String getAttributesFromExtensionElementByName(ExtensionElement extensionElement, String attributesName) {
if (extensionElement == null
|| StringUtils.isEmpty(attributesName)) {
return null;
}
Map<String, List<ExtensionAttribute>> stringListMap = extensionElement.getAttributes();
for (Map.Entry<String, List<ExtensionAttribute>> listEntry : stringListMap.entrySet()) {
if (listEntry.getKey().equals(attributesName)) {
return listEntry.getValue() != null && listEntry.getValue().size() > 0 ? listEntry.getValue().get(0).getValue() : null;
}
}
return null;
}
源代码地址
引用
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献2条内容
所有评论(0)