liyong
3 天以前 4635770544e2d57416ad88a8983ee293919f5fec
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,8 +3,20 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.bean.dto.purchase.PurchaseInboundDto;
import com.ruoyi.account.bean.dto.purchase.PurchaseReturnDto;
import com.ruoyi.account.bean.dto.sales.SalesOutboundDto;
import com.ruoyi.account.bean.dto.sales.SalesReturnDto;
import com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo;
import com.ruoyi.account.bean.vo.purchase.PurchaseReturnVo;
import com.ruoyi.account.bean.vo.sales.SalesOutboundVo;
import com.ruoyi.account.bean.vo.sales.SalesReturnVo;
import com.ruoyi.account.mapper.AccountStatementMapper;
import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper;
import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.basic.mapper.CustomerMapper;
@@ -25,11 +37,13 @@
import com.ruoyi.home.dto.*;
import com.ruoyi.home.mapper.HomeMapper;
import com.ruoyi.home.service.HomeService;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.production.bean.dto.ProductionProductOutputDto;
import com.ruoyi.production.mapper.*;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
@@ -41,7 +55,9 @@
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -67,6 +83,10 @@
public class HomeServiceImpl implements HomeService {
    private final SalesLedgerMapper salesLedgerMapper;
    private final StockOutRecordMapper stockOutRecordMapper;
    private final ReturnManagementMapper returnManagementMapper;
    private final StockInRecordMapper stockInRecordMapper;
    private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
@@ -104,6 +124,7 @@
    private final AccountPurchasePaymentMapper accountPurchasePaymentMapper;
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    private final AccountStatementMapper accountStatementMapper;
    private final ProductionAccountMapper productionAccountMapper;
@@ -131,12 +152,8 @@
                            salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList()));
            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper
                    .selectList(salesLedgerProductMapperLambdaQueryWrapper);
            // 未开票金额
            BigDecimal noInvoiceAmountTotal = salesLedgerProducts.stream().map(SalesLedgerProduct::getNoInvoiceAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            homeBusinessDto.setMonthSaleMoney(contractAmount.setScale(2, RoundingMode.HALF_UP).toString());
            homeBusinessDto.setMonthSaleHaveMoney(noInvoiceAmountTotal.setScale(2, RoundingMode.HALF_UP).toString());
            homeBusinessDto.setMonthSaleHaveMoney(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP).toString());
        }
        // 创建LambdaQueryWrapper
        LambdaQueryWrapper<PurchaseLedger> queryWrapper = new LambdaQueryWrapper<>();
@@ -159,14 +176,8 @@
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            //  待付款总金额
            BigDecimal unReceiptPaymentAmount = salesLedgerProductsCopy.stream()
                    .map(SalesLedgerProduct::getPendingTicketsTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            homeBusinessDto.setMonthPurchaseMoney(receiveAmount.setScale(2, RoundingMode.HALF_UP).toString());
            homeBusinessDto.setMonthPurchaseHaveMoney(unReceiptPaymentAmount.setScale(2, RoundingMode.HALF_UP).toString());
            homeBusinessDto.setMonthPurchaseHaveMoney(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP).toString());
        }
        // 统计库存
        BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal();
