feat(console): add dashboard timezone preaggregation
This commit is contained in:
parent
45cb56b45a
commit
c2bee924ce
@ -67,15 +67,18 @@ public class DatavRestController extends BaseController {
|
||||
public Map<String, Object> backfillCountryDashboard(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
@RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin) {
|
||||
@RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin,
|
||||
@RequestParam(value = "statTimezones", required = false) String statTimezones) {
|
||||
LocalDate parsedStartDate = LocalDate.parse(startDate);
|
||||
LocalDate parsedEndDate = LocalDate.parse(endDate);
|
||||
countryDashboardDailyMetricService.refreshRange(parsedStartDate, parsedEndDate, sysOrigin);
|
||||
List<String> refreshedTimezones = countryDashboardDailyMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
return Map.of(
|
||||
"status", "success",
|
||||
"startDate", parsedStartDate.toString(),
|
||||
"endDate", parsedEndDate.toString(),
|
||||
"sysOrigin", sysOrigin);
|
||||
"sysOrigin", sysOrigin,
|
||||
"statTimezones", refreshedTimezones);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,10 +6,12 @@ import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardDaily
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardPeriodMetricDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.WeekFields;
|
||||
@ -44,6 +46,9 @@ public class CountryDashboardDailyMetricService {
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
private static final WeekFields WEEK_FIELDS = WeekFields.ISO;
|
||||
private static final ZoneId STORAGE_ZONE_ID = ZoneId.of("Asia/Riyadh");
|
||||
private static final String STORAGE_TIMEZONE = "Asia/Riyadh";
|
||||
private static final String STORAGE_TIMEZONE_OFFSET = "+03:00";
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
@ -51,37 +56,54 @@ public class CountryDashboardDailyMetricService {
|
||||
@Value("${red-circle.country-dashboard.refresh-sys-origins:LIKEI}")
|
||||
private String refreshSysOrigins;
|
||||
|
||||
@Value("${red-circle.country-dashboard.refresh-timezones:Asia/Riyadh,UTC,Asia/Shanghai}")
|
||||
private String refreshTimezones;
|
||||
|
||||
private volatile boolean metricSchemaEnsured;
|
||||
|
||||
public void refreshRecentDays(int days) {
|
||||
ensureMetricSchema();
|
||||
int safeDays = Math.max(days, 1);
|
||||
LocalDate today = LocalDate.now();
|
||||
List<LocalDate> statDates = new ArrayList<>();
|
||||
for (int i = safeDays - 1; i >= 0; i--) {
|
||||
statDates.add(today.minusDays(i));
|
||||
}
|
||||
List<LocalDate> storageStatDates = recentDates(safeDays, STORAGE_ZONE_ID);
|
||||
List<StatTimezone> statTimezones = resolveStatTimezones(null);
|
||||
|
||||
List<String> sysOrigins = resolveSysOrigins();
|
||||
for (String sysOrigin : sysOrigins) {
|
||||
for (LocalDate statDate : statDates) {
|
||||
for (LocalDate statDate : storageStatDates) {
|
||||
try {
|
||||
refreshDay(statDate, sysOrigin);
|
||||
refreshDailyMetric(statDate, sysOrigin);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard daily metric failed, statDate={}, sysOrigin={}",
|
||||
statDate, sysOrigin, ex);
|
||||
}
|
||||
}
|
||||
refreshAffectedPeriods(statDates, sysOrigin, false);
|
||||
try {
|
||||
refreshAll(sysOrigin);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard all metric failed, sysOrigin={}", sysOrigin, ex);
|
||||
for (StatTimezone statTimezone : statTimezones) {
|
||||
List<LocalDate> statDates = recentDates(safeDays, statTimezone.zoneId());
|
||||
for (LocalDate statDate : statDates) {
|
||||
try {
|
||||
refreshPeriod(toDayWindow(statDate), sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard day period metric failed, statDate={}, sysOrigin={}, statTimezone={}",
|
||||
statDate, sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
refreshAffectedPeriods(statDates, sysOrigin, statTimezone, false);
|
||||
try {
|
||||
refreshAll(sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard all metric failed, sysOrigin={}, statTimezone={}",
|
||||
sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin) {
|
||||
refreshRange(startDate, endDate, sysOrigin, null);
|
||||
}
|
||||
|
||||
public List<String> refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin,
|
||||
String statTimezonesValue) {
|
||||
ensureMetricSchema();
|
||||
if (startDate == null || endDate == null) {
|
||||
throw new IllegalArgumentException("startDate and endDate are required");
|
||||
@ -104,16 +126,25 @@ public class CountryDashboardDailyMetricService {
|
||||
for (long i = 0; i < dayCount; i++) {
|
||||
statDates.add(startDate.plusDays(i));
|
||||
}
|
||||
List<StatTimezone> statTimezones = resolveStatTimezones(statTimezonesValue);
|
||||
|
||||
log.info("refresh country dashboard range start, startDate={}, endDate={}, days={}, sysOrigin={}",
|
||||
startDate, endDate, dayCount, safeSysOrigin);
|
||||
log.info("refresh country dashboard range start, startDate={}, endDate={}, days={}, sysOrigin={}, statTimezones={}",
|
||||
startDate, endDate, dayCount, safeSysOrigin,
|
||||
statTimezones.stream().map(StatTimezone::id).toList());
|
||||
for (LocalDate statDate : statDates) {
|
||||
refreshDay(statDate, safeSysOrigin);
|
||||
refreshDailyMetric(statDate, safeSysOrigin);
|
||||
}
|
||||
refreshAffectedPeriods(statDates, safeSysOrigin, true);
|
||||
refreshAll(safeSysOrigin);
|
||||
log.info("refresh country dashboard range success, startDate={}, endDate={}, days={}, sysOrigin={}, costMs={}",
|
||||
startDate, endDate, dayCount, safeSysOrigin, System.currentTimeMillis() - start);
|
||||
for (StatTimezone statTimezone : statTimezones) {
|
||||
for (LocalDate statDate : statDates) {
|
||||
refreshPeriod(toDayWindow(statDate), safeSysOrigin, statTimezone);
|
||||
}
|
||||
refreshAffectedPeriods(statDates, safeSysOrigin, statTimezone, true);
|
||||
refreshAll(safeSysOrigin, statTimezone);
|
||||
}
|
||||
log.info("refresh country dashboard range success, startDate={}, endDate={}, days={}, sysOrigin={}, statTimezones={}, costMs={}",
|
||||
startDate, endDate, dayCount, safeSysOrigin,
|
||||
statTimezones.stream().map(StatTimezone::id).toList(), System.currentTimeMillis() - start);
|
||||
return statTimezones.stream().map(StatTimezone::id).toList();
|
||||
}
|
||||
|
||||
private void ensureMetricSchema() {
|
||||
@ -127,42 +158,62 @@ public class CountryDashboardDailyMetricService {
|
||||
countryDashboardDAO.createDailyMetricTable();
|
||||
countryDashboardDAO.createPeriodMetricTable();
|
||||
countryDashboardDAO.createPeriodUserMetricTable();
|
||||
countryDashboardDAO.ensurePeriodMetricTimezoneSchema();
|
||||
metricSchemaEnsured = true;
|
||||
log.info("country dashboard metric tables ensured");
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshDay(LocalDate statDate, String sysOrigin) {
|
||||
ensureMetricSchema();
|
||||
refreshDailyMetric(statDate, sysOrigin);
|
||||
for (StatTimezone statTimezone : resolveStatTimezones(null)) {
|
||||
refreshPeriod(toDayWindow(statDate), sysOrigin, statTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshDailyMetric(LocalDate statDate, String sysOrigin) {
|
||||
long start = System.currentTimeMillis();
|
||||
LocalDateTime startTime = statDate.atStartOfDay();
|
||||
LocalDateTime endTime = statDate.plusDays(1).atStartOfDay();
|
||||
Long startTimeMillis = Timestamp.valueOf(startTime).getTime();
|
||||
Long endTimeMillis = Timestamp.valueOf(endTime).getTime();
|
||||
Long startTimeMillis = statDate.atStartOfDay(STORAGE_ZONE_ID).toInstant().toEpochMilli();
|
||||
Long endTimeMillis = statDate.plusDays(1).atStartOfDay(STORAGE_ZONE_ID).toInstant().toEpochMilli();
|
||||
Map<String, CountryDashboardDailyMetricDTO> rows = new LinkedHashMap<>();
|
||||
|
||||
mergeSql(rows, countryDashboardDAO.listNewUsers(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listNewUserRecharge(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listOfficialRecharge(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listMifapayRecharge(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listGoogleRecharge(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listDealerRecharge(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listSalaryExchange(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listLuckyGift(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listWalletGame(
|
||||
PERIOD_DAY, startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin),
|
||||
PERIOD_DAY, startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET),
|
||||
statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listLuckyBoxGame(
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin), statDate, sysOrigin);
|
||||
PERIOD_DAY, startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET), statDate, sysOrigin);
|
||||
mergeSql(rows, countryDashboardDAO.listGameUser(
|
||||
PERIOD_DAY, startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin),
|
||||
PERIOD_DAY, startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET),
|
||||
statDate, sysOrigin);
|
||||
|
||||
List<CountryDashboardDailyMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
@ -174,103 +225,122 @@ public class CountryDashboardDailyMetricService {
|
||||
log.info("refresh country dashboard daily metric success, statDate={}, sysOrigin={}, countries={}, deleted={}, inserted={}, costMs={}",
|
||||
statDate, sysOrigin, metrics.size(), writeResult.deleted(), writeResult.inserted(),
|
||||
System.currentTimeMillis() - start);
|
||||
refreshPeriod(toDayWindow(statDate), sysOrigin);
|
||||
}
|
||||
|
||||
private void refreshAffectedPeriods(List<LocalDate> statDates, String sysOrigin,
|
||||
boolean failFast) {
|
||||
StatTimezone statTimezone, boolean failFast) {
|
||||
Set<PeriodWindow> windows = new LinkedHashSet<>();
|
||||
for (LocalDate statDate : statDates) {
|
||||
windows.add(toWeekWindow(statDate));
|
||||
windows.add(toMonthWindow(statDate));
|
||||
windows.add(toWeekWindow(statDate, statTimezone.zoneId()));
|
||||
windows.add(toMonthWindow(statDate, statTimezone.zoneId()));
|
||||
}
|
||||
for (PeriodWindow window : windows) {
|
||||
try {
|
||||
refreshPeriod(window, sysOrigin);
|
||||
refreshPeriod(window, sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
if (failFast) {
|
||||
throw ex;
|
||||
}
|
||||
log.error("refresh country dashboard period metric failed, periodType={}, periodKey={}, sysOrigin={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, ex);
|
||||
log.error("refresh country dashboard period metric failed, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshPeriod(PeriodWindow window, String sysOrigin) {
|
||||
private void refreshPeriod(PeriodWindow window, String sysOrigin, StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
LocalDateTime startTime = window.periodStartDate().atStartOfDay();
|
||||
LocalDateTime endTime = window.periodEndDate().atStartOfDay();
|
||||
Long startTimeMillis = Timestamp.valueOf(startTime).getTime();
|
||||
Long endTimeMillis = Timestamp.valueOf(endTime).getTime();
|
||||
TimeWindow timeWindow = toStorageTimeWindow(window, statTimezone);
|
||||
LocalDateTime startTime = timeWindow.startTime();
|
||||
LocalDateTime endTime = timeWindow.endTime();
|
||||
Long startTimeMillis = timeWindow.startTimeMillis();
|
||||
Long endTimeMillis = timeWindow.endTimeMillis();
|
||||
Map<String, CountryDashboardPeriodMetricDTO> rows = new LinkedHashMap<>();
|
||||
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listNewUserRecharge(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listOfficialRecharge(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listMifapayRecharge(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listGoogleRecharge(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listDealerRecharge(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listSalaryExchange(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listLuckyGift(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listWalletGame(
|
||||
window.periodType(), startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin),
|
||||
window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()),
|
||||
window, sysOrigin, statTimezone);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listLuckyBoxGame(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin), window, sysOrigin);
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
countryDashboardDAO.deletePeriodUserMetrics(window.periodType(), window.periodKey(), sysOrigin);
|
||||
countryDashboardDAO.deletePeriodUserMetrics(window.periodType(), window.periodKey(),
|
||||
statTimezone.id(), sysOrigin);
|
||||
int newUsers = countryDashboardDAO.insertPeriodNewUserMetrics(
|
||||
window.periodType(), window.periodKey(), window.periodName(),
|
||||
window.periodStartDate(), window.periodEndDate(), startTime, endTime, sysOrigin);
|
||||
window.periodStartDate(), window.periodEndDate(), statTimezone.id(),
|
||||
startTime, endTime, sysOrigin);
|
||||
int luckyGiftUsers = countryDashboardDAO.insertPeriodLuckyGiftUserMetrics(
|
||||
window.periodType(), window.periodKey(), window.periodName(),
|
||||
window.periodStartDate(), window.periodEndDate(), startTime, endTime, sysOrigin);
|
||||
window.periodStartDate(), window.periodEndDate(), statTimezone.id(),
|
||||
startTime, endTime, sysOrigin);
|
||||
int gameUsers = countryDashboardDAO.insertPeriodGameUserMetrics(
|
||||
window.periodType(), window.periodKey(), window.periodName(),
|
||||
window.periodStartDate(), window.periodEndDate(), startTime, endTime,
|
||||
window.periodStartDate(), window.periodEndDate(), statTimezone.id(), startTime, endTime,
|
||||
startTimeMillis, endTimeMillis, sysOrigin);
|
||||
|
||||
mergePeriodUserCounts(rows, countryDashboardDAO.listPeriodUserMetricCounts(
|
||||
window.periodType(), window.periodKey(), sysOrigin), window, sysOrigin);
|
||||
window.periodType(), window.periodKey(), statTimezone.id(), sysOrigin),
|
||||
window, sysOrigin, statTimezone);
|
||||
List<CountryDashboardPeriodMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
int deleted = countryDashboardDAO.deletePeriodMetrics(window.periodType(), window.periodKey(), sysOrigin);
|
||||
int deleted = countryDashboardDAO.deletePeriodMetrics(window.periodType(), window.periodKey(),
|
||||
statTimezone.id(), sysOrigin);
|
||||
int inserted = metrics.isEmpty() ? 0 : countryDashboardDAO.upsertPeriodMetrics(metrics);
|
||||
return new RefreshWriteResult(deleted, inserted, newUsers + luckyGiftUsers + gameUsers);
|
||||
});
|
||||
|
||||
log.info("refresh country dashboard period metric success, periodType={}, periodKey={}, sysOrigin={}, countries={}, deleted={}, inserted={}, userRows={}, costMs={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, rows.size(), writeResult.deleted(),
|
||||
writeResult.inserted(), writeResult.userRows(), System.currentTimeMillis() - start);
|
||||
log.info("refresh country dashboard period metric success, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}, countries={}, deleted={}, inserted={}, userRows={}, costMs={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), rows.size(),
|
||||
writeResult.deleted(), writeResult.inserted(), writeResult.userRows(),
|
||||
System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private void refreshAll(String sysOrigin) {
|
||||
private void refreshAll(String sysOrigin, StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
PeriodWindow window = new PeriodWindow(PERIOD_ALL, PERIOD_ALL, "全部", null, null);
|
||||
Map<String, CountryDashboardPeriodMetricDTO> rows = new LinkedHashMap<>();
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listAllPeriodAmountsFromDaily(sysOrigin),
|
||||
window, sysOrigin);
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listAllPeriodAmountsFromPeriodDays(
|
||||
statTimezone.id(), sysOrigin), window, sysOrigin, statTimezone);
|
||||
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
countryDashboardDAO.deletePeriodUserMetrics(PERIOD_ALL, PERIOD_ALL, sysOrigin);
|
||||
int userRows = countryDashboardDAO.insertAllPeriodUserMetricsFromDaily(sysOrigin);
|
||||
countryDashboardDAO.deletePeriodUserMetrics(PERIOD_ALL, PERIOD_ALL,
|
||||
statTimezone.id(), sysOrigin);
|
||||
int userRows = countryDashboardDAO.insertAllPeriodUserMetricsFromDaily(
|
||||
statTimezone.id(), sysOrigin);
|
||||
mergePeriodUserCounts(rows, countryDashboardDAO.listPeriodUserMetricCounts(
|
||||
PERIOD_ALL, PERIOD_ALL, sysOrigin), window, sysOrigin);
|
||||
PERIOD_ALL, PERIOD_ALL, statTimezone.id(), sysOrigin), window, sysOrigin,
|
||||
statTimezone);
|
||||
List<CountryDashboardPeriodMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
int deleted = countryDashboardDAO.deletePeriodMetrics(PERIOD_ALL, PERIOD_ALL, sysOrigin);
|
||||
int deleted = countryDashboardDAO.deletePeriodMetrics(PERIOD_ALL, PERIOD_ALL,
|
||||
statTimezone.id(), sysOrigin);
|
||||
int inserted = metrics.isEmpty() ? 0 : countryDashboardDAO.upsertPeriodMetrics(metrics);
|
||||
return new RefreshWriteResult(deleted, inserted, userRows);
|
||||
});
|
||||
|
||||
log.info("refresh country dashboard all metric success, sysOrigin={}, countries={}, deleted={}, inserted={}, userRows={}, costMs={}",
|
||||
sysOrigin, rows.size(), writeResult.deleted(), writeResult.inserted(),
|
||||
log.info("refresh country dashboard all metric success, sysOrigin={}, statTimezone={}, countries={}, deleted={}, inserted={}, userRows={}, costMs={}",
|
||||
sysOrigin, statTimezone.id(), rows.size(), writeResult.deleted(), writeResult.inserted(),
|
||||
writeResult.userRows(), System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
@ -298,12 +368,14 @@ public class CountryDashboardDailyMetricService {
|
||||
}
|
||||
|
||||
private void mergePeriodAmounts(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
List<CountryDashboardMetricDTO> metrics, PeriodWindow window, String sysOrigin) {
|
||||
List<CountryDashboardMetricDTO> metrics, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (CountryDashboardMetricDTO metric : metrics) {
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin);
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
|
||||
statTimezone);
|
||||
row.setNewUserRecharge(add(row.getNewUserRecharge(), metric.getNewUserRecharge()));
|
||||
row.setOfficialRecharge(add(row.getOfficialRecharge(), metric.getOfficialRecharge()));
|
||||
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
|
||||
@ -318,12 +390,14 @@ public class CountryDashboardDailyMetricService {
|
||||
}
|
||||
|
||||
private void mergePeriodUserCounts(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
List<CountryDashboardMetricDTO> metrics, PeriodWindow window, String sysOrigin) {
|
||||
List<CountryDashboardMetricDTO> metrics, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (CountryDashboardMetricDTO metric : metrics) {
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin);
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
|
||||
statTimezone);
|
||||
row.setCountryNewUser(safeLong(metric.getCountryNewUser()));
|
||||
row.setLuckyGiftUser(safeLong(metric.getLuckyGiftUser()));
|
||||
row.setGameUser(safeLong(metric.getGameUser()));
|
||||
@ -344,13 +418,15 @@ public class CountryDashboardDailyMetricService {
|
||||
}
|
||||
|
||||
private CountryDashboardPeriodMetricDTO getPeriodRow(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
CountryDashboardMetricDTO metric, PeriodWindow window, String sysOrigin) {
|
||||
CountryDashboardMetricDTO metric, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
String countryCode = hasText(metric.getCountryCode()) ? metric.getCountryCode() : "UNKNOWN";
|
||||
String countryName = hasText(metric.getCountryName()) ? metric.getCountryName() : countryCode;
|
||||
return rows.computeIfAbsent(countryCode,
|
||||
key -> new CountryDashboardPeriodMetricDTO()
|
||||
.setPeriodType(window.periodType())
|
||||
.setPeriodKey(window.periodKey())
|
||||
.setStatTimezone(statTimezone.id())
|
||||
.setPeriodName(window.periodName())
|
||||
.setPeriodStartDate(window.periodStartDate())
|
||||
.setPeriodEndDate(window.periodEndDate())
|
||||
@ -364,27 +440,93 @@ public class CountryDashboardDailyMetricService {
|
||||
return new PeriodWindow(PERIOD_DAY, key, key, statDate, statDate.plusDays(1));
|
||||
}
|
||||
|
||||
private PeriodWindow toWeekWindow(LocalDate statDate) {
|
||||
private PeriodWindow toWeekWindow(LocalDate statDate, ZoneId statZoneId) {
|
||||
LocalDate start = statDate.with(DayOfWeek.MONDAY);
|
||||
LocalDate end = capCurrentPeriodEnd(start.plusDays(7));
|
||||
LocalDate end = capCurrentPeriodEnd(start.plusDays(7), statZoneId);
|
||||
int weekYear = statDate.get(WEEK_FIELDS.weekBasedYear());
|
||||
int week = statDate.get(WEEK_FIELDS.weekOfWeekBasedYear());
|
||||
String key = String.format(Locale.ROOT, "%d-W%02d", weekYear, week);
|
||||
return new PeriodWindow(PERIOD_WEEK, key, key, start, end);
|
||||
}
|
||||
|
||||
private PeriodWindow toMonthWindow(LocalDate statDate) {
|
||||
private PeriodWindow toMonthWindow(LocalDate statDate, ZoneId statZoneId) {
|
||||
LocalDate start = statDate.withDayOfMonth(1);
|
||||
LocalDate end = capCurrentPeriodEnd(start.plusMonths(1));
|
||||
LocalDate end = capCurrentPeriodEnd(start.plusMonths(1), statZoneId);
|
||||
String key = statDate.format(MONTH_FORMATTER);
|
||||
return new PeriodWindow(PERIOD_MONTH, key, key, start, end);
|
||||
}
|
||||
|
||||
private LocalDate capCurrentPeriodEnd(LocalDate naturalEndExclusive) {
|
||||
LocalDate tomorrow = LocalDate.now().plusDays(1);
|
||||
private LocalDate capCurrentPeriodEnd(LocalDate naturalEndExclusive, ZoneId statZoneId) {
|
||||
LocalDate tomorrow = LocalDate.now(statZoneId).plusDays(1);
|
||||
return naturalEndExclusive.isAfter(tomorrow) ? tomorrow : naturalEndExclusive;
|
||||
}
|
||||
|
||||
private List<LocalDate> recentDates(int days, ZoneId zoneId) {
|
||||
LocalDate today = LocalDate.now(zoneId);
|
||||
List<LocalDate> statDates = new ArrayList<>();
|
||||
for (int i = days - 1; i >= 0; i--) {
|
||||
statDates.add(today.minusDays(i));
|
||||
}
|
||||
return statDates;
|
||||
}
|
||||
|
||||
private TimeWindow toStorageTimeWindow(PeriodWindow window, StatTimezone statTimezone) {
|
||||
Instant startInstant = window.periodStartDate().atStartOfDay(statTimezone.zoneId()).toInstant();
|
||||
Instant endInstant = window.periodEndDate().atStartOfDay(statTimezone.zoneId()).toInstant();
|
||||
return new TimeWindow(
|
||||
LocalDateTime.ofInstant(startInstant, STORAGE_ZONE_ID),
|
||||
LocalDateTime.ofInstant(endInstant, STORAGE_ZONE_ID),
|
||||
startInstant.toEpochMilli(),
|
||||
endInstant.toEpochMilli());
|
||||
}
|
||||
|
||||
private List<StatTimezone> resolveStatTimezones(String statTimezonesValue) {
|
||||
String config = trimToNull(statTimezonesValue);
|
||||
if (config == null) {
|
||||
config = trimToNull(refreshTimezones);
|
||||
}
|
||||
if (config == null) {
|
||||
config = "Asia/Riyadh,UTC,Asia/Shanghai";
|
||||
}
|
||||
|
||||
Map<String, StatTimezone> zones = new LinkedHashMap<>();
|
||||
Arrays.stream(config.split(","))
|
||||
.map(String::trim)
|
||||
.map(this::normalizeStatTimezone)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(zone -> zones.putIfAbsent(zone.id(), zone));
|
||||
if (zones.isEmpty()) {
|
||||
zones.put(STORAGE_TIMEZONE, new StatTimezone(STORAGE_TIMEZONE, STORAGE_ZONE_ID,
|
||||
STORAGE_TIMEZONE_OFFSET));
|
||||
}
|
||||
return new ArrayList<>(zones.values());
|
||||
}
|
||||
|
||||
private StatTimezone normalizeStatTimezone(String statTimezone) {
|
||||
String value = trimToNull(statTimezone);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String upperValue = value.toUpperCase(Locale.ROOT);
|
||||
ZoneId zoneId = switch (upperValue) {
|
||||
case "UTC", "Z", "+00:00" -> ZoneId.of("UTC");
|
||||
case "ASIA/SHANGHAI", "BEIJING", "CHINA", "+08:00" -> ZoneId.of("Asia/Shanghai");
|
||||
case "ASIA/RIYADH", "RIYADH", "SERVER", "+03:00" -> STORAGE_ZONE_ID;
|
||||
default -> null;
|
||||
};
|
||||
if (zoneId == null) {
|
||||
log.warn("ignore unsupported country dashboard stat timezone, statTimezone={}", statTimezone);
|
||||
return null;
|
||||
}
|
||||
return new StatTimezone(zoneId.getId(), zoneId, zoneOffset(zoneId));
|
||||
}
|
||||
|
||||
private static String zoneOffset(ZoneId zoneId) {
|
||||
ZoneOffset offset = zoneId.getRules().getOffset(Instant.now());
|
||||
String id = offset.getId();
|
||||
return "Z".equals(id) ? "+00:00" : id;
|
||||
}
|
||||
|
||||
private List<String> resolveSysOrigins() {
|
||||
String config = trimToNull(refreshSysOrigins);
|
||||
if (config == null) {
|
||||
@ -433,6 +575,13 @@ public class CountryDashboardDailyMetricService {
|
||||
LocalDate periodStartDate, LocalDate periodEndDate) {
|
||||
}
|
||||
|
||||
private record StatTimezone(String id, ZoneId zoneId, String offset) {
|
||||
}
|
||||
|
||||
private record TimeWindow(LocalDateTime startTime, LocalDateTime endTime,
|
||||
Long startTimeMillis, Long endTimeMillis) {
|
||||
}
|
||||
|
||||
private record RefreshWriteResult(int deleted, int inserted, int userRows) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,13 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
@ -54,8 +58,11 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
private static final String COLLECTION_GIFT_GIVE_RUNNING_WATER = "gift_give_running_water";
|
||||
private static final String COLLECTION_USER_DAILY_ACTIVE_LOG = "user_daily_active_log";
|
||||
private static final String PERIOD_ALL = "ALL";
|
||||
private static final ZoneId STORAGE_ZONE_ID = ZoneId.of("Asia/Riyadh");
|
||||
private static final ZoneId DEFAULT_STAT_ZONE_ID = STORAGE_ZONE_ID;
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final int USER_COUNTRY_BATCH_SIZE = 1000;
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
@ -69,7 +76,7 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
if (shouldUsePrecomputedMetrics(condition)) {
|
||||
mergeSql(rows, countryDashboardDAO.listPrecomputedPeriodMetrics(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.statTimezone, condition.sysOrigin));
|
||||
} else {
|
||||
mergeLiveSql(rows, condition);
|
||||
}
|
||||
@ -95,7 +102,8 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
.setPeriodType(condition.periodType)
|
||||
.setStartDate(condition.startDate)
|
||||
.setEndDate(condition.endDate)
|
||||
.setComputedAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
|
||||
.setStatTimezone(condition.statTimezone)
|
||||
.setComputedAt(ZonedDateTime.now(condition.statZoneId).format(DATETIME_FORMATTER))
|
||||
.setTotal(total)
|
||||
.setRecords(records);
|
||||
}
|
||||
@ -103,39 +111,50 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
private void mergeLiveSql(Map<String, CountryDashboardMetricCO> rows, QueryCondition condition) {
|
||||
mergeSql(rows, countryDashboardDAO.listNewUsers(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listNewUserRecharge(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listOfficialRecharge(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listMifapayRecharge(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listGoogleRecharge(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listDealerRecharge(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listSalaryExchange(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listLuckyGift(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listWalletGame(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.startTimeMillis(), condition.endTimeMillis(),
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listLuckyBoxGame(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
mergeSql(rows, countryDashboardDAO.listGameUser(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.startTimeMillis(), condition.endTimeMillis(),
|
||||
condition.countryKeyword, condition.sysOrigin));
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
}
|
||||
|
||||
private boolean shouldUsePrecomputedMetrics(QueryCondition condition) {
|
||||
@ -146,10 +165,11 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
if (condition.startTime != null || condition.endTime != null) {
|
||||
return false;
|
||||
}
|
||||
long count = safeLong(countryDashboardDAO.countAllPrecomputedPeriodMetrics(condition.sysOrigin));
|
||||
long count = safeLong(countryDashboardDAO.countAllPrecomputedPeriodMetrics(
|
||||
condition.statTimezone, condition.sysOrigin));
|
||||
if (count <= 0) {
|
||||
log.warn("country dashboard all precomputed metric missing, skip live all-history scan, sysOrigin={}",
|
||||
condition.sysOrigin);
|
||||
log.warn("country dashboard all precomputed metric missing, skip live all-history scan, sysOrigin={}, statTimezone={}",
|
||||
condition.sysOrigin, condition.statTimezone);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -159,26 +179,27 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
return false;
|
||||
}
|
||||
long actualPeriods = safeLong(countryDashboardDAO.countPrecomputedPeriods(
|
||||
condition.periodType, periodKeys, condition.sysOrigin));
|
||||
condition.periodType, periodKeys, condition.statTimezone, condition.sysOrigin));
|
||||
boolean complete = actualPeriods >= periodKeys.size();
|
||||
if (!complete) {
|
||||
log.info("country dashboard use live sql, period precomputed metric incomplete, periodType={}, sysOrigin={}, start={}, end={}, expectedPeriods={}, actualPeriods={}",
|
||||
condition.periodType, condition.sysOrigin, condition.startDate, condition.endDate,
|
||||
log.info("country dashboard use live sql, period precomputed metric incomplete, periodType={}, sysOrigin={}, statTimezone={}, start={}, end={}, expectedPeriods={}, actualPeriods={}",
|
||||
condition.periodType, condition.sysOrigin, condition.statTimezone, condition.startDate, condition.endDate,
|
||||
periodKeys.size(), actualPeriods);
|
||||
}
|
||||
return complete;
|
||||
}
|
||||
|
||||
private List<String> expectedPrecomputedPeriodKeys(QueryCondition condition) {
|
||||
if (condition.startTime == null || condition.endTime == null
|
||||
|| !condition.startTime.isBefore(condition.endTime)) {
|
||||
if (condition.startDateValue == null || condition.endDateValue == null
|
||||
|| condition.endDateValue.isBefore(condition.startDateValue)) {
|
||||
return List.of();
|
||||
}
|
||||
LocalDate start = condition.startTime.toLocalDate();
|
||||
LocalDate endExclusive = condition.endTime.toLocalDate();
|
||||
LocalDate start = condition.startDateValue;
|
||||
LocalDate endExclusive = condition.endDateValue.plusDays(1);
|
||||
return switch (condition.periodType) {
|
||||
case "DAY" -> expectedDayKeys(start, endExclusive);
|
||||
case "WEEK", "MONTH" -> expectedAlignedPeriodKeys(condition.periodType, start, endExclusive);
|
||||
case "WEEK", "MONTH" -> expectedAlignedPeriodKeys(condition.periodType, start,
|
||||
endExclusive, condition.statZoneId);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
@ -192,10 +213,10 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
}
|
||||
|
||||
private List<String> expectedAlignedPeriodKeys(String periodType, LocalDate start,
|
||||
LocalDate endExclusive) {
|
||||
LocalDate endExclusive, ZoneId statZoneId) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
LocalDate cursor = start;
|
||||
LocalDate tomorrow = LocalDate.now().plusDays(1);
|
||||
LocalDate tomorrow = LocalDate.now(statZoneId).plusDays(1);
|
||||
while (cursor.isBefore(endExclusive)) {
|
||||
LocalDate periodStart = periodStart(periodType, cursor);
|
||||
if (!cursor.equals(periodStart)) {
|
||||
@ -251,10 +272,11 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
|
||||
Long total = countryDashboardDAO.countRechargeDetails(
|
||||
condition.startTime, condition.endTime, condition.countryKeyword, countryCodes,
|
||||
condition.sysOrigin);
|
||||
condition.sysOrigin, condition.storageTimezoneOffset, condition.statTimezoneOffset);
|
||||
List<CountryDashboardRechargeDetailCO> records = countryDashboardDAO.listRechargeDetails(
|
||||
condition.startTime, condition.endTime, condition.countryKeyword, countryCodes,
|
||||
condition.sysOrigin, offset, limit)
|
||||
condition.sysOrigin, condition.storageTimezoneOffset, condition.statTimezoneOffset,
|
||||
offset, limit)
|
||||
.stream()
|
||||
.map(this::toRechargeDetailCO)
|
||||
.toList();
|
||||
@ -358,12 +380,13 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
private void mergeRetention(Map<String, CountryDashboardMetricCO> rows, QueryCondition condition) {
|
||||
List<CountryDashboardRetentionUserDTO> cohorts = countryDashboardDAO.listNewUserCohorts(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.sysOrigin);
|
||||
condition.countryKeyword, condition.sysOrigin,
|
||||
condition.storageTimezoneOffset, condition.statTimezoneOffset);
|
||||
if (cohorts == null || cohorts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate today = LocalDate.now(condition.statZoneId);
|
||||
Set<Long> userIds = new HashSet<>();
|
||||
Set<String> activeDates = new HashSet<>();
|
||||
for (CountryDashboardRetentionUserDTO cohort : cohorts) {
|
||||
@ -398,13 +421,13 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
Criteria.where("dynamicContentId").is(null),
|
||||
Criteria.where("dynamicContentId").is("")
|
||||
));
|
||||
return aggregateGiftAmount(criteria, "$giftValue.actualAmount", false);
|
||||
return aggregateGiftAmount(criteria, "$giftValue.actualAmount", false, condition.statTimezone);
|
||||
}
|
||||
|
||||
private List<MongoMetric> listLuckyGiftAnchorShare(QueryCondition condition) {
|
||||
List<Criteria> criteria = baseGiftCriteria(condition);
|
||||
criteria.add(Criteria.where("luckyGift").is(Boolean.TRUE));
|
||||
return aggregateGiftAmount(criteria, "$acceptUsers.targetAmount", true);
|
||||
return aggregateGiftAmount(criteria, "$acceptUsers.targetAmount", true, condition.statTimezone);
|
||||
}
|
||||
|
||||
private List<MongoUserDayMetric> listDailyActive(QueryCondition condition) {
|
||||
@ -428,11 +451,11 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
List<Criteria> criteria = new ArrayList<>();
|
||||
criteria.add(Criteria.where("refunded").ne(Boolean.TRUE));
|
||||
criteria.add(Criteria.where("userId").ne(null));
|
||||
if (condition.startTime != null) {
|
||||
criteria.add(Criteria.where("createTime").gte(Timestamp.valueOf(condition.startTime)));
|
||||
if (condition.startInstant != null) {
|
||||
criteria.add(Criteria.where("createTime").gte(Timestamp.from(condition.startInstant)));
|
||||
}
|
||||
if (condition.endTime != null) {
|
||||
criteria.add(Criteria.where("createTime").lt(Timestamp.valueOf(condition.endTime)));
|
||||
if (condition.endInstant != null) {
|
||||
criteria.add(Criteria.where("createTime").lt(Timestamp.from(condition.endInstant)));
|
||||
}
|
||||
if (hasText(condition.sysOrigin)) {
|
||||
criteria.add(Criteria.where("sysOrigin").is(condition.sysOrigin));
|
||||
@ -462,13 +485,13 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
}
|
||||
|
||||
private List<MongoMetric> aggregateGiftAmount(List<Criteria> criteria, String amountPath,
|
||||
boolean unwindAcceptUsers) {
|
||||
boolean unwindAcceptUsers, String statTimezone) {
|
||||
List<AggregationOperation> operations = new ArrayList<>();
|
||||
operations.add(Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))));
|
||||
if (unwindAcceptUsers) {
|
||||
operations.add(Aggregation.unwind("acceptUsers"));
|
||||
}
|
||||
operations.add(projectGiftDayAmount(amountPath));
|
||||
operations.add(projectGiftDayAmount(amountPath, statTimezone));
|
||||
operations.add(Aggregation.group("userId", "day").sum("amount").as("amount"));
|
||||
|
||||
return mongoTemplate.aggregate(Aggregation.newAggregation(operations),
|
||||
@ -480,12 +503,14 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
private AggregationOperation projectGiftDayAmount(String amountPath) {
|
||||
private AggregationOperation projectGiftDayAmount(String amountPath, String statTimezone) {
|
||||
return context -> new Document("$project",
|
||||
new Document("userId", "$userId")
|
||||
.append("amount", amountPath)
|
||||
.append("day", new Document("$dateToString",
|
||||
new Document("format", "%Y-%m-%d").append("date", "$createTime"))));
|
||||
new Document("format", "%Y-%m-%d")
|
||||
.append("date", "$createTime")
|
||||
.append("timezone", statTimezone))));
|
||||
}
|
||||
|
||||
private MongoMetric toMongoMetric(Document document) {
|
||||
@ -752,30 +777,49 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
private final String periodType;
|
||||
private final String startDate;
|
||||
private final String endDate;
|
||||
private final String statTimezone;
|
||||
private final ZoneId statZoneId;
|
||||
private final String storageTimezoneOffset;
|
||||
private final String statTimezoneOffset;
|
||||
private final LocalDate startDateValue;
|
||||
private final LocalDate endDateValue;
|
||||
private final LocalDateTime startTime;
|
||||
private final LocalDateTime endTime;
|
||||
private final Instant startInstant;
|
||||
private final Instant endInstant;
|
||||
private final String countryKeyword;
|
||||
private final String sysOrigin;
|
||||
|
||||
private QueryCondition(String periodType, String startDate, String endDate,
|
||||
LocalDateTime startTime, LocalDateTime endTime, String countryKeyword,
|
||||
String sysOrigin) {
|
||||
String statTimezone, ZoneId statZoneId, String storageTimezoneOffset,
|
||||
String statTimezoneOffset, LocalDate startDateValue, LocalDate endDateValue,
|
||||
LocalDateTime startTime, LocalDateTime endTime, Instant startInstant,
|
||||
Instant endInstant, String countryKeyword, String sysOrigin) {
|
||||
this.periodType = periodType;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.statTimezone = statTimezone;
|
||||
this.statZoneId = statZoneId;
|
||||
this.storageTimezoneOffset = storageTimezoneOffset;
|
||||
this.statTimezoneOffset = statTimezoneOffset;
|
||||
this.startDateValue = startDateValue;
|
||||
this.endDateValue = endDateValue;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.startInstant = startInstant;
|
||||
this.endInstant = endInstant;
|
||||
this.countryKeyword = countryKeyword;
|
||||
this.sysOrigin = sysOrigin;
|
||||
}
|
||||
|
||||
private static QueryCondition of(CountryDashboardQryCmd cmd) {
|
||||
String periodType = normalizePeriodType(cmd == null ? null : cmd.getPeriodType());
|
||||
ZoneId statZoneId = normalizeStatZoneId(cmd == null ? null : cmd.getStatTimezone());
|
||||
LocalDate start = parseDate(cmd == null ? null : cmd.getStartDate());
|
||||
LocalDate end = parseDate(cmd == null ? null : cmd.getEndDate());
|
||||
|
||||
if (!PERIOD_ALL.equals(periodType) && start == null && end == null) {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate today = LocalDate.now(statZoneId);
|
||||
switch (periodType) {
|
||||
case "WEEK" -> {
|
||||
start = today.with(DayOfWeek.MONDAY);
|
||||
@ -802,12 +846,27 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
throw new IllegalArgumentException("endDate must not be before startDate.");
|
||||
}
|
||||
|
||||
Instant startInstant = start == null ? null : start.atStartOfDay(statZoneId).toInstant();
|
||||
Instant endInstant = end == null ? null : end.plusDays(1).atStartOfDay(statZoneId).toInstant();
|
||||
LocalDateTime storageStartTime = startInstant == null ? null
|
||||
: LocalDateTime.ofInstant(startInstant, STORAGE_ZONE_ID);
|
||||
LocalDateTime storageEndTime = endInstant == null ? null
|
||||
: LocalDateTime.ofInstant(endInstant, STORAGE_ZONE_ID);
|
||||
|
||||
return new QueryCondition(
|
||||
periodType,
|
||||
start == null ? null : DATE_FORMATTER.format(start),
|
||||
end == null ? null : DATE_FORMATTER.format(end),
|
||||
start == null ? null : start.atStartOfDay(),
|
||||
end == null ? null : end.plusDays(1).atStartOfDay(),
|
||||
statZoneId.getId(),
|
||||
statZoneId,
|
||||
zoneOffset(STORAGE_ZONE_ID),
|
||||
zoneOffset(statZoneId),
|
||||
start,
|
||||
end,
|
||||
storageStartTime,
|
||||
storageEndTime,
|
||||
startInstant,
|
||||
endInstant,
|
||||
trimToNull(cmd == null ? null : cmd.getCountryKeyword()),
|
||||
trimToNull(cmd == null ? null : cmd.getSysOrigin())
|
||||
);
|
||||
@ -832,11 +891,15 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
}
|
||||
|
||||
private Long startTimeMillis() {
|
||||
return startTime == null ? null : Timestamp.valueOf(startTime).getTime();
|
||||
return startInstant == null ? null : startInstant.toEpochMilli();
|
||||
}
|
||||
|
||||
private Long endTimeMillis() {
|
||||
return endTime == null ? null : Timestamp.valueOf(endTime).getTime();
|
||||
return endInstant == null ? null : endInstant.toEpochMilli();
|
||||
}
|
||||
|
||||
private boolean isStorageTimezone() {
|
||||
return STORAGE_ZONE_ID.equals(statZoneId);
|
||||
}
|
||||
|
||||
private static String normalizePeriodType(String periodType) {
|
||||
@ -851,6 +914,27 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
};
|
||||
}
|
||||
|
||||
private static ZoneId normalizeStatZoneId(String statTimezone) {
|
||||
String value = trimToNull(statTimezone);
|
||||
if (value == null) {
|
||||
return DEFAULT_STAT_ZONE_ID;
|
||||
}
|
||||
value = value.trim();
|
||||
String upperValue = value.toUpperCase(Locale.ROOT);
|
||||
return switch (upperValue) {
|
||||
case "UTC", "Z", "+00:00" -> ZoneId.of("UTC");
|
||||
case "ASIA/SHANGHAI", "BEIJING", "CHINA", "+08:00" -> ZoneId.of("Asia/Shanghai");
|
||||
case "ASIA/RIYADH", "RIYADH", "SERVER", "+03:00" -> STORAGE_ZONE_ID;
|
||||
default -> DEFAULT_STAT_ZONE_ID;
|
||||
};
|
||||
}
|
||||
|
||||
private static String zoneOffset(ZoneId zoneId) {
|
||||
ZoneOffset offset = zoneId.getRules().getOffset(Instant.now());
|
||||
String id = offset.getId();
|
||||
return "Z".equals(id) ? "+00:00" : id;
|
||||
}
|
||||
|
||||
private static LocalDate parseDate(String value) {
|
||||
String fixed = trimToNull(value);
|
||||
return fixed == null ? null : LocalDate.parse(fixed, DATE_FORMATTER);
|
||||
|
||||
@ -22,6 +22,8 @@ public class CountryDashboardCO implements Serializable {
|
||||
|
||||
private String endDate;
|
||||
|
||||
private String statTimezone;
|
||||
|
||||
private String computedAt;
|
||||
|
||||
private CountryDashboardMetricCO total;
|
||||
|
||||
@ -30,6 +30,11 @@ public class CountryDashboardQryCmd implements Serializable {
|
||||
*/
|
||||
private String endDate;
|
||||
|
||||
/**
|
||||
* 统计时区,默认 Asia/Riyadh.
|
||||
*/
|
||||
private String statTimezone;
|
||||
|
||||
/**
|
||||
* 国家编码或国家名称关键字.
|
||||
*/
|
||||
|
||||
@ -25,61 +25,79 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
|
||||
int createPeriodUserMetricTable();
|
||||
|
||||
int ensurePeriodMetricTimezoneSchema();
|
||||
|
||||
List<CountryDashboardMetricDTO> listNewUsers(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listNewUserRecharge(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listOfficialRecharge(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listMifapayRecharge(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listGoogleRecharge(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listDealerRecharge(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listSalaryExchange(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listLuckyGift(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listWalletGame(
|
||||
@Param("periodType") String periodType,
|
||||
@ -88,14 +106,18 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("startTimeMillis") Long startTimeMillis,
|
||||
@Param("endTimeMillis") Long endTimeMillis,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listLuckyBoxGame(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listGameUser(
|
||||
@Param("periodType") String periodType,
|
||||
@ -104,7 +126,9 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("startTimeMillis") Long startTimeMillis,
|
||||
@Param("endTimeMillis") Long endTimeMillis,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardMetricDTO> listPrecomputedMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@ -118,6 +142,7 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
Long countPrecomputedMetricDates(
|
||||
@ -128,9 +153,12 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
Long countPrecomputedPeriods(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKeys") Collection<String> periodKeys,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
Long countAllPrecomputedPeriodMetrics(@Param("sysOrigin") String sysOrigin);
|
||||
Long countAllPrecomputedPeriodMetrics(
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int deleteDailyMetrics(
|
||||
@Param("statDate") LocalDate statDate,
|
||||
@ -141,6 +169,7 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
int deletePeriodMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int upsertPeriodMetrics(@Param("metrics") List<CountryDashboardPeriodMetricDTO> metrics);
|
||||
@ -148,6 +177,7 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
int deletePeriodUserMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int insertPeriodNewUserMetrics(
|
||||
@ -156,6 +186,7 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("periodName") String periodName,
|
||||
@Param("periodStartDate") LocalDate periodStartDate,
|
||||
@Param("periodEndDate") LocalDate periodEndDate,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@ -166,6 +197,7 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("periodName") String periodName,
|
||||
@Param("periodStartDate") LocalDate periodStartDate,
|
||||
@Param("periodEndDate") LocalDate periodEndDate,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@ -176,27 +208,35 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("periodName") String periodName,
|
||||
@Param("periodStartDate") LocalDate periodStartDate,
|
||||
@Param("periodEndDate") LocalDate periodEndDate,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("startTimeMillis") Long startTimeMillis,
|
||||
@Param("endTimeMillis") Long endTimeMillis,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int insertAllPeriodUserMetricsFromDaily(@Param("sysOrigin") String sysOrigin);
|
||||
int insertAllPeriodUserMetricsFromDaily(
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardMetricDTO> listPeriodUserMetricCounts(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardMetricDTO> listAllPeriodAmountsFromDaily(@Param("sysOrigin") String sysOrigin);
|
||||
List<CountryDashboardMetricDTO> listAllPeriodAmountsFromPeriodDays(
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardRetentionUserDTO> listNewUserCohorts(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardUserCountryDTO> listUserCountryByIds(@Param("userIds") Collection<Long> userIds);
|
||||
|
||||
@ -205,7 +245,9 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("countryCodes") String countryCodes,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardRechargeDetailDTO> listRechargeDetails(
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@ -213,6 +255,8 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("countryCodes") String countryCodes,
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset,
|
||||
@Param("offset") int offset,
|
||||
@Param("limit") int limit);
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@ public class CountryDashboardPeriodMetricDTO {
|
||||
|
||||
private String periodKey;
|
||||
|
||||
private String statTimezone;
|
||||
|
||||
private String periodName;
|
||||
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`period_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
|
||||
`period_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期键',
|
||||
`stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
|
||||
`period_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '周期名称',
|
||||
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
|
||||
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
|
||||
@ -62,9 +63,9 @@
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `sys_origin`, `country_code`),
|
||||
KEY `idx_origin_period` (`sys_origin`, `period_type`, `period_key`),
|
||||
KEY `idx_period_date` (`period_type`, `period_start_date`, `period_end_date`)
|
||||
UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`),
|
||||
KEY `idx_origin_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`),
|
||||
KEY `idx_period_date` (`stat_timezone`, `period_type`, `period_start_date`, `period_end_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏周期预聚合'
|
||||
</update>
|
||||
|
||||
@ -73,6 +74,7 @@
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`period_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
|
||||
`period_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期键',
|
||||
`stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
|
||||
`period_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '周期名称',
|
||||
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
|
||||
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
|
||||
@ -83,29 +85,290 @@
|
||||
`metric_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '人数指标类型',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `sys_origin`, `country_code`, `user_id`, `metric_type`),
|
||||
KEY `idx_period_metric_country` (`period_type`, `period_key`, `sys_origin`, `metric_type`, `country_code`),
|
||||
KEY `idx_origin_user_metric` (`sys_origin`, `user_id`, `metric_type`)
|
||||
UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `user_id`, `metric_type`),
|
||||
KEY `idx_period_metric_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `metric_type`, `country_code`),
|
||||
KEY `idx_origin_user_metric` (`sys_origin`, `stat_timezone`, `user_id`, `metric_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏周期用户去重明细'
|
||||
</update>
|
||||
|
||||
<update id="ensurePeriodMetricTimezoneSchema">
|
||||
SET @country_dashboard_schema_lock = GET_LOCK('country_dashboard_period_timezone_schema', 60);
|
||||
SET @current_schema = DATABASE();
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'stat_timezone'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''Asia/Riyadh'' COMMENT ''统计时区'' AFTER `period_key`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND COLUMN_NAME = 'stat_timezone'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` ADD COLUMN `stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''Asia/Riyadh'' COMMENT ''统计时区'' AFTER `period_key`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'uk_period_origin_country'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'period_type,period_key,stat_timezone,sys_origin,country_code',
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `uk_period_origin_country`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'uk_period_origin_country'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_origin_period'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'sys_origin,stat_timezone,period_type,period_key',
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `idx_origin_period`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_origin_period'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD KEY `idx_origin_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_period_date'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'stat_timezone,period_type,period_start_date,period_end_date',
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `idx_period_date`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_period_date'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD KEY `idx_period_date` (`stat_timezone`, `period_type`, `period_start_date`, `period_end_date`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'uk_period_user_metric'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'period_type,period_key,stat_timezone,sys_origin,country_code,user_id,metric_type',
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `uk_period_user_metric`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'uk_period_user_metric'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` ADD UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `user_id`, `metric_type`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_period_metric_country'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'period_type,period_key,stat_timezone,sys_origin,metric_type,country_code',
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `idx_period_metric_country`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_period_metric_country'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` ADD KEY `idx_period_metric_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `metric_type`, `country_code`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_origin_user_metric'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NOT NULL
|
||||
AND @index_columns <> 'sys_origin,stat_timezone,user_id,metric_type',
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `idx_origin_user_metric`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
SET @index_columns = (
|
||||
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_origin_user_metric'
|
||||
);
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_schema_lock = 1
|
||||
AND @index_columns IS NULL,
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` ADD KEY `idx_origin_user_metric` (`sys_origin`, `stat_timezone`, `user_id`, `metric_type`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
DO IF(@country_dashboard_schema_lock = 1, RELEASE_LOCK('country_dashboard_period_timezone_schema'), 0);
|
||||
</update>
|
||||
|
||||
<sql id="CountryColumns">
|
||||
NULLIF(u.country_code, '') AS countryCode,
|
||||
NULLIF(u.country_name, '') AS countryName
|
||||
</sql>
|
||||
|
||||
<sql id="StatTimeExprT">
|
||||
<choose>
|
||||
<when test="storageTimezoneOffset != null and storageTimezoneOffset != '' and statTimezoneOffset != null and statTimezoneOffset != ''">
|
||||
CONVERT_TZ(t.create_time, #{storageTimezoneOffset}, #{statTimezoneOffset})
|
||||
</when>
|
||||
<otherwise>t.create_time</otherwise>
|
||||
</choose>
|
||||
</sql>
|
||||
|
||||
<sql id="StatTimeExprU">
|
||||
<choose>
|
||||
<when test="storageTimezoneOffset != null and storageTimezoneOffset != '' and statTimezoneOffset != null and statTimezoneOffset != ''">
|
||||
CONVERT_TZ(u.create_time, #{storageTimezoneOffset}, #{statTimezoneOffset})
|
||||
</when>
|
||||
<otherwise>u.create_time</otherwise>
|
||||
</choose>
|
||||
</sql>
|
||||
|
||||
<sql id="PeriodColumnsT">
|
||||
<choose>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(t.create_time, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(t.create_time, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(t.create_time, '%Y-%m')</when>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%Y-%m')</when>
|
||||
<otherwise>'ALL'</otherwise>
|
||||
</choose>
|
||||
AS periodKey,
|
||||
<choose>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(t.create_time, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(t.create_time, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(t.create_time, '%Y-%m')</when>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(<include refid="StatTimeExprT"/>, '%Y-%m')</when>
|
||||
<otherwise>'全部'</otherwise>
|
||||
</choose>
|
||||
AS periodName
|
||||
@ -113,16 +376,16 @@
|
||||
|
||||
<sql id="PeriodColumnsU">
|
||||
<choose>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(u.create_time, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(u.create_time, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(u.create_time, '%Y-%m')</when>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m')</when>
|
||||
<otherwise>'ALL'</otherwise>
|
||||
</choose>
|
||||
AS periodKey,
|
||||
<choose>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(u.create_time, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(u.create_time, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(u.create_time, '%Y-%m')</when>
|
||||
<when test="periodType == 'DAY'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m-%d')</when>
|
||||
<when test="periodType == 'WEEK'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m')</when>
|
||||
<otherwise>'全部'</otherwise>
|
||||
</choose>
|
||||
AS periodName
|
||||
@ -418,6 +681,7 @@
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = #{periodType}
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="periodType != 'ALL' and startTime != null">
|
||||
AND t.period_start_date >= DATE(#{startTime})
|
||||
</if>
|
||||
@ -440,6 +704,7 @@
|
||||
SELECT COUNT(DISTINCT t.period_key)
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = #{periodType}
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="periodKeys != null and periodKeys.size() > 0">
|
||||
AND t.period_key IN
|
||||
<foreach collection="periodKeys" item="periodKey" open="(" separator="," close=")">
|
||||
@ -456,6 +721,7 @@
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = 'ALL'
|
||||
AND t.period_key = 'ALL'
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
@ -537,6 +803,7 @@
|
||||
DELETE FROM country_dashboard_period_metric
|
||||
WHERE period_type = #{periodType}
|
||||
AND period_key = #{periodKey}
|
||||
AND stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
@ -546,6 +813,7 @@
|
||||
INSERT INTO country_dashboard_period_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
@ -572,6 +840,7 @@
|
||||
(
|
||||
#{item.periodType},
|
||||
#{item.periodKey},
|
||||
#{item.statTimezone},
|
||||
#{item.periodName},
|
||||
#{item.periodStartDate},
|
||||
#{item.periodEndDate},
|
||||
@ -619,6 +888,7 @@
|
||||
DELETE FROM country_dashboard_period_user_metric
|
||||
WHERE period_type = #{periodType}
|
||||
AND period_key = #{periodKey}
|
||||
AND stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
@ -628,6 +898,7 @@
|
||||
INSERT IGNORE INTO country_dashboard_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
@ -640,6 +911,7 @@
|
||||
SELECT DISTINCT
|
||||
#{periodType},
|
||||
#{periodKey},
|
||||
#{statTimezone},
|
||||
#{periodName},
|
||||
#{periodStartDate},
|
||||
#{periodEndDate},
|
||||
@ -661,6 +933,7 @@
|
||||
INSERT IGNORE INTO country_dashboard_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
@ -673,6 +946,7 @@
|
||||
SELECT DISTINCT
|
||||
#{periodType},
|
||||
#{periodKey},
|
||||
#{statTimezone},
|
||||
#{periodName},
|
||||
#{periodStartDate},
|
||||
#{periodEndDate},
|
||||
@ -696,6 +970,7 @@
|
||||
INSERT IGNORE INTO country_dashboard_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
@ -708,6 +983,7 @@
|
||||
SELECT DISTINCT
|
||||
#{periodType},
|
||||
#{periodKey},
|
||||
#{statTimezone},
|
||||
#{periodName},
|
||||
#{periodStartDate},
|
||||
#{periodEndDate},
|
||||
@ -741,6 +1017,7 @@
|
||||
INSERT IGNORE INTO country_dashboard_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
@ -753,6 +1030,7 @@
|
||||
SELECT DISTINCT
|
||||
'ALL',
|
||||
'ALL',
|
||||
#{statTimezone},
|
||||
'全部',
|
||||
NULL,
|
||||
NULL,
|
||||
@ -763,6 +1041,7 @@
|
||||
t.metric_type
|
||||
FROM country_dashboard_period_user_metric t
|
||||
WHERE t.period_type = 'DAY'
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
@ -781,13 +1060,14 @@
|
||||
FROM country_dashboard_period_user_metric t
|
||||
WHERE t.period_type = #{periodType}
|
||||
AND t.period_key = #{periodKey}
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
GROUP BY countryCode, periodKey
|
||||
</select>
|
||||
|
||||
<select id="listAllPeriodAmountsFromDaily"
|
||||
<select id="listAllPeriodAmountsFromPeriodDays"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO">
|
||||
SELECT
|
||||
t.country_code AS countryCode,
|
||||
@ -804,8 +1084,9 @@
|
||||
IFNULL(SUM(t.lucky_gift_payout), 0) AS luckyGiftPayout,
|
||||
IFNULL(SUM(t.game_total_flow), 0) AS gameTotalFlow,
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout
|
||||
FROM country_dashboard_daily_metric t
|
||||
WHERE 1 = 1
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = 'DAY'
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
@ -841,9 +1122,9 @@
|
||||
<include refid="UserTimeWhere"/>
|
||||
<include refid="SourceSysOriginWhere"/>
|
||||
<choose>
|
||||
<when test="periodType == 'DAY'">AND DATE(u.create_time) = DATE(t.create_time)</when>
|
||||
<when test="periodType == 'WEEK'">AND DATE_FORMAT(u.create_time, '%x-W%v') = DATE_FORMAT(t.create_time, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">AND DATE_FORMAT(u.create_time, '%Y-%m') = DATE_FORMAT(t.create_time, '%Y-%m')</when>
|
||||
<when test="periodType == 'DAY'">AND DATE(<include refid="StatTimeExprU"/>) = DATE(<include refid="StatTimeExprT"/>)</when>
|
||||
<when test="periodType == 'WEEK'">AND DATE_FORMAT(<include refid="StatTimeExprU"/>, '%x-W%v') = DATE_FORMAT(<include refid="StatTimeExprT"/>, '%x-W%v')</when>
|
||||
<when test="periodType == 'MONTH'">AND DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m') = DATE_FORMAT(<include refid="StatTimeExprT"/>, '%Y-%m')</when>
|
||||
</choose>
|
||||
GROUP BY countryCode, countryName, periodKey, periodName
|
||||
</select>
|
||||
@ -945,7 +1226,10 @@
|
||||
t.amount,
|
||||
t.coinQuantity,
|
||||
t.remark,
|
||||
DATE_FORMAT(t.createTime, '%Y-%m-%d %H:%i:%s') AS createTime
|
||||
DATE_FORMAT(
|
||||
CONVERT_TZ(t.createTime, #{storageTimezoneOffset}, #{statTimezoneOffset}),
|
||||
'%Y-%m-%d %H:%i:%s'
|
||||
) AS createTime
|
||||
FROM (
|
||||
<include refid="RechargeDetailSource"/>
|
||||
) t
|
||||
@ -1067,7 +1351,7 @@
|
||||
u.id AS userId,
|
||||
<include refid="CountryColumns"/>,
|
||||
<include refid="PeriodColumnsU"/>,
|
||||
DATE_FORMAT(u.create_time, '%Y-%m-%d') AS registerDate
|
||||
DATE_FORMAT(<include refid="StatTimeExprU"/>, '%Y-%m-%d') AS registerDate
|
||||
FROM user_base_info u
|
||||
WHERE 1 = 1
|
||||
<include refid="UserWhere"/>
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
- `periodType`: `DAY` / `WEEK` / `MONTH` / `ALL`
|
||||
- `startDate`, `endDate`: 闭区间日期,后端转为 `[startDate 00:00:00, endDate + 1 day 00:00:00)`
|
||||
- `statTimezone`: 统计时区,默认 `Asia/Riyadh`,支持 `Asia/Riyadh` / `UTC` / `Asia/Shanghai`
|
||||
- `countryKeyword`: 国家码或国家名模糊匹配
|
||||
- `sysOrigin`: 系统来源,预聚合查询必须传
|
||||
|
||||
@ -32,7 +33,7 @@
|
||||
|
||||
### `country_dashboard_period_metric`
|
||||
|
||||
周期汇总表,按 `period_type + period_key + sys_origin + country_code` 唯一。
|
||||
周期汇总表,按 `period_type + period_key + stat_timezone + sys_origin + country_code` 唯一。
|
||||
|
||||
周期键示例:
|
||||
|
||||
@ -45,7 +46,7 @@
|
||||
|
||||
### `country_dashboard_period_user_metric`
|
||||
|
||||
周期用户去重明细表,按 `period_type + period_key + sys_origin + country_code + user_id + metric_type` 唯一。
|
||||
周期用户去重明细表,按 `period_type + period_key + stat_timezone + sys_origin + country_code + user_id + metric_type` 唯一。
|
||||
|
||||
当前维护的 `metric_type`:
|
||||
|
||||
@ -62,6 +63,7 @@
|
||||
走预聚合的条件:
|
||||
|
||||
- `sysOrigin` 不为空。
|
||||
- 对应 `statTimezone` 的周期预聚合已经存在。
|
||||
- `DAY`: 查询范围内每一天都有 `country_dashboard_period_metric` 数据。
|
||||
- `WEEK/MONTH`: 查询范围必须按周期边界对齐,当前未结束的周/月允许到今天。
|
||||
- `ALL`: 无 `startDate/endDate` 时固定走 `period_type=ALL, period_key=ALL`。如果汇总表未回填,返回空汇总,不再回退全历史实时扫描。
|
||||
@ -95,10 +97,18 @@ red-circle:
|
||||
|
||||
刷新链路:
|
||||
|
||||
1. 刷新日表 `country_dashboard_daily_metric`。
|
||||
2. 刷新对应 `DAY` 周期表和用户去重明细。
|
||||
3. 根据最近日期刷新受影响的 `WEEK`、`MONTH`。
|
||||
4. 基于已存在的日表和日级用户明细刷新 `ALL`。
|
||||
1. 按利雅得时区刷新日表 `country_dashboard_daily_metric`。
|
||||
2. 按配置的统计时区刷新对应 `DAY` 周期表和用户去重明细。
|
||||
3. 按配置的统计时区刷新受影响的 `WEEK`、`MONTH`。
|
||||
4. 基于同一 `stat_timezone` 下已存在的 `DAY` 周期表和日级用户明细刷新 `ALL`。
|
||||
|
||||
默认刷新时区:
|
||||
|
||||
```yaml
|
||||
red-circle:
|
||||
country-dashboard:
|
||||
refresh-timezones: Asia/Riyadh,UTC,Asia/Shanghai
|
||||
```
|
||||
|
||||
## 指标来源
|
||||
|
||||
@ -126,6 +136,7 @@ SQL 预聚合指标:
|
||||
|
||||
- `sql/20260507_country_dashboard_daily_metric.sql`
|
||||
- `sql/20260507_country_dashboard_period_metric.sql`
|
||||
- `sql/20260508_country_dashboard_period_metric_timezone.sql`
|
||||
|
||||
为避免遗漏,console 的刷新和手动回填入口会先执行同等的 `CREATE TABLE IF NOT EXISTS` 检查,再写入预聚合数据。
|
||||
|
||||
@ -139,7 +150,13 @@ SQL 预聚合指标:
|
||||
curl -X POST 'http://127.0.0.1:{console_port}/console/datav/country-dashboard/backfill?startDate=2026-04-08&endDate=2026-05-07&sysOrigin=LIKEI'
|
||||
```
|
||||
|
||||
该入口会按天重算日表和 `DAY` 周期,再刷新受影响的 `WEEK`、`MONTH` 和 `ALL`。
|
||||
该入口默认回填 `Asia/Riyadh`、`UTC`、`Asia/Shanghai` 三个统计时区。需要指定时区时可以传:
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://127.0.0.1:{console_port}/console/datav/country-dashboard/backfill?startDate=2026-04-08&endDate=2026-05-07&sysOrigin=LIKEI&statTimezones=Asia/Riyadh,UTC,Asia/Shanghai'
|
||||
```
|
||||
|
||||
该入口会按天重算日表,并按每个 `stat_timezone` 重算 `DAY` 周期,再刷新受影响的 `WEEK`、`MONTH` 和 `ALL`。
|
||||
|
||||
## 验证 SQL
|
||||
|
||||
@ -156,19 +173,19 @@ ORDER BY stat_date DESC;
|
||||
检查周期汇总:
|
||||
|
||||
```sql
|
||||
SELECT period_type, period_key, sys_origin, COUNT(1)
|
||||
SELECT stat_timezone, period_type, period_key, sys_origin, COUNT(1)
|
||||
FROM country_dashboard_period_metric
|
||||
WHERE sys_origin = 'LIKEI'
|
||||
GROUP BY period_type, period_key, sys_origin
|
||||
ORDER BY period_type, period_key DESC;
|
||||
GROUP BY stat_timezone, period_type, period_key, sys_origin
|
||||
ORDER BY stat_timezone, period_type, period_key DESC;
|
||||
```
|
||||
|
||||
检查用户去重明细:
|
||||
|
||||
```sql
|
||||
SELECT period_type, period_key, metric_type, COUNT(1)
|
||||
SELECT stat_timezone, period_type, period_key, metric_type, COUNT(1)
|
||||
FROM country_dashboard_period_user_metric
|
||||
WHERE sys_origin = 'LIKEI'
|
||||
GROUP BY period_type, period_key, metric_type
|
||||
ORDER BY period_type, period_key DESC, metric_type;
|
||||
GROUP BY stat_timezone, period_type, period_key, metric_type
|
||||
ORDER BY stat_timezone, period_type, period_key DESC, metric_type;
|
||||
```
|
||||
|
||||
@ -4,6 +4,7 @@ CREATE TABLE IF NOT EXISTS `country_dashboard_period_metric` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`period_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
|
||||
`period_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期键',
|
||||
`stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
|
||||
`period_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '周期名称',
|
||||
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
|
||||
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
|
||||
@ -27,15 +28,16 @@ CREATE TABLE IF NOT EXISTS `country_dashboard_period_metric` (
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `sys_origin`, `country_code`),
|
||||
KEY `idx_origin_period` (`sys_origin`, `period_type`, `period_key`),
|
||||
KEY `idx_period_date` (`period_type`, `period_start_date`, `period_end_date`)
|
||||
UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`),
|
||||
KEY `idx_origin_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`),
|
||||
KEY `idx_period_date` (`stat_timezone`, `period_type`, `period_start_date`, `period_end_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏周期预聚合';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_period_user_metric` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`period_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
|
||||
`period_key` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '周期键',
|
||||
`stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
|
||||
`period_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '周期名称',
|
||||
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
|
||||
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
|
||||
@ -46,7 +48,7 @@ CREATE TABLE IF NOT EXISTS `country_dashboard_period_user_metric` (
|
||||
`metric_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '人数指标类型',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `sys_origin`, `country_code`, `user_id`, `metric_type`),
|
||||
KEY `idx_period_metric_country` (`period_type`, `period_key`, `sys_origin`, `metric_type`, `country_code`),
|
||||
KEY `idx_origin_user_metric` (`sys_origin`, `user_id`, `metric_type`)
|
||||
UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `user_id`, `metric_type`),
|
||||
KEY `idx_period_metric_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `metric_type`, `country_code`),
|
||||
KEY `idx_origin_user_metric` (`sys_origin`, `stat_timezone`, `user_id`, `metric_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏周期用户去重明细';
|
||||
|
||||
141
sql/20260508_country_dashboard_period_metric_timezone.sql
Normal file
141
sql/20260508_country_dashboard_period_metric_timezone.sql
Normal file
@ -0,0 +1,141 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @current_schema = DATABASE();
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'stat_timezone'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''Asia/Riyadh'' COMMENT ''统计时区'' AFTER `period_key`'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND COLUMN_NAME = 'stat_timezone'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` ADD COLUMN `stat_timezone` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''Asia/Riyadh'' COMMENT ''统计时区'' AFTER `period_key`'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'uk_period_origin_country'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `uk_period_origin_country`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_metric`
|
||||
ADD UNIQUE KEY `uk_period_origin_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`);
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_origin_period'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `idx_origin_period`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_metric`
|
||||
ADD KEY `idx_origin_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`);
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND INDEX_NAME = 'idx_period_date'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` DROP INDEX `idx_period_date`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_metric`
|
||||
ADD KEY `idx_period_date` (`stat_timezone`, `period_type`, `period_start_date`, `period_end_date`);
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'uk_period_user_metric'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `uk_period_user_metric`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_user_metric`
|
||||
ADD UNIQUE KEY `uk_period_user_metric` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `user_id`, `metric_type`);
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_period_metric_country'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `idx_period_metric_country`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_user_metric`
|
||||
ADD KEY `idx_period_metric_country` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `metric_type`, `country_code`);
|
||||
|
||||
SET @ddl = IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_user_metric'
|
||||
AND INDEX_NAME = 'idx_origin_user_metric'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_user_metric` DROP INDEX `idx_origin_user_metric`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE `country_dashboard_period_user_metric`
|
||||
ADD KEY `idx_origin_user_metric` (`sys_origin`, `stat_timezone`, `user_id`, `metric_type`);
|
||||
Loading…
x
Reference in New Issue
Block a user