feat(console): add game summary country ranking

This commit is contained in:
hy001 2026-05-13 17:43:42 +08:00
parent b1e68f00e8
commit 83b61e94dd
7 changed files with 224 additions and 4 deletions

View File

@ -73,6 +73,18 @@ public class DatavRestController extends BaseController {
return countryDashboardGameMetricService.pageGameMetrics(cmd);
}
@GetMapping("/country-dashboard/game-metrics/games")
public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetricGames(
CountryDashboardGameMetricQryCmd cmd) {
return countryDashboardGameMetricService.pageGameSummaries(cmd);
}
@GetMapping("/country-dashboard/game-metrics/country-rank")
public List<CountryDashboardGameMetricCO> countryDashboardGameMetricCountryRanks(
CountryDashboardGameMetricQryCmd cmd) {
return countryDashboardGameMetricService.listGameCountryRanks(cmd);
}
@PostMapping("/country-dashboard/backfill")
public Map<String, Object> backfillCountryDashboard(
@RequestParam String startDate,

View File

@ -83,10 +83,7 @@ public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGa
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 gameProvider = normalizeGameProvider(cmd == null ? null : cmd.getGameProvider());
String gameKeyword = trimToNull(cmd == null ? null : cmd.getGameKeyword());
Long total = countryDashboardDAO.countGamePeriodMetrics(
@ -108,6 +105,76 @@ public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGa
return result;
}
@Override
public PageResult<CountryDashboardGameMetricCO> pageGameSummaries(
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 = normalizeSummarySort(cmd == null ? null : cmd.getSortField(),
cmd == null ? null : cmd.getSortOrder());
String countryCodes = normalizeCsv(cmd == null ? null : cmd.getCountryCodes());
String gameProvider = normalizeGameProvider(cmd == null ? null : cmd.getGameProvider());
String gameKeyword = trimToNull(cmd == null ? null : cmd.getGameKeyword());
Long total = countryDashboardDAO.countGameSummaryMetrics(
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.listGameSummaryMetrics(
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 List<CountryDashboardGameMetricCO> listGameCountryRanks(
CountryDashboardGameMetricQryCmd cmd) {
ensureMetricSchema();
QueryCondition condition = QueryCondition.of(cmd);
if (!hasText(condition.sysOrigin)) {
return List.of();
}
String gameProvider = normalizeGameProvider(cmd == null ? null : cmd.getGameProvider());
String gameId = trimToNull(cmd == null ? null : cmd.getGameId());
if (gameProvider == null || gameId == null) {
return List.of();
}
int limit = Math.max(1, cmd.getRankLimit() == null ? 100 : cmd.getRankLimit());
limit = Math.min(500, limit);
String countryCodes = normalizeCsv(cmd.getCountryCodes());
return countryDashboardDAO.listGameCountryRankMetrics(
condition.periodType, condition.startTime, condition.endTime,
PERIOD_ALL.equals(condition.periodType) ? PERIOD_ALL : null,
condition.statTimezone, condition.countryKeyword, countryCodes, gameProvider, null,
gameId, condition.sysOrigin, limit)
.stream()
.map(this::toCO)
.toList();
}
@Override
public void refreshRecentDays(int days) {
ensureMetricSchema();
@ -457,6 +524,24 @@ public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGa
return new SortSpec(column, direction);
}
private static SortSpec normalizeSummarySort(String sortField, String sortOrder) {
String column = switch (Objects.toString(sortField, "")) {
case "payoutAmount" -> "payoutAmount";
case "profitAmount" -> "profitAmount";
case "userCount" -> "userCount";
case "orderCount" -> "orderCount";
case "consumeAmount" -> "consumeAmount";
default -> null;
};
String direction = "ascend".equalsIgnoreCase(sortOrder) ? "ASC" : "DESC";
return new SortSpec(column, direction);
}
private static String normalizeGameProvider(String gameProvider) {
String value = trimToNull(gameProvider);
return value == null ? null : value.toUpperCase(Locale.ROOT);
}
private static StatTimezone normalizeStatTimezone(String statTimezone) {
String value = trimToNull(statTimezone);
if (value == null) {

View File

@ -36,11 +36,21 @@ public class CountryDashboardGameMetricQryCmd extends CountryDashboardQryCmd {
*/
private String gameProvider;
/**
* 游戏 ID.
*/
private String gameId;
/**
* 游戏 ID 或名称关键字.
*/
private String gameKeyword;
/**
* 国家排名返回条数.
*/
private Integer rankLimit;
/**
* 排序字段.
*/

View File

@ -13,6 +13,10 @@ public interface CountryDashboardGameMetricService {
PageResult<CountryDashboardGameMetricCO> pageGameMetrics(CountryDashboardGameMetricQryCmd cmd);
PageResult<CountryDashboardGameMetricCO> pageGameSummaries(CountryDashboardGameMetricQryCmd cmd);
List<CountryDashboardGameMetricCO> listGameCountryRanks(CountryDashboardGameMetricQryCmd cmd);
void refreshRecentDays(int days);
List<String> refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin,

View File

@ -364,4 +364,46 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
@Param("sortDirection") String sortDirection,
@Param("offset") int offset,
@Param("limit") int limit);
Long countGameSummaryMetrics(
@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> listGameSummaryMetrics(
@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);
List<CountryDashboardGameMetricDTO> listGameCountryRankMetrics(
@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("gameId") String gameId,
@Param("sysOrigin") String sysOrigin,
@Param("limit") int limit);
}

View File

@ -1846,6 +1846,71 @@
LIMIT #{limit} OFFSET #{offset}
</select>
<select id="countGameSummaryMetrics" resultType="java.lang.Long">
SELECT COUNT(1)
FROM (
SELECT 1
FROM country_dashboard_game_period_metric t
<include refid="GameMetricFilterWhere"/>
GROUP BY t.game_provider, t.game_id
) summary
</select>
<select id="listGameSummaryMetrics"
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
SELECT
MAX(t.period_key) AS periodKey,
MAX(t.period_name) AS periodName,
MAX(t.stat_timezone) AS statTimezone,
MAX(t.game_provider) AS gameProvider,
t.game_id AS gameId,
MAX(t.game_name) AS gameName,
COALESCE(SUM(t.consume_amount), 0) AS consumeAmount,
COALESCE(SUM(t.payout_amount), 0) AS payoutAmount,
COALESCE(SUM(t.profit_amount), 0) AS profitAmount,
COALESCE(SUM(t.user_count), 0) AS userCount,
COALESCE(SUM(t.order_count), 0) AS orderCount,
MAX(t.refreshed_at) AS refreshedAt
FROM country_dashboard_game_period_metric t
<include refid="GameMetricFilterWhere"/>
GROUP BY t.game_provider, t.game_id
<choose>
<when test="sortColumn != null and sortColumn != ''">
ORDER BY ${sortColumn} ${sortDirection}, gameProvider ASC, gameId ASC
</when>
<otherwise>
ORDER BY consumeAmount DESC, gameProvider ASC, gameId ASC
</otherwise>
</choose>
LIMIT #{limit} OFFSET #{offset}
</select>
<select id="listGameCountryRankMetrics"
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardGameMetricDTO">
SELECT
MAX(t.period_key) AS periodKey,
MAX(t.period_name) AS periodName,
MAX(t.stat_timezone) AS statTimezone,
t.country_code AS countryCode,
MAX(t.country_name) AS countryName,
MAX(t.game_provider) AS gameProvider,
MAX(t.game_id) AS gameId,
MAX(t.game_name) AS gameName,
COALESCE(SUM(t.consume_amount), 0) AS consumeAmount,
COALESCE(SUM(t.payout_amount), 0) AS payoutAmount,
COALESCE(SUM(t.profit_amount), 0) AS profitAmount,
COALESCE(SUM(t.user_count), 0) AS userCount,
COALESCE(SUM(t.order_count), 0) AS orderCount,
MAX(t.refreshed_at) AS refreshedAt
FROM country_dashboard_game_period_metric t
<include refid="GameMetricFilterWhere"/>
AND t.game_provider = #{gameProvider}
AND t.game_id = #{gameId}
GROUP BY t.country_code
ORDER BY consumeAmount DESC, countryCode ASC
LIMIT #{limit}
</select>
<select id="listNewUserCohorts"
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO">
SELECT

View File

@ -75,4 +75,6 @@ red-circle:
- /console/datav/country-dashboard
- /console/datav/country-dashboard/recharge-details
- /console/datav/country-dashboard/game-metrics
- /console/datav/country-dashboard/game-metrics/games
- /console/datav/country-dashboard/game-metrics/country-rank
- /console/user/base/info/im/sig