gongchunyi
2 天以前 940ceef4c4adf20c0b9da9270f1cd5ed58e14a4c
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -20,6 +20,7 @@
import com.ruoyi.collaborativeApproval.pojo.Notice;
import com.ruoyi.common.enums.ApproveTypeEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.dto.MapDto;
@@ -35,6 +36,7 @@
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.ProductWorkOrder;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
@@ -47,7 +49,9 @@
import com.ruoyi.purchase.pojo.PaymentRegistration;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityUnqualified;
import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
@@ -93,9 +97,6 @@
    private StockInventoryMapper stockInventoryMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
    private QualityInspectMapper qualityStatisticsMapper;
    @Autowired
@@ -112,32 +113,36 @@
    @Autowired
    private NoticeMapper noticeMapper;
    @Autowired
    private ProductOrderMapper productOrderMapper;
    @Autowired
    private ProductWorkOrderMapper productWorkOrderMapper;
    @Autowired
    private ProductModelMapper productModelMapper;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private StockUtils stockUtils;
    @Autowired
    private StaffOnJobMapper staffOnJobMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private SupplierManageMapper supplierManageMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserDeptMapper sysUserDeptMapper;
    @Autowired
    private HomeMapper homeMapper;
    @Autowired
    private ProductionProductOutputMapper productionProductOutputMapper;
    @Autowired
    private QualityInspectMapper qualityInspectMapper;
    @Autowired
    private QualityUnqualifiedMapper qualityUnqualifiedMapper;
    @Override
    public HomeBusinessDto business() {
@@ -263,8 +268,8 @@
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 修改:使用
                                                                                              // todayContractAmount 而不是
                                                                                              // yesterdayContractAmount
        // todayContractAmount 而不是
        // yesterdayContractAmount
        // 日环比
        String chain = "";
        if (subtract1.compareTo(BigDecimal.ZERO) == 0
@@ -458,8 +463,8 @@
        }
        // 应收
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
        // .ge(SalesLedger::getEntryDate, startDate)
        // .lt(SalesLedger::getEntryDate, endDate)
                // .ge(SalesLedger::getEntryDate, startDate)
                // .lt(SalesLedger::getEntryDate, endDate)
        );
        // BigDecimal receivableMoney =
        // salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO,
@@ -468,8 +473,8 @@
        // 应付
        List<PurchaseLedger> procurementRecords = purchaseLedgerMapper
                .selectList(new LambdaQueryWrapper<PurchaseLedger>()
                // .ge(PurchaseLedger::getEntryDate, startDate)
                // .lt(PurchaseLedger::getEntryDate, endDate)
                        // .ge(PurchaseLedger::getEntryDate, startDate)
                        // .lt(PurchaseLedger::getEntryDate, endDate)
                );
        // BigDecimal payableMoney =
        // procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO,
@@ -477,8 +482,8 @@
        BigDecimal payableMoney = sumAmount(procurementRecords, PurchaseLedger::getContractAmount);
        // 预收
        List<ReceiptPayment> receiptPayments = receiptPaymentMapper.selectList(new LambdaQueryWrapper<ReceiptPayment>()
        // .ge(ReceiptPayment::getReceiptPaymentDate, startDate)
        // .lt(ReceiptPayment::getReceiptPaymentDate, endDate)
                // .ge(ReceiptPayment::getReceiptPaymentDate, startDate)
                // .lt(ReceiptPayment::getReceiptPaymentDate, endDate)
        );
        // BigDecimal advanceMoney =
        // receiptPayments.stream().map(ReceiptPayment::getReceiptPaymentAmount).reduce(BigDecimal.ZERO,
@@ -487,8 +492,8 @@
        // 预付
        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper
                .selectList(new LambdaQueryWrapper<PaymentRegistration>()
                // .ge(PaymentRegistration::getPaymentDate, startDate)
                // .lt(PaymentRegistration::getPaymentDate, endDate)
                        // .ge(PaymentRegistration::getPaymentDate, startDate)
                        // .lt(PaymentRegistration::getPaymentDate, endDate)
                );
        // BigDecimal prepayMoney =
        // paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO,
