刘帅
2026-09-17 dce5a34e89123f487cd22b9a4e5254ffc8a5df75
cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/code/DataStatisticsController.java
@@ -7,6 +7,12 @@
import com.dsh.course.feignClient.account.*;
import com.dsh.course.feignClient.account.model.*;
import com.dsh.course.feignClient.activity.PointMercharsClient;
import com.dsh.course.feignClient.activity.CouponClient;
import com.dsh.course.feignClient.activity.PayHuiminClient;
import com.dsh.course.feignClient.activity.model.CouponDeductionQuery;
import com.dsh.course.feignClient.activity.model.CouponDeductionVO;
import com.dsh.course.feignClient.activity.model.HuiminPayQuery;
import com.dsh.course.feignClient.activity.model.UserCouponPayment;
import com.dsh.course.feignClient.competition.CompetitionClient;
import com.dsh.course.feignClient.competition.model.CompetionVO;
import com.dsh.course.feignClient.competition.model.Competition;
@@ -43,6 +49,7 @@
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -72,6 +79,76 @@
            return storeId;
        }
        return null;
    }
    /**
     * 运营商统计的门店范围:平台(0)统计平台门店,其他值统计对应运营商门店。
     */
    private List<TStore> listStatisticsStores(Integer operatorId, boolean enabledOnly) {
        LambdaQueryWrapper<TStore> wrapper = new LambdaQueryWrapper<>();
        if (enabledOnly) {
            wrapper.eq(TStore::getState, 1);
        } else {
            wrapper.ne(TStore::getState, 3);
        }
        if (Integer.valueOf(0).equals(operatorId)) {
            wrapper.eq(TStore::getType, 1);
        } else {
            wrapper.eq(TStore::getType, 2).eq(TStore::getOperatorId, operatorId);
        }
        return storeService.list(wrapper);
    }
    /** 获取统计范围门店发生课程、订场或赛事消费的用户。 */
    private List<Integer> listStatisticsAppUserIds(Integer operatorId) {
        List<Integer> storeIds = listStatisticsStores(operatorId, true).stream()
                .map(TStore::getId).collect(Collectors.toList());
        if (storeIds.isEmpty()) {
            return Collections.singletonList(-1);
        }
        List<Integer> userIds = new ArrayList<>();
        List<Integer> courseUserIds = coursePackagePaymentClient.queryPaymentCoursePackage(storeIds);
        if (courseUserIds != null) {
            userIds.addAll(courseUserIds);
        }
        userIds.addAll(siteBookingService.list(new QueryWrapper<SiteBooking>()
                        .in("storeId", storeIds).eq("state", 1))
                .stream().map(SiteBooking::getAppUserId).filter(Objects::nonNull)
                .collect(Collectors.toList()));
        List<Integer> competitionUserIds = competitionClient.queryAppUserId(storeIds);
        if (competitionUserIds != null) {
            userIds.addAll(competitionUserIds);
        }
        List<Integer> validUserIds = userIds.stream().filter(Objects::nonNull).distinct()
                .collect(Collectors.toList());
        return validUserIds.isEmpty() ? Collections.singletonList(-1) : validUserIds;
    }
    /**
     * 计算门店每日营业整小时数。相同的起止时间表示全天营业,结束时间早于
     * 开始时间时按跨午夜营业处理。
     */
    private long getDailyBusinessHours(String startTime, String endTime) {
        if (ToolUtil.isEmpty(startTime) || ToolUtil.isEmpty(endTime)) {
            return 0;
        }
        try {
            LocalTime start = LocalTime.parse(startTime);
            LocalTime end = LocalTime.parse(endTime);
            long minutes = Duration.between(start, end).toMinutes();
            if (minutes <= 0) {
                minutes += Duration.ofDays(1).toMinutes();
            }
            return Duration.ofMinutes(minutes).toHours();
        } catch (DateTimeException e) {
            return 0;
        }
    }
    private long getStoreOperatingHours(TStore store, Date now) {
        long operatingDays = Math.max(0, DateUtil.betweenDay(store.getCreateTime(), now, true));
        return operatingDays * getDailyBusinessHours(store.getStartTime(), store.getEndTime());
    }
    @Autowired
@@ -106,6 +183,10 @@
    private SiteClient siteClient;
    @Resource
    private PointMercharsClient pointMercharsClient;
    @Resource
    private CouponClient couponClient;
    @Resource
    private PayHuiminClient payHuiminClient;
    @Resource
    private RefundCoursePackageClient refundCoursePackageClient;
    @Resource
