2026-08-07 bb1d7eeda426e66393cf4403cb3ef082db696bd9
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -3,14 +3,26 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.bean.dto.purchase.PurchaseInboundDto;
import com.ruoyi.account.bean.dto.purchase.PurchaseReturnDto;
import com.ruoyi.account.bean.dto.sales.SalesOutboundDto;
import com.ruoyi.account.bean.dto.sales.SalesReturnDto;
import com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo;
import com.ruoyi.account.bean.vo.purchase.PurchaseReturnVo;
import com.ruoyi.account.bean.vo.sales.SalesOutboundVo;
import com.ruoyi.account.bean.vo.sales.SalesReturnVo;
import com.ruoyi.account.mapper.AccountStatementMapper;
import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper;
import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
import com.ruoyi.account.pojo.AccountStatement;
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
import com.ruoyi.approve.mapper.ApprovalTaskMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalTask;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
@@ -20,6 +32,7 @@
import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
import com.ruoyi.collaborativeApproval.pojo.Notice;
import com.ruoyi.common.enums.ApproveTypeEnum;
import com.ruoyi.common.enums.TypeEnums;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
@@ -29,11 +42,13 @@
import com.ruoyi.home.dto.*;
import com.ruoyi.home.mapper.HomeMapper;
import com.ruoyi.home.service.HomeService;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.production.bean.dto.ProductionProductOutputDto;
import com.ruoyi.production.mapper.*;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
@@ -45,7 +60,9 @@
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -71,6 +88,10 @@
public class HomeServiceImpl implements HomeService {
    private final SalesLedgerMapper salesLedgerMapper;
    private final StockOutRecordMapper stockOutRecordMapper;
    private final ReturnManagementMapper returnManagementMapper;
    private final StockInRecordMapper stockInRecordMapper;
    private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
@@ -81,6 +102,10 @@
    private final QualityInspectMapper qualityStatisticsMapper;
    private final ApproveProcessMapper approveProcessMapper;
    private final ApprovalTaskMapper approvalTaskMapper;
    private final ApprovalInstanceMapper approvalInstanceMapper;
    private final SysDeptMapper sysDeptMapper;
@@ -368,40 +393,58 @@
    @Override
    public List<ApproveProcess> todos() throws ParseException {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<ApproveProcess> approveProcesses = new ArrayList<>();
        // 查询旧审批表(approve_process)
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
                .ne(ApproveProcess::getApproveStatus, 2);
        // .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
        List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(approveProcesses)) {
            approveProcesses = new ArrayList<>();
        List<ApproveProcess> oldList = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(oldList)) {
            oldList.forEach(a -> {
                if (a.getApproveType() != null) {
                    a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
                }
            });
            approveProcesses.addAll(oldList);
        }
        approveProcesses.forEach(a -> {
            if (a.getApproveType() != null) {
                a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
        // 查询新审批表(approval_task + approval_instance)
        List<ApprovalTask> pendingTasks = approvalTaskMapper.selectList(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, loginUser.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        if (!CollectionUtils.isEmpty(pendingTasks)) {
            List<Long> instanceIds = pendingTasks.stream()
                    .map(ApprovalTask::getInstanceId)
                    .distinct()
                    .collect(Collectors.toList());
            List<ApprovalInstance> instances = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>()
                    .in(ApprovalInstance::getId, instanceIds)
                    .eq(ApprovalInstance::getDeleted, 0));
            Map<Long, ApprovalInstance> instanceMap = instances.stream()
                    .collect(Collectors.toMap(ApprovalInstance::getId, i -> i, (a, b) -> a));
            for (ApprovalTask task : pendingTasks) {
                ApprovalInstance instance = instanceMap.get(task.getInstanceId());
                if (instance == null) {
                    continue;
                }
                ApproveProcess ap = new ApproveProcess();
                ap.setId(instance.getId());
                ap.setApproveId(instance.getInstanceNo());
                ap.setApproveReason(instance.getTitle());
                ap.setApproveTime(instance.getApplyTime() != null ? java.sql.Timestamp.valueOf(instance.getApplyTime()) : null);
                ap.setApproveUserName(instance.getApplicantName());
                ap.setApproveDeptName(instance.getTemplateName());
                if (instance.getBusinessType() != null) {
                    ap.setApproveType(instance.getBusinessType().intValue());
                    ap.setApproveTypeName(TypeEnums.getLabelByValue(instance.getBusinessType()));
                }
                approveProcesses.add(ap);
            }
        });
        // // 查询未领用劳保记录
        // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //
        // LaborIssue laborIssue1 = new LaborIssue();
        // laborIssue1.setAdoptedDate(new Date());
        // laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
        // List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);
        // //staff_join_leave_record表被删除
        // if(!CollectionUtils.isEmpty(laborIssues)){
        // for (LaborIssue laborIssue : laborIssues) {
        // ApproveProcess approveProcess = new ApproveProcess();
        // approveProcess.setApproveId(laborIssue.getOrderNo());
        // approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
        // approveProcess.setApproveTime(laborIssue.getIssueDate());
        // approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" +
        // laborIssue.getDictName() + "超时未领取");
        // approveProcesses.add(approveProcess);
        // }
        // }
        }
        return approveProcesses;
    }
@@ -417,14 +460,26 @@
        LocalDate startDate = range[0];
        LocalDate endDate = range[1];
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //收款
        BigDecimal advanceMoney = sumCollectionAmount(startDate, endDate);
        //付款
        BigDecimal prepayMoney = sumPaymentAmount(startDate, endDate);
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(advanceMoney))));
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(prepayMoney))));
        //应收金额=销售出库-销售退货
        dto.setReceivableMoney(scaleMoney(maxZero(receivableBase.subtract(salesReturnAmount))));
        //应付金额=采购入库-采购退货
        dto.setPayableMoney(scaleMoney(maxZero(payableBase.subtract(purchaseReturnAmount))));
        //收款金额=收款单
        dto.setAdvanceMoney(scaleMoney(advanceMoney));
        //付款金额=付款单
        dto.setPrepayMoney(scaleMoney(prepayMoney));
        return dto;
    }
