Commit d2750f08ce67e6cfa2db4873bdf54bb93bcc439e
1 parent
d34fef67
仿钉钉流程设计- 审批节点添加拒绝处理方式
Showing
10 changed files
with
254 additions
and
48 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTimerBoundaryEventType.java renamed to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventType.java
| @@ -5,20 +5,21 @@ import lombok.AllArgsConstructor; | @@ -5,20 +5,21 @@ import lombok.AllArgsConstructor; | ||
| 5 | import lombok.Getter; | 5 | import lombok.Getter; |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| 8 | - * 定时器边界事件类型枚举 | 8 | + * BPM 边界事件 (boundary event) 自定义类型枚举 |
| 9 | * | 9 | * |
| 10 | * @author jason | 10 | * @author jason |
| 11 | */ | 11 | */ |
| 12 | @Getter | 12 | @Getter |
| 13 | @AllArgsConstructor | 13 | @AllArgsConstructor |
| 14 | -public enum BpmTimerBoundaryEventType { | 14 | +public enum BpmBoundaryEventType { |
| 15 | 15 | ||
| 16 | - USER_TASK_TIMEOUT(1,"用户任务超时"); | 16 | + USER_TASK_TIMEOUT(1,"用户任务超时"), |
| 17 | + USER_TASK_REJECT_POST_PROCESS(2, "用户任务拒绝后处理"); | ||
| 17 | 18 | ||
| 18 | private final Integer type; | 19 | private final Integer type; |
| 19 | private final String name; | 20 | private final String name; |
| 20 | 21 | ||
| 21 | - public static BpmTimerBoundaryEventType typeOf(Integer type) { | 22 | + public static BpmBoundaryEventType typeOf(Integer type) { |
| 22 | return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); | 23 | return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); |
| 23 | } | 24 | } |
| 24 | } | 25 | } |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerType.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.enums.definition; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Getter; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * BPM 用户任务拒绝处理类型枚举 | ||
| 9 | + * | ||
| 10 | + * @author jason | ||
| 11 | + */ | ||
| 12 | +@Getter | ||
| 13 | +@AllArgsConstructor | ||
| 14 | +public enum BpmUserTaskRejectHandlerType { | ||
| 15 | + | ||
| 16 | + TERMINATION(1, "终止流程"), | ||
| 17 | + RETURN_PRE_USER_TASK(2, "驳回到用户任务"); | ||
| 18 | + | ||
| 19 | + private final Integer type; | ||
| 20 | + private final String name; | ||
| 21 | + | ||
| 22 | + public static BpmUserTaskRejectHandlerType typeOf(Integer type) { | ||
| 23 | + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); | ||
| 24 | + } | ||
| 25 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| @@ -31,14 +31,24 @@ public interface BpmnModelConstants { | @@ -31,14 +31,24 @@ public interface BpmnModelConstants { | ||
| 31 | String USER_TASK_CANDIDATE_PARAM = "candidateParam"; | 31 | String USER_TASK_CANDIDATE_PARAM = "candidateParam"; |
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | + * BPMN ExtensionElement 的扩展属性,用于标记边界事件类型 | ||
| 35 | + */ | ||
| 36 | + String BOUNDARY_EVENT_TYPE = "boundaryEventType"; | ||
| 37 | + | ||
| 38 | + /** | ||
| 34 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 | 39 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 |
| 35 | */ | 40 | */ |
| 36 | String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction"; | 41 | String USER_TASK_TIMEOUT_HANDLER_ACTION = "timeoutAction"; |
| 37 | 42 | ||
| 38 | /** | 43 | /** |
| 39 | - * BPMN ExtensionElement 的扩展属性,用于标记定时边界事件类型 | 44 | + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 |
| 45 | + */ | ||
| 46 | + String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的回退的任务 Id | ||
| 40 | */ | 50 | */ |
| 41 | - String TIMER_BOUNDARY_EVENT_TYPE = "timerBoundaryEventType"; | 51 | + String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; |
| 42 | 52 | ||
| 43 | /** | 53 | /** |
| 44 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 | 54 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 |
| @@ -66,4 +76,5 @@ public interface BpmnModelConstants { | @@ -66,4 +76,5 @@ public interface BpmnModelConstants { | ||
| 66 | */ | 76 | */ |
| 67 | Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class); | 77 | Set<Class<? extends FlowNode>> SUPPORT_CONVERT_SIMPLE_FlOW_NODES = ImmutableSet.of(UserTask.class, EndEvent.class); |
| 68 | 78 | ||
| 79 | + String REJECT_POST_PROCESS_MESSAGE_NAME = "message_reject_post_process"; | ||
| 69 | } | 80 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
| @@ -2,23 +2,41 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | @@ -2,23 +2,41 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||
| 6 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskReturnReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | ||
| 8 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; | ||
| 9 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||
| 10 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | ||
| 11 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | ||
| 12 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | ||
| 5 | import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService; | 13 | import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService; |
| 14 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | ||
| 6 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; | 15 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; |
| 7 | import com.google.common.collect.ImmutableSet; | 16 | import com.google.common.collect.ImmutableSet; |
| 17 | +import jakarta.annotation.Resource; | ||
| 8 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
| 19 | +import org.flowable.bpmn.model.BoundaryEvent; | ||
| 20 | +import org.flowable.bpmn.model.BpmnModel; | ||
| 21 | +import org.flowable.bpmn.model.FlowElement; | ||
| 9 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; | 22 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; |
| 10 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; | 23 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; |
| 11 | import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; | 24 | import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; |
| 12 | import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; | 25 | import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; |
| 26 | +import org.flowable.engine.delegate.event.FlowableMessageEvent; | ||
| 13 | import org.flowable.engine.history.HistoricActivityInstance; | 27 | import org.flowable.engine.history.HistoricActivityInstance; |
| 14 | import org.flowable.task.api.Task; | 28 | import org.flowable.task.api.Task; |
| 15 | import org.springframework.context.annotation.Lazy; | 29 | import org.springframework.context.annotation.Lazy; |
| 16 | import org.springframework.stereotype.Component; | 30 | import org.springframework.stereotype.Component; |
| 17 | 31 | ||
| 18 | -import jakarta.annotation.Resource; | ||
| 19 | import java.util.List; | 32 | import java.util.List; |
| 33 | +import java.util.Objects; | ||
| 20 | import java.util.Set; | 34 | import java.util.Set; |
| 21 | 35 | ||
| 36 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; | ||
| 37 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_REJECT_POST_PROCESS; | ||
| 38 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseBoundaryEventExtensionElement; | ||
| 39 | + | ||
| 22 | /** | 40 | /** |
| 23 | * 监听 {@link Task} 的开始与完成 | 41 | * 监听 {@link Task} 的开始与完成 |
| 24 | * | 42 | * |
| @@ -34,15 +52,22 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | @@ -34,15 +52,22 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | ||
| 34 | @Resource | 52 | @Resource |
| 35 | @Lazy // 解决循环依赖 | 53 | @Lazy // 解决循环依赖 |
| 36 | private BpmActivityService activityService; | 54 | private BpmActivityService activityService; |
| 55 | + @Resource | ||
| 56 | + @Lazy // 解决循环依赖 | ||
| 57 | + private BpmProcessInstanceService processInstanceService; | ||
| 58 | + @Resource | ||
| 59 | + @Lazy // 延迟加载,避免循环依赖 | ||
| 60 | + private BpmModelService bpmModelService; | ||
| 37 | 61 | ||
| 38 | public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() | 62 | public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() |
| 39 | .add(FlowableEngineEventType.TASK_CREATED) | 63 | .add(FlowableEngineEventType.TASK_CREATED) |
| 40 | .add(FlowableEngineEventType.TASK_ASSIGNED) | 64 | .add(FlowableEngineEventType.TASK_ASSIGNED) |
| 41 | -// .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。 | 65 | + //.add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。 |
| 66 | + .add(FlowableEngineEventType.ACTIVITY_MESSAGE_RECEIVED) | ||
| 42 | .add(FlowableEngineEventType.ACTIVITY_CANCELLED) | 67 | .add(FlowableEngineEventType.ACTIVITY_CANCELLED) |
| 43 | .build(); | 68 | .build(); |
| 44 | 69 | ||
| 45 | - public BpmTaskEventListener(){ | 70 | + public BpmTaskEventListener() { |
| 46 | super(TASK_EVENTS); | 71 | super(TASK_EVENTS); |
| 47 | } | 72 | } |
| 48 | 73 | ||
| @@ -53,7 +78,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | @@ -53,7 +78,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | ||
| 53 | 78 | ||
| 54 | @Override | 79 | @Override |
| 55 | protected void taskAssigned(FlowableEngineEntityEvent event) { | 80 | protected void taskAssigned(FlowableEngineEntityEvent event) { |
| 56 | - taskService.updateTaskExtAssign((Task)event.getEntity()); | 81 | + taskService.updateTaskExtAssign((Task) event.getEntity()); |
| 57 | } | 82 | } |
| 58 | 83 | ||
| 59 | @Override | 84 | @Override |
| @@ -72,4 +97,47 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | @@ -72,4 +97,47 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | ||
| 72 | }); | 97 | }); |
| 73 | } | 98 | } |
| 74 | 99 | ||
| 100 | + @Override | ||
| 101 | + protected void activityMessageReceived(FlowableMessageEvent event) { | ||
| 102 | + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(event.getProcessDefinitionId()); | ||
| 103 | + FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, event.getActivityId()); | ||
| 104 | + if (element instanceof BoundaryEvent) { | ||
| 105 | + BoundaryEvent boundaryEvent = (BoundaryEvent) element; | ||
| 106 | + String boundaryEventType = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); | ||
| 107 | + // 如果自定义类型为拒绝后处理,进行拒绝处理 | ||
| 108 | + if (Objects.equals(USER_TASK_REJECT_POST_PROCESS.getType(), NumberUtils.parseInt(boundaryEventType))) { | ||
| 109 | + String rejectHandlerType = parseBoundaryEventExtensionElement((BoundaryEvent) element, BpmnModelConstants.USER_TASK_REJECT_HANDLER_TYPE); | ||
| 110 | + rejectHandler(boundaryEvent, event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), NumberUtils.parseInt(rejectHandlerType)); | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + private void rejectHandler(BoundaryEvent boundaryEvent, String processInstanceId, String taskDefineKey, Integer rejectHandlerType) { | ||
| 116 | + BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); | ||
| 117 | + if (userTaskRejectHandlerType != null) { | ||
| 118 | + List<Task> taskList = taskService.getAssignedTaskListByConditions(processInstanceId, null, taskDefineKey); | ||
| 119 | + taskList.forEach(task -> { | ||
| 120 | + Integer taskStatus = FlowableUtils.getTaskStatus(task); | ||
| 121 | + // 只有处于拒绝状态下才处理 | ||
| 122 | + if (Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), taskStatus)) { | ||
| 123 | + // 终止流程 | ||
| 124 | + if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.TERMINATION) { | ||
| 125 | + processInstanceService.updateProcessInstanceReject(task.getProcessInstanceId(), FlowableUtils.getTaskReason(task)); | ||
| 126 | + return; | ||
| 127 | + } | ||
| 128 | + // 驳回 | ||
| 129 | + if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) { | ||
| 130 | + String returnTaskId = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.USER_TASK_REJECT_RETURN_TASK_ID); | ||
| 131 | + if (returnTaskId != null) { | ||
| 132 | + BpmTaskReturnReqVO reqVO = new BpmTaskReturnReqVO().setId(task.getId()) | ||
| 133 | + .setTargetTaskDefinitionKey(returnTaskId) | ||
| 134 | + .setReason("任务拒绝回退"); | ||
| 135 | + taskService.returnTask(getLoginUserId(), reqVO); | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + }); | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + | ||
| 75 | } | 143 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java
| 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 3 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 5 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | 4 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; |
| 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; |
| 8 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTimerBoundaryEventType; | 7 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; |
| 9 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum; | 8 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum; |
| 10 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 11 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage; | 10 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage; |
| @@ -18,7 +17,6 @@ import jakarta.annotation.Resource; | @@ -18,7 +17,6 @@ import jakarta.annotation.Resource; | ||
| 18 | import lombok.extern.slf4j.Slf4j; | 17 | import lombok.extern.slf4j.Slf4j; |
| 19 | import org.flowable.bpmn.model.BoundaryEvent; | 18 | import org.flowable.bpmn.model.BoundaryEvent; |
| 20 | import org.flowable.bpmn.model.BpmnModel; | 19 | import org.flowable.bpmn.model.BpmnModel; |
| 21 | -import org.flowable.bpmn.model.ExtensionElement; | ||
| 22 | import org.flowable.bpmn.model.FlowElement; | 20 | import org.flowable.bpmn.model.FlowElement; |
| 23 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; | 21 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; |
| 24 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; | 22 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; |
| @@ -29,7 +27,6 @@ import org.springframework.context.annotation.Lazy; | @@ -29,7 +27,6 @@ import org.springframework.context.annotation.Lazy; | ||
| 29 | import org.springframework.stereotype.Component; | 27 | import org.springframework.stereotype.Component; |
| 30 | 28 | ||
| 31 | import java.util.List; | 29 | import java.util.List; |
| 32 | -import java.util.Optional; | ||
| 33 | import java.util.Set; | 30 | import java.util.Set; |
| 34 | 31 | ||
| 35 | /** | 32 | /** |
| @@ -69,23 +66,21 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe | @@ -69,23 +66,21 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe | ||
| 69 | // 如果是定时器边界事件 | 66 | // 如果是定时器边界事件 |
| 70 | if (element instanceof BoundaryEvent) { | 67 | if (element instanceof BoundaryEvent) { |
| 71 | BoundaryEvent boundaryEvent = (BoundaryEvent) element; | 68 | BoundaryEvent boundaryEvent = (BoundaryEvent) element; |
| 72 | - ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(BpmnModelConstants.TIMER_BOUNDARY_EVENT_TYPE)); | ||
| 73 | - Integer timerBoundaryEventType = NumberUtils.parseInt(Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null)); | ||
| 74 | - BpmTimerBoundaryEventType bpmTimerBoundaryEventType = BpmTimerBoundaryEventType.typeOf(timerBoundaryEventType); | 69 | + String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); |
| 70 | + BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType)); | ||
| 75 | // 类型为用户任务超时未处理的情况 | 71 | // 类型为用户任务超时未处理的情况 |
| 76 | - if (bpmTimerBoundaryEventType == BpmTimerBoundaryEventType.USER_TASK_TIMEOUT) { | ||
| 77 | - ExtensionElement timeoutActionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION)); | ||
| 78 | - Integer timeoutAction = NumberUtils.parseInt(Optional.ofNullable(timeoutActionElement).map(ExtensionElement::getElementText).orElse(null)); | ||
| 79 | - processUserTaskTimeout(event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), timeoutAction); | 72 | + if (bpmTimerBoundaryEventType == BpmBoundaryEventType.USER_TASK_TIMEOUT) { |
| 73 | + String timeoutAction = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_ACTION); | ||
| 74 | + userTaskTimeoutHandler(event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), NumberUtils.parseInt(timeoutAction)); | ||
| 80 | } | 75 | } |
| 81 | } | 76 | } |
| 82 | } | 77 | } |
| 83 | 78 | ||
| 84 | - private void processUserTaskTimeout(String processInstanceId, String taskDefKey, Integer timeoutAction) { | 79 | + private void userTaskTimeoutHandler(String processInstanceId, String taskDefKey, Integer timeoutAction) { |
| 85 | BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); | 80 | BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); |
| 86 | if (userTaskTimeoutAction != null) { | 81 | if (userTaskTimeoutAction != null) { |
| 87 | - // 查询超时未处理的任务 | ||
| 88 | - List<Task> taskList = bpmTaskService.getAssignedTaskListByConditions(processInstanceId, taskDefKey); | 82 | + // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ??? |
| 83 | + List<Task> taskList = bpmTaskService.getAssignedTaskListByConditions(processInstanceId, null, taskDefKey); | ||
| 89 | taskList.forEach(task -> { | 84 | taskList.forEach(task -> { |
| 90 | // 自动提醒 | 85 | // 自动提醒 |
| 91 | if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) { | 86 | if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) { |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/simple/SimpleModelUserTaskConfig.java
| 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.simple; | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.simple; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | ||
| 3 | import lombok.Data; | 4 | import lombok.Data; |
| 4 | 5 | ||
| 5 | import java.util.List; | 6 | import java.util.List; |
| @@ -33,12 +34,15 @@ public class SimpleModelUserTaskConfig { | @@ -33,12 +34,15 @@ public class SimpleModelUserTaskConfig { | ||
| 33 | */ | 34 | */ |
| 34 | private Integer approveMethod; | 35 | private Integer approveMethod; |
| 35 | 36 | ||
| 36 | - | ||
| 37 | /** | 37 | /** |
| 38 | * 超时处理 | 38 | * 超时处理 |
| 39 | */ | 39 | */ |
| 40 | private TimeoutHandler timeoutHandler; | 40 | private TimeoutHandler timeoutHandler; |
| 41 | 41 | ||
| 42 | + /** | ||
| 43 | + * 用户任务拒绝处理 | ||
| 44 | + */ | ||
| 45 | + private RejectHandler rejectHandler; | ||
| 42 | 46 | ||
| 43 | @Data | 47 | @Data |
| 44 | public static class TimeoutHandler { | 48 | public static class TimeoutHandler { |
| @@ -62,7 +66,20 @@ public class SimpleModelUserTaskConfig { | @@ -62,7 +66,20 @@ public class SimpleModelUserTaskConfig { | ||
| 62 | * 如果执行动作是自动提醒, 最大提醒次数 | 66 | * 如果执行动作是自动提醒, 最大提醒次数 |
| 63 | */ | 67 | */ |
| 64 | private Integer maxRemindCount; | 68 | private Integer maxRemindCount; |
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Data | ||
| 72 | + public static class RejectHandler { | ||
| 65 | 73 | ||
| 74 | + /** | ||
| 75 | + * 用户任务拒绝处理类型 {@link BpmUserTaskRejectHandlerType} | ||
| 76 | + */ | ||
| 77 | + private Integer type; | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 用户任务拒绝后驳回的节点 Id | ||
| 81 | + */ | ||
| 82 | + private String returnNodeId; | ||
| 66 | } | 83 | } |
| 67 | 84 | ||
| 68 | } | 85 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; | @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; | ||
| 5 | import cn.hutool.core.util.ArrayUtil; | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; | ||
| 8 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 9 | import org.flowable.bpmn.converter.BpmnXMLConverter; | 10 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 10 | import org.flowable.bpmn.model.Process; | 11 | import org.flowable.bpmn.model.Process; |
| @@ -360,4 +361,32 @@ public class BpmnModelUtils { | @@ -360,4 +361,32 @@ public class BpmnModelUtils { | ||
| 360 | return userTaskList; | 361 | return userTaskList; |
| 361 | } | 362 | } |
| 362 | 363 | ||
| 364 | + /** | ||
| 365 | + * 在用户任务中查找自定义的边界事件 | ||
| 366 | + * | ||
| 367 | + * @param userTask 用户任务 | ||
| 368 | + * @param bpmBoundaryEventType 自定义的边界事件类型 | ||
| 369 | + */ | ||
| 370 | + public static BoundaryEvent findCustomBoundaryEventOfUserTask(UserTask userTask, BpmBoundaryEventType bpmBoundaryEventType) { | ||
| 371 | + if (userTask == null) { | ||
| 372 | + return null; | ||
| 373 | + } | ||
| 374 | + BoundaryEvent result = null; | ||
| 375 | + for (BoundaryEvent item : userTask.getBoundaryEvents()) { | ||
| 376 | + String boundaryEventType = parseBoundaryEventExtensionElement(item, BpmnModelConstants.BOUNDARY_EVENT_TYPE); | ||
| 377 | + if (Objects.equals(bpmBoundaryEventType.getType(), NumberUtils.parseInt(boundaryEventType))) { | ||
| 378 | + result = item; | ||
| 379 | + break; | ||
| 380 | + } | ||
| 381 | + } | ||
| 382 | + return result; | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | + public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) { | ||
| 386 | + if (boundaryEvent == null) { | ||
| 387 | + return null; | ||
| 388 | + } | ||
| 389 | + ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement)); | ||
| 390 | + return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null); | ||
| 391 | + } | ||
| 363 | } | 392 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; | ||
| 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 15 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; | 15 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; |
| 16 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig; | 16 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig; |
| 17 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelUserTaskConfig.RejectHandler; | ||
| 17 | import org.flowable.bpmn.BpmnAutoLayout; | 18 | import org.flowable.bpmn.BpmnAutoLayout; |
| 18 | import org.flowable.bpmn.model.Process; | 19 | import org.flowable.bpmn.model.Process; |
| 19 | import org.flowable.bpmn.model.*; | 20 | import org.flowable.bpmn.model.*; |
| @@ -22,13 +23,14 @@ import java.util.List; | @@ -22,13 +23,14 @@ import java.util.List; | ||
| 22 | import java.util.Map; | 23 | import java.util.Map; |
| 23 | import java.util.Objects; | 24 | import java.util.Objects; |
| 24 | 25 | ||
| 26 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_REJECT_POST_PROCESS; | ||
| 27 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT; | ||
| 25 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.END_EVENT; | 28 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.END_EVENT; |
| 26 | -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTimerBoundaryEventType.USER_TASK_TIMEOUT; | 29 | +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK; |
| 27 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; | 30 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; |
| 28 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | 31 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 29 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; | 32 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*; |
| 30 | -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; | ||
| 31 | -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; | 33 | +import static org.flowable.bpmn.constants.BpmnXMLConstants.*; |
| 32 | 34 | ||
| 33 | /** | 35 | /** |
| 34 | * 仿钉钉快搭模型相关的工具方法 | 36 | * 仿钉钉快搭模型相关的工具方法 |
| @@ -42,12 +44,12 @@ public class SimpleModelUtils { | @@ -42,12 +44,12 @@ public class SimpleModelUtils { | ||
| 42 | /** | 44 | /** |
| 43 | * 所有审批人同意的表达式 | 45 | * 所有审批人同意的表达式 |
| 44 | */ | 46 | */ |
| 45 | - public static final String ALL_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances >= 0 }"; | 47 | + public static final String ALL_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances >= nrOfInstances }"; |
| 46 | 48 | ||
| 47 | /** | 49 | /** |
| 48 | * 任一一名审批人同意的表达式 | 50 | * 任一一名审批人同意的表达式 |
| 49 | */ | 51 | */ |
| 50 | - public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances >= nrOfInstances }"; | 52 | + public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }"; |
| 51 | 53 | ||
| 52 | /** | 54 | /** |
| 53 | * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善) | 55 | * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善) |
| @@ -59,6 +61,12 @@ public class SimpleModelUtils { | @@ -59,6 +61,12 @@ public class SimpleModelUtils { | ||
| 59 | */ | 61 | */ |
| 60 | public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { | 62 | public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { |
| 61 | BpmnModel bpmnModel = new BpmnModel(); | 63 | BpmnModel bpmnModel = new BpmnModel(); |
| 64 | + // 不加这个 解析 Message 会报 NPE 异常 | ||
| 65 | + bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); | ||
| 66 | + Message rejectPostProcessMsg = new Message(); | ||
| 67 | + rejectPostProcessMsg.setName(REJECT_POST_PROCESS_MESSAGE_NAME); | ||
| 68 | + bpmnModel.addMessage(rejectPostProcessMsg); | ||
| 69 | + | ||
| 62 | Process mainProcess = new Process(); | 70 | Process mainProcess = new Process(); |
| 63 | mainProcess.setId(processId); | 71 | mainProcess.setId(processId); |
| 64 | mainProcess.setName(processName); | 72 | mainProcess.setName(processName); |
| @@ -214,6 +222,12 @@ public class SimpleModelUtils { | @@ -214,6 +222,12 @@ public class SimpleModelUtils { | ||
| 214 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); | 222 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); |
| 215 | mainProcess.addFlowElement(boundaryEvent); | 223 | mainProcess.addFlowElement(boundaryEvent); |
| 216 | } | 224 | } |
| 225 | + if (userTaskConfig.getRejectHandler() != null) { | ||
| 226 | + // 添加用户任务拒绝 Message Boundary Event, 用于任务的拒绝处理 | ||
| 227 | + BoundaryEvent boundaryEvent = buildUserTaskRejectBoundaryEvent(userTask, userTaskConfig.getRejectHandler()); | ||
| 228 | + mainProcess.addFlowElement(boundaryEvent); | ||
| 229 | + } | ||
| 230 | + | ||
| 217 | break; | 231 | break; |
| 218 | } | 232 | } |
| 219 | case COPY_TASK: { | 233 | case COPY_TASK: { |
| @@ -270,10 +284,27 @@ public class SimpleModelUtils { | @@ -270,10 +284,27 @@ public class SimpleModelUtils { | ||
| 270 | } | 284 | } |
| 271 | } | 285 | } |
| 272 | 286 | ||
| 287 | + private static BoundaryEvent buildUserTaskRejectBoundaryEvent(UserTask userTask, RejectHandler rejectHandler) { | ||
| 288 | + BoundaryEvent messageBoundaryEvent = new BoundaryEvent(); | ||
| 289 | + messageBoundaryEvent.setId("Event-" + IdUtil.fastUUID()); | ||
| 290 | + // 设置关联的任务为不会被中断 | ||
| 291 | + messageBoundaryEvent.setCancelActivity(false); | ||
| 292 | + messageBoundaryEvent.setAttachedToRef(userTask); | ||
| 293 | + MessageEventDefinition messageEventDefinition = new MessageEventDefinition(); | ||
| 294 | + messageEventDefinition.setMessageRef(REJECT_POST_PROCESS_MESSAGE_NAME); | ||
| 295 | + messageBoundaryEvent.addEventDefinition(messageEventDefinition); | ||
| 296 | + addExtensionElement(messageBoundaryEvent, BOUNDARY_EVENT_TYPE, USER_TASK_REJECT_POST_PROCESS.getType().toString()); | ||
| 297 | + addExtensionElement(messageBoundaryEvent, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType())); | ||
| 298 | + if (Objects.equals(rejectHandler.getType(), RETURN_PRE_USER_TASK.getType())) { | ||
| 299 | + addExtensionElement(messageBoundaryEvent, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); | ||
| 300 | + } | ||
| 301 | + return messageBoundaryEvent; | ||
| 302 | + } | ||
| 303 | + | ||
| 273 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { | 304 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { |
| 274 | // 定时器边界事件 | 305 | // 定时器边界事件 |
| 275 | BoundaryEvent boundaryEvent = new BoundaryEvent(); | 306 | BoundaryEvent boundaryEvent = new BoundaryEvent(); |
| 276 | - boundaryEvent.setId(IdUtil.fastUUID()); | 307 | + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); |
| 277 | // 设置关联的任务为不会被中断 | 308 | // 设置关联的任务为不会被中断 |
| 278 | boundaryEvent.setCancelActivity(false); | 309 | boundaryEvent.setCancelActivity(false); |
| 279 | boundaryEvent.setAttachedToRef(userTask); | 310 | boundaryEvent.setAttachedToRef(userTask); |
| @@ -286,7 +317,7 @@ public class SimpleModelUtils { | @@ -286,7 +317,7 @@ public class SimpleModelUtils { | ||
| 286 | } | 317 | } |
| 287 | boundaryEvent.addEventDefinition(eventDefinition); | 318 | boundaryEvent.addEventDefinition(eventDefinition); |
| 288 | // 添加定时器边界事件类型 | 319 | // 添加定时器边界事件类型 |
| 289 | - addExtensionElement(boundaryEvent, TIMER_BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString()); | 320 | + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, USER_TASK_TIMEOUT.getType().toString()); |
| 290 | // 添加超时执行动作元素 | 321 | // 添加超时执行动作元素 |
| 291 | addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); | 322 | addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); |
| 292 | return boundaryEvent; | 323 | return boundaryEvent; |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| @@ -90,6 +90,8 @@ public interface BpmTaskService { | @@ -90,6 +90,8 @@ public interface BpmTaskService { | ||
| 90 | */ | 90 | */ |
| 91 | void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); | 91 | void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); |
| 92 | 92 | ||
| 93 | + | ||
| 94 | + | ||
| 93 | /** | 95 | /** |
| 94 | * 将流程任务分配给指定用户 | 96 | * 将流程任务分配给指定用户 |
| 95 | * | 97 | * |
| @@ -129,10 +131,11 @@ public interface BpmTaskService { | @@ -129,10 +131,11 @@ public interface BpmTaskService { | ||
| 129 | 131 | ||
| 130 | /** | 132 | /** |
| 131 | * 根据条件查询已经分配的用户任务列表 | 133 | * 根据条件查询已经分配的用户任务列表 |
| 132 | - * @param processInstanceId 流程实例编号 | 134 | + * @param processInstanceId 流程实例编号,不允许为空 |
| 135 | + * @param executionId execution Id | ||
| 133 | * @param taskDefineKey 任务定义 Key | 136 | * @param taskDefineKey 任务定义 Key |
| 134 | */ | 137 | */ |
| 135 | - List<Task> getAssignedTaskListByConditions(String processInstanceId, String taskDefineKey); | 138 | + List<Task> getAssignedTaskListByConditions(String processInstanceId, String executionId, String taskDefineKey); |
| 136 | 139 | ||
| 137 | /** | 140 | /** |
| 138 | * 获取当前任务的可回退的 UserTask 集合 | 141 | * 获取当前任务的可回退的 UserTask 集合 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -9,16 +9,18 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -9,16 +9,18 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 9 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 9 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 10 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 10 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 11 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | 11 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 12 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | ||
| 13 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | ||
| 14 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | 12 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 15 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 13 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 16 | 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.BpmBoundaryEventType; | ||
| 17 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; |
| 18 | import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; | 17 | import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; |
| 19 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; | 18 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| 20 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; | 19 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| 21 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; | 20 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; |
| 21 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||
| 22 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | ||
| 23 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | ||
| 22 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | 24 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| 23 | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; | 25 | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; |
| 24 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 26 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| @@ -26,6 +28,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | @@ -26,6 +28,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | ||
| 26 | import jakarta.annotation.Resource; | 28 | import jakarta.annotation.Resource; |
| 27 | import jakarta.validation.Valid; | 29 | import jakarta.validation.Valid; |
| 28 | import lombok.extern.slf4j.Slf4j; | 30 | import lombok.extern.slf4j.Slf4j; |
| 31 | +import org.flowable.bpmn.model.BoundaryEvent; | ||
| 29 | import org.flowable.bpmn.model.BpmnModel; | 32 | import org.flowable.bpmn.model.BpmnModel; |
| 30 | import org.flowable.bpmn.model.FlowElement; | 33 | import org.flowable.bpmn.model.FlowElement; |
| 31 | import org.flowable.bpmn.model.UserTask; | 34 | import org.flowable.bpmn.model.UserTask; |
| @@ -33,6 +36,7 @@ import org.flowable.engine.HistoryService; | @@ -33,6 +36,7 @@ import org.flowable.engine.HistoryService; | ||
| 33 | import org.flowable.engine.ManagementService; | 36 | import org.flowable.engine.ManagementService; |
| 34 | import org.flowable.engine.RuntimeService; | 37 | import org.flowable.engine.RuntimeService; |
| 35 | import org.flowable.engine.TaskService; | 38 | import org.flowable.engine.TaskService; |
| 39 | +import org.flowable.engine.runtime.Execution; | ||
| 36 | import org.flowable.engine.runtime.ProcessInstance; | 40 | import org.flowable.engine.runtime.ProcessInstance; |
| 37 | import org.flowable.task.api.DelegationState; | 41 | import org.flowable.task.api.DelegationState; |
| 38 | import org.flowable.task.api.Task; | 42 | import org.flowable.task.api.Task; |
| @@ -245,7 +249,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -245,7 +249,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 245 | 249 | ||
| 246 | /** | 250 | /** |
| 247 | * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: | 251 | * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: |
| 248 | - * | 252 | + * <p> |
| 249 | * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 | 253 | * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 |
| 250 | * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 | 254 | * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 |
| 251 | * | 255 | * |
| @@ -278,7 +282,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -278,7 +282,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 278 | taskService.resolveTask(parentTaskId); | 282 | taskService.resolveTask(parentTaskId); |
| 279 | // 3.1.2 更新流程任务 status | 283 | // 3.1.2 更新流程任务 status |
| 280 | updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); | 284 | updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); |
| 281 | - // 3.2 情况二:处理向【向后】加签 | 285 | + // 3.2 情况二:处理向【向后】加签 |
| 282 | } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { | 286 | } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { |
| 283 | // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 | 287 | // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 |
| 284 | // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 | 288 | // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 |
| @@ -333,14 +337,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -333,14 +337,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 333 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | 337 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| 334 | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); | 338 | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); |
| 335 | 339 | ||
| 336 | - // 3. 更新流程实例,审批不通过! | 340 | + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 341 | + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||
| 342 | + // 寻找用户任务的自定义拒绝后处理边界事件 | ||
| 343 | + BoundaryEvent rejectBoundaryEvent = BpmnModelUtils.findCustomBoundaryEventOfUserTask((UserTask) flowElement, | ||
| 344 | + BpmBoundaryEventType.USER_TASK_REJECT_POST_PROCESS); | ||
| 345 | + | ||
| 346 | + if (rejectBoundaryEvent != null) { | ||
| 347 | + Execution execution = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()) | ||
| 348 | + .activityId(rejectBoundaryEvent.getId()).singleResult(); | ||
| 349 | + if (execution != null) { | ||
| 350 | + // 3.1 触发消息边界事件. 进一步的处理交给 BpmTaskEventListener | ||
| 351 | + runtimeService.messageEventReceived(BpmnModelConstants.REJECT_POST_PROCESS_MESSAGE_NAME, execution.getId()); | ||
| 352 | + return; | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + // 3.2 没有找到拒绝后处理边界事件, 更新流程实例,审批不通过! | ||
| 337 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), reqVO.getReason()); | 356 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), reqVO.getReason()); |
| 338 | } | 357 | } |
| 339 | 358 | ||
| 340 | /** | 359 | /** |
| 341 | * 更新流程任务的 status 状态 | 360 | * 更新流程任务的 status 状态 |
| 342 | * | 361 | * |
| 343 | - * @param id 任务编号 | 362 | + * @param id 任务编号 |
| 344 | * @param status 状态 | 363 | * @param status 状态 |
| 345 | */ | 364 | */ |
| 346 | private void updateTaskStatus(String id, Integer status) { | 365 | private void updateTaskStatus(String id, Integer status) { |
| @@ -350,7 +369,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -350,7 +369,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 350 | /** | 369 | /** |
| 351 | * 更新流程任务的 status 状态、reason 理由 | 370 | * 更新流程任务的 status 状态、reason 理由 |
| 352 | * | 371 | * |
| 353 | - * @param id 任务编号 | 372 | + * @param id 任务编号 |
| 354 | * @param status 状态 | 373 | * @param status 状态 |
| 355 | * @param reason 理由(审批通过、审批不通过的理由) | 374 | * @param reason 理由(审批通过、审批不通过的理由) |
| 356 | */ | 375 | */ |
| @@ -434,9 +453,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -434,9 +453,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 434 | } | 453 | } |
| 435 | 454 | ||
| 436 | @Override | 455 | @Override |
| 437 | - public List<Task> getAssignedTaskListByConditions(String processInstanceId, String defineKey) { | ||
| 438 | - TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId) | ||
| 439 | - .taskDefinitionKey(defineKey).active().taskAssigned().includeTaskLocalVariables(); | 456 | + public List<Task> getAssignedTaskListByConditions(String processInstanceId, String executionId, String defineKey) { |
| 457 | + Assert.notNull(processInstanceId, "processInstanceId 不能为空"); | ||
| 458 | + TaskQuery taskQuery = taskService.createTaskQuery().taskAssigned().processInstanceId(processInstanceId).active() | ||
| 459 | + .includeTaskLocalVariables(); | ||
| 460 | + if (StrUtil.isNotEmpty(executionId)) { | ||
| 461 | + taskQuery.executionId(executionId); | ||
| 462 | + } | ||
| 463 | + if (StrUtil.isNotEmpty(defineKey)) { | ||
| 464 | + taskQuery.taskDefinitionKey(defineKey); | ||
| 465 | + } | ||
| 440 | return taskQuery.list(); | 466 | return taskQuery.list(); |
| 441 | } | 467 | } |
| 442 | 468 | ||
| @@ -664,7 +690,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -664,7 +690,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 664 | List<Long> currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner | 690 | List<Long> currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner |
| 665 | Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); | 691 | Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); |
| 666 | if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { | 692 | if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { |
| 667 | - List<AdminUserRespDTO> userList = adminUserApi.getUserList( CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())); | 693 | + List<AdminUserRespDTO> userList = adminUserApi.getUserList(CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())); |
| 668 | throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); | 694 | throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); |
| 669 | } | 695 | } |
| 670 | return taskEntity; | 696 | return taskEntity; |
| @@ -673,8 +699,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -673,8 +699,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 673 | /** | 699 | /** |
| 674 | * 创建加签子任务 | 700 | * 创建加签子任务 |
| 675 | * | 701 | * |
| 676 | - * @param userIds 被加签的用户 ID | ||
| 677 | - * @param taskEntity 被加签的任务 | 702 | + * @param userIds 被加签的用户 ID |
| 703 | + * @param taskEntity 被加签的任务 | ||
| 678 | */ | 704 | */ |
| 679 | private void createSignTaskList(List<String> userIds, TaskEntityImpl taskEntity) { | 705 | private void createSignTaskList(List<String> userIds, TaskEntityImpl taskEntity) { |
| 680 | if (CollUtil.isEmpty(userIds)) { | 706 | if (CollUtil.isEmpty(userIds)) { |
| @@ -703,7 +729,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -703,7 +729,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 703 | // 2.1 向前加签,设置审批人 | 729 | // 2.1 向前加签,设置审批人 |
| 704 | if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { | 730 | if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { |
| 705 | task.setAssignee(assignee); | 731 | task.setAssignee(assignee); |
| 706 | - // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 | 732 | + // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 |
| 707 | } else { | 733 | } else { |
| 708 | task.setOwner(assignee); | 734 | task.setOwner(assignee); |
| 709 | } | 735 | } |