Commit 32804d3e0bf1844c5be658a20c9a893772dacf8f
1 parent
521cc3de
【功能重构】工作流:重构流程不通过、取消的处理逻辑,完全转向 flowable 的 moveActivityIdsToSingleActivityId API
Showing
8 changed files
with
65 additions
and
81 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmDeleteReasonEnum.java renamed to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
| @@ -5,13 +5,13 @@ import lombok.AllArgsConstructor; | @@ -5,13 +5,13 @@ import lombok.AllArgsConstructor; | ||
| 5 | import lombok.Getter; | 5 | import lombok.Getter; |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| 8 | - * 流程实例/任务的删除原因枚举 | 8 | + * 流程实例/任务的的处理原因枚举 |
| 9 | * | 9 | * |
| 10 | * @author 芋道源码 | 10 | * @author 芋道源码 |
| 11 | */ | 11 | */ |
| 12 | @Getter | 12 | @Getter |
| 13 | @AllArgsConstructor | 13 | @AllArgsConstructor |
| 14 | -public enum BpmDeleteReasonEnum { | 14 | +public enum BpmReasonEnum { |
| 15 | 15 | ||
| 16 | // ========== 流程实例的独有原因 ========== | 16 | // ========== 流程实例的独有原因 ========== |
| 17 | 17 | ||
| @@ -36,10 +36,4 @@ public enum BpmDeleteReasonEnum { | @@ -36,10 +36,4 @@ public enum BpmDeleteReasonEnum { | ||
| 36 | return StrUtil.format(reason, args); | 36 | return StrUtil.format(reason, args); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | - // ========== 逻辑 ========== | ||
| 40 | - | ||
| 41 | - public static boolean isRejectReason(String reason) { | ||
| 42 | - return StrUtil.startWith(reason, "审批不通过任务,原因:"); | ||
| 43 | - } | ||
| 44 | - | ||
| 45 | } | 39 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| @@ -89,11 +89,6 @@ public interface BpmProcessInstanceConvert { | @@ -89,11 +89,6 @@ public interface BpmProcessInstanceConvert { | ||
| 89 | @Mapping(source = "from.id", target = "to.id", ignore = true) | 89 | @Mapping(source = "from.id", target = "to.id", ignore = true) |
| 90 | void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); | 90 | void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); |
| 91 | 91 | ||
| 92 | - default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, HistoricProcessInstance instance, Integer status) { | ||
| 93 | - return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) | ||
| 94 | - .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); | ||
| 95 | - } | ||
| 96 | - | ||
| 97 | default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {; | 92 | default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {; |
| 98 | return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) | 93 | return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) |
| 99 | .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); | 94 | .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmConstants.java
| @@ -16,6 +16,14 @@ public class BpmConstants { | @@ -16,6 +16,14 @@ public class BpmConstants { | ||
| 16 | */ | 16 | */ |
| 17 | public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; | 17 | public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; |
| 18 | /** | 18 | /** |
| 19 | + * 流程实例的变量 - 理由 | ||
| 20 | + * | ||
| 21 | + * 例如说:审批不通过的理由(目前审核通过暂时不会记录) | ||
| 22 | + * | ||
| 23 | + * @see ProcessInstance#getProcessVariables() | ||
| 24 | + */ | ||
| 25 | + public static final String PROCESS_INSTANCE_VARIABLE_REASON = "PROCESS_REASON"; | ||
| 26 | + /** | ||
| 19 | * 流程实例的变量 - 发起用户选择的审批人 Map | 27 | * 流程实例的变量 - 发起用户选择的审批人 Map |
| 20 | * | 28 | * |
| 21 | * @see ProcessInstance#getProcessVariables() | 29 | * @see ProcessInstance#getProcessVariables() |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
| @@ -6,7 +6,6 @@ import jakarta.annotation.Resource; | @@ -6,7 +6,6 @@ import jakarta.annotation.Resource; | ||
| 6 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; | 6 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; |
| 7 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; | 7 | import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; |
| 8 | import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; | 8 | import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; |
| 9 | -import org.flowable.engine.delegate.event.FlowableCancelledEvent; | ||
| 10 | import org.flowable.engine.runtime.ProcessInstance; | 9 | import org.flowable.engine.runtime.ProcessInstance; |
| 11 | import org.springframework.context.annotation.Lazy; | 10 | import org.springframework.context.annotation.Lazy; |
| 12 | import org.springframework.stereotype.Component; | 11 | import org.springframework.stereotype.Component; |
| @@ -21,25 +20,19 @@ import java.util.Set; | @@ -21,25 +20,19 @@ import java.util.Set; | ||
| 21 | @Component | 20 | @Component |
| 22 | public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { | 21 | public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { |
| 23 | 22 | ||
| 23 | + public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() | ||
| 24 | + .add(FlowableEngineEventType.PROCESS_COMPLETED) | ||
| 25 | + .build(); | ||
| 26 | + | ||
| 24 | @Resource | 27 | @Resource |
| 25 | - @Lazy | 28 | + @Lazy // 延迟加载,避免循环依赖 |
| 26 | private BpmProcessInstanceService processInstanceService; | 29 | private BpmProcessInstanceService processInstanceService; |
| 27 | 30 | ||
| 28 | - public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() | ||
| 29 | - .add(FlowableEngineEventType.PROCESS_CANCELLED) | ||
| 30 | - .add(FlowableEngineEventType.PROCESS_COMPLETED) | ||
| 31 | - .build(); | ||
| 32 | - | ||
| 33 | public BpmProcessInstanceEventListener(){ | 31 | public BpmProcessInstanceEventListener(){ |
| 34 | super(PROCESS_INSTANCE_EVENTS); | 32 | super(PROCESS_INSTANCE_EVENTS); |
| 35 | } | 33 | } |
| 36 | 34 | ||
| 37 | @Override | 35 | @Override |
| 38 | - protected void processCancelled(FlowableCancelledEvent event) { | ||
| 39 | - processInstanceService.updateProcessInstanceWhenCancel(event); | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | - @Override | ||
| 43 | protected void processCompleted(FlowableEngineEntityEvent event) { | 36 | protected void processCompleted(FlowableEngineEntityEvent event) { |
| 44 | processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity()); | 37 | processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity()); |
| 45 | } | 38 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
| @@ -6,11 +6,9 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI | @@ -6,11 +6,9 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI | ||
| 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; |
| 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; |
| 8 | import jakarta.validation.Valid; | 8 | import jakarta.validation.Valid; |
| 9 | -import org.flowable.engine.delegate.event.FlowableCancelledEvent; | ||
| 10 | import org.flowable.engine.history.HistoricProcessInstance; | 9 | import org.flowable.engine.history.HistoricProcessInstance; |
| 11 | import org.flowable.engine.runtime.ProcessInstance; | 10 | import org.flowable.engine.runtime.ProcessInstance; |
| 12 | 11 | ||
| 13 | -import java.util.Collection; | ||
| 14 | import java.util.List; | 12 | import java.util.List; |
| 15 | import java.util.Map; | 13 | import java.util.Map; |
| 16 | import java.util.Set; | 14 | import java.util.Set; |
| @@ -121,31 +119,15 @@ public interface BpmProcessInstanceService { | @@ -121,31 +119,15 @@ public interface BpmProcessInstanceService { | ||
| 121 | void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); | 119 | void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); |
| 122 | 120 | ||
| 123 | /** | 121 | /** |
| 124 | - * 更新 ProcessInstance 拓展记录为取消 | ||
| 125 | - * | ||
| 126 | - * @param event 流程取消事件 | ||
| 127 | - */ | ||
| 128 | - void updateProcessInstanceWhenCancel(FlowableCancelledEvent event); | ||
| 129 | - | ||
| 130 | - /** | ||
| 131 | - * 更新 ProcessInstance 为完成 | ||
| 132 | - * | ||
| 133 | - * @param instance 流程任务 | ||
| 134 | - */ | ||
| 135 | - void updateProcessInstanceWhenApprove(ProcessInstance instance); | ||
| 136 | - | ||
| 137 | - /** | ||
| 138 | * 更新 ProcessInstance 为不通过 | 122 | * 更新 ProcessInstance 为不通过 |
| 139 | * | 123 | * |
| 140 | * @param processInstance 流程实例 | 124 | * @param processInstance 流程实例 |
| 141 | - * @param activityIds 当前未完成活动节点 Id | ||
| 142 | - * @param endId 结束节点 Id | ||
| 143 | * @param reason 理由。例如说,审批不通过时,需要传递该值 | 125 | * @param reason 理由。例如说,审批不通过时,需要传递该值 |
| 144 | */ | 126 | */ |
| 145 | - void updateProcessInstanceReject(ProcessInstance processInstance, Collection<String> activityIds, String endId, String reason); | 127 | + void updateProcessInstanceReject(ProcessInstance processInstance, String reason); |
| 146 | 128 | ||
| 147 | /** | 129 | /** |
| 148 | - * 当流程结束时候,更新 ProcessInstance 为通过 | 130 | + * 处理 ProcessInstance 完成(审批通过、不通过、取消) |
| 149 | * | 131 | * |
| 150 | * @param instance 流程任务 | 132 | * @param instance 流程任务 |
| 151 | */ | 133 | */ |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .processInstanceTenantId(FlowableUtils.getTenantId()) .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // TODO @芋艿 这种情况不会发生了。 拒绝时候不会删除流程 // // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 // if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { // return; // } // 1. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 2. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(ProcessInstance processInstance, Collection<String> activityIds, String endId, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 跳转到流程结束 EndEvent 节点,结束流程 runtimeService.createChangeActivityStateBuilder() .processInstanceId(processInstance.getProcessInstanceId()) .moveActivityIdsToSingleActivityId(CollUtil.newArrayList(activityIds), endId) .changeState(); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); // 当流程状态还是审批状态中, 更新为审批通过 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { updateProcessInstanceWhenApprove(instance); } // 审批不通过状态。已经在 updateProcessInstanceReject 处理 } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } } | ||
| 2 | \ No newline at end of file | 1 | \ No newline at end of file |
| 2 | +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource @Lazy // 避免循环依赖 private BpmTaskService taskService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .processInstanceTenantId(FlowableUtils.getTenantId()) .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 取消流程 updateProcessInstanceCancel(cancelReqVO.getId(), BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 2. 取消流程 AdminUserRespDTO user = adminUserApi.getUser(userId); updateProcessInstanceCancel(cancelReqVO.getId(), BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); } private void updateProcessInstanceCancel(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason); // 2. 结束流程 taskService.moveTaskToEnd(id); } @Override public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) { runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_REASON, BpmReasonEnum.REJECT_TASK.format(reason)); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { // 1.1 获取当前状态 Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); String reason = (String) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_REASON); // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, status); } // 2. 发送对应的消息通知 if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); } // 3. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); } } | ||
| 3 | \ No newline at end of file | 3 | \ No newline at end of file |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| @@ -90,8 +90,6 @@ public interface BpmTaskService { | @@ -90,8 +90,6 @@ 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 | - | ||
| 95 | /** | 93 | /** |
| 96 | * 将流程任务分配给指定用户 | 94 | * 将流程任务分配给指定用户 |
| 97 | * | 95 | * |
| @@ -101,6 +99,13 @@ public interface BpmTaskService { | @@ -101,6 +99,13 @@ public interface BpmTaskService { | ||
| 101 | void transferTask(Long userId, BpmTaskTransferReqVO reqVO); | 99 | void transferTask(Long userId, BpmTaskTransferReqVO reqVO); |
| 102 | 100 | ||
| 103 | /** | 101 | /** |
| 102 | + * 将指定流程实例的、进行中的流程任务,移动到结束节点 | ||
| 103 | + * | ||
| 104 | + * @param processInstanceId 流程编号 | ||
| 105 | + */ | ||
| 106 | + void moveTaskToEnd(String processInstanceId); | ||
| 107 | + | ||
| 108 | + /** | ||
| 104 | * 更新 Task 状态,在创建时 | 109 | * 更新 Task 状态,在创建时 |
| 105 | * | 110 | * |
| 106 | * @param task 任务实体 | 111 | * @param task 任务实体 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | ||
| 11 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | 11 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 12 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; | 12 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; |
| 13 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | 13 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; |
| 14 | -import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; | 14 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; |
| 15 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; | 15 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; | 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| 17 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; | 17 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; |
| @@ -327,15 +327,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -327,15 +327,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 327 | throw exception(PROCESS_INSTANCE_NOT_EXISTS); | 327 | throw exception(PROCESS_INSTANCE_NOT_EXISTS); |
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | - // 2. 处理当前任务 | ||
| 331 | // 2.1 更新流程任务为不通过 | 330 | // 2.1 更新流程任务为不通过 |
| 332 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); | 331 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); |
| 333 | - // 2.2 添加评论 | 332 | + // 2.2 添加流程评论 |
| 334 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | 333 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| 335 | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); | 334 | BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); |
| 336 | - | ||
| 337 | - // 3. 处理其他进行中的任务 | ||
| 338 | - // 3.1 如果当前任务时被加签的,则加它的根任务也标记成未通过 | 335 | + // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 |
| 339 | // 疑问:为什么要标记未通过呢? | 336 | // 疑问:为什么要标记未通过呢? |
| 340 | // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 | 337 | // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 |
| 341 | if (task.getParentTaskId() != null) { | 338 | if (task.getParentTaskId() != null) { |
| @@ -345,41 +342,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -345,41 +342,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 345 | taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | 342 | taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |
| 346 | BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); | 343 | BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); |
| 347 | } | 344 | } |
| 348 | - // 3.2 其它未结束的任务,直接取消 | ||
| 349 | - // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? | ||
| 350 | - // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 | ||
| 351 | - List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), null, null, null); | ||
| 352 | - taskList.forEach(otherTask -> { | ||
| 353 | - if (!otherTask.getId().equals(task.getId())) { // 不需要处理当前任务 | ||
| 354 | - return; | ||
| 355 | - } | ||
| 356 | - Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); | ||
| 357 | - if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { | ||
| 358 | - return; | ||
| 359 | - } | ||
| 360 | - updateTaskStatusWhenCanceled(otherTask.getId()); | ||
| 361 | - }); | ||
| 362 | - taskList.stream().filter(otherTask -> !otherTask.getId().equals(task.getId())) // 需要排除当前任务 | ||
| 363 | - .forEach(otherTask -> updateTaskStatusWhenCanceled(otherTask.getId())); | ||
| 364 | 345 | ||
| 365 | - // 4.1 驳回到指定的任务节点 | 346 | + // 3. 根据不同的 RejectHandler 处理策略 |
| 366 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | 347 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 367 | FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | 348 | FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| 349 | + // 3.1 情况一:驳回到指定的任务节点 | ||
| 368 | BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); | 350 | BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); |
| 369 | if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) { | 351 | if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) { |
| 370 | String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement); | 352 | String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement); |
| 371 | Assert.notNull(returnTaskId, "回退的节点不能为空"); | 353 | Assert.notNull(returnTaskId, "回退的节点不能为空"); |
| 372 | - BpmTaskReturnReqVO returnReq = new BpmTaskReturnReqVO().setId(task.getId()).setTargetTaskDefinitionKey(returnTaskId) | ||
| 373 | - .setReason(reqVO.getReason()); | ||
| 374 | - returnTask(userId, returnReq); | 354 | + returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId()) |
| 355 | + .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason())); | ||
| 375 | return; | 356 | return; |
| 376 | } | 357 | } |
| 377 | - | ||
| 378 | - // 4.2 终止流程 | ||
| 379 | - Set<String> activityIds = convertSet(taskList, Task::getTaskDefinitionKey); | ||
| 380 | - EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); | ||
| 381 | - Assert.notNull(endEvent, "结束节点不能未空"); | ||
| 382 | - processInstanceService.updateProcessInstanceReject(instance, activityIds, endEvent.getId(), reqVO.getReason()); | 358 | + // 3.2 情况二:直接结束,审批不通过 |
| 359 | + processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过 | ||
| 360 | + moveTaskToEnd(task.getProcessInstanceId()); // 结束流程 | ||
| 383 | } | 361 | } |
| 384 | 362 | ||
| 385 | /** | 363 | /** |
| @@ -449,7 +427,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -449,7 +427,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 449 | log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); | 427 | log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); |
| 450 | return; | 428 | return; |
| 451 | } | 429 | } |
| 452 | - updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); | 430 | + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); |
| 453 | // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 | 431 | // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 |
| 454 | } | 432 | } |
| 455 | 433 | ||
| @@ -666,6 +644,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -666,6 +644,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 666 | } | 644 | } |
| 667 | 645 | ||
| 668 | @Override | 646 | @Override |
| 647 | + public void moveTaskToEnd(String processInstanceId) { | ||
| 648 | + List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null, null); | ||
| 649 | + if (CollUtil.isEmpty(taskList)) { | ||
| 650 | + return; | ||
| 651 | + } | ||
| 652 | + | ||
| 653 | + // 1. 其它未结束的任务,直接取消 | ||
| 654 | + // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? | ||
| 655 | + // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 | ||
| 656 | + taskList.forEach(task -> { | ||
| 657 | + Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); | ||
| 658 | + if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { | ||
| 659 | + return; | ||
| 660 | + } | ||
| 661 | + updateTaskStatusWhenCanceled(task.getId()); | ||
| 662 | + }); | ||
| 663 | + | ||
| 664 | + // 2. 终止流程 | ||
| 665 | + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); | ||
| 666 | + List<String> activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); | ||
| 667 | + EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); | ||
| 668 | + Assert.notNull(endEvent, "结束节点不能未空"); | ||
| 669 | + runtimeService.createChangeActivityStateBuilder() | ||
| 670 | + .processInstanceId(processInstanceId) | ||
| 671 | + .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) | ||
| 672 | + .changeState(); | ||
| 673 | + } | ||
| 674 | + | ||
| 675 | + @Override | ||
| 669 | @Transactional(rollbackFor = Exception.class) | 676 | @Transactional(rollbackFor = Exception.class) |
| 670 | public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { | 677 | public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { |
| 671 | // 1. 获取和校验任务 | 678 | // 1. 获取和校验任务 |