@@ -442,18 +497,23 @@
    @Override
    public Map<String, Object> approveAndDeviceTodos() {
        // 审批协同待办
        Long aLong = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
        // 旧审批表待办
        Long oldCount = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
                .eq(ApproveProcess::getApproveUserCurrentId, SecurityUtils.getUserId())
                .eq(ApproveProcess::getApproveDelete, 0)
                .in(ApproveProcess::getApproveStatus, 0, 1, 3));
        // 新审批表待办
        Long newCount = approvalTaskMapper.selectCount(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, SecurityUtils.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        // 设备报修待办
        Long aLong1 = deviceRepairMapper.selectCount(new LambdaQueryWrapper<DeviceRepair>()
                .eq(DeviceRepair::getStatus, 0)
                .eq(DeviceRepair::getRepairName, SecurityUtils.getLoginUser().getNickName()));
        return new HashMap<String, Object>() {
            {
                put("approveTodo", aLong);
                put("approveTodo", oldCount + newCount);
                put("deviceRepairTodo", aLong1);
            }
        };
@@ -637,36 +697,39 @@
    }
    @Override
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type) {
        LocalDate today = LocalDate.now();
    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        switch (type) {
            case 1: // 周
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2: // 月
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3: // 季度
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
            default:
                return new ArrayList<>();
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            switch (type) {
                case 1: // 周
                    startDate = today.with(DayOfWeek.MONDAY);
                    endDate = today.with(DayOfWeek.SUNDAY);
                    break;
                case 2: // 月
                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                    break;
                case 3: // 季度
                    Month currentMonth = today.getMonth();
                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<PurchaseLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
                .ge("entry_date", startDate)
                .le("entry_date", endDate)
                // .ne("approval_status", 3)
                .groupBy("supplier_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
@@ -802,17 +865,81 @@
                majorDto.setRate("0.00");
            }
            // 查询各产品小类的采购数量
            Map<String, BigDecimal> purchaseDataMap = new java.util.HashMap<>();
            List<com.ruoyi.dto.MapDto> purchaseDistribution = homeMapper.selectPurchaseCategoryDistribution();
            if (purchaseDistribution != null) {
                for (com.ruoyi.dto.MapDto item : purchaseDistribution) {
                    if (item.getName() != null && item.getValue() != null) {
                        purchaseDataMap.put(item.getName(), new BigDecimal(item.getValue()));
                    }
                }
            }
            BigDecimal majorTotalChildrenValue = BigDecimal.ZERO;
            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
            for (Product minor : children) {
                ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
                minorDto.setName(minor.getProductName());
                minorDto.setValue("0");
                BigDecimal minorValue = purchaseDataMap.getOrDefault(minor.getProductName(), BigDecimal.ZERO);
                minorDto.setValue(minorValue.toString());
                minorDto.setRate("0.00");
                minorDto.setChildren(new ArrayList<>());
                minorDtos.add(minorDto);
                majorTotalChildrenValue = majorTotalChildrenValue.add(minorValue);
            }
            majorDto.setChildren(minorDtos);
            // 使用实际采购数据更新大类的 value
            if (majorTotalChildrenValue.compareTo(BigDecimal.ZERO) > 0) {
                majorDto.setValue(majorTotalChildrenValue.toString());
            }
            majorItems.add(majorDto);
        }
        // 计算大类占比
        BigDecimal totalAllValue = BigDecimal.ZERO;
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            try {
                totalAllValue = totalAllValue.add(new BigDecimal(m.getValue()));
            } catch (NumberFormatException ignored) {
            }
        }
        if (totalAllValue.compareTo(BigDecimal.ZERO) > 0) {
            for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
                try {
                    BigDecimal val = new BigDecimal(m.getValue());
                    m.setRate(val.divide(totalAllValue, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP)
                            .toString());
                } catch (NumberFormatException ignored) {
                }
            }
        }
        // 计算小类占比(在同大类内)
        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
            if (m.getChildren() == null || m.getChildren().isEmpty()) {
                continue;
            }
            BigDecimal parentVal = BigDecimal.ZERO;
            for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                try {
                    parentVal = parentVal.add(new BigDecimal(child.getValue()));
                } catch (NumberFormatException ignored) {
                }
            }
            if (parentVal.compareTo(BigDecimal.ZERO) > 0) {
                for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
                    try {
                        BigDecimal childVal = new BigDecimal(child.getValue());
                        child.setRate(childVal.divide(parentVal, 4, RoundingMode.HALF_UP)
                                .multiply(new BigDecimal("100"))
                                .setScale(2, RoundingMode.HALF_UP)
                                .toString());
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
        }
        dto.setItems(majorItems);
@@ -820,38 +947,42 @@
    }
    @Override
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type) {
        LocalDate today = LocalDate.now();
        LocalDate startDate = null;
        LocalDate endDate = null;
        switch (type) {
            case 1:
                startDate = today.with(DayOfWeek.MONDAY);
                endDate = today.with(DayOfWeek.SUNDAY);
                break;
            case 2:
                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                break;
            case 3:
                Month currentMonth = today.getMonth();
                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                break;
    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDateStr, String endDateStr) {
        LocalDate startDate;
        LocalDate endDate;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            startDate = LocalDate.parse(startDateStr);
            endDate = LocalDate.parse(endDateStr);
        } else {
            LocalDate today = LocalDate.now();
            switch (type) {
                case 1:
                    startDate = today.with(DayOfWeek.MONDAY);
                    endDate = today.with(DayOfWeek.SUNDAY);
                    break;
                case 2:
                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
                    break;
                case 3:
                    Month currentMonth = today.getMonth();
                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
                    break;
                default:
                    return new ArrayList<>();
            }
        }
        QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
                .isNotNull("customer_name")
                .between("entry_date", startDate, endDate)
                .groupBy("customer_name")
                .orderByDesc("total_amount")
                .last("LIMIT 5");
        if (startDate != null && endDate != null) {
            queryWrapper.between("entry_date", startDate, endDate);
        }
        List<Map<String, Object>> maps = salesLedgerMapper.selectMaps(queryWrapper);
        List<CustomerContributionRankingDto> result = new ArrayList<>();
@@ -867,9 +998,13 @@
    }
    @Override
    public List<MapDto> productSalesAnalysis() {
        // 获取所有产品大类的销售额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
    public List<MapDto> productSalesAnalysis(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -904,9 +1039,13 @@
    }
    @Override
    public List<MapDto> rawMaterialPurchaseAmountRatio() {
        // 获取所有原材料的采购额
        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
    public List<MapDto> rawMaterialPurchaseAmountRatio(String startDateStr, String endDateStr) {
        List<Map<String, Object>> analysisResults;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysisByDate(startDateStr, endDateStr);
        } else {
            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
        }
        if (CollectionUtils.isEmpty(analysisResults)) {
            return new ArrayList<>();
        }
@@ -941,25 +1080,47 @@
    }
    @Override
    public List<MapDto> salesPurchaseStorageProductCount() {
    public List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonth = now.minusMonths(1);
        LocalDateTime lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        LocalDateTime lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        LocalDateTime currentMonthStart;
        LocalDateTime currentMonthEnd;
        LocalDateTime lastMonthStart;
        LocalDateTime lastMonthEnd;
        //  销售
        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, now);
        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
        //  采购
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, now);
        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
            LocalDate start = LocalDate.parse(startDate);
            LocalDate end = LocalDate.parse(endDate);
            currentMonthStart = start.atStartOfDay();
            currentMonthEnd = end.atTime(LocalTime.MAX);
            long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(start, end) + 1;
            LocalDate lastStart = start.minusDays(daysBetween);
            LocalDate lastEnd = start.minusDays(1);
            lastMonthStart = lastStart.atStartOfDay();
            lastMonthEnd = lastEnd.atTime(LocalTime.MAX);
        } else {
            currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            currentMonthEnd = now;
            LocalDateTime lastMonth = now.minusMonths(1);
            lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
            lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        }
        // 销售订单数(统计sales_ledger表)
        int currentSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, currentMonthStart)
                .le(SalesLedger::getEntryDate, currentMonthEnd)));
        int lastSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, lastMonthStart)
                .le(SalesLedger::getEntryDate, lastMonthEnd)));
        // 采购产品数
        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, currentMonthEnd);
        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
        //  储存
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, now);
        // 储存产品数
        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthEnd);
        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
        List<MapDto> list = new ArrayList<>();
        list.add(createMapDto("销售产品数", currentSales, lastSales));
        list.add(createMapDto("销售订单数", currentSales, lastSales));
        list.add(createMapDto("采购产品数", currentPurchase, lastPurchase));
        list.add(createMapDto("储存产品数", currentStorage, lastStorage));
@@ -988,7 +1149,7 @@
    }
    @Override
    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
    public List<Map<String, Object>> productInOutAnalysis(Integer type, String startDateStr, String endDateStr) {
        String targetName;
        if (type == 1) {
            targetName = "原材料";
@@ -1013,8 +1174,17 @@
        LocalDate now = LocalDate.now();
        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
        String startDate = now.minusDays(6).toString();
        String endDate = now.toString();
        LocalDate start;
        LocalDate end;
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            start = LocalDate.parse(startDateStr);
            end = LocalDate.parse(endDateStr);
        } else {
            end = now;
            start = now.minusDays(6);
        }
        String startDate = start.toString();
        String endDate = end.toString();
        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
                endDate + " 23:59:59");
@@ -1051,7 +1221,10 @@
    }
    @Override
    public List<MapDto> productTurnoverDays() {
    public List<MapDto> productTurnoverDays(String startDateStr, String endDateStr) {
        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
            return homeMapper.productTurnoverDaysByDate(startDateStr, endDateStr);
        }
        return homeMapper.productTurnoverDays();
    }
@@ -1239,33 +1412,21 @@
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //销售出库
        BigDecimal receivableBase = sumSalesContractAmount(startDate, endDate);
        String collectionRate = toRateString(monthlyIncome, receivableBase);
        String currentMonthText = currentMonth.toString();
        List<AccountStatement> receivableStatements = defaultList(accountStatementMapper.selectList(
                new LambdaQueryWrapper<AccountStatement>()
                        .eq(AccountStatement::getAccountType, 1)
                        .le(AccountStatement::getStatementMonth, currentMonthText)));
        long overdueNum = receivableStatements.stream()
                .filter(item -> item.getStatementMonth() != null && item.getStatementMonth().compareTo(currentMonthText) < 0)
                .filter(item -> defaultDecimal(item.getClosingBalance()).compareTo(BigDecimal.ZERO) > 0)
                .count();
        long totalReceivableCount = receivableStatements.size();
        String overdueRate = totalReceivableCount == 0
                ? "0.00"
                : BigDecimal.valueOf(overdueNum)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(totalReceivableCount), 2, RoundingMode.HALF_UP)
                .toString();
        //销售退货
        BigDecimal salesReturnAmount = sumSalesReturnAmount(startDate, endDate);
        //回款率=收款/(销售出库-销售退货)应收
        String collectionRate = toRateString(monthlyIncome, receivableBase.subtract(salesReturnAmount));
        //逾期数=(销售出库金额-销售退货金额)应收金额-收款金额
        BigDecimal overdueAmount = receivableBase.subtract(salesReturnAmount).subtract(monthlyIncome);
        //逾期率=逾期数/应收金额
        String overdueRate = toRateString(overdueAmount, receivableBase);
        dto.setMonthlyIncome(scaleMoney(monthlyIncome));
        dto.setCollectionRate(collectionRate);
        dto.setOverdueNum(BigDecimal.valueOf(overdueNum));
        dto.setOverdueNum(overdueAmount);
        dto.setOverdueRate(overdueRate);
        return dto;
    }
@@ -1277,16 +1438,25 @@
        YearMonth currentMonth = YearMonth.from(today);
        LocalDate startDate = currentMonth.atDay(1);
        LocalDate endDate = currentMonth.atEndOfMonth();
        //支出
        BigDecimal monthlyExpenditure = sumPaymentAmount(startDate, endDate);
        //采购入库
        BigDecimal payableBase = sumPurchaseContractAmount(startDate, endDate);
        //采购退货
        BigDecimal purchaseReturnAmount = sumPurchaseReturnAmount(startDate, endDate);
        //付款率=付款/(采购入库-采购退货)应付
        String paymentRate = toRateString(monthlyExpenditure, payableBase.subtract(purchaseReturnAmount));
        //收款
        BigDecimal monthlyIncome = sumCollectionAmount(startDate, endDate);
        //毛利润= 收款-支出
        BigDecimal grossProfit = monthlyIncome.subtract(monthlyExpenditure);
        //利润率=毛利润/收款
        String profitMarginRate = toRateString(grossProfit, monthlyIncome);
        dto.setMonthlyExpenditure(scaleMoney(monthlyExpenditure));
        dto.setPaymentRate(toRateString(monthlyExpenditure, payableBase));
        dto.setPaymentRate(paymentRate);
        dto.setGrossProfit(scaleMoney(grossProfit));
        dto.setProfitMarginRate(toRateString(grossProfit, monthlyIncome));
        dto.setProfitMarginRate(profitMarginRate);
        return dto;
    }
@@ -2317,20 +2487,43 @@
        return new LocalDate[]{startDate, endDate};
    }
    //计算日期内的销售出库金额
    private BigDecimal sumSalesContractAmount(LocalDate startDate, LocalDate endDate) {
        List<SalesLedger> salesLedgers = defaultList(salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
                .ge(SalesLedger::getEntryDate, toDate(startDate))
                .lt(SalesLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(salesLedgers, SalesLedger::getContractAmount);
        SalesOutboundDto salesOutboundDto = new SalesOutboundDto();
        salesOutboundDto.setStartDate(startDate);
        salesOutboundDto.setEndDate(endDate);
        List<SalesOutboundVo> salesOutboundVos = stockOutRecordMapper.listPageAccountSales(new Page(1, -1), salesOutboundDto).getRecords();
        return sumAmount(salesOutboundVos, SalesOutboundVo::getOutboundAmount);
    }
    //计算日期内的销售退货金额
    private BigDecimal sumSalesReturnAmount(LocalDate startDate, LocalDate endDate) {
        SalesReturnDto salesReturnDto = new SalesReturnDto();
        salesReturnDto.setStartDate(startDate);
        salesReturnDto.setEndDate(endDate);
        List<SalesReturnVo> salesReturnVos = returnManagementMapper.listPageAccountSalesReturn(new Page(1, -1), salesReturnDto).getRecords();
        return sumAmount(salesReturnVos, SalesReturnVo::getRefundAmount);
    }
    //计算日期内的采购入库金额
    private BigDecimal sumPurchaseContractAmount(LocalDate startDate, LocalDate endDate) {
        List<PurchaseLedger> purchaseLedgers = defaultList(purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
                .ge(PurchaseLedger::getEntryDate, toDate(startDate))
                .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(endDate))));
        return sumAmount(purchaseLedgers, PurchaseLedger::getContractAmount);
        PurchaseInboundDto purchaseInboundDto = new PurchaseInboundDto();
        purchaseInboundDto.setStartDate(startDate);
        purchaseInboundDto.setEndDate(endDate);
        List<PurchaseInboundVo> purchaseInboundVos = stockInRecordMapper.listPageAccountPurchase(new Page(1, -1), purchaseInboundDto).getRecords();
        return sumAmount(purchaseInboundVos, PurchaseInboundVo::getInboundAmount);
    }
    //计算日期内的采购退货金额
    private BigDecimal sumPurchaseReturnAmount(LocalDate startDate, LocalDate endDate) {
        PurchaseReturnDto purchaseReturnDto = new PurchaseReturnDto();
        purchaseReturnDto.setStartDate(startDate);
        purchaseReturnDto.setEndDate(endDate);
        List<PurchaseReturnVo> purchaseReturnVos = purchaseReturnOrdersMapper.listPageAccountPurchaseReturn(new Page(1, -1), purchaseReturnDto).getRecords();
        return sumAmount(purchaseReturnVos, PurchaseReturnVo::getTotalAmount);
    }
    //计算日期内的总收款金额
    private BigDecimal sumCollectionAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList(
                new LambdaQueryWrapper<AccountSalesCollection>()
@@ -2339,6 +2532,7 @@
        return sumAmount(collections, AccountSalesCollection::getCollectionAmount);
    }
    //计算日期内的总付款金额
    private BigDecimal sumPaymentAmount(LocalDate startDate, LocalDate endDate) {
        List<AccountPurchasePayment> payments = defaultList(accountPurchasePaymentMapper.selectList(
                new LambdaQueryWrapper<AccountPurchasePayment>()