Commit c104191821afd7fa7cd1a1e935aaa9deb85f9f3d

Authored by YunaiV
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
... ... @@ -7,6 +7,8 @@ import lombok.Data;
7 7 * BPM 流程 MetaInfo Response DTO
8 8 * 主要用于 { Model#setMetaInfo(String)} 的存储
9 9 *
  10 + * 最终,它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的
  11 + *
10 12 * @author 芋道源码
11 13 */
12 14 @Data
... ...
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 }
... ...