diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java index 01d43335d..a0ec3e40a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -34,7 +34,7 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent @Override protected void processCompleted(FlowableEngineEntityEvent event) { - processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity()); + processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index a2af341b3..e20a6b64a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -48,17 +48,16 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @Override protected void taskCreated(FlowableEngineEntityEvent event) { - taskService.updateTaskStatusWhenCreated((Task) event.getEntity()); + taskService.processTaskCreated((Task) event.getEntity()); } @Override protected void taskAssigned(FlowableEngineEntityEvent event) { - taskService.updateTaskExtAssign((Task) event.getEntity()); + taskService.processTaskAssigned((Task) event.getEntity()); } @Override protected void activityCancelled(FlowableActivityCancelledEvent event) { - // TODO @jason:如果用户主动取消,可能需要考虑这个 List activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); if (CollUtil.isEmpty(activityList)) { log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); @@ -69,7 +68,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { if (StrUtil.isEmpty(activity.getTaskId())) { return; } - taskService.updateTaskStatusWhenCanceled(activity.getTaskId()); + taskService.processTaskCanceled(activity.getTaskId()); }); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java index 9f2c40d21..ffa578fbb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java @@ -42,7 +42,6 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe @Resource @Lazy // 延迟加载,避免循环依赖 private BpmModelService bpmModelService; - @Resource @Lazy // 延迟加载,避免循环依赖 private BpmTaskService bpmTaskService; @@ -67,7 +66,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe // 如果是定时器边界事件 if (element instanceof BoundaryEvent) { BoundaryEvent boundaryEvent = (BoundaryEvent) element; - String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); + String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType)); // 类型为用户任务超时未处理的情况 if (bpmTimerBoundaryEventType == BpmBoundaryEventType.USER_TASK_TIMEOUT) { @@ -81,8 +80,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); if (userTaskTimeoutAction != null) { // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ??? - List taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true, - null, taskDefKey); + List taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefKey); taskList.forEach(task -> { // 自动提醒 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.REMINDER) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 3bf323e4b..0c7266f8f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -22,6 +22,8 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. */ public interface BpmProcessInstanceService { + // ========== Query 查询相关方法 ========== + /** * 获得流程实例 * @@ -74,6 +76,8 @@ public interface BpmProcessInstanceService { return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); } + // ========== Update 写入相关方法 ========== + /** * 获得流程实例的分页 * @@ -126,11 +130,13 @@ public interface BpmProcessInstanceService { */ void updateProcessInstanceReject(ProcessInstance processInstance, String reason); + // ========== Event 事件相关方法 ========== + /** - * 处理 ProcessInstance 完成(审批通过、不通过、取消) + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 * * @param instance 流程任务 */ - void updateProcessInstanceWhenCompleted(ProcessInstance instance); + void processProcessInstanceCompleted(ProcessInstance instance); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index d881e80a9..8cee79442 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -1 +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.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. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @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 getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List getHistoricProcessInstances(Set ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult 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 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 variables, String businessKey, Map> 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> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List 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 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)); } } \ No newline at end of file +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. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @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; // ========== Query 查询相关方法 ========== @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List getHistoricProcessInstances(Set ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult 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 processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } // ========== Update 写入相关方法 ========== @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 variables, String businessKey, Map> 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> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List 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 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)); } // ========== Event 事件相关方法 ========== @Override public void processProcessInstanceCompleted(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)); } } \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index a7c9a2c85..9858f6889 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -20,6 +20,8 @@ import java.util.Map; */ public interface BpmTaskService { + // ========== Query 查询相关方法 ========== + /** * 获得待办的流程任务分页 * @@ -74,6 +76,51 @@ public interface BpmTaskService { */ List getTaskListByProcessInstanceId(String processInstanceId); + /** + * 获取任务 + * + * @param id 任务编号 + * @return 任务 + */ + Task getTask(String id); + + /** + * 根据条件查询正在进行中的任务 + * + * @param processInstanceId 流程实例编号,不允许为空 + * @param assigned 是否分配了审批人,允许空 + * @param taskDefineKey 任务定义 Key,允许空 + */ + List getRunningTaskListByProcessInstanceId(String processInstanceId, + Boolean assigned, + String taskDefineKey); + + /** + * 获取当前任务的可回退的 UserTask 集合 + * + * @param id 当前的任务 ID + * @return 可以回退的节点列表 + */ + List getUserTaskListByReturn(String id); + + /** + * 获取指定任务的子任务列表 + * + * @param parentTaskId 父任务ID + * @return 子任务列表 + */ + List getTaskListByParentTaskId(String parentTaskId); + + /** + * 通过任务 ID,查询任务名 Map + * + * @param taskIds 任务 ID + * @return 任务 ID 与名字的 Map + */ + Map getTaskNameByTaskIds(Collection taskIds); + + // ========== Update 写入相关方法 ========== + /** * 通过任务 * @@ -105,56 +152,6 @@ public interface BpmTaskService { */ void moveTaskToEnd(String processInstanceId); - /** - * 更新 Task 状态,在创建时 - * - * @param task 任务实体 - */ - void updateTaskStatusWhenCreated(Task task); - - /** - * 更新 Task 状态,在取消时 - * - * @param taskId 任务的编号 - */ - void updateTaskStatusWhenCanceled(String taskId); - - /** - * 更新 Task 拓展记录,并发送通知 - * - * @param task 任务实体 - */ - void updateTaskExtAssign(Task task); - - /** - * 获取任务 - * - * @param id 任务编号 - * @return 任务 - */ - Task getTask(String id); - - /** - * 根据条件查询正在进行中的任务 - * - * @param processInstanceId 流程实例编号,不允许为空 - * @param assigned 是否分配了审批人,允许空 - * @param executionId execution Id,允许空 - * @param taskDefineKey 任务定义 Key,允许空 - */ - List getRunningTaskListByProcessInstanceId(String processInstanceId, - Boolean assigned, - String executionId, - String taskDefineKey); - - /** - * 获取当前任务的可回退的 UserTask 集合 - * - * @param id 当前的任务 ID - * @return 可以回退的节点列表 - */ - List getUserTaskListByReturn(String id); - /** * 将任务回退到指定的 targetDefinitionKey 位置 * @@ -187,20 +184,27 @@ public interface BpmTaskService { */ void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO); - /** - * 获取指定任务的子任务列表 - * - * @param parentTaskId 父任务ID - * @return 子任务列表 - */ - List getTaskListByParentTaskId(String parentTaskId); + // ========== Event 事件相关方法 ========== /** - * 通过任务 ID,查询任务名 Map + * 处理 Task 创建事件,目前是更新它的状态为审批中 * - * @param taskIds 任务 ID - * @return 任务 ID 与名字的 Map + * @param task 任务实体 */ - Map getTaskNameByTaskIds(Collection taskIds); + void processTaskCreated(Task task); + + /** + * 处理 Task 取消事件,目前是更新它的状态为已取消 + * + * @param taskId 任务的编号 + */ + void processTaskCanceled(String taskId); + + /** + * 处理 Task 设置审批人事件,目前是发送审批消息 + * + * @param task 任务实体 + */ + void processTaskAssigned(Task task); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index d07534e01..79f43cd15 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -84,6 +84,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Resource private AdminUserApi adminUserApi; + // ========== Query 查询相关方法 ========== + @Override public PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) { TaskQuery taskQuery = taskService.createTaskQuery() @@ -172,6 +174,156 @@ public class BpmTaskServiceImpl implements BpmTaskService { return tasks; } + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + private Task validateTask(Long userId, String taskId) { + Task task = validateTaskExist(taskId); + if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + return task; + } + + private Task validateTaskExist(String id) { + Task task = getTask(id); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + return task; + } + + @Override + public Task getTask(String id) { + return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public List getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) { + Assert.notNull(processInstanceId, "processInstanceId 不能为空"); + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active() + .includeTaskLocalVariables(); + if (BooleanUtil.isTrue(assigned)) { + taskQuery.taskAssigned(); + } else if (BooleanUtil.isFalse(assigned)) { + taskQuery.taskUnassigned(); + } + if (StrUtil.isNotEmpty(defineKey)) { + taskQuery.taskDefinitionKey(defineKey); + } + return taskQuery.list(); + } + + private HistoricTaskInstance getHistoricTask(String id) { + return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public List getUserTaskListByReturn(String id) { + // 1.1 校验当前任务 task 存在 + Task task = validateTaskExist(id); + // 1.2 根据流程定义获取流程模型信息 + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + if (source == null) { + throw exception(TASK_NOT_EXISTS); + } + + // 2.1 查询该任务的前置任务节点的 key 集合 + List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); + if (CollUtil.isEmpty(previousUserList)) { + return Collections.emptyList(); + } + // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 + previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); + return previousUserList; + } + + /** + * 获得所有子任务列表 + * + * @param parentTask 父任务 + * @return 所有子任务列表 + */ + private List getAllChildTaskList(Task parentTask) { + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTask); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + Task task = stack.pop(); + List childTaskList = getTaskListByParentTaskId(task.getId()); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(childTaskList); + result.addAll(childTaskList); + } + } + return result; + } + + @Override + public List getTaskListByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 + String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); + } + + /** + * 获取子任务个数 + * + * @param parentTaskId 父任务 ID + * @return 剩余子任务个数 + */ + private Long getTaskCountByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); + } + + /** + * 获得任务根任务的父任务编号 + * + * @param task 任务 + * @return 根任务的父任务编号 + */ + private String getTaskRootParentId(Task task) { + if (task == null || task.getParentTaskId() == null) { + return null; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + return null; + } + if (parentTask.getParentTaskId() == null) { + return parentTask.getId(); + } + task = parentTask; + } + throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); + } + + @Override + public Map getTaskNameByTaskIds(Collection taskIds) { + if (CollUtil.isEmpty(taskIds)) { + return Collections.emptyMap(); + } + List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); + return convertMap(tasks, Task::getId, Task::getName); + } + + // ========== Update 写入相关方法 ========== + @Override @Transactional(rollbackFor = Exception.class) public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { @@ -382,132 +534,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_REASON, reason); } - /** - * 校验任务是否存在,并且是否是分配给自己的任务 - * - * @param userId 用户 id - * @param taskId task id - */ - private Task validateTask(Long userId, String taskId) { - Task task = validateTaskExist(taskId); - if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) { - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); - } - return task; - } - - @Override - public void updateTaskStatusWhenCreated(Task task) { - Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); - if (status != null) { - log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); - return; - } - updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); - } - - /** - * 重要补充说明:该方法目前主要有两个情况会调用到: - * - * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 - * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 - */ - @Override - public void updateTaskStatusWhenCanceled(String taskId) { - Task task = getTask(taskId); - // 1. 可能只是活动,不是任务,所以查询不到 - if (task == null) { - log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); - return; - } - - // 2. 更新 task 状态 + 原因 - Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); - if (BpmTaskStatusEnum.isEndStatus(status)) { - log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); - return; - } - updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); - // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 - } - - @Override - public void updateTaskExtAssign(Task task) { - // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - @Override - public void afterCommit() { - if (StrUtil.isEmpty(task.getAssignee())) { - return; - } - ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (processInstance != null) { - AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); - messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); - } - } - - }); - } - - private Task validateTaskExist(String id) { - Task task = getTask(id); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } - return task; - } - - @Override - public Task getTask(String id) { - return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); - } - - @Override - public List getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String defineKey) { - Assert.notNull(processInstanceId, "processInstanceId 不能为空"); - TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active() - .includeTaskLocalVariables(); - if (BooleanUtil.isTrue(assigned)) { - taskQuery.taskAssigned(); - } else if (BooleanUtil.isFalse(assigned)) { - taskQuery.taskUnassigned(); - } - if (StrUtil.isNotEmpty(executionId)) { - taskQuery.executionId(executionId); - } - if (StrUtil.isNotEmpty(defineKey)) { - taskQuery.taskDefinitionKey(defineKey); - } - return taskQuery.list(); - } - - private HistoricTaskInstance getHistoricTask(String id) { - return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); - } - - @Override - public List getUserTaskListByReturn(String id) { - // 1.1 校验当前任务 task 存在 - Task task = validateTaskExist(id); - // 1.2 根据流程定义获取流程模型信息 - BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); - FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - if (source == null) { - throw exception(TASK_NOT_EXISTS); - } - - // 2.1 查询该任务的前置任务节点的 key 集合 - List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); - if (CollUtil.isEmpty(previousUserList)) { - return Collections.emptyList(); - } - // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 - previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); - return previousUserList; - } - @Override @Transactional(rollbackFor = Exception.class) public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { @@ -645,7 +671,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override public void moveTaskToEnd(String processInstanceId) { - List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null, null); + List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null); if (CollUtil.isEmpty(taskList)) { return; } @@ -658,7 +684,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { return; } - updateTaskStatusWhenCanceled(task.getId()); + processTaskCanceled(task.getId()); }); // 2. 终止流程 @@ -842,84 +868,61 @@ public class BpmTaskServiceImpl implements BpmTaskService { return task; } - /** - * 获得所有子任务列表 - * - * @param parentTask 父任务 - * @return 所有子任务列表 - */ - private List getAllChildTaskList(Task parentTask) { - List result = new ArrayList<>(); - // 1. 递归获取子级 - Stack stack = new Stack<>(); - stack.push(parentTask); - // 2. 递归遍历 - for (int i = 0; i < Short.MAX_VALUE; i++) { - if (stack.isEmpty()) { - break; - } - // 2.1 获取子任务们 - Task task = stack.pop(); - List childTaskList = getTaskListByParentTaskId(task.getId()); - // 2.2 如果非空,则添加到 stack 进一步递归 - if (CollUtil.isNotEmpty(childTaskList)) { - stack.addAll(childTaskList); - result.addAll(childTaskList); - } + // ========== Event 事件相关方法 ========== + + @Override + public void processTaskCreated(Task task) { + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + if (status != null) { + log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); + return; } - return result; + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + } + + /** + * 重要补充说明:该方法目前主要有两个情况会调用到: + * + * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 + * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 + */ + @Override + public void processTaskCanceled(String taskId) { + Task task = getTask(taskId); + // 1. 可能只是活动,不是任务,所以查询不到 + if (task == null) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); + return; + } + + // 2. 更新 task 状态 + 原因 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(status)) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); + return; + } + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 } @Override - public List getTaskListByParentTaskId(String parentTaskId) { - String tableName = managementService.getTableName(TaskEntity.class); - // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 - String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); - } + public void processTaskAssigned(Task task) { + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - /** - * 获取子任务个数 - * - * @param parentTaskId 父任务 ID - * @return 剩余子任务个数 - */ - private Long getTaskCountByParentTaskId(String parentTaskId) { - String tableName = managementService.getTableName(TaskEntity.class); - String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); - } - - /** - * 获得任务根任务的父任务编号 - * - * @param task 任务 - * @return 根任务的父任务编号 - */ - private String getTaskRootParentId(Task task) { - if (task == null || task.getParentTaskId() == null) { - return null; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - Task parentTask = getTask(task.getParentTaskId()); - if (parentTask == null) { - return null; + @Override + public void afterCommit() { + if (StrUtil.isEmpty(task.getAssignee())) { + return; + } + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance != null) { + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); + messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); + } } - if (parentTask.getParentTaskId() == null) { - return parentTask.getId(); - } - task = parentTask; - } - throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); - } - @Override - public Map getTaskNameByTaskIds(Collection taskIds) { - if (CollUtil.isEmpty(taskIds)) { - return Collections.emptyMap(); - } - List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); - return convertMap(tasks, Task::getId, Task::getName); + }); } }