Commit 40db4cf7b7f3eda04ba7665593d2c866e2ce2604

Authored by YunaiV
1 parent 71e42cb0

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

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 @@ -15,7 +15,7 @@ public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuabl
15 15
16 START_USER_AUDIT(1), // 由发起人对自己审批 16 START_USER_AUDIT(1), // 由发起人对自己审批
17 SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 17 SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过
18 - ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批 18 + ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过
19 19
20 private final Integer type; 20 private final Integer type;
21 21
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; @@ -2,11 +2,15 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; 9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
  10 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
8 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
9 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
10 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
11 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 import com.google.common.annotations.VisibleForTesting; 16 import com.google.common.annotations.VisibleForTesting;
@@ -14,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; @@ -14,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
14 import org.flowable.bpmn.model.BpmnModel; 18 import org.flowable.bpmn.model.BpmnModel;
15 import org.flowable.bpmn.model.UserTask; 19 import org.flowable.bpmn.model.UserTask;
16 import org.flowable.engine.delegate.DelegateExecution; 20 import org.flowable.engine.delegate.DelegateExecution;
  21 +import org.flowable.engine.runtime.ProcessInstance;
17 22
18 import java.util.HashMap; 23 import java.util.HashMap;
19 import java.util.List; 24 import java.util.List;
@@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker { @@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker {
86 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); 91 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
87 // 1.2 移除被禁用的用户 92 // 1.2 移除被禁用的用户
88 removeDisableUsers(userIds); 93 removeDisableUsers(userIds);
  94 + // 1.3 移除发起人的用户
  95 + removeStartUserIfSkip(execution, userIds);
89 96
90 // 2. 校验是否有候选人 97 // 2. 校验是否有候选人
91 if (CollUtil.isEmpty(userIds)) { 98 if (CollUtil.isEmpty(userIds)) {
@@ -108,6 +115,29 @@ public class BpmTaskCandidateInvoker { @@ -108,6 +115,29 @@ public class BpmTaskCandidateInvoker {
108 }); 115 });
109 } 116 }
110 117
  118 + /**
  119 + * 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人
  120 + *
  121 + * 注意:如果只有一个候选人,则不处理,避免无法审批
  122 + *
  123 + * @param execution 执行中的任务
  124 + * @param assigneeUserIds 当前分配的候选人
  125 + */
  126 + @VisibleForTesting
  127 + void removeStartUserIfSkip(DelegateExecution execution, Set<Long> assigneeUserIds) {
  128 + if (CollUtil.size(assigneeUserIds) <= 1) {
  129 + return;
  130 + }
  131 + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(execution.getCurrentFlowElement());
  132 + if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
  133 + return;
  134 + }
  135 + ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class)
  136 + .getProcessInstance(execution.getProcessInstanceId());
  137 + Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId());
  138 + assigneeUserIds.remove(Long.valueOf(processInstance.getStartUserId()));
  139 + }
  140 +
111 private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { 141 private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
112 BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); 142 BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
113 Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); 143 Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
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 { @@ -53,6 +53,10 @@ public class BpmnModelUtils {
53 return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); 53 return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
54 } 54 }
55 55
  56 + public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
  57 + return NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
  58 + }
  59 +
