Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

This commit is contained in:
jason 2024-08-17 12:19:39 +08:00
commit d4306846f9
9 changed files with 46 additions and 43 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); // 转交给部门负责人审批参考飞书若部门负责人为空则自动通过 TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批参考飞书若部门负责人为空则自动通过
private final Integer type; private final Integer type;

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.bpm.enums.definition; package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@ -14,24 +13,20 @@ import java.util.Arrays;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum BpmUserTaskTimeoutHandlerType implements IntArrayValuable { public enum BpmUserTaskTimeoutHandlerTypeEnum implements IntArrayValuable {
REMINDER(1,"自动提醒"), REMINDER(1,"自动提醒"),
APPROVE(2, "自动同意"), APPROVE(2, "自动同意"),
REJECT(3, "自动拒绝"); REJECT(3, "自动拒绝");
// TODO @jasontype 是不是更合适哈 private final Integer type;
private final Integer action;
private final String name; private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskTimeoutHandlerType::getAction).toArray(); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray();
public static BpmUserTaskTimeoutHandlerType typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getAction().equals(type), values());
}
@Override @Override
public int[] array() { public int[] array() {
return ARRAYS; return ARRAYS;
} }
} }

View File

@ -22,6 +22,12 @@ public enum BpmReasonEnum {
// ========== 流程任务的独有原因 ========== // ========== 流程任务的独有原因 ==========
CANCEL_BY_SYSTEM("系统自动取消"), // 场景非常多比如说1多任务审批已经满足条件无需审批该任务2流程实例被取消无需审批该任务等等 CANCEL_BY_SYSTEM("系统自动取消"), // 场景非常多比如说1多任务审批已经满足条件无需审批该任务2流程实例被取消无需审批该任务等等
TIMEOUT_APPROVE("审批超时,系统自动通过"),
TIMEOUT_REJECT("审批超时,系统自动不通过"),
ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"),
ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"),
ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"),
ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"),
; ;
private final String reason; private final String reason;

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
@ -97,20 +98,22 @@ public class BpmSimpleModelNodeVO {
private String returnNodeId; private String returnNodeId;
} }
// TODO @芋艿参数校验
@Data @Data
@Schema(description = "审批节点超时处理策略") @Schema(description = "审批节点超时处理策略")
@Valid
public static class TimeoutHandler { public static class TimeoutHandler {
@Schema(description = "是否开启超时处理", example = "false") @Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否开启超时处理不能为空")
private Boolean enable; private Boolean enable;
// TODO @jasontype 是不是更合适哈 @Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Schema(description = "任务超时未处理的行为", example = "1") @NotNull(message = "任务超时未处理的行为不能为空")
@InEnum(BpmUserTaskTimeoutHandlerType.class) @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class)
private Integer action; private Integer type;
@Schema(description = "超时时间", example = "PT6H") @Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H")
@NotEmpty(message = "超时时间不能为空")
private String timeDuration; private String timeDuration;
@Schema(description = "最大提醒次数", example = "1") @Schema(description = "最大提醒次数", example = "1")

View File

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

View File

@ -106,10 +106,10 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
} }
// 2. 处理超时 // 2. 处理超时
String timeoutAction = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent,
BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION); BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE);
String taskKey = boundaryEvent.getAttachedToRefId(); String taskKey = boundaryEvent.getAttachedToRefId();
taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutAction)); taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType));
} }
} }

View File

