From 7e9f46c8cb12e4c59a8bf529a9280067d217fe12 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期四, 02 四月 2026 09:40:45 +0800
Subject: [PATCH] feat: 完成固废消纳量看板接口

---
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java | 1920 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 1,809 insertions(+), 111 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 ae752df..79d9102 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -2,16 +2,16 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.mapper.AccountExpenseMapper;
 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;
 import com.ruoyi.basic.mapper.ProductMapper;
-import com.ruoyi.basic.mapper.ProductModelMapper;
 import com.ruoyi.basic.mapper.SupplierManageMapper;
 import com.ruoyi.basic.pojo.Customer;
 import com.ruoyi.basic.pojo.Product;
@@ -20,43 +20,47 @@
 import com.ruoyi.collaborativeApproval.pojo.Notice;
 import com.ruoyi.common.enums.ApproveTypeEnum;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.device.mapper.DeviceRepairMapper;
 import com.ruoyi.device.pojo.DeviceRepair;
 import com.ruoyi.dto.MapDto;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.home.dto.*;
+import com.ruoyi.home.mapper.HomeMapper;
 import com.ruoyi.home.service.HomeService;
-import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
-import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
-import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.production.dto.ProductOrderDto;
 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.production.mapper.*;
+import com.ruoyi.production.pojo.*;
+import com.ruoyi.production.service.*;
+import com.ruoyi.productionPlan.enums.AddressRegionEnum;
+import com.ruoyi.productionPlan.mapper.SalesDeliveryMapper;
+import com.ruoyi.productionPlan.pojo.SalesDelivery;
 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.domain.SysDictData;
 import com.ruoyi.project.system.mapper.SysDeptMapper;
-import com.ruoyi.project.system.mapper.SysUserDeptMapper;
-import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.mapper.SysDictDataMapper;
 import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
 import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
 import com.ruoyi.purchase.pojo.PaymentRegistration;
 import com.ruoyi.purchase.pojo.PurchaseLedger;
 import com.ruoyi.quality.mapper.QualityInspectMapper;
+import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
 import com.ruoyi.quality.pojo.QualityInspect;
+import com.ruoyi.quality.pojo.QualityUnqualified;
 import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
 import com.ruoyi.sales.pojo.ReceiptPayment;
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.production.utils.UnitUtils;
 import com.ruoyi.staff.mapper.StaffOnJobMapper;
 import com.ruoyi.staff.pojo.StaffOnJob;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.energy.mapper.EnergyConsumptionDetailMapper;
+import com.ruoyi.energy.dto.EnergyCostDto;
+import com.ruoyi.energy.vo.EnergyStatisticsVo;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -93,9 +97,6 @@
     private StockInventoryMapper stockInventoryMapper;
 
     @Autowired
-    private ProcurementRecordMapper procurementRecordStorageMapper;
-
-    @Autowired
     private QualityInspectMapper qualityStatisticsMapper;
 
     @Autowired
@@ -112,32 +113,51 @@
 
     @Autowired
     private NoticeMapper noticeMapper;
+
     @Autowired
     private ProductOrderMapper productOrderMapper;
+
     @Autowired
     private ProductWorkOrderMapper productWorkOrderMapper;
-    @Autowired
-    private ProductModelMapper productModelMapper;
+
     @Autowired
     private ProductMapper productMapper;
-    @Autowired
-    private StockUtils stockUtils;
+
     @Autowired
     private StaffOnJobMapper staffOnJobMapper;
+
     @Autowired
     private CustomerMapper customerMapper;
+
     @Autowired
     private SupplierManageMapper supplierManageMapper;
-    @Autowired
-    private SysUserMapper sysUserMapper;
-    @Autowired
-    private SysUserDeptMapper sysUserDeptMapper;
 
     @Autowired
     private HomeMapper homeMapper;
 
     @Autowired
     private ProductionProductOutputMapper productionProductOutputMapper;
+
+    @Autowired
+    private QualityInspectMapper qualityInspectMapper;
+
+    @Autowired
+    private EnergyConsumptionDetailMapper energyConsumptionDetailMapper;
+
+    @Autowired
+    private QualityUnqualifiedMapper qualityUnqualifiedMapper;
+
+    @Autowired
+    private ProductProcessMapper productProcessMapper;
+
+    @Autowired
+    private AccountExpenseMapper accountExpenseMapper;
+
+    @Autowired
+    private AccountIncomeMapper accountIncomeMapper;
+
+    @Autowired
+    private SalesDeliveryMapper salesDeliveryMapper;
 
     @Override
     public HomeBusinessDto business() {
@@ -172,28 +192,31 @@
         LambdaQueryWrapper<PurchaseLedger> queryWrapper = new LambdaQueryWrapper<>();
         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)
+            LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapperCopy = new LambdaQueryWrapper<>();
+            salesLedgerProductMapperLambdaQueryWrapperCopy.eq(SalesLedgerProduct::getType, 2) // 閲囪喘绫诲瀷
                     .in(SalesLedgerProduct::getSalesLedgerId,
                             purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
+
             List<SalesLedgerProduct> salesLedgerProductsCopy = salesLedgerProductMapper
                     .selectList(salesLedgerProductMapperLambdaQueryWrapperCopy);
-            // 鍚堣鍚堝悓閲戦
+
+            // 鍚堣鍚堝悓鎬婚噾棰�
             BigDecimal receiveAmount = purchaseLedgers.stream()
                     .map(PurchaseLedger::getContractAmount)
                     .filter(Objects::nonNull)
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
-            // 鏈紑绁ㄩ噾棰�
+
+            //  寰呬粯娆炬�婚噾棰�
             BigDecimal unReceiptPaymentAmount = salesLedgerProductsCopy.stream()
-                    .map(SalesLedgerProduct::getNoInvoiceAmount)
+                    .map(SalesLedgerProduct::getPendingTicketsTotal)
                     .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());
         }
         // 缁熻搴撳瓨
         BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal();
@@ -263,8 +286,8 @@
                 .filter(Objects::nonNull)
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
         BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 淇敼锛氫娇鐢�
-                                                                                              // todayContractAmount 鑰屼笉鏄�
-                                                                                              // yesterdayContractAmount
+        // todayContractAmount 鑰屼笉鏄�
+        // yesterdayContractAmount
         // 鏃ョ幆姣�
         String chain = "";
         if (subtract1.compareTo(BigDecimal.ZERO) == 0
@@ -324,10 +347,10 @@
             // 褰撴湀鐨勭粨鏉熸棩鏈燂紙姣忔湀鏈�鍚庝竴澶╋級
             LocalDate monthEnd = currentMonth.withDayOfMonth(currentMonth.lengthOfMonth());
 
-            // 鏋勫缓褰撴湀鐨勬煡璇㈡潯浠讹紙濡傛灉鎯充竴娆℃�ф煡鍏�4涓湀鏁版嵁鍐嶅唴瀛樼瓫閫夛紝鍙紭鍖栦负鍏堟煡鍏ㄥ啀寰幆绛涢�夛級
+            // 鏋勫缓褰撴湀鐨勬煡璇㈡潯浠�
             LambdaQueryWrapper<QualityInspect> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.ge(QualityInspect::getCheckTime, monthStart)
-                    .le(QualityInspect::getCheckTime, monthEnd); // 绛涢�夊綋鏈堟暟鎹�
+            queryWrapper.ge(QualityInspect::getCheckTime, monthStart.toString())
+                    .le(QualityInspect::getCheckTime, monthEnd.toString());
             List<QualityInspect> monthInspects = qualityStatisticsMapper.selectList(queryWrapper);
             BigDecimal reduce = monthInspects.stream()
                     .filter(inspect -> inspect.getInspectType().equals(0))
@@ -458,8 +481,8 @@
         }
         // 搴旀敹
         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,
@@ -468,8 +491,8 @@
         // 搴斾粯
         List<PurchaseLedger> procurementRecords = purchaseLedgerMapper
                 .selectList(new LambdaQueryWrapper<PurchaseLedger>()
-                // .ge(PurchaseLedger::getEntryDate, startDate)
-                // .lt(PurchaseLedger::getEntryDate, endDate)
+                        // .ge(PurchaseLedger::getEntryDate, startDate)
+                        // .lt(PurchaseLedger::getEntryDate, endDate)
                 );
         // BigDecimal payableMoney =
         // procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO,
@@ -477,8 +500,8 @@
         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,
@@ -487,8 +510,8 @@
         // 棰勪粯
         List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper
                 .selectList(new LambdaQueryWrapper<PaymentRegistration>()
-                // .ge(PaymentRegistration::getPaymentDate, startDate)
-                // .lt(PaymentRegistration::getPaymentDate, endDate)
+                        // .ge(PaymentRegistration::getPaymentDate, startDate)
+                        // .lt(PaymentRegistration::getPaymentDate, endDate)
                 );
         // BigDecimal prepayMoney =
         // paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO,
@@ -552,7 +575,7 @@
         productionProgressDto.setCompletedOrderDetails(productOrderDtos);
         long totalCount = productOrderDtos.size();
         long count = productOrderDtos.stream().filter(
-                productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
+                        productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
                 .count();
         long count2 = productOrderDtos.stream()
                 .filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(BigDecimal.ZERO) == 0)
@@ -627,9 +650,9 @@
 
         for (SysDept dept : depts) {
             if ("0".equals(dept.getStatus()) && "0".equals(dept.getDelFlag())) {
-                Long count = sysUserDeptMapper.selectCount(new LambdaQueryWrapper<SysUserDept>()
-                        .eq(SysUserDept::getDeptId, dept.getDeptId()));
-                if (count > 0) {
+                Long count = staffOnJobMapper.selectCount(new QueryWrapper<StaffOnJob>()
+                        .eq("sys_dept_id", dept.getDeptId()).eq("staff_state", 1));
+                if (count != null && count > 0) {
                     Map<String, Object> map = new HashMap<>();
                     map.put("name", dept.getDeptName());
                     map.put("count", count);
@@ -689,12 +712,11 @@
     }
 
     private Long countStaff(LocalDateTime dateTime) {
-        Long sysUserCount = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>()
-                .eq(SysUser::getDelFlag, "0")
-                .le(SysUser::getCreateTime, dateTime));
         Long staffCountItem = staffOnJobMapper.selectCount(new LambdaQueryWrapper<StaffOnJob>()
+                .isNotNull(StaffOnJob::getStaffState)
+                .eq(StaffOnJob::getStaffState, 1)
                 .le(StaffOnJob::getCreateTime, dateTime));
-        return sysUserCount + staffCountItem;
+        return staffCountItem;
     }
 
     private Long countCustomers(LocalDateTime dateTime) {
@@ -1026,28 +1048,20 @@
 
     @Override
     public List<MapDto> salesPurchaseStorageProductCount() {
-        LocalDate now = LocalDate.now();
-        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+        LocalDateTime lastMonth = now.minusMonths(1);
+        LocalDateTime lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+        LocalDateTime lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
 
-        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 currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, now);
         int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
-
-        // 閲囪喘
-        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart,
-                currentMonthNow);
+        //  閲囪喘
+        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, now);
         int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
-
-        // 鍌ㄥ瓨
-        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthNow);
+        //  鍌ㄥ瓨
+        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, now);
         int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
 
         List<MapDto> list = new ArrayList<>();
