From a8e4132c3e2e9c3b3fcb0360901b35571b6464ea Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期五, 28 八月 2026 10:13:01 +0800
Subject: [PATCH] feat: 综合业务模块功能补充与实体拓展

---
 src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java |  292 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 261 insertions(+), 31 deletions(-)

diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
index 251ff05..6d3b8d6 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -25,6 +25,7 @@
 import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
 import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
 import com.ruoyi.common.enums.*;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.framework.security.LoginUser;
@@ -47,6 +48,11 @@
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.staff.mapper.HolidayApplicationMapper;
 import com.ruoyi.staff.pojo.HolidayApplication;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
+import com.ruoyi.device.pojo.DeviceLedger;
+import com.ruoyi.device.service.IDeviceLedgerService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -93,6 +99,8 @@
     private final EnterpriseNewsMapper enterpriseNewsMapper;
     private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
     private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
+    private final IDeviceStatusChangeRecordService deviceStatusChangeRecordService;
+    private final IDeviceLedgerService deviceLedgerService;
 
     @Override
     public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
@@ -104,6 +112,29 @@
         }
         records.forEach(vo -> {
             vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType()));
+
+            // 鏍规嵁涓氬姟绫诲瀷鏌ヨ瀵瑰簲鐨勫崟鍙�
+            if (vo.getBusinessType() != null && vo.getBusinessId() != null) {
+                if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(vo.getBusinessType())) {
+                    // 閲囪喘瀹℃壒 - 鏌ヨ閲囪喘鍗曞彿
+                    PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(vo.getBusinessId());
+                    if (purchaseLedger != null) {
+                        vo.setPurchaseContractNumber(purchaseLedger.getPurchaseContractNumber());
+                    }
+                } else if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(vo.getBusinessType())) {
+                    // 鎶ヤ环瀹℃壒 - 鏌ヨ鎶ヤ环鍗曞彿
+                    SalesQuotation salesQuotation = salesQuotationMapper.selectById(vo.getBusinessId());
+                    if (salesQuotation != null) {
+                        vo.setQuotationNo(salesQuotation.getQuotationNo());
+                    }
+                } else if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(vo.getBusinessType())) {
+                    // 鍙戣揣瀹℃壒 - 鏌ヨ鍙戣揣鍗曞彿
+                    ShippingInfo shippingInfo = shippingInfoMapper.selectById(vo.getBusinessId());
+                    if (shippingInfo != null) {
+                        vo.setShippingNo(shippingInfo.getShippingNo());
+                    }
+                }
+            }
         });
         Long currentUserId = SecurityUtils.getUserId();
 