@@ -552,7 +557,7 @@
        productionProgressDto.setCompletedOrderDetails(productOrderDtos);
        long totalCount = productOrderDtos.size();
        long count = productOrderDtos.stream().filter(
                productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
                        productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
                .count();
        long count2 = productOrderDtos.stream()
                .filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(BigDecimal.ZERO) == 0)
@@ -627,9 +632,9 @@
        for (SysDept dept : depts) {
            if ("0".equals(dept.getStatus()) && "0".equals(dept.getDelFlag())) {
                Long count = sysUserDeptMapper.selectCount(new LambdaQueryWrapper<SysUserDept>()
                        .eq(SysUserDept::getDeptId, dept.getDeptId()));
                if (count > 0) {
                Long count = staffOnJobMapper.selectCount(new QueryWrapper<StaffOnJob>()
                        .eq("sys_dept_id", dept.getDeptId()).eq("staff_state", 1));
                if (count != null && count > 0) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("name", dept.getDeptName());
                    map.put("count", count);
@@ -689,12 +694,11 @@
    }
    private Long countStaff(LocalDateTime dateTime) {
        Long sysUserCount = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getDelFlag, "0")
                .le(SysUser::getCreateTime, dateTime));
        Long staffCountItem = staffOnJobMapper.selectCount(new LambdaQueryWrapper<StaffOnJob>()
                .isNotNull(StaffOnJob::getStaffState)
                .eq(StaffOnJob::getStaffState, 1)
                .le(StaffOnJob::getCreateTime, dateTime));
        return sysUserCount + staffCountItem;
        return staffCountItem;
    }
    private Long countCustomers(LocalDateTime dateTime) {
@@ -1788,10 +1792,10 @@
    }
    @Autowired
    private com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    @Override
    public List<MapDto> productionAccountingAnalysis(Integer type) {
    public List<ProductionAccountingDto> productionAccountingAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = today;
@@ -1813,8 +1817,9 @@
                break;
        }
        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String endStr = endDate.plusDays(1).atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<Map<String, Object>> wagesList = salesLedgerProductionAccountingMapper.selectDailyWagesStats(startStr,
                endStr);
@@ -1823,16 +1828,17 @@
            return new ArrayList<>();
        }
        List<MapDto> result = new ArrayList<>();
        List<ProductionAccountingDto> result = new ArrayList<>();
        for (Map<String, Object> map : wagesList) {
            MapDto dto = new MapDto();
            dto.setName((String) map.get("date"));
            BigDecimal wages = (BigDecimal) map.get("wages");
            dto.setValue(wages != null ? wages.toString() : "0");
            ProductionAccountingDto dto = new ProductionAccountingDto();
            dto.setDateStr(map.get("dateStr").toString());
            dto.setNumberOfCompleted(((BigDecimal) map.get("numberOfCompleted")).intValue());
            dto.setAmount(map.get("amount") != null ? (BigDecimal) map.get("amount") : BigDecimal.ZERO);
            dto.setPassRate(map.get("passRate") != null ? (BigDecimal) map.get("passRate") : BigDecimal.ZERO);
            result.add(dto);
        }
        result.sort(Comparator.comparing(MapDto::getName));
        result.sort(Comparator.comparing(ProductionAccountingDto::getDateStr));
        return result;
    }
