记录一下对flowable流程引擎创建自定义下拉框属性步骤方法

目录

  • 说明
  • 添加自定义属性
    • 思路
    • 步骤
      • 找到 flowable的静态画图页面文件
      • 找到bpmnjson的数据文件
      • 修改找到bpmnjson的数据
      • 添加自定义下拉框属性类型
      • 添加自定义属性转换器
      • 获取自定义属性值

说明

Flowable 一款流行的bpmn2流程引擎,里面包含了绘制bpmn2的流程图工具。本次flowable 版本为 6.4.2 , 6.5开始flowable走商用路线了。6.4.2基本上满足了常用流程引擎的功能。

在绘制用户任务流程图的时候,除了bpmn2规定的一些规范属性值外,我们希望对流程属性有一些自定义的属性信息,例如这个流程是专门用于提交表单的还是用于审批的,这个流程是否可以被终止等等,虽然客户采用各种排他网关或并行网关解决,但流程图必会很复杂。

例如下图中的 【任务类型】 【可被撤销】【可终止流程】属性,就是自定义属性。

可以想象一些,如果没有自定义属性,所有的步骤都可能具备 终止,撤销操作,那么如果全部用网关来画,这个图一定成了蛛网形状了。

添加自定义属性

思路

可能你会不断百度或者其他搜索引擎查询相关博客
可能你会自告奋勇阅读源码
可能你会…

以上不管你怎么弄,其实都需要你花费一定的精力和毅力去做,中国的博客目前为止,至少关于flowable 的系统的、完整的、流畅的解决你的困扰,还很少。
因此,最后还得靠自己的思维方法解决自己的业务场景!

步骤

要在flowable 原有的画图网页上,添加自定义页面,

找到 flowable的静态画图页面文件

首先你的找到 flowable的静态画图页面, 下载源码,找画bpmn2的工程文件。。。 总之最后的工程文件路径是如下面截图所示:

如果你觉得很烦,或者你不想自己再搞一个画图工程,从flowable源码中copy页面文件,copy一系列springBoot 的依赖工程 等等,你可以下载下面的已经处理过的,免登录flowable-modeler工程:

https://gitee.com/banana6/flowable-bpmn2-modeler

当然其他人分享的,都大同小异,毕竟都是copy源代码修改的。

找到bpmnjson的数据文件

找到 stencilset_bpmn_*.json 文件,
我的这里有中英文国际化,因此有两个,并且放在了resources目录下,通过代码自定义访问加载。
不管什么加载方式,文件名称叫什么,文件的内容都一样,前端请求你给它一样的内容即可。

修改找到bpmnjson的数据

修改位置在开头json节点属性 propertyPackages中:


