gongchunyi
3 天以前 711a9201dfcb8fe8d445a6cf940bc7e4a9182e9d
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,22 @@
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.productionPlan.pojo.ProductionPlan;
import com.ruoyi.productionPlan.service.ProductionPlanService;
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 +56,15 @@
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.EnergyMapper;
import com.ruoyi.energy.pojo.Energy;
import com.ruoyi.energy.dto.EnergyCostDto;
import com.ruoyi.energy.mapper.EnergyConsumptionDetailMapper;
import com.ruoyi.energy.vo.EnergyStatisticsVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -145,6 +146,9 @@
    private QualityInspectMapper qualityInspectMapper;
    @Autowired
    private EnergyConsumptionDetailMapper energyConsumptionDetailMapper;
    @Autowired
    private QualityUnqualifiedMapper qualityUnqualifiedMapper;
    @Autowired
@@ -158,6 +162,10 @@
    @Autowired
    private SalesDeliveryMapper salesDeliveryMapper;
    @Autowired
    private EnergyMapper energyMapper;
    @Override
    public HomeBusinessDto business() {
@@ -2523,16 +2531,1172 @@
    @Override
    public Map<String, Long> total() {
        Map<String, Long> map = new HashMap<>();
        //总销售金额
        List<SalesDelivery> salesDeliveries = salesDeliveryMapper.selectList(null);
        long sum = salesDeliveries.stream().mapToLong(value -> Long.parseLong(value.getPrice().toString())).sum();
        map.put("price",sum/1000);//单位w
        //总销售金额
        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",salesDeliveries.stream().count());
        //总销售区?
        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);
        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, // 区域名作为key
                            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, // 区域名作为key
                            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, // 区域名作为key
                            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, // 区域名作为key
                            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;
    }
    /**
     * 根据前端传参 年/月 转换为对应LocalDate列表
     *
     * @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();
        }
        //  根据产品类型字典编码找到对应的生产订单ID
        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获取物料表ID
        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) {
        //  获取动态字典ID
        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)) {
            // 获取单位
            List<Energy> energies = energyMapper.selectList(Wrappers.<Energy>lambdaQuery()
                    .in(Energy::getEnergyTyep, Arrays.asList("水", "电", "气")));
            String waterUnit = energies.stream().filter(e -> "水".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse("");
            String electricityUnit = energies.stream().filter(e -> "电".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse("");
            String steamUnit = energies.stream().filter(e -> "气".equals(e.getEnergyTyep())).map(Energy::getUnit).findFirst().orElse("");
            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);
                    dayDto.setWaterUnit(waterUnit);
                    dayDto.setElectricityUnit(electricityUnit);
                    dayDto.setSteamUnit(steamUnit);
                }
            }
        }
        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;
    }
    @Override
    public Map<String, Long> manage() {
        Map<String, Long> map = new HashMap<>();
        //生产订单总数
        Long total = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().ne("status", 4));
        map.put("total", total);
        //已完成订单数
        Long completed = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 3));
        map.put("completed", completed);
        //未完成订单数
        Long uncompleted = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 1));
        map.put("uncompleted", uncompleted);
        //部分完成订单数
        Long partialCompleted = productOrderMapper.selectCount(new QueryWrapper<ProductOrder>().eq("status", 2));
        map.put("partialCompleted", partialCompleted);
        //来料检总数
        Long materialInspection = qualityInspectMapper.selectCount(new QueryWrapper<QualityInspect>().eq("inspect_type", 0));
        map.put("materialInspection", materialInspection);
        return map;
    }
    @Autowired
    private ProductionPlanService productionPlanService;
    @Override
    public List<PlanTrendsDto> planTrends(Integer type) {
        LocalDate now = LocalDate.now();
        LocalDate startDate;
        DateTimeFormatter formatter;
        int points;
        if (type == null) type = 1;
        if (type == 1) { // 日
            points = 1;
            startDate = now;
            formatter = DateTimeFormatter.ofPattern("MM-dd");
        } else if (type == 2) { // 周
            startDate = now.with(DayOfWeek.MONDAY);
            points = (int) ChronoUnit.DAYS.between(startDate, now) + 1;
            formatter = DateTimeFormatter.ofPattern("MM-dd");
        } else { // 月
            startDate = now.with(TemporalAdjusters.firstDayOfMonth());
            points = now.getDayOfMonth();
            formatter = DateTimeFormatter.ofPattern("MM-dd");
        }
        Map<String, PlanTrendsDto> resultMap = new LinkedHashMap<>();
        for (int i = 0; i < points; i++) {
            String label = startDate.plusDays(i).format(formatter);
            PlanTrendsDto dto = new PlanTrendsDto();
            dto.setDateStr(label);
            dto.setPlannedVolume(BigDecimal.ZERO);
            dto.setLowerVolume(BigDecimal.ZERO);
            dto.setCompletionVolume(BigDecimal.ZERO);
            resultMap.put(label, dto);
        }
        //  统计计划量
        List<ProductionPlan> plans = productionPlanService.list(Wrappers.<ProductionPlan>lambdaQuery()
                .ge(ProductionPlan::getStartDate, Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant())));
        for (ProductionPlan plan : plans) {
            if (plan.getStartDate() == null || plan.getVolume() == null) continue;
            LocalDate d = plan.getStartDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            String label = d.format(formatter);
            if (resultMap.containsKey(label)) {
                PlanTrendsDto dto = resultMap.get(label);
                dto.setPlannedVolume(dto.getPlannedVolume().add(plan.getVolume()));
            }
        }
        //  统计下发量
        List<ProductOrder> orders = productOrderMapper.selectList(Wrappers.<ProductOrder>lambdaQuery()
                .ge(ProductOrder::getStartTime, startDate.atStartOfDay()));
        for (ProductOrder order : orders) {
            if (order.getStartTime() == null || order.getQuantity() == null) continue;
            LocalDate d = order.getStartTime().toLocalDate();
            String label = d.format(formatter);
            if (resultMap.containsKey(label)) {
                PlanTrendsDto dto = resultMap.get(label);
                dto.setLowerVolume(dto.getLowerVolume().add(order.getQuantity()));
            }
        }
        //  统计完成量
        List<ProductionProductOutput> outputs = productionProductOutputMapper.selectList(Wrappers.<ProductionProductOutput>lambdaQuery()
                .ge(ProductionProductOutput::getCreateTime, startDate.atStartOfDay()));
        for (ProductionProductOutput output : outputs) {
            if (output.getCreateTime() == null || output.getQuantity() == null) continue;
            LocalDate d = output.getCreateTime().toLocalDate();
            String label = d.format(formatter);
            if (resultMap.containsKey(label)) {
                PlanTrendsDto dto = resultMap.get(label);
                dto.setCompletionVolume(dto.getCompletionVolume().add(output.getQuantity()));
            }
        }
        return new ArrayList<>(resultMap.values());
    }
}