Commit 44083c96c390e6d627a9bd507ebe8b9662c06c7f

Authored by 芋道源码
Committed by Gitee
2 parents 40b3c494 42eb89b2

!1276 fix: 发起流程报错,返回的VO缺少字段,icon为null时,copyTo转换异常问题

Merge pull request !1276 from SamllNorth_Lee/feature/bpm
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
@@ -44,6 +44,11 @@ public class BpmModelMetaInfoVO { @@ -44,6 +44,11 @@ public class BpmModelMetaInfoVO {
44 private Integer formType; 44 private Integer formType;
45 @Schema(description = "表单编号", example = "1024") 45 @Schema(description = "表单编号", example = "1024")
46 private Long formId; // formType 为 NORMAL 使用,必须非空 46 private Long formId; // formType 为 NORMAL 使用,必须非空
  47 +
  48 + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
  49 + private String formConf;
  50 + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
  51 + private List<String> formFields;
47 @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") 52 @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
48 private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 53 private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
49 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") 54 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java
@@ -21,6 +21,9 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @@ -21,6 +21,9 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO {
21 @NotEmpty(message = "流程名称不能为空") 21 @NotEmpty(message = "流程名称不能为空")
22 private String name; 22 private String name;
23 23
  24 + @Schema(description = "表单名字", example = "请假表单")
  25 + private String formName;
  26 +
24 @Schema(description = "流程分类", example = "1") 27 @Schema(description = "流程分类", example = "1")
25 private String category; 28 private String category;
26 29
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
@@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 6
7 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
  8 +import java.util.List;
8 9
9 @Schema(description = "管理后台 - 流程定义 Response VO") 10 @Schema(description = "管理后台 - 流程定义 Response VO")
10 @Data 11 @Data
@@ -19,7 +20,10 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @@ -19,7 +20,10 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
19 @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") 20 @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
20 private String name; 21 private String name;
21 22
22 - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") 23 + @Schema(description = "表单名字", example = "请假表单")
  24 + private String formName;
  25 +
  26 + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao")
23 private String key; 27 private String key;
24 28
25 @Schema(description = "流程分类", example = "1") 29 @Schema(description = "流程分类", example = "1")
@@ -27,14 +31,11 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @@ -27,14 +31,11 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
27 @Schema(description = "流程分类名字", example = "请假") 31 @Schema(description = "流程分类名字", example = "请假")
28 private String categoryName; 32 private String categoryName;
29 33
30 - @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")  
31 - private String modelId;  
32 -  
33 @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") 34 @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
34 private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 35 private Integer modelType; // 参见 BpmModelTypeEnum 枚举类
35 36
36 - @Schema(description = "表单名字", example = "请假表单")  
37 - private String formName; 37 + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
  38 + private String modelId;
38 39
39 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 40 @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
40 private Integer suspensionState; // 参见 SuspensionState 枚举 41 private Integer suspensionState; // 参见 SuspensionState 枚举
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -907,9 +907,16 @@ public class BpmnModelUtils { @@ -907,9 +907,16 @@ public class BpmnModelUtils {
907 * @return 符合条件的路径 907 * @return 符合条件的路径
908 */ 908 */
909 private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) { 909 private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) {
910 - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),  
911 - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())  
912 - && (evalConditionExpress(variables, flow.getConditionExpression()))); 910 + // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值,
  911 + SequenceFlow matchSequenceFlow;
  912 + if (CollUtil.isNotEmpty(variables)){
  913 + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
  914 + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
  915 + && (evalConditionExpress(variables, flow.getConditionExpression())));
  916 + }else {
  917 + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
  918 + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()));
  919 + }