@@ -1146,12 +1160,6 @@
     public List<MapDto> productTurnoverDays() {
         return homeMapper.productTurnoverDays();
     }
-
-    @Autowired
-    private AccountExpenseMapper accountExpenseMapper;
-
-    @Autowired
-    private AccountIncomeMapper accountIncomeMapper;
 
     public List<Map<String, Object>> incomeExpenseAnalysis(Integer type) {
 
@@ -1276,36 +1284,21 @@
         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);
+        List<IncomeExpenseAnalysisDto> incomeList = accountIncomeMapper.selectIncomeStats(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);
-
+            BigDecimal profit = income.subtract(expense);
             dto.setValue(profit.setScale(2, RoundingMode.HALF_UP).toString());
             result.add(dto);
         }
@@ -1742,9 +1735,11 @@
                     .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)));
+                            Collectors.reducing(BigDecimal.ZERO, i -> {
+                                BigDecimal qty = (i.getQuantity() != null) ? i.getQuantity() : BigDecimal.ZERO;
+                                BigDecimal scrap = (i.getScrapQty() != null) ? i.getScrapQty() : BigDecimal.ZERO;
+                                return qty.subtract(scrap);
+                            }, BigDecimal::add)));
 
             finishMap.forEach((date, qty) -> {
                 WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
@@ -1788,10 +1783,10 @@
     }
 
     @Autowired
-    private com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
+    private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
 
     @Override
-    public List<MapDto> productionAccountingAnalysis(Integer type) {
+    public List<ProductionAccountingDto> productionAccountingAnalysis(Integer type) {
         LocalDate today = LocalDate.now();
         LocalDate startDate;
         LocalDate endDate = today;
@@ -1813,8 +1808,9 @@
                 break;
         }
 
-        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
-        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        String endStr = endDate.plusDays(1).atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
 
         List<Map<String, Object>> wagesList = salesLedgerProductionAccountingMapper.selectDailyWagesStats(startStr,
                 endStr);
@@ -1823,16 +1819,17 @@
             return new ArrayList<>();
         }
 
-        List<MapDto> result = new ArrayList<>();
+        List<ProductionAccountingDto> 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");
+            ProductionAccountingDto dto = new ProductionAccountingDto();
+            dto.setDateStr(map.get("dateStr").toString());
+            dto.setNumberOfCompleted(((BigDecimal) map.get("numberOfCompleted")).intValue());
+            dto.setAmount(map.get("amount") != null ? (BigDecimal) map.get("amount") : BigDecimal.ZERO);
+            dto.setPassRate(map.get("passRate") != null ? (BigDecimal) map.get("passRate") : BigDecimal.ZERO);
             result.add(dto);
         }
 
-        result.sort(Comparator.comparing(MapDto::getName));
+        result.sort(Comparator.comparing(ProductionAccountingDto::getDateStr));
 
         return result;
     }
@@ -1886,4 +1883,1705 @@
         return dto;
     }
 
