浏览代码

Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
lizhixian 5 月之前
父节点
当前提交
149cab1dac

+ 0 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java

@@ -45,10 +45,6 @@ public class BpmModelMetaInfoVO {
     @Schema(description = "表单编号", example = "1024")
     private Long formId; // formType 为 NORMAL 使用,必须非空
 
-    @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
-    private String formConf;
-    @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
-    private List<String> formFields;
     @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
     private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
     @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")

+ 0 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java

@@ -21,9 +21,6 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO {
     @NotEmpty(message = "流程名称不能为空")
     private String name;
 
-    @Schema(description = "表单名字", example = "请假表单")
-    private String formName;
-
     @Schema(description = "流程分类", example = "1")
     private String category;
 

+ 7 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java

@@ -20,9 +20,6 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
     @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
     private String name;
 
-    @Schema(description = "表单名字", example = "请假表单")
-    private String formName;
-
     @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao")
     private String key;
 
@@ -37,6 +34,13 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
     @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
     private String modelId;
 
+    @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String formConf;
+    @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<String> formFields;
+    @Schema(description = "表单名字", example = "请假表单")
+    private String formName;
+
     @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer suspensionState; // 参见 SuspensionState 枚举
 

+ 9 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java

@@ -907,9 +907,17 @@ public class BpmnModelUtils {
      * @return 符合条件的路径
      */
     private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map<String, Object> variables) {
-        SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
+        // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值,
+        // TODO @小北:是不是还是保证,编辑的时候,如果计算下一个节点,还是 variables 是完整体?而不是空的!!!(可以微信讨论下)
+        SequenceFlow matchSequenceFlow;
+        if (CollUtil.isNotEmpty(variables)) {
+             matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
                     flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
                             && (evalConditionExpress(variables, flow.getConditionExpression())));
+        } else {
+            matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
+                    flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()));
+        }
         if (matchSequenceFlow == null) {
             matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
                     flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java

@@ -194,7 +194,7 @@ public class FlowableUtils {
     @SuppressWarnings("unchecked")
     public static Map<String, List<Long>> getStartUserSelectAssignees(Map<String, Object> processVariables) {
         if (processVariables == null) {
-            return null;
+            return new HashMap<>();
         }
         return (Map<String, List<Long>>) processVariables.get(
                 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
@@ -219,7 +219,7 @@ public class FlowableUtils {
     @SuppressWarnings("unchecked")
     public static Map<String, List<Long>> getApproveUserSelectAssignees(Map<String, Object> processVariables) {
         if (processVariables == null) {
-            return null;
+            return new HashMap<>();
         }
         return (Map<String, List<Long>>) processVariables.get(
                 BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES);

+ 2 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -231,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService {
         repositoryService.saveModel(model);
     }
 
-    private void validateBpmnXml(byte[] bpmnBytes, int type) {
+    private void validateBpmnXml(byte[] bpmnBytes, Integer type) {
         BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
         if (bpmnModel == null) {
             throw exception(MODEL_NOT_EXISTS);
@@ -248,6 +248,7 @@ public class BpmModelServiceImpl implements BpmModelService {
                 throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());
             }
         });
+        // TODO @小北:是不是可以 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);然后,最好判空。。。极端情况下,没 usertask ,哈哈哈哈。
         // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”
         Map<Integer, UserTask> userTaskMap = new HashMap<>();
         // BPMN 设计器,校验第一个用户任务节点

+ 17 - 31
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

@@ -557,26 +557,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         // 2.2 添加评论
         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
                 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
-
-        // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空
-        // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题
-        // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值)
-        // 历史中的变量值
-        // 3.1 设置流程变量
-        Map<String, Object> processVariables = new HashMap<>();
-        // 3.2 获取历史中流程变量
-        if (CollUtil.isNotEmpty(instance.getProcessVariables())) {
-            processVariables.putAll(instance.getProcessVariables());
-        }
-        // 3.3 合并前端传递的流程变量,以前端为准
-        if (CollUtil.isNotEmpty(reqVO.getVariables())) {
-            processVariables.putAll(reqVO.getVariables());
-        }
-        // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑
-        Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,
+        // 2.3 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑
+        Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(),
                 bpmnModel, reqVO.getNextAssignees(), instance);
-        // 3.4 调用 BPM complete 去完成任务
         runtimeService.setVariables(task.getProcessInstanceId(), variables);
+        // 2.4 调用 BPM complete 去完成任务
         taskService.complete(task.getId(), variables, true);
 
         // 【加签专属】处理加签任务
@@ -601,10 +586,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         // 1. 获取下一个将要执行的节点集合
         FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
         List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
-        // 2. 循环下一个将要执行的节点集合
+
+        // 2. 校验选择的下一个节点的审批人,是否合法
         Map<String, List<Long>> processVariables;
         for (FlowNode nextFlowNode : nextFlowNodes) {
-            // 获取任务节点中的审批人策略
             Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
             // 2.1 情况一:如果节点中的审批人策略为 发起人自选
             if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
@@ -614,16 +599,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                     throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
                 }
                 processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables());
-                if (processVariables == null){
+                // 特殊:如果当前节点已经存在审批人,则不允许覆盖
+                // TODO @小北:【不用改】通过 if return,让逻辑更简洁一点;虽然会多判断一次 processVariables,但是 if else 层级更少。
+                if (processVariables != null
+                        && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) {
+                    continue;
+                }
+                // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES
+                if (processVariables == null) {
                     processVariables = new HashMap<>();
-                }else {
-                    List<Long> startUserSelectAssignee = processVariables.get(nextFlowNode.getId());
-                    // 特殊:如果当前节点已经存在审批人,则不允许覆盖
-                    if (CollUtil.isNotEmpty(startUserSelectAssignee)) {
-                        continue;
-                    }
                 }
-                // 校验通过的全部节点和审批人
                 processVariables.put(nextFlowNode.getId(), assignees);
                 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables);
             }
@@ -635,16 +620,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                     throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
                 }
                 processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
-                if (processVariables == null){
+                if (processVariables == null) {
                     processVariables = new HashMap<>();
-                }else  {
+                } else  {
                     List<Long> approveUserSelectAssignee = processVariables.get(nextFlowNode.getId());
                     // 特殊:如果当前节点已经存在审批人,则不允许覆盖
+                    // TODO @小北:这种,应该可以覆盖呢。
                     if (CollUtil.isNotEmpty(approveUserSelectAssignee)) {
                         continue;
                     }
                 }
-                // 校验通过的全部节点和审批人
+                // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES
                 processVariables.put(nextFlowNode.getId(), assignees);
                 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables);
             }