56 public static String parseExtensionElement(FlowElement flowElement, String elementName) { 60 public static String parseExtensionElement(FlowElement flowElement, String elementName) {
57 if (flowElement == null) { 61 if (flowElement == null) {
58 return null; 62 return null;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
1 package cn.iocoder.yudao.module.bpm.service.task; 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.util.*; 5 import cn.hutool.core.util.*;
5 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 8 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 9 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 11 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
10 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 12 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; 13 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
12 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; 14 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
  15 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
13 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; 16 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
14 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType; 17 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
15 import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; 18 import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
@@ -22,6 +25,8 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -22,6 +25,8 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
22 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; 25 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
23 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; 26 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
24 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; 27 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
  28 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  29 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
25 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 30 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
26 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 31 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
27 import jakarta.annotation.Resource; 32 import jakarta.annotation.Resource;
@@ -47,7 +52,6 @@ import org.springframework.stereotype.Service; @@ -47,7 +52,6 @@ import org.springframework.stereotype.Service;
47 import org.springframework.transaction.annotation.Transactional; 52 import org.springframework.transaction.annotation.Transactional;
48 import org.springframework.transaction.support.TransactionSynchronization; 53 import org.springframework.transaction.support.TransactionSynchronization;
49 import org.springframework.transaction.support.TransactionSynchronizationManager; 54 import org.springframework.transaction.support.TransactionSynchronizationManager;
50 -import org.springframework.util.Assert;  
51 55
52 import java.util.*; 56 import java.util.*;
53 import java.util.stream.Stream; 57 import java.util.stream.Stream;
@@ -86,6 +90,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -86,6 +90,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
86 90
87 @Resource 91 @Resource
88 private AdminUserApi adminUserApi; 92 private AdminUserApi adminUserApi;
  93 + @Resource
  94 + private DeptApi deptApi;
89 95
90 // ========== Query 查询相关方法 ========== 96 // ========== Query 查询相关方法 ==========
91 97
@@ -500,11 +506,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -500,11 +506,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
500 506
501 // 3. 根据不同的 RejectHandler 处理策略 507 // 3. 根据不同的 RejectHandler 处理策略
502 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); 508 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
503 - FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); 509 + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
504 // 3.1 情况一:驳回到指定的任务节点 510 // 3.1 情况一:驳回到指定的任务节点
505 - BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); 511 + BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
506 if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) { 512 if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
507 - String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement); 513 + String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
508 Assert.notNull(returnTaskId, "回退的节点不能为空"); 514 Assert.notNull(returnTaskId, "回退的节点不能为空");
509 returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId()) 515 returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
510 .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason())); 516 .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
@@ -924,17 +930,47 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -924,17 +930,47 @@ public class BpmTaskServiceImpl implements BpmTaskService {
924 log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); 930 log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
925 return; 931 return;
926 } 932 }
927 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());  
928 - if (bpmnModel == null) {  
929 - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());  
930 - return;  
931 - }  
932 933
933 // 审批人与提交人为同一人时,根据策略进行处理 934 // 审批人与提交人为同一人时,根据策略进行处理
934 if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { 935 if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
935 - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()  
936 - .setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过"));  
937 - return; 936 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
  937 + if (bpmnModel == null) {
  938 + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId());
  939 + return;
  940 + }
  941 + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
  942 + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
  943 +
  944 + // 情况一:自动跳过
  945 + if (ObjectUtils.equalsAny(assignStartUserHandlerType,
  946 + BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
  947 + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
  948 + .setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过"));
  949 + return;
  950 + }
  951 + // 情况二:转交给部门负责人审批
  952 + if (ObjectUtils.equalsAny(assignStartUserHandlerType,
  953 + BpmUserTaskAssignStartUserHandlerTypeEnum.ASSIGN_DEPT_LEADER.getType())) {
  954 + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
  955 + Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
  956 + DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
  957 + Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId());
  958 + // 找不到部门负责人的情况下,自动审批通过
  959 + // noinspection DataFlowIssue
  960 + if (dept.getLeaderUserId() == null) {
  961 + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO()
  962 + .setId(task.getId()).setReason("审批人与提交人为同一人时,找不到部门负责人,自动通过"));
  963 + return;
  964 + }
  965 + // 找得到部门负责人的情况下,修改负责人
  966 + if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
  967 + getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
  968 + .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
  969 + .setReason("审批人与提交人为同一人时,转交给部门负责人审批"));
  970 + return;
  971 + }
  972 + // 如果部门负责人是自己,还是自己审批吧~
  973 + }
938 } 974 }
939 975
940 AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); 976 AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));