{"title" : "Process editor","namespace" : "http://b3mn.org/stencilset/bpmn2.0#","description" : "BPMN process editor","propertyPackages" : [ {"name" : "process_idpackage","properties" : [ {"id" : "process_id","type" : "String","title" : "流程标识","value" : "process","description" : "定义流程的唯一标识符.","popular" : true} ]},....  这里面添加  .....

里面添加属性内容:

{"name" : "revokeflagpackage","properties" : [ {"id" : "revokeflag","type" : "Boolean","title" : "可被撤销","value" : "false","description" : "定义该步骤提交后是否可以撤销.","popular" : true} ]},{"name" : "endflagpackage","properties" : [ {"id" : "endflag","type" : "Boolean","title" : "可终止流程","value" : "false","description" : "定义该步骤提交后是否可以撤销.","popular" : true} ]}, {"name" : "nodetypepackage","properties" : [ {"id" : "nodetype","type" : "nodetype","title" : "任务类型","value" : "commit","description" : "节点分为普通节点和审核节点.","popular" : true} ]}

关联到 userTask 节点, 就在刚才配置文件的底端 id = “UserTask” 节点,修改propertyPackages 属性值,将上面的3个自定义的属性id加入其中:

{"type" : "node","id" : "UserTask","title" : "用户任务",......"propertyPackages" : [...... , "nodetypepackage", "revokeflagpackage","endflagpackage", ...... ],......}

英文配置文件同步修改,当然如果你没有英文环境,则可以不修改。

添加自定义下拉框属性类型

目前flowable 画图页面上属性中,有各种数据类型,文本类型,布尔类型、还有其他复合类型, 可以说每一个属性除了 文本、布尔、数字等基础类型外其他的每一个属性都对应一个数据类型(一系列 html、js文件)。

以上的这些内置的属性类型配置信息,可查看 配置文件 properties.js 得知:

我们上面的 自定义【任务类型】属性类型就是下拉框,里面包含两个值“commit” 和 “audit” ,这里找了很久,通过比对 【多实例类型】 属性相关配置文件,进行添加修改相应文件,步骤如下:

  1. 在 properties.js 中添加如下属性:
    "nodetype" : {"readModeTemplateUrl": "editor-app/configuration/properties/default-value-display-template.html","writeModeTemplateUrl": "editor-app/configuration/properties/nodetype-property-write-template.html"},

看到 上面的 “nodetype” 了吗,和 之前在 bpmnjson 添加的内容中的 “type” : “nodetype” 对应上,名称保持一致。

  1. 创建对应的html 和 js文件

就是上面 添加的 nodetype-property-write-template.html 文件,路径在
static/modeler/editor-app/configuration/properties/

内容如下:

<div ng-controller="FlowableNodeTypeCtrl"><select ng-model="property.value" ng-change="nodeTypeChanged()"><option>commit</option><option>audit</option></select>
</div>

然后继续创建 js 文件:properties-nodetype-controller.js ,路径在
static/modeler/editor-app/configuration/

angular.module('flowableModeler').controller('FlowableNodeTypeCtrl', [ '$scope', function($scope) {if ($scope.property.value === undefined){$scope.property.value = 'commit';}$scope.nodeTypeChanged = function() {$scope.updatePropertyInModel($scope.property);};
}]);

其中 html中的 <div ng-controller=“FlowableNodeTypeCtrl”> 和 js 中的 .controller(‘FlowableNodeTypeCtrl’, 对应上即可,这里都是 angularJS语法,没学过也无妨,照抄多实例应用里面的属性文件即可。

  1. 最后在首页 index.html中加载js文件
<script src="editor-app/configuration/properties-nodetype-controller.js" type="text/javascript"></script>

添加自定义属性转换器

到目前为止,静态页面上的工作处理完毕,接下来就是后端java代码处理,

指导思想:

  • 页面上的属性 -----导出--------》 bpmn2 XML 文件
  • bpmn2 XML 文件 ------导入-------》 页面上的属性
  • 页面上的属性 -----保存--------》 Flowable java 对象 -----持久化—》 数据库表
  • 数据库表 -----查询--------》 Flowable java 对象 -----转换—》 页面上的属性

对于flowable 这种流程引擎架构,上面所列出的一系列数据格式的转换必定会有一个转换器角色的功能类来实现 !

而且 转换器角色的功能类 肯定不止一个, 必定都实现或者由哪一个控制类统一管控, flowable用的最多的应该是 用户任务,UserTask,转换器的名称一般编程中都喜欢用 Converter 来表示, 因此:

使用IDEA 强大的搜索功能来找一找: UserTaskConverter , 还真有!

org.flowable.editor.language.json.converter.UserTaskJsonConverter


当然一般 你可能是 根据前端 F12 查看,到底调用哪一个接口,然在顺藤摸瓜进行一番debug查找,最后一定能找到 UserTaskJsonConverter。

创建自定义属性转换器:
这里省略了一系列 debug的痛苦过程,直接说结果:

继承 UserTaskJsonConverter 创建名称为 CustomizeUserTaskJsonConverter 的转换器类。

package com.xxx.bbb.aaa.convert;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.middol.flowable.modeler.utils.ExtensionAttributeUtils;
import org.flowable.bpmn.model.*;
import org.flowable.editor.language.json.converter.ActivityProcessor;
import org.flowable.editor.language.json.converter.BaseBpmnJsonConverter;
import org.flowable.editor.language.json.converter.UserTaskJsonConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;import java.util.Collections;
import java.util.List;
import java.util.Map;/*** 新建自定义userTaskjson解析器CustomizeUserTaskJsonConverter** @author guzt*/
public class CustomizeUserTaskJsonConverter extends UserTaskJsonConverter {private static final Logger LOGGER = LoggerFactory.getLogger(CustomizeUserTaskJsonConverter.class);private static final String NODE_TYPE_KEY = "nodetype";private static final String REVOKE_FLAG_KEY = "revokeflag";private static final String END_FLAG_KEY = "endflag";/*** 覆盖原 UserTaskJsonConverter 中的 fillTypes 方法** @param convertersToBpmnMap map* @param convertersToJsonMap map* @see org.flowable.editor.language.json.converter.BpmnJsonConverter  中的静态代码块*/public static void fillTypes(Map<String, Class<? extends BaseBpmnJsonConverter>> convertersToBpmnMap,Map<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>> convertersToJsonMap) {fillJsonTypes(convertersToBpmnMap);fillBpmnTypes(convertersToJsonMap);}/*** 覆盖原 UserTaskJsonConverter 中的 fillBpmnTypes 方法** @param convertersToJsonMap map* @see org.flowable.editor.language.json.converter.BpmnJsonConverter  中的静态代码块*/public static void fillBpmnTypes(Map<Class<? extends BaseElement>, Class<? extends BaseBpmnJsonConverter>> convertersToJsonMap) {convertersToJsonMap.put(UserTask.class, CustomizeUserTaskJsonConverter.class);}/*** 覆盖原 UserTaskJsonConverter 中的 fillJsonTypes 方法** @param convertersToBpmnMap map* @see org.flowable.editor.language.json.converter.BpmnJsonConverter  中的静态代码块*/public static void fillJsonTypes(Map<String, Class<? extends BaseBpmnJsonConverter>> convertersToBpmnMap) {convertersToBpmnMap.put(STENCIL_TASK_USER, CustomizeUserTaskJsonConverter.class);}// 主要用于 外部 xml 导入到 flowable画图页面中@Overridepublic void convertToJson(BaseElement baseElement, ActivityProcessor processor, BpmnModel model, FlowElementsContainer container, ArrayNode shapesArrayNode, double subProcessX, double subProcessY) {super.convertToJson(baseElement, processor, model, container, shapesArrayNode, subProcessX, subProcessY);if (baseElement instanceof UserTask) {LOGGER.debug("userTaskId = {} 扩展属性 = {}", baseElement.getId(), baseElement.getAttributes());Map<String, List<ExtensionAttribute>> stringListMap = baseElement.getAttributes();String nodetype = stringListMap.get(NODE_TYPE_KEY).get(0).getValue();String revokeflag = stringListMap.get(REVOKE_FLAG_KEY).get(0).getValue();String endflag = stringListMap.get(END_FLAG_KEY).get(0).getValue();shapesArrayNode.forEach(node -> {if (baseElement.getId().equals(node.get("resourceId").textValue())) {ObjectNode properties = (ObjectNode) node.get("properties");properties.set(NODE_TYPE_KEY, new TextNode(nodetype));properties.set(REVOKE_FLAG_KEY, BooleanNode.valueOf(Boolean.parseBoolean(revokeflag)));properties.set(END_FLAG_KEY, BooleanNode.valueOf(Boolean.parseBoolean(endflag)));}});}}// 主要用于 页面属性加载和导出 xml使用@Overrideprotected FlowElement convertJsonToElement(JsonNode elementNode, JsonNode modelNode,Map<String, JsonNode> shapeMap) {FlowElement flowElement = super.convertJsonToElement(elementNode, modelNode, shapeMap);if (flowElement instanceof UserTask) {LOGGER.debug("进入自定义属性解析CustomizeUserTaskJsonConverter...");Map<String, List<ExtensionAttribute>> attributes = flowElement.getAttributes();String nodetype = getPropertyValueAsString(NODE_TYPE_KEY, elementNode);if (StringUtils.isEmpty(nodetype)) {LOGGER.debug("nodetype 属性为空,设置为默认值");nodetype = "commit";}attributes.put(NODE_TYPE_KEY, Collections.singletonList(ExtensionAttributeUtils.generate(NODE_TYPE_KEY, nodetype)));String revokeflag = getPropertyValueAsString(REVOKE_FLAG_KEY, elementNode);if (StringUtils.isEmpty(revokeflag)) {LOGGER.debug("revokeflag 属性为空,设置为默认值");revokeflag = "false";}attributes.put(REVOKE_FLAG_KEY, Collections.singletonList(ExtensionAttributeUtils.generate(REVOKE_FLAG_KEY, revokeflag)));String endflag = getPropertyValueAsString(END_FLAG_KEY, elementNode);if (StringUtils.isEmpty(endflag)) {LOGGER.debug("endflag 属性为空,设置为默认值");endflag = "false";}attributes.put(END_FLAG_KEY, Collections.singletonList(ExtensionAttributeUtils.generate(END_FLAG_KEY, endflag)));LOGGER.debug("自定义属性解析CustomizeUserTaskJsonConverter 完成");LOGGER.debug("当前 attributes 为 {}", attributes);}return flowElement;}}

看到里面的 三个属性了吗,就是一开始我们在js文件中添加的3个自定义属性的 id值

    private static final String NODE_TYPE_KEY = "nodetype";private static final String REVOKE_FLAG_KEY = "revokeflag";private static final String END_FLAG_KEY = "endflag";

生效自定义属性转换器:
让 CustomizeUserTaskJsonConverter 在运行期间替换 UserTaskJsonConverter 还需要做如下事情:

创建一个 spring Bean 名称叫什么无所谓,暂且为 CustomBpmnJsonConverter ,必须 继承 BpmnJsonConverter,
为什么要继承 BpmnJsonConverter ? 因为没办法,convertersToBpmnMap convertersToJsonMap 是 protected 类型,而正是这两个Map 来存储当解析用户任务节点时用哪一个转换器。


package com.xxx.bbb.aaa.convert;import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.springframework.stereotype.Component;/**
* 目的是将BpmnJsonConverter中的 UserTaskJsonConverter 设置替换成 CustomizeUserTaskJsonConverter。
* 为什么要继承 BpmnJsonConverter ? 因为没办法,convertersToBpmnMap  convertersToJsonMap 是 protected 类型
*
* @author guzt
* @see org.flowable.editor.language.json.converter.BpmnJsonConverter  中的静态代码块
*/
@Component
public class CustomBpmnJsonConverter extends BpmnJsonConverter {static {CustomizeUserTaskJsonConverter.fillTypes(convertersToBpmnMap, convertersToJsonMap);}
}

上面用到的工具类 ExtensionAttributeUtils 代码如下:

package com.xxx.bbb.aaa.convert;import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;/*** @author guzt*/
public class ExtensionAttributeUtils {public static ExtensionAttribute generate(String key, String val) {ExtensionAttribute ea = new ExtensionAttribute();ea.setNamespace(BpmnJsonConverter.MODELER_NAMESPACE);ea.setName(key);ea.setNamespacePrefix("custom");ea.setValue(val);return ea;}}

自此,准备工作全部结束,然后编译项目,重新启动看看,是否有3个自定义属性,而且可以导入,而且可以将外部的xml 文件导入进去也能解析出自定义属性值。

获取自定义属性值

我们调用 flowable 中查询 userTask 接口获得 用户任务列表时 或者 查询流程定义的属性信息时,总之是 org.flowable.bpmn.model.UserTask 类或其子类。

UserTask 可以 通过调用 getAttributes(); 方法获得自定义属性值。

    protected static final String NODE_TYPE_KEY = "nodetype";protected static final String REVOKE_FLAG_KEY = "revokeflag";protected static final String END_FLAG_KEY = "endflag";Map<String, List<ExtensionAttribute>> attributes = task.getAttributes();List<ExtensionAttribute> att1 = attributes.get(NODE_TYPE_KEY);if (CollectionUtil.isNotEmpty(att1)) {System.out.println("nodetype 的值为:" + att1.get(0).getValue()); }List<ExtensionAttribute> att2 = attributes.get(REVOKE_FLAG_KEY);if (CollectionUtil.isNotEmpty(att1)) {System.out.println("revokeflag的值为:" + att1.get(0).getValue()); }List<ExtensionAttribute> att3 = attributes.get(END_FLAG_KEY);if (CollectionUtil.isNotEmpty(att1)) {System.out.println("endflag的值为:" + att1.get(0).getValue()); }

Over , Thanks 如有不对地方请指正

flowable-bpmn2添加自定义节点属性相关推荐

  1. flowable设计器节点属性扩展_Flowable-流程定义扩展属性

    上次讲述了任务节点的扩展属性,这次讲解一下流程的扩展属性 前端 1 stencilset_bpmn.json 中增加属性 { "name" : "process_devi ...

  2. 驰骋工作流引擎的流程属性-节点属性-前台操作

    1:    工作流引擎功能-流程属性 •    支持客户参与流程:比如在一个erp系统里供应商相对企业来说是外部用户,在一个学校系统里,教师是内部用户,学生是外部用户.ccflow支持外部用户登录参与 ...

  3. DOM(Document object madle) 文档对象模型: 元素节点 文本节点 属性节点

    [DOM树节点] DOM 节点分为三大类:元素节点 文本节点 属性节点 文本节点 属性节点 为元素节点的两个子节点 通过getElement系列方法,可以去到元素节点. [查看节点] 1.docume ...

  4. 【Groovy】自定义 Xml 生成器 BuilderSupport ( 构造 Xml 节点类 | 封装节点名称、节点值、节点属性、子节点 | 将封装的节点数据转为 Xml 字符串 )

    文章目录 一.构造 Xml 节点类 1.封装节点名称.节点值.节点属性.子节点 2.将封装的节点数据转为 Xml 字符串 二.Xml 节点类完整代码 一.构造 Xml 节点类 生成 Xml 数据前 , ...

  5. 【Groovy】自定义 Xml 生成器 BuilderSupport ( 继承 BuilderSupport 抽象类 | 在 createNode 方法中获取节点名称、节点属性、节点值信息 )

    文章目录 一.继承 BuilderSupport 抽象类 二.在 createNode 方法中获取节点名称.节点属性.节点值信息 三.完整代码示例 1.MyBuilderSupport 生成器代码 2 ...

  6. 【Groovy】Xml 反序列化 ( 使用 XmlParser 解析 Xml 文件 | 获取 Xml 文件中的节点和属性 | 获取 Xml 文件中的节点属性 )

    文章目录 一.创建 XmlParser 解析器 二.获取 Xml 文件中的节点 三.获取 Xml 文件中的节点属性 四.完整代码示例 一.创建 XmlParser 解析器 创建 XmlParser 解 ...

  7. 【Groovy】编译时元编程 ( AST 语法树分析 | ClassNode 根节点 | 方法 Methods 节点 | 字段 Fields 节点 | 属性 Properties 节点 )

    文章目录 一.AST 语法树分析 一.AST 语法树分析 在上一篇博客 [Groovy]编译时元编程 ( 编译时元编程引入 | 声明需要编译时处理的类 | 分析 Groovy 类的 AST 语法树 ) ...

  8. 元素节点、属性节点、文本节点 的节点属性

    1 .nodeName 元素节点        元素标记名/节点名称, 相当于tagName .属性节点       属性节点返回属性名, 文本节点          文本节点返回#text.node ...

  9. 转:ECharts图表组件之简单关系图:如何轻松实现另类站点地图且扩展节点属性实现点击节点页面跳转...

    站点地图不外乎就是罗列一个网站的层次结构,提炼地讲就是一个关系结构图.那么我们如何巧用ECharts图表组件内的简单关系结构图来实现一个站点的地图结构呢?另外如何点击某个节点的时候实现页面跳转呢? 针 ...

最新文章

  1. Deep Learning 学习随记(三)续 Softmax regression练习
  2. “去中心化”为何意义重大?
  3. 精通python设计模式-浅谈Python设计模式 - 原型模式
  4. 将图卷积神经网络用于解码分子生成
  5. 求1-100之间的奇数和、偶数和
  6. go 分段锁ConcurrentMap,map+读写锁,sync.map的效率测试
  7. 自定义 URL Scheme 完全指南
  8. java帐篷_Java多线程之 Park和Unpark(十四)
  9. Spring项目启动加载xml配置文件替换数据库提高响应速度
  10. 什么是计算机游戏技术,dlss技术是什么意思有什么用?目前支持dlss的游戏有哪些?...
  11. GCC、VS对C++标准的支持情况总结(转载)
  12. HyperLedger Composer 查看所有容器 | 进入指定容器
  13. 185.部门工资前三高的员工
  14. 硕士转计算机科学,普利茅斯大学计算机科学(转专业)理学硕士研究生申请要求及申请材料要求清单...
  15. xss.haozi练习通关详解
  16. jca 实例 java_jca工具分析was的javacore实例解析
  17. 已成功与服务器建立连接,但是在登录过程中发生错误。
  18. 新浪和腾讯微博教程(一)
  19. osgEarth测高程方法
  20. 麦芽糖-刀豆球蛋白A,maltose-ConcanavalinA,刀豆球蛋白A-PEG-麦芽糖

热门文章

  1. /var/ha/soc/hagsdsocket./cdrom/rootpre.sh[338]: /usr/lpp/ssp/bin/spget_syspar
  2. 主流的B/S架构模式在软考教程里居然是被一笔带过的。
  3. ajax里的append,使用AJAX源和appendTo理解和实现jQuery自动完成
  4. 禁止Altium designer(其他软件同样适用)联网的配置操作
  5. OPT机器视觉12月高峰论坛一览表
  6. 《2022 大数据技术与架构视频合集》附PPT和文档,限时下载
  7. 前端web:响应式Web开发优缺点总结
  8. 2022年房地产开发行业研究报告
  9. fio模拟mysql写入速度_fio 测试工具
  10. Hadoop国内镜像下载地址:极速