【功能新增】审批节点的审批人与发起人相同时,对应的处理类型的配置

This commit is contained in:
YunaiV 2024-08-17 10:19:20 +08:00
parent fe3ca8deba
commit 71e42cb0a1
7 changed files with 96 additions and 39 deletions

View File

@ -4,7 +4,6 @@ import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
// TODO @jason这个是不是可以去掉了哈
/**
* BPM 边界事件 (boundary event) 自定义类型枚举
*
@ -14,8 +13,7 @@ import lombok.Getter;
@AllArgsConstructor
public enum BpmBoundaryEventType {
USER_TASK_TIMEOUT(1,"用户任务超时"),
USER_TASK_REJECT_POST_PROCESS(2, "用户任务拒绝后处理");
USER_TASK_TIMEOUT(1,"用户任务超时");
private final Integer type;
private final String name;
@ -23,4 +21,5 @@ public enum BpmBoundaryEventType {
public static BpmBoundaryEventType typeOf(Integer type) {
return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values());
}
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* BPM 用户任务的审批人与发起人相同时处理类型枚举
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Getter
public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuable {
START_USER_AUDIT(1), // 由发起人对自己审批
SKIP(2), // 自动跳过参考飞书1如果当前节点还有其他审批人则交由其他审批人进行审批2如果当前节点没有其他审批人则该节点自动通过
ASSIGN_DEPT_LEADER(3); // 转交给部门负责人审批
private final Integer type;
@Override
public int[] array() {
return new int[0];
}
}

View File

@ -1,10 +1,7 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
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.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -84,6 +81,10 @@ public class BpmSimpleModelNodeVO {
*/
private TimeoutHandler timeoutHandler;
@Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1")
@InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class)
private Integer assignStartUserHandlerType;
@Data
@Schema(description = "审批节点拒绝处理策略")
public static class RejectHandler {
@ -132,14 +133,7 @@ public class BpmSimpleModelNodeVO {
}
// Map<String, Integer> formPermissions; 表单权限仅发起审批抄送节点会使用
// Integer approveMethod; 审批方式仅审批节点会使用
// TODO @jason 后面和前端一起调整一下下面的 是优先级
// TODO @芋艿 审批人的选择
// TODO @芋艿 没有人的策略
// TODO @芋艿 审批拒绝的策略
// TODO @芋艿 配置的可操作列表操作权限
// TODO @芋艿 表单的权限列表
// TODO @芋艿 超时配置要支持指定时间点指定时间间隔
// TODO @芋艿条件建议可以固化的一些选项然后有个表达式兜底要支持
}

View File

@ -176,7 +176,6 @@ public interface BpmTaskConvert {
childTask.setParentTaskId(parentTask.getId());
childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
childTask.setProcessInstanceId(parentTask.getProcessInstanceId());
// childTask.setExecutionId(parentTask.getExecutionId()); // TODO 芋艿新加的不太确定尴尬不加时子任务不通过会失败报错加了子任务审批通过会失败报错
childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId());
childTask.setPriority(parentTask.getPriority());

View File

