Commit c104191821afd7fa7cd1a1e935aaa9deb85f9f3d
1 parent
559bab57
BPM:增强 model 流程模型部署时,各种参数校验
Showing
14 changed files
with
98 additions
and
161 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| ... | ... | @@ -10,16 +10,9 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| 10 | 10 | public interface ErrorCodeConstants { |
| 11 | 11 | |
| 12 | 12 | // ========== 通用流程处理 模块 1-009-000-000 ========== |
| 13 | - ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1_009_000_002, "获取高亮流程图异常"); | |
| 14 | 13 | |
| 15 | 14 | // ========== OA 流程模块 1-009-001-000 ========== |
| 16 | 15 | ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在"); |
| 17 | - ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_002, "项目经理岗位未设置"); | |
| 18 | - ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_009, "部门的项目经理不存在"); | |
| 19 | - ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_004, "部门经理岗位未设置"); | |
| 20 | - ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在"); | |
| 21 | - ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置"); | |
| 22 | - ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1"); | |
| 23 | 16 | |
| 24 | 17 | // ========== 流程模型 1-009-002-000 ========== |
| 25 | 18 | ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); |
| ... | ... | @@ -28,7 +21,8 @@ public interface ErrorCodeConstants { |
| 28 | 21 | ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); |
| 29 | 22 | ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," + |
| 30 | 23 | "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); |
| 31 | - ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化"); | |
| 24 | + ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); | |
| 25 | + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); | |
| 32 | 26 | |
| 33 | 27 | // ========== 流程定义 1-009-003-000 ========== |
| 34 | 28 | ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.io.IoUtils; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 9 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 9 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; |
| 10 | 11 | import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; |
| 11 | 12 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| ... | ... | @@ -112,7 +113,7 @@ public class BpmModelController { |
| 112 | 113 | @Operation(summary = "导入模型") |
| 113 | 114 | @PreAuthorize("@ss.hasPermission('bpm:model:import')") |
| 114 | 115 | public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException { |
| 115 | - BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO); | |
| 116 | + BpmModelCreateReqVO createReqVO = BeanUtils.toBean(importReqVO, BpmModelCreateReqVO.class); | |
| 116 | 117 | // 读取文件 |
| 117 | 118 | String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false); |
| 118 | 119 | return success(modelService.createModel(createReqVO, bpmnXml)); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| ... | ... | @@ -104,7 +104,7 @@ public class BpmTaskController { |
| 104 | 104 | convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); |
| 105 | 105 | // 获得 Form Map |
| 106 | 106 | Map<Long, BpmFormDO> formMap = formService.getFormMap( |
| 107 | - convertSet(taskList, task -> Long.parseLong(task.getFormKey()))); | |
| 107 | + convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey()))); | |
| 108 | 108 | return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance, |
| 109 | 109 | formMap, userMap, deptMap)); |
| 110 | 110 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
| ... | ... | @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModeImportReqVO; | |
| 11 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelCreateReqVO; |
| 12 | 11 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; |
| 13 | 12 | 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 |
| 15 | 14 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 16 | 15 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 17 | 16 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; |
| 18 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; | |
| 19 | 17 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 20 | 18 | import org.flowable.engine.repository.Deployment; |
| 21 | 19 | import org.flowable.engine.repository.Model; |
| 22 | 20 | import org.flowable.engine.repository.ProcessDefinition; |
| 23 | 21 | import org.mapstruct.Mapper; |
| 24 | -import org.mapstruct.MappingTarget; | |
| 25 | 22 | import org.mapstruct.factory.Mappers; |
| 26 | 23 | |
| 27 | 24 | import java.util.List; |
| ... | ... | @@ -43,7 +40,7 @@ public interface BpmModelConvert { |
| 43 | 40 | Map<String, BpmCategoryDO> categoryMap, Map<String, Deployment> deploymentMap, |
| 44 | 41 | Map<String, ProcessDefinition> processDefinitionMap) { |
| 45 | 42 | List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getList(), model -> { |
| 46 | - BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); | |
| 43 | + BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); | |
| 47 | 44 | BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; |
| 48 | 45 | BpmCategoryDO category = categoryMap.get(model.getCategory()); |
| 49 | 46 | Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; |
| ... | ... | @@ -55,7 +52,7 @@ public interface BpmModelConvert { |
| 55 | 52 | |
| 56 | 53 | default BpmModelRespVO buildModel(Model model, |
| 57 | 54 | byte[] bpmnBytes) { |
| 58 | - BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); | |
| 55 | + BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model); | |
| 59 | 56 | BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); |
| 60 | 57 | if (ArrayUtil.isNotEmpty(bpmnBytes)) { |
| 61 | 58 | modelVO.setBpmnXml(new String(bpmnBytes)); |
| ... | ... | @@ -95,27 +92,6 @@ public interface BpmModelConvert { |
| 95 | 92 | return modelRespVO; |
| 96 | 93 | } |
| 97 | 94 | |
| 98 | - BpmModelCreateReqVO convert(BpmModeImportReqVO bean); | |
| 99 | - | |
| 100 | - default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) { | |
| 101 | - BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO(); | |
| 102 | - createReqDTO.setModelId(model.getId()); | |
| 103 | - createReqDTO.setName(model.getName()); | |
| 104 | - createReqDTO.setKey(model.getKey()); | |
| 105 | - createReqDTO.setCategory(model.getCategory()); | |
| 106 | - BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); | |
| 107 | - // metaInfo | |
| 108 | - copyTo(metaInfo, createReqDTO); | |
| 109 | - // form | |
| 110 | - if (form != null) { | |
| 111 | - createReqDTO.setFormConf(form.getConf()); | |
| 112 | - createReqDTO.setFormFields(form.getFields()); | |
| 113 | - } | |
| 114 | - return createReqDTO; | |
| 115 | - } | |
| 116 | - | |
| 117 | - void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to); | |
| 118 | - | |
| 119 | 95 | default void copyToCreateModel(Model model, BpmModelCreateReqVO bean) { |
| 120 | 96 | model.setName(bean.getName()); |
| 121 | 97 | model.setKey(bean.getKey()); |
| ... | ... | @@ -126,7 +102,7 @@ public interface BpmModelConvert { |
| 126 | 102 | default void copyToUpdateModel(Model model, BpmModelUpdateReqVO bean) { |
| 127 | 103 | model.setName(bean.getName()); |
| 128 | 104 | model.setCategory(bean.getCategory()); |
| 129 | - model.setMetaInfo(buildMetaInfoStr(JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class), | |
| 105 | + model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model), | |
| 130 | 106 | bean.getDescription(), bean.getFormType(), bean.getFormId(), |
| 131 | 107 | bean.getFormCustomCreatePath(), bean.getFormCustomViewPath())); |
| 132 | 108 | } |
| ... | ... | @@ -149,4 +125,8 @@ public interface BpmModelConvert { |
| 149 | 125 | return JsonUtils.toJsonString(metaInfo); |
| 150 | 126 | } |
| 151 | 127 | |
| 128 | + default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) { | |
| 129 | + return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); | |
| 130 | + } | |
| 131 | + | |
| 152 | 132 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
| ... | ... | @@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro |
| 10 | 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 11 | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 13 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; | |
| 14 | 13 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 15 | 14 | import org.flowable.engine.repository.Deployment; |
| 16 | 15 | import org.flowable.engine.repository.ProcessDefinition; |
| ... | ... | @@ -69,8 +68,6 @@ public interface BpmProcessDefinitionConvert { |
| 69 | 68 | }); |
| 70 | 69 | } |
| 71 | 70 | |
| 72 | - BpmProcessDefinitionInfoDO convert2(BpmProcessDefinitionCreateReqDTO bean); | |
| 73 | - | |
| 74 | 71 | @Mapping(source = "from.id", target = "to.id", ignore = true) |
| 75 | 72 | void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); |
| 76 | 73 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| ... | ... | @@ -80,7 +80,7 @@ public interface BpmTaskConvert { |
| 80 | 80 | taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); |
| 81 | 81 | taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)); |
| 82 | 82 | // 表单信息 |
| 83 | - BpmFormDO form = MapUtil.get(formMap, Long.parseLong(task.getFormKey()), BpmFormDO.class); | |
| 83 | + BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class); | |
| 84 | 84 | if (form != null) { |
| 85 | 85 | taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf()) |
| 86 | 86 | .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task)); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -71,26 +71,15 @@ public class BpmnModelUtils { |
| 71 | 71 | return result; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | - /** | |
| 75 | - * 比较 两个bpmnModel 是否相同 | |
| 76 | - * @param oldModel 老的bpmn model | |
| 77 | - * @param newModel 新的bpmn model | |
| 78 | - */ | |
| 79 | - public static boolean equals(BpmnModel oldModel, BpmnModel newModel) { | |
| 80 | - // 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较 | |
| 81 | - return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel)); | |
| 82 | - } | |
| 83 | - | |
| 84 | - /** | |
| 85 | - * 把 bpmnModel 转换成 byte[] | |
| 86 | - * @param model bpmnModel | |
| 87 | - */ | |
| 88 | - public static byte[] getBpmnBytes(BpmnModel model) { | |
| 89 | - if (model == null) { | |
| 90 | - return new byte[0]; | |
| 74 | + public static StartEvent getStartEvent(BpmnModel model) { | |
| 75 | + Process process = model.getMainProcess(); | |
| 76 | + // 从 initialFlowElement 找 | |
| 77 | + FlowElement startElement = process.getInitialFlowElement(); | |
| 78 | + if (startElement instanceof StartEvent) { | |
| 79 | + return (StartEvent) startElement; | |
| 91 | 80 | } |
| 92 | - BpmnXMLConverter converter = new BpmnXMLConverter(); | |
| 93 | - return converter.convertToXML(model); | |
| 81 | + // 从 flowElementList 找 | |
| 82 | + return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); | |
| 94 | 83 | } |
| 95 | 84 | |
| 96 | 85 | public static BpmnModel getBpmnModel(byte[] bpmnBytes) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormService.java
| ... | ... | @@ -83,12 +83,4 @@ public interface BpmFormService { |
| 83 | 83 | */ |
| 84 | 84 | PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO); |
| 85 | 85 | |
| 86 | - /** | |
| 87 | - * 校验流程表单已配置 | |
| 88 | - * | |
| 89 | - * @param configStr configStr 字段 | |
| 90 | - * @return 流程表单 | |
| 91 | - */ | |
| 92 | - BpmFormDO checkFormConfig(String configStr); | |
| 93 | - | |
| 94 | 86 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
| ... | ... | @@ -11,17 +11,14 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmFormConvert; |
| 11 | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; |
| 14 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; | |
| 15 | 14 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO; |
| 16 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; | |
| 15 | +import jakarta.annotation.Resource; | |
| 17 | 16 | import org.springframework.stereotype.Service; |
| 18 | 17 | import org.springframework.validation.annotation.Validated; |
| 19 | 18 | |
| 20 | -import jakarta.annotation.Resource; | |
| 21 | 19 | import java.util.*; |
| 22 | 20 | |
| 23 | 21 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 24 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | |
| 25 | 22 | |
| 26 | 23 | /** |
| 27 | 24 | * 动态表单 Service 实现类 |
| ... | ... | @@ -92,24 +89,6 @@ public class BpmFormServiceImpl implements BpmFormService { |
| 92 | 89 | return formMapper.selectPage(pageReqVO); |
| 93 | 90 | } |
| 94 | 91 | |
| 95 | - // TODO @芋艿:这里没搞完! | |
| 96 | - @Override | |
| 97 | - public BpmFormDO checkFormConfig(String configStr) { | |
| 98 | - BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(configStr, BpmModelMetaInfoRespDTO.class); | |
| 99 | - if (metaInfo == null || metaInfo.getFormType() == null) { | |
| 100 | - throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
| 101 | - } | |
| 102 | - // 校验表单存在 | |
| 103 | - if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { | |
| 104 | - BpmFormDO form = getForm(metaInfo.getFormId()); | |
| 105 | - if (form == null) { | |
| 106 | - throw exception(FORM_NOT_EXISTS); | |
| 107 | - } | |
| 108 | - return form; | |
| 109 | - } | |
| 110 | - return null; | |
| 111 | - } | |
| 112 | - | |
| 113 | 92 | /** |
| 114 | 93 | * 校验 Field,避免 field 重复 |
| 115 | 94 | * | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
| ... | ... | @@ -13,12 +13,14 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; |
| 15 | 15 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 16 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 16 | 17 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; |
| 17 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; | |
| 18 | 18 | import jakarta.annotation.Resource; |
| 19 | 19 | import jakarta.validation.Valid; |
| 20 | 20 | import lombok.extern.slf4j.Slf4j; |
| 21 | 21 | import org.flowable.bpmn.model.BpmnModel; |
| 22 | +import org.flowable.bpmn.model.StartEvent; | |
| 23 | +import org.flowable.bpmn.model.UserTask; | |
| 22 | 24 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 23 | 25 | import org.flowable.engine.RepositoryService; |
| 24 | 26 | import org.flowable.engine.repository.Model; |
| ... | ... | @@ -85,7 +87,9 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 85 | 87 | @Override |
| 86 | 88 | @Transactional(rollbackFor = Exception.class) |
| 87 | 89 | public String createModel(@Valid BpmModelCreateReqVO createReqVO, String bpmnXml) { |
| 88 | - validateKeyNCName(createReqVO.getKey()); | |
| 90 | + if (!ValidationUtils.isXmlNCName(createReqVO.getName())) { | |
| 91 | + throw exception(MODEL_KEY_VALID); | |
| 92 | + } | |
| 89 | 93 | // 校验流程标识已经存在 |
| 90 | 94 | Model keyModel = getModelByKey(createReqVO.getKey()); |
| 91 | 95 | if (keyModel != null) { |
| ... | ... | @@ -129,26 +133,16 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 129 | 133 | throw exception(MODEL_NOT_EXISTS); |
| 130 | 134 | } |
| 131 | 135 | // 1.2 校验流程图 |
| 132 | - // TODO 芋艿:校验流程图的有效性;例如说,是否有开始的元素,是否有结束的元素; | |
| 133 | 136 | byte[] bpmnBytes = getModelBpmnXML(model.getId()); |
| 134 | - if (bpmnBytes == null) { | |
| 135 | - throw exception(MODEL_NOT_EXISTS); | |
| 136 | - } | |
| 137 | + validateBpmnXml(bpmnBytes); | |
| 137 | 138 | // 1.3 校验表单已配 |
| 138 | - BpmFormDO form = validateFormConfig(model.getMetaInfo()); | |
| 139 | + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); | |
| 140 | + BpmFormDO form = validateFormConfig(metaInfo); | |
| 139 | 141 | // 1.4 校验任务分配规则已配置 |
| 140 | 142 | taskCandidateInvoker.validateBpmnConfig(bpmnBytes); |
| 141 | 143 | |
| 142 | - // 1.5 校验模型是否发生修改。如果未修改,则不允许创建 | |
| 143 | - BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form) | |
| 144 | - .setBpmnBytes(bpmnBytes); | |
| 145 | - // TODO @芋艿:这里比较可能有点问题 | |
| 146 | -// if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { // 流程定义的信息相等 | |
| 147 | -// throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS); | |
| 148 | -// } | |
| 149 | - | |
| 150 | 144 | // 2.1 创建流程定义 |
| 151 | - String definitionId = processDefinitionService.createProcessDefinition(definitionCreateReqDTO); | |
| 145 | + String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form); | |
| 152 | 146 | |
| 153 | 147 | // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 |
| 154 | 148 | updateProcessDefinitionSuspended(model.getDeploymentId()); |
| ... | ... | @@ -159,6 +153,25 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 159 | 153 | repositoryService.saveModel(model); |
| 160 | 154 | } |
| 161 | 155 | |
| 156 | + private void validateBpmnXml(byte[] bpmnBytes) { | |
| 157 | + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); | |
| 158 | + if (bpmnModel == null) { | |
| 159 | + throw exception(MODEL_NOT_EXISTS); | |
| 160 | + } | |
| 161 | + // 1. 没有 StartEvent | |
| 162 | + StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); | |
| 163 | + if (startEvent == null) { | |
| 164 | + throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); | |
| 165 | + } | |
| 166 | + // 2. 校验 UserTask 的 name 都配置了 | |
| 167 | + List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); | |
| 168 | + userTasks.forEach(userTask -> { | |
| 169 | + if (StrUtil.isEmpty(userTask.getName())) { | |
| 170 | + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); | |
| 171 | + } | |
| 172 | + }); | |
| 173 | + } | |
| 174 | + | |
| 162 | 175 | @Override |
| 163 | 176 | @Transactional(rollbackFor = Exception.class) |
| 164 | 177 | public void deleteModel(String id) { |
| ... | ... | @@ -195,32 +208,32 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 195 | 208 | return repositoryService.getBpmnModel(processDefinitionId); |
| 196 | 209 | } |
| 197 | 210 | |
| 198 | - private void validateKeyNCName(String key) { | |
| 199 | - if (!ValidationUtils.isXmlNCName(key)) { | |
| 200 | - throw exception(MODEL_KEY_VALID); | |
| 201 | - } | |
| 202 | - } | |
| 203 | - | |
| 204 | 211 | /** |
| 205 | 212 | * 校验流程表单已配置 |
| 206 | 213 | * |
| 207 | - * @param metaInfoStr 流程模型 metaInfo 字段 | |
| 208 | - * @return 流程表单 | |
| 214 | + * @param metaInfo 流程模型元数据 | |
| 215 | + * @return 表单配置 | |
| 209 | 216 | */ |
| 210 | - private BpmFormDO validateFormConfig(String metaInfoStr) { | |
| 211 | - BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(metaInfoStr, BpmModelMetaInfoRespDTO.class); | |
| 217 | + private BpmFormDO validateFormConfig(BpmModelMetaInfoRespDTO metaInfo) { | |
| 212 | 218 | if (metaInfo == null || metaInfo.getFormType() == null) { |
| 213 | 219 | throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); |
| 214 | 220 | } |
| 215 | 221 | // 校验表单存在 |
| 216 | 222 | if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { |
| 223 | + if (metaInfo.getFormId() == null) { | |
| 224 | + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
| 225 | + } | |
| 217 | 226 | BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); |
| 218 | 227 | if (form == null) { |
| 219 | 228 | throw exception(FORM_NOT_EXISTS); |
| 220 | 229 | } |
| 221 | 230 | return form; |
| 231 | + } else { | |
| 232 | + if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { | |
| 233 | + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); | |
| 234 | + } | |
| 235 | + return null; | |
| 222 | 236 | } |
| 223 | - return null; | |
| 224 | 237 | } |
| 225 | 238 | |
| 226 | 239 | private void saveModelBpmnXml(Model model, String bpmnXml) { |
| ... | ... | @@ -231,8 +244,11 @@ public class BpmModelServiceImpl implements BpmModelService { |
| 231 | 244 | } |
| 232 | 245 | |
| 233 | 246 | /** |
| 234 | - * 挂起 deploymentId 对应的流程定义。 这里一个deploymentId 只关联一个流程定义 | |
| 235 | - * @param deploymentId 流程发布Id. | |
| 247 | + * 挂起 deploymentId 对应的流程定义 | |
| 248 | + * | |
| 249 | + * 注意:这里一个 deploymentId 只关联一个流程定义 | |
| 250 | + * | |
| 251 | + * @param deploymentId 流程发布Id | |
| 236 | 252 | */ |
| 237 | 253 | private void updateProcessDefinitionSuspended(String deploymentId) { |
| 238 | 254 | if (StrUtil.isEmpty(deploymentId)) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
| ... | ... | @@ -2,10 +2,11 @@ package cn.iocoder.yudao.module.bpm.service.definition; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; |
| 5 | +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; | |
| 5 | 6 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 6 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; | |
| 7 | -import jakarta.validation.Valid; | |
| 7 | +import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; | |
| 8 | 8 | import org.flowable.engine.repository.Deployment; |
| 9 | +import org.flowable.engine.repository.Model; | |
| 9 | 10 | import org.flowable.engine.repository.ProcessDefinition; |
| 10 | 11 | |
| 11 | 12 | import java.util.Collection; |
| ... | ... | @@ -41,12 +42,15 @@ public interface BpmProcessDefinitionService { |
| 41 | 42 | List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState); |
| 42 | 43 | |
| 43 | 44 | /** |
| 44 | - * 创建流程定义 | |
| 45 | + * 基于流程模型,创建流程定义 | |
| 45 | 46 | * |
| 46 | - * @param createReqDTO 创建信息 | |
| 47 | + * @param model 流程模型 | |
| 48 | + * @param modelMetaInfo 流程模型元信息 | |
| 49 | + * @param bpmnBytes BPMN XML 字节数组 | |
| 50 | + * @param form 表单 | |
| 47 | 51 | * @return 流程编号 |
| 48 | 52 | */ |
| 49 | - String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO); | |
| 53 | + String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, BpmFormDO form); | |
| 50 | 54 | |
| 51 | 55 | /** |
| 52 | 56 | * 更新流程定义状态 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
| ... | ... | @@ -3,22 +3,23 @@ package cn.iocoder.yudao.module.bpm.service.definition; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 7 | 8 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; |
| 9 | -import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert; | |
| 10 | +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; | |
| 10 | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 11 | 12 | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; |
| 12 | 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 13 | -import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; | |
| 14 | +import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; | |
| 14 | 15 | import jakarta.annotation.Resource; |
| 15 | -import jakarta.validation.Valid; | |
| 16 | 16 | import lombok.extern.slf4j.Slf4j; |
| 17 | 17 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 18 | 18 | import org.flowable.bpmn.model.BpmnModel; |
| 19 | 19 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 20 | 20 | import org.flowable.engine.RepositoryService; |
| 21 | 21 | import org.flowable.engine.repository.Deployment; |
| 22 | +import org.flowable.engine.repository.Model; | |
| 22 | 23 | import org.flowable.engine.repository.ProcessDefinition; |
| 23 | 24 | import org.flowable.engine.repository.ProcessDefinitionQuery; |
| 24 | 25 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -103,11 +104,12 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | @Override |
| 106 | - public String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) { | |
| 107 | + public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, | |
| 108 | + byte[] bpmnBytes, BpmFormDO form) { | |
| 107 | 109 | // 创建 Deployment 部署 |
| 108 | 110 | Deployment deploy = repositoryService.createDeployment() |
| 109 | - .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory()) | |
| 110 | - .addBytes(createReqDTO.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) | |
| 111 | + .key(model.getKey()).name(model.getName()).category(model.getCategory()) | |
| 112 | + .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) | |
| 111 | 113 | .tenantId(TenantContextHolder.getTenantIdStr()) |
| 112 | 114 | .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 |
| 113 | 115 | .deploy(); |
| ... | ... | @@ -115,20 +117,23 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 115 | 117 | // 设置 ProcessDefinition 的 category 分类 |
| 116 | 118 | ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() |
| 117 | 119 | .deploymentId(deploy.getId()).singleResult(); |
| 118 | - repositoryService.setProcessDefinitionCategory(definition.getId(), createReqDTO.getCategory()); | |
| 120 | + repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); | |
| 119 | 121 | // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定 |
| 120 | 122 | // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 |
| 121 | 123 | // 否则,会导致 ProcessDefinition 的分页无法查询到。 |
| 122 | - if (!Objects.equals(definition.getKey(), createReqDTO.getKey())) { | |
| 123 | - throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, createReqDTO.getKey(), definition.getKey()); | |
| 124 | + if (!Objects.equals(definition.getKey(), model.getKey())) { | |
| 125 | + throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey()); | |
| 124 | 126 | } |
| 125 | - if (!Objects.equals(definition.getName(), createReqDTO.getName())) { | |
| 126 | - throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, createReqDTO.getName(), definition.getName()); | |
| 127 | + if (!Objects.equals(definition.getName(), model.getName())) { | |
| 128 | + throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName()); | |
| 127 | 129 | } |
| 128 | 130 | |
| 129 | 131 | // 插入拓展表 |
| 130 | - BpmProcessDefinitionInfoDO definitionDO = BpmProcessDefinitionConvert.INSTANCE.convert2(createReqDTO) | |
| 131 | - .setProcessDefinitionId(definition.getId()); | |
| 132 | + BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) | |
| 133 | + .setModelId(model.getId()).setProcessDefinitionId(definition.getId()); | |
| 134 | + if (form != null) { | |
| 135 | + definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); | |
| 136 | + } | |
| 132 | 137 | processDefinitionMapper.insert(definitionDO); |
| 133 | 138 | return definition.getId(); |
| 134 | 139 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.service.definition.dto; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | -import cn.hutool.core.util.StrUtil; | |
| 5 | 3 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; |
| 6 | -import lombok.Data; | |
| 7 | - | |
| 8 | -import jakarta.validation.constraints.AssertTrue; | |
| 9 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 10 | 5 | import jakarta.validation.constraints.NotNull; |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 11 | 8 | import java.util.List; |
| 12 | -import java.util.Objects; | |
| 13 | 9 | |
| 14 | 10 | /** |
| 15 | 11 | * 流程定义创建 Request DTO |
| ... | ... | @@ -82,22 +78,4 @@ public class BpmProcessDefinitionCreateReqDTO { |
| 82 | 78 | */ |
| 83 | 79 | private String formCustomViewPath; |
| 84 | 80 | |
| 85 | - @AssertTrue(message = "流程表单信息不全") | |
| 86 | - public boolean isNormalFormTypeValid() { | |
| 87 | - // 如果非业务表单,则直接通过 | |
| 88 | - if (!Objects.equals(formType, BpmModelFormTypeEnum.NORMAL.getType())) { | |
| 89 | - return true; | |
| 90 | - } | |
| 91 | - return formId != null && StrUtil.isNotEmpty(formConf) && CollUtil.isNotEmpty(formFields); | |
| 92 | - } | |
| 93 | - | |
| 94 | - @AssertTrue(message = "业务表单信息不全") | |
| 95 | - public boolean isNormalCustomTypeValid() { | |
| 96 | - // 如果非业务表单,则直接通过 | |
| 97 | - if (!Objects.equals(formType, BpmModelFormTypeEnum.CUSTOM.getType())) { | |
| 98 | - return true; | |
| 99 | - } | |
| 100 | - return StrUtil.isNotEmpty(formCustomCreatePath) && StrUtil.isNotEmpty(formCustomViewPath); | |
| 101 | - } | |
| 102 | - | |
| 103 | 81 | } | ... | ... |