remove 移除原生ui接口,增加新ui接口

This commit is contained in:
songgaoshuai 2024-02-01 11:47:27 +08:00
parent ebbd85275c
commit d7a738648e
12 changed files with 400 additions and 2461 deletions

File diff suppressed because one or more lines are too long

View File

@ -53,6 +53,19 @@
<version>6.8.0</version> <version>6.8.0</version>
</dependency> </dependency>
<!-- svg转png图片工具-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>1.10</version>
<exclusions>
<exclusion>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--系统模块--> <!--系统模块-->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>

View File

@ -1,36 +1,26 @@
package org.dromara.workflow.controller; package org.dromara.workflow.controller;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.resource.ResourceUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.idempotent.annotation.RepeatSubmit; 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.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.workflow.domain.bo.ModelBo; import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.AccountVo; import org.dromara.workflow.domain.vo.ModelVo;
import org.dromara.workflow.domain.vo.ResultListDataRepresentation;
import org.dromara.workflow.service.IActModelService; import org.dromara.workflow.service.IActModelService;
import org.flowable.engine.RepositoryService; import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -59,20 +49,6 @@ public class ActModelController extends BaseController {
return actModelService.page(modelBo); return actModelService.page(modelBo);
} }
/**
* 设计器登录信息
*/
@GetMapping("/rest/account")
public String getAccount() {
AccountVo accountVo = new AccountVo();
LoginUser loginUser = LoginHelper.getLoginUser();
accountVo.setId(Convert.toStr(loginUser.getUserId()));
accountVo.setFirstName("");
accountVo.setLastName(loginUser.getUsername());
accountVo.setFullName(loginUser.getUsername());
return JsonUtils.toJsonString(accountVo);
}
/** /**
* 新增模型 * 新增模型
* *
@ -80,7 +56,7 @@ public class ActModelController extends BaseController {
*/ */
@Log(title = "模型管理", businessType = BusinessType.INSERT) @Log(title = "模型管理", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping("/rest/models") @PostMapping("/save")
public R<Void> saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) { public R<Void> saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) {
return toAjax(actModelService.saveNewModel(modelBo)); return toAjax(actModelService.saveNewModel(modelBo));
} }
@ -88,25 +64,35 @@ public class ActModelController extends BaseController {
/** /**
* 查询模型 * 查询模型
* *
* @param modelId 模型id * @param id 模型id
*/ */
@GetMapping("/rest/models/{modelId}/editor/json") @GetMapping("/getInfo/{id}")
public ObjectNode getModelInfo(@NotBlank(message = "模型id不能为空") @PathVariable String modelId) { public R<ModelVo> getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) {
return actModelService.getModelInfo(modelId); return R.ok(actModelService.getInfo(id));
} }
/** /**
* 编辑模型 * 修改模型信息
* *
* @param modelId 模型id * @param modelBo 模型数据
* @param values 模型数据
*/ */
@Log(title = "模型管理", businessType = BusinessType.UPDATE) @Log(title = "模型管理", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PostMapping(value = "/rest/models/{modelId}/editor/json") @PostMapping(value = "/update")
public R<Void> editModel(@PathVariable String modelId, @RequestParam MultiValueMap<String, String> values) { public R<Void> update(@RequestBody ModelBo modelBo) {
return toAjax(actModelService.editModel(modelId, values)); return toAjax(actModelService.update(modelBo));
}
/**
* 编辑XMl模型
*
* @param modelBo 模型数据
*/
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping(value = "/editModelXml")
public R<Void> editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) {
return toAjax(actModelService.editModelXml(modelBo));
} }
/** /**
@ -146,36 +132,4 @@ public class ActModelController extends BaseController {
HttpServletResponse response) { HttpServletResponse response) {
actModelService.exportZip(modelId, response); actModelService.exportZip(modelId, response);
} }
/**
* 查询用户
*
* @param filter 参数
*/
@GetMapping(value = "/rest/editor-users")
public ResultListDataRepresentation editorUsers(@RequestParam(value = "filter", required = false) String filter) {
return actModelService.getUsers(filter);
}
/**
* 查询用户组
*
* @param filter 参数
*/
@GetMapping(value = "/rest/editor-groups")
public ResultListDataRepresentation editorGroups(@RequestParam(required = false, value = "filter") String filter) {
return actModelService.getGroups(filter);
}
/**
* 解析翻译模型设计器内容
*/
@GetMapping(value = "/rest/stencil-sets/editor")
public String getStencilset() {
try (InputStream inputStream = ResourceUtil.getStream("static/stencilset.json")) {
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new SecurityException("Error while loading stencil set", e);
}
}
} }

