11
gongchunyi
7 天以前 1f5fdca1ab73461fb930f64c26dcb4038a9d4bf7
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,10 +3,18 @@
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.AccountStatement;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
@@ -29,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;
@@ -45,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;
@@ -71,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;
@@ -417,14 +433,26 @@
        LocalDate startDate = range[0];
        LocalDate endDate = range[1];
        //销售出库
        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);
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(advanceMoney))));
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(prepayMoney))));
        //应收金额=销售出库-销售退货
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(salesReturnAmount))));
        //应付金额=采购入库-采购退货
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(purchaseReturnAmount))));
        //收款金额=收款单
        dto.setAdvanceMoney(scaleMoney(advanceMoney));
        //付款金额=付款单
        dto.setPrepayMoney(scaleMoney(prepayMoney));
        return dto;
    }
@@ -991,7 +1019,7 @@
    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
        String targetName;
        if (type == 1) {
            targetName = "原材料";
            targetName = "原料";
        } else if (type == 2) {
            targetName = "成品";
        } else if (type == 3) {
@@ -1239,33 +1267,21 @@
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        String collectionRate = toRateString(monthlyIncome, receivableBase);
        String currentMonthText = currentMonth.toString();
        List<AccountStatement> receivableStatements = defaultList(accountStatementMapper.selectList(
                new LambdaQueryWrapper<AccountStatement>()
                        .eq(AccountStatement::getAccountType, 1)
                        .le(AccountStatement::getStatementMonth, currentMonthText)));
        long overdueNum = receivableStatements.stream()
                .filter(item -> item.getStatementMonth() != null && item.getStatementMonth().compareTo(currentMonthText) < 0)
                .filter(item -> defaultDecimal(item.getClosingBalance()).compareTo(BigDecimal.ZERO) > 0)
                .count();
        long totalReceivableCount = receivableStatements.size();
        String overdueRate = totalReceivableCount == 0
                ? "0.00"
                : BigDecimal.valueOf(overdueNum)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(totalReceivableCount), 2, RoundingMode.HALF_UP)
                .toString();
        //销售退货
        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(BigDecimal.valueOf(overdueNum));
        dto.setOverdueNum(overdueAmount);
        dto.setOverdueRate(overdueRate);
        return dto;
    }
@@ -1277,16 +1293,25 @@
        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);
        dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure));
        dto.setPaymentRate(toRateString(monthlyExpenditure, payableBase));
        dto.setPaymentRate(paymentRate);
        dto.setGrossProfit(scaleMoney(grossProfit));
        dto.setProfitMarginRate(toRateString(grossProfit, monthlyIncome));
        dto.setProfitMarginRate(profitMarginRate);
        return dto;
    }
