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 | 5 | import lombok.Getter; |
| 6 | 6 | |
| 7 | 7 | /** |
| 8 | - * 定时器边界事件类型枚举 | |
| 8 | + * BPM 边界事件 (boundary event) 自定义类型枚举 | |
| 9 | 9 | * |
| 10 | 10 | * @author jason |
| 11 | 11 | */ |
| 12 | 12 | @Getter |
| 13 | 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 | 19 | private final Integer type; |
| 19 | 20 | private final String name; |
| 20 | 21 | |
| 21 | - public static BpmTimerBoundaryEventType typeOf(Integer type) { | |
| 22 | + public static BpmBoundaryEventType typeOf(Integer type) { | |
| 22 | 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 | 31 | String USER_TASK_CANDIDATE_PARAM = "candidateParam"; |
| 32 | 32 | |
| 33 | 33 | /** |
| 34 | + * BPMN ExtensionElement 的扩展属性,用于标记边界事件类型 | |
| 35 | + */ | |
| 36 | + String BOUNDARY_EVENT_TYPE = "boundaryEventType"; | |
| 37 | + | |
| 38 | + /** | |
| 34 | 39 | * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 |
| 35 | 40 | */ |
| 36 | 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 | 54 | * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 |
| ... | ... | @@ -66,4 +76,5 @@ public interface BpmnModelConstants { |
| 66 | 76 | */ |
| 67 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 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 | 13 | import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService; |
| 14 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 6 | 15 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; |
| 7 | 16 | import com.google.common.collect.ImmutableSet; |
| 17 | +import jakarta.annotation.Resource; | |
| 8 | 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 | 22 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; |
| 10 | 23 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; |
| 11 | 24 | import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; |
| 12 | 25 | import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; |
| 26 | +import org.flowable.engine.delegate.event.FlowableMessageEvent; | |
| 13 | 27 | import org.flowable.engine.history.HistoricActivityInstance; |
| 14 | 28 | import org.flowable.task.api.Task; |
| 15 | 29 | import org.springframework.context.annotation.Lazy; |
| 16 | 30 | import org.springframework.stereotype.Component; |
| 17 | 31 | |
| 18 | -import jakarta.annotation.Resource; | |
| 19 | 32 | import java.util.List; |
| 33 | +import java.util.Objects; | |
| 20 | 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 | 41 | * 监听 {@link Task} 的开始与完成 |
| 24 | 42 | * |
| ... | ... | @@ -34,15 +52,22 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 34 | 52 | @Resource |
| 35 | 53 | @Lazy // 解决循环依赖 |
| 36 | 54 | private BpmActivityService activityService; |
| 55 | + @Resource | |
| 56 | + @Lazy // 解决循环依赖 | |
| 57 | + private BpmProcessInstanceService processInstanceService; | |
| 58 | + @Resource | |
| 59 | + @Lazy // 延迟加载,避免循环依赖 | |
| 60 | + private BpmModelService bpmModelService; | |
| 37 | 61 | |
| 38 | 62 | public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() |
| 39 | 63 | .add(FlowableEngineEventType.TASK_CREATED) |
| 40 | 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 | 67 | .add(FlowableEngineEventType.ACTIVITY_CANCELLED) |
| 43 | 68 | .build(); |
| 44 | 69 | |
| 45 | - public BpmTaskEventListener(){ | |
| 70 | + public BpmTaskEventListener() { | |
| 46 | 71 | super(TASK_EVENTS); |
| 47 | 72 | } |
| 48 | 73 | |
| ... | ... | @@ -53,7 +78,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 53 | 78 | |
| 54 | 79 | @Override |
| 55 | 80 | protected void taskAssigned(FlowableEngineEntityEvent event) { |
| 56 | - taskService.updateTaskExtAssign((Task)event.getEntity()); | |
| 81 | + taskService.updateTaskExtAssign((Task) event.getEntity()); | |
| 57 | 82 | } |
| 58 | 83 | |
| 59 | 84 | @Override |
| ... | ... | @@ -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 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 5 | 4 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 6 | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; |
| 7 | 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 | 8 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum; |
| 10 | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 11 | 10 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.mq.message.task.TodoTaskReminderMessage; |
| ... | ... | @@ -18,7 +17,6 @@ import jakarta.annotation.Resource; |
| 18 | 17 | import lombok.extern.slf4j.Slf4j; |
| 19 | 18 | import org.flowable.bpmn.model.BoundaryEvent; |
| 20 | 19 | import org.flowable.bpmn.model.BpmnModel; |
| 21 | -import org.flowable.bpmn.model.ExtensionElement; | |
| 22 | 20 | import org.flowable.bpmn.model.FlowElement; |
| 23 | 21 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; |
| 24 | 22 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; |
| ... | ... | @@ -29,7 +27,6 @@ import org.springframework.context.annotation.Lazy; |
| 29 | 27 | import org.springframework.stereotype.Component; |
| 30 | 28 | |
| 31 | 29 | import java.util.List; |
| 32 | -import java.util.Optional; | |
| 33 | 30 | import java.util.Set; |
| 34 | 31 | |
| 35 | 32 | /** |
| ... | ... | @@ -69,23 +66,21 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe |
| 69 | 66 | // 如果是定时器边界事件 |
| 70 | 67 | if (element instanceof BoundaryEvent) { |
| 71 | 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 | 80 | BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); |
| 86 | 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 | 84 | taskList.forEach(task -> { |
| 90 | 85 | // 自动提醒 |
| 91 | 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 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.simple; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | |
| 3 | 4 | import lombok.Data; |
| 4 | 5 | |
| 5 | 6 | import java.util.List; |
| ... | ... | @@ -33,12 +34,15 @@ public class SimpleModelUserTaskConfig { |
| 33 | 34 | */ |
| 34 | 35 | private Integer approveMethod; |
| 35 | 36 | |
| 36 | - | |
| 37 | 37 | /** |
| 38 | 38 | * 超时处理 |
| 39 | 39 | */ |
| 40 | 40 | private TimeoutHandler timeoutHandler; |
| 41 | 41 | |
| 42 | + /** | |
| 43 | + * 用户任务拒绝处理 | |
| 44 | + */ | |
| 45 | + private RejectHandler rejectHandler; | |
| 42 | 46 | |
| 43 | 47 | @Data |
| 44 | 48 | public static class TimeoutHandler { |
| ... | ... | @@ -62,7 +66,20 @@ public class SimpleModelUserTaskConfig { |
| 62 | 66 | * 如果执行动作是自动提醒, 最大提醒次数 |
| 63 | 67 | */ |
| 64 | 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 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; | |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 9 | 10 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 10 | 11 | import org.flowable.bpmn.model.Process; |
| ... | ... | @@ -360,4 +361,32 @@ public class BpmnModelUtils { |
| 360 | 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 | 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 15 | 15 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.simple.SimpleModelConditionGroups; |
| 16 | 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 | 18 | import org.flowable.bpmn.BpmnAutoLayout; |
| 18 | 19 | import org.flowable.bpmn.model.Process; |
| 19 | 20 | import org.flowable.bpmn.model.*; |
| ... | ... | @@ -22,13 +23,14 @@ import java.util.List; |
| 22 | 23 | import java.util.Map; |
| 23 | 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 | 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 | 30 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER; |
| 28 | 31 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 29 | 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 | 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 | 55 | * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善) |
| ... | ... | @@ -59,6 +61,12 @@ public class SimpleModelUtils { |
| 59 | 61 | */ |
| 60 | 62 | public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { |
| 61 | 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 | 70 | Process mainProcess = new Process(); |
| 63 | 71 | mainProcess.setId(processId); |
| 64 | 72 | mainProcess.setName(processName); |
| ... | ... | @@ -214,6 +222,12 @@ public class SimpleModelUtils { |
| 214 | 222 | BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler()); |
| 215 | 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 | 231 | break; |
| 218 | 232 | } |
| 219 | 233 | case COPY_TASK: { |
| ... | ... | @@ -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 | 304 | private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) { |
| 274 | 305 | // 定时器边界事件 |
| 275 | 306 | BoundaryEvent boundaryEvent = new BoundaryEvent(); |
| 276 | - boundaryEvent.setId(IdUtil.fastUUID()); | |
| 307 | + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); | |
| 277 | 308 | // 设置关联的任务为不会被中断 |
| 278 | 309 | boundaryEvent.setCancelActivity(false); |
| 279 | 310 | boundaryEvent.setAttachedToRef(userTask); |
| ... | ... | @@ -286,7 +317,7 @@ public class SimpleModelUtils { |
| 286 | 317 | } |
| 287 | 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 | 322 | addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_ACTION, StrUtil.toStringOrNull(timeoutHandler.getAction())); |
| 292 | 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 | 90 | */ |
| 91 | 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 | 131 | |
| 130 | 132 | /** |
| 131 | 133 | * 根据条件查询已经分配的用户任务列表 |
| 132 | - * @param processInstanceId 流程实例编号 | |
| 134 | + * @param processInstanceId 流程实例编号,不允许为空 | |
| 135 | + * @param executionId execution Id | |
| 133 | 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 | 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 | 9 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 10 | 10 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 11 | 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 | 12 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 15 | 13 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 16 | 14 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 15 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; | |
| 17 | 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; |
| 18 | 17 | import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; |
| 19 | 18 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| 20 | 19 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| 21 | 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 | 24 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| 23 | 25 | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; |
| 24 | 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 | 28 | import jakarta.annotation.Resource; |
| 27 | 29 | import jakarta.validation.Valid; |
| 28 | 30 | import lombok.extern.slf4j.Slf4j; |
| 31 | +import org.flowable.bpmn.model.BoundaryEvent; | |
| 29 | 32 | import org.flowable.bpmn.model.BpmnModel; |
| 30 | 33 | import org.flowable.bpmn.model.FlowElement; |
| 31 | 34 | import org.flowable.bpmn.model.UserTask; |
| ... | ... | @@ -33,6 +36,7 @@ import org.flowable.engine.HistoryService; |
| 33 | 36 | import org.flowable.engine.ManagementService; |
| 34 | 37 | import org.flowable.engine.RuntimeService; |
| 35 | 38 | import org.flowable.engine.TaskService; |
| 39 | +import org.flowable.engine.runtime.Execution; | |
| 36 | 40 | import org.flowable.engine.runtime.ProcessInstance; |
| 37 | 41 | import org.flowable.task.api.DelegationState; |
| 38 | 42 | import org.flowable.task.api.Task; |
| ... | ... | @@ -245,7 +249,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 245 | 249 | |
| 246 | 250 | /** |
| 247 | 251 | * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: |
| 248 | - * | |
| 252 | + * <p> | |
| 249 | 253 | * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 |
| 250 | 254 | * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 |
| 251 | 255 | * |
| ... | ... | @@ -278,7 +282,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 278 | 282 | taskService.resolveTask(parentTaskId); |
| 279 | 283 | // 3.1.2 更新流程任务 status |
| 280 | 284 | updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); |
| 281 | - // 3.2 情况二:处理向【向后】加签 | |
| 285 | + // 3.2 情况二:处理向【向后】加签 | |
| 282 | 286 | } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { |
| 283 | 287 | // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 |
| 284 | 288 | // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 |
| ... | ... | @@ -333,14 +337,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 333 | 337 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| 334 | 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 | 356 | processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), reqVO.getReason()); |
| 338 | 357 | } |
| 339 | 358 | |
| 340 | 359 | /** |
| 341 | 360 | * 更新流程任务的 status 状态 |
| 342 | 361 | * |
| 343 | - * @param id 任务编号 | |
| 362 | + * @param id 任务编号 | |
| 344 | 363 | * @param status 状态 |
| 345 | 364 | */ |
| 346 | 365 | private void updateTaskStatus(String id, Integer status) { |
| ... | ... | @@ -350,7 +369,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 350 | 369 | /** |
| 351 | 370 | * 更新流程任务的 status 状态、reason 理由 |
| 352 | 371 | * |
| 353 | - * @param id 任务编号 | |
| 372 | + * @param id 任务编号 | |
| 354 | 373 | * @param status 状态 |
| 355 | 374 | * @param reason 理由(审批通过、审批不通过的理由) |
| 356 | 375 | */ |
| ... | ... | @@ -434,9 +453,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 434 | 453 | } |
| 435 | 454 | |
| 436 | 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 | 466 | return taskQuery.list(); |
| 441 | 467 | } |
| 442 | 468 | |
| ... | ... | @@ -664,7 +690,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 664 | 690 | List<Long> currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner |
| 665 | 691 | Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); |
| 666 | 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 | 694 | throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); |
| 669 | 695 | } |
| 670 | 696 | return taskEntity; |
| ... | ... | @@ -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 | 705 | private void createSignTaskList(List<String> userIds, TaskEntityImpl taskEntity) { |
| 680 | 706 | if (CollUtil.isEmpty(userIds)) { |
| ... | ... | @@ -703,7 +729,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 703 | 729 | // 2.1 向前加签,设置审批人 |
| 704 | 730 | if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { |
| 705 | 731 | task.setAssignee(assignee); |
| 706 | - // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 | |
| 732 | + // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 | |
| 707 | 733 | } else { |
| 708 | 734 | task.setOwner(assignee); |
| 709 | 735 | } | ... | ... |