@@ -302,19 +313,35 @@
            queryWrapper.ge(QualityInspect::getCheckTime, monthStart.toString())
                    .le(QualityInspect::getCheckTime, monthEnd.toString());
            List<QualityInspect> monthInspects = qualityStatisticsMapper.selectList(queryWrapper);
            // 统计总数量(合格数量 + 不合格数量)
            BigDecimal reduce = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(0))
                    .map(QualityInspect::getQuantity)
                    .map(inspect -> {
                        BigDecimal qualified = inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO;
                        BigDecimal unqualified = inspect.getUnqualifiedQuantity() != null ? inspect.getUnqualifiedQuantity() : BigDecimal.ZERO;
                        return qualified.add(unqualified);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            supplierNum = supplierNum.add(reduce);
            BigDecimal reduce1 = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(1))
                    .map(QualityInspect::getQuantity)
                    .map(inspect -> {
                        BigDecimal qualified = inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO;
                        BigDecimal unqualified = inspect.getUnqualifiedQuantity() != null ? inspect.getUnqualifiedQuantity() : BigDecimal.ZERO;
                        return qualified.add(unqualified);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            processNum = processNum.add(reduce1);
            BigDecimal reduce2 = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(2))
                    .map(QualityInspect::getQuantity)
                    .map(inspect -> {
                        BigDecimal qualified = inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO;
                        BigDecimal unqualified = inspect.getUnqualifiedQuantity() != null ? inspect.getUnqualifiedQuantity() : BigDecimal.ZERO;
                        return qualified.add(unqualified);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            factoryNum = factoryNum.add(reduce2);
@@ -324,25 +351,22 @@
            // 1. 供应商检验(类型0)- 合格数量
            BigDecimal supplierQualified = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(0)
                            && "合格".equals(inspect.getCheckResult()))
                    .map(QualityInspect::getQuantity)
                    .filter(inspect -> inspect.getInspectType().equals(0))
                    .map(inspect -> inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setSupplierNum(supplierQualified);
            // 2. 工序检验(类型1)- 合格数量
            BigDecimal processQualified = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(1)
                            && "合格".equals(inspect.getCheckResult()))
                    .map(QualityInspect::getQuantity)
                    .filter(inspect -> inspect.getInspectType().equals(1))
                    .map(inspect -> inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setProcessNum(processQualified);
            // 3. 工厂检验(类型2)- 合格数量
            BigDecimal factoryQualified = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(2)
                            && "合格".equals(inspect.getCheckResult()))
                    .map(QualityInspect::getQuantity)
                    .filter(inspect -> inspect.getInspectType().equals(2))
                    .map(inspect -> inspect.getQualifiedQuantity() != null ? inspect.getQualifiedQuantity() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            item.setFactoryNum(factoryQualified);
@@ -404,43 +428,33 @@
     */
    @Override
    public StatisticsReceivablePayableDto statisticsReceivablePayable(Integer type) {
        StatisticsReceivablePayableDto statisticsReceivablePayableDto = new StatisticsReceivablePayableDto();
        LocalDate today = LocalDate.now();
        LocalDate startDate = null;
        LocalDate endDate = null;
        switch (type) {
            case 1:
                // 获取本周周一
                startDate = today.with(DayOfWeek.MONDAY);
                // 获取本周周日
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2:
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3:
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
        StatisticsReceivablePayableDto dto = new StatisticsReceivablePayableDto();
        LocalDate[] range = resolveFinanceRange(type);
        LocalDate startDate = range[0];
        LocalDate endDate = range[1];
                startDate = today.withMonth(firstMonthOfQuarter.getValue())
                        .with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue())
                        .with(TemporalAdjusters.lastDayOfMonth());
                break;
        }
        // 应收
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //收款
        BigDecimal advanceMoney = sumCollectionAmount(startDate, endDate);
        //付款
        BigDecimal prepayMoney = sumPaymentAmount(startDate, endDate);
        // 应付
        // 预收
        // 预付
        return statisticsReceivablePayableDto;
        //应收金额=销售出库-销售退货
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(salesReturnAmount))));
        //应付金额=采购入库-采购退货
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(purchaseReturnAmount))));
        //收款金额=收款单
        dto.setAdvanceMoney(scaleMoney(advanceMoney));
        //付款金额=付款单
        dto.setPrepayMoney(scaleMoney(prepayMoney));
        return dto;
    }
    public static <T> BigDecimal sumAmount(List<T> list, java.util.function.Function<T, BigDecimal> amountExtractor) {
@@ -1249,145 +1263,55 @@
    @Override
    public MonthlyIncomeDto monthlyIncome() {
        MonthlyIncomeDto dto = new MonthlyIncomeDto();
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SalesLedgerProduct::getType, 1);
        wrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        wrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(products)) {
            return dto;
        }
        BigDecimal collected = products.stream()
                .map(SalesLedgerProduct::getInvoiceTotal)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setMonthlyIncome(collected);
        BigDecimal overdue = products.stream()
                .map(SalesLedgerProduct::getPendingInvoiceTotal)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setOverdueNum(overdue);
        BigDecimal total = collected.add(overdue);
        if (total.compareTo(BigDecimal.ZERO) > 0) {
            String collectionRate = collected.divide(total, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setCollectionRate(collectionRate);
            String overdueRate = overdue.divide(total, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setOverdueRate(overdueRate);
        }
        LocalDate today = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //回款率=收款/(销售出库-销售退货)应收
        String collectionRate = toRateString(monthlyIncome, receivableBase.subtract(salesReturnAmount));
        //逾期数=(销售出库金额-销售退货金额)应收金额-收款金额
        BigDecimal overdueAmount = receivableBase.subtract(salesReturnAmount).subtract(monthlyIncome);
        //逾期率=逾期数/应收金额
        String overdueRate = toRateString(overdueAmount, receivableBase);
        dto.setMonthlyIncome(scaleMoney(monthlyIncome));
        dto.setCollectionRate(collectionRate);
        dto.setOverdueNum(overdueAmount);
        dto.setOverdueRate(overdueRate);
        return dto;
    }
    @Override
    public MonthlyExpenditureDto monthlyExpenditure() {
        MonthlyExpenditureDto dto = new MonthlyExpenditureDto();
        LocalDate today = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //支出
        BigDecimal monthlyExpenditure = sumPaymentAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //付款率=付款/(采购入库-采购退货)应付
        String paymentRate = toRateString(monthlyExpenditure, payableBase.subtract(purchaseReturnAmount));
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //毛利润= 收款-支出
        BigDecimal grossProfit = monthlyIncome.subtract(monthlyExpenditure);
        //利润率=毛利润/收款
        String profitMarginRate = toRateString(grossProfit, monthlyIncome);
        // 当月时间范围
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        // 采购台账(type = 2)
        LambdaQueryWrapper<SalesLedgerProduct> purchaseWrapper = new LambdaQueryWrapper<>();
        purchaseWrapper.eq(SalesLedgerProduct::getType, 2);
        purchaseWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        purchaseWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> purchaseProducts = salesLedgerProductMapper.selectList(purchaseWrapper);
        BigDecimal rawMaterialCost = BigDecimal.ZERO; // 原材料成本
        BigDecimal paidAmount = BigDecimal.ZERO; // 已付款金额
        BigDecimal pendingAmount = BigDecimal.ZERO; // 待付款金额
        if (!CollectionUtils.isEmpty(purchaseProducts)) {
            for (SalesLedgerProduct p : purchaseProducts) {
                if (p.getTaxInclusiveTotalPrice() != null) {
                    rawMaterialCost = rawMaterialCost.add(p.getTaxInclusiveTotalPrice());
                }
                if (p.getTicketsTotal() != null) {
                    paidAmount = paidAmount.add(p.getTicketsTotal());
                }
                if (p.getPendingTicketsTotal() != null) {
                    pendingAmount = pendingAmount.add(p.getPendingTicketsTotal());
                }
            }
        }
        // 其他费用
        // 月度总支出
        BigDecimal monthlyExpenditure = BigDecimal.ZERO;
        dto.setMonthlyExpenditure(monthlyExpenditure);
        // 已付款 ÷(已付款 + 待付款)
        BigDecimal totalPayable = paidAmount.add(pendingAmount);
        if (totalPayable.compareTo(BigDecimal.ZERO) > 0) {
            String paymentRate = paidAmount
                    .divide(totalPayable, 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setPaymentRate(paymentRate);
        }
        // 售台账(type = 1)
        LambdaQueryWrapper<SalesLedgerProduct> salesWrapper = new LambdaQueryWrapper<>();
        salesWrapper.eq(SalesLedgerProduct::getType, 1);
        salesWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
        salesWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
        List<SalesLedgerProduct> salesProducts = salesLedgerProductMapper.selectList(salesWrapper);
        BigDecimal revenue = BigDecimal.ZERO;
        if (!CollectionUtils.isEmpty(salesProducts)) {
            for (SalesLedgerProduct s : salesProducts) {
                if (s.getInvoiceAmount() != null) {
                    revenue = revenue.add(s.getInvoiceAmount());
                }
            }
        }
        // 毛利润 & 利润率
        if (revenue.compareTo(BigDecimal.ZERO) > 0) {
            // 毛利润 = 销售收入 - 原材料成本
            BigDecimal grossProfit = revenue.subtract(rawMaterialCost);
            dto.setGrossProfit(grossProfit);
            // 利润率 = (销售收入 - 月度总支出) / 销售收入
            BigDecimal profit = revenue.subtract(monthlyExpenditure);
            String profitMarginRate = profit
                    .divide(revenue, 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setProfitMarginRate(profitMarginRate);
        }
        dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure));
        dto.setPaymentRate(paymentRate);
        dto.setGrossProfit(scaleMoney(grossProfit));
        dto.setProfitMarginRate(profitMarginRate);
        return dto;
    }
@@ -1826,11 +1750,8 @@
        BigDecimal unqualifiedCount = BigDecimal.ZERO;
        for (QualityInspect item : list) {
            if ("合格".equals(item.getCheckResult())) {
                qualifiedCount = qualifiedCount.add(item.getQuantity());
            } else {
                unqualifiedCount = unqualifiedCount.add(item.getQuantity());
            }
            qualifiedCount = qualifiedCount.add(item.getQualifiedQuantity() != null ? item.getQualifiedQuantity() : BigDecimal.ZERO);
            unqualifiedCount = unqualifiedCount.add(item.getUnqualifiedQuantity() != null ? item.getUnqualifiedQuantity() : BigDecimal.ZERO);
        }
        BigDecimal totalCount = qualifiedCount.add(unqualifiedCount);
@@ -2099,13 +2020,11 @@
                continue;
            }
            BigDecimal quantity = item.getQuantity();
            BigDecimal qualifiedQty = item.getQualifiedQuantity() != null ? item.getQualifiedQuantity() : BigDecimal.ZERO;
            BigDecimal unqualifiedQty = item.getUnqualifiedQuantity() != null ? item.getUnqualifiedQuantity() : BigDecimal.ZERO;
            if ("合格".equals(item.getCheckResult())) {
                dto.setQualifiedCount(dto.getQualifiedCount().add(quantity));
            } else {
                dto.setUnqualifiedCount(dto.getUnqualifiedCount().add(quantity));
            }
            dto.setQualifiedCount(dto.getQualifiedCount().add(qualifiedQty));
            dto.setUnqualifiedCount(dto.getUnqualifiedCount().add(unqualifiedQty));
        }
        // 计算合格率
