2026-08-07 bb1d7eeda426e66393cf4403cb3ef082db696bd9
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -18,7 +18,11 @@
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
import com.ruoyi.approve.mapper.ApprovalTaskMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalTask;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
@@ -28,6 +32,7 @@
import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
import com.ruoyi.collaborativeApproval.pojo.Notice;
import com.ruoyi.common.enums.ApproveTypeEnum;
import com.ruoyi.common.enums.TypeEnums;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
@@ -97,6 +102,10 @@
    private final QualityInspectMapper qualityStatisticsMapper;
    private final ApproveProcessMapper approveProcessMapper;
    private final ApprovalTaskMapper approvalTaskMapper;
    private final ApprovalInstanceMapper approvalInstanceMapper;
    private final SysDeptMapper sysDeptMapper;
@@ -313,19 +322,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);
@@ -335,25 +360,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);
@@ -371,40 +393,58 @@
    @Override
    public List<ApproveProcess> todos() throws ParseException {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<ApproveProcess> approveProcesses = new ArrayList<>();
        // 查询旧审批表(approve_process)
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
                .ne(ApproveProcess::getApproveStatus, 2);
        // .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
        List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(approveProcesses)) {
            approveProcesses = new ArrayList<>();
        List<ApproveProcess> oldList = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(oldList)) {
            oldList.forEach(a -> {
                if (a.getApproveType() != null) {
                    a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
                }
            });
            approveProcesses.addAll(oldList);
        }
        approveProcesses.forEach(a -> {
            if (a.getApproveType() != null) {
                a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
        // 查询新审批表(approval_task + approval_instance)
        List<ApprovalTask> pendingTasks = approvalTaskMapper.selectList(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, loginUser.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        if (!CollectionUtils.isEmpty(pendingTasks)) {
            List<Long> instanceIds = pendingTasks.stream()
                    .map(ApprovalTask::getInstanceId)
                    .distinct()
                    .collect(Collectors.toList());
            List<ApprovalInstance> instances = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>()
                    .in(ApprovalInstance::getId, instanceIds)
                    .eq(ApprovalInstance::getDeleted, 0));
            Map<Long, ApprovalInstance> instanceMap = instances.stream()
                    .collect(Collectors.toMap(ApprovalInstance::getId, i -> i, (a, b) -> a));
            for (ApprovalTask task : pendingTasks) {
                ApprovalInstance instance = instanceMap.get(task.getInstanceId());
                if (instance == null) {
                    continue;
                }
                ApproveProcess ap = new ApproveProcess();
                ap.setId(instance.getId());
                ap.setApproveId(instance.getInstanceNo());
                ap.setApproveReason(instance.getTitle());
                ap.setApproveTime(instance.getApplyTime() != null ? java.sql.Timestamp.valueOf(instance.getApplyTime()) : null);
                ap.setApproveUserName(instance.getApplicantName());
                ap.setApproveDeptName(instance.getTemplateName());
                if (instance.getBusinessType() != null) {
                    ap.setApproveType(instance.getBusinessType().intValue());
                    ap.setApproveTypeName(TypeEnums.getLabelByValue(instance.getBusinessType()));
                }
                approveProcesses.add(ap);
            }
        });
        // // 查询未领用劳保记录
        // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //
        // LaborIssue laborIssue1 = new LaborIssue();
        // laborIssue1.setAdoptedDate(new Date());
        // laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
        // List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);
        // //staff_join_leave_record表被删除
        // if(!CollectionUtils.isEmpty(laborIssues)){
        // for (LaborIssue laborIssue : laborIssues) {
        // ApproveProcess approveProcess = new ApproveProcess();
        // approveProcess.setApproveId(laborIssue.getOrderNo());
        // approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
        // approveProcess.setApproveTime(laborIssue.getIssueDate());
        // approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" +
        // laborIssue.getDictName() + "超时未领取");
        // approveProcesses.add(approveProcess);
        // }
        // }
        }
        return approveProcesses;
    }
