Commit 36a77251af93c0bc0013d2d9969f7c25a7c2711f

Authored by YunaiV
1 parent 32804d3e

【代码优化】工作流:BpmTaskService、BpmProcessInstanceService 区分读方法、写、event 方法的区域,提升可阅读性

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 @@ -34,7 +34,7 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
34 34
35 @Override 35 @Override
36 protected void processCompleted(FlowableEngineEntityEvent event) { 36 protected void processCompleted(FlowableEngineEntityEvent event) {
37 - processInstanceService.updateProcessInstanceWhenCompleted((ProcessInstance)event.getEntity()); 37 + processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
38 } 38 }
39 39
40 } 40 }
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 { @@ -48,17 +48,16 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
48 48
49 @Override 49 @Override
50 protected void taskCreated(FlowableEngineEntityEvent event) { 50 protected void taskCreated(FlowableEngineEntityEvent event) {
51 - taskService.updateTaskStatusWhenCreated((Task) event.getEntity()); 51 + taskService.processTaskCreated((Task) event.getEntity());
52 } 52 }
53 53
54 @Override 54 @Override
55 protected void taskAssigned(FlowableEngineEntityEvent event) { 55 protected void taskAssigned(FlowableEngineEntityEvent event) {
56 - taskService.updateTaskExtAssign((Task) event.getEntity()); 56 + taskService.processTaskAssigned((Task) event.getEntity());
57 } 57 }
58 58
59 @Override 59 @Override
60 protected void activityCancelled(FlowableActivityCancelledEvent event) { 60 protected void activityCancelled(FlowableActivityCancelledEvent event) {
61 - // TODO @jason:如果用户主动取消,可能需要考虑这个  
62 List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); 61 List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
63 if (CollUtil.isEmpty(activityList)) { 62 if (CollUtil.isEmpty(activityList)) {
64 log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); 63 log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
@@ -69,7 +68,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -69,7 +68,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
69 if (StrUtil.isEmpty(activity.getTaskId())) { 68 if (StrUtil.isEmpty(activity.getTaskId())) {
70 return; 69 return;
71 } 70 }
72 - taskService.updateTaskStatusWhenCanceled(activity.getTaskId()); 71 + taskService.processTaskCanceled(activity.getTaskId());
73 }); 72 });
74 } 73 }
75 74
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 @@ -42,7 +42,6 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe
42 @Resource 42 @Resource
43 @Lazy // 延迟加载,避免循环依赖 43 @Lazy // 延迟加载,避免循环依赖
44 private BpmModelService bpmModelService; 44 private BpmModelService bpmModelService;
45 -  
46 @Resource 45 @Resource
47 @Lazy // 延迟加载,避免循环依赖 46 @Lazy // 延迟加载,避免循环依赖
48 private BpmTaskService bpmTaskService; 47 private BpmTaskService bpmTaskService;
@@ -67,7 +66,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe @@ -67,7 +66,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe
67 // 如果是定时器边界事件 66 // 如果是定时器边界事件
68 if (element instanceof BoundaryEvent) { 67 if (element instanceof BoundaryEvent) {
69 BoundaryEvent boundaryEvent = (BoundaryEvent) element; 68 BoundaryEvent boundaryEvent = (BoundaryEvent) element;
70 - String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE); 69 + String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE);
71 BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType)); 70 BpmBoundaryEventType bpmTimerBoundaryEventType = BpmBoundaryEventType.typeOf(NumberUtils.parseInt(boundaryEventType));
72 // 类型为用户任务超时未处理的情况 71 // 类型为用户任务超时未处理的情况
73 if (bpmTimerBoundaryEventType == BpmBoundaryEventType.USER_TASK_TIMEOUT) { 72 if (bpmTimerBoundaryEventType == BpmBoundaryEventType.USER_TASK_TIMEOUT) {
@@ -81,8 +80,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe @@ -81,8 +80,7 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe
81 BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); 80 BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction);
82 if (userTaskTimeoutAction != null) { 81 if (userTaskTimeoutAction != null) {
83 // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ??? 82 // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ???
84 - List<Task> taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true,  
85 - null, taskDefKey); 83 + List<Task> taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefKey);
86 taskList.forEach(task -> { 84 taskList.forEach(task -> {
87 // 自动提醒 85 // 自动提醒
88 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.REMINDER) { 86 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.REMINDER) {
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. @@ -22,6 +22,8 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
22 */ 22 */
23 public interface BpmProcessInstanceService { 23 public interface BpmProcessInstanceService {
24 24
  25 + // ========== Query 查询相关方法 ==========
  26 +
25 /** 27 /**
26 * 获得流程实例 28 * 获得流程实例
27 * 29 *
@@ -74,6 +76,8 @@ public interface BpmProcessInstanceService { @@ -74,6 +76,8 @@ public interface BpmProcessInstanceService {
74 return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); 76 return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
75 } 77 }
76 78
  79 + // ========== Update 写入相关方法 ==========
  80 +
77 /** 81 /**
78 * 获得流程实例的分页 82 * 获得流程实例的分页
79 * 83 *
@@ -126,11 +130,13 @@ public interface BpmProcessInstanceService { @@ -126,11 +130,13 @@ public interface BpmProcessInstanceService {
126 */ 130 */
127 void updateProcessInstanceReject(ProcessInstance processInstance, String reason); 131 void updateProcessInstanceReject(ProcessInstance processInstance, String reason);
128 132
  133 + // ========== Event 事件相关方法 ==========
  134 +
129 /** 135 /**
130 - * 处理 ProcessInstance 完成(审批通过、不通过、取消) 136 + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消
131 * 137 *
132 * @param instance 流程任务 138 * @param instance 流程任务
133 */ 139 */
134 - void updateProcessInstanceWhenCompleted(ProcessInstance instance); 140 + void processProcessInstanceCompleted(ProcessInstance instance);
135 141
136 } 142 }
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.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)); } }  
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; // ========== Query 查询相关方法 ========== @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); } // ========== 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<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)); } // ========== 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)); } }
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
@@ -20,6 +20,8 @@ import java.util.Map; @@ -20,6 +20,8 @@ import java.util.Map;
20 */ 20 */
21 public interface BpmTaskService { 21 public interface BpmTaskService {
22 22
  23 + // ========== Query 查询相关方法 ==========
  24 +
23 /** 25 /**
24 * 获得待办的流程任务分页 26 * 获得待办的流程任务分页
25 * 27 *
@@ -75,85 +77,80 @@ public interface BpmTaskService { @@ -75,85 +77,80 @@ public interface BpmTaskService {
75 List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId); 77 List<HistoricTaskInstance> getTaskListByProcessInstanceId(String processInstanceId);
76 78
77 /** 79 /**
78 - * 通过任务 80 + * 获取任务
79 * 81 *
80 - * @param userId 用户编号  
81 - * @param reqVO 通过请求 82 + * @param id 任务编号
  83 + * @return 任务
82 */ 84 */
83 - void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO); 85 + Task getTask(String id);
84 86
85 /** 87 /**
86 - * 不通过任务 88 + * 根据条件查询正在进行中的任务
87 * 89 *
88 - * @param userId 用户编号  
89 - * @param reqVO 不通过请求 90 + * @param processInstanceId 流程实例编号,不允许为空
  91 + * @param assigned 是否分配了审批人,允许空
  92 + * @param taskDefineKey 任务定义 Key,允许空
90 */ 93 */
91 - void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); 94 + List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,
  95 + Boolean assigned,
  96 + String taskDefineKey);