@ -27,7 +27,7 @@ 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.OperationButtonSetting;
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; 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.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*;
import static org.flowable.bpmn.constants.BpmnXMLConstants.*; import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
@ -341,7 +341,7 @@ public class SimpleModelUtils {
// 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 // 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理
if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, node.getTimeoutHandler()); BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler());
flowElements.add(boundaryEvent); flowElements.add(boundaryEvent);
} }
return flowElements; return flowElements;
@ -354,7 +354,7 @@ public class SimpleModelUtils {
* @param timeoutHandler 超时处理器 * @param timeoutHandler 超时处理器
* @return BoundaryEvent 超时事件 * @return BoundaryEvent 超时事件
*/ */
private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) { private static BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) {
// 1.1 定时器边界事件 // 1.1 定时器边界事件
BoundaryEvent boundaryEvent = new BoundaryEvent(); BoundaryEvent boundaryEvent = new BoundaryEvent();
boundaryEvent.setId("Event-" + IdUtil.fastUUID()); boundaryEvent.setId("Event-" + IdUtil.fastUUID());
@ -363,7 +363,7 @@ public class SimpleModelUtils {
// 1.2 定义超时时间最大提醒次数 // 1.2 定义超时时间最大提醒次数
TimerEventDefinition eventDefinition = new TimerEventDefinition(); TimerEventDefinition eventDefinition = new TimerEventDefinition();
eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration());
if (Objects.equals(REMINDER.getAction(), timeoutHandler.getAction()) && if (Objects.equals(REMINDER.getType(), timeoutHandler.getType()) &&
timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {
eventDefinition.setTimeCycle(String.format("R%d/%s", eventDefinition.setTimeCycle(String.format("R%d/%s",
timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
@ -373,7 +373,7 @@ public class SimpleModelUtils {
// 2.1 添加定时器边界事件类型 // 2.1 添加定时器边界事件类型
addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString()); addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString());
// 2.2 添加超时执行动作元素 // 2.2 添加超时执行动作元素
addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, StrUtil.toStringOrNull(timeoutHandler.getType()));
return boundaryEvent; return boundaryEvent;
} }

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
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.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.flowable.bpmn.model.UserTask; import org.flowable.bpmn.model.UserTask;
import org.flowable.task.api.Task; import org.flowable.task.api.Task;
@ -212,8 +213,8 @@ public interface BpmTaskService {
* *
* @param processInstanceId 流程示例编号 * @param processInstanceId 流程示例编号
* @param taskDefineKey 任务 Key * @param taskDefineKey 任务 Key
* @param taskAction 处理类型 * @param handlerType 处理类型参见 {@link BpmUserTaskTimeoutHandlerTypeEnum}
*/ */
void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer taskAction); void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
} }

View File

@ -14,7 +14,7 @@ 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.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.BpmUserTaskTimeoutHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
@ -944,13 +944,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 情况一自动跳过 // 情况一自动跳过
if (ObjectUtils.equalsAny(assignStartUserHandlerType, if (ObjectUtils.equalsAny(assignStartUserHandlerType,
BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO() getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
.setId(task.getId()).setReason("审批人与提交人为同一人时,自动通过")); .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));
return; return;
} }
// 情况二转交给部门负责人审批 // 情况二转交给部门负责人审批
if (ObjectUtils.equalsAny(assignStartUserHandlerType, if (ObjectUtils.equalsAny(assignStartUserHandlerType,
BpmUserTaskAssignStartUserHandlerTypeEnum.ASSIGN_DEPT_LEADER.getType())) { BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null; DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
@ -958,15 +958,15 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 找不到部门负责人的情况下自动审批通过 // 找不到部门负责人的情况下自动审批通过
// noinspection DataFlowIssue // noinspection DataFlowIssue
if (dept.getLeaderUserId() == null) { if (dept.getLeaderUserId() == null) {
getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO() getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
.setId(task.getId()).setReason("审批人与提交人为同一人时,找不到部门负责人,自动通过")); .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));
return; return;
} }
// 找得到部门负责人的情况下修改负责人 // 找得到部门负责人的情况下修改负责人
if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
.setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
.setReason("审批人与提交人为同一人时,转交给部门负责人审批")); .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));
return; return;
} }
// 如果部门负责人是自己还是自己审批吧~ // 如果部门负责人是自己还是自己审批吧~
@ -982,7 +982,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer taskAction) { public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) {
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
if (processInstance == null) { if (processInstance == null) {
log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId); log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId);
@ -997,7 +997,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> { taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> {
// 情况一自动提醒 // 情况一自动提醒
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.REMINDER.getAction())) { if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType())) {
messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO() messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO()
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
.setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee()))); .setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee())));
@ -1005,16 +1005,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
} }
// 情况二自动同意 // 情况二自动同意
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.APPROVE.getAction())) { if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.APPROVE.getType())) {
approveTask(Long.parseLong(task.getAssignee()), approveTask(Long.parseLong(task.getAssignee()),
new BpmTaskApproveReqVO().setId(task.getId()).setReason("超时系统自动同意")); new BpmTaskApproveReqVO().setId(task.getId()).setReason(BpmReasonEnum.TIMEOUT_APPROVE.getReason()));
return; return;
} }
// 情况三自动拒绝 // 情况三自动拒绝
if (Objects.equals(taskAction, BpmUserTaskTimeoutHandlerType.REJECT.getAction())) { if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REJECT.getType())) {
rejectTask(Long.parseLong(task.getAssignee()), rejectTask(Long.parseLong(task.getAssignee()),
new BpmTaskRejectReqVO().setId(task.getId()).setReason("超时系统自动拒绝")); new BpmTaskRejectReqVO().setId(task.getId()).setReason(BpmReasonEnum.REJECT_TASK.getReason()));
} }
})); }));
} }