gongchunyi
21 小时以前 579f5ecc15a375651956485f6086c4b8e1eef088
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -1,39 +1,47 @@
package com.ruoyi.home.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.mapper.AccountIncomeMapper;
import com.ruoyi.account.pojo.AccountExpense;
import com.ruoyi.home.mapper.HomeMapper;
import com.ruoyi.account.mapper.AccountExpenseMapper;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
import com.ruoyi.collaborativeApproval.pojo.Notice;
import com.ruoyi.common.enums.ApproveTypeEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
import com.ruoyi.device.mapper.DeviceRepairMapper;
import com.ruoyi.device.pojo.DeviceMaintenance;
import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.dto.MapDto;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.home.dto.*;
import com.ruoyi.home.service.HomeService;
import com.ruoyi.lavorissue.mapper.LavorIssueMapper;
import com.ruoyi.lavorissue.pojo.LaborIssue;
import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.CustomStorage;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.dto.ProductOrderDto;
import com.ruoyi.production.dto.ProductWorkOrderDto;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.mapper.ProductOrderMapper;
import com.ruoyi.production.mapper.ProductWorkOrderMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.ProductOrder;
import com.ruoyi.production.mapper.ProductionProductInputMapper;
import com.ruoyi.production.mapper.ProductionProductOutputMapper;
import com.ruoyi.production.pojo.ProductWorkOrder;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.domain.SysUserDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PaymentRegistration;
@@ -46,6 +54,9 @@
import com.ruoyi.sales.pojo.ReceiptPayment;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -54,12 +65,11 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
@@ -80,13 +90,10 @@
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private ProcurementRecordOutMapper procurementRecordOutMapper;
    private StockInventoryMapper stockInventoryMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
    private CustomStorageMapper customStorageMapper;
    @Autowired
    private QualityInspectMapper qualityStatisticsMapper;
@@ -101,9 +108,6 @@
    private PaymentRegistrationMapper paymentRegistrationMapper;
    @Autowired
    private LavorIssueMapper lavorIssueMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
@@ -111,9 +115,29 @@
    @Autowired
    private ProductOrderMapper productOrderMapper;
    @Autowired
    private ProductionProductMainMapper productionProductMainMapper;
    @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;
    @Override
    public HomeBusinessDto business() {
@@ -123,18 +147,20 @@
        YearMonth currentMonth = YearMonth.from(now);
        // 创建LambdaQueryWrapper
        LambdaQueryWrapper<SalesLedger> salesLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        salesLedgerLambdaQueryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 大于等于本月第一天
        salesLedgerLambdaQueryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay()) // 大于等于本月第一天
                .lt(SalesLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 小于下月第一天
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(salesLedgerLambdaQueryWrapper);
        if(!CollectionUtils.isEmpty(salesLedgers)){
        if (!CollectionUtils.isEmpty(salesLedgers)) {
            // 合计合同金额
            BigDecimal contractAmount = salesLedgers.stream().map(SalesLedger::getContractAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapper = new LambdaQueryWrapper<SalesLedgerProduct>();
            salesLedgerProductMapperLambdaQueryWrapper.eq(SalesLedgerProduct::getType, 1)
                    .in(SalesLedgerProduct::getSalesLedgerId, salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList()));
            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(salesLedgerProductMapperLambdaQueryWrapper);
                    .in(SalesLedgerProduct::getSalesLedgerId,
                            salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList()));
            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper
                    .selectList(salesLedgerProductMapperLambdaQueryWrapper);
            // 未开票金额
            BigDecimal noInvoiceAmountTotal = salesLedgerProducts.stream().map(SalesLedgerProduct::getNoInvoiceAmount)
                    .filter(Objects::nonNull)
@@ -144,15 +170,17 @@
        }
        // 创建LambdaQueryWrapper
        LambdaQueryWrapper<PurchaseLedger> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(PurchaseLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 大于等于本月第一天
        queryWrapper.ge(PurchaseLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay()) // 大于等于本月第一天
                .lt(PurchaseLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 小于下月第一天
        // 执行查询并计算总和
        List<PurchaseLedger> purchaseLedgers = purchaseLedgerMapper.selectList(queryWrapper);
        if(!CollectionUtils.isEmpty(purchaseLedgers)){
        if (!CollectionUtils.isEmpty(purchaseLedgers)) {
            LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapperCopy = new LambdaQueryWrapper<SalesLedgerProduct>();
            salesLedgerProductMapperLambdaQueryWrapperCopy.eq(SalesLedgerProduct::getType, 2)
                    .in(SalesLedgerProduct::getSalesLedgerId, purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
            List<SalesLedgerProduct> salesLedgerProductsCopy = salesLedgerProductMapper.selectList(salesLedgerProductMapperLambdaQueryWrapperCopy);
                    .in(SalesLedgerProduct::getSalesLedgerId,
                            purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
            List<SalesLedgerProduct> salesLedgerProductsCopy = salesLedgerProductMapper
                    .selectList(salesLedgerProductMapperLambdaQueryWrapperCopy);
            // 合计合同金额
            BigDecimal receiveAmount = purchaseLedgers.stream()
                    .map(PurchaseLedger::getContractAmount)
@@ -164,37 +192,16 @@
                    .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(unReceiptPaymentAmount.setScale(2, RoundingMode.HALF_UP).toString());
        }
        // 统计库存
        List<ProcurementRecordStorage> procurementRecordStorages = procurementRecordStorageMapper.selectList(null);
        BigDecimal stockAmount = procurementRecordStorages.stream()
                .map(ProcurementRecordStorage::getInboundNum)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 自定义库存
        List<CustomStorage> customStorages = customStorageMapper.selectList(null);
        BigDecimal customStockAmount = customStorages.stream()
                .map(CustomStorage::getInboundNum)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(null);
        BigDecimal outboundAmount = procurementRecordOuts.stream()
                .map(ProcurementRecordOut::getInboundNum)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal stock = stockAmount.add(customStockAmount).subtract(outboundAmount);
        BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal();
        homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString());
        // 获取当天入库数量
        LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordStorageLambdaQueryWrapper.ge(ProcurementRecordStorage::getCreateTime, now)  // 大于等于当天
                .lt(ProcurementRecordStorage::getCreateTime, now.plusDays(1)); // 小于明天
        List<ProcurementRecordStorage> procurementRecordStorages1 = procurementRecordStorageMapper.selectList(procurementRecordStorageLambdaQueryWrapper);
        BigDecimal stockAmount1 = procurementRecordStorages1.stream()
                .map(ProcurementRecordStorage::getInboundNum)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        homeBusinessDto.setInventoryNum(stock.setScale(2, RoundingMode.HALF_UP).toString());
        homeBusinessDto.setTodayInventoryNum(stockAmount1.setScale(2, RoundingMode.HALF_UP).toString());
        BigDecimal bigDecimal = stockInventoryMapper.selectTotalByDate(LocalDate.now());
        homeBusinessDto.setTodayInventoryNum(bigDecimal.setScale(2, RoundingMode.HALF_UP).toString());
        return homeBusinessDto;
    }
@@ -215,7 +222,7 @@
        // 获取本周周日
        LocalDate endOfWeek = today.with(DayOfWeek.SUNDAY);
        List<SalesLedger> salesLedgers1 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, startOfWeek)  // 大于等于本周周一
                .ge(SalesLedger::getEntryDate, startOfWeek) // 大于等于本周周一
                .lt(SalesLedger::getEntryDate, endOfWeek.plusDays(1))); // 修改:使用 lt 并加上一天来包含周日
        BigDecimal weekContractAmount = salesLedgers1.stream().map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
@@ -224,7 +231,7 @@
        LocalDate lastYearStartOfWeek = today.minusYears(1).with(DayOfWeek.MONDAY);
        LocalDate lastYearEndOfWeek = today.minusYears(1).with(DayOfWeek.SUNDAY);
        List<SalesLedger> salesLedgers2 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, lastYearStartOfWeek)  // 大于等于去年本周周一
                .ge(SalesLedger::getEntryDate, lastYearStartOfWeek) // 大于等于去年本周周一
                .lt(SalesLedger::getEntryDate, lastYearEndOfWeek.plusDays(1))); // 修改:使用 lt 并加上一天来包含周日
        BigDecimal lastYearWeekContractAmount = salesLedgers2.stream().map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