View File

@ -5,6 +5,7 @@ import jakarta.validation.constraints.Pattern;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.workflow.common.PageEntity; import org.dromara.workflow.common.PageEntity;
import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.constant.FlowConstant;
@ -23,6 +24,12 @@ public class ModelBo extends PageEntity implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 模型id
*/
@NotBlank(message = "模型ID不能为空", groups = {EditGroup.class})
private String id;
/** /**
* 模型名称 * 模型名称
*/ */
@ -42,6 +49,18 @@ public class ModelBo extends PageEntity implements Serializable {
@NotBlank(message = "模型分类不能为空", groups = {AddGroup.class}) @NotBlank(message = "模型分类不能为空", groups = {AddGroup.class})
private String categoryCode; private String categoryCode;
/**
* 模型XML
*/
@NotBlank(message = "模型XML不能为空", groups = {AddGroup.class})
private String xml;
/**
* 模型SVG图片
*/
@NotBlank(message = "模型SVG不能为空", groups = {EditGroup.class})
private String svg;
/** /**
* 备注 * 备注
*/ */

View File

@ -1,21 +0,0 @@
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,48 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 模型视图对象
*
* @author may
*/
@Data
public class ModelVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 模型id
*/
private String id;
/**
* 模型名称
*/
private String name;
/**
* 模型标识key
*/
private String key;
/**
* 模型分类
*/
private String categoryCode;
/**
* 模型XML
*/
private String xml;
/**
* 备注
*/
private String description;
}

View File

@ -1,39 +0,0 @@
/* 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.domain.vo;
import lombok.Data;
import java.util.List;
/**
* @author Tijs Rademakers
*/
@Data
public class ResultListDataRepresentation {
protected Integer size;
protected Long total;
protected Integer start;
protected List<?> data;
public ResultListDataRepresentation(List<?> data) {
this.data = data;
if (data != null) {
size = data.size();
total = (long) data.size();
start = 0;
}
}
}

View File

@ -1,30 +0,0 @@
/* 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.domain.vo;
import lombok.Data;
/**
* @author Joram Barrez
*/
@Data
public class UserRepresentation {
protected String id;
protected String firstName;
protected String lastName;
protected String email;
protected String fullName;
protected String tenantId;
}

View File

@ -1,12 +1,11 @@
package org.dromara.workflow.service; package org.dromara.workflow.service;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.workflow.domain.bo.ModelBo; import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.ResultListDataRepresentation; import org.dromara.workflow.domain.vo.ModelVo;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
import org.springframework.util.MultiValueMap;
/** /**
* 模型管理 服务层 * 模型管理 服务层
@ -36,16 +35,23 @@ public interface IActModelService {
* @param modelId 模型id * @param modelId 模型id
* @return 模型数据 * @return 模型数据
*/ */
ObjectNode getModelInfo(String modelId); ModelVo getInfo(String modelId);
/** /**
* 编辑模型 * 修改模型信息
* *
* @param modelId 模型id * @param modelBo 模型数据
* @param values 模型数据
* @return 结果 * @return 结果
*/ */
boolean editModel(String modelId, MultiValueMap<String, String> values); boolean update(ModelBo modelBo);
/**
* 编辑模型XML
*
* @param modelBo 模型数据
* @return 结果
*/
boolean editModelXml(ModelBo modelBo);
/** /**
* 模型部署 * 模型部署
@ -62,20 +68,4 @@ public interface IActModelService {
* @param response 相应 * @param response 相应
*/ */
void exportZip(String modelId, HttpServletResponse response); void exportZip(String modelId, HttpServletResponse response);
/**
* 查询用户
*
* @param filter 参数
* @return 结果
*/
ResultListDataRepresentation getUsers(String filter);
/**
* 查询用户组
*
* @param filter 参数
* @return 结果
*/
ResultListDataRepresentation getGroups(String filter);
} }