@@ -143,7 +224,7 @@
        if(objectId>0){
            operatorId=objectId;
            if(storeId==null){
                cityManagerId= storeService.list(new QueryWrapper<TStore>().eq("cityManagerId", objectId)).stream().map(TStore::getId).collect(Collectors.toList());
                cityManagerId= storeService.list(new QueryWrapper<TStore>().eq("cityManagerId", objectId).ne("state", 3)).stream().map(TStore::getId).collect(Collectors.toList());
            }else {
                cityManagerId.add(storeId);
            }
@@ -155,10 +236,13 @@
        RechargeRecordsQuery rechargeRecordsQuery = new RechargeRecordsQuery();
        // 赛事活动查询query
        CompetitionQuery competitionQuery = new CompetitionQuery();
        competitionQuery.setIncludeRefund(true);
        // 会员支付查询query
        IncomeQuery incomeQuery = new IncomeQuery();
        incomeQuery.setIncludeRefund(true);
        // 场地预约记录查询query
        SiteBookingQuery siteBookingQuery = new SiteBookingQuery();
        siteBookingQuery.setIncludeRefund(true);
        // 智慧球场查询query
        GameDataQuery gameDataQuery = new GameDataQuery();
        // 筛选运营商的是时候使用
@@ -166,7 +250,7 @@
        // 根据运营商ID 查询运营商下的所有门店
        if (storeId==null){
            if (operatorId!=null){
                List<TStore> stores = storeService.list(new QueryWrapper<TStore>().eq("operatorId", operatorId));
                List<TStore> stores = listStatisticsStores(operatorId, false);
                // 拿到该运营商下的门店id集合
                storeIds = stores.stream().map(TStore::getId).collect(Collectors.toList());
                query.setStoresIds(storeIds);
@@ -193,9 +277,21 @@
                 incomeQuery.setInsertType(roleType);
        rechargeRecordsQuery.setObjectId(UserExt.getUser().getObjectId());
        List<IncomeVO> incomeVOS = new ArrayList<>();
        Set<Long> usedCouponIds = new HashSet<>();
        // 会员支付记录
        List<VipPayment> vipPayments = vipPaymentClient.registrationList(incomeQuery);
        for (VipPayment vipPayment : vipPayments) {
            if (vipPayment.getPayStatus() == 3 && vipPayment.getAmount() != null) {
                IncomeVO payment = new IncomeVO();
                payment.setInsertTime(vipPayment.getPayTime() == null ? vipPayment.getInsertTime() : vipPayment.getPayTime());
                payment.setAmount(BigDecimal.valueOf(vipPayment.getAmount()));
                incomeVOS.add(payment);
                IncomeVO refund = new IncomeVO();
                refund.setInsertTime(vipPayment.getRefundTime() == null ? vipPayment.getInsertTime() : vipPayment.getRefundTime());
                refund.setRefundAmount(BigDecimal.valueOf(vipPayment.getAmount()));
                incomeVOS.add(refund);
                continue;
            }
            IncomeVO temp = new IncomeVO();
            if (vipPayment.getState()!=3){
                temp.setInsertTime(vipPayment.getPayTime());
@@ -229,6 +325,10 @@
                temp.setAmount(registerOrderVO.getMoney());
            }
            if (registerOrderVO.getPayStatus()==2){
                if (registerOrderVO.getUserCouponId() != null) {
                    usedCouponIds.add(registerOrderVO.getUserCouponId());
                    temp.setId(registerOrderVO.getUserCouponId().toString());
                }
                incomeVOS.add(temp);
            }
        }
@@ -239,13 +339,28 @@
        // 报名活动
        List<PaymentCompetition> competitions = competitionClient.listAllPayment(competitionQuery);
        for (PaymentCompetition competition : competitions) {
            if (competition.getPayStatus() != 2) {
                continue;
            }
            if (competition.getPayType()==3 || competition.getPayType()==4){
                continue;
            }
            if (competition.getState()==3)continue;
            if (competition.getState()==3) {
                continue;
            }
            if (competition.getPayStatus() == 3 && competition.getAmount() != null) {
                IncomeVO payment = new IncomeVO();
                payment.setInsertTime(competition.getPayTime() == null
                        ? competition.getInsertTime() : competition.getPayTime());
                payment.setAmount(BigDecimal.valueOf(competition.getAmount()));
                incomeVOS.add(payment);
                IncomeVO refund = new IncomeVO();
                refund.setInsertTime(competition.getRefundTime() == null
                        ? competition.getInsertTime() : competition.getRefundTime());
                refund.setRefundAmount(BigDecimal.valueOf(competition.getAmount()));
                incomeVOS.add(refund);
                continue;
            }
            if (competition.getPayStatus() != 2) {
                continue;
            }
            IncomeVO temp = new IncomeVO();
            temp.setInsertTime(competition.getPayTime());
            if (competition.getAmount() != null) {
@@ -260,7 +375,22 @@
            List<SiteBooking> siteBookings = siteClient.listAll(siteBookingQuery);
            for (SiteBooking siteBooking : siteBookings) {
                IncomeVO temp = new IncomeVO();
                if(siteBooking.getStatus()==0 || siteBooking.getStatus()==5){
                if (siteBooking.getStatus() == 5) {
                    if (siteBooking.getPayMoney() != null) {
                        IncomeVO payment = new IncomeVO();
                        payment.setInsertTime(siteBooking.getPayTime() == null
                                ? siteBooking.getInsertTime() : siteBooking.getPayTime());
                        payment.setAmount(BigDecimal.valueOf(siteBooking.getPayMoney()));
                        incomeVOS.add(payment);
                        IncomeVO refund = new IncomeVO();
                        refund.setInsertTime(siteBooking.getCancelTime() == null
                                ? siteBooking.getInsertTime() : siteBooking.getCancelTime());
                        refund.setRefundAmount(BigDecimal.valueOf(siteBooking.getPayMoney()));
                        incomeVOS.add(refund);
                    }
                    continue;
                }
                if (siteBooking.getStatus() == 0) {
                    continue;
                }
                if (siteBooking.getPayType() ==3 || siteBooking.getPayType() ==4)continue;
@@ -268,7 +398,22 @@
                if (siteBooking.getPayMoney() != null) {
                    temp.setAmount(new BigDecimal(siteBooking.getPayMoney().toString()));
                }
                if (siteBooking.getUserCouponId() != null) {
                    usedCouponIds.add(siteBooking.getUserCouponId());
                    temp.setId(siteBooking.getUserCouponId().toString());
                }
                incomeVOS.add(temp);
            }
        }
        Map<Long, BigDecimal> couponDeductionAmounts = getCouponDeductionAmounts(usedCouponIds);
        applyCouponDeductions(incomeVOS, couponDeductionAmounts);
        // 惠民卡和优惠券购买不归属到单一课程/场地门店,当前仅在平台或运营商汇总中计入。
        if (storeId == null) {
            appendHuiminIncome(incomeVOS, operatorId);
            if (operatorId == null) {
                appendCouponPurchaseIncome(incomeVOS);
            }
        }
@@ -316,176 +461,211 @@
                incomeVOS.add(temp);
            }
        }
        // 最终返回的数据
        Map<String, List<Object>> monthlyData = new HashMap<>();
        // 创建一个 Map 用于存储每年的累计 amount
        Map<Integer, BigDecimal> yearToTotalAmountMap = new HashMap<>();
        // 初始化一个 BigDecimal 用于累加
        BigDecimal totalAmount1 = BigDecimal.ZERO;
        // 用于返回图表上的总营收 年度营收 月度 周度 日度
        List<Object> objects = new ArrayList<>();
        // 遍历 incomeVOS
        for (IncomeVO incomeVO1 : incomeVOS) {
            // 总营收
            BigDecimal total = incomeVO1.getAmount();
            // 累加到总金额
            totalAmount1 = totalAmount1.add(total);
            Date insertTime = incomeVO1.getInsertTime();
            BigDecimal amount1 = incomeVO1.getAmount();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(insertTime);
            int year = calendar.get(Calendar.YEAR); // 获取年份
            // 更新年份对应的累计 amount
            if (yearToTotalAmountMap.containsKey(year)) {
                BigDecimal totalAmount = yearToTotalAmountMap.get(year);
                yearToTotalAmountMap.put(year, totalAmount.add(amount1));
            } else {
                yearToTotalAmountMap.put(year, amount1);
            }
        }
        objects.add(totalAmount1);
        // 创建一个新的列表,按年份升序排列
        List<Integer> sortedYears = new ArrayList<>(yearToTotalAmountMap.keySet());
        Collections.sort(sortedYears);
        // 创建一个新的 IncomeVO 列表,包含按年份累计的 amount
        List<Object> resultIncomeVOS = new ArrayList<>();
        // 当前年往前推十年
        List<Integer> integers1 = new ArrayList<>();
        int years = DateUtil.year(new Date());
        for (int i = 0; i < 10; i++) {
            integers1.add(years-i);
         }
        List<Object> collect = integers1.stream().sorted().collect(Collectors.toList());
        List<Object> objects1 = new ArrayList<>();
        for (Object o : collect) {
            BigDecimal orDefault = yearToTotalAmountMap.getOrDefault(Integer.valueOf(o.toString()), BigDecimal.ZERO);
            objects1.add(orDefault);
        }
        monthlyData.put("yearIncome",objects1);
        BigDecimal yearIn = new BigDecimal("0.00");
        for (Object o : objects1) {
            yearIn = yearIn.add(new BigDecimal(o.toString()));
        }
        BigDecimal orDefault = yearToTotalAmountMap.getOrDefault(years, BigDecimal.ZERO);
        objects.add(orDefault);
        // 创建一个 Map 用于存储今年每个月的累计 amount
        Map<Integer, BigDecimal> monthToTotalAmountMap = new HashMap<>();
        Calendar calendar = Calendar.getInstance();
        int currentYear = calendar.get(Calendar.YEAR);
        List<BigDecimal> monthlyAmountList = new ArrayList<>(Collections.nCopies(12, BigDecimal.ZERO));
        for (IncomeVO incomeVO1 : incomeVOS) {
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(incomeVO1.getInsertTime());
            int year = calendar1.get(Calendar.YEAR);
            int month = calendar1.get(Calendar.MONTH) + 1; // 月份从0开始,所以需要加1
            if (year == currentYear) {
                BigDecimal totalAmount = monthlyAmountList.get(month - 1);
                totalAmount = totalAmount.add(incomeVO1.getAmount());
                monthlyAmountList.set(month - 1, totalAmount);
            }
        }
        monthlyData.put("monthIncome", Collections.singletonList(monthlyAmountList));
        //yearMount
        BigDecimal monthI = new BigDecimal("0.00");
        for (BigDecimal bigDecimal : monthlyAmountList) {
            monthI = monthI.add(bigDecimal);
        }
        objects.add(monthI);
        // 获取当前月份
        LocalDate currentDate = LocalDate.now();
        int currentMonth = currentDate.getMonthValue();
        // 初始化一个长度为当前月份的天数的列表,并将每一天的金额初始化为零
        int daysInCurrentMonth = currentDate.lengthOfMonth();
        List<BigDecimal> weekAmount = new ArrayList<>();
        // 获取最近四周
        LocalDateTime minNow = LocalDateTime.now().with(LocalTime.MIN);
        LocalDateTime maxNow = LocalDateTime.now().with(LocalTime.MAX);
        // 周度数据
        LocalDateTime week1Start = minNow.minusDays(6);
        LocalDateTime week1End   = maxNow;
        LocalDateTime week2Start = minNow.minusDays(13);
        LocalDateTime week2End = minNow.minusDays(7);
        LocalDateTime week3Start = minNow.minusDays(20);
        LocalDateTime week3End = minNow.minusDays(14);
        LocalDateTime week4Start = minNow.minusDays(27);
        LocalDateTime week4End = minNow.minusDays(21);
        // 计算每个周的amount总和
        BigDecimal tAmount1 = BigDecimal.ZERO;
        BigDecimal tAmount2 = BigDecimal.ZERO;
        BigDecimal tAmount3 = BigDecimal.ZERO;
        BigDecimal tAmount4 = BigDecimal.ZERO;
        for (IncomeVO incomeVO1 : incomeVOS) {
            Date insertTime = incomeVO1.getInsertTime();
            // 将Date转换为LocalDateTime
            Instant instant = insertTime.toInstant();
            ZoneId zoneId = ZoneId.systemDefault();
            LocalDateTime dateTime = instant.atZone(zoneId).toLocalDateTime();
            // 判断该数据属于哪个周
            if (dateTime.isAfter(week1Start) && dateTime.isBefore(week1End)) {
                BigDecimal amount1 = incomeVO1.getAmount();
                tAmount1=tAmount1.add(amount1);
            } else if (dateTime.isAfter(week2Start) && dateTime.isBefore(week2End)) {
                BigDecimal amount1 = incomeVO1.getAmount();
                tAmount2=tAmount2.add(amount1);
            } else if (dateTime.isAfter(week3Start) && dateTime.isBefore(week3End)) {
                BigDecimal amount1 = incomeVO1.getAmount();
                tAmount4=tAmount3.add(amount1);
            } else if (dateTime.isAfter(week4Start) && dateTime.isBefore(week4End)) {
                BigDecimal amount1 = incomeVO1.getAmount();
                tAmount4=tAmount4.add(amount1);
            }
        }
        weekAmount.add(tAmount1);
        weekAmount.add(tAmount2);
        weekAmount.add(tAmount3);
        weekAmount.add(tAmount4);
        monthlyData.put("weekIncome", Collections.singletonList(weekAmount));
        BigDecimal weekIn = new BigDecimal("0.00");
        for (BigDecimal bigDecimal : weekAmount) {
            weekIn = weekIn.add(bigDecimal);
        }
        objects.add(weekIn);
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<BigDecimal> amountByDay = new ArrayList<>(Collections.nCopies(7, BigDecimal.ZERO));
        for (IncomeVO incomeVO3 : incomeVOS) {
            Date insertTime = incomeVO3.getInsertTime();
            // 将 Date 转换为 LocalDateTime
            LocalDateTime insertDateTime = LocalDateTime.ofInstant(insertTime.toInstant(), ZoneId.systemDefault());
            // 计算日期与当前日期的差距
            long daysDifference = ChronoUnit.DAYS.between(insertDateTime.toLocalDate(), currentDateTime.toLocalDate());
            // 如果日期在七天内,累加金额
            if (daysDifference >= 0 && daysDifference < 7) {
                int index = (int) (6 - daysDifference); // 计算对应的索引位置
                BigDecimal amount3 = incomeVO3.getAmount();
                BigDecimal updatedAmount = amountByDay.get(index).add(amount3);
                amountByDay.set(index, updatedAmount);
            }
        }
        monthlyData.put("dayIncome", Collections.singletonList(amountByDay));
        BigDecimal totalToday = new BigDecimal("0.00");
        for (BigDecimal bigDecimal : amountByDay) {
            totalToday = totalToday.add(bigDecimal);
        }
        objects.add(totalToday);
        // 最后put
        monthlyData.put("yearMount",objects);
        ArrayList<Object> integers = new ArrayList<>();
        int year = DateUtil.year(new Date());
        for (int i = 0; i < 10; i++) {
            integers.add(year-i);
        }
        List<Object> collect1 = integers.stream().sorted().collect(Collectors.toList());
        monthlyData.put("year",collect1);
        Map<String, Object> monthlyData = buildIncomeStatistics(incomeVOS);
        return monthlyData;
    }
    /** 惠民卡采用支付、退款两条事实流水,赠送卡不计营收。 */
    private void appendHuiminIncome(List<IncomeVO> incomeRecords, Integer operatorId) {
        HuiminPayQuery query = new HuiminPayQuery();
        query.setGiftPaymentType(3);
        if (operatorId != null) {
            query.setOperatorId(operatorId.longValue());
        }
        List<SalesDetailVO> records = payHuiminClient.exportData(query);
        if (records == null) {
            return;
        }
        for (SalesDetailVO record : records) {
            if (record == null || record.getSalesMoney() == null || record.getPaymentTime() == null) {
                continue;
            }
            IncomeVO payment = new IncomeVO();
            payment.setInsertTime(record.getPaymentTime());
            payment.setAmount(record.getSalesMoney());
            incomeRecords.add(payment);
            if (Integer.valueOf(3).equals(record.getStatus())
                    || Integer.valueOf(2).equals(record.getRefundStatus())
                    || Integer.valueOf(3).equals(record.getRefundStatus())) {
                IncomeVO refund = new IncomeVO();
                refund.setInsertTime(record.getRefundTime() == null ? record.getPaymentTime() : record.getRefundTime());
                refund.setRefundAmount(record.getSalesMoney());
                incomeRecords.add(refund);
            }
        }
    }
    /** 仅计入积分+现金的成功优惠券购买,纯积分兑换不形成现金营收。 */
    private void appendCouponPurchaseIncome(List<IncomeVO> incomeRecords) {
        List<UserCouponPayment> records = couponClient.listPaidCouponPayments();
        if (records == null) {
            return;
        }
        for (UserCouponPayment record : records) {
            if (record == null || record.getCash() == null || record.getPaymentTime() == null) {
                continue;
            }
            IncomeVO payment = new IncomeVO();
            payment.setInsertTime(record.getPaymentTime());
            payment.setAmount(BigDecimal.valueOf(record.getCash()));
            incomeRecords.add(payment);
        }
    }
    /** 构建总计及年、月、周、日四个粒度的四维收入统计结果。 */
    private Map<String, Object> buildIncomeStatistics(List<IncomeVO> incomeRecords) {
        Map<String, Object> result = new HashMap<>();
        LocalDate today = LocalDate.now();
        int currentYear = today.getYear();
        List<IncomeStatisticsVO> yearly = new ArrayList<>();
        List<Object> years = new ArrayList<>();
        List<String> yearLabels = new ArrayList<>();
        for (int year = currentYear - 9; year <= currentYear; year++) {
            final int statisticYear = year;
            yearly.add(sumIncomeStatistics(incomeRecords, record -> getRecordDate(record)
                    .getYear() == statisticYear));
            years.add(year);
            yearLabels.add(String.valueOf(year));
        }
        List<IncomeStatisticsVO> monthly = new ArrayList<>();
        List<String> monthLabels = new ArrayList<>();
        for (int month = 1; month <= 12; month++) {
            final int statisticMonth = month;
            monthly.add(sumIncomeStatistics(incomeRecords, record -> {
                LocalDate date = getRecordDate(record);
                return date.getYear() == currentYear && date.getMonthValue() == statisticMonth;
            }));
            monthLabels.add(String.format("%d-%02d", currentYear, month));
        }
        List<IncomeStatisticsVO> weekly = new ArrayList<>();
        List<String> weekLabels = new ArrayList<>();
        for (int weekIndex = 3; weekIndex >= 0; weekIndex--) {
            LocalDate startDate = today.minusDays(weekIndex * 7L + 6L);
            LocalDate endDate = today.minusDays(weekIndex * 7L);
            weekly.add(sumIncomeStatistics(incomeRecords, record -> {
                LocalDate date = getRecordDate(record);
                return !date.isBefore(startDate) && !date.isAfter(endDate);
            }));
            weekLabels.add(startDate.format(DateTimeFormatter.ISO_LOCAL_DATE)
                    + " 至 " + endDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
        }
        List<IncomeStatisticsVO> daily = new ArrayList<>();
        List<String> dayLabels = new ArrayList<>();
        for (int dayIndex = 6; dayIndex >= 0; dayIndex--) {
            LocalDate statisticDate = today.minusDays(dayIndex);
            daily.add(sumIncomeStatistics(incomeRecords, record -> getRecordDate(record)
                    .equals(statisticDate)));
            dayLabels.add(statisticDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
        }
        IncomeStatisticsVO total = sumIncomeStatistics(incomeRecords, record -> true);
        IncomeStatisticsVO currentYearStatistics = yearly.get(yearly.size() - 1);
        IncomeStatisticsVO currentMonthStatistics = monthly.get(today.getMonthValue() - 1);
        IncomeStatisticsVO currentWeekStatistics = sumIncomeStatistics(incomeRecords, record -> {
            LocalDate date = getRecordDate(record);
            return !date.isBefore(today.minusDays(6)) && !date.isAfter(today);
        });
        IncomeStatisticsVO currentDayStatistics = daily.get(daily.size() - 1);
        result.put("incomeStatistics", total);
        result.put("netIncomeAmount", total.getNetIncomeAmount());
        result.put("couponDeductionAmount", total.getCouponDeductionAmount());
        result.put("refundAmount", total.getRefundAmount());
        result.put("yearIncomeStatistics", yearly);
        result.put("monthIncomeStatistics", monthly);
        result.put("weekIncomeStatistics", weekly);
        result.put("dayIncomeStatistics", daily);
        result.put("year", years);
        result.put("yearLabels", yearLabels);
        result.put("monthLabels", monthLabels);
        result.put("weekLabels", weekLabels);
        result.put("dayLabels", dayLabels);
        // 兼容既有图表消费字段,值调整为总营收金额。
        result.put("yearIncome", yearly.stream().map(IncomeStatisticsVO::getTotalIncomeAmount)
                .collect(Collectors.toList()));
        result.put("monthIncome", Collections.singletonList(monthly.stream()
                .map(IncomeStatisticsVO::getTotalIncomeAmount).collect(Collectors.toList())));
        result.put("weekIncome", Collections.singletonList(weekly.stream()
                .map(IncomeStatisticsVO::getTotalIncomeAmount).collect(Collectors.toList())));
        result.put("dayIncome", Collections.singletonList(daily.stream()
                .map(IncomeStatisticsVO::getTotalIncomeAmount).collect(Collectors.toList())));
        result.put("yearMount", Arrays.asList(total.getTotalIncomeAmount(),
                currentYearStatistics.getTotalIncomeAmount(), currentMonthStatistics.getTotalIncomeAmount(),
                currentWeekStatistics.getTotalIncomeAmount(), currentDayStatistics.getTotalIncomeAmount()));
        return result;
    }
    /** 按给定时间条件汇总四个收入维度。 */
    private IncomeStatisticsVO sumIncomeStatistics(List<IncomeVO> incomeRecords,
                                                     Predicate<IncomeVO> timePredicate) {
        IncomeStatisticsVO statistics = new IncomeStatisticsVO();
        for (IncomeVO record : incomeRecords) {
            if (record.getInsertTime() == null || !timePredicate.test(record)) {
                continue;
            }
            BigDecimal receivedAmount = getZeroIfNull(record.getAmount());
            BigDecimal couponAmount = getZeroIfNull(record.getCouponDeductionAmount());
            BigDecimal refundAmount = getZeroIfNull(record.getRefundAmount());
            statistics.setTotalIncomeAmount(statistics.getTotalIncomeAmount()
                    .add(receivedAmount).add(couponAmount));
            statistics.setCouponDeductionAmount(statistics.getCouponDeductionAmount().add(couponAmount));
            statistics.setRefundAmount(statistics.getRefundAmount().add(refundAmount));
        }
        statistics.setNetIncomeAmount(statistics.getTotalIncomeAmount()
                .subtract(statistics.getCouponDeductionAmount())
                .subtract(statistics.getRefundAmount()));
        return statistics;
    }
    /** 将收入记录时间转换为本地日期。 */
    private LocalDate getRecordDate(IncomeVO record) {
        return LocalDateTime.ofInstant(record.getInsertTime().toInstant(), ZoneId.systemDefault()).toLocalDate();
    }
    /** 空金额按零参与统计。 */
    private BigDecimal getZeroIfNull(BigDecimal amount) {
        return amount == null ? BigDecimal.ZERO : amount;
    }
    /** 将批量查询到的优惠券抵扣金额回填到对应收入记录。 */
    private void applyCouponDeductions(List<IncomeVO> incomeRecords,
                                       Map<Long, BigDecimal> couponDeductionAmounts) {
        for (IncomeVO record : incomeRecords) {
            if (record.getId() == null) {
                continue;
            }
            try {
                record.setCouponDeductionAmount(couponDeductionAmounts.getOrDefault(
                        Long.valueOf(record.getId()), BigDecimal.ZERO));
            } catch (NumberFormatException ignored) {
                record.setCouponDeductionAmount(BigDecimal.ZERO);
            }
        }
    }
    /**
     * 批量获取已使用优惠券的抵扣金额,避免按订单逐条调用优惠券服务。
     *
     * @param usedCouponIds 用户优惠券领取记录 ID 集合
     * @return 用户优惠券领取记录 ID 与抵扣金额的映射(单位:元)
     */
    private Map<Long, BigDecimal> getCouponDeductionAmounts(Set<Long> usedCouponIds) {
        if (usedCouponIds.isEmpty()) {
            return Collections.emptyMap();
        }
        CouponDeductionQuery query = new CouponDeductionQuery();
        query.setUserCouponIds(new ArrayList<>(usedCouponIds));
        List<CouponDeductionVO> deductions = couponClient.queryDeductionAmounts(query);
        if (deductions == null || deductions.isEmpty()) {
            return Collections.emptyMap();
        }
        return deductions.stream()
                .filter(deduction -> deduction.getUserCouponId() != null)
                .collect(Collectors.toMap(CouponDeductionVO::getUserCouponId,
                        deduction -> getZeroIfNull(deduction.getDeductionAmount()), BigDecimal::add));
    }
    /**
@@ -693,10 +873,7 @@
    public Object backYys(Integer id) {
        id = resolveOperatorId(id);
        HashMap<String, Object> map = new HashMap<>();
        List<Integer> userPt = appUserClient.getUserYYs(id);
        if(userPt.size()==0){
            userPt.add(-1);
        }
        List<Integer> userPt = listStatisticsAppUserIds(id);
        ArrayList<Object> integers = new ArrayList<>();
        int year = DateUtil.year(new Date());
        List<TBackRecord> thisYear = backRecordService.list(new LambdaQueryWrapper<TBackRecord>().like(TBackRecord::getTime, year).in(TBackRecord::getUserId,userPt));
@@ -806,6 +983,10 @@
        //赛事
        List<Integer> list2 = competitionClient.queryAppUserId(collect1);
        list.addAll(list2);
        // 没有消费用户时,避免 MyBatis-Plus 生成 userId IN () 的非法 SQL。
        if (list.isEmpty()) {
            list.add(-1);
        }
        //门票
        HashMap<String, Object> map = new HashMap<>();
@@ -926,8 +1107,12 @@
    @ResponseBody
    public Object stuYys(Integer id) {
        id = resolveOperatorId(id);
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        // 运营商没有门店时,后续查询不能生成 storeId IN ()。
        if (collect1.isEmpty()) {
            collect1.add(-1);
        }
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
        //场地
@@ -937,6 +1122,10 @@
        //赛事
        List<Integer> list2 = competitionClient.queryAppUserId(collect1);
        list.addAll(list2);
        // 下游会员统计同样使用 IN 查询,空用户列表按无匹配记录处理。
        if (list.isEmpty()) {
            list.add(-1);
        }
        //门票
        HashMap<String, Object> map = appUserClient.stuYys(list);
@@ -974,7 +1163,7 @@
    @ResponseBody
    public Object courseYys(Integer id) {
        id = resolveOperatorId(id);
        List<Integer> userPt = appUserClient.getUserYYs(id);
        List<Integer> userPt = listStatisticsAppUserIds(id);
        HashMap<String, Object> map = coursePackagePaymentClient.coursePt(userPt);
        return map;
    }
@@ -991,7 +1180,7 @@
    @ResponseBody
    public Object userAndVipYys(Integer id) {
        id = resolveOperatorId(id);
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
@@ -1031,23 +1220,10 @@
        List<Integer> userPt = appUserClient.getUserPt(Arrays.asList(1));
        HashMap<String, Object> map = competitionClient.actPt(userPt);
        List<TStore> list = storeService.list(new LambdaQueryWrapper<TStore>().eq(TStore::getOperatorId, 0));
        List<TStore> list = storeService.list(new LambdaQueryWrapper<TStore>().eq(TStore::getOperatorId, 0).ne(TStore::getState, 3));
        long l1=0;
        for (TStore tStore : list) {
            long l = DateUtil.betweenDay(tStore.getCreateTime(), new Date(), true);
            String startTime = tStore.getStartTime();
            String endTime = tStore.getEndTime();
            String s = startTime.split(":")[0];
            String s1 = endTime.split(":")[1];
            Integer integer = Integer.valueOf(s);
            Integer integer1 = Integer.valueOf(s1);
            Integer a =0;
            if(integer==0 && integer1==0){
                a=24;
            }else {
                a= integer1-integer;
            }
            l1 += l * a;
            l1 += getStoreOperatingHours(tStore, new Date());
        }
        map.put("allHour",l1);
        return map;
@@ -1057,7 +1233,7 @@
    public Object actYys(Integer id) {
        id = resolveOperatorId(id);
        CompetionVO competionVO = new CompetionVO();
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
@@ -1076,23 +1252,10 @@
        competionVO.setStoreIds(collect1);
        HashMap<String, Object> map = competitionClient.actYys(competionVO);
        List<TStore> lists = storeService.list(new LambdaQueryWrapper<TStore>().eq(TStore::getOperatorId, id));
        List<TStore> lists = listStatisticsStores(id, true);
        long l1=0;
        for (TStore tStore : lists) {
            long l = DateUtil.betweenDay(tStore.getCreateTime(), new Date(), true);
            String startTime = tStore.getStartTime();
            String endTime = tStore.getEndTime();
            String s = startTime.split(":")[0];
            String s1 = endTime.split(":")[1];
            Integer integer = Integer.valueOf(s);
            Integer integer1 = Integer.valueOf(s1);
            Integer a =0;
            if(integer==0 && integer1==0){
                a=24;
            }else {
                a= integer-integer1;
            }
            l1 += l * a;
            l1 += getStoreOperatingHours(tStore, new Date());
        }
        map.put("allHour",l1);
        return map;
@@ -1100,39 +1263,22 @@
    @RequestMapping(value = "/actStore")
    @ResponseBody
    public Object actStore(Integer id) {
        List<Integer> collect1 = Arrays.asList(id);
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
        //场地
        List<SiteBooking> list1 = siteBookingService.list(new QueryWrapper<SiteBooking>().in("storeId", collect1).eq("state", 1));
        List<Integer> collect = list1.stream().map(SiteBooking::getAppUserId).collect(Collectors.toList());
        list.addAll(collect);
        //赛事
        List<Integer> list2 = competitionClient.queryAppUserId(collect1);
        list.addAll(list2);
        //门票
        HashMap<String, Object> map = competitionClient.actPt(list);
        List<TStore> lists = storeService.list(new LambdaQueryWrapper<TStore>().eq(TStore::getOperatorId, 0));
        long l1=0;
        for (TStore tStore : lists) {
            long l = DateUtil.betweenDay(tStore.getCreateTime(), new Date(), true);
            String startTime = tStore.getStartTime();
            String endTime = tStore.getEndTime();
            String s = startTime.split(":")[0];
            String s1 = endTime.split(":")[1];
            Integer integer = Integer.valueOf(s);
            Integer integer1 = Integer.valueOf(s1);
            Integer a =0;
            if(integer==0 && integer1==0){
                a=24;
            }else {
                a= integer1-integer;
            }
            l1 += l * a;
        if (Integer.valueOf(3).equals(UserExt.getUser().getObjectType())) {
            id = UserExt.getUser().getObjectId();
        } else {
            id = resolveStoreId(id);
        }
        map.put("allHour",l1);
        TStore store = id == null ? null : storeService.getById(id);
        CompetionVO competionVO = new CompetionVO();
        if (store == null || !Integer.valueOf(1).equals(store.getState())) {
            competionVO.setStoreIds(Collections.singletonList(-1));
        } else {
            List<Integer> storeIds = Collections.singletonList(store.getId());
            competionVO.setStoreIds(storeIds);
        }
        HashMap<String, Object> map = competitionClient.actStore(competionVO);
        map.put("allHour", store == null || !Integer.valueOf(1).equals(store.getState())
                ? 0 : getStoreOperatingHours(store, new Date()));
        return map;
    }
@@ -1167,7 +1313,7 @@
//        List<User> list = sysUserService.list(new LambdaQueryWrapper<User>().eq(User::getObjectType, 2));
//        List<Integer> ids = list.stream().map(User::getId).collect(Collectors.toList());
//        List<Integer> userIds = appUserClient.getUserYYs(id);
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        QueryDataFee queryDataFee = new QueryDataFee(time, collect1);
        // 课程收入
@@ -1213,7 +1359,7 @@
        if(collect.size()==0){
            collect.add(-1);
        }
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collect));
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collect).ne(TSite::getState, 3));
        for (SiteBooking siteBooking : list1) {
            for (TSite tSite : list3) {
                if(siteBooking.getSiteId().equals(tSite.getId())){
@@ -1223,7 +1369,7 @@
        }
        List<TSiteType> list2 = siteTypeService.list();
        List<TSiteType> list2 = siteTypeService.list(new LambdaQueryWrapper<TSiteType>().ne(TSiteType::getState, 3));
        List<Map<String,Object>> mapList = new ArrayList<>();
@@ -1279,6 +1425,10 @@
        //赛事
        List<Integer> list2 = competitionClient.queryAppUserId(collect1);
        list.addAll(list2);
        // 没有消费用户时,避免 MyBatis-Plus 生成 appUserId IN () 的非法 SQL。
        if (list.isEmpty()) {
            list.add(-1);
        }
        //门票
        QueryDataFee queryDataFee = new QueryDataFee(time, list);
@@ -1321,9 +1471,9 @@
        List<Integer> collects = list1.stream().map(SiteBooking::getSiteId).collect(Collectors.toList());
        if(collects.size()==0){
            collect.add(-1);
            collects.add(-1);
        }
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collects));
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collects).ne(TSite::getState, 3));
        for (SiteBooking siteBooking : list1) {
            for (TSite tSite : list3) {
                if(siteBooking.getSiteId().equals(tSite.getId())){
@@ -1333,7 +1483,7 @@
        }
        List<TSiteType> lists2 = siteTypeService.list();
        List<TSiteType> lists2 = siteTypeService.list(new LambdaQueryWrapper<TSiteType>().ne(TSiteType::getState, 3));
        List<Map<String,Object>> mapList = new ArrayList<>();
@@ -1464,7 +1614,7 @@
        if(collect.size()==0){
            collect.add(-1);
        }
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collect));
        List<TSite> list3 = siteService.list(new LambdaQueryWrapper<TSite>().in(TSite::getId, collect).ne(TSite::getState, 3));
        for (SiteBooking siteBooking : list1) {
            for (TSite tSite : list3) {
                if(siteBooking.getSiteId().equals(tSite.getId())){
@@ -1474,7 +1624,7 @@
        }
        List<TSiteType> list2 = siteTypeService.list();
        List<TSiteType> list2 = siteTypeService.list(new LambdaQueryWrapper<TSiteType>().ne(TSiteType::getState, 3));
        List<Map<String,Object>> mapList = new ArrayList<>();
@@ -1541,7 +1691,7 @@
    @ResponseBody
    public Object stuUserDataYys(Integer id) {
        id = resolveOperatorId(id);
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
@@ -1580,7 +1730,7 @@
    @ResponseBody
    public Object selectYysJx(Integer id) {
        id = resolveOperatorId(id);
        List<TStore> list4 = storeService.list(new QueryWrapper<TStore>().eq("operatorId", id).eq("state", 1));
        List<TStore> list4 = listStatisticsStores(id, true);
        List<Integer> collect1 = list4.stream().map(TStore::getId).collect(Collectors.toList());
        //课程
        List<Integer> list = coursePackagePaymentClient.queryPaymentCoursePackage(collect1);
@@ -1644,7 +1794,3 @@
        return map1;
    }
}