From 11214e3074266a23fe61e8eebbce647fdb7305ef Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 12 六月 2026 18:02:03 +0800
Subject: [PATCH] 报价单修改-优化,增加导入记录,降价历史

---
 src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java |  442 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 413 insertions(+), 29 deletions(-)

diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
index 1ec6a8d..c1084b3 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
@@ -7,32 +7,50 @@
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.approve.pojo.ApproveProcess;
-import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
 import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo;
-import com.ruoyi.approve.bean.vo.ApproveProcessVO;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import com.ruoyi.approve.pojo.ApproveProcess;
+import com.ruoyi.approve.service.ApprovalInstanceService;
+import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
 import com.ruoyi.basic.mapper.CustomerMapper;
 import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.common.enums.IsDeleteEnum;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.sales.dto.SalesQuotationDto;
+import com.ruoyi.sales.dto.SalesQuotationImportDto;
+import com.ruoyi.sales.dto.SalesQuotationMainImportDto;
+import com.ruoyi.sales.dto.SalesQuotationProductImportDto;
+import com.ruoyi.sales.mapper.SalesQuotationImportLogMapper;
 import com.ruoyi.sales.mapper.SalesQuotationMapper;
+import com.ruoyi.sales.mapper.SalesQuotationPriceHistoryMapper;
 import com.ruoyi.sales.mapper.SalesQuotationProductMapper;
-import com.ruoyi.sales.pojo.SalesQuotation;
-import com.ruoyi.sales.pojo.SalesQuotationProduct;
+import com.ruoyi.sales.pojo.*;
 import com.ruoyi.sales.service.SalesQuotationProductService;
 import com.ruoyi.sales.service.SalesQuotationService;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.math.BigDecimal;
 import java.time.LocalDate;
-import java.util.Collections;
-import java.util.List;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 @Transactional(rollbackFor = Exception.class)
 @RequiredArgsConstructor
@@ -40,9 +58,13 @@
     private final SalesQuotationProductMapper salesQuotationProductMapper;
     private final SalesQuotationMapper salesQuotationMapper;
     private final SalesQuotationProductService salesQuotationProductService;
+    private final SalesQuotationPriceHistoryMapper priceHistoryMapper;
+    private final SalesQuotationImportLogMapper importLogMapper;
 
     private final ApproveProcessServiceImpl approveProcessService;
     private final CustomerMapper customerMapper;
+    private final ApprovalTemplateMapper approvalTemplateMapper;
+    private final ApprovalInstanceService approvalInstanceService;
 
     @Override
     public IPage<SalesQuotationDto> listPage(Page page, SalesQuotationDto salesQuotationDto) {
@@ -50,10 +72,26 @@
         if(CollectionUtils.isEmpty(salesQuotationDtoIPage.getRecords())){
             return salesQuotationDtoIPage;
         }
-        salesQuotationDtoIPage.getRecords().forEach(record -> {
-            List<SalesQuotationProduct> products = salesQuotationProductMapper.selectBySalesQuotationId(record.getId());
-            record.setProducts(products);
-        });
+
+        // 鎵归噺鏌ヨ浜у搧锛岄伩鍏� N+1 闂
+        List<Long> quotationIds = salesQuotationDtoIPage.getRecords().stream()
+                .map(SalesQuotationDto::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!quotationIds.isEmpty()) {
+            List<SalesQuotationProduct> allProducts = salesQuotationProductMapper.selectList(
+                    new LambdaQueryWrapper<SalesQuotationProduct>()
+                            .in(SalesQuotationProduct::getSalesQuotationId, quotationIds)
+            );
+
+            Map<Long, List<SalesQuotationProduct>> productMap = allProducts.stream()
+                    .collect(Collectors.groupingBy(SalesQuotationProduct::getSalesQuotationId));
+
+            salesQuotationDtoIPage.getRecords().forEach(record ->
+                    record.setProducts(productMap.getOrDefault(record.getId(), new ArrayList<>()))
+            );
+        }
         return salesQuotationDtoIPage;
     }
 
