BPM:增强 model 流程模型部署时,各种参数校验

This commit is contained in:
YunaiV 2024-03-20 18:51:36 +08:00
parent 559bab571a
commit c104191821
14 changed files with 98 additions and 161 deletions

View File

@ -10,16 +10,9 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
public interface ErrorCodeConstants { public interface ErrorCodeConstants {
// ========== 通用流程处理 模块 1-009-000-000 ========== // ========== 通用流程处理 模块 1-009-000-000 ==========
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1_009_000_002, "获取高亮流程图异常");
// ========== OA 流程模块 1-009-001-000 ========== // ========== OA 流程模块 1-009-001-000 ==========
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在"); ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在");
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_002, "项目经理岗位未设置");
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_009, "部门的项目经理不存在");
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_004, "部门经理岗位未设置");
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在");
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置");
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1");
// ========== 流程模型 1-009-002-000 ========== // ========== 流程模型 1-009-002-000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程");
@ -28,7 +21,8 @@ public interface ErrorCodeConstants {
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," + ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
"原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败原因BPMN 流程图中,没有开始事件");
ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败原因BPMN 流程图中,用户任务({})的名字不存在");
// ========== 流程定义 1-009-003-000 ========== // ========== 流程定义 1-009-003-000 ==========
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.io.IoUtils; import cn.iocoder.yudao.framework.common.util.io.IoUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
@ -112,7 +113,7 @@ public class BpmModelController {
@Operation(summary = "导入模型") @Operation(summary = "导入模型")
@PreAuthorize("@ss.hasPermission('bpm:model:import')") @PreAuthorize("@ss.hasPermission('bpm:model:import')")
public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException { public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException {
BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO); BpmModelCreateReqVO createReqVO = BeanUtils.toBean(importReqVO, BpmModelCreateReqVO.class);
// 读取文件 // 读取文件
String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false); String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false);
return success(modelService.createModel(createReqVO, bpmnXml)); return success(modelService.createModel(createReqVO, bpmnXml));

View File

@ -104,7 +104,7 @@ public class BpmTaskController {
convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
// 获得 Form Map // 获得 Form Map
Map<Long, BpmFormDO> formMap = formService.getFormMap( Map<Long, BpmFormDO> formMap = formService.getFormMap(
convertSet(taskList, task -> Long.parseLong(task.getFormKey()))); convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance, return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance,
formMap, userMap, deptMap)); formMap, userMap, deptMap));
} }

View File

@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModeImportReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelUpdateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelUpdateReqVO;
@ -15,13 +14,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
@ -43,7 +40,7 @@ public interface BpmModelConvert {
Map<String, BpmCategoryDO> categoryMap, Map<String, Deployment> deploymentMap, Map<String, BpmCategoryDO> categoryMap, Map<String, Deployment> deploymentMap,
Map<String, ProcessDefinition> processDefinitionMap) { Map<String, ProcessDefinition> processDefinitionMap) {
List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getList(), model -> { List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getList(), model -> {
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
BpmCategoryDO category = categoryMap.get(model.getCategory()); BpmCategoryDO category = categoryMap.get(model.getCategory());
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
@ -55,7 +52,7 @@ public interface BpmModelConvert {
default BpmModelRespVO buildModel(Model model, default BpmModelRespVO buildModel(Model model,
byte[] bpmnBytes) { byte[] bpmnBytes) {
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
if (ArrayUtil.isNotEmpty(bpmnBytes)) { if (ArrayUtil.isNotEmpty(bpmnBytes)) {
modelVO.setBpmnXml(new String(bpmnBytes)); modelVO.setBpmnXml(new String(bpmnBytes));
@ -95,27 +92,6 @@ public interface BpmModelConvert {
return modelRespVO; return modelRespVO;
} }
BpmModelCreateReqVO convert(BpmModeImportReqVO bean);
default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) {
BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO();
createReqDTO.setModelId(model.getId());
createReqDTO.setName(model.getName());
createReqDTO.setKey(model.getKey());
createReqDTO.setCategory(model.getCategory());
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
// metaInfo
copyTo(metaInfo, createReqDTO);
// form
if (form != null) {
createReqDTO.setFormConf(form.getConf());
createReqDTO.setFormFields(form.getFields());
}
return createReqDTO;
}
void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to);
default void copyToCreateModel(Model model, BpmModelCreateReqVO bean) { default void copyToCreateModel(Model model, BpmModelCreateReqVO bean) {
model.setName(bean.getName()); model.setName(bean.getName());
model.setKey(bean.getKey()); model.setKey(bean.getKey());
@ -126,7 +102,7 @@ public interface BpmModelConvert {
default void copyToUpdateModel(Model model, BpmModelUpdateReqVO bean) { default void copyToUpdateModel(Model model, BpmModelUpdateReqVO bean) {
model.setName(bean.getName()); model.setName(bean.getName());
model.setCategory(bean.getCategory()); model.setCategory(bean.getCategory());
model.setMetaInfo(buildMetaInfoStr(JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class), model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
bean.getDescription(), bean.getFormType(), bean.getFormId(), bean.getDescription(), bean.getFormType(), bean.getFormId(),
bean.getFormCustomCreatePath(), bean.getFormCustomViewPath())); bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
} }
@ -149,4 +125,8 @@ public interface BpmModelConvert {
return JsonUtils.toJsonString(metaInfo); return JsonUtils.toJsonString(metaInfo);
} }
default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) {
return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
}
} }

View File

@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
@ -69,8 +68,6 @@ public interface BpmProcessDefinitionConvert {
}); });
} }
BpmProcessDefinitionInfoDO convert2(BpmProcessDefinitionCreateReqDTO bean);
@Mapping(source = "from.id", target = "to.id", ignore = true) @Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);

