From b504b19b0881a7c1bd9e280ba41e993956b7f94e Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 30 一月 2026 16:41:56 +0800
Subject: [PATCH] feat: 进销存生产分析大屏

---
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java | 1330 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 1,123 insertions(+), 207 deletions(-)

diff --git a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
index cb5d600..ae752df 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -2,8 +2,11 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.mapper.AccountIncomeMapper;
+import com.ruoyi.account.pojo.AccountExpense;
+import com.ruoyi.home.mapper.HomeMapper;
+import com.ruoyi.account.mapper.AccountExpenseMapper;
 import com.ruoyi.approve.mapper.ApproveProcessMapper;
 import com.ruoyi.approve.pojo.ApproveProcess;
 import com.ruoyi.basic.mapper.CustomerMapper;
@@ -12,10 +15,10 @@
 import com.ruoyi.basic.mapper.SupplierManageMapper;
 import com.ruoyi.basic.pojo.Customer;
 import com.ruoyi.basic.pojo.Product;
-import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.basic.pojo.SupplierManage;
 import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
 import com.ruoyi.collaborativeApproval.pojo.Notice;
+import com.ruoyi.common.enums.ApproveTypeEnum;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.device.mapper.DeviceRepairMapper;
 import com.ruoyi.device.pojo.DeviceRepair;
@@ -30,11 +33,15 @@
 import com.ruoyi.production.dto.ProductWorkOrderDto;
 import com.ruoyi.production.mapper.ProductOrderMapper;
 import com.ruoyi.production.mapper.ProductWorkOrderMapper;
+import com.ruoyi.production.mapper.ProductionProductInputMapper;
+import com.ruoyi.production.mapper.ProductionProductOutputMapper;
 import com.ruoyi.production.pojo.ProductWorkOrder;
 import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.domain.SysUserDept;
 import com.ruoyi.project.system.mapper.SysDeptMapper;
 import com.ruoyi.project.system.mapper.SysUserDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
 import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
 import com.ruoyi.purchase.pojo.PaymentRegistration;
@@ -44,8 +51,6 @@
 import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
-import com.ruoyi.project.system.domain.SysUser;
-import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.sales.pojo.ReceiptPayment;
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
@@ -83,6 +88,9 @@
 
     @Autowired
     private SalesLedgerProductMapper salesLedgerProductMapper;
+
+    @Autowired
+    private StockInventoryMapper stockInventoryMapper;
 
     @Autowired
     private ProcurementRecordMapper procurementRecordStorageMapper;
@@ -124,7 +132,12 @@
     private SysUserMapper sysUserMapper;
     @Autowired
     private SysUserDeptMapper sysUserDeptMapper;
-    private StockInventoryMapper stockInventoryMapper;
+
+    @Autowired
+    private HomeMapper homeMapper;
+
+    @Autowired
+    private ProductionProductOutputMapper productionProductOutputMapper;
 
     @Override
     public HomeBusinessDto business() {
@@ -134,7 +147,7 @@
         YearMonth currentMonth = YearMonth.from(now);
         // 鍒涘缓LambdaQueryWrapper
         LambdaQueryWrapper<SalesLedger> salesLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        salesLedgerLambdaQueryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 澶т簬绛変簬鏈湀绗竴澶�
+        salesLedgerLambdaQueryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay()) // 澶т簬绛変簬鏈湀绗竴澶�
                 .lt(SalesLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 灏忎簬涓嬫湀绗竴澶�
         List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(salesLedgerLambdaQueryWrapper);
         if (!CollectionUtils.isEmpty(salesLedgers)) {
@@ -144,8 +157,10 @@
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
             LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapper = new LambdaQueryWrapper<SalesLedgerProduct>();
             salesLedgerProductMapperLambdaQueryWrapper.eq(SalesLedgerProduct::getType, 1)
-                    .in(SalesLedgerProduct::getSalesLedgerId, salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList()));
-            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(salesLedgerProductMapperLambdaQueryWrapper);
+                    .in(SalesLedgerProduct::getSalesLedgerId,
+                            salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList()));
+            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper
+                    .selectList(salesLedgerProductMapperLambdaQueryWrapper);
             // 鏈紑绁ㄩ噾棰�
             BigDecimal noInvoiceAmountTotal = salesLedgerProducts.stream().map(SalesLedgerProduct::getNoInvoiceAmount)
                     .filter(Objects::nonNull)
@@ -155,15 +170,17 @@
         }
         // 鍒涘缓LambdaQueryWrapper
         LambdaQueryWrapper<PurchaseLedger> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.ge(PurchaseLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 澶т簬绛変簬鏈湀绗竴澶�
+        queryWrapper.ge(PurchaseLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay()) // 澶т簬绛変簬鏈湀绗竴澶�
                 .lt(PurchaseLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 灏忎簬涓嬫湀绗竴澶�
         // 鎵ц鏌ヨ骞惰绠楁�诲拰
         List<PurchaseLedger> purchaseLedgers = purchaseLedgerMapper.selectList(queryWrapper);
         if (!CollectionUtils.isEmpty(purchaseLedgers)) {
             LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapperCopy = new LambdaQueryWrapper<SalesLedgerProduct>();
             salesLedgerProductMapperLambdaQueryWrapperCopy.eq(SalesLedgerProduct::getType, 2)
-                    .in(SalesLedgerProduct::getSalesLedgerId, purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
-            List<SalesLedgerProduct> salesLedgerProductsCopy = salesLedgerProductMapper.selectList(salesLedgerProductMapperLambdaQueryWrapperCopy);
+                    .in(SalesLedgerProduct::getSalesLedgerId,
+                            purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
+            List<SalesLedgerProduct> salesLedgerProductsCopy = salesLedgerProductMapper
+                    .selectList(salesLedgerProductMapperLambdaQueryWrapperCopy);
             // 鍚堣鍚堝悓閲戦
             BigDecimal receiveAmount = purchaseLedgers.stream()
                     .map(PurchaseLedger::getContractAmount)
@@ -175,41 +192,16 @@
                     .filter(Objects::nonNull)
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
             homeBusinessDto.setMonthPurchaseMoney(receiveAmount.setScale(2, RoundingMode.HALF_UP).toString());
-            homeBusinessDto.setMonthPurchaseHaveMoney(unReceiptPaymentAmount.setScale(2, RoundingMode.HALF_UP).toString());
+            homeBusinessDto
+                    .setMonthPurchaseHaveMoney(unReceiptPaymentAmount.setScale(2, RoundingMode.HALF_UP).toString());
         }
         // 缁熻搴撳瓨
-//        List<ProcurementRecordStorage> procurementRecordStorages = procurementRecordStorageMapper.selectList(null);
-//        BigDecimal stockAmount = procurementRecordStorages.stream()
-//                .map(ProcurementRecordStorage::getInboundNum)
-//                .filter(Objects::nonNull)
-//                .reduce(BigDecimal.ZERO, BigDecimal::add);
-//        // 鑷畾涔夊簱瀛�
-//        List<CustomStorage> customStorages = customStorageMapper.selectList(null);
-//        BigDecimal customStockAmount = customStorages.stream()
-//                .map(CustomStorage::getInboundNum)
-//                .filter(Objects::nonNull)
-//                .reduce(BigDecimal.ZERO, BigDecimal::add);
-//        List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(null);
-//        BigDecimal outboundAmount = procurementRecordOuts.stream()
-//                .map(ProcurementRecordOut::getInboundNum)
-//                .filter(Objects::nonNull)
-//                .reduce(BigDecimal.ZERO, BigDecimal::add);
-//        BigDecimal stock = stockAmount.add(customStockAmount).subtract(outboundAmount);
         BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal();
         homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString());
 
-
         // 鑾峰彇褰撳ぉ鍏ュ簱鏁伴噺
-        LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
-        procurementRecordStorageLambdaQueryWrapper.ge(ProcurementRecordStorage::getCreateTime, now)  // 澶т簬绛変簬褰撳ぉ
-                .lt(ProcurementRecordStorage::getCreateTime, now.plusDays(1)); // 灏忎簬鏄庡ぉ
-        List<ProcurementRecordStorage> procurementRecordStorages1 = procurementRecordStorageMapper.selectList(procurementRecordStorageLambdaQueryWrapper);
-        BigDecimal stockAmount1 = procurementRecordStorages1.stream()
-                .map(ProcurementRecordStorage::getInboundNum)
-                .filter(Objects::nonNull)
-                .reduce(BigDecimal.ZERO, BigDecimal::add);
-//        homeBusinessDto.setInventoryNum(stock.setScale(2, RoundingMode.HALF_UP).toString());
-        homeBusinessDto.setTodayInventoryNum(stockAmount1.setScale(2, RoundingMode.HALF_UP).toString());
+        BigDecimal bigDecimal = stockInventoryMapper.selectTotalByDate(LocalDate.now());
+        homeBusinessDto.setTodayInventoryNum(bigDecimal.setScale(2, RoundingMode.HALF_UP).toString());
         return homeBusinessDto;
     }
 
@@ -230,7 +222,7 @@
         // 鑾峰彇鏈懆鍛ㄦ棩
         LocalDate endOfWeek = today.with(DayOfWeek.SUNDAY);
         List<SalesLedger> salesLedgers1 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
-                .ge(SalesLedger::getEntryDate, startOfWeek)  // 澶т簬绛変簬鏈懆鍛ㄤ竴
+                .ge(SalesLedger::getEntryDate, startOfWeek) // 澶т簬绛変簬鏈懆鍛ㄤ竴
                 .lt(SalesLedger::getEntryDate, endOfWeek.plusDays(1))); // 淇敼锛氫娇鐢� lt 骞跺姞涓婁竴澶╂潵鍖呭惈鍛ㄦ棩
         BigDecimal weekContractAmount = salesLedgers1.stream().map(SalesLedger::getContractAmount)
                 .filter(Objects::nonNull)
@@ -239,7 +231,7 @@
         LocalDate lastYearStartOfWeek = today.minusYears(1).with(DayOfWeek.MONDAY);
         LocalDate lastYearEndOfWeek = today.minusYears(1).with(DayOfWeek.SUNDAY);
         List<SalesLedger> salesLedgers2 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
-                .ge(SalesLedger::getEntryDate, lastYearStartOfWeek)  // 澶т簬绛変簬鍘诲勾鏈懆鍛ㄤ竴
+                .ge(SalesLedger::getEntryDate, lastYearStartOfWeek) // 澶т簬绛変簬鍘诲勾鏈懆鍛ㄤ竴
                 .lt(SalesLedger::getEntryDate, lastYearEndOfWeek.plusDays(1))); // 淇敼锛氫娇鐢� lt 骞跺姞涓婁竴澶╂潵鍖呭惈鍛ㄦ棩
         BigDecimal lastYearWeekContractAmount = salesLedgers2.stream().map(SalesLedger::getContractAmount)
                 .filter(Objects::nonNull)