@@ -139,10 +170,67 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean add(ApprovalInstanceDto approvalInstanceDto) {
-        String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no");
+        String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", approvalInstanceDto.getCreateTime() != null ? approvalInstanceDto.getCreateTime() : LocalDateTime.now());
         approvalInstanceDto.setInstanceNo(instanceNo);
         approvalInstanceDto.setStatus("PENDING");
         approvalInstanceDto.setCurrentLevel(1);
+
+        if (approvalInstanceDto.getApplicantId() == null) {
+            approvalInstanceDto.setApplicantId(SecurityUtils.getUserId());
+            LoginUser loginUser = SecurityUtils.getLoginUser();
+            if (loginUser != null && loginUser.getUser() != null) {
+                approvalInstanceDto.setApplicantName(loginUser.getUser().getNickName());
+            }
+        }
+
+        // 鏍¢獙璁惧鐘舵�佸彉鏇村鎵癸細濡傛灉璇ヨ澶囧凡鏈夋鍦ㄥ鎵圭殑浠诲姟锛屾棤娉曞啀娆¤繘琛屽鎵�
+        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(approvalInstanceDto.getBusinessType())) {
+            Long deviceId = approvalInstanceDto.getBusinessId();
+
+            long pendingCount = deviceStatusChangeRecordService.count(Wrappers.<DeviceStatusChangeRecord>lambdaQuery()
+                    .eq(DeviceStatusChangeRecord::getDeviceId, deviceId)
+                    .eq(DeviceStatusChangeRecord::getApprovalStatus, "PENDING")
+                    .eq(DeviceStatusChangeRecord::getDeleted, 0)
+            );
+            if (pendingCount > 0) {
+                throw new ServiceException("璇ヨ澶囧凡鏈夋鍦ㄨ繘琛岀殑鐘舵�佸彉鏇村鎵癸紝鏃犳硶鍐嶆鎻愪氦");
+            }
+
+            DeviceLedger device = deviceLedgerService.getById(deviceId);
+            if (device == null) {
+                throw new ServiceException("璁惧涓嶅瓨鍦�");
+            }
+
+            Integer targetStatus = null;
+            String reason = "";
+            if (StringUtils.hasText(approvalInstanceDto.getFormConfig())) {
+                try {
+                    JSONObject formObj = JSONObject.parseObject(approvalInstanceDto.getFormConfig());
+                    if (formObj.containsKey("targetStatus")) {
+                        targetStatus = formObj.getInteger("targetStatus");
+                    } else if (formObj.containsKey("status")) {
+                        targetStatus = formObj.getInteger("status");
+                    }
+                    if (formObj.containsKey("reason")) {
+                        reason = formObj.getString("reason");
+                    }
+                } catch (Exception e) {
+                    log.error("瑙f瀽鐘舵�佸彉鏇磋〃鍗曞け璐�", e);
+                }
+            }
+
+            DeviceStatusChangeRecord record = new DeviceStatusChangeRecord();
+            record.setDeviceId(deviceId);
+            record.setOriginalStatus(Integer.valueOf(device.getStatus()));
+            record.setTargetStatus(targetStatus);
+            record.setReason(reason);
+            record.setApprovalStatus("PENDING");
+            deviceStatusChangeRecordService.save(record);
+
+            // 缁戝畾鍒板叧鑱旇褰曠殑涓婚敭
+            approvalInstanceDto.setBusinessId(record.getId());
+        }
+
         boolean saved = this.save(approvalInstanceDto);
         if (!saved) {
             return false;
@@ -158,6 +246,16 @@
     public Boolean update(ApprovalInstanceDto approvalInstanceDto) {
         if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
             return false;
+        }
+        // 鍒ゆ柇鏄惁鏈夋鍦ㄨ繘琛岀殑瀹℃壒浠诲姟锛屾湁鍒欎笉鍏佽淇敼
+        long pendingTaskCount = approvalTaskService.count(
+                Wrappers.<ApprovalTask>lambdaQuery()
+                        .eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+        );
+        if (pendingTaskCount > 0) {
+            throw new ServiceException("璇ュ鎵瑰崟鏈夋鍦ㄨ繘琛岀殑瀹℃壒浠诲姟锛屼笉鍏佽淇敼");
         }
         boolean updated = this.updateById(approvalInstanceDto);
         if (!updated) {
@@ -250,13 +348,94 @@
         );
         //瀹℃壒鎷掔粷鐨勫鐞�
         if ("REJECTED".equals(approveAction)) {
-            return rejectCurrentNode(instance, currentNode, now);
+            return rejectCurrentNode(instance, currentNode, approvalInstanceDto.getApproveComment(), now);
         }
         if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
             return R.ok("瀹℃壒鎴愬姛锛岀瓑寰呭叾浠栧鎵逛汉澶勭悊");
         }
 
         return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R autoApprove(Long instanceId) {
+        if (instanceId == null) {
+            return R.fail("瀹℃壒瀹炰緥 ID 涓嶈兘涓虹┖");
+        }
+
+        ApprovalInstance instance = getPendingApprovalInstance(instanceId);
+        if (instance == null) {
+            return R.fail("瀹℃壒瀹炰緥涓嶅瓨鍦�");
+        }
+        if ("REJECTED".equals(instance.getStatus())) {
+            return R.fail("瀹℃壒宸查┏鍥烇紝鏃犳硶鑷姩閫氳繃");
+        }
+        if ("APPROVED".equals(instance.getStatus())) {
+            return R.ok("瀹℃壒宸插畬鎴�");
+        }
+
+        ApprovalInstanceDto autoApproveDto = new ApprovalInstanceDto();
+        autoApproveDto.setId(instanceId);
+        autoApproveDto.setApproveComment("绯荤粺鑷姩瀹℃壒");
+
+        int loopCount = 0;
+        while (loopCount++ < 20) {
+            ApprovalInstance currentInstance = getPendingApprovalInstance(instanceId);
+            if (currentInstance == null) {
+                return R.fail("瀹℃壒瀹炰緥涓嶅瓨鍦�");
+            }
+            if ("APPROVED".equals(currentInstance.getStatus())) {
+                return R.ok("瀹℃壒宸插畬鎴�");
+            }
+            if ("REJECTED".equals(currentInstance.getStatus())) {
+                return R.fail("瀹℃壒宸查┏鍥烇紝鏃犳硶鑷姩閫氳繃");
+            }
+
+            ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(currentInstance.getId());
+            if (currentNode == null) {
+                currentInstance.setStatus("APPROVED");
+                currentInstance.setFinishTime(LocalDateTime.now());
+                this.updateById(currentInstance);
+                handleBusinessAfterApprovalFinished(currentInstance, null);
+                return R.ok("瀹℃壒宸插畬鎴�");
+            }
+
+            List<ApprovalTask> pendingTasks = approvalTaskService.list(
+                    Wrappers.<ApprovalTask>lambdaQuery()
+                            .eq(ApprovalTask::getInstanceId, currentInstance.getId())
+                            .eq(ApprovalTask::getNodeId, currentNode.getId())
+                            .eq(ApprovalTask::getTaskStatus, "PENDING")
+                            .eq(ApprovalTask::getDeleted, 0)
+            );
+
+            LocalDateTime now = LocalDateTime.now();
+            for (ApprovalTask currentTask : pendingTasks) {
+                if (!updateCurrentTask(autoApproveDto, "APPROVED", currentTask, now)) {
+                    return R.fail("褰撳墠浠诲姟宸茶澶勭悊锛岃鍒锋柊鍚庨噸璇�");
+                }
+                saveApprovalRecord(
+                        currentInstance.getId(),
+                        currentNode.getId(),
+                        currentTask.getId(),
+                        0L,
+                        "绯荤粺鑷姩瀹℃壒",
+                        "APPROVED",
+                        autoApproveDto.getApproveComment()
+                );
+            }
+
+            if (!approveProcessConfigNodeUtils.canProceedToNextLevel(currentInstance.getId(), currentNode.getApproveType())) {
+                return R.ok("瀹℃壒鎴愬姛锛岀瓑寰呭叾浠栧鎵逛汉澶勭悊");
+            }
+
+            R moveResult = moveToNextLevel(currentInstance, currentNode, autoApproveDto, now, false);
+            if (!R.isSuccess(moveResult)) {
+                return moveResult;
+            }
+        }
+
+        return R.fail("鑷姩瀹℃壒寰幆娆℃暟瓒呴檺");
     }
 
     private String normalizeApproveAction(String approveAction) {
@@ -295,7 +474,7 @@
         );
     }
 
