刘帅
2026-09-29 8a89ba02db5c19db669a5cf9bb7d443223bdfb71
cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/code/DataStatisticsController.java
@@ -104,9 +104,7 @@
        return storeId;
    }
    /**
     * 运营商统计的门店范围:平台(0)统计平台门店,其他值统计对应运营商门店。
     */
    /** 平台(0)或运营商统计均以门店的 operatorId 作为唯一归属依据。 */
    private List<TStore> listStatisticsStores(Integer operatorId, boolean enabledOnly) {
        LambdaQueryWrapper<TStore> wrapper = new LambdaQueryWrapper<>();
        if (enabledOnly) {
@@ -116,10 +114,8 @@
        }
        if (operatorId == null) {
            // 平台统计:所有门店总和,不按门店所属运营商过滤。
        } else if (Integer.valueOf(0).equals(operatorId)) {
            wrapper.eq(TStore::getType, 1);
        } else {
            wrapper.eq(TStore::getType, 2).eq(TStore::getOperatorId, operatorId);
            wrapper.eq(TStore::getOperatorId, operatorId);
        }
        return storeService.list(wrapper);
    }
@@ -299,7 +295,9 @@
        // 下游多处查询会在门店集合为空时忽略门店过滤。运营商没有任何门店时,
        // 直接返回完整的零值结构,避免被误统计为全平台数据。
        if (operatorId != null && storeId == null
                && listStatisticsStores(operatorId, false).isEmpty()) {
                && storeService.count(new LambdaQueryWrapper<TStore>()
                        .eq(TStore::getOperatorId, operatorId)
                        .ne(TStore::getState, 3)) == 0) {
            Map<String, Object> emptyIncomeData = buildIncomeStatistics(Collections.emptyList());
            emptyIncomeData.put("incomeTypeNet", buildIncomeTypeNet(Collections.emptyList(), null));
            Map<String, Object> emptyTicketIncome = new HashMap<>();
@@ -318,10 +316,12 @@
        Integer statisticsScopeObjectId = statisticsScopeType == 3 ? storeId
                : (statisticsScopeType == 2 ? operatorId : null);
        List<Integer> cityManagerId= new ArrayList<>();
        // 积分商品收入接口按门店 ID 过滤;空集合会被其 SQL 视为“不过滤”,
        // 因此运营商统计必须传入该运营商名下的全部门店,不能传空集合。
        List<Integer> cityManagerId = new ArrayList<>();
        if (Integer.valueOf(2).equals(statisticsScopeType) && statisticsScopeObjectId != null) {
            cityManagerId = storeService.list(new QueryWrapper<TStore>()
                            .eq("cityManagerId", statisticsScopeObjectId).ne("state", 3))
                            .eq("operatorId", statisticsScopeObjectId).ne("state", 3))
                    .stream().map(TStore::getId).collect(Collectors.toList());
        } else if (Integer.valueOf(3).equals(statisticsScopeType) && statisticsScopeObjectId != null) {
            cityManagerId.add(statisticsScopeObjectId);
@@ -589,23 +589,27 @@
        // 门票饼图需要日/月/季/年卡拆分;此前该数据只在 type1 内部返回,
        // 现随统一营收接口返回,供平台页直接使用。
        monthlyData.put("ticketIncome", getTicketIncome(time, operatorId, storeId));
        // 平台收入类型饼图还需要课程、订场分类明细。此前页面只能读取 type1
        // 的模板数据,筛选后 getIncomeData 缺少该字段,导致课程图显示为空。
        if (operatorId == null && storeId == null) {
            Map<String, Object> platformTypeData = (Map<String, Object>) type1(time);
            monthlyData.put("courseData", platformTypeData.get("courseData"));
            monthlyData.put("siteData", platformTypeData.get("siteData"));
        }
        return monthlyData;
    }
    private Map<String, Object> getTicketIncome(String time, Integer operatorId, Integer storeId) {
        List<Integer> userIds;
        List<Integer> storeIds;
        if (storeId != null) {
            userIds = listStatisticsAppUserIdsForStore(storeId);
            storeIds = Collections.singletonList(storeId);
        } else if (operatorId != null) {
            userIds = listStatisticsAppUserIds(operatorId);
            storeIds = listStatisticsStoreIds(operatorId, false);
        } else {
            userIds = listStatisticsAppUserIds(null);
            // 平台统计不限制 storeId,包含全国/城市通用门票等购买记录 storeId 为空的数据。
            storeIds = Collections.emptyList();
        }
        if (userIds == null) {
            userIds = new ArrayList<>();
        }
        // 活动服务对空集合会补充 -1,传入可变副本以兼容该历史实现。
        return pointMercharsClient.queryFee(new QueryDataFee(time, new ArrayList<>(userIds)));
        return pointMercharsClient.queryTicketFee(new QueryDataFee(time, new ArrayList<>(storeIds)));
    }
    /** 惠民卡采用支付、退款两条事实流水,赠送卡不计营收。 */
