From 79cceefe43f14643781b76def9b980398990d458 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期三, 20 五月 2026 11:48:10 +0800
Subject: [PATCH] feat(approve): 添加审批实例相关DTO、VO类及审批流程工具类

---
 src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java |  363 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 362 insertions(+), 1 deletions(-)

diff --git a/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java b/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
index 4ae03e6..239726a 100644
--- a/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
+++ b/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
@@ -1,9 +1,370 @@
 package com.ruoyi.approve.utils;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalInstanceNode;
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.ruoyi.approve.service.ApprovalInstanceNodeService;
+import com.ruoyi.approve.service.ApprovalRecordService;
+import com.ruoyi.approve.service.ApprovalTaskService;
+import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService;
+import com.ruoyi.approve.service.ApprovalTemplateNodeService;
 import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 瀹℃壒娴佺▼鑺傜偣宸ュ叿绫�
+ */
+@Component
 @RequiredArgsConstructor
 public class ApproveProcessConfigNodeUtils {
 
+    private final ApprovalInstanceNodeService instanceNodeService;
+    private final ApprovalTaskService approvalTaskService;
+    private final ApprovalRecordService approvalRecordService;
+    private final ApprovalTemplateNodeService approvalTemplateNodeService;
+    private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService;
 
-}
+    /**
+     * 鎸夊綋鍓嶅眰绾у垱寤哄鎵硅妭鐐瑰拰瀹℃壒浠诲姟銆�
+     * 璇ラ噸杞戒細鍚屾椂鍐欏叆涓�鏉″彂璧峰鎵硅褰曘��
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance) {
+        return createCurrentNodeAndTasks(instance, true);
+    }
+
+    /**
+     * 鎸夊綋鍓嶅眰绾у垱寤哄鎵硅妭鐐瑰拰瀹℃壒浠诲姟銆�
+     *
+     * @param instance 瀹℃壒瀹炰緥
+     * @param createSubmitRecord 鏄惁鍒涘缓鍙戣捣瀹℃壒璁板綍
+     * @return 鍒涘缓鍑虹殑褰撳墠鑺傜偣瀹炰緥
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance, boolean createSubmitRecord) {
+        if (instance == null || instance.getId() == null) {
+            throw new RuntimeException("瀹℃壒瀹炰緥涓嶈兘涓虹┖");
+        }
+        if (instance.getTemplateId() == null) {
+            throw new RuntimeException("瀹℃壒妯℃澘涓嶈兘涓虹┖");
+        }
+
+        Integer currentLevel = instance.getCurrentLevel() == null ? 1 : instance.getCurrentLevel();
+
+        ApprovalInstanceNode existsNode = instanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instance.getId())
+                        .eq(ApprovalInstanceNode::getLevelNo, currentLevel)
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+        if (existsNode != null) {
+            return existsNode;
+        }
+
+        ApprovalTemplateNode templateNode = approvalTemplateNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNode::getLevelNo, currentLevel)
+                        .orderByAsc(ApprovalTemplateNode::getId)
+                        .last("LIMIT 1")
+        );
+        if (templateNode == null) {
+            throw new RuntimeException("鏈壘鍒板綋鍓嶅眰绾у搴旂殑瀹℃壒妯℃澘鑺傜偣");
+        }
+
+        List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverService.list(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId())
+                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
+                        .orderByAsc(ApprovalTemplateNodeApprover::getSortNo)
+        );
+        if (approvers == null || approvers.isEmpty()) {
+            throw new RuntimeException("褰撳墠瀹℃壒鑺傜偣鏈厤缃鎵逛汉");
+        }
+
+        ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
+        instanceNode.setInstanceId(instance.getId());
+        instanceNode.setLevelNo(templateNode.getLevelNo());
+        instanceNode.setApproveType(templateNode.getApproveType());
+        instanceNode.setStatus("PENDING");
+        instanceNode.setStartTime(LocalDateTime.now());
+        instanceNode.setDeleted((byte) 0);
+        instanceNodeService.save(instanceNode);
+
+        List<ApprovalTask> taskList = new ArrayList<>(approvers.size());
+        for (ApprovalTemplateNodeApprover approver : approvers) {
+            ApprovalTask task = new ApprovalTask();
+            task.setInstanceId(instance.getId());
+            task.setNodeId(instanceNode.getId());
+            task.setLevelNo(instanceNode.getLevelNo());
+            task.setApproverId(approver.getApproverId());
+            task.setApproverName(approver.getApproverName());
+            task.setTaskStatus("PENDING");
+            task.setIsRead((byte) 0);
+            task.setDeleted((byte) 0);
+            taskList.add(task);
+        }
+        approvalTaskService.saveBatch(taskList);
+
+        if (createSubmitRecord) {
+            ApprovalRecord record = new ApprovalRecord();
+            record.setInstanceId(instance.getId());
+            record.setNodeId(instanceNode.getId());
+            record.setOperatorId(instance.getApplicantId());
+            record.setOperatorName(instance.getApplicantName());
+            record.setAction("SUBMIT");
+            record.setComment("鍙戣捣瀹℃壒");
+            record.setDeleted((byte) 0);
+            approvalRecordService.save(record);
+        }
+
+        return instanceNode;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠寰呭鐞嗚妭鐐广��
+     */
+    public ApprovalInstanceNode getCurrentNode(Long instanceId) {
+        if (instanceId == null) {
+            return null;
+        }
+
+        return instanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instanceId)
+                        .eq(ApprovalInstanceNode::getStatus, "PENDING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .orderByAsc(ApprovalInstanceNode::getLevelNo)
+                        .last("LIMIT 1")
+        );
+    }
+
+    /**
+     * 鏌ヨ褰撳墠瀹℃壒灞傜骇銆�
+     */
+    public Integer getCurrentLevel(Long instanceId) {
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        return currentNode != null ? currentNode.getLevelNo() : null;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣涓嬬殑寰呭鎵逛换鍔°��
+     */
+    public List<ApprovalTask> getCurrentPendingTasks(Long instanceId) {
+        if (instanceId == null) {
+            return List.of();
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return List.of();
+        }
+
+        return approvalTaskService.list(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .orderByAsc(ApprovalTask::getLevelNo)
+        );
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鍦ㄥ綋鍓嶈妭鐐逛笂鐨勫緟瀹℃壒浠诲姟銆�
+     */
+    public ApprovalTask getCurrentUserTask(Long instanceId, Long userId) {
+        if (instanceId == null || userId == null) {
+            return null;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return null;
+        }
+
+        return approvalTaskService.getOne(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁鏄綋鍓嶅鎵逛汉銆�
+     */
+    public boolean isCurrentApprover(Long instanceId, Long userId) {
+        return getCurrentUserTask(instanceId, userId) != null;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣鐨勫鎵逛汉 ID 鍒楄〃銆�
+     */
+    public List<Long> getCurrentNodeApproverIds(Long instanceId) {
+        return getCurrentPendingTasks(instanceId).stream()
+                .map(ApprovalTask::getApproverId)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣鍓╀綑寰呭鎵逛汉鏁般��
+     */
+    public int getRemainingApproverCount(Long instanceId) {
+        return getCurrentPendingTasks(instanceId).size();
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣宸插悓鎰忎汉鏁般��
+     */
+    public int getApprovedCount(Long instanceId) {
+        if (instanceId == null) {
+            return 0;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return 0;
+        }
+
+        return Math.toIntExact(approvalTaskService.count(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "APPROVED")
+                        .eq(ApprovalTask::getDeleted, 0)
+        ));
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣宸叉嫆缁濅汉鏁般��
+     */
+    public int getRejectedCount(Long instanceId) {
+        if (instanceId == null) {
+            return 0;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return 0;
+        }
+
+        return Math.toIntExact(approvalTaskService.count(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "REJECTED")
+                        .eq(ApprovalTask::getDeleted, 0)
+        ));
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鑺傜偣鏄惁鍙互娴佽浆鍒颁笅涓�灞傘��
+     */
+    public boolean canProceedToNextLevel(Long instanceId, String approveType) {
+        if (instanceId == null || approveType == null) {
+            return false;
+        }
+
+        if (getRejectedCount(instanceId) > 0) {
+            return false;
+        }
+
+        int totalApproverCount = getCurrentPendingTasks(instanceId).size() + getApprovedCount(instanceId);
+        int approvedCount = getApprovedCount(instanceId);
+
+        if ("AND".equalsIgnoreCase(approveType)) {
+            return approvedCount > 0 && approvedCount == totalApproverCount;
+        }
+        if ("OR".equalsIgnoreCase(approveType)) {
+            return approvedCount > 0;
+        }
+        return false;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鍦ㄥ綋鍓嶈妭鐐逛笂鐨勪换鍔$姸鎬併��
+     */
+    public String getUserTaskStatus(Long instanceId, Long userId) {
+        if (instanceId == null || userId == null) {
+            return null;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return null;
+        }
+
+        ApprovalTask task = approvalTaskService.getOne(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+
+        return task != null ? task.getTaskStatus() : null;
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾鐢ㄦ埛鐨勫叏閮ㄥ緟瀹℃壒浠诲姟銆�
+     */
+    public List<ApprovalTask> getUserAllPendingTasks(Long userId) {
+        if (userId == null) {
+            return List.of();
+        }
+
+        return approvalTaskService.list(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .orderByDesc(ApprovalTask::getCreateTime)
+        );
+    }
+
+    /**
+     * 鏌ヨ瀹℃壒瀹炰緥鐨勮繘搴︽憳瑕併��
+     */
+    public String getApprovalProgress(Long instanceId) {
+        if (instanceId == null) {
+            return "鏃犳晥鐨勫鎵瑰疄渚�";
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return "瀹℃壒宸插畬鎴愭垨灏氭湭寮�濮�";
+        }
+
+        int approvedCount = getApprovedCount(instanceId);
+        int rejectedCount = getRejectedCount(instanceId);
+        int pendingCount = getRemainingApproverCount(instanceId);
+        int totalCount = approvedCount + rejectedCount + pendingCount;
+
+        return String.format(
+                "绗�%d绾у鎵癸細鎬讳汉鏁�=%d锛屽凡鍚屾剰=%d锛屽凡鎷掔粷=%d锛屽緟瀹℃壒=%d",
+                currentNode.getLevelNo(),
+                totalCount,
+                approvedCount,
+                rejectedCount,
+                pendingCount
+        );
+    }
+}
\ No newline at end of file

--
Gitblit v1.9.3