@@ -61,11 +99,13 @@
     public boolean add(SalesQuotationDto salesQuotationDto) {
         LoginUser loginUser = SecurityUtils.getLoginUser();
         SalesQuotation salesQuotation = new SalesQuotation();
-        Customer customer = customerMapper.selectById(Long.valueOf(salesQuotationDto.getCustomerId()));
+        BeanUtils.copyProperties(salesQuotationDto, salesQuotation);
+        salesQuotation.setId(null);
+        Customer customer = customerMapper.selectById(salesQuotationDto.getCustomerId());
         if (ObjectUtils.isNotEmpty(customer))  {
             salesQuotation.setCustomer(customer.getCustomerName());
         }
-        String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no");
+        String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no", salesQuotationDto.getCreateTime() != null ? salesQuotationDto.getCreateTime() : LocalDateTime.now());
         salesQuotation.setQuotationNo(quotationNo);
         salesQuotation.setStatus("寰呭鎵�");
         salesQuotationMapper.insert(salesQuotation);
@@ -80,22 +120,35 @@
         }).collect(Collectors.toList());
         salesQuotationProductService.saveBatch(products);
         // 鎶ヤ环瀹℃壒
-        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
-        approveProcessVO.setApproveType(6);
-        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
-        approveProcessVO.setApproveReason(quotationNo);
-        approveProcessVO.setApproveUserIds(salesQuotationDto.getApproveUserIds());
-        approveProcessVO.setApproveUser(loginUser.getUserId());
-        approveProcessVO.setApproveTime(LocalDate.now().toString());
-        approveProcessVO.setPrice(salesQuotationDto.getTotalAmount());
+        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getBusinessType, 6L)
+                        .eq(ApprovalTemplate::getDeleted, 0)
+                        .orderByDesc(ApprovalTemplate::getId)
+                        .last("LIMIT 1")
+        );
+        if (approvalTemplate == null) {
+            throw new RuntimeException("璇峰厛閰嶇疆鎶ヤ环瀹℃壒妯℃澘");
+        }
+        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
+        approvalInstance.setTemplateId(approvalTemplate.getId());
+        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
+        approvalInstance.setBusinessId(salesQuotation.getId());
+        approvalInstance.setBusinessType(6L);
+        approvalInstance.setCurrentLevel(1);
+        approvalInstance.setTitle(quotationNo+"瀹℃壒");
+        approvalInstance.setApplicantId(loginUser.getUserId());
+        approvalInstance.setApplicantName(loginUser.getNickName());
+        approvalInstance.setApplyTime(LocalDateTime.now());
         try {
-            approveProcessService.addApprove(approveProcessVO);
-        }catch (Exception e){
-            log.error("SalesQuotationServiceImpl error:{}", e);
-            throw                                new RuntimeException("瀹℃壒澶辫触");
+            approvalInstanceService.add(approvalInstance);
+        } catch (Exception e) {
+            log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e);
+            throw new RuntimeException("瀹℃壒澶辫触: " + e.getMessage(), e);
         }
         return true;
     }
+
     @Override
     public boolean edit(SalesQuotationDto salesQuotationDto) {
         SalesQuotation salesQuotation = new SalesQuotation();
@@ -121,12 +174,38 @@
 
         salesQuotationProductService.saveBatch(products);
         // 淇敼鎶ヤ环瀹℃壒
-        vo.setApproveUserIds(salesQuotationDto.getApproveUserIds());
-        vo.setApproveType(6);
-        vo.setApproveReason(salesQuotationDto.getQuotationNo());
-        approveProcessService.updateApproveUser(vo);
+        // 鍏堢粨鏉熶箣鍓嶆湭缁撴潫鐨勬姤浠峰鎵�
+        approvalInstanceService.lambdaUpdate().set(ApprovalInstance::getStatus,"REJECTED").eq(ApprovalInstance::getBusinessId,salesQuotation.getId()).eq(ApprovalInstance::getBusinessType,6L).update();
+
+        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getBusinessType, 6L)
+                        .eq(ApprovalTemplate::getDeleted, 0)
+                        .orderByDesc(ApprovalTemplate::getId)
+                        .last("LIMIT 1")
+        );
+        if (approvalTemplate == null) {
+            throw new RuntimeException("璇峰厛閰嶇疆鎶ヤ环瀹℃壒妯℃澘");
+        }
+        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
+        approvalInstance.setTemplateId(approvalTemplate.getId());
+        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
+        approvalInstance.setBusinessId(salesQuotation.getId());
+        approvalInstance.setBusinessType(6L);
+        approvalInstance.setCurrentLevel(1);
+        approvalInstance.setTitle(salesQuotation.getQuotationNo()+"瀹℃壒");
+        approvalInstance.setApplicantId(SecurityUtils.getUserId());
+        approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName());
+        approvalInstance.setApplyTime(LocalDateTime.now());
+        try {
+            approvalInstanceService.add(approvalInstance);
+        } catch (Exception e) {
+            log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e);
+            throw new RuntimeException("瀹℃壒澶辫触: " + e.getMessage(), e);
+        }
         return true;
     }
