添加工作流模型新增,修改,查询,删除

This commit is contained in:
gssong 2023-06-03 21:56:57 +08:00
parent 5125dc5af2
commit 831b6b850a
15 changed files with 2582 additions and 5 deletions

View File

@ -10,7 +10,9 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
* @author Lion Li
*/
@SpringBootApplication
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class})
public class DromaraApplication {
public static void main(String[] args) {

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,11 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>6.8.0</version>
</dependency>
<!-- 绘制flowable流程图 -->
<dependency>
<groupId>org.flowable</groupId>
@ -66,6 +71,12 @@
<artifactId>xercesImpl</artifactId>
<version>${xerces.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-json-converter</artifactId>
<version>6.8.0</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@ -0,0 +1,111 @@
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.AccountVo;
import org.dromara.workflow.service.IActModelService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Model;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
/**
* 模型管理 控制层
*
* @author may
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/model")
public class ActModelController extends BaseController {
private final RepositoryService repositoryService;
private final IActModelService iActModelService;
@GetMapping("/list")
public TableDataInfo<Model> getByPage(ModelBo modelBo) {
return iActModelService.getByPage(modelBo);
}
/**
* 参考官方提供的响应数据
* "{\"id\":\"admin\",\"firstName\":\"Test\",\"lastName\":\"Administrator\",\"email\":\"admin@flowable.org\",\"fullName\":\"Test Administrator\",\"groups\":[],\"privileges\":[\"access-idm\",\"access-rest-api\",\"access-task\",\"access-modeler\",\"access-admin\"]}";
*/
@GetMapping("/rest/account")
@SaIgnore
public String getAccount() {
AccountVo accountVo = new AccountVo();
accountVo.setId("admin");
accountVo.setFirstName("");
accountVo.setLastName("admin");
accountVo.setFullName("admin");
return JsonUtils.toJsonString(accountVo);
}
/**
* 新增模型
*
* @param modelBo 模型请求对象
*/
@Log(title = "模型管理", businessType = BusinessType.INSERT)
@PostMapping("/rest/models")
public R<Void> saveNewModel(@RequestBody ModelBo modelBo) {
return toAjax(iActModelService.saveNewModel(modelBo));
}
/**
* 查询模型
*
* @param modelId 模型id
*/
@GetMapping("/rest/models/{modelId}/editor/json")
@SaIgnore
public ObjectNode getModelInfo(@PathVariable String modelId) {
return iActModelService.getModelInfo(modelId);
}
/**
* 编辑模型
*
* @param modelId 模型id
* @param values 模型数据
*/
@SaIgnore
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
@PostMapping(value = "/rest/models/{modelId}/editor/json")
public R<Void> editModel(@PathVariable String modelId, @RequestParam MultiValueMap<String, String> values) {
return toAjax(iActModelService.editModel(modelId,values));
}
/**
* 删除流程模型
*
* @param ids 模型id
*/
@Log(title = "模型管理", businessType = BusinessType.DELETE)
@RepeatSubmit()
@DeleteMapping("/{ids}")
@Transactional(rollbackFor = Exception.class)
public R<Void> delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) {
for (String id : ids) {
repositoryService.deleteModel(id);
}
return R.ok();
}
}

View File

@ -0,0 +1,40 @@
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.workflow.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.*;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("/workflow/model")
public class StencilsetRestResource {
@SaIgnore
@GetMapping(value="/rest/stencil-sets/editor")
public String getStencilset() {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("static/stencilset.json");
try {
assert inputStream != null;
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new SecurityException("Error while loading stencil set", e);
}
}
}

View File

@ -1 +0,0 @@
package org.dromara.workflow.controller;

View File

@ -0,0 +1,40 @@
package org.dromara.workflow.domain.bo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import java.io.Serial;
import java.io.Serializable;
/**
* 模型请求对象
*
* @author may
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ModelBo extends PageEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 模型名称
*/
@NotBlank(message = "模型名称不能为空", groups = {AddGroup.class})
private String name;
/**
* 模型标识key
*/
@NotBlank(message = "模型标识key不能为空", groups = {AddGroup.class})
private String key;
/**
* 备注
*/
private String description;
}

View File