View File

@ -7,34 +7,24 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil; import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysRole;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysRoleMapper;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.domain.bo.ModelBo; import org.dromara.workflow.domain.bo.ModelBo;
import org.dromara.workflow.domain.vo.GroupRepresentation; import org.dromara.workflow.domain.vo.ModelVo;
import org.dromara.workflow.domain.vo.ResultListDataRepresentation;
import org.dromara.workflow.domain.vo.UserRepresentation;
import org.dromara.workflow.service.IActModelService; import org.dromara.workflow.service.IActModelService;
import org.dromara.workflow.utils.WorkflowUtils; import org.dromara.workflow.utils.ModelUtils;
import org.flowable.bpmn.model.BpmnModel; 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.RepositoryService;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
@ -43,21 +33,18 @@ import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.validation.ValidationError; import org.flowable.validation.ValidationError;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import static org.dromara.workflow.common.constant.FlowConstant.NAMESPACE;
import static org.flowable.editor.constants.ModelDataJsonConstants.*;
/** /**
* 模型管理 服务层实现 * 模型管理 服务层实现
* *
@ -95,16 +82,6 @@ public class ActModelServiceImpl implements IActModelService {
query.orderByCreateTime().desc(); query.orderByCreateTime().desc();
// 分页查询 // 分页查询
List<Model> modelList = query.listPage(modelBo.getPageNum(), modelBo.getPageSize()); List<Model> modelList = query.listPage(modelBo.getPageNum(), modelBo.getPageSize());
if (CollUtil.isNotEmpty(modelList)) {
modelList.parallelStream().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(); long total = query.count();
return new TableDataInfo<>(modelList, total); return new TableDataInfo<>(modelList, total);
@ -124,38 +101,22 @@ public class ActModelServiceImpl implements IActModelService {
String key = modelBo.getKey(); String key = modelBo.getKey();
String name = modelBo.getName(); String name = modelBo.getName();
String description = modelBo.getDescription(); String description = modelBo.getDescription();
String categoryCode = modelBo.getCategoryCode();
String xml = modelBo.getXml();
Model checkModel = repositoryService.createModelQuery().modelKey(key).modelTenantId(TenantHelper.getTenantId()).singleResult(); Model checkModel = repositoryService.createModelQuery().modelKey(key).modelTenantId(TenantHelper.getTenantId()).singleResult();
if (ObjectUtil.isNotNull(checkModel)) { if (ObjectUtil.isNotNull(checkModel)) {
throw new ServiceException("模型key已存在"); throw new ServiceException("模型key已存在");
} }
// 1. 初始空的模型 //初始空的模型
Model model = repositoryService.newModel(); Model model = repositoryService.newModel();
model.setKey(key); model.setKey(key);
model.setName(name); model.setName(name);
model.setCategory(modelBo.getCategoryCode());
model.setVersion(version); model.setVersion(version);
model.setTenantId(TenantHelper.getTenantId()); model.setCategory(categoryCode);
ObjectMapper objectMapper = JsonUtils.getObjectMapper(); model.setMetaInfo(description);
// 封装模型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); repositoryService.saveModel(model);
// 封装模型对象基础数据json串 repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
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; return true;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -166,93 +127,97 @@ public class ActModelServiceImpl implements IActModelService {
/** /**
* 查询模型 * 查询模型
* *
* @param modelId 模型id * @param id 模型id
* @return 模型数据 * @return 模型数据
*/ */
@Override @Override
public ObjectNode getModelInfo(String modelId) { public ModelVo getInfo(String id) {
ObjectNode modelNode = null; ModelVo modelVo = new ModelVo();
Model model = repositoryService.createModelQuery().modelId(modelId).modelTenantId(TenantHelper.getTenantId()).singleResult(); Model model = repositoryService.getModel(id);
ObjectMapper objectMapper = JsonUtils.getObjectMapper();
if (model != null) { if (model != null) {
try { 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());
modelNode.put("key", model.getKey());
Integer version = model.getVersion();
if (version > 0) {
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId()); byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
BpmnModel bpmnModel = WorkflowUtils.xmlToBpmnModel(modelEditorSource); modelVo.setXml(StrUtil.utf8Str(modelEditorSource));
BpmnJsonConverter bpmnJsonConverter = new BpmnJsonConverter(); modelVo.setId(model.getId());
ObjectNode jsonNodes = bpmnJsonConverter.convertToJson(bpmnModel); modelVo.setKey(model.getKey());
modelNode.set("model", jsonNodes); modelVo.setName(model.getName());
} else { modelVo.setCategoryCode(model.getCategory());
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree( modelVo.setDescription(model.getMetaInfo());
new String(repositoryService.getModelEditorSource(model.getId()), StandardCharsets.UTF_8)); return modelVo;
modelNode.set("model", editorJsonNode);
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new ServiceException(e.getMessage());
} }
} }
return modelNode; return modelVo;
} }
/** /**
* 编辑模型 * 修改模型信息
* *
* @param modelId 模型id * @param modelBo 模型数据
* @param values 模型数据 * @return 结果
*/
@Override
public boolean update(ModelBo modelBo) {
try {
Model model = repositoryService.getModel(modelBo.getId());
List<Model> list = repositoryService.createModelQuery().modelTenantId(TenantHelper.getTenantId()).modelKey(modelBo.getKey()).list();
list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
throw new ServiceException("模型KEY已存在");
});
model.setKey(modelBo.getKey());
model.setName(modelBo.getName());
model.setCategory(modelBo.getCategoryCode());
model.setMetaInfo(modelBo.getDescription());
repositoryService.saveModel(model);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
return true;
}
/**
* 编辑模型XML
*
* @param modelBo 模型数据
* @return 结果 * @return 结果
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean editModel(String modelId, MultiValueMap<String, String> values) { public boolean editModelXml(ModelBo modelBo) {
try { try {
Model model = repositoryService.createModelQuery().modelId(modelId).modelTenantId(TenantHelper.getTenantId()).singleResult(); String xml = modelBo.getXml();
ObjectMapper objectMapper = JsonUtils.getObjectMapper(); String svg = modelBo.getSvg();
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo()); String modelId = modelBo.getId();
String key = modelBo.getKey();
modelJson.put(MODEL_NAME, values.getFirst("name")); String name = modelBo.getName();
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description")); BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
modelJson.put(MODEL_REVISION, model.getVersion() + 1); ModelUtils.checkBpmnModel(bpmnModel);
model.setMetaInfo(modelJson.toString()); Model model = repositoryService.getModel(modelId);
model.setName(values.getFirst("name")); List<Model> list = repositoryService.createModelQuery().modelTenantId(TenantHelper.getTenantId()).modelKey(key).list();
// 每次保存把版本更新+1 list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
model.setVersion(model.getVersion() + 1); throw new ServiceException("模型KEY已存在");
// 获取唯一标识key });
String key = values.getFirst("key");
// 校验key命名规范 // 校验key命名规范
if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) { if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) {
throw new ServiceException("模型标识key只能字符或者下划线开头"); throw new ServiceException("模型标识KEY只能字符或者下划线开头!");
} }
List<Model> list = repositoryService.createModelQuery().modelKey(key).modelTenantId(TenantHelper.getTenantId()).list();
list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
throw new ServiceException("模型标识key已存在");
});
model.setKey(key); model.setKey(key);
model.setName(name);
model.setVersion(model.getVersion() + 1);
repositoryService.saveModel(model); repositoryService.saveModel(model);
JsonNode jsonNode = objectMapper.readTree(values.getFirst("json_xml")); repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
// 校验流程标识 // 转换图片
String processId = jsonNode.get("properties").get("process_id").textValue(); InputStream svgStream = new ByteArrayInputStream(StrUtil.utf8Bytes(svg));
if (StringUtils.isBlank(processId)) { TranscoderInput input = new TranscoderInput(svgStream);
throw new ServiceException("流程标识不能为空!");
}
if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, processId)) {
throw new ServiceException("流程标识只能字符或者下划线开头!");
}
byte[] xmlBytes = WorkflowUtils.bpmnJsonToXmlBytes(Objects.requireNonNull(JsonUtils.toJsonString(jsonNode)).getBytes(StandardCharsets.UTF_8)); PNGTranscoder transcoder = new PNGTranscoder();
if (ArrayUtil.isEmpty(xmlBytes)) { ByteArrayOutputStream outStream = new ByteArrayOutputStream();
throw new ServiceException("模型不能为空!"); TranscoderOutput output = new TranscoderOutput(outStream);
}
repositoryService.addModelEditorSource(model.getId(), xmlBytes); transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -276,14 +241,14 @@ public class ActModelServiceImpl implements IActModelService {
throw new ServiceException("模型数据为空,请先设计流程定义模型,再进行部署!"); throw new ServiceException("模型数据为空,请先设计流程定义模型,再进行部署!");
} }
if (JSONUtil.isTypeJSON(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString()))) { if (JSONUtil.isTypeJSON(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString()))) {
byte[] bytes = WorkflowUtils.bpmnJsonToXmlBytes(xmlBytes); byte[] bytes = ModelUtils.bpmnJsonToXmlBytes(xmlBytes);
if (ArrayUtil.isEmpty(bytes)) { if (ArrayUtil.isEmpty(bytes)) {
throw new ServiceException("模型不能为空,请至少设计一条主线流程!"); throw new ServiceException("模型不能为空,请至少设计一条主线流程!");
} }
} }
BpmnModel bpmnModel = WorkflowUtils.xmlToBpmnModel(xmlBytes); BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xmlBytes);
// 校验模型 // 校验模型
WorkflowUtils.checkBpmnModel(bpmnModel); ModelUtils.checkBpmnModel(bpmnModel);
List<ValidationError> validationErrors = repositoryService.validateProcess(bpmnModel); List<ValidationError> validationErrors = repositoryService.validateProcess(bpmnModel);
if (CollUtil.isNotEmpty(validationErrors)) { if (CollUtil.isNotEmpty(validationErrors)) {
String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(",")); String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(","));
@ -337,7 +302,7 @@ public class ActModelServiceImpl implements IActModelService {
Model model = repositoryService.getModel(modelId); Model model = repositoryService.getModel(modelId);
byte[] xmlBytes = repositoryService.getModelEditorSource(modelId); byte[] xmlBytes = repositoryService.getModelEditorSource(modelId);
if (ObjectUtil.isNotNull(model)) { if (ObjectUtil.isNotNull(model)) {
if (JSONUtil.isTypeJSON(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString())) && ArrayUtil.isEmpty(WorkflowUtils.bpmnJsonToXmlBytes(xmlBytes))) { if (JSONUtil.isTypeJSON(IOUtils.toString(xmlBytes, StandardCharsets.UTF_8.toString())) && ArrayUtil.isEmpty(ModelUtils.bpmnJsonToXmlBytes(xmlBytes))) {
zipName = "模型不能为空,请至少设计一条主线流程!"; zipName = "模型不能为空,请至少设计一条主线流程!";
zos.putNextEntry(new ZipEntry(zipName + ".txt")); zos.putNextEntry(new ZipEntry(zipName + ".txt"));
zos.write(zipName.getBytes(StandardCharsets.UTF_8)); zos.write(zipName.getBytes(StandardCharsets.UTF_8));
@ -371,49 +336,4 @@ public class ActModelServiceImpl implements IActModelService {
} }
} }
} }
/**
* 查询用户
*
* @param filter 参数
*/
@Override
public ResultListDataRepresentation getUsers(String filter) {
LambdaQueryWrapper<SysUser> wrapper = Wrappers.lambdaQuery();
wrapper.like(StringUtils.isNotBlank(filter), SysUser::getNickName, filter);
List<SysUser> sysUsers = sysUserMapper.selectList(wrapper);
List<UserRepresentation> userRepresentations = new ArrayList<>();
for (SysUser sysUser : sysUsers) {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setFullName(sysUser.getNickName());
userRepresentation.setLastName(sysUser.getNickName());
userRepresentation.setTenantId(sysUser.getTenantId());
userRepresentation.setEmail(sysUser.getEmail());
userRepresentation.setId(sysUser.getUserId().toString());
userRepresentations.add(userRepresentation);
}
return new ResultListDataRepresentation(userRepresentations);
}
/**
* 查询用户组
*
* @param filter 参数
*/
@Override
public ResultListDataRepresentation getGroups(String filter) {
LambdaQueryWrapper<SysRole> wrapper = Wrappers.lambdaQuery();
wrapper.like(StringUtils.isNotBlank(filter), SysRole::getRoleName, filter);
List<SysRole> sysRoles = sysRoleMapper.selectList(wrapper);
List<GroupRepresentation> result = StreamUtils.toList(sysRoles, sysRole -> {
GroupRepresentation groupRepresentation = new GroupRepresentation();
groupRepresentation.setId(sysRole.getRoleId().toString());
groupRepresentation.setName(sysRole.getRoleName());
groupRepresentation.setType(sysRole.getRoleKey());
return groupRepresentation;
});
return new ResultListDataRepresentation(result);
}
} }