@@ -250,7 +242,8 @@
         if (subtract.compareTo(BigDecimal.ZERO) == 0 || lastYearWeekContractAmount.compareTo(BigDecimal.ZERO) == 0) {
             weekYny = "0.00";
         } else {
-            weekYny = String.format("%.2f", subtract.divide(lastYearWeekContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
+            weekYny = String.format("%.2f", subtract.divide(lastYearWeekContractAmount, 2, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100")));
 
         }
 
@@ -258,26 +251,29 @@
         LocalDate yesterday = today.minusDays(1);
         LocalDate plusDays = today.plusDays(1);
         List<SalesLedger> salesLedgers3 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
-                .ge(SalesLedger::getEntryDate, today)  // 澶т簬绛変簬浠婂ぉ
+                .ge(SalesLedger::getEntryDate, today) // 澶т簬绛変簬浠婂ぉ
                 .lt(SalesLedger::getEntryDate, plusDays)); // 淇敼锛氫娇鐢� lt 骞跺姞涓婁竴澶╂潵鍖呭惈褰撳ぉ
         BigDecimal todayContractAmount = salesLedgers3.stream().map(SalesLedger::getContractAmount)
                 .filter(Objects::nonNull)
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
         List<SalesLedger> salesLedgers4 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
-                .ge(SalesLedger::getEntryDate, yesterday)  // 澶т簬绛変簬鏄ㄥぉ
+                .ge(SalesLedger::getEntryDate, yesterday) // 澶т簬绛変簬鏄ㄥぉ
                 .lt(SalesLedger::getEntryDate, today)); // 淇敼锛氫娇鐢� lt 鑰屼笉鏄� gt
         BigDecimal lastYearYesterdayContractAmount = salesLedgers4.stream().map(SalesLedger::getContractAmount)
                 .filter(Objects::nonNull)
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
-        BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 淇敼锛氫娇鐢� todayContractAmount 鑰屼笉鏄� yesterdayContractAmount
+        BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 淇敼锛氫娇鐢�
+                                                                                              // todayContractAmount 鑰屼笉鏄�
+                                                                                              // yesterdayContractAmount
         // 鏃ョ幆姣�
         String chain = "";
-        if (subtract1.compareTo(BigDecimal.ZERO) == 0 || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0) {
+        if (subtract1.compareTo(BigDecimal.ZERO) == 0
+                || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0) {
             chain = "0.00";
         } else {
-            chain = String.format("%.2f", subtract1.divide(lastYearYesterdayContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
+            chain = String.format("%.2f", subtract1.divide(lastYearYesterdayContractAmount, 2, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100")));
         }
-
 
         AnalysisCustomerContractAmountsDto analysisCustomerContractAmountsDto = new AnalysisCustomerContractAmountsDto();
         // 淇敼锛氬皢鍚堝悓閲戦淇濈暀涓や綅灏忔暟
@@ -285,8 +281,9 @@
         analysisCustomerContractAmountsDto.setYny(weekYny);
         analysisCustomerContractAmountsDto.setChain(chain);
 
-        Map<String, BigDecimal> collect = salesLedgers.stream().collect(Collectors.groupingBy(SalesLedger::getCustomerName, Collectors.reducing(BigDecimal.ZERO,
-                SalesLedger::getContractAmount, BigDecimal::add)));
+        Map<String, BigDecimal> collect = salesLedgers.stream()
+                .collect(Collectors.groupingBy(SalesLedger::getCustomerName, Collectors.reducing(BigDecimal.ZERO,
+                        SalesLedger::getContractAmount, BigDecimal::add)));
         List<MapDto> mapDtos = new ArrayList<>();
         collect.forEach((k, v) -> {
             MapDto mapDto = new MapDto();
@@ -296,14 +293,14 @@
             if (contractAmount.compareTo(new BigDecimal(0)) == 0) {
                 mapDto.setRate("0");
             } else {
-                mapDto.setRate(String.format("%.2f", v.divide(contractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))));
+                mapDto.setRate(String.format("%.2f",
+                        v.divide(contractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))));
             }
             mapDtos.add(mapDto);
         });
         analysisCustomerContractAmountsDto.setItem(mapDtos);
         return analysisCustomerContractAmountsDto;
     }
-
 
     @Override
     public QualityStatisticsDto qualityStatistics() {
@@ -394,28 +391,37 @@
         approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                 .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
                 .ne(ApproveProcess::getApproveStatus, 2);
-//                .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
+        // .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
         List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
         if (CollectionUtils.isEmpty(approveProcesses)) {
             approveProcesses = new ArrayList<>();
         }
-//        // 鏌ヨ鏈鐢ㄥ姵淇濊褰�
-//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-//
-//        LaborIssue laborIssue1 = new LaborIssue();
-//        laborIssue1.setAdoptedDate(new Date());
-//        laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
-//        List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);  //staff_join_leave_record琛ㄨ鍒犻櫎
-//        if(!CollectionUtils.isEmpty(laborIssues)){
-//            for (LaborIssue laborIssue : laborIssues) {
-//                ApproveProcess approveProcess = new ApproveProcess();
-//                approveProcess.setApproveId(laborIssue.getOrderNo());
-//                approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
-//                approveProcess.setApproveTime(laborIssue.getIssueDate());
-//                approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" + laborIssue.getDictName() + "瓒呮椂鏈鍙�");
-//                approveProcesses.add(approveProcess);
-//            }
-//        }
+
+        approveProcesses.forEach(a -> {
+            if (a.getApproveType() != null) {
+                a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
+            }
+        });
+        // // 鏌ヨ鏈鐢ㄥ姵淇濊褰�
+        // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        //
+        // LaborIssue laborIssue1 = new LaborIssue();
+        // laborIssue1.setAdoptedDate(new Date());
+        // laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
+        // List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);
+        // //staff_join_leave_record琛ㄨ鍒犻櫎
+        // if(!CollectionUtils.isEmpty(laborIssues)){
+        // for (LaborIssue laborIssue : laborIssues) {
+        // ApproveProcess approveProcess = new ApproveProcess();
+        // approveProcess.setApproveId(laborIssue.getOrderNo());
+        // approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
+        // approveProcess.setApproveTime(laborIssue.getIssueDate());
+        // approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" +
+        // laborIssue.getDictName() + "瓒呮椂鏈鍙�");
+        // approveProcesses.add(approveProcess);
+        // }
+        // }
+
         return approveProcesses;
     }
 
@@ -452,31 +458,41 @@
         }
         // 搴旀敹
         List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
-//                .ge(SalesLedger::getEntryDate, startDate)
-//                .lt(SalesLedger::getEntryDate, endDate)
+        // .ge(SalesLedger::getEntryDate, startDate)
+        // .lt(SalesLedger::getEntryDate, endDate)
         );
-//        BigDecimal receivableMoney = salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        // BigDecimal receivableMoney =
+        // salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO,
+        // BigDecimal::add);
         BigDecimal receivableMoney = sumAmount(salesLedgers, SalesLedger::getContractAmount);
         // 搴斾粯
-        List<PurchaseLedger> procurementRecords = purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
-//                .ge(PurchaseLedger::getEntryDate, startDate)
-//                .lt(PurchaseLedger::getEntryDate, endDate)
-        );
-//        BigDecimal payableMoney = procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        List<PurchaseLedger> procurementRecords = purchaseLedgerMapper
+                .selectList(new LambdaQueryWrapper<PurchaseLedger>()
+                // .ge(PurchaseLedger::getEntryDate, startDate)
+                // .lt(PurchaseLedger::getEntryDate, endDate)
+                );
+        // BigDecimal payableMoney =
+        // procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO,
+        // BigDecimal::add);
         BigDecimal payableMoney = sumAmount(procurementRecords, PurchaseLedger::getContractAmount);
         // 棰勬敹
         List<ReceiptPayment> receiptPayments = receiptPaymentMapper.selectList(new LambdaQueryWrapper<ReceiptPayment>()
-//                .ge(ReceiptPayment::getReceiptPaymentDate, startDate)
-//                .lt(ReceiptPayment::getReceiptPaymentDate, endDate)
+        // .ge(ReceiptPayment::getReceiptPaymentDate, startDate)
+        // .lt(ReceiptPayment::getReceiptPaymentDate, endDate)
         );
-//        BigDecimal advanceMoney = receiptPayments.stream().map(ReceiptPayment::getReceiptPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        // BigDecimal advanceMoney =
+        // receiptPayments.stream().map(ReceiptPayment::getReceiptPaymentAmount).reduce(BigDecimal.ZERO,
+        // BigDecimal::add);
         BigDecimal advanceMoney = sumAmount(receiptPayments, ReceiptPayment::getReceiptPaymentAmount);
         // 棰勪粯
-        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new LambdaQueryWrapper<PaymentRegistration>()
-//                .ge(PaymentRegistration::getPaymentDate, startDate)
-//                .lt(PaymentRegistration::getPaymentDate, endDate)
-        );
-//        BigDecimal prepayMoney = paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
+        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper
+                .selectList(new LambdaQueryWrapper<PaymentRegistration>()
+                // .ge(PaymentRegistration::getPaymentDate, startDate)
+                // .lt(PaymentRegistration::getPaymentDate, endDate)
+                );
+        // BigDecimal prepayMoney =
+        // paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO,
+        // BigDecimal::add);
         BigDecimal prepayMoney = sumAmount(paymentRegistrations, PaymentRegistration::getCurrentPaymentAmount);
         StatisticsReceivablePayableDto statisticsReceivablePayableDto = new StatisticsReceivablePayableDto();
         statisticsReceivablePayableDto.setPayableMoney(payableMoney.subtract(prepayMoney));
