| | |
| | | if (summary != null) { |
| | | result.putAll(summary); |
| | | } |
| | | // 充电用户数精确去重(用户-站点对表, 跨站不重复); 对表未回填时回退到按站点求和 |
| | | Long pairCount = this.baseMapper.userSiteTableCount(); |
| | | if (pairCount != null && pairCount > 0) { |
| | | Long exactUserCount = this.baseMapper.userCountFromStat(siteIds); |
| | | if (exactUserCount != null) { |
| | | result.put("chargingUserCount", exactUserCount); |
| | | } |
| | | } |
| | | Long registered = this.baseMapper.registeredFromStat(); |
| | | result.put("registeredUserCount", registered == null ? 0L : registered); |
| | | } catch (Exception e) { |
| | |
| | | LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); |
| | | while (!month.isAfter(currentMonth)) { |
| | | writeMonthSlice(month, month.plusMonths(1)); |
| | | writeUserSitePairs(month, month.plusMonths(1)); |
| | | month = month.plusMonths(1); |
| | | } |
| | | // 2. 近90天日切片 |
| | |
| | | LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); |
| | | writeMonthSlice(currentMonth, currentMonth.plusMonths(1)); |
| | | writeDaySlice(LocalDate.now()); |
| | | writeUserSitePairs(LocalDate.now(), LocalDate.now().plusDays(1)); |
| | | writeGlobal(); |
| | | log.info("大屏预聚合表刷新完成, 耗时 {}ms", System.currentTimeMillis() - start); |
| | | } |
| | | |
| | | /** |
| | | * 写入一个月度切片聚合(M 行) |
| | | * 写入时间切片内的 (site_id, app_user_id) 对(充电用户精确去重用, INSERT IGNORE 幂等) |
| | | */ |
| | | private void writeMonthSlice(LocalDate monthStart, LocalDate monthEnd) { |
| | | List<Map<String, Object>> rows = this.baseMapper.monthSliceAgg( |
| | | monthStart.atStartOfDay().format(LARGE_SCREEN_SQL_FMT), |
| | | monthEnd.atStartOfDay().format(LARGE_SCREEN_SQL_FMT)); |
| | | if (rows == null || rows.isEmpty()) { |
| | | private void writeUserSitePairs(LocalDate sliceStart, LocalDate sliceEnd) { |
| | | List<Map<String, Object>> pairs = this.baseMapper.userSitePairsAgg( |
| | | sliceStart.atStartOfDay().format(LARGE_SCREEN_SQL_FMT), |
| | | sliceEnd.atStartOfDay().format(LARGE_SCREEN_SQL_FMT)); |
| | | if (pairs == null || pairs.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Map<String, Object>> rows = new ArrayList<>(); |
| | | for (Map<String, Object> pair : pairs) { |
| | | if (pair == null || pair.get("site_id") == null || pair.get("app_user_id") == null) { |
| | | continue; |
| | | } |
| | | Map<String, Object> row = new HashMap<>(); |
| | | row.put("siteId", pair.get("site_id")); |
| | | row.put("appUserId", pair.get("app_user_id")); |
| | | rows.add(row); |
| | | } |
| | | if (!rows.isEmpty()) { |
| | | this.baseMapper.insertUserSitePairs(rows); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 写入一个月度切片聚合(M 行) |
| | | * 金额/订单数取 monthSliceAgg(已结束+已支付口径); |
| | | * 充电用户数取 monthSliceUserAgg(工作台口径: 该站点所有订单的去重用户, 不区分状态/删除标记) |
| | | */ |
| | | private void writeMonthSlice(LocalDate monthStart, LocalDate monthEnd) { |
| | | String startTime = monthStart.atStartOfDay().format(LARGE_SCREEN_SQL_FMT); |
| | | String endTime = monthEnd.atStartOfDay().format(LARGE_SCREEN_SQL_FMT); |
| | | List<Map<String, Object>> rows = this.baseMapper.monthSliceAgg(startTime, endTime); |
| | | List<Map<String, Object>> userRows = this.baseMapper.monthSliceUserAgg(startTime, endTime); |
| | | if ((rows == null || rows.isEmpty()) && (userRows == null || userRows.isEmpty())) { |
| | | return; |
| | | } |
| | | Map<Object, BigDecimal> userCountBySite = new HashMap<>(); |
| | | if (userRows != null) { |
| | | for (Map<String, Object> userRow : userRows) { |
| | | if (userRow != null && userRow.get("site_id") != null) { |
| | | userCountBySite.put(userRow.get("site_id"), decimalValue(userRow, "user_count")); |
| | | } |
| | | } |
| | | } |
| | | String period = monthStart.format(LARGE_SCREEN_MONTH_FMT); |
| | | List<Map<String, Object>> statRows = new ArrayList<>(); |
| | | Map<Object, Boolean> written = new HashMap<>(); |
| | | if (rows != null) { |
| | | for (Map<String, Object> row : rows) { |
| | | if (row == null || row.get("site_id") == null) { |
| | | continue; |
| | |
| | | BigDecimal serviceFee = decimalValue(row, "service_fee"); |
| | | Map<String, Object> stat = new HashMap<>(); |
| | | stat.put("statType", "M"); |
| | | stat.put("period", monthStart.format(LARGE_SCREEN_MONTH_FMT)); |
| | | stat.put("period", period); |
| | | stat.put("siteId", row.get("site_id")); |
| | | stat.put("orderCount", decimalValue(row, "order_count")); |
| | | stat.put("userCount", decimalValue(row, "user_count")); |
| | | stat.put("userCount", userCountBySite.getOrDefault(row.get("site_id"), BigDecimal.ZERO)); |
| | | stat.put("capacity", decimalValue(row, "capacity")); |
| | | stat.put("electricFee", electricFee); |
| | | stat.put("serviceFee", serviceFee); |
| | |
| | | stat.put("totalPower", BigDecimal.ZERO); |
| | | stat.put("registeredUserCount", 0L); |
| | | statRows.add(stat); |
| | | written.put(row.get("site_id"), Boolean.TRUE); |
| | | } |
| | | } |
| | | // 当月仅有充电用户、暂无已结束订单的站点, 补一行保用户数(金额为0) |
| | | for (Map.Entry<Object, BigDecimal> entry : userCountBySite.entrySet()) { |
| | | if (!written.containsKey(entry.getKey())) { |
| | | Map<String, Object> stat = new HashMap<>(); |
| | | stat.put("statType", "M"); |
| | | stat.put("period", period); |
| | | stat.put("siteId", entry.getKey()); |
| | | stat.put("orderCount", BigDecimal.ZERO); |
| | | stat.put("userCount", entry.getValue()); |
| | | stat.put("capacity", BigDecimal.ZERO); |
| | | stat.put("electricFee", BigDecimal.ZERO); |
| | | stat.put("serviceFee", BigDecimal.ZERO); |
| | | stat.put("income", BigDecimal.ZERO); |
| | | stat.put("totalPower", BigDecimal.ZERO); |
| | | stat.put("registeredUserCount", 0L); |
| | | statRows.add(stat); |
| | | } |
| | | } |
| | | if (!statRows.isEmpty()) { |
| | | this.baseMapper.replaceSiteStatBatch(statRows); |