@@ -457,18 +497,23 @@
    @Override
    public Map<String, Object> approveAndDeviceTodos() {
        // 审批协同待办
        Long aLong = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
        // 旧审批表待办
        Long oldCount = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
                .eq(ApproveProcess::getApproveUserCurrentId, SecurityUtils.getUserId())
                .eq(ApproveProcess::getApproveDelete, 0)
                .in(ApproveProcess::getApproveStatus, 0, 1, 3));
        // 新审批表待办
        Long newCount = approvalTaskMapper.selectCount(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, SecurityUtils.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        // 设备报修待办
        Long aLong1 = deviceRepairMapper.selectCount(new LambdaQueryWrapper<DeviceRepair>()
                .eq(DeviceRepair::getStatus, 0)
                .eq(DeviceRepair::getRepairName, SecurityUtils.getLoginUser().getNickName()));
        return new HashMap<String, Object>() {
            {
                put("approveTodo", aLong);
                put("approveTodo", oldCount + newCount);
                put("deviceRepairTodo", aLong1);
            }
        };
@@ -652,36 +697,39 @@
    }
    @Override
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type) {
        LocalDate today = LocalDate.now();
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2: // 月
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3: // 季度
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                return new ArrayList<>();
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            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);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<PurchaseLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
                .ge("entry_date", startDate)
                .le("entry_date", endDate)
                // .ne("approval_status", 3)
                .groupBy("supplier_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
@@ -817,17 +865,81 @@
                majorDto.setRate("0.00");
            }
            // 查询各产品小类的采购数量
            Map<String, BigDecimal> purchaseDataMap = new java.util.HashMap<>();
            List<com.ruoyi.dto.MapDto> purchaseDistribution = homeMapper.selectPurchaseCategoryDistribution();
            if (purchaseDistribution != null) {
                for (com.ruoyi.dto.MapDto item : purchaseDistribution) {
                    if (item.getName() != null && item.getValue() != null) {
                        purchaseDataMap.put(item.getName(), new BigDecimal(item.getValue()));
                    }
                }
            }
            BigDecimal majorTotalChildrenValue = BigDecimal.ZERO;
            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
            for (Product minor : children) {
                ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
                minorDto.setName(minor.getProductName());
                minorDto.setValue("0");
                BigDecimal minorValue = purchaseDataMap.getOrDefault(minor.getProductName(), BigDecimal.ZERO);
                minorDto.setValue(minorValue.toString());
                minorDto.setRate("0.00");
                minorDto.setChildren(new ArrayList<>());
                minorDtos.add(minorDto);
                majorTotalChildrenValue = majorTotalChildrenValue.add(minorValue);
            }
            majorDto.setChildren(minorDtos);
            // 使用实际采购数据更新大类的 value
            if (majorTotalChildrenValue.compareTo(BigDecimal.ZERO) > 0) {
                majorDto.setValue(majorTotalChildrenValue.toString());
            }
            majorItems.add(majorDto);
        }
        // 计算大类占比
        BigDecimal totalAllValue = BigDecimal.ZERO;
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            try {
                totalAllValue = totalAllValue.add(new BigDecimal(m.getValue()));
            } catch (NumberFormatException ignored) {
            }
        }
        if (totalAllValue.compareTo(BigDecimal.ZERO) > 0) {
            for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
                try {
                    BigDecimal val = new BigDecimal(m.getValue());
                    m.setRate(val.divide(totalAllValue, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP)
                            .toString());
                } catch (NumberFormatException ignored) {
                }
            }
        }
        // 计算小类占比(在同大类内)
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            if (m.getChildren() == null || m.getChildren().isEmpty()) {
                continue;
            }
            BigDecimal parentVal = BigDecimal.ZERO;
            for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                try {
                    parentVal = parentVal.add(new BigDecimal(child.getValue()));
                } catch (NumberFormatException ignored) {
                }
            }
            if (parentVal.compareTo(BigDecimal.ZERO) > 0) {
                for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                    try {
                        BigDecimal childVal = new BigDecimal(child.getValue());
                        child.setRate(childVal.divide(parentVal, 4, RoundingMode.HALF_UP)
                                .multiply(new BigDecimal("100"))
                                .setScale(2, RoundingMode.HALF_UP)
                                .toString());
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
        }
        dto.setItems(majorItems);
@@ -835,38 +947,42 @@
    }
    @Override
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type) {
        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);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            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);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
                .isNotNull("customer_name")
                .between("entry_date", startDate, endDate)
                .groupBy("customer_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
        if (startDate != null && endDate != null) {
            queryWrapper.between("entry_date", startDate, endDate);
        }
        List<Map<String, Object>> maps = salesLedgerMapper.selectMaps(queryWrapper);
        List<CustomerContributionRankingDto> result = new ArrayList<>();
@@ -882,9 +998,13 @@
    }
    @Override
    public List<MapDto> productSalesAnalysis() {
        // 获取所有产品大类的销售额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
    public List<MapDto> productSalesAnalysis(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -919,9 +1039,13 @@
    }
    @Override
    public List<MapDto> rawMaterialPurchaseAmountRatio() {
        // 获取所有原材料的采购额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
    public List<MapDto> rawMaterialPurchaseAmountRatio(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -956,25 +1080,47 @@
    }
    @Override
    public List<MapDto> salesPurchaseStorageProductCount() {
    public List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonth = now.minusMonths(1);
        LocalDateTime lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        LocalDateTime currentMonthStart;
        LocalDateTime currentMonthEnd;
        LocalDateTime lastMonthStart;
        LocalDateTime lastMonthEnd;
        //  销售
        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, now);
        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
        //  采购
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, now);
        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
            LocalDate start = LocalDate.parse(startDate);
            LocalDate end = LocalDate.parse(endDate);
            currentMonthStart = start.atStartOfDay();
            currentMonthEnd = end.atTime(LocalTime.MAX);
            long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(start, end) + 1;
            LocalDate lastStart = start.minusDays(daysBetween);
            LocalDate lastEnd = start.minusDays(1);
            lastMonthStart = lastStart.atStartOfDay();
            lastMonthEnd = lastEnd.atTime(LocalTime.MAX);
        } else {
            currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            currentMonthEnd = now;
            LocalDateTime lastMonth = now.minusMonths(1);
            lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        }
        // 销售订单数(统计sales_ledger表)
        int currentSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, currentMonthStart)
                .le(SalesLedger::getEntryDate, currentMonthEnd)));
        int lastSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, lastMonthStart)
                .le(SalesLedger::getEntryDate, lastMonthEnd)));
        // 采购产品数
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, currentMonthEnd);
        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
        //  储存
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, now);
        // 储存产品数
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthEnd);
        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
        List<MapDto> list = new ArrayList<>();
        list.add(createMapDto("销售产品数", currentSales, lastSales));
        list.add(createMapDto("销售订单数", currentSales, lastSales));
        list.add(createMapDto("采购产品数", currentPurchase, lastPurchase));
        list.add(createMapDto("储存产品数", currentStorage, lastStorage));
