13 KiB
Admin 数据大屏游戏数据表技术方案
目标
在新 admin 的 统计管理 / 数据大屏 中增加独立的“游戏数据”表,用于按日期、国家、游戏维度查看消耗和收益数据。
页面查询不能直接扫描 likei_wallet.wallet_gold_asset_record_1..12、第三方回调日志或游戏明细表。所有 admin 查询默认读取游戏维度预聚合表;只有后台刷新任务和手动回填任务允许按时间窗口扫描原始数据。
现状
当前国家数据大屏入口:
- 前端路由:
chatapp3-admin/apps/src/router/routes/modules/statistics.ts - 前端页面:
chatapp3-admin/apps/src/views/statistics/datav.vue - 前端接口:
chatapp3-admin/apps/src/api/legacy/datav.ts - 后端 Controller:
DatavRestController - 后端 Service:
CountryDashboardServiceImpl - 后端 Mapper:
CountryDashboardDAO.xml - 现有查询文档:
chatapp3-java/rc-service/rc-service-console/docs/country-dashboard-data-query.md
现有大屏已经聚合了国家维度的 game_total_flow、game_payout、game_user,但没有保留游戏维度。新增功能不改动现有总览口径,新增一套游戏维度查询和预聚合表。
页面设计
在 datav.vue 的表格区域增加第三个 tab:
指标总览充值相关数据游戏数据
“游戏数据”表复用页面已有筛选项:
- 系统:
sysOrigin - 统计时区:
statTimezone - 周期:
periodType - 日期范围:
startDate/endDate - 国家多选:
countryCodes
新增游戏筛选项:
gameProvider:游戏厂商,支持INTERNAL、BAISHUN、GAME_OPEN、LINGXIAN,可为空。gameKeyword:游戏 ID 或游戏名称模糊搜索。
表格字段:
| 字段 | 说明 |
|---|---|
| 日期/周期 | periodKey,DAY 显示日期,WEEK/MONTH/ALL 显示周期 |
| 国家 | countryCode / countryName |
| 游戏厂商 | gameProvider |
| 游戏 ID | gameId |
| 游戏名称 | gameName |
| 消耗 | 用户玩游戏支出,现有钱包口径为 type = 1 |
| 返奖 | 游戏返还给用户,现有钱包口径为 type = 0 |
| 收益 | consumeAmount - payoutAmount |
| 返奖率 | payoutAmount / consumeAmount |
| 收益率 | profitAmount / consumeAmount |
| 游戏用户数 | 周期内去重用户数 |
| 流水笔数 | 周期内消耗和返奖记录数 |
| 更新时间 | 预聚合刷新时间 |
表格必须服务端分页,默认 limit=50,最大 limit=200。
后端接口
新增接口:
GET /console/datav/country-dashboard/game-metrics
请求参数:
| 参数 | 必填 | 说明 |
|---|---|---|
sysOrigin |
是 | 系统来源 |
periodType |
是 | DAY / WEEK / MONTH / ALL |
startDate |
DAY/WEEK/MONTH 必填 | 查询开始日期 |
endDate |
DAY/WEEK/MONTH 必填 | 查询结束日期 |
statTimezone |
否 | 默认 Asia/Riyadh |
countryCodes |
否 | 多国家码,逗号分隔 |
countryKeyword |
否 | 国家码或国家名搜索 |
gameProvider |
否 | 游戏厂商 |
gameKeyword |
否 | 游戏 ID 或名称搜索 |
cursor |
否 | 页码,默认 1 |
limit |
否 | 页大小,默认 50,最大 200 |
sortField |
否 | 允许 consumeAmount/payoutAmount/profitAmount/userCount/orderCount |
sortOrder |
否 | ascend/descend |
响应结构:
{
"current": 1,
"size": 50,
"total": 120,
"records": [
{
"periodKey": "2026-05-12",
"periodName": "2026-05-12",
"statTimezone": "Asia/Riyadh",
"countryCode": "SA",
"countryName": "Saudi Arabia",
"gameProvider": "BAISHUN",
"gameId": "1067",
"gameName": "example game",
"consumeAmount": "1000.00",
"payoutAmount": "720.00",
"profitAmount": "280.00",
"payoutRate": "72.00%",
"profitRate": "28.00%",
"userCount": 86,
"orderCount": 320,
"refreshedAt": "2026-05-12 10:20:00"
}
]
}
后端新增对象建议:
CountryDashboardGameMetricQryCmdCountryDashboardGameMetricCOCountryDashboardGameMetricDTOCountryDashboardGameMetricServiceCountryDashboardGameMetricDAOCountryDashboardGameMetricDAO.xml
预聚合表
country_dashboard_game_period_metric
页面直接查询这张表。
CREATE TABLE IF NOT EXISTS `country_dashboard_game_period_metric` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`period_type` varchar(16) NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
`period_key` varchar(32) NOT NULL COMMENT '周期键',
`stat_timezone` varchar(64) NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
`period_name` varchar(64) NOT NULL DEFAULT '' COMMENT '周期名称',
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
`sys_origin` varchar(32) NOT NULL DEFAULT '' COMMENT '系统来源',
`country_code` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
`country_name` varchar(128) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
`game_provider` varchar(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
`game_id` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
`game_name` varchar(128) 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` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `game_provider`, `game_id`),
KEY `idx_game_period_query` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`),
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='国家数据大屏游戏维度周期预聚合';
country_dashboard_game_period_user_metric
用于 WEEK/MONTH/ALL 去重用户数,避免直接把日用户数相加导致重复。
CREATE TABLE IF NOT EXISTS `country_dashboard_game_period_user_metric` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`period_type` varchar(16) NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
`period_key` varchar(32) NOT NULL COMMENT '周期键',
`stat_timezone` varchar(64) NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
`period_name` varchar(64) NOT NULL DEFAULT '' COMMENT '周期名称',
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
`sys_origin` varchar(32) NOT NULL DEFAULT '' COMMENT '系统来源',
`country_code` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
`country_name` varchar(128) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
`game_provider` varchar(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
`game_id` varchar(64) 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='国家数据大屏游戏维度周期用户去重明细';
数据来源和游戏识别
以钱包流水作为财务主口径:
wallet_gold_asset_record_1..12.type = 1:消耗wallet_gold_asset_record_1..12.type = 0:返奖- 金额沿用现有大屏口径:
penny_amount / 100.00 LUCKY_GIFT、LUCKY_GIFT_GOLD_REWARD继续归属幸运礼物模块,不并入游戏数据表。
游戏维度来源:
| 来源 | 识别方式 | 名称补全 |
|---|---|---|
| 百顺 | 钱包 event_id = baishun_order_idempotency.wallet_event_id,取 vendor_game_id;兜底解析 event_type = BAISHUN_GAME_{gameId} |
baishun_game_catalog.name |
| 统一三方游戏 | 钱包 event_id = CONCAT('GAME_OPEN:', game_open_callback_log.order_id),取 game_open_callback_log.game_id;兜底解析 event_type = GAME_OPEN_{gameId} |
lingxian_game_catalog.name 或 sys_game_list_config.name |
| Lucky Box | game_lucky_box_count 固定为 gameProvider=INTERNAL, gameId=LUCKY_BOX |
固定名称 Lucky Box |
| 其他站内游戏 | 按 event_type 映射稳定 gameId,例如 FRUIT_GAME/GAME_FRUIT_* -> FRUIT_GAME、LUDO_* -> LUDO_GAME |
代码内映射或后续配置表 |
如果某条流水无法识别具体游戏,落到:
gameProvider = UNKNOWNgameId = event_typegameName = event_type
这样可以保证金额不丢失,同时便于后续补映射。
刷新和回填
新增刷新服务建议为 CountryDashboardGameMetricService。
默认任务:
- 每 10 分钟刷新最近 1 天。
- 每天 00:30 刷新最近 8 天。
- 支持手动回填,单次最大 366 天,和现有国家大屏保持一致。
刷新逻辑:
- 按
sysOrigin和statTimezone计算周期窗口,转成存储时区时间窗口。 - 扫描窗口内原始游戏流水,按
period + country + gameProvider + gameId聚合金额和流水笔数。 - 删除对应周期的
country_dashboard_game_period_user_metric,写入去重用户明细。 - 从用户明细聚合
user_count。 - 删除并写入对应周期的
country_dashboard_game_period_metric。 ALL周期的金额从DAY周期表汇总,用户数从日级用户明细去重汇总。
刷新任务失败只影响游戏数据表,不影响现有国家大屏总览。
查询性能要求
页面查询只能访问 country_dashboard_game_period_metric,禁止在请求链路访问以下原始表:
likei_wallet.wallet_gold_asset_record_1..12game_open_callback_logbaishun_order_idempotencygame_lucky_box_count- 第三方游戏 catalog 表
分页查询必须满足:
sysOrigin必传。periodType、statTimezone必须落到联合索引前缀。DAY/WEEK/MONTH查询必须带日期范围,并转成period_key或period_start_date范围。ALL只查period_type='ALL' AND period_key='ALL',不回退全历史实时扫描。countryKeyword和gameKeyword只允许在已经命中周期范围之后过滤;大范围模糊搜索需要限制日期范围。- 只允许白名单排序字段,避免动态 SQL 注入和无法使用索引的任意排序。
limit上限 200。
刷新任务扫描原始钱包分表时必须带:
sys_origincreate_time毫秒闭开区间- 游戏
event_type白名单或前缀条件
现有 wallet 分表有 idx_sorigin_etype_ctime(sys_origin,event_type,create_time) 和 idx_event(event_id),刷新 SQL 应优先利用这两个索引。不能对 create_time 列包函数做过滤,只能在 SELECT 或 GROUP BY 阶段转换时间。
实施步骤
- 新增 SQL 迁移:创建
country_dashboard_game_period_metric和country_dashboard_game_period_user_metric。 - 新增后端 DTO/Cmd/CO/DAO/Service,并在
DatavRestController暴露/country-dashboard/game-metrics。 - 新增刷新任务和手动回填入口,复用现有
red-circle.country-dashboard.refresh-sys-origins、refresh-timezones配置。 - 新 admin 新增接口方法
countryDashboardGameMetrics。 datav.vue增加“游戏数据”tab、游戏筛选项和分页表格。- 首次上线后先回填最近 8 天,再回填历史需要展示的范围。
验证方案
SQL 验证:
EXPLAIN
SELECT *
FROM country_dashboard_game_period_metric
WHERE sys_origin = 'LIKEI'
AND stat_timezone = 'Asia/Riyadh'
AND period_type = 'DAY'
AND period_key BETWEEN '2026-05-01' AND '2026-05-12'
ORDER BY consume_amount DESC
LIMIT 50;
预期使用 idx_game_period_query 或 idx_game_period_sort,不能全表扫描。
数据口径验证:
SELECT SUM(consume_amount), SUM(payout_amount)
FROM country_dashboard_game_period_metric
WHERE sys_origin = 'LIKEI'
AND stat_timezone = 'Asia/Riyadh'
AND period_type = 'DAY'
AND period_key = '2026-05-12';
对比现有 country_dashboard_period_metric 同日同国家汇总的 game_total_flow/game_payout,差异应只来自“无法识别但仍落 UNKNOWN”的映射归类,不应出现金额丢失。
功能验证:
- 查询 DAY 日期范围,表格按日期、国家、游戏拆分。
- 查询 WEEK/MONTH,用户数为周期内去重,不是日用户数累加。
- 查询 ALL,不触发实时原始流水扫描。
- 国家筛选、游戏厂商筛选、游戏关键字筛选、分页、排序都能返回稳定结果。
- 刷新任务失败时,现有数据大屏总览接口不受影响。