913 if (matchSequenceFlow == null) { 920 if (matchSequenceFlow == null) {
914 matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), 921 matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
915 flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); 922 flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -40,9 +40,7 @@ import org.springframework.stereotype.Service; @@ -40,9 +40,7 @@ import org.springframework.stereotype.Service;
40 import org.springframework.transaction.annotation.Transactional; 40 import org.springframework.transaction.annotation.Transactional;
41 import org.springframework.validation.annotation.Validated; 41 import org.springframework.validation.annotation.Validated;
42 42
43 -import java.util.List;  
44 -import java.util.Map;  
45 -import java.util.Objects; 43 +import java.util.*;
46 44
47 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
48 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 46 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -209,11 +207,11 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -209,11 +207,11 @@ public class BpmModelServiceImpl implements BpmModelService {
209 public void deployModel(Long userId, String id) { 207 public void deployModel(Long userId, String id) {
210 // 1.1 校验流程模型存在 208 // 1.1 校验流程模型存在
211 Model model = validateModelManager(id, userId); 209 Model model = validateModelManager(id, userId);
  210 + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
212 // 1.2 校验流程图 211 // 1.2 校验流程图
213 byte[] bpmnBytes = getModelBpmnXML(model.getId()); 212 byte[] bpmnBytes = getModelBpmnXML(model.getId());
214 - validateBpmnXml(bpmnBytes); 213 + validateBpmnXml(bpmnBytes, metaInfo.getType());
215 // 1.3 校验表单已配 214 // 1.3 校验表单已配
216 - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);  
217 BpmFormDO form = validateFormConfig(metaInfo); 215 BpmFormDO form = validateFormConfig(metaInfo);
218 // 1.4 校验任务分配规则已配置 216 // 1.4 校验任务分配规则已配置
219 taskCandidateInvoker.validateBpmnConfig(bpmnBytes); 217 taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
@@ -233,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -233,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService {
233 repositoryService.saveModel(model); 231 repositoryService.saveModel(model);
234 } 232 }
235 233
236 - private void validateBpmnXml(byte[] bpmnBytes) { 234 + private void validateBpmnXml(byte[] bpmnBytes, int type) {
237 BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); 235 BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
238 if (bpmnModel == null) { 236 if (bpmnModel == null) {
239 throw exception(MODEL_NOT_EXISTS); 237 throw exception(MODEL_NOT_EXISTS);
@@ -243,24 +241,23 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -243,24 +241,23 @@ public class BpmModelServiceImpl implements BpmModelService {
243 if (startEvent == null) { 241 if (startEvent == null) {
244 throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); 242 throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS);
245 } 243 }
246 - // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况  
247 - List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();  
248 - // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。  
249 - // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务???  
250 - if (CollUtil.isNotEmpty(outgoingFlows)) {  
251 - FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();  
252 - Integer candidateStrategy = parseCandidateStrategy(targetFlowElement);  
253 - if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {  
254 - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName());  
255 - }  
256 - }  
257 - // 3. 校验 UserTask 的 name 都配置了 244 + // 2. 校验 UserTask 的 name 都配置了
258 List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); 245 List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
259 userTasks.forEach(userTask -> { 246 userTasks.forEach(userTask -> {
260 if (StrUtil.isEmpty(userTask.getName())) { 247 if (StrUtil.isEmpty(userTask.getName())) {
261 throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); 248 throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());
262 } 249 }
263 }); 250 });
  251 + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”
  252 + Map<Integer, UserTask> userTaskMap = new HashMap<>();
  253 + // BPMN 设计器,校验第一个用户任务节点
  254 + userTaskMap.put(BpmModelTypeEnum.BPMN.getType(), userTasks.get(0));
  255 + // SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点
  256 + userTaskMap.put(BpmModelTypeEnum.SIMPLE.getType(), userTasks.get(1));
  257 + Integer candidateStrategy = parseCandidateStrategy(userTaskMap.get(type));
  258 + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
  259 + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTaskMap.get(type).getName());
  260 + }
264 } 261 }
265 262
266 @Override 263 @Override
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -221,8 +221,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -221,8 +221,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
221 processDefinitionInfo, 221 processDefinitionInfo,
222 processVariables, activities); 222 processVariables, activities);
223 // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 223 // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点
224 - // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空  
225 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { 224 + if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) {
226 simulateActivityNodes.removeIf(node -> 225 simulateActivityNodes.removeIf(node ->
227 BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); 226 BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()));
228 } 227 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -558,18 +558,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -558,18 +558,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
558 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), 558 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
559 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); 559 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
560 // 2.3 调用 BPM complete 去完成任务 560 // 2.3 调用 BPM complete 去完成任务
561 - // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用  
562 - if (CollUtil.isNotEmpty(reqVO.getVariables())) {  
563 - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑  
564 - Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(),  
565 - bpmnModel, reqVO.getNextAssignees(), instance);  
566 - // 完成任务  
567 - runtimeService.setVariables(task.getProcessInstanceId(), variables);  
568 - taskService.complete(task.getId(), variables, true);  
569 - } else {  
570 - // 完成任务  
571 - taskService.complete(task.getId());  
572 - } 561 + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑
  562 + Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(),
  563 + bpmnModel, reqVO.getNextAssignees(), instance);
  564 + // 完成任务
  565 + runtimeService.setVariables(task.getProcessInstanceId(), variables);
  566 + taskService.complete(task.getId(), variables, true);