@@ -232,10 +239,11 @@
        BigDecimal subtract = weekContractAmount.subtract(lastYearWeekContractAmount);
        String weekYny = "";
        // 周同比
        if(subtract.compareTo(BigDecimal.ZERO) == 0 || lastYearWeekContractAmount.compareTo(BigDecimal.ZERO) == 0){
        if (subtract.compareTo(BigDecimal.ZERO) == 0 || lastYearWeekContractAmount.compareTo(BigDecimal.ZERO) == 0) {
            weekYny = "0.00";
        }else{
            weekYny = String.format("%.2f", subtract.divide(lastYearWeekContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
        } else {
            weekYny = String.format("%.2f", subtract.divide(lastYearWeekContractAmount, 2, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100")));
        }
@@ -243,26 +251,29 @@
        LocalDate yesterday = today.minusDays(1);
        LocalDate plusDays = today.plusDays(1);
        List<SalesLedger> salesLedgers3 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, today)  // 大于等于今天
                .ge(SalesLedger::getEntryDate, today) // 大于等于今天
                .lt(SalesLedger::getEntryDate, plusDays)); // 修改:使用 lt 并加上一天来包含当天
        BigDecimal todayContractAmount = salesLedgers3.stream().map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        List<SalesLedger> salesLedgers4 = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, yesterday)  // 大于等于昨天
                .ge(SalesLedger::getEntryDate, yesterday) // 大于等于昨天
                .lt(SalesLedger::getEntryDate, today)); // 修改:使用 lt 而不是 gt
        BigDecimal lastYearYesterdayContractAmount = salesLedgers4.stream().map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 修改:使用 todayContractAmount 而不是 yesterdayContractAmount
        BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 修改:使用
                                                                                              // todayContractAmount 而不是
                                                                                              // yesterdayContractAmount
        // 日环比
        String chain = "";
        if(subtract1.compareTo(BigDecimal.ZERO) == 0 || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0){
        if (subtract1.compareTo(BigDecimal.ZERO) == 0
                || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0) {
            chain = "0.00";
        }else{
            chain = String.format("%.2f", subtract1.divide(lastYearYesterdayContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
        } else {
            chain = String.format("%.2f", subtract1.divide(lastYearYesterdayContractAmount, 2, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100")));
        }
        AnalysisCustomerContractAmountsDto analysisCustomerContractAmountsDto = new AnalysisCustomerContractAmountsDto();
        // 修改:将合同金额保留两位小数
@@ -270,25 +281,26 @@
        analysisCustomerContractAmountsDto.setYny(weekYny);
        analysisCustomerContractAmountsDto.setChain(chain);
        Map<String, BigDecimal> collect = salesLedgers.stream().collect(Collectors.groupingBy(SalesLedger::getCustomerName, Collectors.reducing(BigDecimal.ZERO,
                SalesLedger::getContractAmount, BigDecimal::add)));
        Map<String, BigDecimal> collect = salesLedgers.stream()
                .collect(Collectors.groupingBy(SalesLedger::getCustomerName, Collectors.reducing(BigDecimal.ZERO,
                        SalesLedger::getContractAmount, BigDecimal::add)));
        List<MapDto> mapDtos = new ArrayList<>();
        collect.forEach((k,v)->{
        collect.forEach((k, v) -> {
            MapDto mapDto = new MapDto();
            mapDto.setName(k);
            // 修改:将金额值保留两位小数
            mapDto.setValue(v.setScale(2, RoundingMode.HALF_UP).toString());
            if(contractAmount.compareTo(new BigDecimal(0)) == 0){
            if (contractAmount.compareTo(new BigDecimal(0)) == 0) {
                mapDto.setRate("0");
            }else{
                mapDto.setRate(String.format("%.2f", v.divide(contractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))));
            } else {
                mapDto.setRate(String.format("%.2f",
                        v.divide(contractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))));
            }
            mapDtos.add(mapDto);
        });
        analysisCustomerContractAmountsDto.setItem(mapDtos);
        return analysisCustomerContractAmountsDto;
    }
    @Override
    public QualityStatisticsDto qualityStatistics() {
@@ -326,7 +338,7 @@
                    .filter(inspect -> inspect.getInspectType().equals(1))
                    .map(QualityInspect::getQuantity)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            processNum= processNum.add(reduce1);
            processNum = processNum.add(reduce1);
            BigDecimal reduce2 = monthInspects.stream()
                    .filter(inspect -> inspect.getInspectType().equals(2))
                    .map(QualityInspect::getQuantity)
@@ -378,34 +390,42 @@
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
                .ne(ApproveProcess::getApproveStatus, 2)
                .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
                .ne(ApproveProcess::getApproveStatus, 2);
        // .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
        List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if(CollectionUtils.isEmpty(approveProcesses)){
        if (CollectionUtils.isEmpty(approveProcesses)) {
            approveProcesses = new ArrayList<>();
        }
        // 查询未领用劳保记录
        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);
        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);
        approveProcesses.forEach(a -> {
            if (a.getApproveType() != null) {
                a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
            }
        }
        });
        // // 查询未领用劳保记录
        // 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;
    }
    /**
     *
     * @param type 1-周 2-月 3-季度
     * @return
     */
@@ -414,7 +434,7 @@
        LocalDate today = LocalDate.now();
        LocalDate startDate = null;
        LocalDate endDate = null;
        switch (type){
        switch (type) {
            case 1:
                // 获取本周周一
                startDate = today.with(DayOfWeek.MONDAY);
@@ -438,31 +458,41 @@
        }
        // 应收
        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, BigDecimal::add);
        // BigDecimal receivableMoney =
        // salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO,
        // BigDecimal::add);
        BigDecimal receivableMoney = sumAmount(salesLedgers, SalesLedger::getContractAmount);
        // 应付
        List<PurchaseLedger> procurementRecords = purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
//                .ge(PurchaseLedger::getEntryDate, startDate)
//                .lt(PurchaseLedger::getEntryDate, endDate)
        );
//        BigDecimal payableMoney = procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        List<PurchaseLedger> procurementRecords = purchaseLedgerMapper
                .selectList(new LambdaQueryWrapper<PurchaseLedger>()
                // .ge(PurchaseLedger::getEntryDate, startDate)
                // .lt(PurchaseLedger::getEntryDate, endDate)
                );
        // BigDecimal payableMoney =
        // procurementRecords.stream().map(PurchaseLedger::getContractAmount).reduce(BigDecimal.ZERO,
        // BigDecimal::add);
        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, BigDecimal::add);
        // BigDecimal advanceMoney =
        // receiptPayments.stream().map(ReceiptPayment::getReceiptPaymentAmount).reduce(BigDecimal.ZERO,
        // BigDecimal::add);
        BigDecimal advanceMoney = sumAmount(receiptPayments, ReceiptPayment::getReceiptPaymentAmount);
        // 预付
        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new LambdaQueryWrapper<PaymentRegistration>()
//                .ge(PaymentRegistration::getPaymentDate, startDate)
//                .lt(PaymentRegistration::getPaymentDate, endDate)
        );
//        BigDecimal prepayMoney = paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper
                .selectList(new LambdaQueryWrapper<PaymentRegistration>()
                // .ge(PaymentRegistration::getPaymentDate, startDate)
                // .lt(PaymentRegistration::getPaymentDate, endDate)
                );
        // BigDecimal prepayMoney =
        // paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO,
        // BigDecimal::add);
        BigDecimal prepayMoney = sumAmount(paymentRegistrations, PaymentRegistration::getCurrentPaymentAmount);
        StatisticsReceivablePayableDto statisticsReceivablePayableDto = new StatisticsReceivablePayableDto();
        statisticsReceivablePayableDto.setPayableMoney(payableMoney.subtract(prepayMoney));
@@ -472,6 +502,7 @@
        return statisticsReceivablePayableDto;
    }
    public static <T> BigDecimal sumAmount(List<T> list, java.util.function.Function<T, BigDecimal> amountExtractor) {
        return list.stream()
                // 提取金额时,将null替换为BigDecimal.ZERO
@@ -479,6 +510,7 @@
                // 累加,初始值为0,避免空流问题
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Autowired
    private DeviceRepairMapper deviceRepairMapper;
@@ -493,10 +525,12 @@
        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("deviceRepairTodo", aLong1);
        }};
        return new HashMap<String, Object>() {
            {
                put("approveTodo", aLong);
                put("deviceRepairTodo", aLong1);
            }
        };
    }
    @Override