View File

@ -0,0 +1,194 @@
package org.dromara.workflow.utils;
import cn.hutool.core.collection.CollUtil;
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 com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.workflow.domain.vo.MultiInstanceVo;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 模型工具
*
* @author may
*/
public class ModelUtils {
public ModelUtils() {
}
public static BpmnModel xmlToBpmnModel(String xml) throws IOException {
if (xml == null) {
throw new ServerException("xml不能为空");
}
try {
InputStream inputStream = new ByteArrayInputStream(StrUtil.utf8Bytes(xml));
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
return new BpmnXMLConverter().convertToBpmnModel(reader);
} catch (XMLStreamException e) {
throw new ServerException(e.getMessage());
}
}
/**
* 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);
}
/**
* xml转为bpmnModel
*
* @param xmlBytes xml
*/
public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException {
ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes);
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream);
return new BpmnXMLConverter().convertToBpmnModel(xtr);
}
/**
* 校验模型
*
* @param bpmnModel bpmn模型
*/
public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException {
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
checkBpmnNode(flowElements, false);
List<SubProcess> subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
if (!CollUtil.isEmpty(subProcessList)) {
for (SubProcess subProcess : subProcessList) {
Collection<FlowElement> subProcessFlowElements = subProcess.getFlowElements();
checkBpmnNode(subProcessFlowElements, true);
}
}
List<MultiInstanceVo> multiInstanceVoList = new ArrayList<>();
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask && ObjectUtil.isNotEmpty(((UserTask) flowElement).getLoopCharacteristics()) && StringUtils.isNotBlank(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem())) {
MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
multiInstanceVo.setAssigneeList(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem());
multiInstanceVoList.add(multiInstanceVo);
}
}
if (CollectionUtil.isNotEmpty(multiInstanceVoList) && multiInstanceVoList.size() > 1) {
Map<String, List<MultiInstanceVo>> assigneeListGroup = StreamUtils.groupByKey(multiInstanceVoList, MultiInstanceVo::getAssigneeList);
for (Map.Entry<String, List<MultiInstanceVo>> entry : assigneeListGroup.entrySet()) {
List<MultiInstanceVo> value = entry.getValue();
if (CollectionUtil.isNotEmpty(value) && value.size() > 1) {
String key = entry.getKey();
throw new ServerException("会签人员集合【" + key + "】重复,请重新设置集合KEY");
}
}
}
}
/**
* 校验bpmn节点是否合法
*
* @param flowElements 节点集合
* @param subtask 是否子流程
*/
private static void checkBpmnNode(Collection<FlowElement> flowElements, boolean subtask) throws ServerException {
if (CollUtil.isEmpty(flowElements)) {
throw new ServerException(subtask ? "子流程必须存在节点" : "必须存在节点!");
}
List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(startEventList)) {
throw new ServerException(subtask ? "子流程必须存在开始节点" : "必须存在开始节点!");
}
if (startEventList.size() > 1) {
throw new ServerException(subtask ? "子流程只能存在一个开始节点" : "只能存在一个开始节点!");
}
StartEvent startEvent = startEventList.get(0);
List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
if (CollUtil.isEmpty(outgoingFlows)) {
throw new ServerException(subtask ? "子流程流程节点为空,请至少设计一条主线流程!" : "流程节点为空,请至少设计一条主线流程!");
}
FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
if (!(targetFlowElement instanceof UserTask) && !subtask) {
throw new ServerException("开始节点后第一个节点必须是用户任务!");
}
List<EndEvent> endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(endEventList)) {
throw new ServerException(subtask ? "子流程必须存在结束节点!" : "必须存在结束节点!");
}
}
/**
* 获取流程全部节点
*
* @param processDefinitionId 流程定义id
*/
public static List<FlowElement> getFlowElements(String processDefinitionId) {
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
List<FlowElement> list = new ArrayList<>();
List<Process> processes = bpmnModel.getProcesses();
Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
buildFlowElements(flowElements, list);
return list;
}
/**
* 递归获取所有节点
*
* @param flowElements 节点信息
* @param list 集合
*/
private static void buildFlowElements(Collection<FlowElement> flowElements, List<FlowElement> list) {
for (FlowElement flowElement : flowElements) {
list.add(flowElement);
if (flowElement instanceof SubProcess) {
Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
buildFlowElements(subFlowElements, list);
}
}
}
}