@@ -509,10 +525,12 @@
         Long aLong1 = deviceRepairMapper.selectCount(new LambdaQueryWrapper<DeviceRepair>()
                 .eq(DeviceRepair::getStatus, 0)
                 .eq(DeviceRepair::getRepairName, SecurityUtils.getLoginUser().getNickName()));
-        return new HashMap<String, Object>() {{
-            put("approveTodo", aLong);
-            put("deviceRepairTodo", aLong1);
-        }};
+        return new HashMap<String, Object>() {
+            {
+                put("approveTodo", aLong);
+                put("deviceRepairTodo", aLong1);
+            }
+        };
     }
 
     @Override
@@ -529,11 +547,16 @@
         ProductOrderDto orderDto = new ProductOrderDto();
         orderDto.setStartTime(LocalDateTime.now().minusMonths(1));
         orderDto.setEndTime(LocalDateTime.now());
-        List<ProductOrderDto> productOrderDtos = productOrderMapper.pageProductOrder(new Page<>(1, -1), orderDto).getRecords();
+        List<ProductOrderDto> productOrderDtos = productOrderMapper.pageProductOrder(new Page<>(1, -1), orderDto)
+                .getRecords();
         productionProgressDto.setCompletedOrderDetails(productOrderDtos);
         long totalCount = productOrderDtos.size();
-        long count = productOrderDtos.stream().filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0).count();
-        long count2 = productOrderDtos.stream().filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(BigDecimal.ZERO) == 0).count();
+        long count = productOrderDtos.stream().filter(
+                productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
+                .count();
+        long count2 = productOrderDtos.stream()
+                .filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(BigDecimal.ZERO) == 0)
+                .count();
         productionProgressDto.setTotalOrderCount(totalCount);
         productionProgressDto.setCompletedOrderCount(count);
         productionProgressDto.setUncompletedOrderCount(count2);
@@ -547,29 +570,38 @@
         ProductWorkOrderDto workOrder = new ProductWorkOrderDto();
         workOrder.setPlanStartTime(LocalDate.now().minusMonths(1));
         workOrder.setPlanEndTime(LocalDate.now());
-        List<ProductWorkOrderDto> productWorkOrders = productWorkOrderMapper.pageProductWorkOrder(new Page<>(1, -1), workOrder).getRecords();
+        List<ProductWorkOrderDto> productWorkOrders = productWorkOrderMapper
+                .pageProductWorkOrder(new Page<>(1, -1), workOrder).getRecords();
         long sum = productWorkOrders.stream()
-                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity().compareTo(productWorkOrder.getCompleteQuantity()) > 0)
+                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity()
+                        .compareTo(productWorkOrder.getCompleteQuantity()) > 0)
                 .map(ProductWorkOrder::getPlanQuantity)
                 .mapToLong(BigDecimal::longValue)
                 .sum();
-        if (sum == 0) return null;
-        productionTurnoverDto.setTotalOrderCount(sum);//鎬诲湪鍒跺搧鏁伴噺
-        productionTurnoverDto.setAverageTurnoverDays(BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(ChronoUnit.DAYS.between(LocalDateTime.now().minusMonths(1), LocalDateTime.now())), 2, RoundingMode.HALF_UP));
+        if (sum == 0)
+            return null;
+        productionTurnoverDto.setTotalOrderCount(sum);// 鎬诲湪鍒跺搧鏁伴噺
+        productionTurnoverDto.setAverageTurnoverDays(BigDecimal.valueOf(sum).divide(
+                BigDecimal.valueOf(ChronoUnit.DAYS.between(LocalDateTime.now().minusMonths(1), LocalDateTime.now())), 2,
+                RoundingMode.HALF_UP));
         long completeQuantity = productWorkOrders.stream()
-                .filter(productWorkOrder -> productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) >= 0)
+                .filter(productWorkOrder -> productWorkOrder.getCompleteQuantity()
+                        .compareTo(productWorkOrder.getPlanQuantity()) >= 0)
                 .map(ProductWorkOrder::getCompleteQuantity)
                 .mapToLong(BigDecimal::longValue)
                 .sum();
-        productionTurnoverDto.setTurnoverEfficiency(BigDecimal.valueOf(completeQuantity).divide(BigDecimal.valueOf(sum), 2, RoundingMode.HALF_UP));
+        productionTurnoverDto.setTurnoverEfficiency(
+                BigDecimal.valueOf(completeQuantity).divide(BigDecimal.valueOf(sum), 2, RoundingMode.HALF_UP));
         Map<String, List<ProductWorkOrderDto>> map = productWorkOrders.stream()
-                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity().compareTo(productWorkOrder.getCompleteQuantity()) > 0)
+                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity()
+                        .compareTo(productWorkOrder.getCompleteQuantity()) > 0)
                 .collect(Collectors.groupingBy(ProductWorkOrderDto::getProcessName));
         List<String> strings = new ArrayList<>();
         List<Long> processQuantityDetails = new ArrayList<>();
         map.entrySet().stream().forEach(entry -> {
             String key = entry.getKey();
-            long completeSum = entry.getValue().stream().map(ProductWorkOrderDto::getCompleteQuantity).mapToLong(BigDecimal::longValue).sum();
+            long completeSum = entry.getValue().stream().map(ProductWorkOrderDto::getCompleteQuantity)
+                    .mapToLong(BigDecimal::longValue).sum();
             strings.add(key);
             processQuantityDetails.add(completeSum);
         });
