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

This commit is contained in:
YunaiV 2024-08-17 11:33:41 +08:00
parent 71e42cb0a1
commit 40db4cf7b7
4 changed files with 83 additions and 13 deletions

View File

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

View File

@ -2,11 +2,15 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; 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.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; 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.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 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.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.annotations.VisibleForTesting; 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.BpmnModel;
import org.flowable.bpmn.model.UserTask; import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -86,6 +91,8 @@ public class BpmTaskCandidateInvoker {
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
// 1.2 移除被禁用的用户 // 1.2 移除被禁用的用户
removeDisableUsers(userIds); removeDisableUsers(userIds);
// 1.3 移除发起人的用户
removeStartUserIfSkip(execution, userIds);
// 2. 校验是否有候选人 // 2. 校验是否有候选人
if (CollUtil.isEmpty(userIds)) { 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) { private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);

View File

@ -53,6 +53,10 @@ public class BpmnModelUtils {
return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); 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) { public static String parseExtensionElement(FlowElement flowElement, String elementName) {
if (flowElement == null) { if (flowElement == null) {
return null; return null;

View File

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