From a0400e48a2e8f50c457e1c4d4ba19bab2a0d1301 Mon Sep 17 00:00:00 2001
From: zss <zss@example.com>
Date: 星期四, 02 四月 2026 08:58:10 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_宁夏_中盛建材' into dev_宁夏_中盛建材

---
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java |  722 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 698 insertions(+), 24 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 a49482e..bf4f2bf 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -5,15 +5,13 @@
 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;
@@ -28,25 +26,20 @@
 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.ProductProcessMapper;
-import com.ruoyi.production.mapper.ProductWorkOrderMapper;
-import com.ruoyi.production.mapper.ProductionProductInputMapper;
-import com.ruoyi.production.mapper.ProductionProductOutputMapper;
-import com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper;
-import com.ruoyi.production.pojo.ProductProcess;
-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.SysDictData;
 import com.ruoyi.project.system.mapper.SysDeptMapper;
+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;
@@ -61,9 +54,13 @@
 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;
@@ -143,6 +140,9 @@
 
     @Autowired
     private QualityInspectMapper qualityInspectMapper;
+
+    @Autowired
+    private EnergyConsumptionDetailMapper energyConsumptionDetailMapper;
 
     @Autowired
     private QualityUnqualifiedMapper qualityUnqualifiedMapper;
@@ -2537,10 +2537,10 @@
                 .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);
+        map.put("volume", finalVolume);
         //绱瀹㈡埛
         Long count = customerMapper.selectCount(null);
-        map.put("customer",count);
+        map.put("customer", count);
         return map;
     }
 
@@ -2550,11 +2550,11 @@
         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()));
+                .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("骞�")){
+            if (salesDeliveryDto.getDays().equals("骞�")) {
                 lastDay = date.with(TemporalAdjusters.lastDayOfYear());
             }
             LocalDate finalLastDay = lastDay;
@@ -2597,11 +2597,11 @@
         List<SalesTotalDetailDto> salesTotalDetailDtos = new ArrayList<>();
         List<LocalDate> dates = convertDateList(salesDeliveryDto.getDays());
         List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(Wrappers.<SalesDelivery>lambdaQuery()
-                .eq(SalesDelivery::getProductName,salesDeliveryDto.getType()));
+                .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("骞�")){
+            if (salesDeliveryDto.getDays().equals("骞�")) {
                 lastDay = date.with(TemporalAdjusters.lastDayOfYear());
             }
             LocalDate finalLastDay = lastDay;
@@ -2646,11 +2646,11 @@
         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()));
+                .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("骞�")){
+            if (salesDeliveryDto.getDays().equals("骞�")) {
                 lastDay = date.with(TemporalAdjusters.lastDayOfYear());
             }
             LocalDate finalLastDay = lastDay;
@@ -2704,7 +2704,7 @@
         for (LocalDate date : dates) {
             LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
             LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
-            if (salesDeliveryDto.getDays().equals("骞�")){
+            if (salesDeliveryDto.getDays().equals("骞�")) {
                 lastDay = date.with(TemporalAdjusters.lastDayOfYear());
             }
             Date startDate = Date.from(firstDay.atStartOfDay(ZoneId.systemDefault()).toInstant());
@@ -2728,7 +2728,7 @@
                                 Collectors.counting()   // 璁℃暟
                         ));
             }
-            regionCountMap.put("鍏ㄩ儴",customers.stream().count());
+            regionCountMap.put("鍏ㄩ儴", customers.stream().count());
             maps.add(regionCountMap);
         }
         salesTotalDto.setDates(dates);
@@ -2738,6 +2738,7 @@
 
     /**
      * 鏍规嵁鍓嶇浼犲弬 骞�/鏈� 杞崲涓哄搴擫ocalDate鍒楄〃
+     *
      * @param days 鍓嶇鍙傛暟锛�"骞�" / "鏈�"
      * @return List<LocalDate>
      */
@@ -2759,4 +2760,677 @@
         }
         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());
+    }
 }

--
Gitblit v1.9.3