92 97
93 /** 98 /**
94 - * 将流程任务分配给指定用户 99 + * 获取当前任务的可回退的 UserTask 集合
95 * 100 *
96 - * @param userId 用户编号  
97 - * @param reqVO 分配请求 101 + * @param id 当前的任务 ID
  102 + * @return 可以回退的节点列表
98 */ 103 */
99 - void transferTask(Long userId, BpmTaskTransferReqVO reqVO); 104 + List<UserTask> getUserTaskListByReturn(String id);
100 105
101 /** 106 /**
102 - * 将指定流程实例的、进行中的流程任务,移动到结束节点 107 + * 获取指定任务的子任务列表
103 * 108 *
104 - * @param processInstanceId 流程编号 109 + * @param parentTaskId 父任务ID
  110 + * @return 子任务列表
105 */ 111 */
106 - void moveTaskToEnd(String processInstanceId); 112 + List<Task> getTaskListByParentTaskId(String parentTaskId);
107 113
108 /** 114 /**
109 - * 更新 Task 状态,在创建时 115 + * 通过任务 ID,查询任务名 Map
110 * 116 *
111 - * @param task 任务实体 117 + * @param taskIds 任务 ID
  118 + * @return 任务 ID 与名字的 Map
112 */ 119 */
113 - void updateTaskStatusWhenCreated(Task task); 120 + Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds);
114 121
115 - /**  
116 - * 更新 Task 状态,在取消时  
117 - *  
118 - * @param taskId 任务的编号  
119 - */  
120 - void updateTaskStatusWhenCanceled(String taskId); 122 + // ========== Update 写入相关方法 ==========
121 123
122 /** 124 /**
123 - * 更新 Task 拓展记录,并发送通知 125 + * 通过任务
124 * 126 *
125 - * @param task 任务实体 127 + * @param userId 用户编号
  128 + * @param reqVO 通过请求
126 */ 129 */
127 - void updateTaskExtAssign(Task task); 130 + void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO);
128 131
129 /** 132 /**
130 - * 获取任务 133 + * 不通过任务
131 * 134 *
132 - * @param id 任务编号  
133 - * @return 任务 135 + * @param userId 用户编号
  136 + * @param reqVO 不通过请求
134 */ 137 */
135 - Task getTask(String id); 138 + void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
136 139
137 /** 140 /**
138 - * 根据条件查询正在进行中的任务 141 + * 将流程任务分配给指定用户
139 * 142 *
140 - * @param processInstanceId 流程实例编号,不允许为空  
141 - * @param assigned 是否分配了审批人,允许空  
142 - * @param executionId execution Id,允许空  
143 - * @param taskDefineKey 任务定义 Key,允许空 143 + * @param userId 用户编号
  144 + * @param reqVO 分配请求
144 */ 145 */
145 - List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId,  
146 - Boolean assigned,  
147 - String executionId,  
148 - String taskDefineKey); 146 + void transferTask(Long userId, BpmTaskTransferReqVO reqVO);
149 147
150 /** 148 /**
151 - * 获取当前任务的可回退的 UserTask 集合 149 + * 将指定流程实例的、进行中的流程任务,移动到结束节点
152 * 150 *
153 - * @param id 当前的任务 ID  
154 - * @return 可以回退的节点列表 151 + * @param processInstanceId 流程编号
155 */ 152 */
156 - List<UserTask> getUserTaskListByReturn(String id); 153 + void moveTaskToEnd(String processInstanceId);
157 154
158 /** 155 /**
159 * 将任务回退到指定的 targetDefinitionKey 位置 156 * 将任务回退到指定的 targetDefinitionKey 位置
@@ -187,20 +184,27 @@ public interface BpmTaskService { @@ -187,20 +184,27 @@ public interface BpmTaskService {
187 */ 184 */
188 void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO); 185 void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO);
189 186
  187 + // ========== Event 事件相关方法 ==========
  188 +