@ -0,0 +1,36 @@
package org.dromara.workflow.domain.bo;
/**
* 分页参数
*
* @author may
*/
public class PageEntity {
/**
* 当前页码
*/
private Integer pageNum = 0;
/**
* 页容量
*/
private Integer pageSize = 10;
public Integer getPageNum() {
return (pageNum - 1) * pageSize;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
}

View File

@ -1 +0,0 @@
package org.dromara.workflow.domain;

View File

@ -0,0 +1,21 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.util.List;
/**
* 参考官方提供的响应数据
*
* @author may
*/
@Data
public class AccountVo {
private String id;
private String firstName;
private String lastName;
private String email;
private String fullName;
private List<String> groups;
private List<String> privileges;
}

View File

@ -0,0 +1,47 @@
package org.dromara.workflow.service;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.ModelBo;
import org.flowable.engine.repository.Model;
import org.springframework.util.MultiValueMap;
/**
* 模型管理 服务层
*
* @author may
*/
public interface IActModelService {
/**
* 分页查询模型
*
* @param modelBo 模型参数
* @return 返回分页列表
*/
TableDataInfo<Model> getByPage(ModelBo modelBo);
/**
* 新增模型
*
* @param modelBo 模型请求对象
* @return 结果
*/
boolean saveNewModel(ModelBo modelBo);
/**
* 查询模型
*
* @param modelId 模型id
* @return 模型数据
*/
ObjectNode getModelInfo(String modelId);
/**
* 编辑模型
*
* @param modelId 模型id
* @param values 模型数据
* @return 结果
*/
boolean editModel(String modelId, MultiValueMap<String, String> values);
}

View File

@ -0,0 +1,233 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.service.IActModelService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.constants.ModelDataJsonConstants;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import static org.flowable.editor.constants.ModelDataJsonConstants.*;
/**
* 模型管理 服务层实现
*
* @author may
*/
@RequiredArgsConstructor
@Service
public class ActModelServiceImpl implements IActModelService {
private final RepositoryService repositoryService;
/**
* 分页查询模型
*
* @param modelBo 模型参数
* @return 返回分页列表
*/
@Override
public TableDataInfo<Model> getByPage(ModelBo modelBo) {
ModelQuery query = repositoryService.createModelQuery();
if (StringUtils.isNotEmpty(modelBo.getName())) {
query.modelNameLike("%" + modelBo.getName() + "%");
}
if (StringUtils.isNotEmpty(modelBo.getKey())) {
query.modelKey(modelBo.getKey());
}
query.orderByLastUpdateTime().desc();
//创建时间降序排列
query.orderByCreateTime().desc();
// 分页查询
List<Model> modelList = query.listPage(modelBo.getPageNum(), modelBo.getPageSize());
if (CollectionUtil.isNotEmpty(modelList)) {
modelList.forEach(e -> {
boolean isNull = JSONUtil.isNull(JSONUtil.parseObj(e.getMetaInfo()).get(ModelDataJsonConstants.MODEL_DESCRIPTION));
if (!isNull) {
e.setMetaInfo((String) JSONUtil.parseObj(e.getMetaInfo()).get(ModelDataJsonConstants.MODEL_DESCRIPTION));
} else {
e.setMetaInfo(StrUtil.EMPTY);
}
});
}
// 总记录数
long total = query.count();
return new TableDataInfo<>(modelList, total);
}
/**
* 新增模型
*
* @param modelBo 模型请求对象
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveNewModel(ModelBo modelBo) {
try {
int version = 0;
String key = modelBo.getKey();
String name = modelBo.getName();
String description = modelBo.getDescription();
Model checkModel = repositoryService.createModelQuery().modelKey(key).singleResult();
if (ObjectUtil.isNotNull(checkModel)) {
throw new ServiceException("模型key已存在!");
}
// 1. 初始空的模型
Model model = repositoryService.newModel();
model.setKey(key);
model.setName(name);
model.setVersion(version);
model.setTenantId(LoginHelper.getTenantId());
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
// 封装模型json对象
ObjectNode objectNode = JsonUtils.getObjectMapper().createObjectNode();
objectNode.put(MODEL_NAME, name);
objectNode.put(MODEL_REVISION, version);
objectNode.put(MODEL_DESCRIPTION, description);
model.setMetaInfo(objectNode.toString());
// 保存初始化的模型基本信息数据
repositoryService.saveModel(model);
String namespace = "http://b3mn.org/stencilset/bpmn2.0#";
// 封装模型对象基础数据json串
ObjectNode editorNode = objectMapper.createObjectNode();
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace", namespace);
editorNode.replace("stencilset", stencilSetNode);
// 标识key
ObjectNode propertiesNode = objectMapper.createObjectNode();
propertiesNode.put("process_id", key);
propertiesNode.put("name", name);
propertiesNode.put("description", description);
editorNode.replace("properties", propertiesNode);
repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));
return true;
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException(e.getMessage());
}
}
/**
* 查询模型
*
* @param modelId 模型id
* @return 模型数据
*/
@Override
public ObjectNode getModelInfo(String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
Integer version = model.getVersion();
if (version > 0) {
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(modelEditorSource);
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream);
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter();
ObjectNode jsonNodes = bpmnJsonConverter.convertToJson(bpmnModel);
modelNode.put("model", jsonNodes);
} else {
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
new String(repositoryService.getModelEditorSource(model.getId()), StandardCharsets.UTF_8));
modelNode.put("model", editorJsonNode);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return modelNode;
}
/**
* 编辑模型
*
* @param modelId 模型id
* @param values 模型数据
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean editModel(String modelId, MultiValueMap<String, String> values) {
try {
Model model = repositoryService.getModel(modelId);
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, values.getFirst("name"));
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName(values.getFirst("name"));
// 每次保存把版本更新+1
model.setVersion(model.getVersion() + 1);
// 获取唯一标识key
String key = objectMapper.readTree(values.getFirst("json_xml")).get("properties").get("process_id").textValue();
model.setKey(key);
repositoryService.saveModel(model);
byte[] xmlBytes = WorkflowUtils.bpmnJsonToXmlBytes(Objects.requireNonNull(values.getFirst("json_xml")).getBytes(StandardCharsets.UTF_8));
repositoryService.addModelEditorSource(model.getId(), xmlBytes);
/*InputStream svgStream = new ByteArrayInputStream(values.getFirst("svg_xml").getBytes(StandardCharsets.UTF_8));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
// Setup output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
// Do the transformation
transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();*/
return true;
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException(e.getMessage());
}
}
}

View File

@ -1 +0,0 @@
package org.dromara.workflow.service.impl;

View File

@ -1 +0,0 @@
package org.dromara.workflow.service;

View File

@ -0,0 +1,42 @@
package org.dromara.workflow.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.json.utils.JsonUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import java.io.IOException;
/**
* 工作流工具
*
* @author may
*/
public class WorkflowUtils {
public WorkflowUtils() {
}
/**
* bpmnModel转为xml
*
* @param jsonBytes json
*/
public static byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException {
if (jsonBytes == null) {
return new byte[0];
}
//1. json字节码转成 BpmnModel 对象
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonBytes);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
if (bpmnModel.getProcesses().isEmpty()) {
return new byte[0];
}
//2.将bpmnModel转为xml
return new BpmnXMLConverter().convertToXML(bpmnModel);
}
}