Browse Source

【功能新增】审批节点的审批人与发起人相同时,具体实现处理。

YunaiV 11 months ago
parent
commit
40db4cf7b7

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

@@ -15,7 +15,7 @@ public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuabl
 
     START_USER_AUDIT(1), // 由发起人对自己审批
     SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过
-    ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批
+    ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过
 
     private final Integer type;
 

+ 30 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java

@@ -2,11 +2,15 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.google.common.annotations.VisibleForTesting;
@@ -14,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.runtime.ProcessInstance;
 
 import java.util.HashMap;
 import java.util.List;
@@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker {
         Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
         // 1.2 移除被禁用的用户
         removeDisableUsers(userIds);
+        // 1.3 移除发起人的用户
+        removeStartUserIfSkip(execution, userIds);
 
         // 2. 校验是否有候选人
         if (CollUtil.isEmpty(userIds)) {
@@ -108,6 +115,29 @@ public class BpmTaskCandidateInvoker {
         });
     }
 
+    /**
+     * 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人
+     *
+     * 注意:如果只有一个候选人,则不处理,避免无法审批
+     *
+     * @param execution 执行中的任务
+     * @param assigneeUserIds 当前分配的候选人
+     */
+    @VisibleForTesting
+    void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
+        if (CollUtil.size(assigneeUserIds) <= 1) {
+            return;
+        }
+        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
+        if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
+            return;
+        }
+        ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
+                .getProcessInstance(execution.getProcessInstanceId());
+        Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
+        assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
+    }
+
     private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
         BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
         Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);

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

@@ -53,6 +53,10 @@ public class BpmnModelUtils {
         return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
     }
 
+    public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
+        return NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
+    }
+
     public static String parseExtensionElement(FlowElement flowElement, String elementName) {
         if (flowElement == null) {
             return null;

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

@@ -1,15 +1,18 @@
 package cn.iocoder.yudao.module.bpm.service.task;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.*;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 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.ObjectUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
 import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
@@ -22,6 +25,8 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import jakarta.annotation.Resource;
@@ -47,7 +52,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.support.TransactionSynchronization;
 import org.springframework.transaction.support.TransactionSynchronizationManager;
-import org.springframework.util.Assert;
 
 import java.util.*;
 import java.util.stream.Stream;
@@ -86,6 +90,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
 
     @Resource
     private AdminUserApi adminUserApi;
+    @Resource
+    private DeptApi deptApi;
 
     // ========== Query 查询相关方法 ==========
 
@@ -500,11 +506,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
 
         // 3. 根据不同的 RejectHandler 处理策略
         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
-        FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
+        FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
         // 3.1 情况一:驳回到指定的任务节点
-        BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
+        BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
         if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
-            String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
+            String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
             Assert.notNull(returnTaskId, "回退的节点不能为空");
             returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
                     .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
@@ -924,17 +930,47 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                     log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
                     return;
                 }
-                BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
-                if (bpmnModel == null) {
-                    log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());
-                    return;
-                }
 
                 // 审批人与提交人为同一人时,根据策略进行处理
                 if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
-                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
-                            .setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过"));
-                    return;
+                    BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
+                    if (bpmnModel == null) {
+                        log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());
+                        return;
+                    }
+                    FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
+                    Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
+
+                    // 情况一:自动跳过
+                    if (ObjectUtils.equalsAny(assignStartUserHandlerType,
+                            BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
+                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
+                                .setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过"));
+                        return;
+                    }
+                    // 情况二:转交给部门负责人审批
+                    if (ObjectUtils.equalsAny(assignStartUserHandlerType,
+                            BpmUserTaskAssignStartUserHandlerTypeEnum.ASSIGN_DEPT_LEADER.getType())) {
+                        AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
+                        Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
+                        DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
+                        Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId());
+                        // 找不到部门负责人的情况下,自动审批通过
+                        // noinspection DataFlowIssue
+                        if (dept.getLeaderUserId() == null) {
+                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
+                                    .setId(task.getId()).setReason("审批人与提交人为同一人时,找不到部门负责人,自动通过"));
+                            return;
+                        }
+                        // 找得到部门负责人的情况下,修改负责人
+                        if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
+                            getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
+                                    .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
+                                    .setReason("审批人与提交人为同一人时,转交给部门负责人审批"));
+                            return;
+                        }
+                        // 如果部门负责人是自己,还是自己审批吧~
+                    }
                 }
 
                 AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));