190 /** 189 /**
191 - * 获取指定任务的子任务列表 190 + * 处理 Task 创建事件,目前是更新它的状态为审批中
192 * 191 *
193 - * @param parentTaskId 父任务ID  
194 - * @return 子任务列表 192 + * @param task 任务实体
195 */ 193 */
196 - List<Task> getTaskListByParentTaskId(String parentTaskId); 194 + void processTaskCreated(Task task);
197 195
198 /** 196 /**
199 - * 通过任务 ID,查询任务名 Map 197 + * 处理 Task 取消事件,目前是更新它的状态为已取消
200 * 198 *
201 - * @param taskIds 任务 ID  
202 - * @return 任务 ID 与名字的 Map 199 + * @param taskId 任务的编号
203 */ 200 */
204 - Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds); 201 + void processTaskCanceled(String taskId);
  202 +
  203 + /**
  204 + * 处理 Task 设置审批人事件,目前是发送审批消息
  205 + *
  206 + * @param task 任务实体
  207 + */
  208 + void processTaskAssigned(Task task);
205 209
206 } 210 }
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 { @@ -84,6 +84,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
84 @Resource 84 @Resource
85 private AdminUserApi adminUserApi; 85 private AdminUserApi adminUserApi;
86 86
  87 + // ========== Query 查询相关方法 ==========
  88 +
87 @Override 89 @Override
88 public PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) { 90 public PageResult<Task> getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) {
89 TaskQuery taskQuery = taskService.createTaskQuery() 91 TaskQuery taskQuery = taskService.createTaskQuery()
@@ -172,6 +174,156 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -172,6 +174,156 @@ public class BpmTaskServiceImpl implements BpmTaskService {
172 return tasks; 174 return tasks;
173 } 175 }
174 176
  177 + /**
  178 + * 校验任务是否存在,并且是否是分配给自己的任务
  179 + *
  180 + * @param userId 用户 id
  181 + * @param taskId task id
  182 + */
  183 + private Task validateTask(Long userId, String taskId) {
  184 + Task task = validateTaskExist(taskId);
  185 + if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
  186 + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);
  187 + }
  188 + return task;
  189 + }
  190 +
  191 + private Task validateTaskExist(String id) {
  192 + Task task = getTask(id);
  193 + if (task == null) {
  194 + throw exception(TASK_NOT_EXISTS);
  195 + }
  196 + return task;
  197 + }
  198 +
  199 + @Override
  200 + public Task getTask(String id) {
  201 + return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult();
  202 + }
  203 +
  204 + @Override
  205 + public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) {
  206 + Assert.notNull(processInstanceId, "processInstanceId 不能为空");
  207 + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active()
  208 + .includeTaskLocalVariables();
  209 + if (BooleanUtil.isTrue(assigned)) {
  210 + taskQuery.taskAssigned();
  211 + } else if (BooleanUtil.isFalse(assigned)) {
  212 + taskQuery.taskUnassigned();
  213 + }
  214 + if (StrUtil.isNotEmpty(defineKey)) {
  215 + taskQuery.taskDefinitionKey(defineKey);
  216 + }
  217 + return taskQuery.list();
  218 + }
  219 +
  220 + private HistoricTaskInstance getHistoricTask(String id) {
  221 + return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult();
  222 + }
  223 +
  224 + @Override
  225 + public List<UserTask> getUserTaskListByReturn(String id) {
  226 + // 1.1 校验当前任务 task 存在
  227 + Task task = validateTaskExist(id);
  228 + // 1.2 根据流程定义获取流程模型信息
  229 + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
  230 + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
  231 + if (source == null) {
  232 + throw exception(TASK_NOT_EXISTS);
  233 + }
  234 +
  235 + // 2.1 查询该任务的前置任务节点的 key 集合
  236 + List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);
  237 + if (CollUtil.isEmpty(previousUserList)) {
  238 + return Collections.emptyList();
  239 + }
  240 + // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
  241 + previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
  242 + return previousUserList;
  243 + }
  244 +
  245 + /**
  246 + * 获得所有子任务列表
  247 + *
  248 + * @param parentTask 父任务
  249 + * @return 所有子任务列表
  250 + */
  251 + private List<Task> getAllChildTaskList(Task parentTask) {
  252 + List<Task> result = new ArrayList<>();
  253 + // 1. 递归获取子级
  254 + Stack<Task> stack = new Stack<>();
  255 + stack.push(parentTask);
  256 + // 2. 递归遍历
  257 + for (int i = 0; i < Short.MAX_VALUE; i++) {
  258 + if (stack.isEmpty()) {
  259 + break;
  260 + }
  261 + // 2.1 获取子任务们
  262 + Task task = stack.pop();
  263 + List<Task> childTaskList = getTaskListByParentTaskId(task.getId());
  264 + // 2.2 如果非空,则添加到 stack 进一步递归
  265 + if (CollUtil.isNotEmpty(childTaskList)) {
  266 + stack.addAll(childTaskList);
  267 + result.addAll(childTaskList);
  268 + }
  269 + }
  270 + return result;
  271 + }
  272 +
  273 + @Override
  274 + public List<Task> getTaskListByParentTaskId(String parentTaskId) {
  275 + String tableName = managementService.getTableName(TaskEntity.class);
  276 + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询
  277 + String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
  278 + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
  279 + }
  280 +
  281 + /**
  282 + * 获取子任务个数
  283 + *
  284 + * @param parentTaskId 父任务 ID
  285 + * @return 剩余子任务个数
  286 + */
  287 + private Long getTaskCountByParentTaskId(String parentTaskId) {
  288 + String tableName = managementService.getTableName(TaskEntity.class);
  289 + String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}";
  290 + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
  291 + }
  292 +
  293 + /**
  294 + * 获得任务根任务的父任务编号
  295 + *
  296 + * @param task 任务
  297 + * @return 根任务的父任务编号
  298 + */
  299 + private String getTaskRootParentId(Task task) {
  300 + if (task == null || task.getParentTaskId() == null) {
  301 + return null;
  302 + }
  303 + for (int i = 0; i < Short.MAX_VALUE; i++) {
  304 + Task parentTask = getTask(task.getParentTaskId());
  305 + if (parentTask == null) {
  306 + return null;
  307 + }
  308 + if (parentTask.getParentTaskId() == null) {
  309 + return parentTask.getId();
  310 + }
  311 + task = parentTask;
  312 + }
  313 + throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId()));
  314 + }
  315 +
  316 + @Override
  317 + public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) {
  318 + if (CollUtil.isEmpty(taskIds)) {
  319 + return Collections.emptyMap();
  320 + }
  321 + List<Task> tasks = taskService.createTaskQuery().taskIds(taskIds).list();
  322 + return convertMap(tasks, Task::getId, Task::getName);
  323 + }
  324 +
  325 + // ========== Update 写入相关方法 ==========
  326 +
