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