@@ -1886,4 +1892,524 @@
        return dto;
    }
}
    @Override
    public List<QualityQualifiedAnalysisDto> rawMaterialDetection(Integer type) {
        return commonDetection(type, 0);
    }
    @Override
    public List<QualityQualifiedAnalysisDto> processDetection(Integer type) {
        return commonDetection(type, 1);
    }
    @Override
    public List<QualityQualifiedAnalysisDto> factoryDetection(Integer type) {
        return commonDetection(type, 2);
    }
    private List<QualityQualifiedAnalysisDto> commonDetection(Integer type, Integer inspectType) {
        LocalDate[] range = calcDateRange(type);
        LocalDate startDate = range[0];
        LocalDate endDate = range[1];
        String startStr = startDate.atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String endStr = endDate.atTime(LocalTime.MAX)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<QualityInspect> list = qualityInspectMapper.selectList(
                new LambdaQueryWrapper<QualityInspect>()
                        .eq(QualityInspect::getInspectType, inspectType)
                        .eq(QualityInspect::getInspectState, 1)
                        .ge(QualityInspect::getCheckTime, startStr)
                        .le(QualityInspect::getCheckTime, endStr));
        return buildQualifiedAnalysis(list);
    }
    private LocalDate[] calcDateRange(Integer type) {
        LocalDate today = LocalDate.now();
        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: // 季度
                int currentMonth = today.getMonthValue();
                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
                startDate = LocalDate.of(today.getYear(), startMonth, 1);
                endDate = LocalDate.of(today.getYear(), startMonth + 2, 1)
                        .with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
        }
        return new LocalDate[]{startDate, endDate};
    }
    private List<QualityQualifiedAnalysisDto> buildQualifiedAnalysis(List<QualityInspect> list) {
        List<QualityQualifiedAnalysisDto> result = new ArrayList<>();
        QualityQualifiedAnalysisDto dto = new QualityQualifiedAnalysisDto();
        if (CollectionUtils.isEmpty(list)) {
            dto.setQualifiedCount(0);
            dto.setUnqualifiedCount(0);
            dto.setQualifiedRate(BigDecimal.ZERO.setScale(2));
            dto.setUnqualifiedRate(BigDecimal.ZERO.setScale(2));
            result.add(dto);
            return result;
        }
        BigDecimal qualifiedCount = BigDecimal.ZERO;
        BigDecimal unqualifiedCount = BigDecimal.ZERO;
        for (QualityInspect item : list) {
            if ("合格".equals(item.getCheckResult())) {
                qualifiedCount = qualifiedCount.add(item.getQuantity());
            } else {
                unqualifiedCount = unqualifiedCount.add(item.getQuantity());
            }
        }
        BigDecimal totalCount = qualifiedCount.add(unqualifiedCount);
        dto.setQualifiedCount(qualifiedCount.intValue());
        dto.setUnqualifiedCount(unqualifiedCount.intValue());
        if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
            dto.setQualifiedRate(BigDecimal.ZERO.setScale(2));
            dto.setUnqualifiedRate(BigDecimal.ZERO.setScale(2));
            result.add(dto);
            return result;
        }
        BigDecimal hundred = BigDecimal.valueOf(100);
        dto.setQualifiedRate(qualifiedCount.divide(totalCount, 4, RoundingMode.HALF_UP)
                .multiply(hundred)
                .setScale(2, RoundingMode.HALF_UP));
        dto.setUnqualifiedRate(unqualifiedCount.divide(totalCount, 4, RoundingMode.HALF_UP)
                .multiply(hundred)
                .setScale(2, RoundingMode.HALF_UP));
        result.add(dto);
        return result;
    }
    @Override
    public QualityInspectionCountDto qualityInspectionCount() {
        // 获取今天的开始和结束日期,包含时分秒
        LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime todayEnd = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).withNano(0);
        // 获取前一天的开始和结束日期,包含时分秒
        LocalDateTime prevStart = todayStart.minusDays(1);
        LocalDateTime prevEnd = todayEnd.minusDays(1);
        // 查询出截止今日的总检验数
        List<QualityInspect> todayList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                // .eq(QualityInspect::getInspectState, 1)
                .le(QualityInspect::getCheckTime, todayEnd));
        // 查询出截止前一天的总检验数
        List<QualityInspect> prevList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                // .eq(QualityInspect::getInspectState, 1)
                .le(QualityInspect::getCheckTime, prevEnd));
        // 计算今日的总检验数
        BigDecimal todayCount = todayList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算前一天的总检验数
        BigDecimal prevCount = prevList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算今日相对昨天的一个总比增长
        BigDecimal growthRate = calcGrowthRate(todayCount, prevCount);
        // 计算今天的待完成数量
        List<QualityInspect> todayPendingList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .eq(QualityInspect::getInspectState, 0)
                .ge(QualityInspect::getCheckTime, todayStart)
                .le(QualityInspect::getCheckTime, todayEnd));
        // 计算前一天的待完成数量
        List<QualityInspect> prevPendingList = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .eq(QualityInspect::getInspectState, 0)
                .ge(QualityInspect::getCheckTime, prevStart)
                .le(QualityInspect::getCheckTime, prevEnd));
        // 计算今天的待完成数量
        BigDecimal todayPendingCount = todayPendingList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算前一天的待完成数量
        BigDecimal prevPendingCount = prevPendingList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算今天的待完成数量相对昨天的一个同比增长
        BigDecimal todayPendingCountGrowthRate = calcGrowthRate(todayPendingCount, prevPendingCount);
        // 计算今天的已完成数量
        List<QualityInspect> todayCompletedList = qualityInspectMapper
                .selectList(new LambdaQueryWrapper<QualityInspect>()
                        .eq(QualityInspect::getInspectState, 1)
                        .ge(QualityInspect::getCheckTime, todayStart)
                        .le(QualityInspect::getCheckTime, todayEnd));
        // 计算前一天的已完成数量
        List<QualityInspect> prevCompletedList = qualityInspectMapper
                .selectList(new LambdaQueryWrapper<QualityInspect>()
                        .eq(QualityInspect::getInspectState, 1)
                        .ge(QualityInspect::getCheckTime, prevStart)
                        .le(QualityInspect::getCheckTime, prevEnd));
        // 计算今天的已完成数量
        BigDecimal todayCompletedCount = todayCompletedList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算前一天的已完成数量
        BigDecimal prevCompletedCount = prevCompletedList.stream()
                .map(QualityInspect::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 计算今天的已完成数量相对昨天的一个同比增长
        BigDecimal todayCompletedCountGrowthRate = calcGrowthRate(todayCompletedCount, prevCompletedCount);
        QualityInspectionCountDto dto = new QualityInspectionCountDto();
        dto.setTotalCount(todayCount);
        dto.setTotalCountGrowthRate(growthRate);
        dto.setTodayPendingCount(todayPendingCount);
        dto.setTodayPendingCountGrowthRate(todayPendingCountGrowthRate);
        dto.setTodayCompletedCount(todayCompletedCount);
        dto.setTodayCompletedCountGrowthRate(todayCompletedCountGrowthRate);
        return dto;
    }
    private BigDecimal calcGrowthRate(BigDecimal today, BigDecimal prev) {
        if (prev == null || prev.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO.setScale(2);
        }
        return today.subtract(prev)
                .divide(prev, 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100))
                .setScale(2, RoundingMode.HALF_UP);
    }
    @Override
    public NonComplianceWarningDto nonComplianceWarning() {
        // 近七天时间区间
        LocalDateTime[] range = lastSevenDaysRange();
        LocalDateTime startTime = range[0];
        LocalDateTime endTime = range[1];
        // 查询近七天已处理不合格数据
        List<QualityUnqualified> list = qualityUnqualifiedMapper.selectList(
                new LambdaQueryWrapper<QualityUnqualified>()
                        .eq(QualityUnqualified::getInspectState, 1)
                        .ge(QualityUnqualified::getCheckTime, startTime)
                        .le(QualityUnqualified::getCheckTime, endTime));
        NonComplianceWarningDto dto = new NonComplianceWarningDto();
        if (CollectionUtils.isEmpty(list)) {
            dto.setRawMaterialRatio(BigDecimal.ZERO);
            dto.setSemiFinishedProductRatio(BigDecimal.ZERO);
            dto.setFinishedProductRatio(BigDecimal.ZERO);
            dto.setChildren(new ArrayList<>());
            return dto;
        }
        // 查询所有产品
        List<Product> products = productMapper.selectList(null);
        Map<Long, Product> productMap = products.stream()
                .collect(Collectors.toMap(Product::getId, p -> p));
        BigDecimal rawMaterialCount = BigDecimal.ZERO;
        BigDecimal semiFinishedCount = BigDecimal.ZERO;
        BigDecimal finishedCount = BigDecimal.ZERO;
        List<NonComplianceWarningDto.Item> children = new ArrayList<>();
        for (QualityUnqualified item : list) {
            BigDecimal quantity = item.getQuantity();
            Long productId = item.getProductId();
            Product product = productMap.get(productId);
            if (product == null) {
                continue;
            }
            // 找到产品大类
            Product parent = product.getParentId() == null
                    ? product
                    : productMap.get(product.getParentId());
            if (parent == null) {
                continue;
            }
            switch (parent.getProductName()) {
                case "原材料":
                    rawMaterialCount = rawMaterialCount.add(quantity);
                    break;
                case "半成品":
                    semiFinishedCount = semiFinishedCount.add(quantity);
                    break;
                case "成品":
                    finishedCount = finishedCount.add(quantity);
                    break;
                default:
                    break;
            }
            // 组装明细
            NonComplianceWarningDto.Item child = new NonComplianceWarningDto.Item();
            // child.setProductTitle(item.getProductName());
            child.setProductTitle(parent.getProductName());
            child.setDescription(item.getDefectivePhenomena());
            child.setDate(formatDate(item.getCheckTime()));
            children.add(child);
        }
        BigDecimal total = rawMaterialCount
                .add(semiFinishedCount)
                .add(finishedCount);
        dto.setRawMaterialRatio(calcRate(rawMaterialCount, total));
        dto.setSemiFinishedProductRatio(calcRate(semiFinishedCount, total));
        dto.setFinishedProductRatio(calcRate(finishedCount, total));
        dto.setChildren(children);
        return dto;
    }
    private BigDecimal calcRate(BigDecimal part, BigDecimal total) {
        if (total == null || total.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO.setScale(2);
        }
        return part.divide(total, 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100))
                .setScale(2, RoundingMode.HALF_UP);
    }
    public static String formatDate(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }
    /**
     * 获取近七天的时间区间(包含今天)
     */
    public static LocalDateTime[] lastSevenDaysRange() {
        LocalDate today = LocalDate.now();
        LocalDateTime startTime = today.minusDays(6).atStartOfDay();
        LocalDateTime endTime = today.atTime(23, 59, 59);
        return new LocalDateTime[]{startTime, endTime};
    }
    @Override
    public List<CompletedInspectionCountDto> completedInspectionCount() {
        // 近七天时间区间
        LocalDateTime[] range = lastSevenDaysRange();
        LocalDateTime startTime = range[0];
        LocalDateTime endTime = range[1];
        // 查询近七天已完成的检验数据
        List<QualityInspect> list = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .eq(QualityInspect::getInspectState, 1)
                .ge(QualityInspect::getCheckTime, startTime)
                .le(QualityInspect::getCheckTime, endTime));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
        Map<String, CompletedInspectionCountDto> resultMap = new LinkedHashMap<>();
        for (int i = 6; i >= 0; i--) {
            LocalDate date = LocalDate.now().minusDays(i);
            String dateStr = date.format(formatter);
            CompletedInspectionCountDto dto = new CompletedInspectionCountDto();
            dto.setDateStr(dateStr);
            dto.setQualifiedCount(BigDecimal.ZERO);
            dto.setUnqualifiedCount(BigDecimal.ZERO);
            dto.setPassRate(BigDecimal.ZERO);
            resultMap.put(dateStr, dto);
        }
        // 累加合格 / 不合格数量
        for (QualityInspect item : list) {
            String dateStr = item.getCheckTime()
                    .toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate()
                    .format(formatter);
            CompletedInspectionCountDto dto = resultMap.get(dateStr);
            if (dto == null) {
                continue;
            }
            BigDecimal quantity = item.getQuantity();
            if ("合格".equals(item.getCheckResult())) {
                dto.setQualifiedCount(dto.getQualifiedCount().add(quantity));
            } else {
                dto.setUnqualifiedCount(dto.getUnqualifiedCount().add(quantity));
            }
        }
        // 计算合格率
        for (CompletedInspectionCountDto dto : resultMap.values()) {
            BigDecimal total = dto.getQualifiedCount().add(dto.getUnqualifiedCount());
            dto.setPassRate(calcRate(dto.getQualifiedCount(), total));
        }
        return new ArrayList<>(resultMap.values());
    }
    @Override
    public List<UnqualifiedProductRankDto> unqualifiedProductRanking() {
        List<QualityInspect> list = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .eq(QualityInspect::getInspectState, 1));
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        Map<Long, String> productNameMap = productMapper.selectList(null)
                .stream()
                .collect(Collectors.toMap(Product::getId, Product::getProductName));
        Map<Long, List<QualityInspect>> groupMap = list.stream()
                .collect(Collectors.groupingBy(QualityInspect::getProductId));
        List<UnqualifiedProductRankDto> resultList = new ArrayList<>();
        for (Map.Entry<Long, List<QualityInspect>> entry : groupMap.entrySet()) {
            Long productId = entry.getKey();
            List<QualityInspect> items = entry.getValue();
            BigDecimal totalCount = BigDecimal.ZERO;
            BigDecimal qualifiedCount = BigDecimal.ZERO;
            BigDecimal unqualifiedCount = BigDecimal.ZERO;
            for (QualityInspect item : items) {
                BigDecimal qty = item.getQuantity();
                totalCount = totalCount.add(qty);
                if ("合格".equals(item.getCheckResult())) {
                    qualifiedCount = qualifiedCount.add(qty);
                } else {
                    unqualifiedCount = unqualifiedCount.add(qty);
                }
            }
            if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            BigDecimal passRate = qualifiedCount
                    .divide(totalCount, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP);
            UnqualifiedProductRankDto dto = new UnqualifiedProductRankDto();
            dto.setProductName(productNameMap.get(productId));
            dto.setTotalCount(totalCount);
            dto.setCompletedCount(qualifiedCount);
            dto.setPassRate(passRate);
            resultList.add(dto);
        }
        resultList.sort(Comparator.comparing(UnqualifiedProductRankDto::getPassRate));
        return resultList.stream().limit(5).collect(Collectors.toList());
    }
    @Override
    public List<MapDto> unqualifiedProductProcessingAnalysis() {
        List<QualityUnqualified> list = qualityUnqualifiedMapper.selectList(null);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        //  统计每种处理结果的数量
        Map<String, BigDecimal> countMap = new HashMap<>();
        for (QualityUnqualified item : list) {
            if (StringUtils.isEmpty(item.getDealResult()) || item.getQuantity() == null) {
                continue;
            }
            countMap.merge(item.getDealResult(), item.getQuantity(), BigDecimal::add);
        }
        if (countMap.isEmpty()) {
            return new ArrayList<>();
        }
        //  计算总数
        BigDecimal totalCount = countMap.values()
                .stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalCount.compareTo(BigDecimal.ZERO) == 0) {
            return new ArrayList<>();
        }
        //  按数量倒序排序
        List<Map.Entry<String, BigDecimal>> sortedList = countMap.entrySet()
                .stream()
                .sorted((a, b) -> b.getValue().compareTo(a.getValue()))
                .collect(Collectors.toList());
        List<MapDto> result = new ArrayList<>();
        int limit = Math.min(3, sortedList.size());
        BigDecimal otherCount = BigDecimal.ZERO;
        for (int i = 0; i < sortedList.size(); i++) {
            Map.Entry<String, BigDecimal> entry = sortedList.get(i);
            if (i < limit) {
                MapDto dto = new MapDto();
                dto.setName(entry.getKey());
                dto.setValue(entry.getValue().setScale(2, RoundingMode.HALF_UP).toPlainString());
                dto.setRate(calcRate(entry.getValue(), totalCount).toString());
                result.add(dto);
            } else {
                otherCount = otherCount.add(entry.getValue());
            }
        }
        if (otherCount.compareTo(BigDecimal.ZERO) > 0) {
            MapDto otherDto = new MapDto();
            otherDto.setName("其他");
            otherDto.setValue(otherCount.setScale(2, RoundingMode.HALF_UP).toPlainString());
            otherDto.setRate(calcRate(otherCount, totalCount).toString());
            result.add(otherDto);
        }
        return result;
    }
}