View File

@ -80,7 +80,7 @@ public interface BpmTaskConvert {
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)); taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
// 表单信息 // 表单信息
BpmFormDO form = MapUtil.get(formMap, Long.parseLong(task.getFormKey()), BpmFormDO.class); BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
if (form != null) { if (form != null) {
taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf()) taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())
.setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task)); .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));

View File

@ -71,26 +71,15 @@ public class BpmnModelUtils {
return result; return result;
} }
/** public static StartEvent getStartEvent(BpmnModel model) {
* 比较 两个bpmnModel 是否相同 Process process = model.getMainProcess();
* @param oldModel 老的bpmn model // initialFlowElement
* @param newModel 新的bpmn model FlowElement startElement = process.getInitialFlowElement();
*/ if (startElement instanceof StartEvent) {
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) { return (StartEvent) startElement;
// 由于 BpmnModel 未提供 equals 方法所以只能转成字节数组进行比较
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
}
/**
* bpmnModel 转换成 byte[]
* @param model bpmnModel
*/
public static byte[] getBpmnBytes(BpmnModel model) {
if (model == null) {
return new byte[0];
} }
BpmnXMLConverter converter = new BpmnXMLConverter(); // flowElementList
return converter.convertToXML(model); return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);
} }
public static BpmnModel getBpmnModel(byte[] bpmnBytes) { public static BpmnModel getBpmnModel(byte[] bpmnBytes) {

View File

@ -83,12 +83,4 @@ public interface BpmFormService {
*/ */
PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO); PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO);
/**
* 校验流程表单已配置
*
* @param configStr configStr 字段
* @return 流程表单
*/
BpmFormDO checkFormConfig(String configStr);
} }

View File