175 @Override 327 @Override
176 @Transactional(rollbackFor = Exception.class) 328 @Transactional(rollbackFor = Exception.class)
177 public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { 329 public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
@@ -382,132 +534,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -382,132 +534,6 @@ public class BpmTaskServiceImpl implements BpmTaskService {
382 taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_REASON, reason); 534 taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_REASON, reason);
383 } 535 }
384 536
385 - /**  
386 - * 校验任务是否存在,并且是否是分配给自己的任务  
387 - *  
388 - * @param userId 用户 id  
389 - * @param taskId task id  
390 - */  
391 - private Task validateTask(Long userId, String taskId) {  
392 - Task task = validateTaskExist(taskId);  
393 - if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {  
394 - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);  
395 - }  
396 - return task;  
397 - }  
398 -  
399 - @Override  
400 - public void updateTaskStatusWhenCreated(Task task) {  
401 - Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);  
402 - if (status != null) {  
403 - log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status);  
404 - return;  
405 - }  
406 - updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus());  
407 - }  
408 -  
409 - /**  
410 - * 重要补充说明:该方法目前主要有两个情况会调用到:  
411 - *  
412 - * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消  
413 - * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消  
414 - */  
415 - @Override  
416 - public void updateTaskStatusWhenCanceled(String taskId) {  
417 - Task task = getTask(taskId);  
418 - // 1. 可能只是活动,不是任务,所以查询不到  
419 - if (task == null) {  
420 - log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId);  
421 - return;  
422 - }  
423 -  
424 - // 2. 更新 task 状态 + 原因  
425 - Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);  
426 - if (BpmTaskStatusEnum.isEndStatus(status)) {  
427 - log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status);  
428 - return;  
429 - }  
430 - updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());  
431 - // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由  
432 - }  
433 -  
434 - @Override  
435 - public void updateTaskExtAssign(Task task) {  
436 - // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。  
437 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
438 -  
439 - @Override  
440 - public void afterCommit() {  
441 - if (StrUtil.isEmpty(task.getAssignee())) {  
442 - return;  
443 - }  
444 - ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());  
445 - if (processInstance != null) {  
446 - AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));  
447 - messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));  
448 - }  
449 - }  
450 -  
451 - });  
452 - }  
453 -  
454 - private Task validateTaskExist(String id) {  
455 - Task task = getTask(id);  
456 - if (task == null) {  
457 - throw exception(TASK_NOT_EXISTS);  
458 - }  
459 - return task;  
460 - }  
461 -  
462 - @Override  
463 - public Task getTask(String id) {  
464 - return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult();  
465 - }  
466 -  
467 - @Override  
468 - public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String defineKey) {  
469 - Assert.notNull(processInstanceId, "processInstanceId 不能为空");  
470 - TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active()  
471 - .includeTaskLocalVariables();  
472 - if (BooleanUtil.isTrue(assigned)) {  
473 - taskQuery.taskAssigned();  
474 - } else if (BooleanUtil.isFalse(assigned)) {  
475 - taskQuery.taskUnassigned();  
476 - }  
477 - if (StrUtil.isNotEmpty(executionId)) {  
478 - taskQuery.executionId(executionId);  
479 - }  
480 - if (StrUtil.isNotEmpty(defineKey)) {  
481 - taskQuery.taskDefinitionKey(defineKey);  
482 - }  
483 - return taskQuery.list();  
484 - }  
485 -  
486 - private HistoricTaskInstance getHistoricTask(String id) {  
487 - return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult();  
488 - }  
489 -  
490 - @Override  
491 - public List<UserTask> getUserTaskListByReturn(String id) {  
492 - // 1.1 校验当前任务 task 存在  
493 - Task task = validateTaskExist(id);  
494 - // 1.2 根据流程定义获取流程模型信息  
495 - BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());  
496 - FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());  
497 - if (source == null) {  
498 - throw exception(TASK_NOT_EXISTS);  
499 - }  
500 -  
501 - // 2.1 查询该任务的前置任务节点的 key 集合  
502 - List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);  
503 - if (CollUtil.isEmpty(previousUserList)) {  
504 - return Collections.emptyList();  
505 - }  
506 - // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回  
507 - previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));  
508 - return previousUserList;  
509 - }  
510 -  
511 @Override 537 @Override
512 @Transactional(rollbackFor = Exception.class) 538 @Transactional(rollbackFor = Exception.class)
513 public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { 539 public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) {
@@ -645,7 +671,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -645,7 +671,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
645 671
646 @Override 672 @Override
647 public void moveTaskToEnd(String processInstanceId) { 673 public void moveTaskToEnd(String processInstanceId) {
648 - List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null, null); 674 + List<Task> taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);
649 if (CollUtil.isEmpty(taskList)) { 675 if (CollUtil.isEmpty(taskList)) {
650 return; 676 return;
651 } 677 }
@@ -658,7 +684,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -658,7 +684,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
658 if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { 684 if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) {
659 return; 685 return;
660 } 686 }
661 - updateTaskStatusWhenCanceled(task.getId()); 687 + processTaskCanceled(task.getId());
662 }); 688 });
663 689
664 // 2. 终止流程 690 // 2. 终止流程
@@ -842,84 +868,61 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -842,84 +868,61 @@ public class BpmTaskServiceImpl implements BpmTaskService {
842 return task; 868 return task;
843 } 869 }
844 870
845 - /**  
846 - * 获得所有子任务列表  
847 - *  
848 - * @param parentTask 父任务  
849 - * @return 所有子任务列表  
850 - */  
851 - private List<Task> getAllChildTaskList(Task parentTask) {  
852 - List<Task> result = new ArrayList<>();  
853 - // 1. 递归获取子级  
854 - Stack<Task> stack = new Stack<>();  
855 - stack.push(parentTask);  
856 - // 2. 递归遍历  
857 - for (int i = 0; i < Short.MAX_VALUE; i++) {  
858 - if (stack.isEmpty()) {  
859 - break;  
860 - }  
861 - // 2.1 获取子任务们  
862 - Task task = stack.pop();  
863 - List<Task> childTaskList = getTaskListByParentTaskId(task.getId());  
864 - // 2.2 如果非空,则添加到 stack 进一步递归  
865 - if (CollUtil.isNotEmpty(childTaskList)) {  
866 - stack.addAll(childTaskList);  
867 - result.addAll(childTaskList);  
868 - }  
869 - }  
870 - return result;  
871 - } 871 + // ========== Event 事件相关方法 ==========
872 872
873 @Override 873 @Override
874 - public List<Task> getTaskListByParentTaskId(String parentTaskId) {  
875 - String tableName = managementService.getTableName(TaskEntity.class);  
876 - // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询  
877 - String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";  
878 - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();  
879 - }  
880 -  
881 - /**  
882 - * 获取子任务个数  
883 - *  
884 - * @param parentTaskId 父任务 ID  
885 - * @return 剩余子任务个数  
886 - */  
887 - private Long getTaskCountByParentTaskId(String parentTaskId) {  
888 - String tableName = managementService.getTableName(TaskEntity.class);  
889 - String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}";  
890 - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); 874 + public void processTaskCreated(Task task) {
  875 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
  876 + if (status != null) {
  877 + log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status);
  878 + return;
  879 + }
  880 + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus());