573 567
574 // 【加签专属】处理加签任务 568 // 【加签专属】处理加签任务
575 handleParentTaskIfSign(task.getParentTaskId()); 569 handleParentTaskIfSign(task.getParentTaskId());
@@ -590,38 +584,31 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -590,38 +584,31 @@ public class BpmTaskServiceImpl implements BpmTaskService {
590 */ 584 */
591 private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel, 585 private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel,
592 Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) { 586 Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) {
593 - // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人  
594 - // TODO @小北:会出现漏选,其实需要的情况哇?  
595 - if (CollUtil.isEmpty(nextAssignees)) {  
596 - return variables;  
597 - }  
598 // 1. 获取下一个将要执行的节点集合 587 // 1. 获取下一个将要执行的节点集合
599 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); 588 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
600 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); 589 List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
601 -  
602 // 2. 循环下一个将要执行的节点集合 590 // 2. 循环下一个将要执行的节点集合
603 - Map<String, List<Long>> processVariables = new HashMap<>(); 591 + Map<String, List<Long>> processVariables;
604 for (FlowNode nextFlowNode : nextFlowNodes) { 592 for (FlowNode nextFlowNode : nextFlowNodes) {
605 - if (!nextAssignees.containsKey(nextFlowNode.getId())) {  
606 - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName());  
607 - }  
608 - List<Long> assignees = nextAssignees.get(nextFlowNode.getId());  
609 - // 2.1 情况一:如果节点中的审批人策略为 发起人自选 593 + // 获取任务节点中的审批人策略
610 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); 594 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
  595 + // 2.1 情况一:如果节点中的审批人策略为 发起人自选
611 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { 596 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
612 - processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());  
613 - if (processVariables == null) {  
614 - processVariables = new HashMap<>();  
615 - }  
616 - List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId());  
617 - // 特殊:如果当前节点已经存在审批人,则不允许覆盖  
618 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) {  
619 - continue;  
620 - }  
621 // 如果节点存在,但未配置审批人 597 // 如果节点存在,但未配置审批人
  598 + List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
622 if (CollUtil.isEmpty(assignees)) { 599 if (CollUtil.isEmpty(assignees)) {
623 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 600 throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
624 } 601 }
  602 + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());
  603 + if (processVariables == null){
  604 + processVariables = new HashMap<>();
  605 + }else {
  606 + List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId());
  607 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖
  608 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) {
  609 + continue;
  610 + }
  611 + }
625 // 校验通过的全部节点和审批人 612 // 校验通过的全部节点和审批人
626 processVariables.put(nextFlowNode.getId(), assignees); 613 processVariables.put(nextFlowNode.getId(), assignees);
627 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); 614 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables);
@@ -629,12 +616,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -629,12 +616,22 @@ public class BpmTaskServiceImpl implements BpmTaskService {
629 // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 616 // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
630 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { 617 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
631 // 如果节点存在,但未配置审批人 618 // 如果节点存在,但未配置审批人
  619 + List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
632 if (CollUtil.isEmpty(assignees)) { 620 if (CollUtil.isEmpty(assignees)) {
633 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); 621 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
634 } 622 }
  623 + processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
  624 + if (processVariables == null){
  625 + processVariables = new HashMap<>();
  626 + }else {
  627 + List<Long> approveUserSelectAssignee = processVariables.get(nextFlowNode.getId());
  628 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖
  629 + if (CollUtil.isNotEmpty(approveUserSelectAssignee)) {
  630 + continue;
  631 + }
  632 + }
635 // 校验通过的全部节点和审批人 633 // 校验通过的全部节点和审批人
636 processVariables.put(nextFlowNode.getId(), assignees); 634 processVariables.put(nextFlowNode.getId(), assignees);
637 - // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖?  
638 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); 635 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables);
639 } 636 }
640 } 637 }