@@ -1293,7 +1297,9 @@
    @RequestMapping(value = "/userAndVipPt")
    @ResponseBody
    public Object userAndVipPt(Integer type) {
        HashMap<String, Object> map = appUserClient.userAndVipYys(listStatisticsAppUserIds(null));
        // 平台用户统计按用户归属全量统计,不能以“已有消费记录”筛选,
        // 否则会遗漏尚未在门店消费的新注册用户。
        HashMap<String, Object> map = appUserClient.userAndVipPt(1);
        return map;
    }
@@ -1550,9 +1556,29 @@
        objects.add(1);
        objects.add(2);
        vipPaymentLambdaQueryWrapper.in(SiteBooking::getPayType,objects);
        List<SiteBooking> list1 = siteBookingService.list(vipPaymentLambdaQueryWrapper);
        List<SiteBooking> list1 = new ArrayList<>(siteBookingService.list(vipPaymentLambdaQueryWrapper));
        double sum = list1.stream().mapToDouble(SiteBooking::getPayMoney).sum();
        map.put("fee5",sum);
        // 智慧球场支付记录的场地可能只保存在 t_game.siteId。ballClient 已补齐该值,
        // 将其转换为订场明细后可与普通订场一起按场地类型统计。
        GameDataQuery gameDataQuery = new GameDataQuery();
        gameDataQuery.setObjectId(id);
        if (ToolUtil.isNotEmpty(time)) {
            gameDataQuery.setStartTime(time.split(" - ")[0] + " 00:00:00");
            gameDataQuery.setEndTime(time.split(" - ")[1] + " 23:59:59");
        }
        double gameIncome = 0D;
        for (TGameRecord gameRecord : ballClient.getData(gameDataQuery)) {
            if (gameRecord.getSiteId() == null || gameRecord.getMoney() == null || !ids.contains(gameRecord.getSiteId())) {
                continue;
            }
            SiteBooking gameBooking = new SiteBooking();
            gameBooking.setSiteId(gameRecord.getSiteId());
            gameBooking.setPayMoney(gameRecord.getMoney().doubleValue());
            list1.add(gameBooking);
            gameIncome += gameRecord.getMoney().doubleValue();
        }
        map.put("fee5", sum + gameIncome);
        List<Integer> collect = list1.stream().map(SiteBooking::getSiteId).collect(Collectors.toList());
@@ -1813,8 +1839,20 @@
        }
        // 平台智慧球场
        List<TGameRecord> data1 = ballClient.getData(gameDataQuery);
        // 将游戏支付转换为场地收入明细参与后续 siteId -> siteTypeId 分类。
        // fee5 仍由普通订场金额与游戏金额分别相加,避免重复计入总额。
        for (TGameRecord gameRecord : data1) {
            if (gameRecord.getSiteId() == null || gameRecord.getMoney() == null) {
                continue;
            }
            SiteBooking gameBooking = new SiteBooking();
            gameBooking.setSiteId(gameRecord.getSiteId());
            gameBooking.setPayMoney(gameRecord.getMoney().doubleValue());
            list1.add(gameBooking);
        }
        double sum1 = data1.stream()
                .map(TGameRecord::getMoney) // 获取每个 TGameRecord 对象的 money 字段
                .filter(Objects::nonNull)
                .mapToDouble(BigDecimal::doubleValue) // 将 BigDecimal 转换为 double
                .sum();
        map.put("fee5",sum+sum1);
@@ -1889,7 +1927,8 @@
    @RequestMapping("/stuUserData")
    @ResponseBody
    public Object stuUserData() {
        HashMap<String, Object> map = appUserClient.queryUserAgeYys(listStatisticsAppUserIds(null));
        // 平台地域/年龄统计按平台注册用户全量口径,不能仅取已有消费记录的用户。
        HashMap<String, Object> map = appUserClient.queryUserAge1();
        return map;
    }