@ -29,12 +29,17 @@ public interface BpmnModelConstants {
String BOUNDARY_EVENT_TYPE = "boundaryEventType";
// TODO @jason这个命名应该也要改哈
// TODO @jason1是不是上面的 timeoutAction 改成 timeoutHandler
/**
* BPMN ExtensionElement 的扩展属性用于标记用户任务超时执行动作
*/
String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction";
// TODO @jason1是不是上面的 timeoutAction 改成 timeoutHandler2rejectHandlerType 改成 rejectHandler
/**
* BPMN ExtensionElement 的扩展属性用于标记用户任务的审批人与发起人相同时对应的处理类型
*/
String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType";
/**
* BPMN ExtensionElement 的扩展属性用于标记用户任务拒绝处理类型
*/

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
@ -25,7 +26,6 @@ import java.util.Objects;
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting;
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerType.REMINDER;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
@ -338,8 +338,9 @@ public class SimpleModelUtils {
List<FlowElement> flowElements = new ArrayList<>();
UserTask userTask = buildBpmnUserTask(node);
flowElements.add(userTask);
// 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理
if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
// 添加用户任务的 Timer Boundary Event, 用于任务的超时处理
BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, node.getTimeoutHandler());
flowElements.add(boundaryEvent);
}
@ -347,31 +348,31 @@ public class SimpleModelUtils {
}
/**
* 添加 UserTask 用户审批的 BoundaryEvent 超时事件
* 添加 UserTask 用户的审批超时 BoundaryEvent 事件
*
* @param userTask 审批任务
* @param timeoutHandler 超时处理器
* @return
* @return BoundaryEvent 超时事件
*/
private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) {
// 定时器边界事件
// 1.1 定时器边界事件
BoundaryEvent boundaryEvent = new BoundaryEvent();
boundaryEvent.setId("Event-" + IdUtil.fastUUID());
// 设置关联的任务为不会被中断
boundaryEvent.setCancelActivity(false);
boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断
boundaryEvent.setAttachedToRef(userTask);
// 1.2 定义超时时间最大提醒次数
TimerEventDefinition eventDefinition = new TimerEventDefinition();
eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration());
if (Objects.equals(REMINDER.getAction(), timeoutHandler.getAction()) &&
timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {
// 最大提醒次数
eventDefinition.setTimeCycle(String.format("R%d/%s", timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
eventDefinition.setTimeCycle(String.format("R%d/%s",
timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
}
boundaryEvent.addEventDefinition(eventDefinition);
// 添加定时器边界事件类型
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString());
// 添加超时执行动作元素
// 2.1 添加定时器边界事件类型
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString());
// 2.2 添加超时执行动作元素
addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction()));
return boundaryEvent;
}
@ -455,8 +456,6 @@ public class SimpleModelUtils {
userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());
}
// TODO 芋艿 + jason要不要基于服务任务实现或签下的审批不通过或者说按比例审批
// TODO @jasonaddCandidateElementsprocessMultiInstanceLoopCharacteristics 建议一起搞哈
// 添加候选人元素
addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask);
@ -468,10 +467,11 @@ public class SimpleModelUtils {
processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask);
// 添加任务被拒绝的处理元素
addTaskRejectElements(node.getRejectHandler(), userTask);
// 添加用户任务的审批人与发起人相同时的处理元素
addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask);
return userTask;
}
private static void addTaskRejectElements(RejectHandler rejectHandler, UserTask userTask) {
if (rejectHandler == null) {
return;
@ -480,6 +480,13 @@ public class SimpleModelUtils {
addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());
}
private static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {
if (assignStartUserHandlerType == null) {
return;
}
addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());
}
private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
BpmApproveMethodEnum bpmApproveMethodEnum = BpmApproveMethodEnum.valueOf(approveMethod);
if (bpmApproveMethodEnum == null || bpmApproveMethodEnum == BpmApproveMethodEnum.RANDOM) {

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
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;
@ -79,7 +80,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Resource
private BpmProcessInstanceCopyService processInstanceCopyService;
@Resource
private BpmModelService bpmModelService;
private BpmModelService modelService;
@Resource
private BpmMessageService messageService;
@ -228,7 +229,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 1.1 校验当前任务 task 存在
Task task = validateTaskExist(id);
// 1.2 根据流程定义获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
if (source == null) {
throw exception(TASK_NOT_EXISTS);
@ -498,7 +499,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
// 3. 根据不同的 RejectHandler 处理策略
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
// 3.1 情况一驳回到指定的任务节点
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
@ -562,7 +563,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
*/
private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
// 1.1 获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId);
// 1.3 获取当前任务节点元素
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
// 1.3 获取跳转的节点元素
@ -690,7 +691,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
});
// 2. 终止流程
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());
BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId());
List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey));
EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
Assert.notNull(endEvent, "结束节点不能未空");
@ -915,13 +916,29 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
public void afterCommit() {
if (StrUtil.isEmpty(task.getAssignee())) {
log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId());
return;
}
ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (processInstance != null) {
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
if (processInstance == null) {
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;
}
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
}
});
@ -966,4 +983,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}));
}
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private BpmTaskServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
}