@@ -1453,95 +1478,80 @@
        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<ProductionTaskStatisticsDto> startList = productionOperationTaskMapper.selectTaskStartStats(startStr, endStr);
        List<ProductionProductOutputDto> outputList = productionProductOutputMapper
                .selectOutputStats(startStr, endStr);
        List<ProductionProductOutputDto> outputList = productionProductOutputMapper.selectOutputStats(startStr, endStr);
        Map<String, WorkOrderEfficiencyDto> dateMap = new HashMap<>();
        // 1. 处理开工数量(统一将时间转为 LocalDate 字符串,去除时分秒)
        if (!CollectionUtils.isEmpty(startList)) {
            for (ProductionTaskStatisticsDto item : startList) {
                if (item.getActualStartTime() != null) {
                    String date = item.getActualStartTime().toString();
                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
                    dto.setDate(date);
                    // 💡 关键修改:如果是 LocalDateTime,必须先 toLocalDate() 再 toString()
                    String dateStr = item.getActualStartTime().toString();
                    WorkOrderEfficiencyDto dto = dateMap.computeIfAbsent(dateStr, k -> {
                        WorkOrderEfficiencyDto newDto = new WorkOrderEfficiencyDto();
                        newDto.setDate(k);
                        newDto.setStartQuantity(BigDecimal.ZERO);
                        newDto.setFinishQuantity(BigDecimal.ZERO);
                        newDto.setYieldRate("0.00");
                        return newDto;
                    });
                    BigDecimal qty = item.getPlanQuantity() != null ? item.getPlanQuantity() : BigDecimal.ZERO;
                    dto.setStartQuantity(dto.getStartQuantity() != null ? dto.getStartQuantity().add(qty) : qty);
                    dateMap.put(date, dto);
                    dto.setStartQuantity(dto.getStartQuantity().add(qty));
                }
            }
        }
        // 完工数量和良品率
        // 2. 处理完工数量和报废数量(仅通过 1 次循环搞定,大幅提升性能)
        if (!CollectionUtils.isEmpty(outputList)) {
            // 定义一个临时的报废数计数器,方便后面算良品率
            Map<String, BigDecimal> scrapMap = new HashMap<>();
            for (ProductionProductOutputDto item : outputList) {
                if (item.getCreateTime() != null) {
                    String date = item.getCreateTime().toLocalDate().toString();
                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
                    dto.setDate(date);
                    String dateStr = item.getCreateTime().toLocalDate().toString();
                    BigDecimal finishQty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO;
                    BigDecimal scrapQty = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO;
                    WorkOrderEfficiencyDto dto = dateMap.computeIfAbsent(dateStr, k -> {
                        WorkOrderEfficiencyDto newDto = new WorkOrderEfficiencyDto();
                        newDto.setDate(k);
                        newDto.setStartQuantity(BigDecimal.ZERO);
                        newDto.setFinishQuantity(BigDecimal.ZERO);
                        newDto.setYieldRate("0.00");
                        return newDto;
                    });
                    dto.setFinishQuantity(
                            dto.getFinishQuantity() != null ? dto.getFinishQuantity().add(finishQty) : finishQty);
                    BigDecimal qty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO;
                    BigDecimal scrap = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO;
                    // 良品数 = 总投入数 - 报废数 (对应你原代码中 finishMap 的逻辑)
                    BigDecimal goodQty = qty.subtract(scrap);
                    dto.setFinishQuantity(dto.getFinishQuantity().add(goodQty));
                    // 累加报废数
                    scrapMap.put(dateStr, scrapMap.getOrDefault(dateStr, BigDecimal.ZERO).add(scrap));
                }
            }
            Map<String, BigDecimal> scrapMap = outputList.stream()
                    .filter(i -> i.getCreateTime() != null)
                    .collect(Collectors.groupingBy(
                            i -> i.getCreateTime().toLocalDate().toString(),
                            Collectors.reducing(BigDecimal.ZERO,
                                    i -> i.getScrapQty() != null ? i.getScrapQty() : BigDecimal.ZERO,
                                    BigDecimal::add)));
            Map<String, BigDecimal> finishMap = outputList.stream()
                    .filter(i -> i.getCreateTime() != null)
                    .collect(Collectors.groupingBy(
                            i -> i.getCreateTime().toLocalDate().toString(),
                            Collectors.reducing(BigDecimal.ZERO, i -> {
                                BigDecimal qty = (i.getQuantity() != null) ? i.getQuantity() : BigDecimal.ZERO;
                                BigDecimal scrap = (i.getScrapQty() != null) ? i.getScrapQty() : BigDecimal.ZERO;
                                return qty.subtract(scrap);
                            }, BigDecimal::add)));
            finishMap.forEach((date, qty) -> {
                WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
                dto.setDate(date);
                dto.setFinishQuantity(qty);
                dateMap.put(date, dto);
            });
            dateMap.forEach((date, dto) -> {
                BigDecimal finish = dto.getFinishQuantity() != null ? dto.getFinishQuantity() : BigDecimal.ZERO;
                BigDecimal scrap = scrapMap.getOrDefault(date, BigDecimal.ZERO);
            // 3. 计算良品率
            dateMap.forEach((dateStr, dto) -> {
                BigDecimal finish = dto.getFinishQuantity();
                BigDecimal scrap = scrapMap.getOrDefault(dateStr, BigDecimal.ZERO);
                BigDecimal total = finish.add(scrap);
                if (total.compareTo(BigDecimal.ZERO) > 0) {
                    BigDecimal rate = finish.divide(total, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP);
                    // 💡 延续你之前的逻辑:使用 RoundingMode.DOWN 截断,防止 99.999% 四舍五入变成 100.00
                    BigDecimal rate = finish.divide(total, 4, RoundingMode.DOWN)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.DOWN);
                    dto.setYieldRate(rate.toString());
                } else {
                    dto.setYieldRate("0.00");
                }
                if (dto.getStartQuantity() == null)
                    dto.setStartQuantity(BigDecimal.ZERO);
                if (dto.getFinishQuantity() == null)
                    dto.setFinishQuantity(BigDecimal.ZERO);
            });
        }
        dateMap.values().forEach(dto -> {
            if (dto.getStartQuantity() == null)
                dto.setStartQuantity(BigDecimal.ZERO);
            if (dto.getFinishQuantity() == null)
                dto.setFinishQuantity(BigDecimal.ZERO);
            if (dto.getYieldRate() == null)
                dto.setYieldRate("0.00");
        });
        // 4. 排序并输出
        return dateMap.values().stream()
                .sorted(Comparator.comparing(WorkOrderEfficiencyDto::getDate))
                .collect(Collectors.toList());
@@ -1890,7 +1900,7 @@
            }
            switch (parent.getProductName()) {
                case "原材料":
                case "原料":
                    rawMaterialCount = rawMaterialCount.add(quantity);
                    break;
                case "半成品":
@@ -2317,20 +2327,43 @@
        return new LocalDate[]{startDate, endDate};
    }
    //计算日期内的销售出库金额
    private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) {
        List<SalesLedger> salesLedgers = defaultList(salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, toDate(startDate))
                .lt(SalesLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(salesLedgers, SalesLedger::getContractAmount);
        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) {
        List<PurchaseLedger> purchaseLedgers = defaultList(purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
                .ge(PurchaseLedger::getEntryDate, toDate(startDate))
                .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(purchaseLedgers, PurchaseLedger::getContractAmount);
        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>()
@@ -2339,6 +2372,7 @@
        return sumAmount(collections, AccountSalesCollection::getCollectionAmount);
    }
    //计算日期内的总付款金额
    private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList(
                new LambdaQueryWrapper<AccountPurchasePayment>()