@@ -510,37 +544,1345 @@
    @Override
    public ProductionProgressDto productionProgress() {
        ProductionProgressDto productionProgressDto = new ProductionProgressDto();
        List<ProductOrder> productOrderList = productOrderMapper.selectList(new LambdaQueryWrapper<ProductOrder>());
        List<ProductOrderDto> productOrderDtoList = new ArrayList<>();
        productOrderList.forEach(productOrder -> {
            ProductOrderDto productOrderDto = productOrderMapper.productMainByOrderId(productOrder);
//            if (productOrderDto != null && productOrderDto.getPlanQuantity() != null && productOrderDto.getQuantity() != null) {
//                productOrderDto.setCompletionStatus((productOrderDto.getPlanQuantity().subtract(productOrderDto.getQuantity())).divide(productOrderDto.getPlanQuantity(), 2, RoundingMode.HALF_UP));
//            }
            productOrderDtoList.add(productOrderDto);
        });
        productionProgressDto.setCompletedOrderDetails(productOrderDtoList);
        // 1. 查询所有生产订单(可根据需求添加过滤条件,如排除已删除、已取消的订单)
        IPage<ProductOrderDto> productOrderDtoIPage = productOrderMapper.pageProductOrder(new Page<>(1, -1), new ProductOrderDto());
        // 2. 初始化汇总数据
        int totalCount = productOrderDtoIPage.getRecords().size();
        int completedCount = (int) productOrderDtoIPage.getRecords().stream().map(productOrderDto -> productOrderMapper.productMainByOrderId(productOrderDto)).filter(productOrderDto1 -> productOrderDto1.getQuantity() != null && productOrderDto1.getQuantity().compareTo(BigDecimal.ZERO) == 0).count();
        // 6. 赋值汇总数据
        ProductOrderDto orderDto = new ProductOrderDto();
        orderDto.setStartTime(LocalDateTime.now().minusMonths(1));
        orderDto.setEndTime(LocalDateTime.now());
        List<ProductOrderDto> productOrderDtos = productOrderMapper.pageProductOrder(new Page<>(1, -1), orderDto)
                .getRecords();
        productionProgressDto.setCompletedOrderDetails(productOrderDtos);
        long totalCount = productOrderDtos.size();
        long count = productOrderDtos.stream().filter(
                productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(productOrderDto.getQuantity()) >= 0)
                .count();
        long count2 = productOrderDtos.stream()
                .filter(productOrderDto -> productOrderDto.getCompleteQuantity().compareTo(BigDecimal.ZERO) == 0)
                .count();
        productionProgressDto.setTotalOrderCount(totalCount);
        productionProgressDto.setCompletedOrderCount(completedCount);
        productionProgressDto.setUncompletedOrderCount(totalCount - completedCount);
        productionProgressDto.setCompletedProgressCount(BigDecimal.valueOf(completedCount).divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP));
        productionProgressDto.setCompletedOrderCount(count);
        productionProgressDto.setUncompletedOrderCount(count2);
        productionProgressDto.setPartialCompletedOrderCount(totalCount - count - count2);
        return productionProgressDto;
    }
    @Override
    public Map<Integer, List<ProductWorkOrderDto>> workInProcessTurnover() {
        List<ProductWorkOrderDto> productWorkOrderDtoList = productWorkOrderMapper.selectProductWorkOrderDtoList();
       //根据状态区分工单的各个状态
        Map<Integer, List<ProductWorkOrderDto>> productWorkOrderDtoMap = productWorkOrderDtoList.stream().collect(Collectors.groupingBy(ProductWorkOrderDto::getStatus));
        return productWorkOrderDtoMap;
    public ProductionTurnoverDto workInProcessTurnover() {
        ProductionTurnoverDto productionTurnoverDto = new ProductionTurnoverDto();
        ProductWorkOrderDto workOrder = new ProductWorkOrderDto();
        workOrder.setPlanStartTime(LocalDate.now().minusMonths(1));
        workOrder.setPlanEndTime(LocalDate.now());
        List<ProductWorkOrderDto> productWorkOrders = productWorkOrderMapper
                .pageProductWorkOrder(new Page<>(1, -1), workOrder).getRecords();
        long sum = productWorkOrders.stream()
                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity()
                        .compareTo(productWorkOrder.getCompleteQuantity()) > 0)
                .map(ProductWorkOrder::getPlanQuantity)
                .mapToLong(BigDecimal::longValue)
                .sum();
        if (sum == 0)
            return null;
        productionTurnoverDto.setTotalOrderCount(sum);// 总在制品数量
        productionTurnoverDto.setAverageTurnoverDays(BigDecimal.valueOf(sum).divide(
                BigDecimal.valueOf(ChronoUnit.DAYS.between(LocalDateTime.now().minusMonths(1), LocalDateTime.now())), 2,
                RoundingMode.HALF_UP));
        long completeQuantity = productWorkOrders.stream()
                .filter(productWorkOrder -> productWorkOrder.getCompleteQuantity()
                        .compareTo(productWorkOrder.getPlanQuantity()) >= 0)
                .map(ProductWorkOrder::getCompleteQuantity)
                .mapToLong(BigDecimal::longValue)
                .sum();
        productionTurnoverDto.setTurnoverEfficiency(
                BigDecimal.valueOf(completeQuantity).divide(BigDecimal.valueOf(sum), 2, RoundingMode.HALF_UP));
        Map<String, List<ProductWorkOrderDto>> map = productWorkOrders.stream()
                .filter(productWorkOrder -> productWorkOrder.getPlanQuantity()
                        .compareTo(productWorkOrder.getCompleteQuantity()) > 0)
                .collect(Collectors.groupingBy(ProductWorkOrderDto::getProcessName));
        List<String> strings = new ArrayList<>();
        List<Long> processQuantityDetails = new ArrayList<>();
        map.entrySet().stream().forEach(entry -> {
            String key = entry.getKey();
            long completeSum = entry.getValue().stream().map(ProductWorkOrderDto::getCompleteQuantity)
                    .mapToLong(BigDecimal::longValue).sum();
            strings.add(key);
            processQuantityDetails.add(completeSum);
        });
        productionTurnoverDto.setProcessDetails(strings);
        productionTurnoverDto.setProcessQuantityDetails(processQuantityDetails);
        return productionTurnoverDto;
    }
    @Override
    public DeptStaffDistributionDto deptStaffDistribution() {
        DeptStaffDistributionDto dto = new DeptStaffDistributionDto();
        List<MapDto> items = new ArrayList<>();
        // 查询所有正常且未删除的部门
        List<SysDept> depts = sysDeptMapper.selectDeptList(new SysDept());
        if (CollectionUtils.isEmpty(depts)) {
            dto.setItems(items);
            return dto;
        }
        long totalUsers = 0;
        List<Map<String, Object>> countsByDept = new ArrayList<>();
        for (SysDept dept : depts) {
            if ("0".equals(dept.getStatus()) && "0".equals(dept.getDelFlag())) {
                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);
                    countsByDept.add(map);
                    totalUsers += count;
                }
            }
        }
        if (totalUsers > 0) {
            BigDecimal total = BigDecimal.valueOf(totalUsers);
            for (Map<String, Object> map : countsByDept) {
                MapDto mapDto = new MapDto();
                mapDto.setName((String) map.get("name"));
                Long count = (Long) map.get("count");
                mapDto.setValue(count.toString());
                mapDto.setRate(BigDecimal.valueOf(count).multiply(new BigDecimal("100"))
                        .divide(total, 2, RoundingMode.HALF_UP).toString());
                items.add(mapDto);
            }
        }
        dto.setTotal(totalUsers);
        dto.setItems(items);
        return dto;
    }
    @Override
    public HomeSummaryDto summaryStatistics() {
        HomeSummaryDto dto = new HomeSummaryDto();
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        YearMonth prevMonth = currentMonth.minusMonths(1);
        LocalDateTime currentMonthEnd = currentMonth.atEndOfMonth().atTime(23, 59, 59);
        LocalDateTime prevMonthEnd = prevMonth.atEndOfMonth().atTime(23, 59, 59);
        // 总工作人员
        Long currentStaff = countStaff(currentMonthEnd);
        Long prevStaff = countStaff(prevMonthEnd);
        dto.setTotalStaff(currentStaff);
        dto.setStaffGrowthRate(calculateMoM(currentStaff, prevStaff));
        // 总客户数
        Long currentCustomers = countCustomers(currentMonthEnd);
        Long prevCustomers = countCustomers(prevMonthEnd);
        dto.setTotalCustomer(currentCustomers);
        dto.setCustomerGrowthRate(calculateMoM(currentCustomers, prevCustomers));
        // 总供应商数
        Long currentSuppliers = countSuppliers(currentMonthEnd);
        Long prevSuppliers = countSuppliers(prevMonthEnd);
        dto.setTotalSupplier(currentSuppliers);
        dto.setSupplierGrowthRate(calculateMoM(currentSuppliers, prevSuppliers));
        return dto;
    }
    private Long countStaff(LocalDateTime dateTime) {
        Long staffCountItem = staffOnJobMapper.selectCount(new LambdaQueryWrapper<StaffOnJob>()
                .isNotNull(StaffOnJob::getStaffState)
                .eq(StaffOnJob::getStaffState, 1)
                .le(StaffOnJob::getCreateTime, dateTime));
        return staffCountItem;
    }
    private Long countCustomers(LocalDateTime dateTime) {
        return customerMapper.selectCount(new LambdaQueryWrapper<Customer>()
                .le(Customer::getMaintenanceTime, dateTime.toLocalDate()));
    }
    private Long countSuppliers(LocalDateTime dateTime) {
        return supplierManageMapper.selectCount(new LambdaQueryWrapper<SupplierManage>()
                .le(SupplierManage::getCreateTime, dateTime));
    }
    private String calculateMoM(Number current, Number prev) {
        BigDecimal curVal = new BigDecimal(current.toString());
        BigDecimal prevVal = new BigDecimal(prev.toString());
        if (prevVal.compareTo(BigDecimal.ZERO) == 0) {
            return curVal.compareTo(BigDecimal.ZERO) > 0 ? "100.00" : "0.00";
        }
        return curVal.subtract(prevVal)
                .divide(prevVal, 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal("100"))
                .setScale(2, RoundingMode.HALF_UP)
                .toString();
    }
    @Override
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(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: // 季度
                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");
        List<Map<String, Object>> maps = purchaseLedgerMapper.selectMaps(queryWrapper);
        return maps.stream().map(map -> {
            SupplierPurchaseRankingDto dto = new SupplierPurchaseRankingDto();
            dto.setSupplierName(map.get("supplier_name") != null ? map.get("supplier_name").toString() : "");
            Object amount = map.get("total_amount");
            dto.setTotalAmount(amount != null ? new BigDecimal(amount.toString()) : BigDecimal.ZERO);
            return dto;
        }).collect(Collectors.toList());
    }
    @Override
    public CustomerRevenueAnalysisDto customerRevenueAnalysis(Long customerId, Integer type) {
        CustomerRevenueAnalysisDto dto = new CustomerRevenueAnalysisDto();
        List<MapDto> items = new ArrayList<>();
        LocalDate today = LocalDate.now();
        LocalDate start;
        LocalDate end;
        boolean groupByMonth = false;
        switch (type) {
            case 1: // 周
                start = today.with(DayOfWeek.MONDAY);
                end = today.with(DayOfWeek.SUNDAY);
                break;
            case 2: // 月
                start = today.with(TemporalAdjusters.firstDayOfMonth());
                end = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3: // 季度
                Month firstMonthOfQuarter = today.getMonth().firstMonthOfQuarter();
                start = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                end = today.withMonth(firstMonthOfQuarter.plus(2).getValue()).with(TemporalAdjusters.lastDayOfMonth());
                groupByMonth = true;
                break;
            default:
                dto.setItems(items);
                return dto;
        }
        List<SalesLedger> list = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .eq(SalesLedger::getCustomerId, customerId)
                .ge(SalesLedger::getEntryDate, start)
                .le(SalesLedger::getEntryDate, end));
        if (groupByMonth) {
            for (int i = 0; i < 3; i++) {
                LocalDate m = start.plusMonths(i);
                String monthName = m.getMonthValue() + "月";
                BigDecimal sum = list.stream()
                        .filter(l -> l.getEntryDate() != null)
                        .filter(l -> {
                            LocalDate ld = l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                            return ld.getMonth() == m.getMonth() && ld.getYear() == m.getYear();
                        })
                        .map(SalesLedger::getContractAmount)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                MapDto mapDto = new MapDto();
                mapDto.setName(monthName);
                mapDto.setValue(sum.setScale(2, RoundingMode.HALF_UP).toString());
                items.add(mapDto);
            }
        } else {
            long days = ChronoUnit.DAYS.between(start, end) + 1;
            for (int i = 0; i < days; i++) {
                LocalDate d = start.plusDays(i);
                String dayName = d.getMonthValue() + "/" + d.getDayOfMonth();
                BigDecimal sum = list.stream()
                        .filter(l -> l.getEntryDate() != null)
                        .filter(l -> l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
                                .equals(d))
                        .map(SalesLedger::getContractAmount)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                MapDto mapDto = new MapDto();
                mapDto.setName(dayName);
                mapDto.setValue(sum.setScale(2, RoundingMode.HALF_UP).toString());
                items.add(mapDto);
            }
        }
        dto.setItems(items);
        return dto;
    }
    @Override
    public ProductCategoryDistributionDto productCategoryDistribution() {
        ProductCategoryDistributionDto dto = new ProductCategoryDistributionDto();
        List<ProductCategoryDistributionDto.MajorCategoryDto> majorItems = new ArrayList<>();
        // 查询所有的产品类别
        List<Product> allProducts = productMapper.selectList(new LambdaQueryWrapper<>());
        if (CollectionUtils.isEmpty(allProducts)) {
            dto.setItems(majorItems);
            return dto;
        }
        // 分离大类和小类
        List<Product> majorCategories = allProducts.stream()
                .filter(p -> p.getParentId() == null)
                .collect(Collectors.toList());
        List<Product> minorCategories = allProducts.stream()
                .filter(p -> p.getParentId() != null)
                .collect(Collectors.toList());
        // 按父类ID对小类进行分组
        Map<Long, List<Product>> minorGroupMap = minorCategories.stream()
                .collect(Collectors.groupingBy(Product::getParentId));
        int totalMinorCount = minorCategories.size();
        for (Product major : majorCategories) {
            ProductCategoryDistributionDto.MajorCategoryDto majorDto = new ProductCategoryDistributionDto.MajorCategoryDto();
            majorDto.setName(major.getProductName());
            List<Product> children = minorGroupMap.getOrDefault(major.getId(), new ArrayList<>());
            int childrenSize = children.size();
            // 设置小类的总数
            majorDto.setValue(String.valueOf(childrenSize));
            // 计算占比
            if (totalMinorCount > 0) {
                BigDecimal rate = BigDecimal.valueOf(childrenSize)
                        .multiply(new BigDecimal("100"))
                        .divide(BigDecimal.valueOf(totalMinorCount), 2, RoundingMode.HALF_UP);
                majorDto.setRate(rate.toString());
            } else {
                majorDto.setRate("0.00");
            }
            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
            for (Product minor : children) {
                ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
                minorDto.setName(minor.getProductName());
                minorDto.setValue("0");
                minorDto.setRate("0.00");
                minorDto.setChildren(new ArrayList<>());
                minorDtos.add(minorDto);
            }
            majorDto.setChildren(minorDtos);
            majorItems.add(majorDto);
        }
        dto.setItems(majorItems);
        return dto;
    }
    @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;
        }
        QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
                .isNotNull("customer_name")
                .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<>();
        for (Map<String, Object> map : maps) {
            CustomerContributionRankingDto rankingDto = new CustomerContributionRankingDto();
            rankingDto.setCustomerName(map.get("customer_name").toString());
            rankingDto
                    .setTotalAmount(map.get("total_amount") != null ? new BigDecimal(map.get("total_amount").toString())
                            : BigDecimal.ZERO);
            result.add(rankingDto);
        }
        return result;
    }
    @Override
    public List<MapDto> productSalesAnalysis() {
        // 获取所有产品大类的销售额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
        // 计算总销售额
        BigDecimal totalSalesAmount = analysisResults.stream()
                .map(r -> (BigDecimal) r.get("value"))
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return analysisResults.stream()
                .map(result -> {
                    MapDto mapDto = new MapDto();
                    String name = (String) result.get("name");
                    BigDecimal value = (BigDecimal) result.get("value");
                    mapDto.setName(name);
                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
                    if (totalSalesAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
                        mapDto.setRate("0.00");
                    } else {
                        String rate = value.divide(totalSalesAmount, 4, RoundingMode.HALF_UP)
                                .multiply(new BigDecimal("100"))
                                .setScale(2, RoundingMode.HALF_UP)
                                .toString();
                        mapDto.setRate(rate);
                    }
                    return mapDto;
                })
                .collect(Collectors.toList());
    }
    @Override
    public List<MapDto> rawMaterialPurchaseAmountRatio() {
        // 获取所有原材料的采购额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
        // 计算总采购额
        BigDecimal totalPurchaseAmount = analysisResults.stream()
                .map(r -> (BigDecimal) r.get("value"))
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return analysisResults.stream()
                .map(result -> {
                    MapDto mapDto = new MapDto();
                    String name = (String) result.get("name");
                    BigDecimal value = (BigDecimal) result.get("value");
                    mapDto.setName(name);
                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
                    if (totalPurchaseAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
                        mapDto.setRate("0.00");
                    } else {
                        String rate = value.divide(totalPurchaseAmount, 4, RoundingMode.HALF_UP)
                                .multiply(new BigDecimal("100"))
                                .setScale(2, RoundingMode.HALF_UP)
                                .toString();
                        mapDto.setRate(rate);
                    }
                    return mapDto;
                })
                .collect(Collectors.toList());
    }
    @Override
    public List<MapDto> salesPurchaseStorageProductCount() {
        LocalDate now = LocalDate.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
        String currentMonthNow = now.format(dtf);
        LocalDate lastMonth = now.minusMonths(1);
        String lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
        String lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).format(dtf);
        // 销售
        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart,
                currentMonthNow);
        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
        // 采购
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart,
                currentMonthNow);
        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
        // 储存
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthNow);
        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
        List<MapDto> list = new ArrayList<>();
        list.add(createMapDto("销售产品数", currentSales, lastSales));
        list.add(createMapDto("采购产品数", currentPurchase, lastPurchase));
        list.add(createMapDto("储存产品数", currentStorage, lastStorage));
        return list;
    }
    private MapDto createMapDto(String name, int current, int last) {
        MapDto dto = new MapDto();
        dto.setName(name);
        dto.setValue(String.valueOf(current));
        if (last == 0) {
            dto.setRate(current > 0 ? "100.00" : "0.00");
        } else {
            BigDecimal curDec = new BigDecimal(current);
            BigDecimal lastDec = new BigDecimal(last);
            // 增长率
            String rate = curDec.subtract(lastDec)
                    .divide(lastDec, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .setScale(2, RoundingMode.HALF_UP)
                    .toString();
            dto.setRate(rate);
        }
        return dto;
    }
    @Override
    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
        String targetName;
        if (type == 1) {
            targetName = "原材料";
        } else if (type == 2) {
            targetName = "成品";
        } else if (type == 3) {
            targetName = "半成品";
        } else {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Product::getProductName, targetName).isNull(Product::getParentId);
        Product rootCategory = productMapper.selectOne(queryWrapper);
        if (rootCategory == null) {
            log.warn("未找到名称为 {} 的顶级产品分类", targetName);
            return new ArrayList<>();
        }
        Long rootCategoryId = rootCategory.getId();
        LocalDate now = LocalDate.now();
        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
        String startDate = now.minusDays(6).toString();
        String endDate = now.toString();
        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
                endDate + " 23:59:59");
        List<Map<String, Object>> outCounts = stockInventoryMapper.selectDailyStockOutCounts(rootCategoryId, startDate,
                endDate + " 23:59:59");
        Map<LocalDate, BigDecimal> inMap = inCounts.stream()
                .collect(Collectors.toMap(
                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
                        m -> (BigDecimal) m.get("count"),
                        BigDecimal::add));
        Map<LocalDate, BigDecimal> outMap = outCounts.stream()
                .collect(Collectors.toMap(
                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
                        m -> (BigDecimal) m.get("count"),
                        BigDecimal::add));
        List<Map<String, Object>> result = new ArrayList<>();
        for (int i = 6; i >= 0; i--) {
            LocalDate date = now.minusDays(i);
            Map<String, Object> dataPoint = new LinkedHashMap<>();
            dataPoint.put("date", date.format(displayDtf)); // 1/23
            dataPoint.put("inCount", inMap.getOrDefault(date, BigDecimal.ZERO));
            dataPoint.put("outCount", outMap.getOrDefault(date, BigDecimal.ZERO));
            result.add(dataPoint);
        }
        return result;
    }
    @Override
    public List<MapDto> productTurnoverDays() {
        return homeMapper.productTurnoverDays();
    }
    @Autowired
    private AccountExpenseMapper accountExpenseMapper;
    @Autowired
    private AccountIncomeMapper accountIncomeMapper;
    public List<Map<String, Object>> incomeExpenseAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate;
        String dateFormat;
        DateTimeFormatter formatter;
        List<String> xAxis = new ArrayList<>();
        // 1. 时间范围 & X 轴
        if (type == 3) {
            // 本季度(按月)
            Month currentMonth = today.getMonth();
            Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
            startDate = today.withMonth(firstMonthOfQuarter.getValue())
                    .with(TemporalAdjusters.firstDayOfMonth());
            endDate = startDate.plusMonths(2)
                    .with(TemporalAdjusters.lastDayOfMonth());
            dateFormat = "%Y-%m";
            formatter = DateTimeFormatter.ofPattern("yyyy-MM");
            LocalDate tmp = startDate;
            while (!tmp.isAfter(endDate)) {
                xAxis.add(tmp.format(formatter));
                tmp = tmp.plusMonths(1);
            }
        } else {
            // 周 / 月(按天)
            dateFormat = "%Y-%m-%d";
            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            if (type == 1) {
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
            } else {
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
            }
            LocalDate tmp = startDate;
            while (!tmp.isAfter(endDate)) {
                xAxis.add(tmp.format(formatter));
                tmp = tmp.plusDays(1);
            }
        }
        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 2. 查询数据
        List<IncomeExpenseAnalysisDto> incomeList = accountIncomeMapper.selectIncomeStats(startStr, endStr, dateFormat);
        // List<IncomeExpenseAnalysisDto> purchaseList =
        // purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr,
                dateFormat);
        // 3. 转 Map(自动合并)
        Map<String, BigDecimal> incomeMap = incomeList.stream()
                .collect(Collectors.toMap(
                        IncomeExpenseAnalysisDto::getDateStr,
                        IncomeExpenseAnalysisDto::getAmount,
                        BigDecimal::add));
        // Map<String, BigDecimal> purchaseMap = purchaseList.stream()
        // .collect(Collectors.toMap(
        // IncomeExpenseAnalysisDto::getDateStr,
        // IncomeExpenseAnalysisDto::getAmount,
        // BigDecimal::add));
        Map<String, BigDecimal> expenseMap = expenseList.stream()
                .collect(Collectors.toMap(
                        IncomeExpenseAnalysisDto::getDateStr,
                        IncomeExpenseAnalysisDto::getAmount,
                        BigDecimal::add));
        // 4. 组装返回
        List<Map<String, Object>> result = new ArrayList<>();
        for (String dateStr : xAxis) {
            Map<String, Object> item = new HashMap<>();
            item.put("date", dateStr);
            // 收入
            BigDecimal income = incomeMap.getOrDefault(dateStr, BigDecimal.ZERO);
            item.put("income", income.setScale(2, RoundingMode.HALF_UP));
            // 支出 = 采购 + 财务支出
            // BigDecimal purchase = purchaseMap.getOrDefault(dateStr, BigDecimal.ZERO);
            BigDecimal expense = expenseMap.getOrDefault(dateStr, BigDecimal.ZERO);
            // BigDecimal totalExpense = purchase.add(expense);
            BigDecimal totalExpense = expense;
            item.put("expense", totalExpense.setScale(2, RoundingMode.HALF_UP));
            result.add(item);
        }
        return result;
    }
    @Override
    public List<MapDto> profitTrendAnalysis() {
        LocalDate today = LocalDate.now();
        LocalDate startDate = today.minusMonths(11).with(TemporalAdjusters.firstDayOfMonth());
        LocalDate endDate = today.with(TemporalAdjusters.lastDayOfMonth());
        String dateFormat = "%Y-%m";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        List<String> months = new ArrayList<>();
        LocalDate temp = startDate;
        while (!temp.isAfter(endDate)) {
            months.add(temp.format(formatter));
            temp = temp.plusMonths(1);
        }
        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // List<IncomeExpenseAnalysisDto> incomeList =
        // salesLedgerMapper.selectIncomeStats(startStr, endStr, dateFormat);
        List<IncomeExpenseAnalysisDto> incomeList = new ArrayList<>();
        List<IncomeExpenseAnalysisDto> purchaseList = purchaseLedgerMapper.selectPurchaseStats(startStr, endStr,
                dateFormat);
        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr,
                dateFormat);
        Map<String, BigDecimal> incomeMap = incomeList.stream().collect(Collectors
                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        Map<String, BigDecimal> purchaseMap = purchaseList.stream().collect(Collectors
                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        Map<String, BigDecimal> expenseMap = expenseList.stream().collect(Collectors
                .toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
        List<MapDto> result = new ArrayList<>();
        for (String month : months) {
            MapDto dto = new MapDto();
            dto.setName(month);
            BigDecimal income = incomeMap.getOrDefault(month, BigDecimal.ZERO);
            BigDecimal purchase = purchaseMap.getOrDefault(month, BigDecimal.ZERO);
            income = BigDecimal.ZERO;
            BigDecimal expense = expenseMap.getOrDefault(month, BigDecimal.ZERO);
            BigDecimal totalExpense = purchase.add(expense);
            BigDecimal profit = income.subtract(totalExpense);
            dto.setValue(profit.setScale(2, RoundingMode.HALF_UP).toString());
            result.add(dto);
        }
        return result;
    }
    @Override
    public List<MapDto> expenseCompositionAnalysis(Integer type) {
        List<MapDto> result = new ArrayList<>();
        if (type == 2) {
            List<MapDto> customerList = salesLedgerMapper.selectCustomerSalesComposition();
            if (customerList != null) {
                result.addAll(customerList);
            }
        } else {
            BigDecimal rawMaterialAmount = salesLedgerProductMapper.selectRawMaterialExpense();
            MapDto rawMaterialDto = new MapDto();
            rawMaterialDto.setName("原材料");
            rawMaterialDto.setValue(rawMaterialAmount != null ? rawMaterialAmount.toString() : "0");
            result.add(rawMaterialDto);
            List<MapDto> expenseList = accountExpenseMapper.selectExpenseComposition(null, null);
            if (expenseList != null) {
                result.addAll(expenseList);
            }
        }
        BigDecimal total = BigDecimal.ZERO;
        for (MapDto dto : result) {
            if (dto.getValue() != null) {
                total = total.add(new BigDecimal(dto.getValue()));
            }
        }
        if (total.compareTo(BigDecimal.ZERO) > 0) {
            for (MapDto dto : result) {
                if (dto.getValue() != null) {
                    BigDecimal val = new BigDecimal(dto.getValue());
                    String rate = val.divide(total, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP)
                            .toString();
                    dto.setRate(rate);
                } else {
                    dto.setRate("0.00");
                }
            }
        } else {
            for (MapDto dto : result) {
                dto.setRate("0.00");
            }
        }
        return result;
    }
    @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);
        }
        return dto;
    }
    public MonthlyExpenditureDto monthlyExpenditure() {
        MonthlyExpenditureDto dto = new MonthlyExpenditureDto();
        // 当月时间范围
        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());
                }
            }
        }
        // 其他费用
        LambdaQueryWrapper<AccountExpense> expenseWrapper = new LambdaQueryWrapper<>();
        expenseWrapper.ge(AccountExpense::getExpenseDate,
                java.sql.Date.valueOf(currentMonth.atDay(1)));
        expenseWrapper.le(AccountExpense::getExpenseDate,
                java.sql.Date.valueOf(currentMonth.atEndOfMonth()));
        List<AccountExpense> expenses = accountExpenseMapper.selectList(expenseWrapper);
        BigDecimal otherExpense = BigDecimal.ZERO;
        if (!CollectionUtils.isEmpty(expenses)) {
            for (AccountExpense e : expenses) {
                if (e.getExpenseMoney() != null) {
                    otherExpense = otherExpense.add(e.getExpenseMoney());
                }
            }
        }
        // 月度总支出
        // BigDecimal monthlyExpenditure = rawMaterialCost.add(otherExpense);
        BigDecimal monthlyExpenditure = otherExpense;
        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);
        }
        return dto;
    }
    @Autowired
    private ProductionProductInputMapper productionProductInputMapper;
    @Override
    public List<InputOutputAnalysisDto> inputOutputAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                break;
            case 2: // 月
                startDate = today.withDayOfMonth(1);
                break;
            case 3: // 季度
                int currentMonth = today.getMonthValue();
                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
                startDate = LocalDate.of(today.getYear(), startMonth, 1);
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                break;
        }
        LocalDate endDate = today.plusDays(1);
        String startStr = startDate.atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String endStr = endDate.atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<Map<String, Object>> inputList = productionProductInputMapper.selectInputStats(startStr, endStr);
        List<Map<String, Object>> outputList = productionProductOutputMapper.selectDailyOutputStats(startStr, endStr);
        Map<String, InputOutputAnalysisDto> dateMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(inputList)) {
            for (Map<String, Object> map : inputList) {
                String date = (String) map.get("date");
                if (date == null)
                    continue;
                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
                dto.setDate(date);
                BigDecimal qty = (BigDecimal) map.get("quantity");
                dto.setInputSum(dto.getInputSum() != null ? dto.getInputSum().add(qty) : qty);
                dateMap.put(date, dto);
            }
        }
        if (!CollectionUtils.isEmpty(outputList)) {
            for (Map<String, Object> map : outputList) {
                String date = (String) map.get("date");
                if (date == null)
                    continue;
                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
                dto.setDate(date);
                BigDecimal qty = (BigDecimal) map.get("quantity");
                dto.setOutputSum(dto.getOutputSum() != null ? dto.getOutputSum().add(qty) : qty);
                dateMap.put(date, dto);
            }
        }
        dateMap.values().forEach(dto -> {
            if (dto.getInputSum() == null)
                dto.setInputSum(BigDecimal.ZERO);
            if (dto.getOutputSum() == null)
                dto.setOutputSum(BigDecimal.ZERO);
        });
        return dateMap.values().stream()
                .sorted(Comparator.comparing(InputOutputAnalysisDto::getDate))
                .collect(Collectors.toList());
    }
    @Override
    public List<MapDto> processOutputAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = today;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                break;
            case 2: // 月
                startDate = today.withDayOfMonth(1);
                break;
            case 3: // 季度
                int currentMonth = today.getMonthValue();
                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
                startDate = LocalDate.of(today.getYear(), startMonth, 1);
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                break;
        }
        ProductWorkOrderDto queryDto = new ProductWorkOrderDto();
        queryDto.setPlanStartTime(startDate);
        queryDto.setPlanEndTime(endDate);
        List<ProductWorkOrderDto> list = productWorkOrderMapper.pageProductWorkOrder(new Page<>(1, -1), queryDto)
                .getRecords();
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        Map<String, BigDecimal> processOutputMap = list.stream()
                .filter(item -> item.getProcessName() != null && item.getCompleteQuantity() != null)
                .collect(Collectors.groupingBy(
                        ProductWorkOrderDto::getProcessName,
                        Collectors.reducing(BigDecimal.ZERO,
                                ProductWorkOrderDto::getCompleteQuantity,
                                BigDecimal::add)));
        BigDecimal total = processOutputMap.values().stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (BigDecimal.ZERO.compareTo(total) == 0) {
            return new ArrayList<>();
        }
        List<MapDto> result = new ArrayList<>();
        processOutputMap.forEach((name, value) -> {
            MapDto dto = new MapDto();
            dto.setName(name);
            dto.setValue(value.stripTrailingZeros().toPlainString());
            BigDecimal rate = value
                    .divide(total, 4, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"));
            dto.setRate(rate.setScale(2, RoundingMode.HALF_UP) + "%");
            result.add(dto);
        });
        return result;
    }
    @Override
    public List<WorkOrderEfficiencyDto> workOrderEfficiencyAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = today;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                break;
            case 2: // 月
                startDate = today.withDayOfMonth(1);
                break;
            case 3: // 季度
                int currentMonth = today.getMonthValue();
                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
                startDate = LocalDate.of(today.getYear(), startMonth, 1);
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                break;
        }
        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<ProductWorkOrderDto> startList = productWorkOrderMapper.selectWorkOrderStartStats(startStr, endStr);
        List<com.ruoyi.production.dto.ProductionProductOutputDto> outputList = productionProductOutputMapper
                .selectOutputStats(startStr, endStr);
        Map<String, WorkOrderEfficiencyDto> dateMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(startList)) {
            for (ProductWorkOrderDto item : startList) {
                if (item.getPlanStartTime() != null) {
                    String date = item.getPlanStartTime().toString();
                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
                    dto.setDate(date);
                    BigDecimal qty = item.getPlanQuantity() != null ? item.getPlanQuantity() : BigDecimal.ZERO;
                    dto.setStartQuantity(dto.getStartQuantity() != null ? dto.getStartQuantity().add(qty) : qty);
                    dateMap.put(date, dto);
                }
            }
        }
        // 完工数量和良品率
        if (!CollectionUtils.isEmpty(outputList)) {
            for (com.ruoyi.production.dto.ProductionProductOutputDto item : outputList) {
                if (item.getCreateTime() != null) {
                    String date = item.getCreateTime().toLocalDate().toString();
                    WorkOrderEfficiencyDto dto = dateMap.getOrDefault(date, new WorkOrderEfficiencyDto());
                    dto.setDate(date);
                    BigDecimal finishQty = item.getQuantity() != null ? item.getQuantity() : BigDecimal.ZERO;
                    BigDecimal scrapQty = item.getScrapQty() != null ? item.getScrapQty() : BigDecimal.ZERO;
                    dto.setFinishQuantity(
                            dto.getFinishQuantity() != null ? dto.getFinishQuantity().add(finishQty) : finishQty);
                }
            }
            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 -> i.getQuantity() != null ? i.getQuantity() : BigDecimal.ZERO,
                                    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);
                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);
                    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");
        });
        return dateMap.values().stream()
                .sorted(Comparator.comparing(WorkOrderEfficiencyDto::getDate))
                .collect(Collectors.toList());
    }
    @Autowired
    private com.ruoyi.production.mapper.SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    @Override
    public List<MapDto> productionAccountingAnalysis(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = today;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                break;
            case 2: // 月
                startDate = today.withDayOfMonth(1);
                break;
            case 3: // 季度
                int currentMonth = today.getMonthValue();
                int startMonth = ((currentMonth - 1) / 3) * 3 + 1;
                startDate = LocalDate.of(today.getYear(), startMonth, 1);
                break;
            default:
                startDate = today.with(DayOfWeek.MONDAY);
                break;
        }
        String startStr = startDate.atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String endStr = endDate.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        List<Map<String, Object>> wagesList = salesLedgerProductionAccountingMapper.selectDailyWagesStats(startStr,
                endStr);
        if (CollectionUtils.isEmpty(wagesList)) {
            return new ArrayList<>();
        }
        List<MapDto> 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");
            result.add(dto);
        }
        result.sort(Comparator.comparing(MapDto::getName));
        return result;
    }
    @Override
    public List<MapDto> orderCount() {
        LocalDate now = LocalDate.now();
        String currentStart = now.withDayOfMonth(1).atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String currentEnd = now.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDate lastMonthDate = now.minusMonths(1);
        String lastStart = lastMonthDate.withDayOfMonth(1).atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String lastEnd = lastMonthDate.withDayOfMonth(lastMonthDate.lengthOfMonth()).atTime(LocalTime.MAX)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        int currentCreated = productOrderMapper.countCreated(currentStart, currentEnd);
        int lastCreated = productOrderMapper.countCreated(lastStart, lastEnd);
        MapDto createdDto = createOrderCountDto("生产订单数", currentCreated, lastCreated);
        int currentCompleted = productOrderMapper.countCompleted(currentStart, currentEnd);
        int lastCompleted = productOrderMapper.countCompleted(lastStart, lastEnd);
        MapDto completedDto = createOrderCountDto("已完成订单数", currentCompleted, lastCompleted);
        int currentPending = productOrderMapper.countPending(currentStart, currentEnd);
        int lastPending = productOrderMapper.countPending(lastStart, lastEnd);
        MapDto pendingDto = createOrderCountDto("待生产订单数", currentPending, lastPending);
        return Arrays.asList(createdDto, completedDto, pendingDto);
    }
    private MapDto createOrderCountDto(String name, int current, int last) {
        MapDto dto = new MapDto();
        dto.setName(name);
        dto.setValue(String.valueOf(current));
        BigDecimal currentVal = new BigDecimal(current);
        BigDecimal lastVal = new BigDecimal(last);
        if (lastVal.compareTo(BigDecimal.ZERO) == 0) {
            dto.setRate(currentVal.compareTo(BigDecimal.ZERO) > 0 ? "100" : "0");
        } else {
            // (Current - Last) / Last * 100
            BigDecimal diff = currentVal.subtract(lastVal);
            BigDecimal rate = diff.divide(lastVal, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")).setScale(0,
                    RoundingMode.HALF_UP);
            dto.setRate(rate.toString());
        }
        return dto;
    }
}