@@ -1003,7 +1149,7 @@
    }
    @Override
    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
    public List<Map<String, Object>> productInOutAnalysis(Integer type, String startDateStr, String endDateStr) {
        String targetName;
        if (type == 1) {
            targetName = "原材料";
@@ -1028,8 +1174,17 @@
        LocalDate now = LocalDate.now();
        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
        String startDate = now.minusDays(6).toString();
        String endDate = now.toString();
        LocalDate start;
        LocalDate end;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            start = LocalDate.parse(startDateStr);
            end = LocalDate.parse(endDateStr);
        } else {
            end = now;
            start = now.minusDays(6);
        }
        String startDate = start.toString();
        String endDate = end.toString();
        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
                endDate + " 23:59:59");
@@ -1066,7 +1221,10 @@
    }
    @Override
    public List<MapDto> productTurnoverDays() {
    public List<MapDto> productTurnoverDays(String startDateStr, String endDateStr) {
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            return homeMapper.productTurnoverDaysByDate(startDateStr, endDateStr);
        }
        return homeMapper.productTurnoverDays();
    }
@@ -1737,11 +1895,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);
@@ -2010,13 +2165,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));
        }
        // 计算合格率
@@ -2057,14 +2210,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) {
@@ -2193,13 +2344,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<>();
@@ -2242,8 +2397,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);
    }
@@ -2251,11 +2409,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;