@ -11,17 +11,14 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmFormConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; import jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.*; import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/** /**
* 动态表单 Service 实现类 * 动态表单 Service 实现类
@ -92,24 +89,6 @@ public class BpmFormServiceImpl implements BpmFormService {
return formMapper.selectPage(pageReqVO); return formMapper.selectPage(pageReqVO);
} }
// TODO @芋艿这里没搞完
@Override
public BpmFormDO checkFormConfig(String configStr) {
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(configStr, BpmModelMetaInfoRespDTO.class);
if (metaInfo == null || metaInfo.getFormType() == null) {
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
}
// 校验表单存在
if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
BpmFormDO form = getForm(metaInfo.getFormId());
if (form == null) {
throw exception(FORM_NOT_EXISTS);
}
return form;
}
return null;
}
/** /**
* 校验 Field避免 field 重复 * 校验 Field避免 field 重复
* *

View File

@ -13,12 +13,14 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService; import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
@ -85,7 +87,9 @@ public class BpmModelServiceImpl implements BpmModelService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createModel(@Valid BpmModelCreateReqVO createReqVO, String bpmnXml) { public String createModel(@Valid BpmModelCreateReqVO createReqVO, String bpmnXml) {
validateKeyNCName(createReqVO.getKey()); if (!ValidationUtils.isXmlNCName(createReqVO.getName())) {
throw exception(MODEL_KEY_VALID);
}
// 校验流程标识已经存在 // 校验流程标识已经存在
Model keyModel = getModelByKey(createReqVO.getKey()); Model keyModel = getModelByKey(createReqVO.getKey());
if (keyModel != null) { if (keyModel != null) {
@ -129,26 +133,16 @@ public class BpmModelServiceImpl implements BpmModelService {
throw exception(MODEL_NOT_EXISTS); throw exception(MODEL_NOT_EXISTS);
} }
// 1.2 校验流程图 // 1.2 校验流程图
// TODO 芋艿校验流程图的有效性例如说是否有开始的元素是否有结束的元素
byte[] bpmnBytes = getModelBpmnXML(model.getId()); byte[] bpmnBytes = getModelBpmnXML(model.getId());
if (bpmnBytes == null) { validateBpmnXml(bpmnBytes);
throw exception(MODEL_NOT_EXISTS);
}
// 1.3 校验表单已配 // 1.3 校验表单已配
BpmFormDO form = validateFormConfig(model.getMetaInfo()); BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
BpmFormDO form = validateFormConfig(metaInfo);
// 1.4 校验任务分配规则已配置 // 1.4 校验任务分配规则已配置
taskCandidateInvoker.validateBpmnConfig(bpmnBytes); taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
// 1.5 校验模型是否发生修改如果未修改则不允许创建
BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form)
.setBpmnBytes(bpmnBytes);
// TODO @芋艿这里比较可能有点问题
// if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { // 流程定义的信息相等
// throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS);
// }
// 2.1 创建流程定义 // 2.1 创建流程定义
String definitionId = processDefinitionService.createProcessDefinition(definitionCreateReqDTO); String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form);
// 2.2 将老的流程定义进行挂起也就是说只有最新部署的流程定义才可以发起任务 // 2.2 将老的流程定义进行挂起也就是说只有最新部署的流程定义才可以发起任务
updateProcessDefinitionSuspended(model.getDeploymentId()); updateProcessDefinitionSuspended(model.getDeploymentId());
@ -159,6 +153,25 @@ public class BpmModelServiceImpl implements BpmModelService {
repositoryService.saveModel(model); repositoryService.saveModel(model);
} }
private void validateBpmnXml(byte[] bpmnBytes) {
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
if (bpmnModel == null) {
throw exception(MODEL_NOT_EXISTS);
}
// 1. 没有 StartEvent
StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);
if (startEvent == null) {
throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS);
}
// 2. 校验 UserTask name 都配置了
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
userTasks.forEach(userTask -> {
if (StrUtil.isEmpty(userTask.getName())) {
throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());
}
});
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deleteModel(String id) { public void deleteModel(String id) {
@ -195,32 +208,32 @@ public class BpmModelServiceImpl implements BpmModelService {
return repositoryService.getBpmnModel(processDefinitionId); return repositoryService.getBpmnModel(processDefinitionId);
} }
private void validateKeyNCName(String key) {
if (!ValidationUtils.isXmlNCName(key)) {
throw exception(MODEL_KEY_VALID);
}
}
/** /**
* 校验流程表单已配置 * 校验流程表单已配置
* *
* @param metaInfoStr 流程模型 metaInfo 字段 * @param metaInfo 流程模型元数据
* @return 流程表单 * @return 表单配置
*/ */
private BpmFormDO validateFormConfig(String metaInfoStr) { private BpmFormDO validateFormConfig(BpmModelMetaInfoRespDTO metaInfo) {
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(metaInfoStr, BpmModelMetaInfoRespDTO.class);
if (metaInfo == null || metaInfo.getFormType() == null) { if (metaInfo == null || metaInfo.getFormType() == null) {
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
} }
// 校验表单存在 // 校验表单存在
if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
if (metaInfo.getFormId() == null) {
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
}
BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());
if (form == null) { if (form == null) {
throw exception(FORM_NOT_EXISTS); throw exception(FORM_NOT_EXISTS);
} }
return form; return form;
} else {
if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) {
throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
}
return null;
} }
return null;
} }
private void saveModelBpmnXml(Model model, String bpmnXml) { private void saveModelBpmnXml(Model model, String bpmnXml) {
@ -231,8 +244,11 @@ public class BpmModelServiceImpl implements BpmModelService {
} }
/** /**
* 挂起 deploymentId 对应的流程定义 这里一个deploymentId 只关联一个流程定义 * 挂起 deploymentId 对应的流程定义
* @param deploymentId 流程发布Id. *
* 注意这里一个 deploymentId 只关联一个流程定义
*
* @param deploymentId 流程发布Id
*/ */
private void updateProcessDefinitionSuspended(String deploymentId) { private void updateProcessDefinitionSuspended(String deploymentId) {
if (StrUtil.isEmpty(deploymentId)) { if (StrUtil.isEmpty(deploymentId)) {

View File

@ -2,10 +2,11 @@ package cn.iocoder.yudao.module.bpm.service.definition;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
import jakarta.validation.Valid;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
import java.util.Collection; import java.util.Collection;
@ -41,12 +42,15 @@ public interface BpmProcessDefinitionService {
List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState); List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState);
/** /**
* 创建流程定义 * 基于流程模型创建流程定义
* *
* @param createReqDTO 创建信息 * @param model 流程模型
* @param modelMetaInfo 流程模型元信息
* @param bpmnBytes BPMN XML 字节数组
* @param form 表单
* @return 流程编号 * @return 流程编号
*/ */
String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO); String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, BpmFormDO form);
/** /**
* 更新流程定义状态 * 更新流程定义状态

View File

@ -3,22 +3,23 @@ package cn.iocoder.yudao.module.bpm.service.definition;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.common.engine.impl.db.SuspensionState;
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.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery; import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -103,11 +104,12 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
} }
@Override @Override
public String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) { public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo,
byte[] bpmnBytes, BpmFormDO form) {
// 创建 Deployment 部署 // 创建 Deployment 部署
Deployment deploy = repositoryService.createDeployment() Deployment deploy = repositoryService.createDeployment()
.key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory()) .key(model.getKey()).name(model.getName()).category(model.getCategory())
.addBytes(createReqDTO.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)
.tenantId(TenantContextHolder.getTenantIdStr()) .tenantId(TenantContextHolder.getTenantIdStr())
.disableSchemaValidation() // 禁用 XML Schema 验证因为有自定义的属性 .disableSchemaValidation() // 禁用 XML Schema 验证因为有自定义的属性
.deploy(); .deploy();
@ -115,20 +117,23 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
// 设置 ProcessDefinition category 分类 // 设置 ProcessDefinition category 分类
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploy.getId()).singleResult(); .deploymentId(deploy.getId()).singleResult();
repositoryService.setProcessDefinitionCategory(definition.getId(), createReqDTO.getCategory()); repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
// 注意 1ProcessDefinition key name 是通过 BPMN 中的 <bpmn2:process /> id name 决定 // 注意 1ProcessDefinition key name 是通过 BPMN 中的 <bpmn2:process /> id name 决定
// 注意 2目前该项目的设计上需要保证 ModelDeploymentProcessDefinition 使用相同的 key保证关联性 // 注意 2目前该项目的设计上需要保证 ModelDeploymentProcessDefinition 使用相同的 key保证关联性
// 否则会导致 ProcessDefinition 的分页无法查询到 // 否则会导致 ProcessDefinition 的分页无法查询到
if (!Objects.equals(definition.getKey(), createReqDTO.getKey())) { if (!Objects.equals(definition.getKey(), model.getKey())) {
throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, createReqDTO.getKey(), definition.getKey()); throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey());
} }
if (!Objects.equals(definition.getName(), createReqDTO.getName())) { if (!Objects.equals(definition.getName(), model.getName())) {
throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, createReqDTO.getName(), definition.getName()); throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName());
} }
// 插入拓展表 // 插入拓展表
BpmProcessDefinitionInfoDO definitionDO = BpmProcessDefinitionConvert.INSTANCE.convert2(createReqDTO) BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)
.setProcessDefinitionId(definition.getId()); .setModelId(model.getId()).setProcessDefinitionId(definition.getId());
if (form != null) {
definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
}
processDefinitionMapper.insert(definitionDO); processDefinitionMapper.insert(definitionDO);
return definition.getId(); return definition.getId();
} }

View File

@ -7,6 +7,8 @@ import lombok.Data;
* BPM 流程 MetaInfo Response DTO * BPM 流程 MetaInfo Response DTO
* 主要用于 { Model#setMetaInfo(String)} 的存储 * 主要用于 { Model#setMetaInfo(String)} 的存储
* *
* 最终它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的
*
* @author 芋道源码 * @author 芋道源码
*/ */
@Data @Data

View File

@ -1,15 +1,11 @@
package cn.iocoder.yudao.module.bpm.service.definition.dto; package cn.iocoder.yudao.module.bpm.service.definition.dto;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import lombok.Data;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* 流程定义创建 Request DTO * 流程定义创建 Request DTO
@ -82,22 +78,4 @@ public class BpmProcessDefinitionCreateReqDTO {
*/ */
private String formCustomViewPath; private String formCustomViewPath;
@AssertTrue(message = "流程表单信息不全")
public boolean isNormalFormTypeValid() {
// 如果非业务表单则直接通过
if (!Objects.equals(formType, BpmModelFormTypeEnum.NORMAL.getType())) {
return true;
}
return formId != null && StrUtil.isNotEmpty(formConf) && CollUtil.isNotEmpty(formFields);
}
@AssertTrue(message = "业务表单信息不全")
public boolean isNormalCustomTypeValid() {
// 如果非业务表单则直接通过
if (!Objects.equals(formType, BpmModelFormTypeEnum.CUSTOM.getType())) {
return true;
}
return StrUtil.isNotEmpty(formCustomCreatePath) && StrUtil.isNotEmpty(formCustomViewPath);
}
} }