目录
一、前言
前面所有的例子都是先基于Activiti-app在线绘制流程图,然后导出为xml文件,最后作为外部资源被部署到数据库的,虽然该种方式功能很强大但是不太好集成,有些现有的在线流程编辑器也实在是过于复杂和丑陋,用户并不需要配置那么多东西。
因此将流程的设计过程交给用户时,用户肯定需要一个美观、简洁的流程设计器,那这玩意就由各公司自己寻找合适的开源工具或者自己开发一个前端流程图组件,我们公司之前便自己开发了一个简便的流程图组件,那么整个流程图的数据格式该怎么定义呢?定义好后该怎么部署到Activiti呢?
二、整合
在整合之前我们肯定需要先拿到json数据,而前端透传过来的流程图数据结构可以是自定义的带上各种各样的业务信息的,只是在转换到Activiti这一层时需要将我们自定义的json数据转化成Activiti所认识的数据,Activiti的流程图保存数据格式我们其实可以拿到,就是在Activiti-app保存时按下f12查看保存请求即可,哈哈哈哈哈是不是很简单?大体思路是不是有了?下面给出一个超级简单流程图以及对应的json数据(为了省空间json数据可自行格式):
{"resourceId":"5c5040fe-b196-433d-b34e-3d01cd6c85dc","properties":{"process_id":"simpleModel","name":"简单流程","documentation":"","process_author":"","process_version":"","process_namespace":"http://www.activiti.org/processdef","executionlisteners":"","eventlisteners":"","signaldefinitions":"","messagedefinitions":""},"stencil":{"id":"BPMNDiagram"},"childShapes":[{"resourceId":"startEvent1","properties":{"overrideid":"","name":"","documentation":"","executionlisteners":"","initiator":"","formkeydefinition":"","formreference":"","formproperties":""},"stencil":{"id":"StartNoneEvent"},"childShapes":[],"outgoing":[{"resourceId":"sid-1007C45C-AB28-4EA8-87C9-0E35B6FFA141"}],"bounds":{"lowerRight":{"x":130,"y":193},"upperLeft":{"x":100,"y":163}},"dockers":[]},{"resourceId":"sid-584C69B4-A76F-4396-8FE1-1FC77C0385CC","properties":{"overrideid":"","name":"组长审批","documentation":"","asynchronousdefinition":"false","exclusivedefinition":"false","executionlisteners":"","multiinstance_type":"None","multiinstance_cardinality":"","multiinstance_collection":"","multiinstance_variable":"","multiinstance_condition":"","isforcompensation":"false","usertaskassignment":{"assignment":{"type":"idm","idm":{"type":"users","candidateUsers":[{"id":"test","firstName":"test","lastName":null,"email":null,"fullName":"test ","groups":[],"$$hashKey":"object:6687"}]}}},"formkeydefinition":"","formreference":"","duedatedefinition":"","prioritydefinition":"","formproperties":"","tasklisteners":""},"stencil":{"id":"UserTask"},"childShapes":[],"outgoing":[{"resourceId":"sid-CD98690F-4EA7-4F5F-9A04-FC604815AD2B"}],"bounds":{"lowerRight":{"x":332,"y":216},"upperLeft":{"x":232,"y":136}},"dockers":[]},{"resourceId":"sid-5D1AABEF-2AB6-46E9-9AD5-0CD36AD6251C","properties":{"overrideid":"","name":"","documentation":"","executionlisteners":""},"stencil":{"id":"EndNoneEvent"},"childShapes":[],"outgoing":[],"bounds":{"lowerRight":{"x":493,"y":193},"upperLeft":{"x":465,"y":165}},"dockers":[]},{"resourceId":"sid-1007C45C-AB28-4EA8-87C9-0E35B6FFA141","properties":{"overrideid":"","name":"","documentation":"","conditionsequenceflow":"","executionlisteners":"","defaultflow":"false"},"stencil":{"id":"SequenceFlow"},"childShapes":[],"outgoing":[{"resourceId":"sid-584C69B4-A76F-4396-8FE1-1FC77C0385CC"}],"bounds":{"lowerRight":{"x":231.4219467051485,"y":177.8161498108401},"upperLeft":{"x":130.3514907948515,"y":176.6057251891599}},"dockers":[{"x":15,"y":15},{"x":50,"y":40}],"target":{"resourceId":"sid-584C69B4-A76F-4396-8FE1-1FC77C0385CC"}},{"resourceId":"sid-CD98690F-4EA7-4F5F-9A04-FC604815AD2B","properties":{"overrideid":"","name":"","documentation":"","conditionsequenceflow":"","executionlisteners":"","defaultflow":"false"},"stencil":{"id":"SequenceFlow"},"childShapes":[],"outgoing":[{"resourceId":"sid-5D1AABEF-2AB6-46E9-9AD5-0CD36AD6251C"}],"bounds":{"lowerRight":{"x":464.14855343232165,"y":178.7738358390709},"upperLeft":{"x":332.24988406767835,"y":176.7652266609291}},"dockers":[{"x":50,"y":40},{"x":14,"y":14}],"target":{"resourceId":"sid-5D1AABEF-2AB6-46E9-9AD5-0CD36AD6251C"}}],"bounds":{"lowerRight":{"x":1200,"y":1050},"upperLeft":{"x":0,"y":0}},"stencilset":{"url":"stencilsets/bpmn2.0/bpmn2.0.json","namespace":"http://b3mn.org/stencilset/bpmn2.0#"},"ssextensions":[]}
既然数据格式有了,剩下的就是将我们自定义的流程图数据取出一部分非业务数据转化成Activiti的数据,以保证引擎能正常流转,对应上面的数据我们抽象出对应的实体类:
public class ChartElement {
/**
* 资源ID
*/
private String resourceId = "";
/**
* 属性,这是最关键的,每一个元素都拥有不同的属性项,如线对象、用户任务对象、网关对象等等,以继承方式实现即可
*/
private BaseProperty properties;
/**
* 流程环节的activiti类型
* key为id
* 形似:
* "stencil": {
"id": "BPMNDiagram"
}
*/
private Map<String, String> stencil = new HashMap<>();
/**
* 子组件
*/
private List<ChartElement> childShapes = new ArrayList<>();
/**
* 组件外连的其他组件Id
* 形似:
* "outgoing": [
{
"resourceId": "sid-F642A425-567D-4D05-9861-E04640D0E4E5"
},
{
"resourceId": "sid-F642A425-567D-4D05-9861-E04640D0E4E5"
}
]
*/
private List<Map<String, String>> outgoing = new ArrayList<>();
/**
* 组件所在位置。
* 形似:
* "bounds": {
"lowerRight": {
"x": 324.64844687000755,
"y": 189.4097335362338
},
"upperLeft": {
"x": 280.62889687999245,
"y": 189.2191727137662
}
}
* lowerRight:组件的右下角坐标
* upperLeft:组件的左上角坐标
*/
private Map<String, Coordinate> bounds = new HashMap<>();
/**
* 连接线上的拐点坐标
* 形似:
* "dockers": [
{
"x": 50,
"y": 40
},
{
"x": 20.5,
"y": 20.5
}
]
*/
private List<Coordinate> dockers = new ArrayList<>();
/**
* 连接线的目标组件ID
* 形如:
* "target": {
"resourceId": "sid-AB6219CA-5A68-4CFC-A7E1-D8F7D6375519"
}
*/
private Map<String, String> target = new HashMap<>();
}
public class BaseProperty {
/**
* 名称
*/
private String name = "";
/**
* 说明
*/
private String document = "";
}
public class UserTaskProperty extends BaseProperty {
/**
* 异步操作标识符,默认false
*/
@JsonProperty("asynchronousdefinition")
private boolean asynchronousDefinition;
/**
* 排他属性,默认false
*/
@JsonProperty("exclusivedefinition")
private boolean exclusiveDefinition;
/**
* 多例模式(会签)
* {@linkplain uyun.hornet.config.api.enums.MultiInstanceType#code}
*/
@JsonProperty("multiinstance_type")
private String multiInstanceType = "";
/**
* 会签状态下,需要分配的人员集合
*/
@JsonProperty("multiinstance_collection")
private String multiInstanceCollection = "";
/**
* 会签状态下,变量
*/
@JsonProperty("multiinstance_variable")
private String multiInstanceVariable = "";
/**
* 用户任务分配的人员
*/
@JsonProperty("usertaskassignment")
private Map<String, Map<String, String>> usertaskAssignment = new HashMap<>();
/**
* 人工任务的监听器,主要是为了自动节点和子流程节点使用
*/
@JsonProperty("tasklisteners")
private Map<String, List<TaskListenerBean>> tasklisteners = new HashMap<>();
public void setTasklisteners(String delegateExpression) {
List<TaskListenerBean> taskListenerBeans = new ArrayList<>();
TaskListenerBean taskListenerBean = new TaskListenerBean();
taskListenerBean.setEvent("create");
taskListenerBean.setDelegateExpression(delegateExpression);
taskListenerBean.setImplementation(delegateExpression);
taskListenerBeans.add(taskListenerBean);
Map<String, List<TaskListenerBean>> listen = new HashMap<>();
listen.put("taskListeners", taskListenerBeans);
this.tasklisteners = listen;
}
class TaskListenerBean{
/**
* 事件类型
*/
private String event;
/**
* 实现类
*/
private String implementation;
/**
* className
*/
private String className;
/**
* 表达式
*/
private String expression;
/**
* 委托表达式
*/
private String delegateExpression;
}
}
抽象类中相关涉及到的类都已放上去,主要是对照着模型的json数据进行抽象即可,在封装时针对不同的元素对象需要赋予不同的属性,具体可以参考Activiti-app的保持格式,即最好是将json数据展开,然后对照着看。
这里假设你已经转换好了,已经得到一个完整的流程图对象ChartElement,剩下的步骤就是需要将该对象转换成BpmnModel了,我们无需手动转换,已经有工具帮我们实现,可直接使用,首先引入对应依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>6.0.0</version>
</dependency>
然后使用如下代码即可:
@Test
public void transformChartTest() throws Exception {
// 假设这是你已经转换好后的整个流程图对象
ChartElement chartElement = new ChartElement();
// 这里借助jackson库来转换
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(mapper.writeValueAsString(chartElement));
// 然后借助工具将jsonNod转换成BpmnModel
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
// 最后直接部署即可,注意流程id不能以数字开头
repositoryService.createDeployment().addBpmnModel("sid-123.bpmn20.xml", bpmnModel).deploy();
}
针对某些特殊节点,如果需要增加监听器,转换方式与界面保持一致即可,可以使用委托表达式来实现,形如:${监听器服务类},该服务类只需交给spring注入即可。
到这里就结束了,可以直接启动流程实例了~网上资料稀叭烂当时找的时候还是很痛苦的,这边特地分享出来,系列到这一步已经能使用Activiti实现绝大多数功能包括整合自己公司的流程图编辑器。
- 2020/7/14 更新实体类
如果该系列对你有帮助的,可以请博主喝杯咖啡嘻嘻嘻
(左支付宝右微信)