@@ -635,19 +667,19 @@
         LocalDateTime currentMonthEnd = currentMonth.atEndOfMonth().atTime(23, 59, 59);
         LocalDateTime prevMonthEnd = prevMonth.atEndOfMonth().atTime(23, 59, 59);
 
-        //  鎬诲伐浣滀汉鍛�
+        // 鎬诲伐浣滀汉鍛�
         Long currentStaff = countStaff(currentMonthEnd);
         Long prevStaff = countStaff(prevMonthEnd);
         dto.setTotalStaff(currentStaff);
         dto.setStaffGrowthRate(calculateMoM(currentStaff, prevStaff));
 
-        //  鎬诲鎴锋暟
+        // 鎬诲鎴锋暟
         Long currentCustomers = countCustomers(currentMonthEnd);
         Long prevCustomers = countCustomers(prevMonthEnd);
         dto.setTotalCustomer(currentCustomers);
         dto.setCustomerGrowthRate(calculateMoM(currentCustomers, prevCustomers));
 
-        //  鎬讳緵搴斿晢鏁�
+        // 鎬讳緵搴斿晢鏁�
         Long currentSuppliers = countSuppliers(currentMonthEnd);
         Long prevSuppliers = countSuppliers(prevMonthEnd);
         dto.setTotalSupplier(currentSuppliers);
@@ -664,7 +696,6 @@
                 .le(StaffOnJob::getCreateTime, dateTime));
         return sysUserCount + staffCountItem;
     }