View File

@ -66,117 +66,6 @@ public class WorkflowUtils {
private static final IWorkflowUserService I_WORKFLOW_USER_SERVICE = SpringUtils.getBean(WorkflowUserServiceImpl.class); private static final IWorkflowUserService I_WORKFLOW_USER_SERVICE = SpringUtils.getBean(WorkflowUserServiceImpl.class);
private static final IActHiProcinstService I_ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.class); private static final IActHiProcinstService I_ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.class);
/**
* 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];
}
//解决设计器选择设置流程发起人设置变量有问题
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
repairInitiatorVariable(flowElements);
// 2.将bpmnModel转为xml
return new BpmnXMLConverter().convertToXML(bpmnModel);
}
/**
* 修复发起人节点变量错误问题
*
* @param flowElements 节点
*/
private static void repairInitiatorVariable(Collection<FlowElement> flowElements) {
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof SubProcess) {
Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
repairInitiatorVariable(subFlowElements);
} else {
if (flowElement instanceof UserTask && INITIATOR_SET.equals(((UserTask) flowElement).getAssignee())) {
((UserTask) flowElement).setAssignee(INITIATOR_SET_UPDATE);
}
}
}
}
/**
* xml转为bpmnModel
*
* @param xmlBytes xml
*/
public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException {
ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes);
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream);
return new BpmnXMLConverter().convertToBpmnModel(xtr);
}
/**
* 校验模型
*
* @param bpmnModel bpmn模型
*/
public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException {
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
checkBpmnNode(flowElements, false);
List<SubProcess> subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
if (!CollUtil.isEmpty(subProcessList)) {
for (SubProcess subProcess : subProcessList) {
Collection<FlowElement> subProcessFlowElements = subProcess.getFlowElements();
checkBpmnNode(subProcessFlowElements, true);
}
}
}
/**
* 校验bpmn节点是否合法
*
* @param flowElements 节点集合
* @param subtask 是否子流程
*/
private static void checkBpmnNode(Collection<FlowElement> flowElements, boolean subtask) throws ServerException {
if (CollUtil.isEmpty(flowElements)) {
throw new ServerException(subtask ? "子流程必须存在节点" : "必须存在节点!");
}
List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(startEventList)) {
throw new ServerException(subtask ? "子流程必须存在开始节点" : "必须存在开始节点!");
}
if (startEventList.size() > 1) {
throw new ServerException(subtask ? "子流程只能存在一个开始节点" : "只能存在一个开始节点!");
}
StartEvent startEvent = startEventList.get(0);
List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
if (CollUtil.isEmpty(outgoingFlows)) {
throw new ServerException(subtask ? "子流程流程节点为空,请至少设计一条主线流程!" : "流程节点为空,请至少设计一条主线流程!");
}
FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
if (!(targetFlowElement instanceof UserTask) && !subtask) {
throw new ServerException("开始节点后第一个节点必须是用户任务!");
}
List<EndEvent> endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList());
if (CollUtil.isEmpty(endEventList)) {
throw new ServerException(subtask ? "子流程必须存在结束节点!" : "必须存在结束节点!");
}
}
/** /**
* 创建一个新任务 * 创建一个新任务
* *