-    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
+    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, String approveComment, LocalDateTime now) {
         if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
             return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
         }
@@ -304,20 +483,8 @@
         instance.setStatus("REJECTED");
         instance.setFinishTime(now);
         this.updateById(instance);
-        // 椹冲洖瀵瑰簲鐨勪紒涓氭柊闂伙紝 宸梾鎶ラ攢
-        if (instance.getBusinessType().equals(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())) {
-            enterpriseNewsMapper.update(
-                    new LambdaUpdateWrapper<EnterpriseNews>()
-                            .eq(EnterpriseNews::getId, instance.getBusinessId())
-                            .set(EnterpriseNews::getStatus, "REJECTED")
-            );
-        }else if (instance.getBusinessType().equals(TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode())||instance.getBusinessType().equals(TypeEnums.EXPENSE_APPROVAL.getCode())) {
-            finReimbursementMapper.update(
-                    new LambdaUpdateWrapper<FinReimbursement>()
-                            .eq(FinReimbursement::getId, instance.getBusinessId())
-                            .set(FinReimbursement::getBillStatus, "REJECTED")
-            );
-        }
+        // 缁熶竴澶勭悊涓氬姟鐘舵�佹洿鏂�
+        handleBusinessAfterApprovalFinished(instance, approveComment);
         return R.ok("瀹℃壒宸查┏鍥�");
     }
 
@@ -325,6 +492,14 @@
                                  ApprovalInstanceNode currentNode,
                                  ApprovalInstanceDto approvalInstanceDto,
                                  LocalDateTime now) {
+        return moveToNextLevel(instance, currentNode, approvalInstanceDto, now, true);
+    }
+
+    private R moveToNextLevel(ApprovalInstance instance,
+                              ApprovalInstanceNode currentNode,
+                              ApprovalInstanceDto approvalInstanceDto,
+                              LocalDateTime now,
+                              boolean notifyNextNode) {
         if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) {
             return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
         }
@@ -348,14 +523,16 @@
             instance.setCurrentLevel(nextLevel);
             instance.setStatus("PENDING");
             this.updateById(instance);
-            List<ApprovalTask> nextTasks = approvalTaskService.list(
-                    Wrappers.<ApprovalTask>lambdaQuery()
-                            .eq(ApprovalTask::getInstanceId, instance.getId())
-                            .eq(ApprovalTask::getNodeId, nextInstanceNode.getId())
-                            .eq(ApprovalTask::getTaskStatus, "PENDING")
-                            .eq(ApprovalTask::getDeleted, 0)
-            );
-            sendApproveNotice(instance, nextTasks);
+            if (notifyNextNode) {
+                List<ApprovalTask> nextTasks = approvalTaskService.list(
+                        Wrappers.<ApprovalTask>lambdaQuery()
+                                .eq(ApprovalTask::getInstanceId, instance.getId())
+                                .eq(ApprovalTask::getNodeId, nextInstanceNode.getId())
+                                .eq(ApprovalTask::getTaskStatus, "PENDING")
+                                .eq(ApprovalTask::getDeleted, 0)
+                );
+                sendApproveNotice(instance, nextTasks);
+            }
             return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
         }
 
