springboot保存bpmn文件及用户自定义参数的保存与获取
假如有如下一份bpmn文件,其中usertask扩展了多个属性,那么如何对其进行保存和获取呢?
Tall is cheap, show you the code!
1.准备
假定bpmn如下:
代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn2:process id="p_1367770574455656450" name="jh0305" isExecutable="true">
<bpmn2:documentation>jh0305</bpmn2:documentation>
<bpmn2:startEvent id="Event_1" name="开始">
<bpmn2:outgoing>SequenceFlow_11hdsvi</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:userTask id="UserTask_0ag1sf3" name="用户任务1" activiti:priority="0">
<bpmn2:extensionElements>
<activiti:customProperties assigneeType="0" />
</bpmn2:extensionElements>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="SequenceFlow_11hdsvi" sourceRef="Event_1" targetRef="UserTask_0ag1sf3" />
<bpmn2:userTask id="Activity_10ovq4m" name="用户任务_2" activiti:formKey="" activiti:priority="1">
<bpmn2:extensionElements>
<activiti:customProperties assigneeType="1"/>
</bpmn2:extensionElements>
</bpmn2:userTask>
<bpmn2:endEvent id="Event_0jg7oml" name="结束节点_0">
<bpmn2:incoming>Flow_1hr1bdj</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="Flow_116afj0" sourceRef="UserTask_0ag1sf3" targetRef="Activity_10ovq4m" />
<bpmn2:sequenceFlow id="Flow_1hr1bdj" sourceRef="Activity_10ovq4m" targetRef="Event_0jg7oml" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_p_1367770574455656450">
<bpmndi:BPMNPlane id="BPMNPlane_p_1367770574455656450" bpmnElement="p_1367770574455656450">
<bpmndi:BPMNEdge id="BPMNEdge_Flow_1hr1bdj" bpmnElement="Flow_1hr1bdj">
<omgdi:waypoint x="790" y="258" />
<omgdi:waypoint x="852" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="BPMNEdge_Flow_116afj0" bpmnElement="Flow_116afj0">
<omgdi:waypoint x="620" y="258" />
<omgdi:waypoint x="690" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_11hdsvi" bpmnElement="SequenceFlow_11hdsvi">
<omgdi:waypoint x="448" y="258" />
<omgdi:waypoint x="520" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="BPMNShape_Event_1" bpmnElement="Event_1">
<omgdc:Bounds x="412" y="240" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_UserTask_0ag1sf3" bpmnElement="UserTask_0ag1sf3">
<omgdc:Bounds x="520" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_Activity_10ovq4m" bpmnElement="Activity_10ovq4m">
<omgdc:Bounds x="690" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_Event_0jg7oml" bpmnElement="Event_0jg7oml">
<omgdc:Bounds x="852" y="240" width="36" height="36" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
注意:两个用户任务节点中的
<bpmn2:extensionElements>
<activiti:customProperties assigneeType="0" />
</bpmn2:extensionElements>
activiti:customProperties即为自定义节点,其中有一个assigneeType属性
2.保存bpmn文件
使用了swagger和lombok
2.1 前端传输的对象模型
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
@ApiModel("流程模型对象")
public class BpmnModelVO implements Serializable {
@ApiModelProperty("bpmn文件的xml字符串")
String bpmnXml;
@ApiModelProperty("流程名称")
String name;
@ApiModelProperty("流程的key")
Long processKey;
@ApiModelProperty("租户id")
String tenantId;
@ApiModelProperty(value = "模型id,新增时为空")
String modelId;
}
2.2 自定义消息转换方法
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.model.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class MyUserTaskJsonConverter extends UserTaskJsonConverter{
private final String assigneeType = "assigneeType";
private final String elememt = "customProperties";
* 将bpmn元素转换成json
@Override
protected void convertElementToJson(ObjectNode propertiesNode, BaseElement baseElement) {
super.convertElementToJson(propertiesNode, baseElement);
UserTask userTask = (UserTask) baseElement;
Map<String, List<ExtensionElement>> customerProperties = userTask.getExtensionElements();
if (!CollectionUtils.isEmpty(customerProperties) && customerProperties.containsKey(elememt)) {
ExtensionElement e = customerProperties.get(elememt).get(0);
Map<String, List<ExtensionAttribute>> attributes = e.getAttributes();
attributes.forEach((key, attr) -> {
for (ExtensionAttribute extensionAttribute : attr) {
setPropertyValue(extensionAttribute.getName(), extensionAttribute.getValue(), propertiesNode);
* 将json转换成bpmn元素
@Override
protected FlowElement convertJsonToElement(JsonNode elementNode, JsonNode modelNode, Map<String, JsonNode> shapeMap) {
FlowElement flowElement = super.convertJsonToElement(elementNode,
modelNode, shapeMap);
//解析新增属性的业务逻辑
UserTask userTask = (UserTask) flowElement;
ExtensionElement ee = new ExtensionElement();
ee.setName("activiti:" + elememt);
ee.setNamespacePrefix("activiti");
Map<String, List<ExtensionAttribute>> attributes = new HashMap<>();
// 这里就是bpmn里自定义的assigneeType
String propertyAssigneeType = getPropertyValueAsString(assigneeType, elementNode);
if (StringUtils.hasLength(propertyAssigneeType)) {
ExtensionAttribute assigneeTypeAttr = new ExtensionAttribute();
assigneeTypeAttr.setName(assigneeType);
assigneeTypeAttr.setValue(propertyAssigneeType);
List<ExtensionAttribute> assigneeTypeList = new ArrayList<>(1);
assigneeTypeList.add(assigneeTypeAttr);
attributes.put(assigneeType, assigneeTypeList);
ee.setAttributes(attributes);
userTask.addExtensionElement(ee);
return userTask;
}
2.3 实现BpmnJsonConverter,替换转换方法
public class CustomBpmnJsonConverter extends BpmnJsonConverter {
public static void initCustomJsonConverter() {
convertersToBpmnMap.put(STENCIL_TASK_USER, MyUserTaskJsonConverter.class);
convertersToJsonMap.put(UserTask.class, MyUserTaskJsonConverter.class);
}
2.4 配置
@Configuration
public class ActivitiAutoConfiguration implements ProcessEngineConfigurationConfigurer {
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
// 其他配置
// 配置自定义用户任务解析器
CustomBpmnJsonConverter.initCustomJsonConverter();
}
2.5 controller类
@RestController
@RequestMapping("model")
@Api(tags = "流程模型接口")
public class ModelController {
private final RepositoryService repositoryService;
private final ObjectMapper objectMapper;
public ModelController(RepositoryService repositoryService, ObjectMapper objectMapper) {
this.repositoryService = repositoryService;
this.objectMapper = objectMapper;
@PostMapping("/save")
@ApiOperation("保存模型")
public ResponseResult<String> saveBpmn(@RequestBody BpmnModelVO bpmn) {
if (bpmn == null || StringUtils.isEmpty(bpmn.getBpmnXml()) || StringUtils.isEmpty(bpmn.getTenantId())
|| StringUtils.isEmpty(bpmn.getProcessKey()) || StringUtils.isEmpty(bpmn.getName()))
return ResponseResult.error("参数不完整");
Model modelData = null;
if (StringUtils.hasLength(bpmn.getModelId()))
modelData = repositoryService.getModel(bpmn.getModelId());
if (modelData == null) {
modelData = repositoryService.newModel();
//与前端保持一致
modelData.setKey("p_" + bpmn.getProcessKey());
modelData.setName(bpmn.getName());
modelData.setTenantId(bpmn.getTenantId());
try {
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStream is = new ByteArrayInputStream(bpmn.getBpmnXml().getBytes(StandardCharsets.UTF_8));
XMLStreamReader xtr = xif.createXMLStreamReader(is);
// 转为bpmnModel
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
// bpmnModel转json
CustomBpmnJsonConverter converter = new CustomBpmnJsonConverter();
ObjectNode jsonNodes = converter.convertToJson(bpmnModel);
ObjectMapper objectMapper = new ObjectMapper();
String jsonXml = objectMapper.writeValueAsString(jsonNodes);
this.flowService.saveModel(repositoryService, modelData, jsonXml, bpmn.getProcessKey());
} catch (XMLStreamException | JsonProcessingException e) {
e.printStackTrace();
return ResponseResult.error(e.getMessage());
return ResponseResult.success(modelData.getId());
@GetMapping("/getById/{modelId}")
@ApiOperation("根据模型id获取模型bpmn的xml字符串")
public ResponseResult<String> getBpmnXmnById(@ApiParam(value = "bpmn模型id", required = true) @PathVariable("modelId") String modelId) {
Model modelData = repositoryService.getModel(modelId);
if (modelData != null)
try {
byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(modelEditorSource);
CustomBpmnJsonConverter converter = new CustomBpmnJsonConverter();
BpmnModel model = converter.convertToBpmnModel(modelNode);// 转model
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);// 转xml
return ResponseResult.success(new String(bpmnBytes));
} catch (IOException e) {
e.printStackTrace();
return ResponseResult.error("获取失败");
return ResponseResult.error("不存在该模型");
}
2.6 ResponseResult类
@Data
public class ResponseResult<T> {
private int code;
private String msg;
private T data;
private ResponseResult(){}
public ResponseResult(int code, String msg) {
this.code = code;
this.msg = msg;
private ResponseResult(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
private ResponseResult(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
this.data = data;
public static <T> ResponseResult<T> success() {
return new ResponseResult<>(ResultCode.SUCCESS);
public static <T> ResponseResult<T> success(T data) {
return new ResponseResult<>(ResultCode.SUCCESS, data);
public static <T> ResponseResult<T> error(ResultCode resultCode) {
return new ResponseResult<>(resultCode);