+
     @Override
     public boolean delete(Long id) {
         SalesQuotation salesQuotation = salesQuotationMapper.selectById(id);
@@ -136,6 +215,7 @@
         // 鍒犻櫎鎶ヤ环瀹℃壒
         ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                 .eq(ApproveProcess::getApproveType, 6)
+                .eq(ApproveProcess::getApproveDelete, IsDeleteEnum.NOT_DELETED)
                 .eq(ApproveProcess::getApproveReason, salesQuotation.getQuotationNo()));
         if(one != null){
             approveProcessService.delByIds(Collections.singletonList(one.getId()));
@@ -143,5 +223,309 @@
         return true;
     }
 
+    @Override
+    public void downloadTemplate(HttpServletResponse response) {
+        // 鎶ヤ环鍗曟暟鎹ず渚�
+        List<SalesQuotationMainImportDto> mainList = new ArrayList<>();
+        SalesQuotationMainImportDto mainExample = new SalesQuotationMainImportDto();
+        mainExample.setQuotationNo("QT202606120001");
+        mainExample.setCustomerName("绀轰緥瀹㈡埛");
+        mainExample.setSalesperson("寮犱笁");
+        mainExample.setPaymentMethod("鏈堢粨30澶�");
+        mainExample.setDeliveryPeriod("7澶�");
+        mainExample.setRemark("绀轰緥鎶ヤ环鍗�");
+        mainList.add(mainExample);
 
+        // 鎶ヤ环浜у搧鏁版嵁绀轰緥
+        List<SalesQuotationProductImportDto> productList = new ArrayList<>();
+        SalesQuotationProductImportDto productExample1 = new SalesQuotationProductImportDto();
+        productExample1.setQuotationNo("QT202606120001");
+        productExample1.setProductCategory("鐢垫睜缁勪欢");
+        productExample1.setSpecificationModel("MODEL-A-100W");
+        productExample1.setUnit("鐗�");
+        productExample1.setUnitPrice(new BigDecimal("150.00"));
+        productList.add(productExample1);
+
+        SalesQuotationProductImportDto productExample2 = new SalesQuotationProductImportDto();
+        productExample2.setQuotationNo("QT202606120001");
+        productExample2.setProductCategory("鐢垫睜缁勪欢");
+        productExample2.setSpecificationModel("MODEL-B-200W");
+        productExample2.setUnit("鐗�");
+        productExample2.setUnitPrice(new BigDecimal("200.00"));
+        productList.add(productExample2);
+
+        // 浣跨敤闈欐�佹柟娉曞鍑哄Sheet妯℃澘
+        Map<String, ExcelUtil.SheetData<?>> sheetDataMap = new LinkedHashMap<>();
+        sheetDataMap.put("鎶ヤ环鍗曟暟鎹�", new ExcelUtil.SheetData<>(mainList, SalesQuotationMainImportDto.class));
+        sheetDataMap.put("鎶ヤ环浜у搧鏁版嵁", new ExcelUtil.SheetData<>(productList, SalesQuotationProductImportDto.class));
+
+        ExcelUtil.exportExcelMultiSheet(response, sheetDataMap, "閿�鍞姤浠峰鍏ユā鏉�");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public SalesQuotationImportLog importQuotation(MultipartFile file) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+
+        // 妫�鏌ュ鎵规ā鏉挎槸鍚﹀瓨鍦�
+        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getBusinessType, 6L)
+                        .eq(ApprovalTemplate::getDeleted, 0)
+                        .orderByDesc(ApprovalTemplate::getId)
+                        .last("LIMIT 1")
+        );
+        if (approvalTemplate == null) {
+            throw new ServiceException("璇峰厛閰嶇疆鎶ヤ环瀹℃壒妯℃澘锛屾棤娉曞鍏�");
+        }
+
+        // 瑙f瀽澶歋heet Excel鏂囦欢
+        ExcelUtil<SalesQuotationMainImportDto> mainUtil = new ExcelUtil<>(SalesQuotationMainImportDto.class);
+        Map<String, List<SalesQuotationMainImportDto>> sheetMap;
+        try {
+            sheetMap = mainUtil.importExcelMultiSheet(Arrays.asList("鎶ヤ环鍗曟暟鎹�", "鎶ヤ环浜у搧鏁版嵁"), file.getInputStream(), 0);
+        } catch (IOException e) {
+            throw new ServiceException("璇诲彇鏂囦欢澶辫触: " + e.getMessage());
+        }
+
+        List<SalesQuotationMainImportDto> mainList = sheetMap.get("鎶ヤ环鍗曟暟鎹�");
+        List<SalesQuotationMainImportDto> productListRaw = sheetMap.get("鎶ヤ环浜у搧鏁版嵁");
+
+        if (CollectionUtils.isEmpty(mainList)) {
+            throw new ServiceException("鎶ヤ环鍗曟暟鎹负绌猴紝璇锋鏌ユā鏉垮唴瀹�");
+        }
+
+        // 灏嗕骇鍝佹暟鎹浆涓烘纭殑DTO绫诲瀷
+        ExcelUtil<SalesQuotationProductImportDto> productUtil = new ExcelUtil<>(SalesQuotationProductImportDto.class);
+        Map<String, List<SalesQuotationProductImportDto>> productSheetMap;
+        try {
+            productSheetMap = productUtil.importExcelMultiSheet(Arrays.asList("鎶ヤ环浜у搧鏁版嵁"), file.getInputStream(), 0);
+        } catch (IOException e) {
+            throw new ServiceException("璇诲彇浜у搧鏁版嵁澶辫触: " + e.getMessage());
+        }
+        List<SalesQuotationProductImportDto> productList = productSheetMap.get("鎶ヤ环浜у搧鏁版嵁");
+
+        // 鐢熸垚鎵规鍙�
+        String batchNo = "QT_IMP_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+
+        // 鍒涘缓瀵煎叆璁板綍
+        SalesQuotationImportLog importLog = new SalesQuotationImportLog();
+        importLog.setBatchNo(batchNo);
+        importLog.setFileName(file.getOriginalFilename());
+        importLog.setTotalCount(mainList.size());
+        importLog.setSuccessCount(0);
+        importLog.setUpdateCount(0);
+        importLog.setNewCount(0);
+        importLog.setFailCount(0);
+        importLog.setStatus("completed");
+        importLog.setCreateUser(loginUser.getUserId());
+        importLog.setCreateUserName(loginUser.getNickName());
+        importLogMapper.insert(importLog);
+
+        // 鏌ヨ鐩稿叧鏁版嵁
+        List<Customer> customers = customerMapper.selectList(
+                new LambdaQueryWrapper<Customer>()
+                        .in(Customer::getCustomerName, mainList.stream()
+                                .map(SalesQuotationMainImportDto::getCustomerName)
+                                .filter(Objects::nonNull)
+                                .collect(Collectors.toList()))
+        );
+        Map<String, Customer> customerMap = customers.stream()
+                .collect(Collectors.toMap(Customer::getCustomerName, c -> c, (a, b) -> a));
+
+        // 鎸夋姤浠峰崟鍙峰垎缁勪骇鍝�
+        Map<String, List<SalesQuotationProductImportDto>> productGroupMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(productList)) {
+            productGroupMap = productList.stream()
+                    .filter(p -> p.getQuotationNo() != null && !p.getQuotationNo().isEmpty())
+                    .collect(Collectors.groupingBy(SalesQuotationProductImportDto::getQuotationNo));
+        }
+
+        int successCount = 0;
+        int newCount = 0;
+        int updateCount = 0;
+        int failCount = 0;
+
+        for (SalesQuotationMainImportDto mainDto : mainList) {
+            try {
+                String customerName = mainDto.getCustomerName();
+                if (customerName == null || customerName.isEmpty()) {
+                    failCount++;
+                    continue;
+                }
+
+                // 鏌ユ壘瀹㈡埛
+                Customer customer = customerMap.get(customerName);
+
+                // 鍒涘缓鎶ヤ环鍗�
+                SalesQuotation quotation = new SalesQuotation();
+                quotation.setCustomer(customerName);
+                quotation.setCustomerId(customer != null ? customer.getId() : null);
+
+                // 鐢熸垚鎶ヤ环鍗曞彿
+                String quotationNo;
+                if (mainDto.getQuotationNo() != null && !mainDto.getQuotationNo().isEmpty()) {
+                    quotationNo = mainDto.getQuotationNo();
+                } else {
+                    quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT", "quotation_no", LocalDateTime.now());
+                }
+                quotation.setQuotationNo(quotationNo);
+
+                // 妫�鏌ユ姤浠峰崟鍙锋槸鍚﹀凡瀛樺湪
+                SalesQuotation existing = salesQuotationMapper.selectOne(
+                        new LambdaQueryWrapper<SalesQuotation>()
+                                .eq(SalesQuotation::getQuotationNo, quotationNo)
+                                .last("LIMIT 1")
+                );
+                boolean isUpdate = existing != null;
+
+                if (isUpdate) {
+                    quotation.setId(existing.getId());
+                    quotation.setQuotationDate(existing.getQuotationDate());
+                    quotation.setStatus(existing.getStatus());
+                } else {
+                    quotation.setQuotationDate(LocalDate.now());
+                    quotation.setStatus("寰呭鎵�");
+                }
+                quotation.setSalesperson(mainDto.getSalesperson());
+                quotation.setPaymentMethod(mainDto.getPaymentMethod());
+                quotation.setDeliveryPeriod(mainDto.getDeliveryPeriod());
+                quotation.setRemark("瀵煎叆鎵规鍙�: " + batchNo + (mainDto.getRemark() != null ? "锛�" + mainDto.getRemark() : ""));
+
+                // 璁$畻鎬婚噾棰濆苟淇濆瓨浜у搧
+                BigDecimal totalAmount = BigDecimal.ZERO;
+                List<SalesQuotationProduct> quotationProducts = new ArrayList<>();
+
+                List<SalesQuotationProductImportDto> products = productGroupMap.get(quotationNo);
+                if (!CollectionUtils.isEmpty(products)) {
+                    for (SalesQuotationProductImportDto productDto : products) {
+                        SalesQuotationProduct product = new SalesQuotationProduct();
+                        product.setProduct(productDto.getProductCategory());
+                        product.setSpecification(productDto.getSpecificationModel());
+                        product.setUnit(productDto.getUnit());
+                        product.setQuantity(0);
+                        product.setUnitPrice(productDto.getUnitPrice() != null ? productDto.getUnitPrice().doubleValue() : 0.0);
+
+                        BigDecimal amount = productDto.getUnitPrice() != null
+                                ? productDto.getUnitPrice()
+                                : BigDecimal.ZERO;
+                        product.setAmount(amount.doubleValue());
+                        totalAmount = totalAmount.add(amount);
+
+                        quotationProducts.add(product);
+                    }
+                }
+
+                quotation.setTotalAmount(totalAmount);
+                if (isUpdate) {
+                    salesQuotationMapper.updateById(quotation);
+                    // 鍒犻櫎鏃т骇鍝佹暟鎹�
+                    salesQuotationProductMapper.delete(new LambdaQueryWrapper<SalesQuotationProduct>()
+                            .eq(SalesQuotationProduct::getSalesQuotationId, existing.getId()));
+                } else {
+                    salesQuotationMapper.insert(quotation);
+                }
+
+                // 淇濆瓨浜у搧骞惰褰曢檷浠峰巻鍙�
+                for (SalesQuotationProduct product : quotationProducts) {
+                    product.setSalesQuotationId(quotation.getId());
+                    salesQuotationProductMapper.insert(product);
+                    recordPriceHistory(quotation.getId(), product, batchNo);
+                }
+
+                if (!isUpdate) {
+                    // 鍒涘缓瀹℃壒瀹炰緥
+                    ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
+                    approvalInstance.setTemplateId(approvalTemplate.getId());
+                    approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
+                    approvalInstance.setBusinessId(quotation.getId());
+                    approvalInstance.setBusinessType(6L);
+                    approvalInstance.setCurrentLevel(1);
+                    approvalInstance.setTitle(quotationNo + "瀹℃壒");
+                    approvalInstance.setApplicantId(loginUser.getUserId());
+                    approvalInstance.setApplicantName(loginUser.getNickName());
+                    approvalInstance.setApplyTime(LocalDateTime.now());
+                    approvalInstanceService.add(approvalInstance);
+                }
+
+                successCount++;
+                if (isUpdate) {
+                    updateCount++;
+                } else {
+                    newCount++;
+                }
+            } catch (Exception e) {
+                log.error("瀵煎叆鎶ヤ环鍗曞け璐�: {}", e.getMessage());
+                failCount++;
+            }
+        }
+
+        // 鏇存柊瀵煎叆璁板綍
+        importLog.setSuccessCount(successCount);
+        importLog.setNewCount(newCount);
+        importLog.setUpdateCount(updateCount);
+        importLog.setFailCount(failCount);
+        importLogMapper.updateById(importLog);
+
+        return importLog;
+    }
+
+    /**
+     * 璁板綍闄嶄环鍘嗗彶
+     */
+    private void recordPriceHistory(Long quotationId, SalesQuotationProduct product, String batchNo) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+
+        // 鏌ユ壘鐩稿悓椤圭洰鍚嶇О鐨勫巻鍙叉姤浠蜂骇鍝�
+        List<SalesQuotationProduct> historyProducts = salesQuotationProductMapper.selectList(
+                new LambdaQueryWrapper<SalesQuotationProduct>()
+                        .eq(SalesQuotationProduct::getProduct, product.getProduct())
+                        .ne(SalesQuotationProduct::getId, product.getId())
+                        .orderByDesc(SalesQuotationProduct::getCreateTime)
+                        .last("LIMIT 1")
+        );
+
+        if (!historyProducts.isEmpty()) {
+            SalesQuotationProduct historyProduct = historyProducts.get(0);
+            BigDecimal oldPrice = historyProduct.getUnitPrice() != null
+                    ? new BigDecimal(historyProduct.getUnitPrice().toString())
+                    : BigDecimal.ZERO;
+            BigDecimal newPrice = product.getUnitPrice() != null
+                    ? new BigDecimal(product.getUnitPrice().toString())
+                    : BigDecimal.ZERO;
+
+            // 濡傛灉浠锋牸鏈夊彉鍖栵紝璁板綍闄嶄环鍘嗗彶
+            if (oldPrice.compareTo(newPrice) != 0) {
+                SalesQuotationPriceHistory priceHistory = new SalesQuotationPriceHistory();
+                priceHistory.setQuotationId(quotationId);
+                priceHistory.setQuotationProductId(product.getId());
+                priceHistory.setProductName(product.getProduct());
+                priceHistory.setSpecification(product.getSpecification());
+                priceHistory.setOldPrice(oldPrice);
+                priceHistory.setNewPrice(newPrice);
+                priceHistory.setPriceChange(newPrice.subtract(oldPrice));
+                priceHistory.setImportBatch(batchNo);
+                priceHistory.setImportTime(LocalDateTime.now());
+                priceHistory.setCreateUser(loginUser.getUserId());
+                priceHistory.setCreateUserName(loginUser.getNickName());
+                priceHistory.setChangeReason(newPrice.compareTo(oldPrice) < 0 ? "闄嶄环" : "娑ㄤ环");
+                priceHistoryMapper.insert(priceHistory);
+            }
+        }
+    }
+
+    @Override
+    public IPage<SalesQuotationImportLog> listImportLog(Page page) {
+        return importLogMapper.selectPage(page,
+                new LambdaQueryWrapper<SalesQuotationImportLog>()
+                        .orderByDesc(SalesQuotationImportLog::getCreateTime));
+    }
+
+    @Override
+    public List<SalesQuotationPriceHistory> listPriceHistory(Long quotationId) {
+        return priceHistoryMapper.selectList(
+                new LambdaQueryWrapper<SalesQuotationPriceHistory>()
+                        .eq(SalesQuotationPriceHistory::getQuotationId, quotationId)
+                        .orderByDesc(SalesQuotationPriceHistory::getImportTime));
+    }
 }

--
Gitblit v1.9.3