@@ -2146,14 +2065,12 @@
            BigDecimal unqualifiedCount = BigDecimal.ZERO;
            for (QualityInspect item : items) {
                BigDecimal qty = item.getQuantity();
                totalCount = totalCount.add(qty);
                BigDecimal qualifiedQty = item.getQualifiedQuantity() != null ? item.getQualifiedQuantity() : BigDecimal.ZERO;
                BigDecimal unqualifiedQty = item.getUnqualifiedQuantity() != null ? item.getUnqualifiedQuantity() : BigDecimal.ZERO;
                if ("合格".equals(item.getCheckResult())) {
                    qualifiedCount = qualifiedCount.add(qty);
                } else {
                    unqualifiedCount = unqualifiedCount.add(qty);
                }
                totalCount = totalCount.add(qualifiedQty.add(unqualifiedQty));
                qualifiedCount = qualifiedCount.add(qualifiedQty);
                unqualifiedCount = unqualifiedCount.add(unqualifiedQty);
            }
            if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
@@ -2282,13 +2199,17 @@
        dto.setProcessNum(sumQuantity(qualityInspectList, 1)); // 过程
        dto.setFactoryNum(sumQuantity(qualityInspectList, 2)); // 出厂
        // 假设 qualityInspectList 是一个 List<QualityInspect> 类型的集合
        Map<String, List<QualityInspect>> groupedByCheckResult = qualityInspectList.stream()
                .collect(Collectors.groupingBy(QualityInspect::getCheckResult));
        List<QualityInspect> qualityInspects = groupedByCheckResult.get("不合格");
        if (ObjectUtils.isNull(qualityInspects) || qualityInspects.size() == 0) {
            return null;
        // 根据 unqualifiedQuantity > 0 筛选不合格记录
        List<QualityInspect> qualityInspects = qualityInspectList.stream()
                .filter(i -> i.getUnqualifiedQuantity() != null && i.getUnqualifiedQuantity().compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        if (ObjectUtils.isEmpty(qualityInspects)) {
            // 即使没有不合格记录,也应该返回统计数据,只是图表项为空
            dto.setItem(new ArrayList<>());
            return dto;
        }
        // 4. 处理图表项 (Item)
        List<QualityStatisticsItem> itemList = new ArrayList<>();
@@ -2331,8 +2252,11 @@
    private BigDecimal sumQuantity(List<QualityInspect> list, Integer type) {
        return list.stream()
                .filter(i -> i.getInspectType().equals(type))
                .map(QualityInspect::getQuantity)
                .filter(Objects::nonNull)
                .map(i -> {
                    BigDecimal qualified = i.getQualifiedQuantity() != null ? i.getQualifiedQuantity() : BigDecimal.ZERO;
                    BigDecimal unqualified = i.getUnqualifiedQuantity() != null ? i.getUnqualifiedQuantity() : BigDecimal.ZERO;
                    return qualified.add(unqualified);
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
@@ -2340,11 +2264,18 @@
        QualityStatisticsItem item = new QualityStatisticsItem();
        item.setDate(dateLabel);
        item.setSupplierNum(list.stream().filter(i -> i.getInspectType() == 0).map(QualityInspect::getQuantity)
        // 统计每种检验类型的不合格数量
        item.setSupplierNum(list.stream()
                .filter(i -> i.getInspectType() == 0)
                .map(i -> i.getUnqualifiedQuantity() != null ? i.getUnqualifiedQuantity() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        item.setProcessNum(list.stream().filter(i -> i.getInspectType() == 1).map(QualityInspect::getQuantity)
        item.setProcessNum(list.stream()
                .filter(i -> i.getInspectType() == 1)
                .map(i -> i.getUnqualifiedQuantity() != null ? i.getUnqualifiedQuantity() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        item.setFactoryNum(list.stream().filter(i -> i.getInspectType() == 2).map(QualityInspect::getQuantity)
        item.setFactoryNum(list.stream()
                .filter(i -> i.getInspectType() == 2)
                .map(i -> i.getUnqualifiedQuantity() != null ? i.getUnqualifiedQuantity() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        return item;
@@ -2384,6 +2315,121 @@
        return productionOperationTaskMapper.calculateProductionStatistics(startDateTime, endDateTime, userId, processIds);
    }
    private LocalDate[] resolveFinanceRange(Integer type) {
        LocalDate today = LocalDate.now();
        int safeType = type == null ? 1 : type;
        LocalDate startDate;
        LocalDate endDate;
        switch (safeType) {
            case 1:
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2:
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3:
                Month firstMonthOfQuarter = today.getMonth().firstMonthOfQuarter();
                startDate = LocalDate.of(today.getYear(), firstMonthOfQuarter, 1);
                endDate = startDate.plusMonths(2).with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
        }
        return new LocalDate[]{startDate, endDate};
    }
    //计算日期内的销售出库金额
    private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) {
        SalesOutboundDto salesOutboundDto = new SalesOutboundDto();
        salesOutboundDto.setStartDate(startDate);
        salesOutboundDto.setEndDate(endDate);
        List<SalesOutboundVo> salesOutboundVos = stockOutRecordMapper.listPageAccountSales(new Page(1, -1), salesOutboundDto).getRecords();
        return sumAmount(salesOutboundVos, SalesOutboundVo::getOutboundAmount);
    }
    //计算日期内的销售退货金额
    private BigDecimal sumSalesReturnAmount(LocalDate startDate, LocalDate endDate) {
        SalesReturnDto salesReturnDto = new SalesReturnDto();
        salesReturnDto.setStartDate(startDate);
        salesReturnDto.setEndDate(endDate);
        List<SalesReturnVo> salesReturnVos = returnManagementMapper.listPageAccountSalesReturn(new Page(1, -1), salesReturnDto).getRecords();
        return sumAmount(salesReturnVos, SalesReturnVo::getRefundAmount);
    }
    //计算日期内的采购入库金额
    private BigDecimal sumPurchaseContractAmount(LocalDate startDate, LocalDate endDate) {
        PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto();
        purchaseInboundDto.setStartDate(startDate);
        purchaseInboundDto.setEndDate(endDate);
        List<PurchaseInboundVo> purchaseInboundVos = stockInRecordMapper.listPageAccountPurchase(new Page(1, -1), purchaseInboundDto).getRecords();
        return sumAmount(purchaseInboundVos, PurchaseInboundVo::getInboundAmount);
    }
    //计算日期内的采购退货金额
    private BigDecimal sumPurchaseReturnAmount(LocalDate startDate, LocalDate endDate) {
        PurchaseReturnDto purchaseReturnDto = new PurchaseReturnDto();
        purchaseReturnDto.setStartDate(startDate);
        purchaseReturnDto.setEndDate(endDate);
        List<PurchaseReturnVo> purchaseReturnVos = purchaseReturnOrdersMapper.listPageAccountPurchaseReturn(new Page(1, -1), purchaseReturnDto).getRecords();
        return sumAmount(purchaseReturnVos, PurchaseReturnVo::getTotalAmount);
    }
    //计算日期内的总收款金额
    private BigDecimal sumCollectionAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList(
                new LambdaQueryWrapper<AccountSalesCollection>()
                        .ge(AccountSalesCollection::getCollectionDate, startDate)
                        .le(AccountSalesCollection::getCollectionDate, endDate)));
        return sumAmount(collections, AccountSalesCollection::getCollectionAmount);
    }
    //计算日期内的总付款金额
    private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList(
                new LambdaQueryWrapper<AccountPurchasePayment>()
                        .ge(AccountPurchasePayment::getPaymentDate, startDate)
                        .le(AccountPurchasePayment::getPaymentDate, endDate)));
        return sumAmount(payments, AccountPurchasePayment::getPaymentAmount);
    }
    private Date toDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }
    private Date toExclusiveEndDate(LocalDate localDate) {
        return Date.from(localDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }
    private String toRateString(BigDecimal numerator, BigDecimal denominator) {
        if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) {
            return "0.00";
        }
        return defaultDecimal(numerator)
                .divide(denominator, 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100))
                .setScale(2, RoundingMode.HALF_UP)
                .toString();
    }
    private BigDecimal maxZero(BigDecimal value) {
        if (value == null) {
            return BigDecimal.ZERO;
        }
        return value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value;
    }
    private BigDecimal scaleMoney(BigDecimal value) {
        return defaultDecimal(value).setScale(2, RoundingMode.HALF_UP);
    }
    private <T> List<T> defaultList(List<T> list) {
        return list == null ? List.of() : list;
    }
    private BigDecimal defaultDecimal(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }