gongchunyi
9 小时以前 98ed3d5f0866c50653bd46ee1daf840d408224ae
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -1,27 +1,43 @@
package com.ruoyi.home.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.ProductModel;
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.mapper.ProductOrderMapper;
import com.ruoyi.production.mapper.ProductWorkOrderMapper;
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;
@@ -34,6 +50,8 @@
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -42,12 +60,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;
/**
@@ -68,13 +85,7 @@
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private ProcurementRecordOutMapper procurementRecordOutMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
    private CustomStorageMapper customStorageMapper;
    @Autowired
    private QualityInspectMapper qualityStatisticsMapper;
@@ -89,10 +100,30 @@
    private PaymentRegistrationMapper paymentRegistrationMapper;
    @Autowired
    private LavorIssueMapper lavorIssueMapper;
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    private NoticeMapper noticeMapper;
    @Autowired
    private ProductOrderMapper productOrderMapper;
    @Autowired
    private ProductWorkOrderMapper productWorkOrderMapper;
    @Autowired
    private ProductModelMapper productModelMapper;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private StockUtils stockUtils;
    @Autowired
    private StaffOnJobMapper staffOnJobMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private SupplierManageMapper supplierManageMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserDeptMapper sysUserDeptMapper;
    @Override
    public HomeBusinessDto business() {
@@ -146,23 +177,35 @@
            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);
//        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);
        IPage<ProductModel> productModelIPage = productModelMapper.listPageProductModel(new Page<>(1, -1), new ProductModel());
        if(!CollectionUtils.isEmpty(productModelIPage.getRecords())){
            //获取规格id
            List<Long> modelIds = productModelIPage.getRecords().stream().map(ProductModel::getId).collect(Collectors.toList());
            BigDecimal stockQuantityTotal = modelIds.stream()
                    .map(stockUtils::getStockQuantity)
                    .map(map -> map.get("stockQuantity"))
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString());
        }
        // 获取当天入库数量
        LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordStorageLambdaQueryWrapper.ge(ProcurementRecordStorage::getCreateTime, now)  // 大于等于当天
@@ -172,7 +215,7 @@
                .map(ProcurementRecordStorage::getInboundNum)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        homeBusinessDto.setInventoryNum(stock.setScale(2, RoundingMode.HALF_UP).toString());
//        homeBusinessDto.setInventoryNum(stock.setScale(2, RoundingMode.HALF_UP).toString());
        homeBusinessDto.setTodayInventoryNum(stockAmount1.setScale(2, RoundingMode.HALF_UP).toString());
        return homeBusinessDto;
    }
@@ -357,29 +400,37 @@
        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;
    }
@@ -420,25 +471,29 @@
//                .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);
//        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)
        );
        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);
//        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));
        statisticsReceivablePayableDto.setReceivableMoney(receivableMoney.subtract(advanceMoney));
@@ -447,7 +502,13 @@
        return statisticsReceivablePayableDto;
    }
    public static <T> BigDecimal sumAmount(List<T> list, java.util.function.Function<T, BigDecimal> amountExtractor) {
        return list.stream()
                // 提取金额时,将null替换为BigDecimal.ZERO
                .map(item -> Optional.ofNullable(amountExtractor.apply(item)).orElse(BigDecimal.ZERO))
                // 累加,初始值为0,避免空流问题
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Autowired
    private DeviceRepairMapper deviceRepairMapper;
@@ -456,6 +517,7 @@
        // 审批协同待办
        Long aLong = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
                .eq(ApproveProcess::getApproveUserCurrentId, SecurityUtils.getUserId())
                .eq(ApproveProcess::getApproveDelete, 0)
                .in(ApproveProcess::getApproveStatus, 0, 1, 3));
        // 设备报修待办
        Long aLong1 = deviceRepairMapper.selectCount(new LambdaQueryWrapper<DeviceRepair>()
@@ -466,4 +528,404 @@
            put("deviceRepairTodo", aLong1);
        }};
    }
    @Override
    public Long noticesCount() {
        // 查询未过期的通知数量:状态为发布且过期时间大于等于当前日期
        return noticeMapper.selectCount(new LambdaQueryWrapper<Notice>()
                .eq(Notice::getStatus, 1) // 1表示发布状态
                .ge(Notice::getExpirationDate, new Date())); // 过期时间大于等于当前日期
    }
    @Override
    public ProductionProgressDto productionProgress() {
        ProductionProgressDto productionProgressDto = new ProductionProgressDto();
        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(count);
        productionProgressDto.setUncompletedOrderCount(count2);
        productionProgressDto.setPartialCompletedOrderCount(totalCount-count-count2);
        return productionProgressDto;
    }
    @Override
    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 = sysUserDeptMapper.selectCount(new LambdaQueryWrapper<SysUserDept>()
                        .eq(SysUserDept::getDeptId, dept.getDeptId()));
                if (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 sysUserCount = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getDelFlag, "0")
                .le(SysUser::getCreateTime, dateTime));
        Long staffCountItem = staffOnJobMapper.selectCount(new LambdaQueryWrapper<StaffOnJob>()
                .le(StaffOnJob::getCreateTime, dateTime));
        return sysUserCount + 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;
    }
}