-
 
     private Long countCustomers(LocalDateTime dateTime) {
         return customerMapper.selectCount(new LambdaQueryWrapper<Customer>()
@@ -695,15 +726,15 @@
         LocalDate startDate;
         LocalDate endDate;
         switch (type) {
-            case 0: // 鍛�
+            case 1: // 鍛�
                 startDate = today.with(DayOfWeek.MONDAY);
                 endDate = today.with(DayOfWeek.SUNDAY);
                 break;
-            case 1: // 鏈�
+            case 2: // 鏈�
                 startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                 endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                 break;
-            case 2: // 瀛e害
+            case 3: // 瀛e害
                 Month currentMonth = today.getMonth();
                 Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                 Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
@@ -719,7 +750,7 @@
         queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
                 .ge("entry_date", startDate)
                 .le("entry_date", endDate)
-//                .ne("approval_status", 3)
+                // .ne("approval_status", 3)
                 .groupBy("supplier_name")
                 .orderByDesc("total_amount")
                 .last("LIMIT 5");
@@ -745,15 +776,15 @@
         boolean groupByMonth = false;
 
         switch (type) {
-            case 0: // 鍛�
+            case 1: // 鍛�
                 start = today.with(DayOfWeek.MONDAY);
                 end = today.with(DayOfWeek.SUNDAY);
                 break;
-            case 1: // 鏈�
+            case 2: // 鏈�
                 start = today.with(TemporalAdjusters.firstDayOfMonth());
                 end = today.with(TemporalAdjusters.lastDayOfMonth());
                 break;
-            case 2: // 瀛e害
+            case 3: // 瀛e害
                 Month firstMonthOfQuarter = today.getMonth().firstMonthOfQuarter();
                 start = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                 end = today.withMonth(firstMonthOfQuarter.plus(2).getValue()).with(TemporalAdjusters.lastDayOfMonth());
@@ -794,7 +825,8 @@
                 String dayName = d.getMonthValue() + "/" + d.getDayOfMonth();
                 BigDecimal sum = list.stream()
                         .filter(l -> l.getEntryDate() != null)
-                        .filter(l -> l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(d))
+                        .filter(l -> l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
+                                .equals(d))
                         .map(SalesLedger::getContractAmount)
                         .filter(Objects::nonNull)
                         .reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -814,109 +846,54 @@
         ProductCategoryDistributionDto dto = new ProductCategoryDistributionDto();
         List<ProductCategoryDistributionDto.MajorCategoryDto> majorItems = new ArrayList<>();
 
-        //  鎵�鏈夌殑浜у搧澶х被鍜屽皬绫�
-        List<Product> allProducts = productMapper.selectList(new LambdaQueryWrapper<Product>());
+        // 鏌ヨ鎵�鏈夌殑浜у搧绫诲埆
+        List<Product> allProducts = productMapper.selectList(new LambdaQueryWrapper<>());
         if (CollectionUtils.isEmpty(allProducts)) {
             dto.setItems(majorItems);
             return dto;
         }
 
-        List<Product> majorCategories = allProducts.stream().filter(p -> p.getParentId() == null).collect(Collectors.toList());
-        List<Product> minorCategories = allProducts.stream().filter(p -> p.getParentId() != null).collect(Collectors.toList());
+        // 鍒嗙澶х被鍜屽皬绫�
+        List<Product> majorCategories = allProducts.stream()
+                .filter(p -> p.getParentId() == null)
+                .collect(Collectors.toList());
+        List<Product> minorCategories = allProducts.stream()
+                .filter(p -> p.getParentId() != null)
+                .collect(Collectors.toList());
 
-        //  浠� sales_ledger_product 鎷垮埌姣忎釜浜у搧鐨勫瀷鍙峰簱瀛�
-        // 闇�鍖呭惈瑙勬牸鍨嬪彿浠ユ敮鎸佷笁绾у垎甯�
-        List<Map<String, Object>> quantityMaps = salesLedgerProductMapper.selectMaps(new QueryWrapper<SalesLedgerProduct>()
-                .select("product_id", "specification_model", "type", "SUM(quantity) as sum_qty")
-                .isNotNull("product_id")
-                .groupBy("product_id", "specification_model", "type"));
+        // 鎸夌埗绫籌D瀵瑰皬绫昏繘琛屽垎缁�
+        Map<Long, List<Product>> minorGroupMap = minorCategories.stream()
+                .collect(Collectors.groupingBy(Product::getParentId));
 
-        Map<Long, Map<String, Map<Integer, BigDecimal>>> modelStockGroups = new HashMap<>();
-        for (Map<String, Object> map : quantityMaps) {
-            Long productId = Long.parseLong(map.get("product_id").toString());
-            String model = map.get("specification_model") != null ? map.get("specification_model").toString() : "鏈煡鍨嬪彿";
-            Integer type = Integer.parseInt(map.get("type").toString());
-            BigDecimal sum = map.get("sum_qty") != null ? new BigDecimal(map.get("sum_qty").toString()) : BigDecimal.ZERO;
+        int totalMinorCount = minorCategories.size();
 
-            modelStockGroups.computeIfAbsent(productId, k -> new HashMap<>())
-                    .computeIfAbsent(model, k -> new HashMap<>())
-                    .put(type, sum);
-        }
-
-        //  搴撳瓨骞舵眹鎬�
-        Map<Long, List<MapDto>> productModelsMap = new HashMap<>();
-        Map<Long, BigDecimal> productTotalStockMap = new HashMap<>();
-
-        for (Long pid : modelStockGroups.keySet()) {
-            Map<String, Map<Integer, BigDecimal>> models = modelStockGroups.get(pid);
-            BigDecimal productStock = BigDecimal.ZERO;
-            List<MapDto> modelDtos = new ArrayList<>();
-
-            for (String modelName : models.keySet()) {
-                Map<Integer, BigDecimal> types = models.get(modelName);
-                BigDecimal procurement = types.getOrDefault(2, BigDecimal.ZERO);
-                BigDecimal sales = types.getOrDefault(1, BigDecimal.ZERO);
-                BigDecimal stock = procurement.subtract(sales);
-                if (stock.compareTo(BigDecimal.ZERO) < 0) stock = BigDecimal.ZERO;
-
-                MapDto modelDto = new MapDto();
-                modelDto.setName(modelName);
-                modelDto.setValue(stock.stripTrailingZeros().toPlainString());
-                modelDtos.add(modelDto);
-                productStock = productStock.add(stock);
-            }
-            productModelsMap.put(pid, modelDtos);
-            productTotalStockMap.put(pid, productStock);
-        }
-
-        BigDecimal totalInventory = productTotalStockMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
-
-        //  鍨嬪彿鍗犳瘮
-        if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
-            for (List<MapDto> dtos : productModelsMap.values()) {
-                for (MapDto m : dtos) {
-                    BigDecimal val = new BigDecimal(m.getValue());
-                    m.setRate(val.multiply(new BigDecimal("100"))
-                            .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
-                }
-            }
-        }
-
-        //  鍒嗗眰鏁版嵁
         for (Product major : majorCategories) {
             ProductCategoryDistributionDto.MajorCategoryDto majorDto = new ProductCategoryDistributionDto.MajorCategoryDto();
             majorDto.setName(major.getProductName());
 
-            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
-            BigDecimal majorStock = BigDecimal.ZERO;
+            List<Product> children = minorGroupMap.getOrDefault(major.getId(), new ArrayList<>());
+            int childrenSize = children.size();
+            // 璁剧疆灏忕被鐨勬�绘暟
+            majorDto.setValue(String.valueOf(childrenSize));
 
-            for (Product minor : minorCategories) {
-                if (major.getId().equals(minor.getParentId())) {
-                    BigDecimal stock = productTotalStockMap.getOrDefault(minor.getId(), BigDecimal.ZERO);
-                    ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
-                    minorDto.setName(minor.getProductName());
-                    minorDto.setValue(stock.stripTrailingZeros().toPlainString());
-
-                    if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
-                        minorDto.setRate(stock.multiply(new BigDecimal("100"))
-                                .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
-                    } else {
-                        minorDto.setRate("0.00");
-                    }
-
-                    minorDto.setChildren(productModelsMap.getOrDefault(minor.getId(), new ArrayList<>()));
-                    minorDtos.add(minorDto);
-                    majorStock = majorStock.add(stock);
-                }
-            }
-
-            majorDto.setValue(majorStock.stripTrailingZeros().toPlainString());
-
-            if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
-                majorDto.setRate(majorStock.multiply(new BigDecimal("100"))
-                        .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
+            // 璁$畻鍗犳瘮
+            if (totalMinorCount > 0) {
+                BigDecimal rate = BigDecimal.valueOf(childrenSize)
+                        .multiply(new BigDecimal("100"))
+                        .divide(BigDecimal.valueOf(totalMinorCount), 2, RoundingMode.HALF_UP);
+                majorDto.setRate(rate.toString());
             } else {
                 majorDto.setRate("0.00");
+            }
+
+            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
+            for (Product minor : children) {
+                ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
+                minorDto.setName(minor.getProductName());
+                minorDto.setValue("0");
+                minorDto.setRate("0.00");
+                minorDto.setChildren(new ArrayList<>());
+                minorDtos.add(minorDto);
             }
             majorDto.setChildren(minorDtos);
             majorItems.add(majorDto);
@@ -932,15 +909,15 @@
         LocalDate startDate = null;
         LocalDate endDate = null;
         switch (type) {
-            case 0:
+            case 1:
                 startDate = today.with(DayOfWeek.MONDAY);
                 endDate = today.with(DayOfWeek.SUNDAY);
                 break;
-            case 1:
+            case 2:
                 startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                 endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                 break;
-            case 2:
+            case 3:
                 Month currentMonth = today.getMonth();
                 Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                 Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
@@ -965,9 +942,948 @@
         for (Map<String, Object> map : maps) {
             CustomerContributionRankingDto rankingDto = new CustomerContributionRankingDto();
             rankingDto.setCustomerName(map.get("customer_name").toString());
-            rankingDto.setTotalAmount(map.get("total_amount") != null ? new BigDecimal(map.get("total_amount").toString()) : BigDecimal.ZERO);
+            rankingDto
+                    .setTotalAmount(map.get("total_amount") != null ? new BigDecimal(map.get("total_amount").toString())
+                            : BigDecimal.ZERO);
             result.add(rankingDto);
         }
         return result;
     }
+
+    @Override
+    public List<MapDto> productSalesAnalysis() {
+        // 鑾峰彇鎵�鏈変骇鍝佸ぇ绫荤殑閿�鍞
+        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
+        if (CollectionUtils.isEmpty(analysisResults)) {
+            return new ArrayList<>();
+        }
+
+        // 璁$畻鎬婚攢鍞
+        BigDecimal totalSalesAmount = analysisResults.stream()
+                .map(r -> (BigDecimal) r.get("value"))
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        return analysisResults.stream()
+                .map(result -> {
+                    MapDto mapDto = new MapDto();
+                    String name = (String) result.get("name");
+                    BigDecimal value = (BigDecimal) result.get("value");
+
+                    mapDto.setName(name);
+                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
+
+                    if (totalSalesAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
+                        mapDto.setRate("0.00");
+                    } else {
+                        String rate = value.divide(totalSalesAmount, 4, RoundingMode.HALF_UP)
+                                .multiply(new BigDecimal("100"))
+                                .setScale(2, RoundingMode.HALF_UP)
+                                .toString();
+                        mapDto.setRate(rate);
+                    }
+                    return mapDto;
+                })
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> rawMaterialPurchaseAmountRatio() {
+        // 鑾峰彇鎵�鏈夊師鏉愭枡鐨勯噰璐
+        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
+        if (CollectionUtils.isEmpty(analysisResults)) {
+            return new ArrayList<>();
+        }
+
+        // 璁$畻鎬婚噰璐
+        BigDecimal totalPurchaseAmount = analysisResults.stream()
+                .map(r -> (BigDecimal) r.get("value"))
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        return analysisResults.stream()
+                .map(result -> {
+                    MapDto mapDto = new MapDto();
+                    String name = (String) result.get("name");
+                    BigDecimal value = (BigDecimal) result.get("value");
+
+                    mapDto.setName(name);
+                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
+
+                    if (totalPurchaseAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
+                        mapDto.setRate("0.00");
+                    } else {
+                        String rate = value.divide(totalPurchaseAmount, 4, RoundingMode.HALF_UP)
+                                .multiply(new BigDecimal("100"))
+                                .setScale(2, RoundingMode.HALF_UP)
+                                .toString();
+                        mapDto.setRate(rate);
+                    }
+                    return mapDto;
+                })
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> salesPurchaseStorageProductCount() {
+        LocalDate now = LocalDate.now();
+        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+        String currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
+        String currentMonthNow = now.format(dtf);
+
+        LocalDate lastMonth = now.minusMonths(1);
+        String lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
+        String lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).format(dtf);
+
+        // 閿�鍞�
+        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart,
+                currentMonthNow);
+        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
+
+        // 閲囪喘
+        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart,
+                currentMonthNow);
+        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
+
+        // 鍌ㄥ瓨
+        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthNow);
+        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
+
+        List<MapDto> list = new ArrayList<>();
+        list.add(createMapDto("閿�鍞骇鍝佹暟", currentSales, lastSales));
+        list.add(createMapDto("閲囪喘浜у搧鏁�", currentPurchase, lastPurchase));
+        list.add(createMapDto("鍌ㄥ瓨浜у搧鏁�", currentStorage, lastStorage));
+
+        return list;
+    }
+
+    private MapDto createMapDto(String name, int current, int last) {
+        MapDto dto = new MapDto();
+        dto.setName(name);
+        dto.setValue(String.valueOf(current));
+
+        if (last == 0) {
+            dto.setRate(current > 0 ? "100.00" : "0.00");
+        } else {
+            BigDecimal curDec = new BigDecimal(current);
+            BigDecimal lastDec = new BigDecimal(last);
+            // 澧為暱鐜�
+            String rate = curDec.subtract(lastDec)
+                    .divide(lastDec, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setRate(rate);
+        }
+        return dto;
+    }
+
+    @Override
+    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
+        String targetName;
+        if (type == 1) {
+            targetName = "鍘熸潗鏂�";
+        } else if (type == 2) {
+            targetName = "鎴愬搧";
+        } else if (type == 3) {
+            targetName = "鍗婃垚鍝�";
+        } else {
+            return new ArrayList<>();
+        }
+
+        LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Product::getProductName, targetName).isNull(Product::getParentId);
+        Product rootCategory = productMapper.selectOne(queryWrapper);
+
+        if (rootCategory == null) {
+            log.warn("鏈壘鍒板悕绉颁负 {} 鐨勯《绾т骇鍝佸垎绫�", targetName);
+            return new ArrayList<>();
+        }
+        Long rootCategoryId = rootCategory.getId();
+
+        LocalDate now = LocalDate.now();
+        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
+
+        String startDate = now.minusDays(6).toString();
+        String endDate = now.toString();
+
+        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
+                endDate + " 23:59:59");
+
+        List<Map<String, Object>> outCounts = stockInventoryMapper.selectDailyStockOutCounts(rootCategoryId, startDate,
+                endDate + " 23:59:59");
+
+        Map<LocalDate, BigDecimal> inMap = inCounts.stream()
+                .collect(Collectors.toMap(
+                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
+                        m -> (BigDecimal) m.get("count"),
+                        BigDecimal::add));
+
+        Map<LocalDate, BigDecimal> outMap = outCounts.stream()
+                .collect(Collectors.toMap(
+                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
+                        m -> (BigDecimal) m.get("count"),
+                        BigDecimal::add));
+
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        for (int i = 6; i >= 0; i--) {
+            LocalDate date = now.minusDays(i);
+
+            Map<String, Object> dataPoint = new LinkedHashMap<>();
+            dataPoint.put("date", date.format(displayDtf)); // 1/23
+            dataPoint.put("inCount", inMap.getOrDefault(date, BigDecimal.ZERO));
+            dataPoint.put("outCount", outMap.getOrDefault(date, BigDecimal.ZERO));
+
+            result.add(dataPoint);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> productTurnoverDays() {
+        return homeMapper.productTurnoverDays();
+    }
+
+    @Autowired
+    private AccountExpenseMapper accountExpenseMapper;
+
+    @Autowired
+    private AccountIncomeMapper accountIncomeMapper;
+
+    public List<Map<String, Object>> incomeExpenseAnalysis(Integer type) {
+
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+        String dateFormat;
+        DateTimeFormatter formatter;
+        List<String> xAxis = new ArrayList<>();
+
+        // 1. 鏃堕棿鑼冨洿 & X 杞�
+        if (type == 3) {
+            // 鏈搴︼紙鎸夋湀锛�
+            Month currentMonth = today.getMonth();
+            Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+            startDate = today.withMonth(firstMonthOfQuarter.getValue())
+                    .with(TemporalAdjusters.firstDayOfMonth());
+            endDate = startDate.plusMonths(2)
+                    .with(TemporalAdjusters.lastDayOfMonth());
+
+            dateFormat = "%Y-%m";
+            formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+            LocalDate tmp = startDate;
+            while (!tmp.isAfter(endDate)) {
+                xAxis.add(tmp.format(formatter));
+                tmp = tmp.plusMonths(1);
+            }
+
+        } else {
+            // 鍛� / 鏈堬紙鎸夊ぉ锛�
+            dateFormat = "%Y-%m-%d";
+            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+            if (type == 1) {
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+            } else {
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+            }
+
+            LocalDate tmp = startDate;
+            while (!tmp.isAfter(endDate)) {
+                xAxis.add(tmp.format(formatter));
+                tmp = tmp.plusDays(1);
+            }
+        }
+
+        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        // 2. 鏌ヨ鏁版嵁
+        List<IncomeExpenseAnalysisDto> incomeList = accountIncomeMapper.selectIncomeStats(startStr, endStr, dateFormat);
+
+        // List<IncomeExpenseAnalysisDto> purchaseList =
+        // purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
+
+        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr,
+                dateFormat);
+
+        // 3. 杞� Map锛堣嚜鍔ㄥ悎骞讹級
+        Map<String, BigDecimal> incomeMap = incomeList.stream()
+                .collect(Collectors.toMap(
+                        IncomeExpenseAnalysisDto::getDateStr,
+                        IncomeExpenseAnalysisDto::getAmount,
+                        BigDecimal::add));
+
+        // Map<String, BigDecimal> purchaseMap = purchaseList.stream()
+        // .collect(Collectors.toMap(
+        // IncomeExpenseAnalysisDto::getDateStr,
+        // IncomeExpenseAnalysisDto::getAmount,
+        // BigDecimal::add));
+
+        Map<String, BigDecimal> expenseMap = expenseList.stream()
+                .collect(Collectors.toMap(
+                        IncomeExpenseAnalysisDto::getDateStr,
+                        IncomeExpenseAnalysisDto::getAmount,
+                        BigDecimal::add));
+
+        // 4. 缁勮杩斿洖
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        for (String dateStr : xAxis) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("date", dateStr);
+
+            // 鏀跺叆
+            BigDecimal income = incomeMap.getOrDefault(dateStr, BigDecimal.ZERO);
+            item.put("income", income.setScale(2, RoundingMode.HALF_UP));
+
+            // 鏀嚭 = 閲囪喘 + 璐㈠姟鏀嚭
+            // BigDecimal purchase = purchaseMap.getOrDefault(dateStr, BigDecimal.ZERO);
+            BigDecimal expense = expenseMap.getOrDefault(dateStr, BigDecimal.ZERO);
+            // BigDecimal totalExpense = purchase.add(expense);
+            BigDecimal totalExpense = expense;
+
+            item.put("expense", totalExpense.setScale(2, RoundingMode.HALF_UP));
+
+            result.add(item);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> profitTrendAnalysis() {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate = today.minusMonths(11).with(TemporalAdjusters.firstDayOfMonth());
+        LocalDate endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+
+        String dateFormat = "%Y-%m";
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+        List<String> months = new ArrayList<>();
+        LocalDate temp = startDate;
+        while (!temp.isAfter(endDate)) {
+            months.add(temp.format(formatter));
+            temp = temp.plusMonths(1);
+        }
+
+        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        // List<IncomeExpenseAnalysisDto> incomeList =
+        // salesLedgerMapper.selectIncomeStats(startStr, endStr, dateFormat);
+        List<IncomeExpenseAnalysisDto> incomeList = new ArrayList<>();
+        List<IncomeExpenseAnalysisDto> purchaseList = purchaseLedgerMapper.selectPurchaseStats(startStr, endStr,
+                dateFormat);
+        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr,
+                dateFormat);
+
+        Map<String, BigDecimal> incomeMap = incomeList.stream().collect(Collectors
+                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+        Map<String, BigDecimal> purchaseMap = purchaseList.stream().collect(Collectors
+                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+        Map<String, BigDecimal> expenseMap = expenseList.stream().collect(Collectors
+                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+
+        List<MapDto> result = new ArrayList<>();
+
+        for (String month : months) {
+            MapDto dto = new MapDto();
+            dto.setName(month);
+
+            BigDecimal income = incomeMap.getOrDefault(month, BigDecimal.ZERO);
+
+            BigDecimal purchase = purchaseMap.getOrDefault(month, BigDecimal.ZERO);
+            income = BigDecimal.ZERO;
+            BigDecimal expense = expenseMap.getOrDefault(month, BigDecimal.ZERO);
+            BigDecimal totalExpense = purchase.add(expense);
+
+            BigDecimal profit = income.subtract(totalExpense);
+
+            dto.setValue(profit.setScale(2, RoundingMode.HALF_UP).toString());
+            result.add(dto);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> expenseCompositionAnalysis(Integer type) {
+        List<MapDto> result = new ArrayList<>();
+
+        if (type == 2) {
+            List<MapDto> customerList = salesLedgerMapper.selectCustomerSalesComposition();
+            if (customerList != null) {
+                result.addAll(customerList);
+            }
+        } else {
+            BigDecimal rawMaterialAmount = salesLedgerProductMapper.selectRawMaterialExpense();
+            MapDto rawMaterialDto = new MapDto();
+            rawMaterialDto.setName("鍘熸潗鏂�");
+            rawMaterialDto.setValue(rawMaterialAmount != null ? rawMaterialAmount.toString() : "0");
+            result.add(rawMaterialDto);
+
+            List<MapDto> expenseList = accountExpenseMapper.selectExpenseComposition(null, null);
+            if (expenseList != null) {
+                result.addAll(expenseList);
+            }
+        }
+
+        BigDecimal total = BigDecimal.ZERO;
+        for (MapDto dto : result) {
+            if (dto.getValue() != null) {
+                total = total.add(new BigDecimal(dto.getValue()));
+            }
+        }
+
+        if (total.compareTo(BigDecimal.ZERO) > 0) {
+            for (MapDto dto : result) {
+                if (dto.getValue() != null) {
+                    BigDecimal val = new BigDecimal(dto.getValue());
+                    String rate = val.divide(total, 4, RoundingMode.HALF_UP)
+                            .multiply(new BigDecimal("100"))
+                            .setScale(2, RoundingMode.HALF_UP)
+                            .toString();
+                    dto.setRate(rate);
+                } else {
+                    dto.setRate("0.00");
+                }
+            }
+        } else {
+            for (MapDto dto : result) {
+                dto.setRate("0.00");
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public MonthlyIncomeDto monthlyIncome() {
+        MonthlyIncomeDto dto = new MonthlyIncomeDto();
+        LocalDate now = LocalDate.now();
+        YearMonth currentMonth = YearMonth.from(now);
+        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
+        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SalesLedgerProduct::getType, 1);
+        wrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        wrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(wrapper);
+
+        if (CollectionUtils.isEmpty(products)) {
+            return dto;
+        }
+
+        BigDecimal collected = products.stream()
+                .map(SalesLedgerProduct::getInvoiceTotal)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        dto.setMonthlyIncome(collected);
+
+        BigDecimal overdue = products.stream()
+                .map(SalesLedgerProduct::getPendingInvoiceTotal)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        dto.setOverdueNum(overdue);
+
+        BigDecimal total = collected.add(overdue);
+
+        if (total.compareTo(BigDecimal.ZERO) > 0) {
+            String collectionRate = collected.divide(total, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setCollectionRate(collectionRate);
+
+            String overdueRate = overdue.divide(total, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setOverdueRate(overdueRate);
+        }
+
+        return dto;
+    }
+
+    public MonthlyExpenditureDto monthlyExpenditure() {
+
+        MonthlyExpenditureDto dto = new MonthlyExpenditureDto();
+
+        // 褰撴湀鏃堕棿鑼冨洿
+        LocalDate now = LocalDate.now();
+        YearMonth currentMonth = YearMonth.from(now);
+        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
+        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        // 閲囪喘鍙拌处锛坱ype = 2锛�
+        LambdaQueryWrapper<SalesLedgerProduct> purchaseWrapper = new LambdaQueryWrapper<>();
+        purchaseWrapper.eq(SalesLedgerProduct::getType, 2);
+        purchaseWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        purchaseWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> purchaseProducts = salesLedgerProductMapper.selectList(purchaseWrapper);
+
+        BigDecimal rawMaterialCost = BigDecimal.ZERO; // 鍘熸潗鏂欐垚鏈�
+        BigDecimal paidAmount = BigDecimal.ZERO; // 宸蹭粯娆鹃噾棰�
+        BigDecimal pendingAmount = BigDecimal.ZERO; // 寰呬粯娆鹃噾棰�
+
+        if (!CollectionUtils.isEmpty(purchaseProducts)) {
+            for (SalesLedgerProduct p : purchaseProducts) {
+
+                if (p.getTaxInclusiveTotalPrice() != null) {
+                    rawMaterialCost = rawMaterialCost.add(p.getTaxInclusiveTotalPrice());
+                }
+
+                if (p.getTicketsTotal() != null) {
+                    paidAmount = paidAmount.add(p.getTicketsTotal());
+                }
+
+                if (p.getPendingTicketsTotal() != null) {
+                    pendingAmount = pendingAmount.add(p.getPendingTicketsTotal());
+                }
+            }
+        }
+
+        // 鍏朵粬璐圭敤
+        LambdaQueryWrapper<AccountExpense> expenseWrapper = new LambdaQueryWrapper<>();
+        expenseWrapper.ge(AccountExpense::getExpenseDate,
+                java.sql.Date.valueOf(currentMonth.atDay(1)));
+        expenseWrapper.le(AccountExpense::getExpenseDate,
+                java.sql.Date.valueOf(currentMonth.atEndOfMonth()));
+
+        List<AccountExpense> expenses = accountExpenseMapper.selectList(expenseWrapper);
+
+        BigDecimal otherExpense = BigDecimal.ZERO;
+        if (!CollectionUtils.isEmpty(expenses)) {
+            for (AccountExpense e : expenses) {
+                if (e.getExpenseMoney() != null) {
+                    otherExpense = otherExpense.add(e.getExpenseMoney());
+                }
+            }
+        }
+
+        // 鏈堝害鎬绘敮鍑�
+        // BigDecimal monthlyExpenditure = rawMaterialCost.add(otherExpense);
+        BigDecimal monthlyExpenditure = otherExpense;
+        dto.setMonthlyExpenditure(monthlyExpenditure);
+
+        // 宸蹭粯娆� 梅锛堝凡浠樻 + 寰呬粯娆撅級
+        BigDecimal totalPayable = paidAmount.add(pendingAmount);
+        if (totalPayable.compareTo(BigDecimal.ZERO) > 0) {
+            String paymentRate = paidAmount
+                    .divide(totalPayable, 4, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setPaymentRate(paymentRate);
+        }
+
+        // 鍞彴璐︼紙type = 1锛�
+        LambdaQueryWrapper<SalesLedgerProduct> salesWrapper = new LambdaQueryWrapper<>();
+        salesWrapper.eq(SalesLedgerProduct::getType, 1);
+        salesWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        salesWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> salesProducts = salesLedgerProductMapper.selectList(salesWrapper);
+
+        BigDecimal revenue = BigDecimal.ZERO;
+        if (!CollectionUtils.isEmpty(salesProducts)) {
+            for (SalesLedgerProduct s : salesProducts) {
+                if (s.getInvoiceAmount() != null) {
+                    revenue = revenue.add(s.getInvoiceAmount());
+                }
+            }
+        }
+
+        // 姣涘埄娑� & 鍒╂鼎鐜�
+        if (revenue.compareTo(BigDecimal.ZERO) > 0) {
+
+            // 姣涘埄娑� = 閿�鍞敹鍏� - 鍘熸潗鏂欐垚鏈�
+            BigDecimal grossProfit = revenue.subtract(rawMaterialCost);
+            dto.setGrossProfit(grossProfit);
+
+            // 鍒╂鼎鐜� = (閿�鍞敹鍏� - 鏈堝害鎬绘敮鍑�) / 閿�鍞敹鍏�
+            BigDecimal profit = revenue.subtract(monthlyExpenditure);
+            String profitMarginRate = profit
+                    .divide(revenue, 4, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+
+            dto.setProfitMarginRate(profitMarginRate);
+        }
+
+        return dto;
+    }
+
+    @Autowired
+    private ProductionProductInputMapper productionProductInputMapper;
+
+    @Override
+    public List<InputOutputAnalysisDto> inputOutputAnalysis(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+
+        switch (type) {
+            case 1: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+            case 2: // 鏈�
+                startDate = today.withDayOfMonth(1);
+                break;
+            case 3: // 瀛e害
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+        }
+
+        LocalDate endDate = today.plusDays(1);
+
+        String startStr = startDate.atStartOfDay()
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        String endStr = endDate.atStartOfDay()
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        List<Map<String, Object>> inputList = productionProductInputMapper.selectInputStats(startStr, endStr);
+        List<Map<String, Object>> outputList = productionProductOutputMapper.selectDailyOutputStats(startStr, endStr);
+
+        Map<String, InputOutputAnalysisDto> dateMap = new HashMap<>();
+
+        if (!CollectionUtils.isEmpty(inputList)) {
+            for (Map<String, Object> map : inputList) {
+                String date = (String) map.get("date");
+                if (date == null)
+                    continue;
+                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
+                dto.setDate(date);
+                BigDecimal qty = (BigDecimal) map.get("quantity");
+                dto.setInputSum(dto.getInputSum() != null ? dto.getInputSum().add(qty) : qty);
+                dateMap.put(date, dto);
+            }
+        }
+
+        if (!CollectionUtils.isEmpty(outputList)) {
+            for (Map<String, Object> map : outputList) {
+                String date = (String) map.get("date");
+                if (date == null)
+                    continue;
+                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
+                dto.setDate(date);
+                BigDecimal qty = (BigDecimal) map.get("quantity");
+                dto.setOutputSum(dto.getOutputSum() != null ? dto.getOutputSum().add(qty) : qty);
+                dateMap.put(date, dto);
+            }
+        }
+
+        dateMap.values().forEach(dto -> {
+            if (dto.getInputSum() == null)
+                dto.setInputSum(BigDecimal.ZERO);
+            if (dto.getOutputSum() == null)
+                dto.setOutputSum(BigDecimal.ZERO);
+        });
+
+        return dateMap.values().stream()
+                .sorted(Comparator.comparing(InputOutputAnalysisDto::getDate))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> processOutputAnalysis(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate = today;
+
+        switch (type) {
+            case 1: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+            case 2: // 鏈�
+                startDate = today.withDayOfMonth(1);
+                break;
+            case 3: // 瀛e害
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+        }
+
+        ProductWorkOrderDto queryDto = new ProductWorkOrderDto();
+        queryDto.setPlanStartTime(startDate);
+        queryDto.setPlanEndTime(endDate);
+
+        List<ProductWorkOrderDto> list = productWorkOrderMapper.pageProductWorkOrder(new Page<>(1, -1), queryDto)
+                .getRecords();
+
+        if (CollectionUtils.isEmpty(list)) {
+            return new ArrayList<>();
+        }
+
+        Map<String, BigDecimal> processOutputMap = list.stream()
+                .filter(item -> item.getProcessName() != null && item.getCompleteQuantity() != null)
+                .collect(Collectors.groupingBy(
+                        ProductWorkOrderDto::getProcessName,
+                        Collectors.reducing(BigDecimal.ZERO,
+                                ProductWorkOrderDto::getCompleteQuantity,
+                                BigDecimal::add)));
+
+        BigDecimal total = processOutputMap.values().stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        if (BigDecimal.ZERO.compareTo(total) == 0) {
+            return new ArrayList<>();
+        }
+
+        List<MapDto> result = new ArrayList<>();
+        processOutputMap.forEach((name, value) -> {
+            MapDto dto = new MapDto();
+            dto.setName(name);
+            dto.setValue(value.stripTrailingZeros().toPlainString());
+
+            BigDecimal rate = value
+                    .divide(total, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"));
+
+            dto.setRate(rate.setScale(2, RoundingMode.HALF_UP) + "%");
+            result.add(dto);
+        });
+
+        return result;
+    }
+
+    @Override
+    public List<WorkOrderEfficiencyDto> workOrderEfficiencyAnalysis(Integer type) {
+
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate = today;
+
+        switch (type) {
+            case 1: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+            case 2: // 鏈�
+                startDate = today.withDayOfMonth(1);
+                break;
+            case 3: // 瀛e害
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+        }
+
+        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        List<ProductWorkOrderDto> startList = productWorkOrderMapper.selectWorkOrderStartStats(startStr, endStr);
+
+        List<com.ruoyi.production.dto.ProductionProductOutputDto> outputList = productionProductOutputMapper
+                .selectOutputStats(startStr, endStr);
+
+        Map<String, WorkOrderEfficiencyDto> dateMap = new HashMap<>();
+
+        if (!CollectionUtils.isEmpty(startList)) {
+            for (ProductWorkOrderDto item : startList) {
+                if (item.getPlanStartTime() != null) {
+                    String date = item.getPlanStartTime().toString();
+                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
+                    dto.setDate(date);
+                    BigDecimal qty = item.getPlanQuantity() != null ? item.getPlanQuantity() : BigDecimal.ZERO;
+                    dto.setStartQuantity(dto.getStartQuantity() != null ? dto.getStartQuantity().add(qty) : qty);
+                    dateMap.put(date, dto);
+                }
+            }
+        }
+
+        // 瀹屽伐鏁伴噺鍜岃壇鍝佺巼
+        if (!CollectionUtils.isEmpty(outputList)) {
+            for (com.ruoyi.production.dto.ProductionProductOutputDto item : outputList) {
+                if (item.getCreateTime() != null) {
+                    String date = item.getCreateTime().toLocalDate().toString();
+                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
+                    dto.setDate(date);
+
+                    BigDecimal finishQty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO;
+                    BigDecimal scrapQty = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO;
+
+                    dto.setFinishQuantity(
+                            dto.getFinishQuantity() != null ? dto.getFinishQuantity().add(finishQty) : finishQty);
+                }
+            }
+
+            Map<String, BigDecimal> scrapMap = outputList.stream()
+                    .filter(i -> i.getCreateTime() != null)
+                    .collect(Collectors.groupingBy(
+                            i -> i.getCreateTime().toLocalDate().toString(),
+                            Collectors.reducing(BigDecimal.ZERO,
+                                    i -> i.getScrapQty() != null ? i.getScrapQty() : BigDecimal.ZERO,
+                                    BigDecimal::add)));
+
+            Map<String, BigDecimal> finishMap = outputList.stream()
+                    .filter(i -> i.getCreateTime() != null)
+                    .collect(Collectors.groupingBy(
+                            i -> i.getCreateTime().toLocalDate().toString(),
+                            Collectors.reducing(BigDecimal.ZERO,
+                                    i -> i.getQuantity() != null ? i.getQuantity() : BigDecimal.ZERO,
+                                    BigDecimal::add)));
+
+            finishMap.forEach((date, qty) -> {
+                WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
+                dto.setDate(date);
+                dto.setFinishQuantity(qty);
+                dateMap.put(date, dto);
+            });
+
+            dateMap.forEach((date, dto) -> {
+                BigDecimal finish = dto.getFinishQuantity() != null ? dto.getFinishQuantity() : BigDecimal.ZERO;
+                BigDecimal scrap = scrapMap.getOrDefault(date, BigDecimal.ZERO);
+                BigDecimal total = finish.add(scrap);
+
+                if (total.compareTo(BigDecimal.ZERO) > 0) {
+                    BigDecimal rate = finish.divide(total, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))
+                            .setScale(2, RoundingMode.HALF_UP);
+                    dto.setYieldRate(rate.toString());
+                } else {
+                    dto.setYieldRate("0.00");
+                }
+
+                if (dto.getStartQuantity() == null)
+                    dto.setStartQuantity(BigDecimal.ZERO);
+                if (dto.getFinishQuantity() == null)
+                    dto.setFinishQuantity(BigDecimal.ZERO);
+            });
+        }
+
+        dateMap.values().forEach(dto -> {
+            if (dto.getStartQuantity() == null)
+                dto.setStartQuantity(BigDecimal.ZERO);
+            if (dto.getFinishQuantity() == null)
+                dto.setFinishQuantity(BigDecimal.ZERO);
+            if (dto.getYieldRate() == null)
+                dto.setYieldRate("0.00");
+        });
+
+        return dateMap.values().stream()
+                .sorted(Comparator.comparing(WorkOrderEfficiencyDto::getDate))
+                .collect(Collectors.toList());
+    }
+
+    @Autowired
+    private com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
+
+    @Override
+    public List<MapDto> productionAccountingAnalysis(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate = today;
+
+        switch (type) {
+            case 1: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+            case 2: // 鏈�
+                startDate = today.withDayOfMonth(1);
+                break;
+            case 3: // 瀛e害
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                break;
+        }
+
+        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        List<Map<String, Object>> wagesList = salesLedgerProductionAccountingMapper.selectDailyWagesStats(startStr,
+                endStr);
+
+        if (CollectionUtils.isEmpty(wagesList)) {
+            return new ArrayList<>();
+        }
+
+        List<MapDto> result = new ArrayList<>();
+        for (Map<String, Object> map : wagesList) {
+            MapDto dto = new MapDto();
+            dto.setName((String) map.get("date"));
+            BigDecimal wages = (BigDecimal) map.get("wages");
+            dto.setValue(wages != null ? wages.toString() : "0");
+            result.add(dto);
+        }
+
+        result.sort(Comparator.comparing(MapDto::getName));
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> orderCount() {
+        LocalDate now = LocalDate.now();
+
+        String currentStart = now.withDayOfMonth(1).atStartOfDay()
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        String currentEnd = now.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        LocalDate lastMonthDate = now.minusMonths(1);
+        String lastStart = lastMonthDate.withDayOfMonth(1).atStartOfDay()
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        String lastEnd = lastMonthDate.withDayOfMonth(lastMonthDate.lengthOfMonth()).atTime(LocalTime.MAX)
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        int currentCreated = productOrderMapper.countCreated(currentStart, currentEnd);
+        int lastCreated = productOrderMapper.countCreated(lastStart, lastEnd);
+        MapDto createdDto = createOrderCountDto("鐢熶骇璁㈠崟鏁�", currentCreated, lastCreated);
+
+        int currentCompleted = productOrderMapper.countCompleted(currentStart, currentEnd);
+        int lastCompleted = productOrderMapper.countCompleted(lastStart, lastEnd);
+        MapDto completedDto = createOrderCountDto("宸插畬鎴愯鍗曟暟", currentCompleted, lastCompleted);
+
+        int currentPending = productOrderMapper.countPending(currentStart, currentEnd);
+        int lastPending = productOrderMapper.countPending(lastStart, lastEnd);
+        MapDto pendingDto = createOrderCountDto("寰呯敓浜ц鍗曟暟", currentPending, lastPending);
+
+        return Arrays.asList(createdDto, completedDto, pendingDto);
+    }
+
+    private MapDto createOrderCountDto(String name, int current, int last) {
+        MapDto dto = new MapDto();
+        dto.setName(name);
+        dto.setValue(String.valueOf(current));
+
+        BigDecimal currentVal = new BigDecimal(current);
+        BigDecimal lastVal = new BigDecimal(last);
+
+        if (lastVal.compareTo(BigDecimal.ZERO) == 0) {
+            dto.setRate(currentVal.compareTo(BigDecimal.ZERO) > 0 ? "100" : "0");
+        } else {
+            // (Current - Last) / Last * 100
+            BigDecimal diff = currentVal.subtract(lastVal);
+            BigDecimal rate = diff.divide(lastVal, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")).setScale(0,
+                    RoundingMode.HALF_UP);
+            dto.setRate(rate.toString());
+        }
+        return dto;
+    }
+
 }

--
Gitblit v1.9.3