+    @Override
+    public QualityQualifiedAnalysisDto rawMaterialDetection(Integer type) {
+        return commonDetection(type, 0);
+    }
+
+    @Override
+    public QualityQualifiedAnalysisDto processDetection(Integer type) {
+        return commonDetection(type, 1);
+    }
+
+    @Override
+    public QualityQualifiedAnalysisDto factoryDetection(Integer type) {
+        return commonDetection(type, 2);
+    }
+
+    private QualityQualifiedAnalysisDto commonDetection(Integer type, Integer inspectType) {
+
+        LocalDate[] range = calcDateRange(type);
+        LocalDate startDate = range[0];
+        LocalDate endDate = range[1];
+
+        String startStr = startDate.toString();
+        String endStr = endDate.toString();
+
+        List<QualityInspect> list = qualityInspectMapper.selectList(
+                new LambdaQueryWrapper<QualityInspect>()
+                        .eq(QualityInspect::getInspectType, inspectType)
+                        .eq(QualityInspect::getInspectState, 1)
+                        .ge(QualityInspect::getCheckTime, startStr)
+                        .le(QualityInspect::getCheckTime, endStr));
+
+        return buildQualifiedAnalysis(list);
+    }
+
+    private LocalDate[] calcDateRange(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+
+        switch (type) {
+            case 1: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 2: // 鏈�
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            case 3: // 瀛e害
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                endDate = LocalDate.of(today.getYear(), startMonth + 2, 1)
+                        .with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+        }
+
+        return new LocalDate[]{startDate, endDate};
+    }
+
+    private QualityQualifiedAnalysisDto buildQualifiedAnalysis(List<QualityInspect> list) {
+        List<QualityQualifiedAnalysisDto> result = new ArrayList<>();
+        QualityQualifiedAnalysisDto dto = new QualityQualifiedAnalysisDto();
+
+        if (CollectionUtils.isEmpty(list)) {
+            dto.setQualifiedCount(0);
+            dto.setUnqualifiedCount(0);
+            dto.setQualifiedRate(BigDecimal.ZERO.setScale(2));
+            dto.setUnqualifiedRate(BigDecimal.ZERO.setScale(2));
+            return dto;
+        }
+
+        BigDecimal qualifiedCount = BigDecimal.ZERO;
+        BigDecimal unqualifiedCount = BigDecimal.ZERO;
+
+        for (QualityInspect item : list) {
+            if ("鍚堟牸".equals(item.getCheckResult())) {
+                qualifiedCount = qualifiedCount.add(item.getQuantity());
+            } else {
+                unqualifiedCount = unqualifiedCount.add(item.getQuantity());
+            }
+        }
+
+        BigDecimal totalCount = qualifiedCount.add(unqualifiedCount);
+
+        dto.setQualifiedCount(qualifiedCount.intValue());
+        dto.setUnqualifiedCount(unqualifiedCount.intValue());
+
+        if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
+            dto.setQualifiedRate(BigDecimal.ZERO.setScale(2));
+            dto.setUnqualifiedRate(BigDecimal.ZERO.setScale(2));
+            return dto;
+        }
+
+        BigDecimal hundred = BigDecimal.valueOf(100);
+
+        dto.setQualifiedRate(qualifiedCount.divide(totalCount, 4, RoundingMode.HALF_UP)
+                .multiply(hundred)
+                .setScale(2, RoundingMode.HALF_UP));
+
+        dto.setUnqualifiedRate(unqualifiedCount.divide(totalCount, 4, RoundingMode.HALF_UP)
+                .multiply(hundred)
+                .setScale(2, RoundingMode.HALF_UP));
+
+        return dto;
+    }
+
+    @Override
+    public QualityInspectionCountDto qualityInspectionCount() {
+        String todayStr = LocalDate.now().toString();
+        String prevDayStr = LocalDate.now().minusDays(1).toString();
+        // 鏌ヨ鍑烘埅姝粖鏃ョ殑鎬绘楠屾暟
+        List<QualityInspect> todayList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .le(QualityInspect::getCheckTime, todayStr));
+        // 鏌ヨ鍑烘埅姝㈠墠涓�澶╃殑鎬绘楠屾暟
+        List<QualityInspect> prevList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .le(QualityInspect::getCheckTime, prevDayStr));
+        // 璁$畻浠婃棩鐨勬�绘楠屾暟
+        BigDecimal todayCount = todayList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 璁$畻鍓嶄竴澶╃殑鎬绘楠屾暟
+        BigDecimal prevCount = prevList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        // 璁$畻浠婃棩鐩稿鏄ㄥぉ鐨勪竴涓�绘瘮澧為暱
+        BigDecimal growthRate = calcGrowthRate(todayCount, prevCount);
+
+        // 璁$畻浠婂ぉ鐨勫緟瀹屾垚鏁伴噺
+        List<QualityInspect> todayPendingList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .eq(QualityInspect::getInspectState, 0)
+                .eq(QualityInspect::getCheckTime, todayStr));
+
+        // 璁$畻鍓嶄竴澶╃殑寰呭畬鎴愭暟閲�
+        List<QualityInspect> prevPendingList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .eq(QualityInspect::getInspectState, 0)
+                .eq(QualityInspect::getCheckTime, prevDayStr));
+        // 璁$畻浠婂ぉ鐨勫緟瀹屾垚鏁伴噺
+        BigDecimal todayPendingCount = todayPendingList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        // 璁$畻鍓嶄竴澶╃殑寰呭畬鎴愭暟閲�
+        BigDecimal prevPendingCount = prevPendingList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 璁$畻浠婂ぉ鐨勫緟瀹屾垚鏁伴噺鐩稿鏄ㄥぉ鐨勪竴涓悓姣斿闀�
+        BigDecimal todayPendingCountGrowthRate = calcGrowthRate(todayPendingCount, prevPendingCount);
+        // 璁$畻浠婂ぉ鐨勫凡瀹屾垚鏁伴噺
+        List<QualityInspect> todayCompletedList = qualityInspectMapper
+                .selectList(new LambdaQueryWrapper<QualityInspect>()
+                        .eq(QualityInspect::getInspectState, 1)
+                        .eq(QualityInspect::getCheckTime, todayStr));
+        // 璁$畻鍓嶄竴澶╃殑宸插畬鎴愭暟閲�
+        List<QualityInspect> prevCompletedList = qualityInspectMapper
+                .selectList(new LambdaQueryWrapper<QualityInspect>()
+                        .eq(QualityInspect::getInspectState, 1)
+                        .eq(QualityInspect::getCheckTime, prevDayStr));
+        // 璁$畻浠婂ぉ鐨勫凡瀹屾垚鏁伴噺
+        BigDecimal todayCompletedCount = todayCompletedList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 璁$畻鍓嶄竴澶╃殑宸插畬鎴愭暟閲�
+        BigDecimal prevCompletedCount = prevCompletedList.stream()
+                .map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 璁$畻浠婂ぉ鐨勫凡瀹屾垚鏁伴噺鐩稿鏄ㄥぉ鐨勪竴涓悓姣斿闀�
+        BigDecimal todayCompletedCountGrowthRate = calcGrowthRate(todayCompletedCount, prevCompletedCount);
+        QualityInspectionCountDto dto = new QualityInspectionCountDto();
+        dto.setTotalCount(todayCount);
+        dto.setTotalCountGrowthRate(growthRate);
+        dto.setTodayPendingCount(todayPendingCount);
+        dto.setTodayPendingCountGrowthRate(todayPendingCountGrowthRate);
+        dto.setTodayCompletedCount(todayCompletedCount);
+        dto.setTodayCompletedCountGrowthRate(todayCompletedCountGrowthRate);
+        return dto;
+    }
+
+    private BigDecimal calcGrowthRate(BigDecimal today, BigDecimal prev) {
+        if (prev == null || prev.compareTo(BigDecimal.ZERO) == 0) {
+            return BigDecimal.ZERO.setScale(2);
+        }
+        return today.subtract(prev)
+                .divide(prev, 4, RoundingMode.HALF_UP)
+                .multiply(BigDecimal.valueOf(100))
+                .setScale(2, RoundingMode.HALF_UP);
+    }
+
+    @Override
+    public NonComplianceWarningDto nonComplianceWarning() {
+        String[] range = lastSevenDaysDateRange();
+        String startStr = range[0];
+        String endStr = range[1];
+
+        // 鏌ヨ杩戜竷澶╁凡澶勭悊涓嶅悎鏍兼暟鎹�
+        List<QualityUnqualified> list = qualityUnqualifiedMapper.selectList(
+                new LambdaQueryWrapper<QualityUnqualified>()
+                        .eq(QualityUnqualified::getInspectState, 1)
+                        .ge(QualityUnqualified::getCheckTime, startStr)
+                        .le(QualityUnqualified::getCheckTime, endStr));
+
+        NonComplianceWarningDto dto = new NonComplianceWarningDto();
+
+        if (CollectionUtils.isEmpty(list)) {
+            dto.setRawMaterialRatio(BigDecimal.ZERO);
+            dto.setSemiFinishedProductRatio(BigDecimal.ZERO);
+            dto.setFinishedProductRatio(BigDecimal.ZERO);
+            dto.setChildren(new ArrayList<>());
+            return dto;
+        }
+
+        // 鏌ヨ鎵�鏈変骇鍝�
+        List<Product> products = productMapper.selectList(null);
+
+        Map<Long, Product> productMap = products.stream()
+                .collect(Collectors.toMap(Product::getId, p -> p));
+
+        BigDecimal rawMaterialCount = BigDecimal.ZERO;
+        BigDecimal semiFinishedCount = BigDecimal.ZERO;
+        BigDecimal finishedCount = BigDecimal.ZERO;
+
+        List<NonComplianceWarningDto.Item> children = new ArrayList<>();
+
+        for (QualityUnqualified item : list) {
+
+            BigDecimal quantity = item.getQuantity();
+            Long productId = item.getProductId();
+
+            Product product = productMap.get(productId);
+            if (product == null) {
+                continue;
+            }
+
+            // 鎵惧埌浜у搧澶х被
+            Product parent = product.getParentId() == null
+                    ? product
+                    : productMap.get(product.getParentId());
+
+            if (parent == null) {
+                continue;
+            }
+
+            switch (parent.getProductName()) {
+                case "鍘熸潗鏂�":
+                    rawMaterialCount = rawMaterialCount.add(quantity);
+                    break;
+                case "鍗婃垚鍝�":
+                    semiFinishedCount = semiFinishedCount.add(quantity);
+                    break;
+                case "鎴愬搧":
+                    finishedCount = finishedCount.add(quantity);
+                    break;
+                default:
+                    break;
+            }
+
+            // 缁勮鏄庣粏
+            NonComplianceWarningDto.Item child = new NonComplianceWarningDto.Item();
+            // child.setProductTitle(item.getProductName());
+            child.setParentProductTitle(parent.getProductName());
+            child.setProductTitle(item.getProductName());
+            child.setDescription(item.getDefectivePhenomena());
+            child.setDate(formatDate(item.getCheckTime()));
+            children.add(child);
+        }
+
+        BigDecimal total = rawMaterialCount
+                .add(semiFinishedCount)
+                .add(finishedCount);
+
+        dto.setRawMaterialRatio(calcRate(rawMaterialCount, total));
+        dto.setSemiFinishedProductRatio(calcRate(semiFinishedCount, total));
+        dto.setFinishedProductRatio(calcRate(finishedCount, total));
+        dto.setChildren(children);
+
+        return dto;
+    }
+
+    private BigDecimal calcRate(BigDecimal part, BigDecimal total) {
+        if (total == null || total.compareTo(BigDecimal.ZERO) == 0) {
+            return BigDecimal.ZERO.setScale(2);
+        }
+        return part.divide(total, 4, RoundingMode.HALF_UP)
+                .multiply(BigDecimal.valueOf(100))
+                .setScale(2, RoundingMode.HALF_UP);
+    }
+
+    public static String formatDate(Date date) {
+        if (date == null) {
+            return null;
+        }
+        return date.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate()
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+    }
+
+    /**
+     * 鑾峰彇杩戜竷澶╃殑鏃ユ湡鍖洪棿锛堜粎鍚勾鏈堟棩锛�
+     */
+    public static String[] lastSevenDaysDateRange() {
+        LocalDate today = LocalDate.now();
+        return new String[]{today.minusDays(6).toString(), today.toString()};
+    }
+
+    @Override
+    public List<CompletedInspectionCountDto> completedInspectionCount() {
+        String[] range = lastSevenDaysDateRange();
+        String startStr = range[0];
+        String endStr = range[1];
+
+        // 鏌ヨ杩戜竷澶╁凡瀹屾垚鐨勬楠屾暟鎹�
+        List<QualityInspect> list = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .eq(QualityInspect::getInspectState, 1)
+                .ge(QualityInspect::getCheckTime, startStr)
+                .le(QualityInspect::getCheckTime, endStr));
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
+
+        Map<String, CompletedInspectionCountDto> resultMap = new LinkedHashMap<>();
+
+        for (int i = 6; i >= 0; i--) {
+            LocalDate date = LocalDate.now().minusDays(i);
+            String dateStr = date.format(formatter);
+
+            CompletedInspectionCountDto dto = new CompletedInspectionCountDto();
+            dto.setDateStr(dateStr);
+            dto.setQualifiedCount(BigDecimal.ZERO);
+            dto.setUnqualifiedCount(BigDecimal.ZERO);
+            dto.setPassRate(BigDecimal.ZERO);
+
+            resultMap.put(dateStr, dto);
+        }
+
+        // 绱姞鍚堟牸 / 涓嶅悎鏍兼暟閲�
+        for (QualityInspect item : list) {
+
+            String dateStr = item.getCheckTime()
+                    .toInstant()
+                    .atZone(ZoneId.systemDefault())
+                    .toLocalDate()
+                    .format(formatter);
+
+            CompletedInspectionCountDto dto = resultMap.get(dateStr);
+            if (dto == null) {
+                continue;
+            }
+
+            BigDecimal quantity = item.getQuantity();
+
+            if ("鍚堟牸".equals(item.getCheckResult())) {
+                dto.setQualifiedCount(dto.getQualifiedCount().add(quantity));
+            } else {
+                dto.setUnqualifiedCount(dto.getUnqualifiedCount().add(quantity));
+            }
+        }
+
+        // 璁$畻鍚堟牸鐜�
+        for (CompletedInspectionCountDto dto : resultMap.values()) {
+            BigDecimal total = dto.getQualifiedCount().add(dto.getUnqualifiedCount());
+            dto.setPassRate(calcRate(dto.getQualifiedCount(), total));
+        }
+
+        return new ArrayList<>(resultMap.values());
+    }
+
+    @Override
+    public List<UnqualifiedProductRankDto> unqualifiedProductRanking() {
+
+        List<QualityInspect> list = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
+                .eq(QualityInspect::getInspectState, 1));
+
+        if (CollectionUtils.isEmpty(list)) {
+            return new ArrayList<>();
+        }
+
+        Map<Long, String> productNameMap = productMapper.selectList(null)
+                .stream()
+                .collect(Collectors.toMap(Product::getId, Product::getProductName));
+
+        Map<Long, List<QualityInspect>> groupMap = list.stream()
+                .collect(Collectors.groupingBy(QualityInspect::getProductId));
+
+        List<UnqualifiedProductRankDto> resultList = new ArrayList<>();
+
+        for (Map.Entry<Long, List<QualityInspect>> entry : groupMap.entrySet()) {
+
+            Long productId = entry.getKey();
+            List<QualityInspect> items = entry.getValue();
+
+            BigDecimal totalCount = BigDecimal.ZERO;
+            BigDecimal qualifiedCount = BigDecimal.ZERO;
+            BigDecimal unqualifiedCount = BigDecimal.ZERO;
+
+            for (QualityInspect item : items) {
+                BigDecimal qty = item.getQuantity();
+                totalCount = totalCount.add(qty);
+
+                if ("鍚堟牸".equals(item.getCheckResult())) {
+                    qualifiedCount = qualifiedCount.add(qty);
+                } else {
+                    unqualifiedCount = unqualifiedCount.add(qty);
+                }
+            }
+
+            if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
+                continue;
+            }
+
+            BigDecimal passRate = qualifiedCount
+                    .divide(totalCount, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP);
+
+            UnqualifiedProductRankDto dto = new UnqualifiedProductRankDto();
+            dto.setProductName(productNameMap.get(productId));
+            dto.setTotalCount(totalCount);
+            dto.setCompletedCount(qualifiedCount);
+            dto.setPassRate(passRate);
+
+            resultList.add(dto);
+        }
+
+        resultList.sort(Comparator.comparing(UnqualifiedProductRankDto::getPassRate));
+
+        return resultList.stream().limit(5).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> unqualifiedProductProcessingAnalysis() {
+
+        List<QualityUnqualified> list = qualityUnqualifiedMapper.selectList(null);
+        if (CollectionUtils.isEmpty(list)) {
+            return new ArrayList<>();
+        }
+
+        // 缁熻姣忕澶勭悊缁撴灉鐨勬暟閲�
+        Map<String, BigDecimal> countMap = new HashMap<>();
+        for (QualityUnqualified item : list) {
+            if (StringUtils.isEmpty(item.getDealResult()) || item.getQuantity() == null) {
+                continue;
+            }
+            countMap.merge(item.getDealResult(), item.getQuantity(), BigDecimal::add);
+        }
+
+        if (countMap.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 璁$畻鎬绘暟
+        BigDecimal totalCount = countMap.values()
+                .stream()
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
+            return new ArrayList<>();
+        }
+
+        // 鎸夋暟閲忓�掑簭鎺掑簭
+        List<Map.Entry<String, BigDecimal>> sortedList = countMap.entrySet()
+                .stream()
+                .sorted((a, b) -> b.getValue().compareTo(a.getValue()))
+                .collect(Collectors.toList());
+
+        List<MapDto> result = new ArrayList<>();
+
+        int limit = Math.min(3, sortedList.size());
+        BigDecimal otherCount = BigDecimal.ZERO;
+
+        for (int i = 0; i < sortedList.size(); i++) {
+            Map.Entry<String, BigDecimal> entry = sortedList.get(i);
+
+            if (i < limit) {
+                MapDto dto = new MapDto();
+                dto.setName(entry.getKey());
+                dto.setValue(entry.getValue().setScale(2, RoundingMode.HALF_UP).toPlainString());
+                dto.setRate(calcRate(entry.getValue(), totalCount).toString());
+                result.add(dto);
+            } else {
+                otherCount = otherCount.add(entry.getValue());
+            }
+        }
+
+        if (otherCount.compareTo(BigDecimal.ZERO) > 0) {
+            MapDto otherDto = new MapDto();
+            otherDto.setName("鍏朵粬");
+            otherDto.setValue(otherCount.setScale(2, RoundingMode.HALF_UP).toPlainString());
+            otherDto.setRate(calcRate(otherCount, totalCount).toString());
+            result.add(otherDto);
+        }
+
+        return result;
+    }
+
+    @Override
+    public QualityStatisticsDto qualityInspectionStatistics(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+
+        switch (type) {
+            case 1: // 鏈懆
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 2: // 鏈湀
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            case 3: // 鏈搴�
+                int currentMonth = today.getMonthValue();
+                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
+                startDate = LocalDate.of(today.getYear(), startMonth, 1);
+                endDate = startDate.plusMonths(2).with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            default:
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+        }
+
+        List<QualityInspect> qualityInspectList = qualityInspectMapper
+                .selectList(new LambdaQueryWrapper<QualityInspect>()
+                        .ge(QualityInspect::getCheckTime, startDate.toString())
+                        .le(QualityInspect::getCheckTime, endDate.toString())
+                        .eq(QualityInspect::getInspectState, 1));
+
+        QualityStatisticsDto dto = new QualityStatisticsDto();
+        dto.setSupplierNum(sumQuantity(qualityInspectList, 0)); // 鍘熸潗鏂�
+        dto.setProcessNum(sumQuantity(qualityInspectList, 1)); // 杩囩▼
+        dto.setFactoryNum(sumQuantity(qualityInspectList, 2)); // 鍑哄巶
+
+        // 鍋囪 qualityInspectList 鏄竴涓� List<QualityInspect> 绫诲瀷鐨勯泦鍚�
+        Map<String, List<QualityInspect>> groupedByCheckResult = qualityInspectList.stream()
+                .collect(Collectors.groupingBy(QualityInspect::getCheckResult));
+        List<QualityInspect> qualityInspects = groupedByCheckResult.get("涓嶅悎鏍�");
+        if (ObjectUtils.isNull(qualityInspects) || qualityInspects.size() == 0) {
+            return null;
+        }
+        // 4. 澶勭悊鍥捐〃椤� (Item)
+        List<QualityStatisticsItem> itemList = new ArrayList<>();
+
+        Map<QualityInspect, LocalDate> dateMap = qualityInspectList.stream()
+                .collect(Collectors.toMap(
+                        i -> i,
+                        i -> i.getCheckTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()));
+        if (type == 3) {
+            // 瀛e害妯″紡锛氭寜鏈堝垎缁�
+            Map<String, List<QualityInspect>> groupByMonth = qualityInspects.stream()
+                    .collect(Collectors.groupingBy(i -> {
+                        LocalDate ld = dateMap.get(i);
+                        return ld.format(DateTimeFormatter.ofPattern("yyyy-MM"));
+                    }));
+
+            for (int i = 0; i < 3; i++) {
+                LocalDate monthDate = startDate.plusMonths(i);
+                String monthStr = monthDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
+                itemList.add(buildItem(monthStr, groupByMonth.getOrDefault(monthStr, new ArrayList<>())));
+            }
+        } else {
+            // 鍛ㄦ垨鏈堟ā寮忥細鎸夊ぉ鍒嗙粍
+            Map<String, List<QualityInspect>> groupByDay = qualityInspects.stream()
+                    .collect(Collectors.groupingBy(i -> {
+                        LocalDate ld = dateMap.get(i);
+                        return ld.format(DateTimeFormatter.ofPattern("MM/dd"));
+                    }));
+            long days = ChronoUnit.DAYS.between(startDate, endDate);
+            for (int i = 0; i <= days; i++) {
+                LocalDate tempDay = startDate.plusDays(i);
+                String dayStr = tempDay.format(DateTimeFormatter.ofPattern("MM/dd"));
+                itemList.add(buildItem(dayStr, groupByDay.getOrDefault(dayStr, new ArrayList<>())));
+            }
+        }
+
+        dto.setItem(itemList);
+        return dto;
+    }
+
+    private BigDecimal sumQuantity(List<QualityInspect> list, Integer type) {
+        return list.stream()
+                .filter(i -> i.getInspectType().equals(type))
+                .map(QualityInspect::getQuantity)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private QualityStatisticsItem buildItem(String dateLabel, List<QualityInspect> list) {
+        QualityStatisticsItem item = new QualityStatisticsItem();
+        item.setDate(dateLabel);
+
+        item.setSupplierNum(list.stream().filter(i -> i.getInspectType() == 0).map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add));
+        item.setProcessNum(list.stream().filter(i -> i.getInspectType() == 1).map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add));
+        item.setFactoryNum(list.stream().filter(i -> i.getInspectType() == 2).map(QualityInspect::getQuantity)
+                .reduce(BigDecimal.ZERO, BigDecimal::add));
+
+        return item;
+    }
+
+    @Override
+    public List<processDataProductionStatisticsDto> processDataProductionStatistics(Integer type,
+                                                                                    List<Long> processIds) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Long userId = SecurityUtils.isAdmin(loginUser.getUserId()) ? null : loginUser.getUserId();
+
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+
+        switch (type) {
+            case 1:
+                startDate = today;
+                endDate = today;
+                break;
+            case 2:
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 3:
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            default:
+                startDate = today;
+                endDate = today;
+        }
+
+        LocalDateTime startDateTime = startDate.atStartOfDay();
+        LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX);
+
+        return productProcessMapper.calculateProductionStatistics(startDateTime, endDateTime, userId, processIds);
+    }
+
+    @Override
+    public Map<String, Long> total() {
+        Map<String, Long> map = new HashMap<>();
+        List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(null);
+        //鎬婚攢鍞噾棰�
+        BigDecimal sum = salesDeliveries.stream()
+                .map(item -> item.getPrice() != null ? new BigDecimal(item.getPrice().toString()) : BigDecimal.ZERO)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        long finalPrice = sum.divide(new BigDecimal("10000"), 0, RoundingMode.HALF_UP).longValue();
+        map.put("price", finalPrice);//鍗曚綅w
+        //鎬诲彂璐у崟
+        map.put("delivery", (long) salesDeliveries.size());
+        //鎬婚攢鍞柟鏁�
+        BigDecimal volume = salesDeliveries.stream()
+                .map(item -> item.getVolume() != null ? new BigDecimal(item.getVolume().toString()) : BigDecimal.ZERO)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        long finalVolume = volume.divide(new BigDecimal("1000"), 0, RoundingMode.HALF_UP).longValue();
+        map.put("volume", finalVolume);
+        //绱瀹㈡埛
+        Long count = customerMapper.selectCount(null);
+        map.put("customer", count);
+        return map;
+    }
+
+    @Override
+    public SalesTotalDto salesAnalysis(SalesDeliveryDto salesDeliveryDto) {
+        SalesTotalDto salesTotalDto = new SalesTotalDto();
+        List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
+        List<Map<String, Long>> maps = new ArrayList<>();
+        List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(Wrappers.<SalesDelivery>lambdaQuery()
+                .eq(SalesDelivery::getProductName, salesDeliveryDto.getType()));
+        for (LocalDate date : dates) {
+            LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
+            LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
+            if (salesDeliveryDto.getDays().equals("骞�")) {
+                lastDay = date.with(TemporalAdjusters.lastDayOfYear());
+            }
+            LocalDate finalLastDay = lastDay;
+            salesDeliveries = salesDeliveries
+                    .stream()
+                    .filter(delivery -> {
+                        LocalDate deliveryDate = delivery.getDeliveryDate();
+                        return !deliveryDate.isBefore(firstDay) && !deliveryDate.isAfter(finalLastDay);
+                    })
+                    .collect(Collectors.toList());
+            Map<String, Long> regionCountMap = Arrays.stream(AddressRegionEnum.values())
+                    .collect(Collectors.toMap(
+                            AddressRegionEnum::getRegionName, // 鍖哄煙鍚嶄綔涓簁ey
+                            enumItem -> 0L                    // 鍒濆鍊煎叏閮ㄤ负0
+                    ));
+            if (!CollectionUtils.isEmpty(salesDeliveries)) {
+                // 鎸夊尯鍩熷垎缁勶紝缁熻姣忎釜鍖哄煙鐨勯攢閲忔�诲拰
+                regionCountMap = salesDeliveries.stream()
+                        .filter(delivery -> delivery.getDeliveryPlace() != null)
+                        .collect(Collectors.groupingBy(
+                                delivery -> {
+                                    AddressRegionEnum regionEnum = AddressRegionEnum.matchRegion(delivery.getDeliveryPlace());
+                                    return regionEnum != null ? regionEnum.getRegionName() : null;
+                                },
+                                Collectors.summingLong(delivery -> delivery.getVolume() != null ? delivery.getVolume().longValue() : 0L)
+                        ));
+            }
+            regionCountMap.put("鍏ㄩ儴", salesDeliveries.stream()
+                    .mapToLong(item -> item.getVolume() != null ? item.getVolume().longValue() : 0L)
+                    .sum());
+            maps.add(regionCountMap);
+        }
+        salesTotalDto.setDates(dates);
+        salesTotalDto.setCustomerTrends(maps);
+        return salesTotalDto;
+    }
+
+    @Override
+    public List<SalesTotalDetailDto> salesRanking(SalesDeliveryDto salesDeliveryDto) {
+        List<SalesTotalDetailDto> salesTotalDetailDtos = new ArrayList<>();
+        List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
+        List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(Wrappers.<SalesDelivery>lambdaQuery()
+                .eq(SalesDelivery::getProductName, salesDeliveryDto.getType()));
+        for (LocalDate date : dates) {
+            LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
+            LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
+            if (salesDeliveryDto.getDays().equals("骞�")) {
+                lastDay = date.with(TemporalAdjusters.lastDayOfYear());
+            }
+            LocalDate finalLastDay = lastDay;
+            salesDeliveries = salesDeliveries
+                    .stream()
+                    .filter(delivery -> {
+                        LocalDate deliveryDate = delivery.getDeliveryDate();
+                        return !deliveryDate.isBefore(firstDay) && !deliveryDate.isAfter(finalLastDay);
+                    })
+                    .collect(Collectors.toList());
+            Map<String, Long> regionCountMap = Arrays.stream(AddressRegionEnum.values())
+                    .collect(Collectors.toMap(
+                            AddressRegionEnum::getRegionName, // 鍖哄煙鍚嶄綔涓簁ey
+                            enumItem -> 0L                    // 鍒濆鍊煎叏閮ㄤ负0
+                    ));
+            if (!CollectionUtils.isEmpty(salesDeliveries)) {
+                // 鎸夊尯鍩熷垎缁勶紝缁熻姣忎釜鍖哄煙鐨勯攢閲忔�诲拰
+                regionCountMap = salesDeliveries.stream()
+                        .filter(delivery -> delivery.getDeliveryPlace() != null)
+                        .collect(Collectors.groupingBy(
+                                delivery -> {
+                                    AddressRegionEnum regionEnum = AddressRegionEnum.matchRegion(delivery.getDeliveryPlace());
+                                    return regionEnum != null ? regionEnum.getRegionName() : null;
+                                },
+                                Collectors.summingLong(delivery -> delivery.getVolume() != null ? delivery.getVolume().longValue() : 0L)
+                        ));
+            }
+            regionCountMap.put("鍏ㄩ儴", salesDeliveries.stream()
+                    .mapToLong(item -> item.getVolume() != null ? item.getVolume().longValue() : 0L)
+                    .sum());
+            SalesTotalDetailDto salesTotalDetailDto = new SalesTotalDetailDto();
+            salesTotalDetailDto.setDate(date);
+            salesTotalDetailDto.setType(salesDeliveryDto.getType());
+            salesTotalDetailDtos.add(salesTotalDetailDto);
+        }
+        return salesTotalDetailDtos;
+    }
+
+    @Override
+    public SalesTotalDto salesAmount(SalesDeliveryDto salesDeliveryDto) {
+        SalesTotalDto salesTotalDto = new SalesTotalDto();
+        List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
+        List<Map<String, Long>> maps = new ArrayList<>();
+        List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(Wrappers.<SalesDelivery>lambdaQuery()
+                .eq(SalesDelivery::getProductName, salesDeliveryDto.getType()));
+        for (LocalDate date : dates) {
+            LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
+            LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
+            if (salesDeliveryDto.getDays().equals("骞�")) {
+                lastDay = date.with(TemporalAdjusters.lastDayOfYear());
+            }
+            LocalDate finalLastDay = lastDay;
+            salesDeliveries = salesDeliveries
+                    .stream()
+                    .filter(delivery -> {
+                        LocalDate deliveryDate = delivery.getDeliveryDate();
+                        return !deliveryDate.isBefore(firstDay) && !deliveryDate.isAfter(finalLastDay);
+                    })
+                    .collect(Collectors.toList());
+            Map<String, Long> regionCountMap = Arrays.stream(AddressRegionEnum.values())
+                    .collect(Collectors.toMap(
+                            AddressRegionEnum::getRegionName, // 鍖哄煙鍚嶄綔涓簁ey
+                            enumItem -> 0L                    // 鍒濆鍊煎叏閮ㄤ负0
+                    ));
+            if (!CollectionUtils.isEmpty(salesDeliveries)) {
+                // 鎸夊尯鍩熷垎缁勶紝缁熻姣忎釜鍖哄煙鐨勯攢閲忔�诲拰
+                regionCountMap = salesDeliveries.stream()
+                        .filter(delivery -> delivery.getDeliveryPlace() != null)
+                        .collect(Collectors.groupingBy(
+                                delivery -> {
+                                    AddressRegionEnum regionEnum = AddressRegionEnum.matchRegion(delivery.getDeliveryPlace());
+                                    return regionEnum != null ? regionEnum.getRegionName() : null;
+                                },
+                                Collectors.summingLong(delivery -> delivery.getPrice() != null ? delivery.getPrice().longValue() : 0L)
+                        ));
+            }
+            regionCountMap.put("鍏ㄩ儴", salesDeliveries.stream()
+                    .mapToLong(item -> item.getPrice() != null ? item.getPrice().longValue() : 0L)
+                    .sum());
+            maps.add(regionCountMap);
+        }
+        salesTotalDto.setDates(dates);
+        salesTotalDto.setCustomerTrends(maps);
+        return salesTotalDto;
+    }
+
+    @Override
+    public List<SalesTotalDetailDto> salesDataRanking(SalesDeliveryDto salesDeliveryDto) {
+        List<SalesTotalDetailDto> salesTotalDetailDtos = new ArrayList<>();
+        List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
+
+        return salesTotalDetailDtos;
+    }
+
+    @Override
+    public SalesTotalDto customerTrends(SalesDeliveryDto salesDeliveryDto) {
+        SalesTotalDto salesTotalDto = new SalesTotalDto();
+        List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
+        List<Map<String, Long>> maps = new ArrayList<>();
+        for (LocalDate date : dates) {
+            LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
+            LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
+            if (salesDeliveryDto.getDays().equals("骞�")) {
+                lastDay = date.with(TemporalAdjusters.lastDayOfYear());
+            }
+            Date startDate = Date.from(firstDay.atStartOfDay(ZoneId.systemDefault()).toInstant());
+            Date endDate = Date.from(lastDay.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());
+            List<Customer> customers = customerMapper.selectList(Wrappers.<Customer>lambdaQuery()
+                    .between(Customer::getMaintenanceTime, startDate, endDate));
+            Map<String, Long> regionCountMap = Arrays.stream(AddressRegionEnum.values())
+                    .collect(Collectors.toMap(
+                            AddressRegionEnum::getRegionName, // 鍖哄煙鍚嶄綔涓簁ey
+                            enumItem -> 0L                    // 鍒濆鍊煎叏閮ㄤ负0
+                    ));
+            if (!CollectionUtils.isEmpty(customers)) {
+                regionCountMap = customers.stream()
+                        // 璋冪敤鏂规硶灏嗗師濮嬪湴鍧�杞崲涓虹洰鏍囧尯鍩�
+                        .map(customer -> AddressRegionEnum.matchRegion(customer.getCompanyAddress()).getRegionName())
+                        // 杩囨护鎺夎浆鎹㈠け璐�/绌虹殑鍖哄煙锛堝彲閫夛紝鏍规嵁涓氬姟闇�姹傦級
+                        .filter(region -> region != null && !region.isEmpty())
+                        // 鎸夊尯鍩熷垎缁勶紝缁熻姣忎釜鍖哄煙鐨勬暟閲�
+                        .collect(Collectors.groupingBy(
+                                region -> region,       // 鍒嗙粍渚濇嵁锛氳浆鎹㈠悗鐨勫尯鍩�
+                                Collectors.counting()   // 璁℃暟
+                        ));
+            }
+            regionCountMap.put("鍏ㄩ儴", customers.stream().count());
+            maps.add(regionCountMap);
+        }
+        salesTotalDto.setDates(dates);
+        salesTotalDto.setCustomerTrends(maps);
+        return salesTotalDto;
+    }
+
+    /**
+     * 鏍规嵁鍓嶇浼犲弬 骞�/鏈� 杞崲涓哄搴擫ocalDate鍒楄〃
+     *
+     * @param days 鍓嶇鍙傛暟锛�"骞�" / "鏈�"
+     * @return List<LocalDate>
+     */
+    public static List<LocalDate> convertDateList(String days) {
+        List<LocalDate> resultList = new ArrayList<>();
+        LocalDate now = LocalDate.now();
+        int currentYear = now.getYear();
+
+        if ("骞�".equals(days)) {
+            // 闇�姹傦細杩�5骞� 鈫� 姣忓勾鐨� 1鏈�1鏃�
+            for (int i = 0; i < 5; i++) {
+                resultList.add(LocalDate.of(currentYear - i, 1, 1));
+            }
+        } else if ("鏈�".equals(days)) {
+            // 闇�姹傦細褰撳勾12涓湀 鈫� 姣忔湀1鏃�
+            for (int month = 1; month <= 12; month++) {
+                resultList.add(LocalDate.of(currentYear, month, 1));
+            }
+        }
+        return resultList;
+    }
+
+    @Autowired
+    private ProductionProductMainService productionProductMainService;
+
+    @Autowired
+    private ProductOrderService productOrderService;
+
+    @Autowired
+    private ProductionProductInputService productionProductInputService;
+
+    @Autowired
+    private ProductionProductOutputService productionProductOutputService;
+
+    @Autowired
+    private IProductionOrderRouteService productionOrderRouteService;
+
+    @Autowired
+    private SysDictDataMapper sysDictDataMapper;
+
+    @Override
+    public Map<String, List<MaterialProductionAnalysisDto>> materialProductionAnalysis(productionStatisticsDto dto) {
+        if (dto == null) {
+            dto = new productionStatisticsDto();
+            dto.setMaterialName("鍏ㄩ儴");
+            dto.setDateType(1);
+        }
+
+        LocalDate now = LocalDate.now();
+        LocalDate startDate = null;
+
+        if (dto.getDateType() == 1) { // 鏈湀鑷充粖
+            startDate = now.withDayOfMonth(1);
+        } else { // 鏈勾鑷充粖
+            startDate = now.withDayOfYear(1);
+        }
+
+        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type");
+        Set<Long> blockDictCodes = sysDictDataList.stream()
+                .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鐮屽潡"))
+                .map(SysDictData::getDictCode)
+                .collect(Collectors.toSet());
+        Set<Long> plateDictCodes = sysDictDataList.stream()
+                .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鏉挎潗"))
+                .map(SysDictData::getDictCode)
+                .collect(Collectors.toSet());
+
+        //  鏌ヨ鎶ュ伐涓昏〃鏁版嵁
+        List<ProductionProductMain> productionProductMainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .between(ProductionProductMain::getReportingTime,
+                        startDate.atStartOfDay(),
+                        now.atTime(LocalTime.MAX)));
+
+        if (CollectionUtils.isEmpty(productionProductMainList)) {
+            return Collections.emptyMap();
+        }
+
+        List<Long> productionMainIds = productionProductMainList.stream()
+                .map(ProductionProductMain::getId)
+                .collect(Collectors.toList());
+
+        //  鑾峰彇浜у嚭鏄庣粏
+        List<ProductionProductOutput> productionProductOutputs = productionProductOutputService.list(new LambdaQueryWrapper<ProductionProductOutput>()
+                .in(ProductionProductOutput::getProductMainId, productionMainIds));
+
+        Map<Long, ProductionProductMain> mainMap = productionProductMainList.stream()
+                .collect(Collectors.toMap(ProductionProductMain::getId, m -> m));
+
+        //  鑾峰彇璁㈠崟宸ヨ壓
+        List<Long> productionOrderIds = productionProductMainList.stream()
+                .map(ProductionProductMain::getProductOrderId)
+                .distinct()
+                .collect(Collectors.toList());
+
+        Map<Long, Long> orderRouteMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(productionOrderIds)) {
+            List<ProductionOrderRoute> routes = productionOrderRouteService.list(Wrappers.<ProductionOrderRoute>lambdaQuery()
+                    .in(ProductionOrderRoute::getOrderId, productionOrderIds));
+            orderRouteMap = routes.stream()
+                    .filter(r -> r.getOrderId() != null && r.getDictCode() != null)
+                    .collect(Collectors.toMap(ProductionOrderRoute::getOrderId, ProductionOrderRoute::getDictCode, (v1, v2) -> v1));
+        }
+
+        List<String> dateLabels = new ArrayList<>();
+        DateTimeFormatter formatter;
+        if (dto.getDateType() == 1) { // 鏈湀鎸夊ぉ
+            formatter = DateTimeFormatter.ofPattern("MM-dd");
+            long days = ChronoUnit.DAYS.between(startDate, now) + 1;
+            for (int i = 0; i < days; i++) {
+                dateLabels.add(startDate.plusDays(i).format(formatter));
+            }
+        } else { // 鏈勾鎸夋湀
+            formatter = DateTimeFormatter.ofPattern("yy-M");
+            int currentMonth = now.getMonthValue();
+            for (int i = 1; i <= currentMonth; i++) {
+                dateLabels.add(startDate.withMonth(i).format(formatter));
+            }
+        }
+
+        Map<String, Map<String, BigDecimal>> aggregateData = new HashMap<>();
+        String[] categories = {"鍏ㄩ儴", "鐮屽潡", "鏉挎潗"};
+        for (String cat : categories) {
+            Map<String, BigDecimal> dayMap = new LinkedHashMap<>();
+            for (String label : dateLabels) {
+                dayMap.put(label, BigDecimal.ZERO);
+            }
+            aggregateData.put(cat, dayMap);
+        }
+
+        //  绱姞鏁版嵁
+        for (ProductionProductOutput output : productionProductOutputs) {
+            ProductionProductMain main = mainMap.get(output.getProductMainId());
+            if (main == null || main.getReportingTime() == null) continue;
+            String label = main.getReportingTime().format(formatter);
+            if (!aggregateData.get("鍏ㄩ儴").containsKey(label)) continue;
+            BigDecimal qty = output.getQuantity() != null ? output.getQuantity() : BigDecimal.ZERO;
+            // 绱姞鎬讳骇鍑�
+            aggregateData.get("鍏ㄩ儴").merge(label, qty, BigDecimal::add);
+            // 鍒嗙被绱姞
+            Long dictCode = orderRouteMap.get(main.getProductOrderId());
+            if (dictCode != null) {
+                if (blockDictCodes.contains(dictCode)) {
+                    aggregateData.get("鐮屽潡").merge(label, qty, BigDecimal::add);
+                } else if (plateDictCodes.contains(dictCode)) {
+                    aggregateData.get("鏉挎潗").merge(label, qty, BigDecimal::add);
+                }
+            }
+        }
+
+        Map<String, List<MaterialProductionAnalysisDto>> result = new LinkedHashMap<>();
+        for (String category : categories) {
+            List<MaterialProductionAnalysisDto> dtoList = new ArrayList<>();
+            for (String label : dateLabels) {
+                MaterialProductionAnalysisDto item = new MaterialProductionAnalysisDto();
+                item.setDateStr(label);
+                item.setTotalOutput(BigDecimal.ZERO);
+                item.setBlockOutput(BigDecimal.ZERO);
+                item.setPlateOutput(BigDecimal.ZERO);
+                BigDecimal val = aggregateData.get(category).get(label);
+                switch (category) {
+                    case "鍏ㄩ儴":
+                        item.setTotalOutput(val);
+                        break;
+                    case "鐮屽潡":
+                        item.setBlockOutput(val);
+                        break;
+                    case "鏉挎潗":
+                        item.setPlateOutput(val);
+                        break;
+                }
+                dtoList.add(item);
+            }
+            result.put(category, dtoList);
+        }
+
+        return result;
+    }
+
+    @Autowired
+    private IProductionOrderStructureService productionOrderStructureService;
+
+    @Autowired
+    private ProductMaterialService productMaterialService;
+
+    @Autowired
+    private ProductMaterialSkuService productMaterialSkuService;
+
+    @Override
+    public List<String> getMaterials(Integer materialType) {
+        // 鑾峰彇浜у搧绫诲瀷瀛楀吀
+        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type");
+        Set<Long> targetDictCodes;
+        if (materialType != null && materialType == 1) { // 鐮屽潡
+            targetDictCodes = sysDictDataList.stream()
+                    .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鐮屽潡"))
+                    .map(SysDictData::getDictCode)
+                    .collect(Collectors.toSet());
+        } else if (materialType != null && materialType == 2) { // 鏉挎潗
+            targetDictCodes = sysDictDataList.stream()
+                    .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鏉挎潗"))
+                    .map(SysDictData::getDictCode)
+                    .collect(Collectors.toSet());
+        } else {
+            return Collections.emptyList();
+        }
+
+        if (CollectionUtils.isEmpty(targetDictCodes)) {
+            return Collections.emptyList();
+        }
+
+        //  鏍规嵁浜у搧绫诲瀷瀛楀吀缂栫爜鎵惧埌瀵瑰簲鐨勭敓浜ц鍗旾D
+        List<ProductionOrderRoute> routes = productionOrderRouteService.list(Wrappers.<ProductionOrderRoute>lambdaQuery()
+                .in(ProductionOrderRoute::getDictCode, targetDictCodes));
+        List<Long> orderIds = routes.stream().map(ProductionOrderRoute::getOrderId).distinct().collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(orderIds)) {
+            return Collections.emptyList();
+        }
+
+        //  鏍规嵁鐢熶骇璁㈠崟ID鏌ヨBOM缁撴瀯涓殑鐗╂枡SKU ID
+        List<ProductionOrderStructure> structures = productionOrderStructureService.list(Wrappers.<ProductionOrderStructure>lambdaQuery()
+                .in(ProductionOrderStructure::getOrderId, orderIds));
+        List<Long> skuIds = structures.stream()
+                .map(ProductionOrderStructure::getProductModelId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(skuIds)) {
+            return Collections.emptyList();
+        }
+
+        //  鏍规嵁SKU ID鑾峰彇鐗╂枡琛↖D
+        List<ProductMaterialSku> skus = productMaterialSkuService.list(Wrappers.<ProductMaterialSku>lambdaQuery()
+                .in(ProductMaterialSku::getId, skuIds));
+        List<Long> productIds = skus.stream()
+                .map(ProductMaterialSku::getProductId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(productIds)) {
+            return Collections.emptyList();
+        }
+
+        //  鏍规嵁鐗╂枡涓昏〃ID鑾峰彇鐗╂枡鍚嶇О
+        List<ProductMaterial> materials = productMaterialService.list(Wrappers.<ProductMaterial>lambdaQuery()
+                .in(ProductMaterial::getId, productIds));
+
+        return materials.stream()
+                .map(ProductMaterial::getProductName)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<ProductionCostAnalysisDto> productionCostAnalysis(productionStatisticsDto dto, Integer materialType) {
+        //  鑾峰彇鍔ㄦ�佸瓧鍏窱D
+        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type");
+        Set<Long> targetDictCodes = sysDictDataList.stream()
+                .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains(materialType == 1 ? "鐮屽潡" : "鏉挎潗"))
+                .map(SysDictData::getDictCode)
+                .collect(Collectors.toSet());
+
+        if (CollectionUtils.isEmpty(targetDictCodes)) return Collections.emptyList();
+
+        //  鑾峰彇璇ョ被鍨嬬殑璁㈠崟
+        List<ProductionOrderRoute> routes = productionOrderRouteService.list(Wrappers.<ProductionOrderRoute>lambdaQuery()
+                .in(ProductionOrderRoute::getDictCode, targetDictCodes));
+        List<Long> orderIds = routes.stream().map(ProductionOrderRoute::getOrderId).distinct().collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(orderIds)) return Collections.emptyList();
+
+        //  鏃堕棿鑼冨洿澶勭悊
+        LocalDate now = LocalDate.now();
+        LocalDateTime yearStart = now.withDayOfYear(1).atStartOfDay();
+        LocalDateTime monthStart = now.withDayOfMonth(1).atStartOfDay();
+
+        LocalDateTime trendStart;
+        DateTimeFormatter trendFormatter;
+        int points;
+        if (dto.getDateType() != null && dto.getDateType() == 1) {
+            trendStart = monthStart;
+            trendFormatter = DateTimeFormatter.ofPattern("MM-dd");
+            points = now.getDayOfMonth();
+        } else {
+            trendStart = yearStart;
+            trendFormatter = DateTimeFormatter.ofPattern("yy-MM");
+            points = now.getMonthValue();
+        }
+
+        //  鏌ヨ鎶ュ伐鏁版嵁
+        List<ProductionProductMain> mainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .in(ProductionProductMain::getProductOrderId, orderIds)
+                .ge(ProductionProductMain::getReportingTime, yearStart));
+        if (CollectionUtils.isEmpty(mainList)) return Collections.emptyList();
+
+        List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+        Map<Long, ProductionProductMain> mainMap = mainList.stream().collect(Collectors.toMap(ProductionProductMain::getId, m -> m));
+
+        //  鏌ヨ鎶曞叆鍜屼骇鍑�
+        List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery()
+                .in(ProductionProductInput::getProductMainId, mainIds));
+        List<ProductionProductOutput> allOutputs = productionProductOutputService.list(Wrappers.<ProductionProductOutput>lambdaQuery()
+                .in(ProductionProductOutput::getProductMainId, mainIds));
+
+        // 鑾峰彇鎶曞叆鐗╂枡鐨勫熀纭�淇℃伅
+        Set<Long> inputSkuIds = allInputs.stream().map(ProductionProductInput::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Set<Long> outputSkuIds = allOutputs.stream().map(ProductionProductOutput::getProductModelId).filter(Objects::nonNull).collect(Collectors.toSet());
+
+        Map<Long, ProductMaterial> inputSkuToMatMap = new HashMap<>();
+        if (!inputSkuIds.isEmpty()) {
+            List<ProductMaterialSku> inputSkus = productMaterialSkuService.listByIds(inputSkuIds);
+            Set<Long> inputActualProdIds = inputSkus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, ProductMaterial> materialMap = productMaterialService.listByIds(inputActualProdIds).stream()
+                    .collect(Collectors.toMap(ProductMaterial::getId, m -> m));
+            inputSkuToMatMap = inputSkus.stream().collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialMap.get(s.getProductId())));
+        }
+
+        Map<Long, String> outputSkuToUnitMap = new HashMap<>();
+        if (!outputSkuIds.isEmpty()) {
+            List<ProductMaterialSku> outSkus = productMaterialSkuService.listByIds(outputSkuIds);
+            Set<Long> outProdIds = outSkus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> matUnitMap = productMaterialService.listByIds(outProdIds).stream().collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getUnit));
+            outputSkuToUnitMap = outSkus.stream().collect(Collectors.toMap(ProductMaterialSku::getId, s -> matUnitMap.getOrDefault(s.getProductId(), "鍚�")));
+        }
+
+        //  鎸夌墿鏂欏垎缁勮仛鍚�
+        Map<Long, ProductMaterial> finalSkuToMatMap = inputSkuToMatMap;
+        Map<String, List<ProductionProductInput>> inputByMaterial = allInputs.stream()
+                .filter(i -> finalSkuToMatMap.containsKey(i.getProductId()) && finalSkuToMatMap.get(i.getProductId()) != null)
+                .collect(Collectors.groupingBy(i -> finalSkuToMatMap.get(i.getProductId()).getProductName()));
+
+        if (dto.getMaterialName() != null && !"鍏ㄩ儴".equals(dto.getMaterialName())) {
+            String target = dto.getMaterialName();
+            inputByMaterial = inputByMaterial.entrySet().stream()
+                    .filter(e -> e.getKey().equals(target))
+                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+        }
+
+        List<ProductionCostAnalysisDto> resultList = new ArrayList<>();
+
+        for (Map.Entry<String, List<ProductionProductInput>> entry : inputByMaterial.entrySet()) {
+            String materialName = entry.getKey();
+            List<ProductionProductInput> materialInputs = entry.getValue();
+
+            ProductionCostAnalysisDto analysisDto = new ProductionCostAnalysisDto();
+            analysisDto.setMaterialName(materialName);
+
+            Set<Long> relatedMainIds = materialInputs.stream().map(ProductionProductInput::getProductMainId).collect(Collectors.toSet());
+            List<ProductionProductOutput> relatedOutputs = allOutputs.stream()
+                    .filter(o -> relatedMainIds.contains(o.getProductMainId()))
+                    .collect(Collectors.toList());
+
+            BigDecimal monthInputSum = BigDecimal.ZERO;
+            BigDecimal monthOutputSum = BigDecimal.ZERO;
+            BigDecimal yearInputSum = BigDecimal.ZERO;
+            BigDecimal yearOutputSum = BigDecimal.ZERO;
+
+            Map<String, InputOutputAnalysisDto> trendMap = new LinkedHashMap<>();
+            for (int i = 0; i < points; i++) {
+                String label;
+                if (dto.getDateType() != null && dto.getDateType() == 1) {
+                    label = now.withDayOfMonth(i + 1).format(trendFormatter);
+                } else {
+                    label = now.withMonth(i + 1).format(trendFormatter);
+                }
+                InputOutputAnalysisDto trendDto = new InputOutputAnalysisDto();
+                trendDto.setDate(label);
+                trendDto.setInputSum(BigDecimal.ZERO);
+                trendDto.setOutputSum(BigDecimal.ZERO);
+                trendMap.put(label, trendDto);
+            }
+
+            for (ProductionProductInput input : materialInputs) {
+                BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit());
+                yearInputSum = yearInputSum.add(qty);
+                LocalDateTime repTime = mainMap.get(input.getProductMainId()).getReportingTime();
+                if (repTime != null && (repTime.isAfter(monthStart) || repTime.isEqual(monthStart))) {
+                    monthInputSum = monthInputSum.add(qty);
+                }
+                if (repTime != null && (repTime.isAfter(trendStart) || repTime.isEqual(trendStart))) {
+                    String label = repTime.format(trendFormatter);
+                    if (trendMap.containsKey(label)) {
+                        trendMap.get(label).setInputSum(trendMap.get(label).getInputSum().add(qty));
+                    }
+                }
+            }
+
+            for (ProductionProductOutput output : relatedOutputs) {
+                String unit = outputSkuToUnitMap.getOrDefault(output.getProductModelId(), "鍚�");
+                BigDecimal qty = UnitUtils.convertValueToTon(output.getQuantity(), unit);
+                yearOutputSum = yearOutputSum.add(qty);
+                LocalDateTime repTime = mainMap.get(output.getProductMainId()).getReportingTime();
+                if (repTime != null && (repTime.isAfter(monthStart) || repTime.isEqual(monthStart))) {
+                    monthOutputSum = monthOutputSum.add(qty);
+                }
+                if (repTime != null && (repTime.isAfter(trendStart) || repTime.isEqual(trendStart))) {
+                    String label = repTime.format(trendFormatter);
+                    if (trendMap.containsKey(label)) {
+                        trendMap.get(label).setOutputSum(trendMap.get(label).getOutputSum().add(qty));
+                    }
+                }
+            }
+
+            analysisDto.setMonthlyConsumption(formatRatio(monthInputSum, monthOutputSum));
+            analysisDto.setYearlyConsumption(formatRatio(yearInputSum, yearOutputSum));
+            analysisDto.setChartData(new ArrayList<>(trendMap.values()));
+            resultList.add(analysisDto);
+        }
+
+        return resultList;
+    }
+
+    private String formatRatio(BigDecimal input, BigDecimal output) {
+        String inputStr = input.setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
+        String outputStr = output.setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
+        return inputStr + "/" + outputStr;
+    }
+
+    @Override
+    public ProductionStatisticsMiddleDto middle() {
+        LocalDate now = LocalDate.now();
+        LocalDate monthStart = now.withDayOfMonth(1);
+        LocalDate yearStart = now.withDayOfYear(1);
+
+        // 鑾峰彇浜у搧绫诲瀷瀛楀吀
+        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type");
+        Set<Long> blockDictCodes = sysDictDataList.stream()
+                .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鐮屽潡"))
+                .map(SysDictData::getDictCode)
+                .collect(Collectors.toSet());
+        Set<Long> plateDictCodes = sysDictDataList.stream()
+                .filter(d -> d.getDictLabel() != null && d.getDictLabel().contains("鏉挎潗"))
+                .map(SysDictData::getDictCode)
+                .collect(Collectors.toSet());
+
+        // 鏌ヨ鏈勾鎶ュ伐涓昏〃
+        List<ProductionProductMain> yearMainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .ge(ProductionProductMain::getReportingTime, yearStart.atStartOfDay()));
+        if (CollectionUtils.isEmpty(yearMainList)) return new ProductionStatisticsMiddleDto();
+
+        List<Long> mainIds = yearMainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+        Map<Long, ProductionProductMain> mainMap = yearMainList.stream().collect(Collectors.toMap(ProductionProductMain::getId, m -> m));
+
+        // 鑾峰彇璁㈠崟淇℃伅
+        List<Long> orderIds = yearMainList.stream().map(ProductionProductMain::getProductOrderId).distinct().collect(Collectors.toList());
+        Map<Long, Long> orderRouteMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(orderIds)) {
+            List<ProductionOrderRoute> routes = productionOrderRouteService.list(Wrappers.<ProductionOrderRoute>lambdaQuery()
+                    .in(ProductionOrderRoute::getOrderId, orderIds));
+            orderRouteMap = routes.stream()
+                    .filter(r -> r.getOrderId() != null && r.getDictCode() != null)
+                    .collect(Collectors.toMap(ProductionOrderRoute::getOrderId, ProductionOrderRoute::getDictCode, (v1, v2) -> v1));
+        }
+
+        // 鑾峰彇浜у嚭鏄庣粏锛堢敤浜庣爩鍧椼�佹澘鏉愪骇閲忥級
+        List<ProductionProductOutput> allOutputs = productionProductOutputService.list(Wrappers.<ProductionProductOutput>lambdaQuery()
+                .in(ProductionProductOutput::getProductMainId, mainIds));
+
+        // 鑾峰彇鎶曞叆鏄庣粏锛堢敤浜庣矇鐓ょ伆銆佺煶鑶忓鐞嗛噺锛�
+        List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery()
+                .in(ProductionProductInput::getProductMainId, mainIds));
+
+        // 缁熶竴澶勭悊瑙勬牸/鐗╂枡鍚嶇О鏄犲皠
+        Set<Long> allSkuIds = new HashSet<>();
+        allOutputs.forEach(o -> allSkuIds.add(o.getProductModelId()));
+        allInputs.forEach(i -> allSkuIds.add(i.getProductId()));
+        allSkuIds.remove(null);
+
+        Map<Long, String> skuToMaterialNameMap = new HashMap<>();
+        if (!allSkuIds.isEmpty()) {
+            List<ProductMaterialSku> skus = productMaterialSkuService.listByIds(allSkuIds);
+            Set<Long> productIds = skus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> materialNameMap = productMaterialService.listByIds(productIds).stream()
+                    .collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getProductName));
+            skuToMaterialNameMap = skus.stream()
+                    .filter(s -> s.getProductId() != null && materialNameMap.containsKey(s.getProductId()))
+                    .collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialNameMap.get(s.getProductId())));
+        }
+
+        ProductionStatisticsMiddleDto result = new ProductionStatisticsMiddleDto();
+        LocalDateTime monthStartDt = monthStart.atStartOfDay();
+
+        // 缁熻鍥哄簾澶勭悊閲�,浠庢姇鍏ヨ幏鍙栨暟鎹�
+        for (ProductionProductInput input : allInputs) {
+            ProductionProductMain main = mainMap.get(input.getProductMainId());
+            if (main == null || main.getReportingTime() == null) continue;
+
+            BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit());
+            boolean isCurrentMonth = !main.getReportingTime().isBefore(monthStartDt);
+            String materialName = skuToMaterialNameMap.get(input.getProductId());
+
+            if ("绮夌叅鐏�".equals(materialName)) {
+                result.setFlyAshYear(result.getFlyAshYear().add(qty));
+                if (isCurrentMonth) result.setFlyAshMonth(result.getFlyAshMonth().add(qty));
+            } else if ("鐭宠啅".equals(materialName)) {
+                result.setGypsumYear(result.getGypsumYear().add(qty));
+                if (isCurrentMonth) result.setGypsumMonth(result.getGypsumMonth().add(qty));
+            }
+        }
+
+        // 缁熻椤圭洰浜ч噺,浠庝骇鍑鸿幏鍙栨暟鎹�
+        for (ProductionProductOutput output : allOutputs) {
+            ProductionProductMain main = mainMap.get(output.getProductMainId());
+            if (main == null || main.getReportingTime() == null) continue;
+
+            BigDecimal qty = UnitUtils.convertValueToTon(output.getQuantity(), "鍚�"); // 浜у嚭鏂归噺閫氬父涓哄惃
+            boolean isCurrentMonth = !main.getReportingTime().isBefore(monthStartDt);
+
+            Long dictCode = orderRouteMap.get(main.getProductOrderId());
+            if (dictCode != null) {
+                if (blockDictCodes.contains(dictCode)) {
+                    result.setBlockYear(result.getBlockYear().add(qty));
+                    if (isCurrentMonth) result.setBlockMonth(result.getBlockMonth().add(qty));
+                } else if (plateDictCodes.contains(dictCode)) {
+                    result.setPlateYear(result.getPlateYear().add(qty));
+                    if (isCurrentMonth) result.setPlateMonth(result.getPlateMonth().add(qty));
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<SolidWasteStatisticsDto> solidWaste(productionStatisticsDto dto) {
+        if (dto == null) {
+            dto = new productionStatisticsDto();
+            dto.setDateType(1);
+        }
+
+        LocalDate now = LocalDate.now();
+        LocalDate startDate = null;
+
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鑷充粖
+            startDate = now.withDayOfMonth(1);
+        } else { // 鏈勾鑷充粖
+            startDate = now.withDayOfYear(1);
+        }
+
+        List<String> dateLabels = new ArrayList<>();
+        DateTimeFormatter formatter;
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鎸夊ぉ
+            formatter = DateTimeFormatter.ofPattern("MM-dd");
+            long days = ChronoUnit.DAYS.between(startDate, now) + 1;
+            for (int i = 0; i < days; i++) {
+                dateLabels.add(startDate.plusDays(i).format(formatter));
+            }
+        } else { // 鏈勾鎸夋湀
+            formatter = DateTimeFormatter.ofPattern("yy-MM");
+            int currentMonth = now.getMonthValue();
+            for (int i = 1; i <= currentMonth; i++) {
+                dateLabels.add(startDate.withMonth(i).format(formatter));
+            }
+        }
+
+        //  鏌ヨ鏃堕棿鑼冨洿鍐呯殑鎶ュ伐鏁版嵁
+        List<ProductionProductMain> mainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .ge(ProductionProductMain::getReportingTime, startDate.atStartOfDay()));
+        if (CollectionUtils.isEmpty(mainList)) return new ArrayList<>();
+
+        List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+        Map<Long, ProductionProductMain> mainMap = mainList.stream().collect(Collectors.toMap(ProductionProductMain::getId, m -> m));
+
+        //  鑾峰彇鎶曞叆鏄庣粏
+        List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery()
+                .in(ProductionProductInput::getProductMainId, mainIds));
+        if (CollectionUtils.isEmpty(allInputs)) return new ArrayList<>();
+
+        //  鑾峰彇瑙勬牸鍜岀墿鏂欏悕绉�
+        Set<Long> skuIds = allInputs.stream().map(ProductionProductInput::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Map<Long, String> skuToMaterialNameMap = new HashMap<>();
+        if (!skuIds.isEmpty()) {
+            List<ProductMaterialSku> skus = productMaterialSkuService.listByIds(skuIds);
+            Set<Long> productIds = skus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> materialNameMap = productMaterialService.listByIds(productIds).stream()
+                    .collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getProductName));
+            skuToMaterialNameMap = skus.stream()
+                    .filter(s -> s.getProductId() != null && materialNameMap.containsKey(s.getProductId()))
+                    .collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialNameMap.get(s.getProductId())));
+        }
+
+        Map<String, SolidWasteStatisticsDto> resultData = new LinkedHashMap<>();
+        for (String label : dateLabels) {
+            SolidWasteStatisticsDto item = new SolidWasteStatisticsDto();
+            item.setDateStr(label);
+            resultData.put(label, item);
+        }
+
+        //  鎸夋棩鏈熷拰鐗╂枡
+        for (ProductionProductInput input : allInputs) {
+            ProductionProductMain main = mainMap.get(input.getProductMainId());
+            if (main == null || main.getReportingTime() == null) continue;
+            String label = main.getReportingTime().format(formatter);
+            if (!resultData.containsKey(label)) continue;
+
+            BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit());
+            String materialName = skuToMaterialNameMap.get(input.getProductId());
+
+            SolidWasteStatisticsDto dayDto = resultData.get(label);
+            if ("绮夌叅鐏�".equals(materialName)) {
+                dayDto.setFlyAsh(dayDto.getFlyAsh().add(qty));
+            } else if ("鐭宠啅".equals(materialName)) {
+                dayDto.setGypsum(dayDto.getGypsum().add(qty));
+            } else if ("鐭崇伆".equals(materialName)) {
+                dayDto.setLime(dayDto.getLime().add(qty));
+            }
+            dayDto.setTotal(dayDto.getTotal().add(qty));
+        }
+
+        return new ArrayList<>(resultData.values());
+    }
+
+    @Override
+    public List<HomeEnergyStatisticsDto> energy(productionStatisticsDto dto) {
+        if (dto == null) {
+            dto = new productionStatisticsDto();
+            dto.setDateType(1);
+        }
+
+        LocalDate now = LocalDate.now();
+        LocalDate startDate = null;
+
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鑷充粖
+            startDate = now.withDayOfMonth(1);
+        } else { // 鏈勾鑷充粖
+            startDate = now.withDayOfYear(1);
+        }
+
+        EnergyStatisticsVo vo = new EnergyStatisticsVo();
+        vo.setStartDate(startDate);
+        vo.setEndDate(now);
+
+        List<EnergyCostDto> costDtos;
+        String labelPattern;
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鎸夊ぉ
+            costDtos = energyConsumptionDetailMapper.energyCostDtos1(vo);
+            labelPattern = "MM-dd";
+        } else { // 鏈勾鎸夋湀
+            costDtos = energyConsumptionDetailMapper.energyCostDtos2(vo);
+            labelPattern = "yy-MM";
+        }
+
+        List<String> dateLabels = new ArrayList<>();
+        DateTimeFormatter labelFormatter = DateTimeFormatter.ofPattern(labelPattern);
+        if (dto.getDateType() != null && dto.getDateType() == 1) {
+            long days = ChronoUnit.DAYS.between(startDate, now) + 1;
+            for (int i = 0; i < days; i++) {
+                dateLabels.add(startDate.plusDays(i).format(labelFormatter));
+            }
+        } else {
+            int currentMonth = now.getMonthValue();
+            for (int i = 1; i <= currentMonth; i++) {
+                dateLabels.add(startDate.withMonth(i).format(labelFormatter));
+            }
+        }
+
+        Map<String, HomeEnergyStatisticsDto> resultData = new LinkedHashMap<>();
+        for (String label : dateLabels) {
+            HomeEnergyStatisticsDto item = new HomeEnergyStatisticsDto();
+            item.setDateStr(label);
+            resultData.put(label, item);
+        }
+
+        if (!CollectionUtils.isEmpty(costDtos)) {
+            for (EnergyCostDto costDto : costDtos) {
+                String dateStr = costDto.getMeterReadingDate();
+                String label = "";
+                try {
+                    if (dto.getDateType() == 1) {
+                        label = LocalDate.parse(dateStr).format(labelFormatter);
+                    } else {
+                        // yyyy-MM 杞寲涓� yy-MM
+                        label = YearMonth.parse(dateStr).format(labelFormatter);
+                    }
+                } catch (Exception e) {
+                    label = dateStr;
+                }
+
+                if (resultData.containsKey(label)) {
+                    HomeEnergyStatisticsDto dayDto = resultData.get(label);
+                    BigDecimal water = costDto.getWaterConsumption() != null ? costDto.getWaterConsumption() : BigDecimal.ZERO;
+                    BigDecimal electricity = costDto.getElectricityConsumption() != null ? costDto.getElectricityConsumption() : BigDecimal.ZERO;
+                    BigDecimal steam = costDto.getGasConsumption() != null ? costDto.getGasConsumption() : BigDecimal.ZERO;
+                    dayDto.setWater(water);
+                    dayDto.setElectricity(electricity);
+                    dayDto.setSteam(steam);
+                }
+            }
+        }
+
+        return new ArrayList<>(resultData.values());
+    }
+
+    @Override
+    public SolidWasteCoreIndicatorsDto coreIndicators(productionStatisticsDto dto) {
+        if (dto == null) {
+            dto = new productionStatisticsDto();
+            dto.setDateType(1);
+        }
+
+        LocalDate now = LocalDate.now();
+        LocalDate startDate = null;
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鑷充粖
+            startDate = now.withDayOfMonth(1);
+        } else { // 鏈勾鑷充粖
+            startDate = now.withDayOfYear(1);
+        }
+
+        // 绱娑堢撼閲忎粠2022-01-01寮�濮�
+        LocalDate cumulativeStartDate = LocalDate.of(2022, 1, 1);
+        // 鏌ヨ浠�2022-01-01鑷充粖鐨勬墍鏈夋姤宸ユ暟鎹�
+        List<ProductionProductMain> mainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .ge(ProductionProductMain::getReportingTime, cumulativeStartDate.atStartOfDay()));
+        if (CollectionUtils.isEmpty(mainList)) return new SolidWasteCoreIndicatorsDto();
+
+        List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+        Map<Long, ProductionProductMain> mainMap = mainList.stream().collect(Collectors.toMap(ProductionProductMain::getId, m -> m));
+
+        // 鑾峰彇鎶曞叆鏄庣粏
+        List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery()
+                .in(ProductionProductInput::getProductMainId, mainIds));
+        if (CollectionUtils.isEmpty(allInputs)) return new SolidWasteCoreIndicatorsDto();
+
+        // 鑾峰彇瑙勬牸鍜岀墿鏂欏悕绉�
+        Set<Long> skuIds = allInputs.stream().map(ProductionProductInput::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Map<Long, String> skuToMaterialNameMap = new HashMap<>();
+        if (!skuIds.isEmpty()) {
+            List<ProductMaterialSku> skus = productMaterialSkuService.listByIds(skuIds);
+            Set<Long> productIds = skus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> materialNameMap = productMaterialService.listByIds(productIds).stream()
+                    .collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getProductName));
+            skuToMaterialNameMap = skus.stream()
+                    .filter(s -> s.getProductId() != null && materialNameMap.containsKey(s.getProductId()))
+                    .collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialNameMap.get(s.getProductId())));
+        }
+
+        BigDecimal totalAmount = BigDecimal.ZERO;
+        BigDecimal cumulativeAmount = BigDecimal.ZERO;
+
+        Set<String> targetMaterials = new HashSet<>(Arrays.asList("绮夌叅鐏�", "鐭宠啅", "鐭崇伆"));
+
+        for (ProductionProductInput input : allInputs) {
+            String materialName = skuToMaterialNameMap.get(input.getProductId());
+            if (materialName == null || !targetMaterials.contains(materialName)) continue;
+
+            ProductionProductMain main = mainMap.get(input.getProductMainId());
+            if (main == null || main.getReportingTime() == null) continue;
+
+            BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit());
+            // 绱姞绱娑堢撼閲�
+            cumulativeAmount = cumulativeAmount.add(qty);
+
+            if (!main.getReportingTime().toLocalDate().isBefore(startDate)) {
+                totalAmount = totalAmount.add(qty);
+            }
+        }
+
+        SolidWasteCoreIndicatorsDto result = new SolidWasteCoreIndicatorsDto();
+        result.setTotalAmount(totalAmount.setScale(2, RoundingMode.HALF_UP));
+        result.setCumulativeAmount(cumulativeAmount.setScale(2, RoundingMode.HALF_UP));
+        return result;
+    }
+
+    @Override
+    public List<SolidWasteStatisticsDto> trends(productionStatisticsDto dto) {
+        return solidWaste(dto);
+    }
+
+    @Override
+    public List<MapDto> typeDistribution(productionStatisticsDto dto) {
+        if (dto == null) {
+            dto = new productionStatisticsDto();
+            dto.setDateType(1);
+        }
+
+        LocalDate now = LocalDate.now();
+        LocalDate startDate = null;
+        if (dto.getDateType() != null && dto.getDateType() == 1) { // 鏈湀鑷充粖
+            startDate = now.withDayOfMonth(1);
+        } else { // 鏈勾鑷充粖
+            startDate = now.withDayOfYear(1);
+        }
+
+        // 鏌ヨ鎶ュ伐涓昏〃
+        List<ProductionProductMain> mainList = productionProductMainService.list(Wrappers.<ProductionProductMain>lambdaQuery()
+                .ge(ProductionProductMain::getReportingTime, startDate.atStartOfDay()));
+        if (CollectionUtils.isEmpty(mainList)) return new ArrayList<>();
+
+        List<Long> mainIds = mainList.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+        
+        // 鑾峰彇鎶曞叆鏄庣粏
+        List<ProductionProductInput> allInputs = productionProductInputService.list(Wrappers.<ProductionProductInput>lambdaQuery()
+                .in(ProductionProductInput::getProductMainId, mainIds));
+        if (CollectionUtils.isEmpty(allInputs)) return new ArrayList<>();
+
+        // 鑾峰彇瑙勬牸鍜岀墿鏂欏悕绉�
+        Set<Long> skuIds = allInputs.stream().map(ProductionProductInput::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Map<Long, String> skuToMaterialNameMap = new HashMap<>();
+        if (!skuIds.isEmpty()) {
+            List<ProductMaterialSku> skus = productMaterialSkuService.listByIds(skuIds);
+            Set<Long> productIds = skus.stream().map(ProductMaterialSku::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());
+            Map<Long, String> materialNameMap = productMaterialService.listByIds(productIds).stream()
+                    .collect(Collectors.toMap(ProductMaterial::getId, ProductMaterial::getProductName));
+            skuToMaterialNameMap = skus.stream()
+                    .filter(s -> s.getProductId() != null && materialNameMap.containsKey(s.getProductId()))
+                    .collect(Collectors.toMap(ProductMaterialSku::getId, s -> materialNameMap.get(s.getProductId())));
+        }
+
+        Map<String, BigDecimal> countMap = new HashMap<>();
+        countMap.put("绮夌叅鐏�", BigDecimal.ZERO);
+        countMap.put("鐭宠啅", BigDecimal.ZERO);
+        countMap.put("鐭崇伆", BigDecimal.ZERO);
+
+        BigDecimal total = BigDecimal.ZERO;
+
+        for (ProductionProductInput input : allInputs) {
+            String materialName = skuToMaterialNameMap.get(input.getProductId());
+            if (materialName == null || !countMap.containsKey(materialName)) continue;
+
+            BigDecimal qty = UnitUtils.convertValueToTon(input.getQuantity(), input.getUnit());
+            countMap.put(materialName, countMap.get(materialName).add(qty));
+            total = total.add(qty);
+        }
+
+        List<MapDto> result = new ArrayList<>();
+        for (Map.Entry<String, BigDecimal> entry : countMap.entrySet()) {
+            MapDto mapDto = new MapDto();
+            mapDto.setName(entry.getKey());
+            BigDecimal value = entry.getValue().setScale(2, RoundingMode.HALF_UP);
+            mapDto.setValue(value.toString());
+
+            if (total.compareTo(BigDecimal.ZERO) > 0) {
+                BigDecimal rate = entry.getValue().divide(total, 4, RoundingMode.HALF_UP)
+                        .multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
+                mapDto.setRate(rate.toString());
+            } else {
+                mapDto.setRate("0.00");
+            }
+            result.add(mapDto);
+        }
+
+        return result;
+    }
 }

--
Gitblit v1.9.3