@@ -371,7 +548,7 @@
             instance.setStatus("APPROVED");
             instance.setFinishTime(now);
             this.updateById(instance);
-            handleBusinessAfterApprovalFinished(instance);
+            handleBusinessAfterApprovalFinished(instance, null);
             return R.ok("瀹℃壒宸插畬鎴�");
         }
 
@@ -379,7 +556,9 @@
         instance.setStatus("PENDING");
         this.updateById(instance);
         approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false);
-        sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        if (notifyNextNode) {
+            sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        }
         return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
     }
 
@@ -406,7 +585,7 @@
         );
     }
 
-    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
+    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance, String rejectReason) {
         String status = instance.getStatus();
         Long businessType = instance.getBusinessType();
         if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
@@ -434,7 +613,39 @@
         }
         if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
             handleNewsApprovalFinished(instance, status);
+            return;
         }
+        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(businessType)) {
+            handleDeviceStatusChangeApprovalFinished(instance, status, rejectReason);
+            return;
+        }
+    }
+
+    private void handleDeviceStatusChangeApprovalFinished(ApprovalInstance instance, String status, String rejectReason) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        DeviceStatusChangeRecord record = deviceStatusChangeRecordService.getById(instance.getBusinessId());
+        if (record == null) {
+            return;
+        }
+        if ("APPROVED".equals(status)) {
+            record.setApprovalStatus("APPROVED");
+            if (record.getTargetStatus() != null) {
+                DeviceLedger device = new DeviceLedger();
+                device.setId(record.getDeviceId());
+                device.setStatus(String.valueOf(record.getTargetStatus()));
+                deviceLedgerService.updateById(device);
+            }
+        } else if ("REJECTED".equals(status)) {
+            record.setApprovalStatus("REJECTED");
+            if (rejectReason != null) {
+                record.setRejectReason(rejectReason);
+            }
+        } else if ("PENDING".equals(status)) {
+            record.setApprovalStatus("PENDING");
+        }
+        deviceStatusChangeRecordService.updateById(record);
     }
 
     private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
@@ -496,7 +707,7 @@
     private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) {
         PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(
                 new LambdaQueryWrapper<PurchaseLedger>()
-                        .eq(PurchaseLedger::getPurchaseContractNumber, instance.getTitle())
+                        .eq(PurchaseLedger::getId, instance.getBusinessId())
                         .last("limit 1")
         );
         if (purchaseLedger == null) {
@@ -534,7 +745,7 @@
     private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) {
         SalesQuotation salesQuote = salesQuotationMapper.selectOne(
                 new LambdaQueryWrapper<SalesQuotation>()
-                        .eq(SalesQuotation::getQuotationNo, instance.getTitle())
+                        .eq(SalesQuotation::getId, instance.getBusinessId())
                         .last("limit 1")
         );
         if (salesQuote == null) {
@@ -629,10 +840,29 @@
 
         String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "瀹℃壒鎻愰啋";
         String message = "瀹℃壒鍗曞彿 " + instance.getInstanceNo() + " 闇�瑕佹偍瀹℃壒";
-        String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId();
+        String jumpPath = getJumpPathByBusinessType(instance.getBusinessType(), instance.getId());
         sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
     }
 
+    private String getJumpPathByBusinessType(Long businessType, Long instanceId) {
+        if (businessType == null) {
+            return "/collaborativeApproval/approve-list?id=" + instanceId;
+        }
+        if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType)) {
+            return "/collaborativeApproval/AttendManage/leave-apply?id=" + instanceId;
+        }
+        if (TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) {
+            return "/collaborativeApproval/AttendManage/overtime-apply?id=" + instanceId;
+        }
+        if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)) {
+            return "/collaborativeApproval/ReimburseManage/travel-reimburse?id=" + instanceId;
+        }
+        if (TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
+            return "/collaborativeApproval/ReimburseManage/cost-reimburse?id=" + instanceId;
+        }
+        return "/collaborativeApproval/approve-list?id=" + instanceId;
+    }
+
     private void sendEnterpriseNewsNotice(Long newsId) {
         EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId);
         if (enterpriseNews == null) {

--
Gitblit v1.9.3