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 15  
16 16 START_USER_AUDIT(1), // 由发起人对自己审批
17 17 SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过
18   - ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批
  18 + ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过
19 19  
20 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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.ObjectUtil;
5 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.extra.spring.SpringUtil;
6 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
  10 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
8 11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
9 12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
10 14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
11 15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 16 import com.google.common.annotations.VisibleForTesting;
... ... @@ -14,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
14 18 import org.flowable.bpmn.model.BpmnModel;
15 19 import org.flowable.bpmn.model.UserTask;
16 20 import org.flowable.engine.delegate.DelegateExecution;
  21 +import org.flowable.engine.runtime.ProcessInstance;
17 22  
18 23 import java.util.HashMap;
19 24 import java.util.List;
... ... @@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker {
86 91 Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
87 92 // 1.2 移除被禁用的用户
88 93 removeDisableUsers(userIds);
  94 + // 1.3 移除发起人的用户
  95 + removeStartUserIfSkip(execution, userIds);
89 96  
90 97 // 2. 校验是否有候选人
91 98 if (CollUtil.isEmpty(userIds)) {
... ... @@ -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 141 private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
112 142 BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
113 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 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 60 public static String parseExtensionElement(FlowElement flowElement, String elementName) {
57 61 if (flowElement == null) {
58 62 return null;
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
1 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 5 import cn.hutool.core.util.*;
5 6 import cn.hutool.extra.spring.SpringUtil;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
8 9 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 11 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
10 12 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
11 13 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
12 14 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
  15 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
13 16 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
14 17 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType;
15 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 25 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
23 26 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
24 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 30 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
26 31 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
27 32 import jakarta.annotation.Resource;
... ... @@ -47,7 +52,6 @@ import org.springframework.stereotype.Service;
47 52 import org.springframework.transaction.annotation.Transactional;
48 53 import org.springframework.transaction.support.TransactionSynchronization;
49 54 import org.springframework.transaction.support.TransactionSynchronizationManager;
50   -import org.springframework.util.Assert;
51 55  
52 56 import java.util.*;
53 57 import java.util.stream.Stream;
... ... @@ -86,6 +90,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
86 90  
87 91 @Resource
88 92 private AdminUserApi adminUserApi;
  93 + @Resource
  94 + private DeptApi deptApi;
89 95  
90 96 // ========== Query 查询相关方法 ==========
91 97  
... ... @@ -500,11 +506,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
500 506  
501 507 // 3. 根据不同的 RejectHandler 处理策略
502 508 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
503   - FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
  509 + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
504 510 // 3.1 情况一:驳回到指定的任务节点
505   - BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
  511 + BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement);
506 512 if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
507   - String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
  513 + String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement);
508 514 Assert.notNull(returnTaskId, "回退的节点不能为空");
509 515 returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId())
510 516 .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
... ... @@ -924,17 +930,47 @@ public class BpmTaskServiceImpl implements BpmTaskService {
924 930 log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId());
925 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 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 976 AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
... ...