From 620bb4712a31791231c4381581f0f60088f079fe Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 27 五月 2026 14:03:45 +0800
Subject: [PATCH] Merge branch 'refs/heads/dev_New_pro' into dev_宁夏_英泽防锈

---
 src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java |  753 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 753 insertions(+), 0 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
new file mode 100644
index 0000000..2a4f52a
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -0,0 +1,753 @@
+package com.ruoyi.approve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
+import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
+import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
+import com.ruoyi.approve.mapper.FinReimbursementMapper;
+import com.ruoyi.approve.pojo.*;
+import com.ruoyi.approve.service.*;
+import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+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;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.procurementrecord.utils.StockUtils;
+import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
+import com.ruoyi.purchase.pojo.PurchaseLedger;
+import com.ruoyi.quality.utils.QualityInspectHelper;
+import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.mapper.SalesQuotationMapper;
+import com.ruoyi.sales.mapper.ShippingInfoMapper;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.SalesQuotation;
+import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.staff.mapper.HolidayApplicationMapper;
+import com.ruoyi.staff.pojo.HolidayApplication;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @since 2026-05-18 03:27:46
+ */
+@Service
+@RequiredArgsConstructor
+public class ApprovalInstanceServiceImpl extends ServiceImpl<ApprovalInstanceMapper, ApprovalInstance> implements ApprovalInstanceService {
+
+    private static final String ENTERPRISE_NEWS_STATUS_PUBLISHED = "PUBLISHED";
+    private static final String ENTERPRISE_NEWS_STATUS_REJECTED = "REJECTED";
+
+    private final ApprovalInstanceMapper approvalInstanceMapper;
+    private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils;
+    private final ApprovalInstanceNodeService approvalInstanceNodeService;
+    private final ApprovalTaskService approvalTaskService;
+    private final ApprovalRecordService approvalRecordService;
+    private final ApprovalTemplateNodeService approvalTemplateNodeService;
+    private final FinReimbursementMapper finReimbursementMapper;
+    private final FileUtil fileUtil;
+    private final ISysNoticeService sysNoticeService;
+    private final PurchaseLedgerMapper purchaseLedgerMapper;
+    private final SalesLedgerProductMapper salesLedgerProductMapper;
+    private final StockUtils stockUtils;
+    private final SalesQuotationMapper salesQuotationMapper;
+    private final ShippingInfoMapper shippingInfoMapper;
+    private final QualityInspectHelper qualityInspectHelper;
+    private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper;
+    private final SysUserMapper sysUserMapper;
+    private final SysUserDeptMapper sysUserDeptMapper;
+    private final SysDeptMapper sysDeptMapper;
+    private final HolidayApplicationMapper holidayApplicationMapper;
+    private final EnterpriseNewsMapper enterpriseNewsMapper;
+    private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
+    private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
+
+    @Override
+    public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
+        IPage<ApprovalInstanceVo> approvalInstanceVoIPage = approvalInstanceMapper.listPage(page, approvalInstanceDto);
+
+        List<ApprovalInstanceVo> records = approvalInstanceVoIPage.getRecords();
+        if (records == null || records.isEmpty()) {
+            return R.ok(approvalInstanceVoIPage);
+        }
+        records.forEach(vo -> {
+            vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType()));
+        });
+        Long currentUserId = SecurityUtils.getUserId();
+
+        List<Long> instanceIds = records.stream()
+                .map(ApprovalInstanceVo::getId)
+                .filter(id -> id != null)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (!instanceIds.isEmpty()) {
+            Map<Long, List<ApprovalRecord>> recordMap = approvalRecordService.list(
+                    Wrappers.<ApprovalRecord>lambdaQuery()
+                            .in(ApprovalRecord::getInstanceId, instanceIds)
+                            .eq(ApprovalRecord::getDeleted, 0)
+            ).stream().collect(Collectors.groupingBy(ApprovalRecord::getInstanceId));
+            Map<Long, List<ApprovalTask>> taskMap = approvalTaskService.list(
+                    Wrappers.<ApprovalTask>lambdaQuery()
+                            .in(ApprovalTask::getInstanceId, instanceIds)
+                            .eq(ApprovalTask::getDeleted, 0)
+            ).stream().collect(Collectors.groupingBy(ApprovalTask::getInstanceId));
+
+            for (ApprovalInstanceVo vo : records) {
+                vo.setIsApprove(approveProcessConfigNodeUtils.isCurrentApprover(vo.getId(), currentUserId));
+                vo.setRecords(recordMap.getOrDefault(vo.getId(), new ArrayList<>()));
+                vo.setTasks(taskMap.getOrDefault(vo.getId(), new ArrayList<>()));
+                vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.APPROVAL_INSTANCE, vo.getId()));
+            }
+
+        }
+        return R.ok(approvalInstanceVoIPage);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(ApprovalInstanceDto approvalInstanceDto) {
+        String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", approvalInstanceDto.getCreateTime() != null ? approvalInstanceDto.getCreateTime() : LocalDateTime.now());
+        approvalInstanceDto.setInstanceNo(instanceNo);
+        approvalInstanceDto.setStatus("PENDING");
+        approvalInstanceDto.setCurrentLevel(1);
+        boolean saved = this.save(approvalInstanceDto);
+        if (!saved) {
+            return false;
+        }
+        approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto);
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
+        sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    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) {
+            return false;
+        }
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids);
+
+        int instanceRows = approvalInstanceMapper.update(
+                null,
+                Wrappers.<ApprovalInstance>lambdaUpdate()
+                        .in(ApprovalInstance::getId, ids)
+                        .eq(ApprovalInstance::getDeleted, 0)
+                        .set(ApprovalInstance::getDeleted, (byte) 1)
+        );
+
+        LambdaUpdateWrapper<ApprovalInstanceNode> nodeUpdateWrapper = Wrappers.lambdaUpdate();
+        nodeUpdateWrapper.in(ApprovalInstanceNode::getInstanceId, ids)
+                .eq(ApprovalInstanceNode::getDeleted, 0)
+                .set(ApprovalInstanceNode::getDeleted, (byte) 1);
+        approvalInstanceNodeService.update(nodeUpdateWrapper);
+
+        LambdaUpdateWrapper<ApprovalTask> taskUpdateWrapper = Wrappers.lambdaUpdate();
+        taskUpdateWrapper.in(ApprovalTask::getInstanceId, ids)
+                .eq(ApprovalTask::getDeleted, 0)
+                .set(ApprovalTask::getDeleted, (byte) 1);
+        approvalTaskService.update(taskUpdateWrapper);
+
+        LambdaUpdateWrapper<ApprovalRecord> recordUpdateWrapper = Wrappers.lambdaUpdate();
+        recordUpdateWrapper.in(ApprovalRecord::getInstanceId, ids)
+                .eq(ApprovalRecord::getDeleted, 0)
+                .set(ApprovalRecord::getDeleted, (byte) 1);
+        approvalRecordService.update(recordUpdateWrapper);
+
+        return instanceRows > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R approve(ApprovalInstanceDto approvalInstanceDto) {
+        if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
+            return R.fail("瀹℃壒瀹炰緥 ID 涓嶈兘涓虹┖");
+        }
+        String approveAction = normalizeApproveAction(approvalInstanceDto.getApproveAction());
+        if (approveAction == null) {
+            return R.fail("瀹℃壒鍔ㄤ綔鍙敮鎸� APPROVED 鎴� REJECTED");
+        }
+
+        ApprovalInstance instance = getPendingApprovalInstance(approvalInstanceDto.getId());
+        if (instance == null) {
+            return R.fail("瀹℃壒瀹炰緥涓嶅瓨鍦�");
+        }
+
+        ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(instance.getId());
+        if (currentNode == null) {
+            return R.fail("褰撳墠娌℃湁寰呭鐞嗙殑瀹℃壒鑺傜偣");
+        }
+
+        Long currentUserId = SecurityUtils.getUserId();
+        ApprovalTask currentTask = approveProcessConfigNodeUtils.getCurrentUserTask(instance.getId(), currentUserId);
+        if (currentTask == null) {
+            return R.fail("褰撳墠鐢ㄦ埛娌℃湁鍙鎵逛换鍔�");
+        }
+
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        String operatorName = loginUser.getUser() != null ? loginUser.getUser().getNickName() : SecurityUtils.getUsername();
+        LocalDateTime now = LocalDateTime.now();
+
+        if (!updateCurrentTask(approvalInstanceDto, approveAction, currentTask, now)) {
+            return R.fail("褰撳墠浠诲姟宸茶澶勭悊锛岃鍒锋柊鍚庨噸璇�");
+        }
+
+        saveApprovalRecord(
+                instance.getId(),
+                currentNode.getId(),
+                currentTask.getId(),
+                currentUserId,
+                operatorName,
+                approveAction,
+                approvalInstanceDto.getApproveComment()
+        );
+        //瀹℃壒鎷掔粷鐨勫鐞�
+        if ("REJECTED".equals(approveAction)) {
+            return rejectCurrentNode(instance, currentNode, now);
+        }
+        if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
+            return R.ok("瀹℃壒鎴愬姛锛岀瓑寰呭叾浠栧鎵逛汉澶勭悊");
+        }
+
+        return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now);
+    }
+
+    private String normalizeApproveAction(String approveAction) {
+        if (!StringUtils.hasText(approveAction)) {
+            return null;
+        }
+        String normalizedAction = approveAction.trim().toUpperCase(Locale.ROOT);
+        return "APPROVED".equals(normalizedAction) || "REJECTED".equals(normalizedAction)
+                ? normalizedAction
+                : null;
+    }
+
+    private ApprovalInstance getPendingApprovalInstance(Long instanceId) {
+        return this.getOne(
+                new LambdaQueryWrapper<ApprovalInstance>()
+                        .eq(ApprovalInstance::getId, instanceId)
+                        .eq(ApprovalInstance::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+    }
+
+    private boolean updateCurrentTask(ApprovalInstanceDto approvalInstanceDto,
+                                      String approveAction,
+                                      ApprovalTask currentTask,
+                                      LocalDateTime now) {
+        // 浠呭厑璁稿緟瀹℃壒浠诲姟琚垚鍔熷鐞嗕竴娆★紝閬垮厤骞跺彂涓嬮噸澶嶅鎵规垚鍔熴��
+        return approvalTaskService.update(
+                Wrappers.<ApprovalTask>lambdaUpdate()
+                        .eq(ApprovalTask::getId, currentTask.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .set(ApprovalTask::getTaskStatus, approveAction)
+                        .set(ApprovalTask::getComment, approvalInstanceDto.getApproveComment())
+                        .set(ApprovalTask::getApproveTime, now)
+                        .set(ApprovalTask::getIsRead, (byte) 1)
+        );
+    }
+
+    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
+        if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
+            return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
+        }
+
+        closePendingTasks(instance.getId(), currentNode.getId());
+        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")
+            );
+        }
+        return R.ok("瀹℃壒宸查┏鍥�");
+    }
+
+    private R approveAndMoveNext(ApprovalInstance instance,
+                                 ApprovalInstanceNode currentNode,
+                                 ApprovalInstanceDto approvalInstanceDto,
+                                 LocalDateTime now) {
+        if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) {
+            return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
+        }
+
+        closePendingTasks(instance.getId(), currentNode.getId());
+
+        int nextLevel = currentNode.getLevelNo() + 1;
+        ApprovalInstanceNode nextInstanceNode = approvalInstanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instance.getId())
+                        .eq(ApprovalInstanceNode::getLevelNo, nextLevel)
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .orderByAsc(ApprovalInstanceNode::getId)
+                        .last("LIMIT 1")
+        );
+
+        if (nextInstanceNode != null) {
+            if (!activateNextInstanceNode(nextInstanceNode.getId(), now)) {
+                return R.ok("涓嬩竴瀹℃壒鑺傜偣宸茶婵�娲伙紝璇峰埛鏂板悗閲嶈瘯");
+            }
+            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);
+            return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
+        }
+
+        ApprovalTemplateNode nextTemplateNode = approvalTemplateNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNode::getLevelNo, nextLevel)
+                        .orderByAsc(ApprovalTemplateNode::getId)
+                        .last("LIMIT 1")
+        );
+
+        if (nextTemplateNode == null) {
+            instance.setStatus("APPROVED");
+            instance.setFinishTime(now);
+            this.updateById(instance);
+            handleBusinessAfterApprovalFinished(instance);
+            return R.ok("瀹℃壒宸插畬鎴�");
+        }
+
+        instance.setCurrentLevel(nextLevel);
+        instance.setStatus("PENDING");
+        this.updateById(instance);
+        approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false);
+        sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
+    }
+
+    private boolean activateNextInstanceNode(Long nodeId, LocalDateTime now) {
+        return approvalInstanceNodeService.update(
+                Wrappers.<ApprovalInstanceNode>lambdaUpdate()
+                        .eq(ApprovalInstanceNode::getId, nodeId)
+                        .eq(ApprovalInstanceNode::getStatus, "WAITING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .set(ApprovalInstanceNode::getStatus, "PENDING")
+                        .set(ApprovalInstanceNode::getStartTime, now)
+        );
+    }
+
+    private boolean updateCurrentNodeStatus(Long nodeId, String targetStatus, LocalDateTime now) {
+        // 浠呭厑璁镐竴涓姹傚皢褰撳墠鑺傜偣浠庡緟澶勭悊鎺ㄨ繘鍒扮洰鏍囩姸鎬侊紝閬垮厤閲嶅娴佽浆銆�
+        return approvalInstanceNodeService.update(
+                Wrappers.<ApprovalInstanceNode>lambdaUpdate()
+                        .eq(ApprovalInstanceNode::getId, nodeId)
+                        .eq(ApprovalInstanceNode::getStatus, "PENDING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .set(ApprovalInstanceNode::getStatus, targetStatus)
+                        .set(ApprovalInstanceNode::getFinishTime, now)
+        );
+    }
+
+    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
+        String status = instance.getStatus();
+        Long businessType = instance.getBusinessType();
+        if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
+            handlePurchaseApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(businessType)) {
+            handleSalesQuotationApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(businessType)) {
+            handleShippingApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)
+                || TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
+            handleReimbursementApprovalFinished(instance, status);
+            return;
+        }
+        // 椹冲洖瀵瑰簲鐨勪紒涓氭柊闂汇�佸姞鐝敵璇枫�佽鍋囩敵璇峰彲浠ラ噸鏂板啀鎻愪氦
+        if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType)
+                || TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) {
+            handleHolidayApplicationApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
+            handleNewsApprovalFinished(instance, status);
+        }
+    }
+
+    private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+
+        FinReimbursement reimbursement = new FinReimbursement();
+        reimbursement.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            reimbursement.setBillStatus("APPROVED");
+            reimbursement.setApprovedTime(instance.getFinishTime());
+        } else if ("REJECTED".equals(status)) {
+            reimbursement.setBillStatus("REJECTED");
+        } else if ("PENDING".equals(status)) {
+            reimbursement.setBillStatus("IN_APPROVAL");
+        } else {
+            return;
+        }
+        finReimbursementMapper.updateById(reimbursement);
+    }
+
+    private void handleNewsApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        EnterpriseNews enterpriseNews = new EnterpriseNews();
+        enterpriseNews.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_PUBLISHED);
+            enterpriseNewsMapper.updateById(enterpriseNews);
+            sendEnterpriseNewsNotice(instance.getBusinessId());
+            return;
+        }
+        if ("REJECTED".equals(status)) {
+            enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_REJECTED);
+            enterpriseNewsMapper.updateById(enterpriseNews);
+        }
+    }
+
+    private void handleHolidayApplicationApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        HolidayApplication holidayApplication = new HolidayApplication();
+        holidayApplication.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            holidayApplication.setStatus("APPROVED");
+        } else if ("REJECTED".equals(status)) {
+            holidayApplication.setStatus("REJECTED");
+        } else if ("PENDING".equals(status)) {
+            holidayApplication.setStatus("PENDING");
+        } else {
+            return;
+        }
+        holidayApplicationMapper.updateById(holidayApplication);
+    }
+
+    private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) {
+        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(
+                new LambdaQueryWrapper<PurchaseLedger>()
+                        .eq(PurchaseLedger::getId, instance.getBusinessId())
+                        .last("limit 1")
+        );
+        if (purchaseLedger == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode());
+            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(
+                    new QueryWrapper<SalesLedgerProduct>().lambda()
+                            .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
+                            .eq(SalesLedgerProduct::getType, 2)
+            );
+            for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
+                if (salesLedgerProduct.getIsChecked()) {
+                    qualityInspectHelper.addQualityInspect(purchaseLedger, salesLedgerProduct);
+                } else {
+                    stockUtils.addStockWithBatchNo(
+                            salesLedgerProduct.getProductModelId(),
+                            salesLedgerProduct.getQuantity(),
+                            StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
+                            purchaseLedger.getId(),
+                            purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId()
+                    );
+                }
+            }
+        } else if ("REJECTED".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.IN_PROGRESS.getCode());
+        }
+        purchaseLedgerMapper.updateById(purchaseLedger);
+    }
+
+    private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) {
+        SalesQuotation salesQuote = salesQuotationMapper.selectOne(
+                new LambdaQueryWrapper<SalesQuotation>()
+                        .eq(SalesQuotation::getId, instance.getBusinessId())
+                        .last("limit 1")
+        );
+        if (salesQuote == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.APPROVED.getCode());
+        } else if ("REJECTED".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.IN_PROGRESS.getCode());
+        }
+        salesQuotationMapper.updateById(salesQuote);
+    }
+
+    private void handleShippingApprovalFinished(ApprovalInstance instance, String status) {
+        ShippingInfo shippingInfo = shippingInfoMapper.selectOne(
+                new LambdaQueryWrapper<ShippingInfo>()
+                        .eq(ShippingInfo::getId, instance.getTitle())
+                        .orderByDesc(ShippingInfo::getCreateTime)
+                        .last("limit 1")
+        );
+        if (shippingInfo == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            shippingInfo.setStatus(ShippingStatusEnum.APPROVED.getCode());
+            shippingInfo.setShippingDate(new Date());
+            stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
+        } else if ("REJECTED".equals(status)) {
+            stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
+            shippingInfo.setStatus(ShippingStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            shippingInfo.setStatus(ShippingStatusEnum.IN_PROGRESS.getCode());
+        }
+        shippingInfoMapper.updateById(shippingInfo);
+    }
+
+    private List<ApprovalTask> createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) {
+        List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverMapper.selectList(
+                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);
+        approvalInstanceNodeService.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);
+        return taskList;
+    }
+
+    private void sendApproveNotice(ApprovalInstance instance, List<ApprovalTask> tasks) {
+        if (instance == null || tasks == null || tasks.isEmpty()) {
+            return;
+        }
+
+        List<Long> approverIds = tasks.stream()
+                .map(ApprovalTask::getApproverId)
+                .filter(id -> id != null && id > 0)
+                .distinct()
+                .collect(Collectors.toList());
+        if (approverIds.isEmpty()) {
+            return;
+        }
+
+        String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "瀹℃壒鎻愰啋";
+        String message = "瀹℃壒鍗曞彿 " + instance.getInstanceNo() + " 闇�瑕佹偍瀹℃壒";
+        String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId();
+        sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
+    }
+
+    private void sendEnterpriseNewsNotice(Long newsId) {
+        EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId);
+        if (enterpriseNews == null) {
+            return;
+        }
+        List<Long> userIds = getEnterpriseNewsNoticeUserIds(enterpriseNews);
+        if (userIds == null || userIds.isEmpty()) {
+            return;
+        }
+        String title = "浼佷笟鏂伴椈";
+        String message = "鎮ㄦ湁鏂扮殑浼佷笟鏂伴椈銆�" + enterpriseNews.getTitle() + "銆嬭鍙婃椂鏌ラ槄";
+        String jumpPath = "/officeProcessAutomation/EnterpriseNews?id=" + newsId;
+        sysNoticeService.simpleNoticeByUser(title, message, userIds, jumpPath);
+    }
+
+    private List<Long> getEnterpriseNewsNoticeUserIds(EnterpriseNews enterpriseNews) {
+        if (enterpriseNews == null || !org.springframework.util.StringUtils.hasText(enterpriseNews.getReadScope())) {
+            return Collections.emptyList();
+        }
+        String readScope = enterpriseNews.getReadScope().trim();
+        if ("all".equals(readScope)) {
+            return sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>()
+                            .select(SysUser::getUserId)
+                            .eq(SysUser::getDelFlag, "0"))
+                    .stream()
+                    .map(SysUser::getUserId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+        }
+        if ("dept".equals(readScope)) {
+            List<Long> deptIds = enterpriseNewsScopeDeptMapper.selectList(
+                            new LambdaQueryWrapper<EnterpriseNewsScopeDept>()
+                                    .eq(EnterpriseNewsScopeDept::getNewsId, enterpriseNews.getId()))
+                    .stream()
+                    .map(EnterpriseNewsScopeDept::getDeptId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+            if (deptIds.isEmpty()) {
+                return Collections.emptyList();
+            }
+            return sysUserDeptMapper.selectDistinctUserIdsByDeptIds(collectDeptIdsWithChildren(deptIds));
+        }
+        if ("custom".equals(readScope)) {
+            return enterpriseNewsScopeUserMapper.selectList(
+                            new LambdaQueryWrapper<EnterpriseNewsScopeUser>()
+                                    .eq(EnterpriseNewsScopeUser::getNewsId, enterpriseNews.getId()))
+                    .stream()
+                    .map(EnterpriseNewsScopeUser::getUserId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    private List<Long> collectDeptIdsWithChildren(List<Long> deptIds) {
+        Set<Long> allDeptIds = new LinkedHashSet<>();
+        for (Long deptId : deptIds) {
+            if (deptId == null) {
+                continue;
+            }
+            allDeptIds.add(deptId);
+            List<SysDept> children = sysDeptMapper.selectChildrenDeptById(deptId);
+            if (children != null && !children.isEmpty()) {
+                for (SysDept child : children) {
+                    if (child != null && child.getDeptId() != null) {
+                        allDeptIds.add(child.getDeptId());
+                    }
+                }
+            }
+        }
+        return new ArrayList<>(allDeptIds);
+    }
+
+    private void closePendingTasks(Long instanceId, Long nodeId) {
+        LambdaUpdateWrapper<ApprovalTask> updateWrapper = Wrappers.lambdaUpdate();
+        updateWrapper.eq(ApprovalTask::getInstanceId, instanceId)
+                .eq(ApprovalTask::getNodeId, nodeId)
+                .eq(ApprovalTask::getTaskStatus, "PENDING")
+                .eq(ApprovalTask::getDeleted, 0)
+                .set(ApprovalTask::getDeleted, (byte) 1);
+        approvalTaskService.update(updateWrapper);
+    }
+
+    private void saveApprovalRecord(Long instanceId,
+                                    Long nodeId,
+                                    Long taskId,
+                                    Long operatorId,
+                                    String operatorName,
+                                    String action,
+                                    String comment) {
+        ApprovalRecord record = new ApprovalRecord();
+        record.setInstanceId(instanceId);
+        record.setNodeId(nodeId);
+        record.setTaskId(taskId);
+        record.setOperatorId(operatorId);
+        record.setOperatorName(operatorName);
+        record.setAction(action);
+        record.setComment(comment);
+        record.setDeleted((byte) 0);
+        approvalRecordService.save(record);
+    }
+
+
+}

--
Gitblit v1.9.3