891 } 881 }
892 882
893 /** 883 /**
894 - * 获得任务根任务的父任务编号 884 + * 重要补充说明:该方法目前主要有两个情况会调用到:
895 * 885 *
896 - * @param task 任务  
897 - * @return 根任务的父任务编号 886 + * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消
  887 + * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消
898 */ 888 */
899 - private String getTaskRootParentId(Task task) {  
900 - if (task == null || task.getParentTaskId() == null) {  
901 - return null; 889 + @Override
  890 + public void processTaskCanceled(String taskId) {
  891 + Task task = getTask(taskId);
  892 + // 1. 可能只是活动,不是任务,所以查询不到
  893 + if (task == null) {
  894 + log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId);
  895 + return;
902 } 896 }
903 - for (int i = 0; i < Short.MAX_VALUE; i++) {  
904 - Task parentTask = getTask(task.getParentTaskId());  
905 - if (parentTask == null) {  
906 - return null;  
907 - }  
908 - if (parentTask.getParentTaskId() == null) {  
909 - return parentTask.getId();  
910 - }  
911 - task = parentTask; 897 +
  898 + // 2. 更新 task 状态 + 原因
  899 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
  900 + if (BpmTaskStatusEnum.isEndStatus(status)) {
  901 + log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status);
  902 + return;
912 } 903 }
913 - throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); 904 + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
  905 + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由
914 } 906 }
915 907
916 @Override 908 @Override
917 - public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) {  
918 - if (CollUtil.isEmpty(taskIds)) {  
919 - return Collections.emptyMap();  
920 - }  
921 - List<Task> tasks = taskService.createTaskQuery().taskIds(taskIds).list();  
922 - return convertMap(tasks, Task::getId, Task::getName); 909 + public void processTaskAssigned(Task task) {
  910 + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
  911 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  912 +
  913 + @Override
  914 + public void afterCommit() {
  915 + if (StrUtil.isEmpty(task.getAssignee())) {
  916 + return;
  917 + }
  918 + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
  919 + if (processInstance != null) {
  920 + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
  921 + messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
  922 + }
  923 + }
  924 +
  925 + });
923 } 926 }
924 927
925 } 928 }