瀏覽代碼

仿钉钉流程设计- 加签拒绝处理

jason 1 年之前
父節點
當前提交
4d49952c52

+ 2 - 4
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java

@@ -13,10 +13,8 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BpmFieldPermissionEnum {
 
-    // TODO @jason:这个顺序要不要改下,和页面保持一致;只读(1)、编辑(2)、隐藏(3)
-    // @芋艿 我看钉钉页面的顺序 是 可编辑 只读 隐藏
-    WRITE(1, "可编辑"),
-    READ(2, "只读"),
+    READ(1, "只读"),
+    WRITE(2, "可编辑"),
     NONE(3, "隐藏");
 
     /**

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

@@ -22,6 +22,7 @@ public enum BpmCommentTypeEnum {
     TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"),
     ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"),
     SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"),
+    REJECT_BY_ADD_SIGN_TASK_REJECT("10", "不通过","系统自动不通过,原因是:加签任务不通过")
     ;
 
     /**

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

@@ -22,6 +22,7 @@ public enum BpmDeleteReasonEnum {
     // ========== 流程任务的独有原因 ==========
 
     CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等
+    AUTO_REJECT_BY_ADD_SIGN_REJECT("系统自动拒绝,原因:加签任务被拒绝") // 加签任务审批不通过,导致任务不通过
     ;
 
     private final String reason;

+ 14 - 19
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java

@@ -1,7 +1,5 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
 import com.google.common.collect.ImmutableSet;
@@ -11,12 +9,10 @@ import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
 import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
 import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
 import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
-import org.flowable.engine.history.HistoricActivityInstance;
 import org.flowable.task.api.Task;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -38,8 +34,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
     public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
             .add(FlowableEngineEventType.TASK_CREATED)
             .add(FlowableEngineEventType.TASK_ASSIGNED)
-            //.add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。
-//            .add(FlowableEngineEventType.ACTIVITY_MESSAGE_RECEIVED)
+//            .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。
             .add(FlowableEngineEventType.ACTIVITY_CANCELLED)
             .build();
 
@@ -59,18 +54,18 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
 
     @Override
     protected void activityCancelled(FlowableActivityCancelledEvent event) {
-        List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
-        if (CollUtil.isEmpty(activityList)) {
-            log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
-            return;
-        }
-        // 遍历处理
-        activityList.forEach(activity -> {
-            if (StrUtil.isEmpty(activity.getTaskId())) {
-                return;
-            }
-            taskService.updateTaskStatusWhenCanceled(activity.getTaskId());
-        });
+        // @芋艿。 这里是不是就可以不要了, 取消的任务状态,在rejectTask 里面做了, 如果在 updateTaskStatusWhenCanceled 里面修改会报错。
+//        List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
+//        if (CollUtil.isEmpty(activityList)) {
+//            log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
+//            return;
+//        }
+//        // 遍历处理
+//        activityList.forEach(activity -> {
+//            if (StrUtil.isEmpty(activity.getTaskId())) {
+//                return;
+//            }
+//            taskService.updateTaskStatusWhenCanceled(activity.getTaskId());
+//        });
     }
-
 }

+ 2 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java

@@ -81,7 +81,8 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe
         BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction);
         if (userTaskTimeoutAction != null) {
             // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ???
-            List<Task> taskList = bpmTaskService.getTaskListByProcessInstanceIdAndAssigned(processInstanceId, null, taskDefKey);
+            List<Task> taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true,
+                    null, taskDefKey);
             taskList.forEach(task -> {
                 // 自动提醒
                 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) {

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

@@ -136,11 +136,12 @@ public interface BpmProcessInstanceService {
     /**
      * 更新 ProcessInstance 为不通过
      *
-     * @param id     流程编号
-     * @param currentActivityId  当前的活动编号
+     * @param processInstance   流程实例
+     * @param activityIds  当前未完成活动节点 Id
+     * @param endId  结束节点 Id
      * @param reason 理由。例如说,审批不通过时,需要传递该值
      */
-    void updateProcessInstanceReject(String id, String currentActivityId,  String reason);
+    void updateProcessInstanceReject(ProcessInstance processInstance, List<String> activityIds, String endId, String reason);
 
     /**
      * 当流程结束时候,更新 ProcessInstance 为通过

文件差異過大導致無法顯示
+ 0 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java


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

@@ -129,14 +129,15 @@ public interface BpmTaskService {
      */
     Task getTask(String id);
 
-    // TODO @jason:jason:这个貌似可以去掉了。
     /**
-     * 根据条件查询已经分配的用户任务列表
+     * 根据条件查询正在进行中的任务
+     *
      * @param processInstanceId 流程实例编号,不允许为空
+     * @param assigned 是否分配了审批人
      * @param executionId execution Id
      * @param taskDefineKey 任务定义 Key
      */
-    List<Task> getTaskListByProcessInstanceIdAndAssigned(String processInstanceId, String executionId, String taskDefineKey);
+    List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String taskDefineKey);
 
     /**
      * 获取当前任务的可回退的 UserTask 集合

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

@@ -1,11 +1,9 @@
 package cn.iocoder.yudao.module.bpm.service.task;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.*;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
@@ -28,6 +26,7 @@ import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.EndEvent;
 import org.flowable.bpmn.model.FlowElement;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.HistoryService;
@@ -54,6 +53,8 @@ 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.enums.task.BpmCommentTypeEnum.REJECT_BY_ADD_SIGN_TASK_REJECT;
+import static cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum.AUTO_REJECT_BY_ADD_SIGN_REJECT;
 
 /**
  * 流程任务实例 Service 实现类
@@ -326,18 +327,40 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         if (instance == null) {
             throw exception(PROCESS_INSTANCE_NOT_EXISTS);
         }
-
-        // 2.1 更新流程实例为不通过
+        // 2.1 更新流程任务为不通过
         updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());
         // 2.2 添加评论
         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
                 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
 
-        // 3.1 解析用户任务的拒绝处理类型
+        // 3.1 如果是被加签任务且是后加签。 更新加签任务状态为取消
+        if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) {
+            List<Task> childTaskList = getTaskListByParentTaskId(task.getId());
+            updateTaskStatusWhenCanceled(childTaskList, reqVO.getReason());
+        }
+        // 3.2 如果是加签的任务
+        if (StrUtil.isNotEmpty(task.getParentTaskId())) {
+            Task signTask = validateTaskExist(task.getParentTaskId());
+            // 3.2.1 更新被加签的任务为不通过
+            if (BpmTaskSignTypeEnum.BEFORE.getType().equals(signTask.getScopeType())) {
+                updateTaskStatusAndReason(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus(), AUTO_REJECT_BY_ADD_SIGN_REJECT.getReason());
+            } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(signTask.getScopeType())) {
+                updateTaskStatus(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus());
+                // 后加签 不添加拒绝意见。因为会把原来的意见覆盖.
+            }
+            // 3.2.2 添加评论
+            taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(),
+                    BpmCommentTypeEnum.REJECT.getType(), BpmCommentTypeEnum.REJECT.formatComment(REJECT_BY_ADD_SIGN_TASK_REJECT));
+            // 3.2.3 更新还在进行中的加签任务状态为取消
+            List<Task> addSignTaskList = getTaskListByParentTaskId(task.getParentTaskId());
+            updateTaskStatusWhenCanceled(CollectionUtils.filterList(addSignTaskList, item -> !item.getId().equals(task.getId())),
+                    reqVO.getReason());
+        }
+
+        // 4.1 驳回到指定的任务节点
         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
         FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
         BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
-        // 3.2 类型为驳回到指定的任务节点
         if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
             String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
             Assert.notNull(returnTaskId, "回退的节点不能为空");
@@ -346,9 +369,25 @@ public class BpmTaskServiceImpl implements BpmTaskService {
             returnTask(userId, returnReq);
             return;
         }
-        // 3.3 其他情况 终止流程。
-        processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(),
-                task.getTaskDefinitionKey(), reqVO.getReason());
+
+        // 4.2.1 更新其它正在运行的任务状态为取消。需要过滤掉当前任务和被加签的任务
+        List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), false, null, null);
+        updateTaskStatusWhenCanceled(CollectionUtils.filterList(taskList, item -> !item.getId().equals(task.getId()) && !item.getId().equals(task.getParentTaskId())),
+                reqVO.getReason());
+        // 4.2.2 终止流程
+        List<String> activityIds = convertList(taskList, Task::getTaskDefinitionKey);
+        EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
+        Assert.notNull(endEvent, "结束节点不能未空");
+        processInstanceService.updateProcessInstanceReject(instance, activityIds, endEvent.getId(), reqVO.getReason());
+    }
+
+    private void updateTaskStatusWhenCanceled(List<Task> taskList, String reason) {
+        taskList.forEach(task -> {
+            updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason());
+            taskService.addComment(task.getId(), task.getProcessInstanceId(),
+                    BpmCommentTypeEnum.CANCEL.getType(), BpmCommentTypeEnum.CANCEL.formatComment(reason));
+
+        });
     }
 
     /**
@@ -399,6 +438,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
 
     @Override
     public void updateTaskStatusWhenCanceled(String taskId) {
+        // @芋艿。这里是不是可以不要了,要不然。  updateTaskStatusAndReason 会报错 task 已经删除了。
         Task task = getTask(taskId);
         // 1. 可能只是活动,不是任务,所以查询不到
         if (task == null) {
@@ -450,10 +490,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
     }
 
     @Override
-    public List<Task> getTaskListByProcessInstanceIdAndAssigned(String processInstanceId, String executionId, String defineKey) {
+    public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String defineKey) {
         Assert.notNull(processInstanceId, "processInstanceId 不能为空");
-        TaskQuery taskQuery = taskService.createTaskQuery().taskAssigned().processInstanceId(processInstanceId).active()
+        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active()
                 .includeTaskLocalVariables();
+        if (BooleanUtil.isTrue(assigned)) {
+            taskQuery.taskAssigned();
+        }
         if (StrUtil.isNotEmpty(executionId)) {
             taskQuery.executionId(executionId);
         }

部分文件因文件數量過多而無法顯示