fix: refresh dashboard active retention metrics

This commit is contained in:
hy001 2026-05-25 11:16:45 +08:00
parent 1520362d9f
commit d53728e874
4 changed files with 149 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -114,6 +114,39 @@ public class CountryDashboardDailyMetricService {
}
}
public void refreshActiveRetentionRecentDays(int days) {
ensureMetricSchema();
int safeDays = Math.max(days, 1);
List<String> sysOrigins = resolveSysOrigins();
List<StatTimezone> statTimezones = resolveStatTimezones(null);
for (String sysOrigin : sysOrigins) {
for (StatTimezone statTimezone : statTimezones) {
List<LocalDate> 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<PeriodWindow> 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<String, CountryDashboardPeriodMetricDTO> rows = new LinkedHashMap<>();
mergePeriodDailyActive(rows, listDailyActive(window, sysOrigin), window, sysOrigin,
statTimezone);
mergePeriodRetention(rows, window, sysOrigin, statTimezone, timeWindow.startTime(),
timeWindow.endTime());
List<CountryDashboardPeriodMetricDTO> 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);

View File

@ -191,6 +191,9 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
int upsertPeriodMetrics(@Param("metrics") List<CountryDashboardPeriodMetricDTO> metrics);
int upsertPeriodActiveRetentionMetrics(
@Param("metrics") List<CountryDashboardPeriodMetricDTO> metrics);
int deletePeriodUserMetrics(
@Param("periodType") String periodType,
@Param("periodKey") String periodKey,

View File

@ -1378,6 +1378,63 @@
</if>
</delete>
<insert id="upsertPeriodActiveRetentionMetrics">
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
<foreach collection="metrics" item="item" separator=",">
(
#{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()
)
</foreach>
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>
<insert id="insertPeriodNewUserMetrics">
INSERT IGNORE INTO country_dashboard_period_user_metric (
period_type,