浏览代码

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

jason 6 月之前
父节点
当前提交
1f2222cf83

+ 1 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java

@@ -25,6 +25,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
     START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定
     APPROVE_NODE(11, "审批人", "userTask"),
     COPY_NODE(12, "抄送人", "serviceTask"),
+    TRANSACTOR_NODE(13, "办理人", "userTask"),
 
     DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
     TRIGGER_NODE(15, "触发器", "serviceTask"),

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

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
 
-import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
@@ -27,8 +26,7 @@ import java.util.List;
 @Data
 public class BpmModelMetaInfoVO {
 
-    @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg")
-    @NotEmpty(message = "流程图标不能为空")
+    @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg")
     @URL(message = "流程图标格式不正确")
     private String icon;
 

+ 3 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java

@@ -85,6 +85,9 @@ public class BpmTaskRespVO {
     @Schema(description = "是否填写审批意见", example = "false")
     private Boolean reasonRequire;
 
+    @Schema(description = "节点类型", example = "10")
+    private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。
+
     @Data
     @Schema(description = "流程实例")
     public static class ProcessInstance {

+ 9 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java

@@ -1,5 +1,7 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
 
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
+
 /**
  * BPMN XML 常量信息
  *
@@ -129,4 +131,11 @@ public interface BpmnModelConstants {
      */
     String REASON_REQUIRE = "reasonRequire";
 
+    /**
+     * 节点类型
+     *
+     * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点
+     */
+    String NODE_TYPE = "nodeType";
+
 }

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

@@ -410,6 +410,26 @@ public class BpmnModelUtils {
         return parseExtensionElement(flowElement, TRIGGER_PARAM);
     }
 
+    /**
+     * 给节点添加节点类型
+     *
+     * @param nodeType 节点类型
+     * @param flowElement 节点
+     */
+    public static void addNodeType(Integer nodeType, FlowElement flowElement) {
+        addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType);
+    }
+
+    /**
+     * 解析节点类型
+     *
+     * @param flowElement 节点
+     * @return 节点类型
+     */
+    public static Integer parseNodeType(FlowElement flowElement) {
+        return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE));
+    }
+
     // ========== BPM 简单查找相关的方法 ==========
 
     /**

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

@@ -40,7 +40,7 @@ public class SimpleModelUtils {
 
     static {
         List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
-                new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(),
+                new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
                 new DelayTimerNodeConvert(), new TriggerNodeConvert(),
                 new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert());
         converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
@@ -445,6 +445,8 @@ public class SimpleModelUtils {
             addSignEnable(node.getSignEnable(), userTask);
             // 审批意见
             addReasonRequire(node.getReasonRequire(), userTask);
+            // 节点类型
+            addNodeType(node.getType(), userTask);
             return userTask;
         }
 
@@ -514,6 +516,15 @@ public class SimpleModelUtils {
 
     }
 
+    private static class TransactorNodeConvert extends ApproveNodeConvert {
+
+        @Override
+        public BpmSimpleModelNodeTypeEnum getType() {
+            return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE;
+        }
+
+    }
+
     private static class CopyNodeConvert implements NodeConvert {
 
         @Override
@@ -785,10 +796,11 @@ public class SimpleModelUtils {
         BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType());
         Assert.notNull(nodeType, "模型节点类型不支持");
 
-        // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE
+        // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE
         if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE
+                || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
             // 添加元素

+ 12 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java

@@ -28,8 +28,7 @@ import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 import static java.util.Collections.emptyList;
 
 /**
@@ -156,16 +155,25 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
 
     @Override
     public void updateProcessDefinitionState(String id, Integer state) {
+        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id);
+        if (processDefinition == null) {
+            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
+        }
+
         // 激活
         if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
-            repositoryService.activateProcessDefinitionById(id, false, null);
+            if (processDefinition.isSuspended()) {
+                repositoryService.activateProcessDefinitionById(id, false, null);
+            }
             return;
         }
         // 挂起
         if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
             // suspendProcessInstances = false,进行中的任务,不进行挂起。
             // 原因:只要新的流程不允许发起即可,老流程继续可以执行。
-            repositoryService.suspendProcessDefinitionById(id, false, null);
+            if (!processDefinition.isSuspended()) {
+                repositoryService.suspendProcessDefinitionById(id, false, null);
+            }
             return;
         }
         log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);

+ 8 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -67,6 +68,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
 import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType;
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
@@ -325,7 +327,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
             ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())
                     .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())
                             ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()
-                            : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
+                            : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别
+                                BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))
                     .setStatus(FlowableUtils.getTaskStatus(task))
                     .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
                     .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))
@@ -402,7 +405,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
             HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同
             ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId())
                     .setName(firstActivity.getActivityName())
-                    .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
+                    .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别
+                            BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()))
                     .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
                     .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
                     .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime()))
@@ -479,7 +483,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         // 1. 开始节点/审批节点
         if (ObjectUtils.equalsAny(node.getType(),
                 BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(),
-                BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) {
+                BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(),
+                BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) {
             List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(),
                     startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables);
             activityNode.setCandidateUserIds(candidateUserIds);

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

@@ -61,6 +61,7 @@ import java.util.stream.Stream;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
 
@@ -162,6 +163,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                 bpmnModel, todoTask.getTaskDefinitionKey());
         Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
         Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
+        Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey()));
 
         // 4. 任务表单
         BpmFormDO taskForm = null;
@@ -170,8 +172,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         }
 
         return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
-                .setSignEnable(signEnable)
-                .setReasonRequire(reasonRequire);
+                .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire);
     }
 
     @Override
@@ -194,6 +195,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
             return PageResult.empty();
         }
         List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
+
+        // 特殊:强制移除自动完成的“发起人”节点
+        // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤
+        tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID));
         return new PageResult<>(tasks, count);
     }