|
|
@@ -1,6 +1,7 @@
|
|
|
package com.qqflow.engine.domain.flow.service.impl;
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
import com.qqflow.engine.common.exception.BusinessException;
|
|
|
import com.qqflow.engine.domain.flow.assembler.ApprovalTaskAssembler;
|
|
|
import com.qqflow.engine.domain.flow.enums.ApprovalAction;
|
|
|
@@ -16,6 +17,7 @@ import com.qqflow.engine.domain.flow.mapper.ProcessInstanceMapper;
|
|
|
import com.qqflow.engine.domain.flow.model.FlowEdge;
|
|
|
import com.qqflow.engine.domain.flow.model.FlowModel;
|
|
|
import com.qqflow.engine.domain.flow.model.FlowNode;
|
|
|
+import com.qqflow.engine.domain.flow.model.SubNodeConfig;
|
|
|
import com.qqflow.engine.domain.flow.po.ApprovalTask;
|
|
|
import com.qqflow.engine.domain.flow.po.ProcessDefinition;
|
|
|
import com.qqflow.engine.domain.flow.po.ProcessInstance;
|
|
|
@@ -198,6 +200,96 @@ public class FlowEngineServiceImpl implements FlowEngineService {
|
|
|
this.executeTransition(instance, currentTask, action, null);
|
|
|
}
|
|
|
|
|
|
+ // ==================== 子节点(子审批步骤)辅助方法 ====================
|
|
|
+
|
|
|
+ private boolean isSubNodesEnabled(FlowNode node) {
|
|
|
+ Map<String, Object> props = node.getProperties();
|
|
|
+ if (props == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Object enabled = props.get("subNodesEnabled");
|
|
|
+ if (enabled instanceof Boolean) {
|
|
|
+ return (Boolean) enabled;
|
|
|
+ }
|
|
|
+ return "true".equalsIgnoreCase(String.valueOf(enabled));
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isSequentialMode(FlowNode node) {
|
|
|
+ Map<String, Object> props = node.getProperties();
|
|
|
+ if (props == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Object mode = props.get("subNodeMode");
|
|
|
+ if (mode == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return "sequential".equalsIgnoreCase(mode.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<SubNodeConfig> extractSubNodes(FlowNode node) {
|
|
|
+ Map<String, Object> props = node.getProperties();
|
|
|
+ if (props == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ Object subNodesObj = props.get("subNodes");
|
|
|
+ if (subNodesObj == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ List<SubNodeConfig> list = OBJECT_MAPPER.convertValue(subNodesObj, new TypeReference<List<SubNodeConfig>>() {});
|
|
|
+ return list != null ? list : Collections.emptyList();
|
|
|
+ } catch (Exception e) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private SubNodeConfig findSubNodeById(FlowNode node, String subNodeId) {
|
|
|
+ return this.extractSubNodes(node).stream()
|
|
|
+ .filter(s -> s.getId() != null && s.getId().equals(subNodeId))
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private int findSubNodeIndex(FlowNode node, String subNodeId) {
|
|
|
+ List<SubNodeConfig> subNodes = this.extractSubNodes(node);
|
|
|
+ for (int i = 0; i < subNodes.size(); i++) {
|
|
|
+ if (subNodes.get(i).getId() != null && subNodes.get(i).getId().equals(subNodeId)) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Long> calculateSubNodeAssignees(SubNodeConfig subNode, FlowNode parentNode, ProcessInstance instance) {
|
|
|
+ String assigneeValue = subNode.getAssigneeValue();
|
|
|
+ if (assigneeValue == null || assigneeValue.isBlank()) {
|
|
|
+ // 子节点未配置审批人时,回退到父节点配置
|
|
|
+ return this.calculateAssignees(parentNode, instance);
|
|
|
+ }
|
|
|
+ return this.doCalculateAssignees(ASSIGNEE_TYPE_ROLE, assigneeValue, instance);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getSubNodeApproveMode(SubNodeConfig subNode) {
|
|
|
+ String mode = subNode.getApproveMode();
|
|
|
+ return mode != null ? mode : "or";
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasExistingTaskForSubNode(Long instanceId, String nodeId, String subNodeId) {
|
|
|
+ if (instanceId == null || nodeId == null || subNodeId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ long count = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instanceId)
|
|
|
+ .eq(ApprovalTask::getNodeId, nodeId)
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNodeId)
|
|
|
+ .in(ApprovalTask::getTaskStatus,
|
|
|
+ TaskStatus.PENDING.getCode(),
|
|
|
+ TaskStatus.HANDLED.getCode(),
|
|
|
+ TaskStatus.SKIPPED.getCode()));
|
|
|
+ return count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void executeTransition(ProcessInstance instance, ApprovalTask currentTask, ApprovalAction action, String comment) {
|
|
|
@@ -209,7 +301,13 @@ public class FlowEngineServiceImpl implements FlowEngineService {
|
|
|
public void executeTransition(ProcessInstance instance, ApprovalTask currentTask, ApprovalAction action, String comment, String targetNodeId) {
|
|
|
switch (action) {
|
|
|
case APPROVE -> this.handleApprove(instance, currentTask, comment, targetNodeId);
|
|
|
- case REJECT -> this.handleReject(instance, currentTask, comment);
|
|
|
+ case REJECT -> {
|
|
|
+ if (currentTask != null && currentTask.getSubNodeId() != null && !currentTask.getSubNodeId().isBlank()) {
|
|
|
+ this.handleSubNodeReject(instance, currentTask, comment);
|
|
|
+ } else {
|
|
|
+ this.handleReject(instance, currentTask, comment);
|
|
|
+ }
|
|
|
+ }
|
|
|
case RETURN -> this.handleReturn(instance, currentTask, comment, targetNodeId);
|
|
|
default -> throw new BusinessException("不支持的操作类型");
|
|
|
}
|
|
|
@@ -322,33 +420,42 @@ public class FlowEngineServiceImpl implements FlowEngineService {
|
|
|
.filter(n -> n.getId().equals(lockedTask.getNodeId()))
|
|
|
.findFirst()
|
|
|
.orElse(null);
|
|
|
- String approveMode;
|
|
|
- if (currentNode == null) {
|
|
|
- approveMode = "or";
|
|
|
- } else {
|
|
|
- approveMode = this.getApproveMode(currentNode);
|
|
|
- }
|
|
|
|
|
|
- // 会签模式:检查同节点是否还有未处理的任务
|
|
|
- if ("and".equals(approveMode)) {
|
|
|
- long pendingCount = this.approvalTaskMapper.selectCount(
|
|
|
- new LambdaQueryWrapper<ApprovalTask>()
|
|
|
- .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
- .eq(ApprovalTask::getNodeId, lockedTask.getNodeId())
|
|
|
- .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode()));
|
|
|
- if (pendingCount > 0) {
|
|
|
- // 还有未处理的任务,不推进流程
|
|
|
+ // 子节点处理
|
|
|
+ if (currentNode != null && this.isSubNodesEnabled(currentNode)) {
|
|
|
+ boolean parentCompleted = this.handleSubNodeApprove(instance, currentNode, lockedTask, model, comment);
|
|
|
+ if (!parentCompleted) {
|
|
|
return;
|
|
|
}
|
|
|
} else {
|
|
|
- // 或签模式:将同节点其他 PENDING 任务标记为 SKIPPED
|
|
|
- this.approvalTaskMapper.update(null,
|
|
|
- new LambdaUpdateWrapper<ApprovalTask>()
|
|
|
- .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
- .eq(ApprovalTask::getNodeId, lockedTask.getNodeId())
|
|
|
- .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode())
|
|
|
- .ne(ApprovalTask::getId, lockedTask.getId())
|
|
|
- .set(ApprovalTask::getTaskStatus, TaskStatus.SKIPPED.getCode()));
|
|
|
+ String approveMode;
|
|
|
+ if (currentNode == null) {
|
|
|
+ approveMode = "or";
|
|
|
+ } else {
|
|
|
+ approveMode = this.getApproveMode(currentNode);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 会签模式:检查同节点是否还有未处理的任务
|
|
|
+ if ("and".equals(approveMode)) {
|
|
|
+ long pendingCount = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, lockedTask.getNodeId())
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode()));
|
|
|
+ if (pendingCount > 0) {
|
|
|
+ // 还有未处理的任务,不推进流程
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 或签模式:将同节点其他 PENDING 任务标记为 SKIPPED
|
|
|
+ this.approvalTaskMapper.update(null,
|
|
|
+ new LambdaUpdateWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, lockedTask.getNodeId())
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode())
|
|
|
+ .ne(ApprovalTask::getId, lockedTask.getId())
|
|
|
+ .set(ApprovalTask::getTaskStatus, TaskStatus.SKIPPED.getCode()));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 指定了目标节点 → 跳过条件判断,直接走该分支
|
|
|
@@ -585,6 +692,12 @@ public class FlowEngineServiceImpl implements FlowEngineService {
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
+ // 审批节点:若开启子节点,则按子节点创建任务,不创建父节点任务
|
|
|
+ if (NodeType.APPROVAL.getCode().equals(node.getType()) && this.isSubNodesEnabled(node)) {
|
|
|
+ this.createSubNodeTasks(instance, node, model, visited, depth, allowRecreate);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
// 确定处理人类型
|
|
|
Map<String, Object> nodeProps = node.getProperties();
|
|
|
String assigneeType = ASSIGNEE_TYPE_ROLE;
|
|
|
@@ -772,4 +885,220 @@ public class FlowEngineServiceImpl implements FlowEngineService {
|
|
|
}
|
|
|
return "remind";
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 子节点任务创建与推进 ====================
|
|
|
+
|
|
|
+ private void createSubNodeTasks(ProcessInstance instance, FlowNode node, FlowModel model,
|
|
|
+ Set<String> visited, int depth, boolean allowRecreate) {
|
|
|
+ List<SubNodeConfig> subNodes = this.extractSubNodes(node);
|
|
|
+ if (subNodes.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ boolean sequential = this.isSequentialMode(node);
|
|
|
+ if (sequential) {
|
|
|
+ SubNodeConfig first = subNodes.get(0);
|
|
|
+ if (!allowRecreate && this.hasExistingTaskForSubNode(instance.getId(), node.getId(), first.getId())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.createSubNodeTask(instance, node, first, model);
|
|
|
+ } else {
|
|
|
+ for (SubNodeConfig subNode : subNodes) {
|
|
|
+ if (!allowRecreate && this.hasExistingTaskForSubNode(instance.getId(), node.getId(), subNode.getId())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ this.createSubNodeTask(instance, node, subNode, model);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createSubNodeTask(ProcessInstance instance, FlowNode parentNode, SubNodeConfig subNode, FlowModel model) {
|
|
|
+ List<Long> assignees = this.calculateSubNodeAssignees(subNode, parentNode, instance);
|
|
|
+ String taskAssigneeType = ASSIGNEE_TYPE_ROLE;
|
|
|
+ if (assignees.isEmpty()) {
|
|
|
+ assignees = Collections.singletonList(instance.getApplicantId());
|
|
|
+ taskAssigneeType = ASSIGNEE_TYPE_USER;
|
|
|
+ }
|
|
|
+ LocalDateTime timeoutTime = this.subNodeTimeoutTime(subNode);
|
|
|
+ String timeoutAction = this.subNodeTimeoutAction(subNode);
|
|
|
+ for (Long assigneeId : assignees) {
|
|
|
+ ApprovalTask task = this.approvalTaskAssembler.buildNew(
|
|
|
+ instance.getId(), parentNode.getId(), parentNode.getName(),
|
|
|
+ parentNode.getType(), subNode.getId(), assigneeId, taskAssigneeType, TaskStatus.PENDING.getCode()
|
|
|
+ );
|
|
|
+ task.setTimeoutTime(timeoutTime);
|
|
|
+ task.setTimeoutAction(timeoutAction);
|
|
|
+ this.approvalTaskMapper.insert(task);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理子节点审批通过。
|
|
|
+ * 返回 true 表示父节点已可继续推进主流程;false 表示仍需等待其他子节点。
|
|
|
+ */
|
|
|
+ private boolean handleSubNodeApprove(ProcessInstance instance, FlowNode currentNode, ApprovalTask lockedTask,
|
|
|
+ FlowModel model, String comment) {
|
|
|
+ List<SubNodeConfig> subNodes = this.extractSubNodes(currentNode);
|
|
|
+ if (subNodes.isEmpty()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ String subNodeId = lockedTask.getSubNodeId();
|
|
|
+ SubNodeConfig currentSubNode = subNodeId != null ? this.findSubNodeById(currentNode, subNodeId) : null;
|
|
|
+ boolean sequential = this.isSequentialMode(currentNode);
|
|
|
+
|
|
|
+ if (currentSubNode != null) {
|
|
|
+ String subMode = this.getSubNodeApproveMode(currentSubNode);
|
|
|
+ if ("and".equals(subMode)) {
|
|
|
+ long pendingCount = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, currentNode.getId())
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNodeId)
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode()));
|
|
|
+ if (pendingCount > 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 或签:将同子节点其他 PENDING 任务标记为 SKIPPED
|
|
|
+ this.approvalTaskMapper.update(null,
|
|
|
+ new LambdaUpdateWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, currentNode.getId())
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNodeId)
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode())
|
|
|
+ .ne(ApprovalTask::getId, lockedTask.getId())
|
|
|
+ .set(ApprovalTask::getTaskStatus, TaskStatus.SKIPPED.getCode()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sequential) {
|
|
|
+ int idx = this.findSubNodeIndex(currentNode, subNodeId);
|
|
|
+ if (idx < 0 || idx >= subNodes.size()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (idx == subNodes.size() - 1) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ SubNodeConfig nextSubNode = subNodes.get(idx + 1);
|
|
|
+ if (!this.hasPendingSubNodeTask(instance.getId(), currentNode.getId(), nextSubNode.getId())) {
|
|
|
+ this.createSubNodeTask(instance, currentNode, nextSubNode, model);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ return this.hasAllSubNodesCompleted(instance, currentNode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理子节点审批驳回。
|
|
|
+ * 顺序模式下驳回到上一个子节点(第一个子节点驳回则驳回整个父节点);
|
|
|
+ * 并行模式下仅重置当前子节点任务为待重新审批。
|
|
|
+ */
|
|
|
+ private void handleSubNodeReject(ProcessInstance instance, ApprovalTask currentTask, String comment) {
|
|
|
+ this.lockInstance(instance.getId());
|
|
|
+ ApprovalTask lockedTask = this.approvalTaskMapper.selectByIdForUpdate(currentTask.getId());
|
|
|
+ if (lockedTask == null || !TaskStatus.PENDING.getCode().equals(lockedTask.getTaskStatus())) {
|
|
|
+ throw new BusinessException("任务已处理");
|
|
|
+ }
|
|
|
+ this.completeCurrentTask(lockedTask, ApprovalResult.REJECT.getCode(), comment);
|
|
|
+
|
|
|
+ FlowModel model = this.getModelByInstance(instance);
|
|
|
+ FlowNode currentNode = safeNodes(model).stream()
|
|
|
+ .filter(n -> n.getId().equals(lockedTask.getNodeId()))
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ if (currentNode == null) {
|
|
|
+ this.rejectInstance(instance);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<SubNodeConfig> subNodes = this.extractSubNodes(currentNode);
|
|
|
+ String subNodeId = lockedTask.getSubNodeId();
|
|
|
+ int idx = this.findSubNodeIndex(currentNode, subNodeId);
|
|
|
+ boolean sequential = this.isSequentialMode(currentNode);
|
|
|
+
|
|
|
+ if (sequential) {
|
|
|
+ if (idx <= 0) {
|
|
|
+ // 第一个子节点驳回,驳回整个父节点
|
|
|
+ this.approvalTaskHelper.cancelPendingTasks(instance.getId(), LocalDateTime.now());
|
|
|
+ this.rejectInstance(instance);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 取消当前及之后子节点的待处理任务
|
|
|
+ List<String> subNodeIdsToCancel = subNodes.subList(idx, subNodes.size()).stream()
|
|
|
+ .map(SubNodeConfig::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (!subNodeIdsToCancel.isEmpty()) {
|
|
|
+ this.approvalTaskMapper.update(null,
|
|
|
+ new LambdaUpdateWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, currentNode.getId())
|
|
|
+ .in(ApprovalTask::getSubNodeId, subNodeIdsToCancel)
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode())
|
|
|
+ .set(ApprovalTask::getTaskStatus, TaskStatus.SKIPPED.getCode()));
|
|
|
+ }
|
|
|
+ // 创建上一个子节点的新任务
|
|
|
+ SubNodeConfig prevSubNode = subNodes.get(idx - 1);
|
|
|
+ if (!this.hasPendingSubNodeTask(instance.getId(), currentNode.getId(), prevSubNode.getId())) {
|
|
|
+ this.createSubNodeTask(instance, currentNode, prevSubNode, model);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 并行模式:重新创建当前子节点任务,旧任务保留为驳回记录
|
|
|
+ SubNodeConfig subNode = this.findSubNodeById(currentNode, subNodeId);
|
|
|
+ if (subNode != null) {
|
|
|
+ this.createSubNodeTask(instance, currentNode, subNode, model);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasAllSubNodesCompleted(ProcessInstance instance, FlowNode currentNode) {
|
|
|
+ List<SubNodeConfig> subNodes = this.extractSubNodes(currentNode);
|
|
|
+ for (SubNodeConfig subNode : subNodes) {
|
|
|
+ long pendingCount = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, currentNode.getId())
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNode.getId())
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode()));
|
|
|
+ long passedCount = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instance.getId())
|
|
|
+ .eq(ApprovalTask::getNodeId, currentNode.getId())
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNode.getId())
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.HANDLED.getCode())
|
|
|
+ .eq(ApprovalTask::getApprovalResult, ApprovalResult.PASS.getCode()));
|
|
|
+ if (pendingCount > 0 || passedCount == 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasPendingSubNodeTask(Long instanceId, String nodeId, String subNodeId) {
|
|
|
+ if (instanceId == null || nodeId == null || subNodeId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ long count = this.approvalTaskMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ApprovalTask>()
|
|
|
+ .eq(ApprovalTask::getInstanceId, instanceId)
|
|
|
+ .eq(ApprovalTask::getNodeId, nodeId)
|
|
|
+ .eq(ApprovalTask::getSubNodeId, subNodeId)
|
|
|
+ .eq(ApprovalTask::getTaskStatus, TaskStatus.PENDING.getCode()));
|
|
|
+ return count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LocalDateTime subNodeTimeoutTime(SubNodeConfig subNode) {
|
|
|
+ Integer hours = subNode.getTimeoutHours();
|
|
|
+ if (hours == null || hours <= 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return LocalDateTime.now().plusHours(hours);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String subNodeTimeoutAction(SubNodeConfig subNode) {
|
|
|
+ String action = subNode.getTimeoutAction();
|
|
|
+ if ("pass".equals(action) || "reject".equals(action)) {
|
|
|
+ return action;
|
|
|
+ }
|
|
|
+ return "remind";
|
|
|
+ }
|
|
|
}
|