From d53728e874792adcb5763f9671444995a53cf982 Mon Sep 17 00:00:00 2001 From: hy001 Date: Mon, 25 May 2026 11:16:45 +0800 Subject: [PATCH] fix: refresh dashboard active retention metrics --- ...tryDashboardActiveRetentionMetricTask.java | 38 +++++++++++++ .../CountryDashboardDailyMetricService.java | 51 +++++++++++++++++ .../rds/dao/datav/CountryDashboardDAO.java | 3 + .../dao/datav/CountryDashboardDAO.xml | 57 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/scheduler/CountryDashboardActiveRetentionMetricTask.java diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/scheduler/CountryDashboardActiveRetentionMetricTask.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/scheduler/CountryDashboardActiveRetentionMetricTask.java new file mode 100644 index 00000000..9963aeb4 --- /dev/null +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/scheduler/CountryDashboardActiveRetentionMetricTask.java @@ -0,0 +1,38 @@ +package com.red.circle.console.app.scheduler; + +import com.red.circle.component.redis.annotation.TaskCacheLock; +import com.red.circle.console.app.service.app.datav.CountryDashboardDailyMetricService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 国家数据大屏活跃和留存增量聚合任务. + */ +@Slf4j +@Component +@ConditionalOnProperty( + name = "red-circle.country-dashboard.active-retention-refresh-enabled", + havingValue = "true", + matchIfMissing = true) +@RequiredArgsConstructor +public class CountryDashboardActiveRetentionMetricTask { + + private final CountryDashboardDailyMetricService countryDashboardDailyMetricService; + + @Value("${red-circle.country-dashboard.active-retention-refresh-days:32}") + private int refreshDays; + + @Scheduled(cron = "${red-circle.country-dashboard.active-retention-refresh-cron:0 */10 * * * ?}", + zone = "Asia/Shanghai") + @TaskCacheLock(key = "COUNTRY_DASHBOARD_ACTIVE_RETENTION_REFRESH", expireSecond = 60 * 8) + public void refreshActiveRetention() { + long start = System.currentTimeMillis(); + countryDashboardDailyMetricService.refreshActiveRetentionRecentDays(refreshDays); + log.info("country dashboard active retention refresh task finished, days={}, costMs={}", + refreshDays, System.currentTimeMillis() - start); + } +} diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/datav/CountryDashboardDailyMetricService.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/datav/CountryDashboardDailyMetricService.java index 87c984ba..472a3682 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/datav/CountryDashboardDailyMetricService.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/datav/CountryDashboardDailyMetricService.java @@ -114,6 +114,39 @@ public class CountryDashboardDailyMetricService { } } + public void refreshActiveRetentionRecentDays(int days) { + ensureMetricSchema(); + int safeDays = Math.max(days, 1); + List sysOrigins = resolveSysOrigins(); + List statTimezones = resolveStatTimezones(null); + for (String sysOrigin : sysOrigins) { + for (StatTimezone statTimezone : statTimezones) { + List statDates = recentDates(safeDays, statTimezone.zoneId()); + for (LocalDate statDate : statDates) { + try { + refreshActiveRetentionPeriod(toDayWindow(statDate), sysOrigin, statTimezone); + } catch (Exception ex) { + log.error("refresh country dashboard active retention day failed, statDate={}, sysOrigin={}, statTimezone={}", + statDate, sysOrigin, statTimezone.id(), ex); + } + } + Set windows = new LinkedHashSet<>(); + for (LocalDate statDate : statDates) { + windows.add(toWeekWindow(statDate, statTimezone.zoneId())); + windows.add(toMonthWindow(statDate, statTimezone.zoneId())); + } + for (PeriodWindow window : windows) { + try { + refreshActiveRetentionPeriod(window, sysOrigin, statTimezone); + } catch (Exception ex) { + log.error("refresh country dashboard active retention period failed, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}", + window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), ex); + } + } + } + } + } + public void refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin) { refreshRange(startDate, endDate, sysOrigin, null); } @@ -352,6 +385,24 @@ public class CountryDashboardDailyMetricService { System.currentTimeMillis() - start); } + private void refreshActiveRetentionPeriod(PeriodWindow window, String sysOrigin, + StatTimezone statTimezone) { + long start = System.currentTimeMillis(); + TimeWindow timeWindow = toStorageTimeWindow(window, statTimezone); + Map rows = new LinkedHashMap<>(); + mergePeriodDailyActive(rows, listDailyActive(window, sysOrigin), window, sysOrigin, + statTimezone); + mergePeriodRetention(rows, window, sysOrigin, statTimezone, timeWindow.startTime(), + timeWindow.endTime()); + List metrics = new ArrayList<>(rows.values()); + int upserted = metrics.isEmpty() ? 0 + : transactionTemplate.execute(status -> + countryDashboardDAO.upsertPeriodActiveRetentionMetrics(metrics)); + log.info("refresh country dashboard active retention success, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}, countries={}, upserted={}, costMs={}", + window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), metrics.size(), + upserted, System.currentTimeMillis() - start); + } + private void refreshAll(String sysOrigin, StatTimezone statTimezone) { long start = System.currentTimeMillis(); PeriodWindow window = new PeriodWindow(PERIOD_ALL, PERIOD_ALL, "全部", null, null); diff --git a/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/database/rds/dao/datav/CountryDashboardDAO.java b/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/database/rds/dao/datav/CountryDashboardDAO.java index b126cb80..eb24e325 100644 --- a/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/database/rds/dao/datav/CountryDashboardDAO.java +++ b/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/database/rds/dao/datav/CountryDashboardDAO.java @@ -191,6 +191,9 @@ public interface CountryDashboardDAO extends BaseDAO { int upsertPeriodMetrics(@Param("metrics") List metrics); + int upsertPeriodActiveRetentionMetrics( + @Param("metrics") List metrics); + int deletePeriodUserMetrics( @Param("periodType") String periodType, @Param("periodKey") String periodKey, diff --git a/rc-service/rc-service-console/console-infrastructure/src/main/resources/dao/datav/CountryDashboardDAO.xml b/rc-service/rc-service-console/console-infrastructure/src/main/resources/dao/datav/CountryDashboardDAO.xml index 7fa8e8bf..12b29bc6 100644 --- a/rc-service/rc-service-console/console-infrastructure/src/main/resources/dao/datav/CountryDashboardDAO.xml +++ b/rc-service/rc-service-console/console-infrastructure/src/main/resources/dao/datav/CountryDashboardDAO.xml @@ -1378,6 +1378,63 @@ + + INSERT INTO country_dashboard_period_metric ( + period_type, + period_key, + stat_timezone, + period_name, + period_start_date, + period_end_date, + sys_origin, + country_code, + country_name, + daily_active_user, + d1_retention_user, + d1_retention_base_user, + d7_retention_user, + d7_retention_base_user, + d30_retention_user, + d30_retention_base_user, + refreshed_at + ) + VALUES + + ( + #{item.periodType}, + #{item.periodKey}, + #{item.statTimezone}, + #{item.periodName}, + #{item.periodStartDate}, + #{item.periodEndDate}, + #{item.sysOrigin}, + #{item.countryCode}, + #{item.countryName}, + IFNULL(#{item.dailyActiveUser}, 0), + IFNULL(#{item.d1RetentionUser}, 0), + IFNULL(#{item.d1RetentionBaseUser}, 0), + IFNULL(#{item.d7RetentionUser}, 0), + IFNULL(#{item.d7RetentionBaseUser}, 0), + IFNULL(#{item.d30RetentionUser}, 0), + IFNULL(#{item.d30RetentionBaseUser}, 0), + NOW() + ) + + ON DUPLICATE KEY UPDATE + period_name = VALUES(period_name), + period_start_date = VALUES(period_start_date), + period_end_date = VALUES(period_end_date), + country_name = VALUES(country_name), + daily_active_user = VALUES(daily_active_user), + d1_retention_user = VALUES(d1_retention_user), + d1_retention_base_user = VALUES(d1_retention_base_user), + d7_retention_user = VALUES(d7_retention_user), + d7_retention_base_user = VALUES(d7_retention_base_user), + d30_retention_user = VALUES(d30_retention_user), + d30_retention_base_user = VALUES(d30_retention_base_user), + refreshed_at = NOW() + + INSERT IGNORE INTO country_dashboard_period_user_metric ( period_type,