feat(console): add country dashboard game metrics
This commit is contained in:
parent
7238e8eb46
commit
f1e5e750bd
@ -1,10 +1,13 @@
|
||||
package com.red.circle.console.adapter.app.user;
|
||||
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardCO;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardGameMetricCO;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardRechargeDetailCO;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardGameMetricQryCmd;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardQryCmd;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardRechargeDetailQryCmd;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardDailyMetricService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardGameMetricService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardService;
|
||||
import com.red.circle.console.app.service.app.user.DatavService;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
@ -36,6 +39,7 @@ public class DatavRestController extends BaseController {
|
||||
private final DatavService userExpandService;
|
||||
private final CountryDashboardService countryDashboardService;
|
||||
private final CountryDashboardDailyMetricService countryDashboardDailyMetricService;
|
||||
private final CountryDashboardGameMetricService countryDashboardGameMetricService;
|
||||
|
||||
@GetMapping("/active/user-country-code")
|
||||
public List<ActiveUserCountryCodeDTO> latestActiveUserCountryCode() {
|
||||
@ -63,6 +67,12 @@ public class DatavRestController extends BaseController {
|
||||
return countryDashboardService.pageRechargeDetails(cmd);
|
||||
}
|
||||
|
||||
@GetMapping("/country-dashboard/game-metrics")
|
||||
public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetrics(
|
||||
CountryDashboardGameMetricQryCmd cmd) {
|
||||
return countryDashboardGameMetricService.pageGameMetrics(cmd);
|
||||
}
|
||||
|
||||
@PostMapping("/country-dashboard/backfill")
|
||||
public Map<String, Object> backfillCountryDashboard(
|
||||
@RequestParam String startDate,
|
||||
@ -81,4 +91,22 @@ public class DatavRestController extends BaseController {
|
||||
"statTimezones", refreshedTimezones);
|
||||
}
|
||||
|
||||
@PostMapping("/country-dashboard/game-metrics/backfill")
|
||||
public Map<String, Object> backfillCountryDashboardGameMetrics(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
@RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin,
|
||||
@RequestParam(value = "statTimezones", required = false) String statTimezones) {
|
||||
LocalDate parsedStartDate = LocalDate.parse(startDate);
|
||||
LocalDate parsedEndDate = LocalDate.parse(endDate);
|
||||
List<String> refreshedTimezones = countryDashboardGameMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
return Map.of(
|
||||
"status", "success",
|
||||
"startDate", parsedStartDate.toString(),
|
||||
"endDate", parsedEndDate.toString(),
|
||||
"sysOrigin", sysOrigin,
|
||||
"statTimezones", refreshedTimezones);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.red.circle.console.app.scheduler;
|
||||
|
||||
import com.red.circle.component.redis.annotation.TaskCacheLock;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardDailyMetricService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardGameMetricService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -17,6 +18,7 @@ import org.springframework.stereotype.Component;
|
||||
public class CountryDashboardDailyMetricTask {
|
||||
|
||||
private final CountryDashboardDailyMetricService countryDashboardDailyMetricService;
|
||||
private final CountryDashboardGameMetricService countryDashboardGameMetricService;
|
||||
|
||||
@Value("${red-circle.country-dashboard.refresh-today-days:1}")
|
||||
private int refreshTodayDays;
|
||||
@ -30,6 +32,7 @@ public class CountryDashboardDailyMetricTask {
|
||||
public void refreshToday() {
|
||||
long start = System.currentTimeMillis();
|
||||
countryDashboardDailyMetricService.refreshRecentDays(refreshTodayDays);
|
||||
countryDashboardGameMetricService.refreshRecentDays(refreshTodayDays);
|
||||
log.info("country dashboard refresh today task finished, days={}, costMs={}",
|
||||
refreshTodayDays, System.currentTimeMillis() - start);
|
||||
}
|
||||
@ -40,6 +43,7 @@ public class CountryDashboardDailyMetricTask {
|
||||
public void refreshRecent() {
|
||||
long start = System.currentTimeMillis();
|
||||
countryDashboardDailyMetricService.refreshRecentDays(refreshRecentDays);
|
||||
countryDashboardGameMetricService.refreshRecentDays(refreshRecentDays);
|
||||
log.info("country dashboard refresh recent task finished, days={}, costMs={}",
|
||||
refreshRecentDays, System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
@ -0,0 +1,646 @@
|
||||
package com.red.circle.console.app.service.app.datav;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardGameMetricCO;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardGameMetricQryCmd;
|
||||
import com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
/**
|
||||
* 国家数据大屏游戏维度预聚合.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGameMetricService {
|
||||
|
||||
private static final String PERIOD_DAY = "DAY";
|
||||
private static final String PERIOD_WEEK = "WEEK";
|
||||
private static final String PERIOD_MONTH = "MONTH";
|
||||
private static final String PERIOD_ALL = "ALL";
|
||||
private static final ZoneId STORAGE_ZONE_ID = ZoneId.of("Asia/Riyadh");
|
||||
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 WeekFields WEEK_FIELDS = WeekFields.ISO;
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
|
||||
@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;
|
||||
|
||||
@Override
|
||||
public PageResult<CountryDashboardGameMetricCO> pageGameMetrics(
|
||||
CountryDashboardGameMetricQryCmd cmd) {
|
||||
ensureMetricSchema();
|
||||
QueryCondition condition = QueryCondition.of(cmd);
|
||||
int cursor = Math.max(1, cmd == null || cmd.getCursor() == null ? 1 : cmd.getCursor());
|
||||
int limit = Math.max(1, cmd == null || cmd.getLimit() == null ? 50 : cmd.getLimit());
|
||||
limit = Math.min(200, limit);
|
||||
int offset = (cursor - 1) * limit;
|
||||
|
||||
PageResult<CountryDashboardGameMetricCO> result = new PageResult<>();
|
||||
result.setCurrent(cursor);
|
||||
result.setSize(limit);
|
||||
if (!hasText(condition.sysOrigin)) {
|
||||
result.setTotal(0L);
|
||||
result.setRecords(List.of());
|
||||
return result;
|
||||
}
|
||||
|
||||
SortSpec sortSpec = normalizeSort(cmd == null ? null : cmd.getSortField(),
|
||||
cmd == null ? null : cmd.getSortOrder());
|
||||
String countryCodes = normalizeCsv(cmd == null ? null : cmd.getCountryCodes());
|
||||
String gameProvider = trimToNull(cmd == null ? null : cmd.getGameProvider());
|
||||
if (gameProvider != null) {
|
||||
gameProvider = gameProvider.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
String gameKeyword = trimToNull(cmd == null ? null : cmd.getGameKeyword());
|
||||
|
||||
Long total = countryDashboardDAO.countGamePeriodMetrics(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
PERIOD_ALL.equals(condition.periodType) ? PERIOD_ALL : null,
|
||||
condition.statTimezone, condition.countryKeyword, countryCodes, gameProvider, gameKeyword,
|
||||
condition.sysOrigin);
|
||||
List<CountryDashboardGameMetricCO> records = countryDashboardDAO.listGamePeriodMetrics(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
PERIOD_ALL.equals(condition.periodType) ? PERIOD_ALL : null,
|
||||
condition.statTimezone, condition.countryKeyword, countryCodes, gameProvider, gameKeyword,
|
||||
condition.sysOrigin, sortSpec.column(), sortSpec.direction(), offset, limit)
|
||||
.stream()
|
||||
.map(this::toCO)
|
||||
.toList();
|
||||
|
||||
result.setTotal(total == null ? 0L : total);
|
||||
result.setRecords(records);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshRecentDays(int days) {
|
||||
ensureMetricSchema();
|
||||
int safeDays = Math.max(days, 1);
|
||||
List<String> sysOrigins = resolveSysOrigins();
|
||||
List<StatTimezone> statTimezones = resolveStatTimezones(null);
|
||||
for (String sysOrigin : sysOrigins) {
|
||||
for (StatTimezone statTimezone : statTimezones) {
|
||||
List<LocalDate> statDates = recentDates(safeDays, statTimezone.zoneId());
|
||||
for (LocalDate statDate : statDates) {
|
||||
try {
|
||||
refreshPeriod(toDayWindow(statDate), sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard game day 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 game all metric failed, sysOrigin={}, statTimezone={}",
|
||||
sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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");
|
||||
}
|
||||
if (endDate.isBefore(startDate)) {
|
||||
throw new IllegalArgumentException("endDate must be greater than or equal to startDate");
|
||||
}
|
||||
long dayCount = ChronoUnit.DAYS.between(startDate, endDate) + 1;
|
||||
if (dayCount > 366) {
|
||||
throw new IllegalArgumentException("backfill range can not exceed 366 days");
|
||||
}
|
||||
String safeSysOrigin = trimToNull(sysOrigin);
|
||||
if (safeSysOrigin == null) {
|
||||
safeSysOrigin = SysOriginPlatformEnum.LIKEI.name();
|
||||
}
|
||||
|
||||
List<LocalDate> statDates = new ArrayList<>();
|
||||
for (long i = 0; i < dayCount; i++) {
|
||||
statDates.add(startDate.plusDays(i));
|
||||
}
|
||||
List<StatTimezone> statTimezones = resolveStatTimezones(statTimezonesValue);
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("refresh country dashboard game range start, startDate={}, endDate={}, days={}, sysOrigin={}, statTimezones={}",
|
||||
startDate, endDate, dayCount, safeSysOrigin,
|
||||
statTimezones.stream().map(StatTimezone::id).toList());
|
||||
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 game 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() {
|
||||
if (metricSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (metricSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
countryDashboardDAO.createGamePeriodMetricTable();
|
||||
countryDashboardDAO.createGamePeriodUserMetricTable();
|
||||
metricSchemaEnsured = true;
|
||||
log.info("country dashboard game metric tables ensured");
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshAffectedPeriods(List<LocalDate> statDates, String sysOrigin,
|
||||
StatTimezone statTimezone, boolean failFast) {
|
||||
Set<PeriodWindow> windows = new LinkedHashSet<>();
|
||||
for (LocalDate statDate : statDates) {
|
||||
windows.add(toWeekWindow(statDate, statTimezone.zoneId()));
|
||||
windows.add(toMonthWindow(statDate, statTimezone.zoneId()));
|
||||
}
|
||||
for (PeriodWindow window : windows) {
|
||||
try {
|
||||
refreshPeriod(window, sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
if (failFast) {
|
||||
throw ex;
|
||||
}
|
||||
log.error("refresh country dashboard game period metric failed, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshPeriod(PeriodWindow window, String sysOrigin, StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
TimeWindow timeWindow = toStorageTimeWindow(window, statTimezone);
|
||||
Map<String, CountryDashboardGameMetricDTO> rows = new LinkedHashMap<>();
|
||||
mergeAmounts(rows, countryDashboardDAO.listWalletGameMetricAmounts(
|
||||
window.periodType(), timeWindow.startTime(), timeWindow.endTime(),
|
||||
timeWindow.startTimeMillis(), timeWindow.endTimeMillis(), sysOrigin,
|
||||
zoneOffset(STORAGE_ZONE_ID), statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergeAmounts(rows, countryDashboardDAO.listLuckyBoxGameMetricAmounts(
|
||||
window.periodType(), timeWindow.startTime(), timeWindow.endTime(), sysOrigin,
|
||||
zoneOffset(STORAGE_ZONE_ID), statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
countryDashboardDAO.deleteGamePeriodUserMetrics(window.periodType(), window.periodKey(),
|
||||
statTimezone.id(), sysOrigin);
|
||||
int walletUsers = countryDashboardDAO.insertWalletGamePeriodUserMetrics(
|
||||
window.periodType(), window.periodKey(), window.periodName(),
|
||||
window.periodStartDate(), window.periodEndDate(), statTimezone.id(),
|
||||
timeWindow.startTime(), timeWindow.endTime(), timeWindow.startTimeMillis(),
|
||||
timeWindow.endTimeMillis(), sysOrigin);
|
||||
int luckyBoxUsers = countryDashboardDAO.insertLuckyBoxGamePeriodUserMetrics(
|
||||
window.periodType(), window.periodKey(), window.periodName(),
|
||||
window.periodStartDate(), window.periodEndDate(), statTimezone.id(),
|
||||
timeWindow.startTime(), timeWindow.endTime(), sysOrigin);
|
||||
mergeUserCounts(rows, countryDashboardDAO.listGamePeriodUserMetricCounts(
|
||||
window.periodType(), window.periodKey(), statTimezone.id(), sysOrigin),
|
||||
window, sysOrigin, statTimezone);
|
||||
List<CountryDashboardGameMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
int deleted = countryDashboardDAO.deleteGamePeriodMetrics(window.periodType(),
|
||||
window.periodKey(), statTimezone.id(), sysOrigin);
|
||||
int inserted = metrics.isEmpty() ? 0 : countryDashboardDAO.upsertGamePeriodMetrics(metrics);
|
||||
return new RefreshWriteResult(deleted, inserted, walletUsers + luckyBoxUsers);
|
||||
});
|
||||
|
||||
log.info("refresh country dashboard game period metric success, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}, rows={}, 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, StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
PeriodWindow window = new PeriodWindow(PERIOD_ALL, PERIOD_ALL, "全部", null, null);
|
||||
Map<String, CountryDashboardGameMetricDTO> rows = new LinkedHashMap<>();
|
||||
mergeAmounts(rows, countryDashboardDAO.listAllGamePeriodAmountsFromPeriodDays(
|
||||
statTimezone.id(), sysOrigin), window, sysOrigin, statTimezone);
|
||||
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
countryDashboardDAO.deleteGamePeriodUserMetrics(PERIOD_ALL, PERIOD_ALL,
|
||||
statTimezone.id(), sysOrigin);
|
||||
int userRows = countryDashboardDAO.insertAllGamePeriodUserMetricsFromDaily(
|
||||
statTimezone.id(), sysOrigin);
|
||||
mergeUserCounts(rows, countryDashboardDAO.listGamePeriodUserMetricCounts(
|
||||
PERIOD_ALL, PERIOD_ALL, statTimezone.id(), sysOrigin), window, sysOrigin, statTimezone);
|
||||
List<CountryDashboardGameMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
int deleted = countryDashboardDAO.deleteGamePeriodMetrics(PERIOD_ALL, PERIOD_ALL,
|
||||
statTimezone.id(), sysOrigin);
|
||||
int inserted = metrics.isEmpty() ? 0 : countryDashboardDAO.upsertGamePeriodMetrics(metrics);
|
||||
return new RefreshWriteResult(deleted, inserted, userRows);
|
||||
});
|
||||
|
||||
log.info("refresh country dashboard game all metric success, sysOrigin={}, statTimezone={}, rows={}, deleted={}, inserted={}, userRows={}, costMs={}",
|
||||
sysOrigin, statTimezone.id(), rows.size(), writeResult.deleted(), writeResult.inserted(),
|
||||
writeResult.userRows(), System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private void mergeAmounts(Map<String, CountryDashboardGameMetricDTO> rows,
|
||||
List<CountryDashboardGameMetricDTO> metrics, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (CountryDashboardGameMetricDTO metric : metrics) {
|
||||
CountryDashboardGameMetricDTO row = getRow(rows, metric, window, sysOrigin, statTimezone);
|
||||
row.setConsumeAmount(add(row.getConsumeAmount(), metric.getConsumeAmount()));
|
||||
row.setPayoutAmount(add(row.getPayoutAmount(), metric.getPayoutAmount()));
|
||||
row.setProfitAmount(add(row.getProfitAmount(), metric.getProfitAmount()));
|
||||
row.setOrderCount(safeLong(row.getOrderCount()) + safeLong(metric.getOrderCount()));
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeUserCounts(Map<String, CountryDashboardGameMetricDTO> rows,
|
||||
List<CountryDashboardGameMetricDTO> metrics, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (CountryDashboardGameMetricDTO metric : metrics) {
|
||||
CountryDashboardGameMetricDTO row = getRow(rows, metric, window, sysOrigin, statTimezone);
|
||||
row.setUserCount(safeLong(metric.getUserCount()));
|
||||
}
|
||||
}
|
||||
|
||||
private CountryDashboardGameMetricDTO getRow(
|
||||
Map<String, CountryDashboardGameMetricDTO> rows, CountryDashboardGameMetricDTO metric,
|
||||
PeriodWindow window, String sysOrigin, StatTimezone statTimezone) {
|
||||
String countryCode = hasText(metric.getCountryCode()) ? metric.getCountryCode() : "UNKNOWN";
|
||||
String countryName = hasText(metric.getCountryName()) ? metric.getCountryName() : countryCode;
|
||||
String gameProvider = hasText(metric.getGameProvider()) ? metric.getGameProvider() : "UNKNOWN";
|
||||
String gameId = hasText(metric.getGameId()) ? metric.getGameId() : "UNKNOWN";
|
||||
String gameName = hasText(metric.getGameName()) ? metric.getGameName() : gameId;
|
||||
return rows.computeIfAbsent(countryCode + "|" + gameProvider + "|" + gameId,
|
||||
key -> new CountryDashboardGameMetricDTO()
|
||||
.setPeriodType(window.periodType())
|
||||
.setPeriodKey(window.periodKey())
|
||||
.setStatTimezone(statTimezone.id())
|
||||
.setPeriodName(window.periodName())
|
||||
.setPeriodStartDate(window.periodStartDate())
|
||||
.setPeriodEndDate(window.periodEndDate())
|
||||
.setSysOrigin(sysOrigin)
|
||||
.setCountryCode(countryCode)
|
||||
.setCountryName(countryName)
|
||||
.setGameProvider(gameProvider)
|
||||
.setGameId(gameId)
|
||||
.setGameName(gameName)
|
||||
.setConsumeAmount(BigDecimal.ZERO)
|
||||
.setPayoutAmount(BigDecimal.ZERO)
|
||||
.setProfitAmount(BigDecimal.ZERO)
|
||||
.setUserCount(0L)
|
||||
.setOrderCount(0L));
|
||||
}
|
||||
|
||||
private CountryDashboardGameMetricCO toCO(CountryDashboardGameMetricDTO dto) {
|
||||
BigDecimal consume = safe(dto.getConsumeAmount());
|
||||
BigDecimal payout = safe(dto.getPayoutAmount());
|
||||
BigDecimal profit = safe(dto.getProfitAmount());
|
||||
return new CountryDashboardGameMetricCO()
|
||||
.setPeriodKey(dto.getPeriodKey())
|
||||
.setPeriodName(dto.getPeriodName())
|
||||
.setStatTimezone(dto.getStatTimezone())
|
||||
.setCountryCode(dto.getCountryCode())
|
||||
.setCountryName(dto.getCountryName())
|
||||
.setGameProvider(dto.getGameProvider())
|
||||
.setGameId(dto.getGameId())
|
||||
.setGameName(dto.getGameName())
|
||||
.setConsumeAmount(consume)
|
||||
.setPayoutAmount(payout)
|
||||
.setProfitAmount(profit)
|
||||
.setPayoutRate(percent(payout, consume))
|
||||
.setProfitRate(percent(profit, consume))
|
||||
.setUserCount(safeLong(dto.getUserCount()))
|
||||
.setOrderCount(safeLong(dto.getOrderCount()))
|
||||
.setRefreshedAt(dto.getRefreshedAt() == null ? null : dto.getRefreshedAt().format(DATETIME_FORMATTER));
|
||||
}
|
||||
|
||||
private PeriodWindow toDayWindow(LocalDate statDate) {
|
||||
String key = statDate.format(DATE_FORMATTER);
|
||||
return new PeriodWindow(PERIOD_DAY, key, key, statDate, statDate.plusDays(1));
|
||||
}
|
||||
|
||||
private PeriodWindow toWeekWindow(LocalDate statDate, ZoneId statZoneId) {
|
||||
LocalDate start = statDate.with(DayOfWeek.MONDAY);
|
||||
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, ZoneId statZoneId) {
|
||||
LocalDate start = statDate.withDayOfMonth(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, ZoneId statZoneId) {
|
||||
LocalDate tomorrow = LocalDate.now(statZoneId).plusDays(1);
|
||||
return naturalEndExclusive.isAfter(tomorrow) ? tomorrow : naturalEndExclusive;
|
||||
}
|
||||
|
||||
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<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 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(CountryDashboardGameMetricServiceImpl::normalizeStatTimezone)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(zone -> zones.putIfAbsent(zone.id(), zone));
|
||||
if (zones.isEmpty()) {
|
||||
zones.put(STORAGE_ZONE_ID.getId(), new StatTimezone(STORAGE_ZONE_ID.getId(), STORAGE_ZONE_ID,
|
||||
zoneOffset(STORAGE_ZONE_ID)));
|
||||
}
|
||||
return new ArrayList<>(zones.values());
|
||||
}
|
||||
|
||||
private List<String> resolveSysOrigins() {
|
||||
String config = trimToNull(refreshSysOrigins);
|
||||
if (config == null) {
|
||||
return List.of(SysOriginPlatformEnum.LIKEI.name());
|
||||
}
|
||||
if ("ALL".equalsIgnoreCase(config)) {
|
||||
return SysOriginPlatformEnum.getVoiceSystems().stream()
|
||||
.map(SysOriginPlatformEnum::name)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
List<String> sysOrigins = Arrays.stream(config.split(","))
|
||||
.map(String::trim)
|
||||
.filter(CountryDashboardGameMetricServiceImpl::hasText)
|
||||
.distinct()
|
||||
.toList();
|
||||
return sysOrigins.isEmpty() ? List.of(SysOriginPlatformEnum.LIKEI.name()) : sysOrigins;
|
||||
}
|
||||
|
||||
private static SortSpec normalizeSort(String sortField, String sortOrder) {
|
||||
String column = switch (Objects.toString(sortField, "")) {
|
||||
case "payoutAmount" -> "t.payout_amount";
|
||||
case "profitAmount" -> "t.profit_amount";
|
||||
case "userCount" -> "t.user_count";
|
||||
case "orderCount" -> "t.order_count";
|
||||
case "consumeAmount" -> "t.consume_amount";
|
||||
default -> null;
|
||||
};
|
||||
String direction = "ascend".equalsIgnoreCase(sortOrder) ? "ASC" : "DESC";
|
||||
return new SortSpec(column, direction);
|
||||
}
|
||||
|
||||
private static 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) {
|
||||
return null;
|
||||
}
|
||||
return new StatTimezone(zoneId.getId(), zoneId, zoneOffset(zoneId));
|
||||
}
|
||||
|
||||
private static String normalizeCsv(String value) {
|
||||
if (!hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
String normalized = Arrays.stream(value.split(","))
|
||||
.map(String::trim)
|
||||
.filter(CountryDashboardGameMetricServiceImpl::hasText)
|
||||
.distinct()
|
||||
.reduce((left, right) -> left + "," + right)
|
||||
.orElse(null);
|
||||
return hasText(normalized) ? normalized : null;
|
||||
}
|
||||
|
||||
private static BigDecimal add(BigDecimal left, BigDecimal right) {
|
||||
return safe(left).add(safe(right));
|
||||
}
|
||||
|
||||
private static BigDecimal safe(BigDecimal value) {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private static BigDecimal percent(BigDecimal numerator, BigDecimal denominator) {
|
||||
BigDecimal safeDenominator = safe(denominator);
|
||||
if (BigDecimal.ZERO.compareTo(safeDenominator) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return safe(numerator)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.divide(safeDenominator, 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private static long safeLong(Long value) {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
|
||||
private static boolean hasText(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private static String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
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 record PeriodWindow(String periodType, String periodKey, String periodName,
|
||||
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) {
|
||||
}
|
||||
|
||||
private record SortSpec(String column, String direction) {
|
||||
}
|
||||
|
||||
private static final class QueryCondition {
|
||||
|
||||
private final String periodType;
|
||||
private final String statTimezone;
|
||||
private final ZoneId statZoneId;
|
||||
private final LocalDate startDateValue;
|
||||
private final LocalDate endDateValue;
|
||||
private final LocalDateTime startTime;
|
||||
private final LocalDateTime endTime;
|
||||
private final String countryKeyword;
|
||||
private final String sysOrigin;
|
||||
|
||||
private QueryCondition(String periodType, String statTimezone, ZoneId statZoneId,
|
||||
LocalDate startDateValue, LocalDate endDateValue, LocalDateTime startTime,
|
||||
LocalDateTime endTime, String countryKeyword, String sysOrigin) {
|
||||
this.periodType = periodType;
|
||||
this.statTimezone = statTimezone;
|
||||
this.statZoneId = statZoneId;
|
||||
this.startDateValue = startDateValue;
|
||||
this.endDateValue = endDateValue;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.countryKeyword = countryKeyword;
|
||||
this.sysOrigin = sysOrigin;
|
||||
}
|
||||
|
||||
private static QueryCondition of(CountryDashboardGameMetricQryCmd 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(statZoneId);
|
||||
switch (periodType) {
|
||||
case PERIOD_WEEK -> {
|
||||
start = today.with(DayOfWeek.MONDAY);
|
||||
end = today.with(DayOfWeek.SUNDAY);
|
||||
}
|
||||
case PERIOD_MONTH -> {
|
||||
start = today.withDayOfMonth(1);
|
||||
end = today.withDayOfMonth(today.lengthOfMonth());
|
||||
}
|
||||
default -> {
|
||||
start = today;
|
||||
end = today;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start != null && end == null) {
|
||||
end = start;
|
||||
}
|
||||
if (start == null && end != null) {
|
||||
start = end;
|
||||
}
|
||||
if (start != null && end != null && end.isBefore(start)) {
|
||||
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,
|
||||
statZoneId.getId(),
|
||||
statZoneId,
|
||||
start,
|
||||
end,
|
||||
storageStartTime,
|
||||
storageEndTime,
|
||||
trimToNull(cmd == null ? null : cmd.getCountryKeyword()),
|
||||
trimToNull(cmd == null ? null : cmd.getSysOrigin())
|
||||
);
|
||||
}
|
||||
|
||||
private static String normalizePeriodType(String periodType) {
|
||||
String value = trimToNull(periodType);
|
||||
if (value == null) {
|
||||
return PERIOD_DAY;
|
||||
}
|
||||
value = value.toUpperCase(Locale.ROOT);
|
||||
return switch (value) {
|
||||
case PERIOD_DAY, PERIOD_WEEK, PERIOD_MONTH, PERIOD_ALL -> value;
|
||||
default -> PERIOD_DAY;
|
||||
};
|
||||
}
|
||||
|
||||
private static ZoneId normalizeStatZoneId(String statTimezone) {
|
||||
StatTimezone normalized = normalizeStatTimezone(statTimezone);
|
||||
return normalized == null ? STORAGE_ZONE_ID : normalized.zoneId();
|
||||
}
|
||||
|
||||
private static LocalDate parseDate(String value) {
|
||||
String fixed = trimToNull(value);
|
||||
return fixed == null ? null : LocalDate.parse(fixed, DATE_FORMATTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.red.circle.console.app.dto.clientobject.datav;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 国家数据大屏游戏维度指标.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class CountryDashboardGameMetricCO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String periodKey;
|
||||
|
||||
private String periodName;
|
||||
|
||||
private String statTimezone;
|
||||
|
||||
private String countryCode;
|
||||
|
||||
private String countryName;
|
||||
|
||||
private String gameProvider;
|
||||
|
||||
private String gameId;
|
||||
|
||||
private String gameName;
|
||||
|
||||
private BigDecimal consumeAmount = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal payoutAmount = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal profitAmount = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal payoutRate = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal profitRate = BigDecimal.ZERO;
|
||||
|
||||
private Long userCount = 0L;
|
||||
|
||||
private Long orderCount = 0L;
|
||||
|
||||
private String refreshedAt;
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.red.circle.console.app.dto.cmd.datav;
|
||||
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 国家数据大屏游戏数据查询.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CountryDashboardGameMetricQryCmd extends CountryDashboardQryCmd {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 页码,从 1 开始.
|
||||
*/
|
||||
private Integer cursor;
|
||||
|
||||
/**
|
||||
* 每页条数.
|
||||
*/
|
||||
private Integer limit;
|
||||
|
||||
/**
|
||||
* 多个国家编码,逗号分隔.
|
||||
*/
|
||||
private String countryCodes;
|
||||
|
||||
/**
|
||||
* 游戏厂商.
|
||||
*/
|
||||
private String gameProvider;
|
||||
|
||||
/**
|
||||
* 游戏 ID 或名称关键字.
|
||||
*/
|
||||
private String gameKeyword;
|
||||
|
||||
/**
|
||||
* 排序字段.
|
||||
*/
|
||||
private String sortField;
|
||||
|
||||
/**
|
||||
* ascend / descend.
|
||||
*/
|
||||
private String sortOrder;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.red.circle.console.app.service.app.datav;
|
||||
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardGameMetricCO;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardGameMetricQryCmd;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 国家数据大屏游戏维度指标.
|
||||
*/
|
||||
public interface CountryDashboardGameMetricService {
|
||||
|
||||
PageResult<CountryDashboardGameMetricCO> pageGameMetrics(CountryDashboardGameMetricQryCmd cmd);
|
||||
|
||||
void refreshRecentDays(int days);
|
||||
|
||||
List<String> refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin,
|
||||
String statTimezones);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.red.circle.console.infra.database.rds.dao.datav;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardDailyMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardPeriodMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRechargeDetailDTO;
|
||||
@ -25,6 +26,10 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
|
||||
int createPeriodUserMetricTable();
|
||||
|
||||
int createGamePeriodMetricTable();
|
||||
|
||||
int createGamePeriodUserMetricTable();
|
||||
|
||||
int ensurePeriodMetricTimezoneSchema();
|
||||
|
||||
List<CountryDashboardMetricDTO> listNewUsers(
|
||||
@ -259,4 +264,102 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset,
|
||||
@Param("offset") int offset,
|
||||
@Param("limit") int limit);
|
||||
|
||||
List<CountryDashboardGameMetricDTO> listWalletGameMetricAmounts(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("startTimeMillis") Long startTimeMillis,
|
||||
@Param("endTimeMillis") Long endTimeMillis,
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
List<CountryDashboardGameMetricDTO> listLuckyBoxGameMetricAmounts(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("storageTimezoneOffset") String storageTimezoneOffset,
|
||||
@Param("statTimezoneOffset") String statTimezoneOffset);
|
||||
|
||||
int deleteGamePeriodUserMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int insertWalletGamePeriodUserMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@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 insertLuckyBoxGamePeriodUserMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@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);
|
||||
|
||||
int insertAllGamePeriodUserMetricsFromDaily(
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardGameMetricDTO> listGamePeriodUserMetricCounts(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardGameMetricDTO> listAllGamePeriodAmountsFromPeriodDays(
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int deleteGamePeriodMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
int upsertGamePeriodMetrics(@Param("metrics") List<CountryDashboardGameMetricDTO> metrics);
|
||||
|
||||
Long countGamePeriodMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("countryCodes") String countryCodes,
|
||||
@Param("gameProvider") String gameProvider,
|
||||
@Param("gameKeyword") String gameKeyword,
|
||||
@Param("sysOrigin") String sysOrigin);
|
||||
|
||||
List<CountryDashboardGameMetricDTO> listGamePeriodMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("periodKey") String periodKey,
|
||||
@Param("statTimezone") String statTimezone,
|
||||
@Param("countryKeyword") String countryKeyword,
|
||||
@Param("countryCodes") String countryCodes,
|
||||
@Param("gameProvider") String gameProvider,
|
||||
@Param("gameKeyword") String gameKeyword,
|
||||
@Param("sysOrigin") String sysOrigin,
|
||||
@Param("sortColumn") String sortColumn,
|
||||
@Param("sortDirection") String sortDirection,
|
||||
@Param("offset") int offset,
|
||||
@Param("limit") int limit);
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
package com.red.circle.console.infra.database.rds.dto.datav;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 国家数据大屏游戏维度聚合结果.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class CountryDashboardGameMetricDTO {
|
||||
|
||||
private String periodType;
|
||||
|
||||
private String periodKey;
|
||||
|
||||
private String statTimezone;
|
||||
|
||||
private String periodName;
|
||||
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
private String sysOrigin;
|
||||
|
||||
private String countryCode;
|
||||
|
||||
private String countryName;
|
||||
|
||||
private String gameProvider;
|
||||
|
||||
private String gameId;
|
||||
|
||||
private String gameName;
|
||||
|
||||
private BigDecimal consumeAmount;
|
||||
|
||||
private BigDecimal payoutAmount;
|
||||
|
||||
private BigDecimal profitAmount;
|
||||
|
||||
private Long userCount;
|
||||
|
||||
private Long orderCount;
|
||||
|
||||
private LocalDateTime refreshedAt;
|
||||
}
|
||||
@ -91,6 +91,61 @@
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏周期用户去重明细'
|
||||
</update>
|
||||
|
||||
<update id="createGamePeriodMetricTable">
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_game_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 '周期结束日期,开区间',
|
||||
`sys_origin` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '系统来源',
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`game_provider` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
|
||||
`game_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
|
||||
`game_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏名称',
|
||||
`consume_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户消耗',
|
||||
`payout_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户返奖',
|
||||
`profit_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '平台收益',
|
||||
`user_count` bigint NOT NULL DEFAULT 0 COMMENT '去重游戏用户数',
|
||||
`order_count` bigint NOT NULL DEFAULT 0 COMMENT '流水笔数',
|
||||
`refreshed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近刷新时间',
|
||||
`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_game_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`, `game_provider`, `game_id`),
|
||||
KEY `idx_game_period_query` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`),
|
||||
KEY `idx_game_period_date` (`sys_origin`, `stat_timezone`, `period_type`, `period_start_date`, `period_end_date`),
|
||||
KEY `idx_game_lookup` (`sys_origin`, `stat_timezone`, `game_provider`, `game_id`, `period_type`, `period_key`),
|
||||
KEY `idx_game_period_sort` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `consume_amount`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期预聚合'
|
||||
</update>
|
||||
|
||||
<update id="createGamePeriodUserMetricTable">
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_game_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 '周期结束日期,开区间',
|
||||
`sys_origin` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '系统来源',
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`game_provider` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
|
||||
`game_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_game_period_user` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `game_provider`, `game_id`, `user_id`),
|
||||
KEY `idx_game_user_count` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `game_provider`, `game_id`, `country_code`),
|
||||
KEY `idx_game_user_lookup` (`sys_origin`, `stat_timezone`, `user_id`, `game_provider`, `game_id`)
|
||||
) 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();
|
||||
@ -519,51 +574,51 @@
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGoldAssetRecordSource">
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_1
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_2
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_3
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_4
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_5
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_6
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_7
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_8
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_9
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_10
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_11
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
UNION ALL
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
SELECT user_id, sys_origin, type, penny_amount, event_type, event_id, FROM_UNIXTIME(create_time / 1000) AS create_time
|
||||
FROM likei_wallet.wallet_gold_asset_record_12
|
||||
<include refid="WalletGoldAssetRecordShardWhere"/>
|
||||
</sql>
|
||||
@ -596,6 +651,7 @@
|
||||
'LOTTERY_REWARD'
|
||||
)
|
||||
OR t.event_type LIKE 'BAISHUN_GAME%'
|
||||
OR t.event_type LIKE 'GAME_OPEN_%'
|
||||
OR t.event_type LIKE 'YOMI_GAME%'
|
||||
OR t.event_type LIKE 'HKYS_GAME%'
|
||||
OR t.event_type LIKE 'HOT_GAME%'
|
||||
@ -605,6 +661,131 @@
|
||||
AND t.event_type NOT LIKE 'ACCEPT_GIFT%'
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGameProviderExpr">
|
||||
CASE
|
||||
WHEN bo.vendor_game_id IS NOT NULL THEN 'BAISHUN'
|
||||
WHEN go.game_id IS NOT NULL THEN 'GAME_OPEN'
|
||||
WHEN t.event_type LIKE 'BAISHUN_GAME%' THEN 'BAISHUN'
|
||||
WHEN t.event_type LIKE 'GAME_OPEN_%' THEN 'GAME_OPEN'
|
||||
WHEN t.event_type LIKE 'YOMI_GAME%' THEN 'YOMI'
|
||||
WHEN t.event_type LIKE 'HKYS_GAME%' THEN 'HKYS'
|
||||
WHEN t.event_type LIKE 'HOT_GAME%' THEN 'HOT'
|
||||
ELSE 'INTERNAL'
|
||||
END
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGameIdExpr">
|
||||
CASE
|
||||
WHEN bo.vendor_game_id IS NOT NULL THEN CAST(bo.vendor_game_id AS CHAR)
|
||||
WHEN go.game_id IS NOT NULL THEN go.game_id
|
||||
WHEN t.event_type LIKE 'BAISHUN_GAME_%' THEN COALESCE(NULLIF(SUBSTRING(t.event_type, 14), ''), t.event_type)
|
||||
WHEN t.event_type LIKE 'GAME_OPEN_%' THEN COALESCE(NULLIF(SUBSTRING(t.event_type, 11), ''), t.event_type)
|
||||
WHEN t.event_type LIKE 'YOMI_GAME_%' THEN COALESCE(NULLIF(SUBSTRING(t.event_type, 11), ''), t.event_type)
|
||||
WHEN t.event_type LIKE 'HKYS_GAME_%' THEN COALESCE(NULLIF(SUBSTRING(t.event_type, 11), ''), t.event_type)
|
||||
WHEN t.event_type LIKE 'HOT_GAME_%' THEN COALESCE(NULLIF(SUBSTRING(t.event_type, 10), ''), t.event_type)
|
||||
WHEN t.event_type IN ('FRUIT_GAME', 'DOUBLE_LAYER_FRUIT', 'GAME_FRUIT_BET', 'GAME_FRUIT_AWARD') THEN 'FRUIT_GAME'
|
||||
WHEN t.event_type IN ('LUDO_GAME', 'LUDO_VICTORY', 'LUDO_REFUND', 'GAME_LUDO_REFUND') THEN 'LUDO_GAME'
|
||||
WHEN t.event_type IN ('GAME_BARBECUE', 'DOUBLE_LAYER_BARBECUE') THEN 'BARBECUE_GAME'
|
||||
WHEN t.event_type IN ('GAME_SLOT_MACHINE') THEN 'SLOT_MACHINE'
|
||||
WHEN t.event_type IN ('TURNTABLE_GAME') THEN 'TURNTABLE_GAME'
|
||||
WHEN t.event_type IN ('GAME_BURST_CRYSTAL') THEN 'BURST_CRYSTAL'
|
||||
WHEN t.event_type IN ('TEEN_PATTI', 'BET_TEEN_PATTI') THEN 'TEEN_PATTI'
|
||||
WHEN t.event_type IN ('LOTTERY_NUMBER_BET', 'LOTTERY_REWARD') THEN 'LOTTERY_NUMBER'
|
||||
ELSE t.event_type
|
||||
END
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGameNameFallbackExpr">
|
||||
CASE
|
||||
WHEN t.event_type IN ('FRUIT_GAME', 'DOUBLE_LAYER_FRUIT', 'GAME_FRUIT_BET', 'GAME_FRUIT_AWARD') THEN 'Fruit Game'
|
||||
WHEN t.event_type IN ('LUDO_GAME', 'LUDO_VICTORY', 'LUDO_REFUND', 'GAME_LUDO_REFUND') THEN 'Ludo Game'
|
||||
WHEN t.event_type IN ('GAME_BARBECUE', 'DOUBLE_LAYER_BARBECUE') THEN 'Barbecue Game'
|
||||
WHEN t.event_type = 'GAME_SLOT_MACHINE' THEN 'Slot Machine'
|
||||
WHEN t.event_type = 'TURNTABLE_GAME' THEN 'Turntable Game'
|
||||
WHEN t.event_type = 'GAME_BURST_CRYSTAL' THEN 'Burst Crystal'
|
||||
WHEN t.event_type IN ('TEEN_PATTI', 'BET_TEEN_PATTI') THEN 'Teen Patti'
|
||||
WHEN t.event_type IN ('LOTTERY_NUMBER_BET', 'LOTTERY_REWARD') THEN 'Lottery Number'
|
||||
ELSE t.event_type
|
||||
END
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGameJoinCatalogs">
|
||||
LEFT JOIN baishun_order_idempotency bo
|
||||
ON bo.sys_origin = t.sys_origin COLLATE utf8mb4_0900_ai_ci
|
||||
AND bo.wallet_event_id = t.event_id COLLATE utf8mb4_0900_ai_ci
|
||||
LEFT JOIN game_open_callback_log go
|
||||
ON go.sys_origin = t.sys_origin COLLATE utf8mb4_0900_ai_ci
|
||||
AND t.event_id COLLATE utf8mb4_0900_ai_ci = CONCAT('GAME_OPEN:', go.order_id)
|
||||
LEFT JOIN baishun_game_catalog bc
|
||||
ON (bo.vendor_game_id IS NOT NULL OR t.event_type LIKE 'BAISHUN_GAME_%')
|
||||
AND bc.sys_origin = t.sys_origin COLLATE utf8mb4_0900_ai_ci
|
||||
AND bc.profile = 'PROD'
|
||||
AND bc.vendor_game_id = CAST(<include refid="WalletGameIdExpr"/> AS UNSIGNED)
|
||||
LEFT JOIN lingxian_game_catalog lc
|
||||
ON (
|
||||
go.game_id IS NOT NULL
|
||||
OR
|
||||
t.event_type LIKE 'GAME_OPEN_%'
|
||||
OR t.event_type LIKE 'YOMI_GAME_%'
|
||||
OR t.event_type LIKE 'HKYS_GAME_%'
|
||||
OR t.event_type LIKE 'HOT_GAME_%'
|
||||
)
|
||||
AND lc.sys_origin = t.sys_origin COLLATE utf8mb4_0900_ai_ci
|
||||
AND lc.profile = 'PROD'
|
||||
AND lc.vendor_game_id = <include refid="WalletGameIdExpr"/> COLLATE utf8mb4_0900_ai_ci
|
||||
LEFT JOIN sys_game_list_config sg
|
||||
ON sg.sys_origin = t.sys_origin COLLATE utf8mb4_0900_ai_ci
|
||||
AND sg.game_id = <include refid="WalletGameIdExpr"/> COLLATE utf8mb4_0900_ai_ci
|
||||
</sql>
|
||||
|
||||
<sql id="WalletGameNameExpr">
|
||||
COALESCE(
|
||||
NULLIF(bc.name, ''),
|
||||
NULLIF(lc.name, ''),
|
||||
NULLIF(sg.name, ''),
|
||||
<include refid="WalletGameNameFallbackExpr"/>
|
||||
)
|
||||
</sql>
|
||||
|
||||
<sql id="GameMetricFilterWhere">
|
||||
WHERE t.period_type = #{periodType}
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
<choose>
|
||||
<when test="periodType == 'ALL'">
|
||||
AND t.period_key = 'ALL'
|
||||
</when>
|
||||
<otherwise>
|
||||
<if test="startTime != null">
|
||||
AND t.period_start_date >= DATE(#{startTime})
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND t.period_start_date < DATE(#{endTime})
|
||||
</if>
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="countryKeyword != null and countryKeyword != ''">
|
||||
AND (
|
||||
t.country_code = #{countryKeyword}
|
||||
OR t.country_name LIKE CONCAT('%', #{countryKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="countryCodes != null and countryCodes != ''">
|
||||
AND FIND_IN_SET(t.country_code, #{countryCodes})
|
||||
</if>
|
||||
<if test="gameProvider != null and gameProvider != ''">
|
||||
AND t.game_provider = #{gameProvider}
|
||||
</if>
|
||||
<if test="gameKeyword != null and gameKeyword != ''">
|
||||
AND (
|
||||
t.game_id = #{gameKeyword}
|
||||
OR t.game_name LIKE CONCAT('%', #{gameKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<select id="listPrecomputedMetrics"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO">
|
||||
SELECT
|
||||
@ -1345,6 +1526,326 @@
|
||||
GROUP BY countryCode, countryName, periodKey, periodName
|
||||
</select>
|
||||
|
||||
<select id="listWalletGameMetricAmounts"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
|
||||
SELECT
|
||||
<include refid="CountryColumns"/>,
|
||||
<include refid="PeriodColumnsT"/>,
|
||||
<include refid="WalletGameProviderExpr"/> AS gameProvider,
|
||||
<include refid="WalletGameIdExpr"/> AS gameId,
|
||||
MAX(<include refid="WalletGameNameExpr"/>) AS gameName,
|
||||
IFNULL(SUM(CASE WHEN t.type = 1 THEN t.penny_amount ELSE 0 END) / 100.00, 0) AS consumeAmount,
|
||||
IFNULL(SUM(CASE WHEN t.type = 0 THEN t.penny_amount ELSE 0 END) / 100.00, 0) AS payoutAmount,
|
||||
IFNULL((SUM(CASE WHEN t.type = 1 THEN t.penny_amount ELSE 0 END)
|
||||
- SUM(CASE WHEN t.type = 0 THEN t.penny_amount ELSE 0 END)) / 100.00, 0) AS profitAmount,
|
||||
COUNT(1) AS orderCount
|
||||
FROM (
|
||||
<include refid="WalletGoldAssetRecordSource"/>
|
||||
) t
|
||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||
<include refid="WalletGameJoinCatalogs"/>
|
||||
WHERE <include refid="WalletGameEventWhere"/>
|
||||
<include refid="UserWhere"/>
|
||||
<include refid="SourceTimeWhere"/>
|
||||
<include refid="SourceSysOriginWhere"/>
|
||||
GROUP BY countryCode, countryName, periodKey, periodName, gameProvider, gameId
|
||||
</select>
|
||||
|
||||
<select id="listLuckyBoxGameMetricAmounts"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
|
||||
SELECT
|
||||
<include refid="CountryColumns"/>,
|
||||
<include refid="PeriodColumnsT"/>,
|
||||
'INTERNAL' AS gameProvider,
|
||||
'LUCKY_BOX' AS gameId,
|
||||
'Lucky Box' AS gameName,
|
||||
IFNULL(SUM(t.pay_amount), 0) AS consumeAmount,
|
||||
IFNULL(SUM(t.gift_amount), 0) AS payoutAmount,
|
||||
IFNULL(SUM(t.pay_amount), 0) - IFNULL(SUM(t.gift_amount), 0) AS profitAmount,
|
||||
COUNT(1) AS orderCount
|
||||
FROM game_lucky_box_count t
|
||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||
WHERE 1 = 1
|
||||
<include refid="UserWhere"/>
|
||||
<include refid="SourceTimeWhere"/>
|
||||
<include refid="SourceSysOriginWhere"/>
|
||||
GROUP BY countryCode, countryName, periodKey, periodName, gameProvider, gameId, gameName
|
||||
</select>
|
||||
|
||||
<delete id="deleteGamePeriodUserMetrics">
|
||||
DELETE FROM country_dashboard_game_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>
|
||||
</delete>
|
||||
|
||||
<insert id="insertWalletGamePeriodUserMetrics">
|
||||
INSERT IGNORE INTO country_dashboard_game_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
sys_origin,
|
||||
country_code,
|
||||
country_name,
|
||||
game_provider,
|
||||
game_id,
|
||||
user_id
|
||||
)
|
||||
SELECT DISTINCT
|
||||
#{periodType},
|
||||
#{periodKey},
|
||||
#{statTimezone},
|
||||
#{periodName},
|
||||
#{periodStartDate},
|
||||
#{periodEndDate},
|
||||
#{sysOrigin},
|
||||
IFNULL(NULLIF(u.country_code, ''), 'UNKNOWN') AS countryCode,
|
||||
IFNULL(NULLIF(u.country_name, ''), IFNULL(NULLIF(u.country_code, ''), 'UNKNOWN')) AS countryName,
|
||||
<include refid="WalletGameProviderExpr"/> AS gameProvider,
|
||||
<include refid="WalletGameIdExpr"/> AS gameId,
|
||||
t.user_id AS userId
|
||||
FROM (
|
||||
<include refid="WalletGoldAssetRecordSource"/>
|
||||
) t
|
||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||
<include refid="WalletGameJoinCatalogs"/>
|
||||
WHERE <include refid="WalletGameEventWhere"/>
|
||||
AND (u.is_del = 0 OR u.is_del IS NULL)
|
||||
<include refid="SourceTimeWhere"/>
|
||||
<include refid="SourceSysOriginWhere"/>
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND u.origin_sys = #{sysOrigin}
|
||||
</if>
|
||||
</insert>
|
||||
|
||||
<insert id="insertLuckyBoxGamePeriodUserMetrics">
|
||||
INSERT IGNORE INTO country_dashboard_game_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
sys_origin,
|
||||
country_code,
|
||||
country_name,
|
||||
game_provider,
|
||||
game_id,
|
||||
user_id
|
||||
)
|
||||
SELECT DISTINCT
|
||||
#{periodType},
|
||||
#{periodKey},
|
||||
#{statTimezone},
|
||||
#{periodName},
|
||||
#{periodStartDate},
|
||||
#{periodEndDate},
|
||||
#{sysOrigin},
|
||||
IFNULL(NULLIF(u.country_code, ''), 'UNKNOWN') AS countryCode,
|
||||
IFNULL(NULLIF(u.country_name, ''), IFNULL(NULLIF(u.country_code, ''), 'UNKNOWN')) AS countryName,
|
||||
'INTERNAL' AS gameProvider,
|
||||
'LUCKY_BOX' AS gameId,
|
||||
t.user_id AS userId
|
||||
FROM game_lucky_box_count t
|
||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||
WHERE 1 = 1
|
||||
AND (u.is_del = 0 OR u.is_del IS NULL)
|
||||
<include refid="SourceTimeWhere"/>
|
||||
<include refid="SourceSysOriginWhere"/>
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND u.origin_sys = #{sysOrigin}
|
||||
</if>
|
||||
</insert>
|
||||
|
||||
<insert id="insertAllGamePeriodUserMetricsFromDaily">
|
||||
INSERT IGNORE INTO country_dashboard_game_period_user_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
sys_origin,
|
||||
country_code,
|
||||
country_name,
|
||||
game_provider,
|
||||
game_id,
|
||||
user_id
|
||||
)
|
||||
SELECT DISTINCT
|
||||
'ALL',
|
||||
'ALL',
|
||||
#{statTimezone},
|
||||
'全部',
|
||||
NULL,
|
||||
NULL,
|
||||
t.sys_origin,
|
||||
t.country_code,
|
||||
t.country_name,
|
||||
t.game_provider,
|
||||
t.game_id,
|
||||
t.user_id
|
||||
FROM country_dashboard_game_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>
|
||||
</insert>
|
||||
|
||||
<select id="listGamePeriodUserMetricCounts"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
|
||||
SELECT
|
||||
t.country_code AS countryCode,
|
||||
MAX(t.country_name) AS countryName,
|
||||
t.period_key AS periodKey,
|
||||
MAX(t.period_name) AS periodName,
|
||||
t.game_provider AS gameProvider,
|
||||
t.game_id AS gameId,
|
||||
COUNT(1) AS userCount
|
||||
FROM country_dashboard_game_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, gameProvider, gameId
|
||||
</select>
|
||||
|
||||
<select id="listAllGamePeriodAmountsFromPeriodDays"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
|
||||
SELECT
|
||||
t.country_code AS countryCode,
|
||||
MAX(t.country_name) AS countryName,
|
||||
'ALL' AS periodKey,
|
||||
'全部' AS periodName,
|
||||
t.game_provider AS gameProvider,
|
||||
t.game_id AS gameId,
|
||||
MAX(t.game_name) AS gameName,
|
||||
IFNULL(SUM(t.consume_amount), 0) AS consumeAmount,
|
||||
IFNULL(SUM(t.payout_amount), 0) AS payoutAmount,
|
||||
IFNULL(SUM(t.profit_amount), 0) AS profitAmount,
|
||||
IFNULL(SUM(t.order_count), 0) AS orderCount
|
||||
FROM country_dashboard_game_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>
|
||||
GROUP BY countryCode, gameProvider, gameId
|
||||
</select>
|
||||
|
||||
<delete id="deleteGamePeriodMetrics">
|
||||
DELETE FROM country_dashboard_game_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>
|
||||
</delete>
|
||||
|
||||
<insert id="upsertGamePeriodMetrics">
|
||||
INSERT INTO country_dashboard_game_period_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
sys_origin,
|
||||
country_code,
|
||||
country_name,
|
||||
game_provider,
|
||||
game_id,
|
||||
game_name,
|
||||
consume_amount,
|
||||
payout_amount,
|
||||
profit_amount,
|
||||
user_count,
|
||||
order_count,
|
||||
refreshed_at
|
||||
)
|
||||
VALUES
|
||||
<foreach collection="metrics" item="metric" separator=",">
|
||||
(
|
||||
#{metric.periodType},
|
||||
#{metric.periodKey},
|
||||
#{metric.statTimezone},
|
||||
#{metric.periodName},
|
||||
#{metric.periodStartDate},
|
||||
#{metric.periodEndDate},
|
||||
#{metric.sysOrigin},
|
||||
#{metric.countryCode},
|
||||
#{metric.countryName},
|
||||
#{metric.gameProvider},
|
||||
#{metric.gameId},
|
||||
#{metric.gameName},
|
||||
#{metric.consumeAmount},
|
||||
#{metric.payoutAmount},
|
||||
#{metric.profitAmount},
|
||||
#{metric.userCount},
|
||||
#{metric.orderCount},
|
||||
NOW()
|
||||
)
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
period_name = VALUES(period_name),
|
||||
period_start_date = VALUES(period_start_date),
|
||||
period_end_date = VALUES(period_end_date),
|
||||
country_name = VALUES(country_name),
|
||||
game_name = VALUES(game_name),
|
||||
consume_amount = VALUES(consume_amount),
|
||||
payout_amount = VALUES(payout_amount),
|
||||
profit_amount = VALUES(profit_amount),
|
||||
user_count = VALUES(user_count),
|
||||
order_count = VALUES(order_count),
|
||||
refreshed_at = VALUES(refreshed_at)
|
||||
</insert>
|
||||
|
||||
<select id="countGamePeriodMetrics" resultType="java.lang.Long">
|
||||
SELECT COUNT(1)
|
||||
FROM country_dashboard_game_period_metric t
|
||||
<include refid="GameMetricFilterWhere"/>
|
||||
</select>
|
||||
|
||||
<select id="listGamePeriodMetrics"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
|
||||
SELECT
|
||||
t.period_key AS periodKey,
|
||||
t.period_name AS periodName,
|
||||
t.stat_timezone AS statTimezone,
|
||||
t.country_code AS countryCode,
|
||||
t.country_name AS countryName,
|
||||
t.game_provider AS gameProvider,
|
||||
t.game_id AS gameId,
|
||||
t.game_name AS gameName,
|
||||
t.consume_amount AS consumeAmount,
|
||||
t.payout_amount AS payoutAmount,
|
||||
t.profit_amount AS profitAmount,
|
||||
t.user_count AS userCount,
|
||||
t.order_count AS orderCount,
|
||||
t.refreshed_at AS refreshedAt
|
||||
FROM country_dashboard_game_period_metric t
|
||||
<include refid="GameMetricFilterWhere"/>
|
||||
<choose>
|
||||
<when test="sortColumn != null and sortColumn != ''">
|
||||
ORDER BY ${sortColumn} ${sortDirection}, t.period_key DESC, t.country_code ASC, t.game_provider ASC, t.game_id ASC
|
||||
</when>
|
||||
<otherwise>
|
||||
ORDER BY t.period_key DESC, t.consume_amount DESC, t.country_code ASC, t.game_provider ASC, t.game_id ASC
|
||||
</otherwise>
|
||||
</choose>
|
||||
LIMIT #{limit} OFFSET #{offset}
|
||||
</select>
|
||||
|
||||
<select id="listNewUserCohorts"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO">
|
||||
SELECT
|
||||
|
||||
@ -74,4 +74,5 @@ red-circle:
|
||||
- /console/datav/online/room/count
|
||||
- /console/datav/country-dashboard
|
||||
- /console/datav/country-dashboard/recharge-details
|
||||
- /console/datav/country-dashboard/game-metrics
|
||||
- /console/user/base/info/im/sig
|
||||
|
||||
52
sql/20260513_country_dashboard_game_period_metric.sql
Normal file
52
sql/20260513_country_dashboard_game_period_metric.sql
Normal file
@ -0,0 +1,52 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_game_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 '周期结束日期,开区间',
|
||||
`sys_origin` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '系统来源',
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`game_provider` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
|
||||
`game_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
|
||||
`game_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏名称',
|
||||
`consume_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户消耗',
|
||||
`payout_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户返奖',
|
||||
`profit_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '平台收益',
|
||||
`user_count` bigint NOT NULL DEFAULT 0 COMMENT '去重游戏用户数',
|
||||
`order_count` bigint NOT NULL DEFAULT 0 COMMENT '流水笔数',
|
||||
`refreshed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近刷新时间',
|
||||
`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_game_period` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`, `game_provider`, `game_id`),
|
||||
KEY `idx_game_period_query` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`),
|
||||
KEY `idx_game_period_date` (`sys_origin`, `stat_timezone`, `period_type`, `period_start_date`, `period_end_date`),
|
||||
KEY `idx_game_lookup` (`sys_origin`, `stat_timezone`, `game_provider`, `game_id`, `period_type`, `period_key`),
|
||||
KEY `idx_game_period_sort` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `consume_amount`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期预聚合';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_game_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 '周期结束日期,开区间',
|
||||
`sys_origin` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '系统来源',
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`game_provider` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
|
||||
`game_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_game_period_user` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `game_provider`, `game_id`, `user_id`),
|
||||
KEY `idx_game_user_count` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `game_provider`, `game_id`, `country_code`),
|
||||
KEY `idx_game_user_lookup` (`sys_origin`, `stat_timezone`, `user_id`, `game_provider`, `game_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期用户去重明细';
|
||||
Loading…
x
Reference in New Issue
Block a user