# 国家数据大屏数据查询技术文档

## 目标

国家数据大屏接口 `/console/datav/country-dashboard` 原来在页面请求时实时扫描订单、用户、游戏流水和 wallet 分表。线上慢查询主要集中在 `wallet_gold_asset_record_1..12` 的游戏流水、游戏人数统计。当前改为 SQL 指标预聚合，页面优先查询汇总表；汇总缺失或请求范围不符合预聚合口径时，才回退原实时 SQL。

## 查询入口

- Controller: `DatavRestController#countryDashboard`
- Service: `CountryDashboardServiceImpl#query`
- DAO: `CountryDashboardDAO`
- Mapper: `CountryDashboardDAO.xml`

查询参数：

- `periodType`: `DAY` / `WEEK` / `MONTH` / `ALL`
- `startDate`, `endDate`: 闭区间日期，后端转为 `[startDate 00:00:00, endDate + 1 day 00:00:00)`
- `statTimezone`: 统计时区，默认 `Asia/Riyadh`，支持 `Asia/Riyadh` / `UTC` / `Asia/Shanghai`
- `countryKeyword`: 国家码或国家名模糊匹配
- `sysOrigin`: 系统来源，预聚合查询必须传

## 预聚合表

### `country_dashboard_daily_metric`

日级 SQL 指标表，按 `stat_date + sys_origin + country_code` 唯一。

用途：

- 保留日级汇总结果。
- 作为 `ALL` 周期金额汇总的数据来源。
- 作为历史回填和排查的基础日表。

### `country_dashboard_period_metric`

周期汇总表，按 `period_type + period_key + stat_timezone + sys_origin + country_code` 唯一。

周期键示例：

- `DAY`: `2026-05-07`
- `WEEK`: `2026-W19`
- `MONTH`: `2026-05`
- `ALL`: `ALL`

页面查询优先读取这张表。

### `country_dashboard_period_user_metric`

周期用户去重明细表，按 `period_type + period_key + stat_timezone + sys_origin + country_code + user_id + metric_type` 唯一。

当前维护的 `metric_type`：

- `COUNTRY_NEW_USER`: 新增用户人数
- `LUCKY_GIFT_USER`: 幸运礼物用户人数
- `GAME_USER`: 游戏用户人数

`WEEK/MONTH/ALL` 的人数类指标不再从日表简单相加，而是基于这张明细表聚合，避免同一个用户跨多天被重复计算。

## 查询路由

`CountryDashboardServiceImpl#shouldUsePrecomputedMetrics` 会先判断是否能走预聚合。

走预聚合的条件：

- `sysOrigin` 不为空。
- 对应 `statTimezone` 的周期预聚合已经存在。
- `DAY`: 查询范围内每一天都有 `country_dashboard_period_metric` 数据。
- `WEEK/MONTH`: 查询范围必须按周期边界对齐，当前未结束的周/月允许到今天。
- `ALL`: 无 `startDate/endDate` 时固定走 `period_type=ALL, period_key=ALL`。如果汇总表未回填，返回空汇总，不再回退全历史实时扫描。

`DAY/WEEK/MONTH` 不满足条件时回退原实时 SQL，保证数据不因预聚合缺失而返回错误。`ALL` 不回退实时 SQL，是为了避免一次后台点击触发全历史 wallet 分表扫描。

## 刷新任务

任务类：`CountryDashboardDailyMetricTask`

默认任务：

- 每 10 分钟刷新最近 1 天：`red-circle.country-dashboard.refresh-today-cron`
- 每天 00:20 刷新最近 8 天：`red-circle.country-dashboard.refresh-recent-cron`

默认平台：

```yaml
red-circle:
  country-dashboard:
    refresh-sys-origins: LIKEI
```

需要刷新多个平台时配置为逗号分隔，例如：

```yaml
red-circle:
  country-dashboard:
    refresh-sys-origins: LIKEI,TARAB
```

刷新链路：

1. 按利雅得时区刷新日表 `country_dashboard_daily_metric`。
2. 按配置的统计时区刷新对应 `DAY` 周期表和用户去重明细。
3. 按配置的统计时区刷新受影响的 `WEEK`、`MONTH`。
4. 基于同一 `stat_timezone` 下已存在的 `DAY` 周期表和日级用户明细刷新 `ALL`。

默认刷新时区：

```yaml
red-circle:
  country-dashboard:
    refresh-timezones: Asia/Riyadh,UTC,Asia/Shanghai
```

## 指标来源

SQL 预聚合指标：

- 新增用户：`user_base_info`
- 新用户充值、官方充值、Google 充值：`order_purchase_history`
- MifaPay 充值：`order_user_purchase_pay`
- 代理充值：`likei_wallet.user_freight_balance_running_water`
- 工资兑换：`user_salary_account_running_water`, `user_salary_diamond_running_water`
- 幸运礼物流水/返奖：`game_lucky_gift_count`
- 游戏流水/返奖：`wallet_gold_asset_record_1..12`, `game_lucky_box_count`
- 游戏人数：`country_dashboard_period_user_metric` 的 `GAME_USER`

仍实时从 Mongo 聚合的指标：

- 礼物消耗：`gift_give_running_water`
- 幸运礼物主播分成：`gift_give_running_water`
- 日活：`user_daily_active_log`
- 留存：`user_daily_active_log` + 注册 cohort

## 上线和回填

上线前可以先执行：

- `sql/20260507_country_dashboard_daily_metric.sql`
- `sql/20260507_country_dashboard_period_metric.sql`
- `sql/20260508_country_dashboard_period_metric_timezone.sql`

为避免遗漏，console 的刷新和手动回填入口会先执行同等的 `CREATE TABLE IF NOT EXISTS` 检查，再写入预聚合数据。

首次上线后，预聚合表为空时接口会回退实时 SQL。为了尽快切到预聚合，需要先回填最近常用查询范围，例如最近 8 天、当前周、当前月。

`ALL` 的数据由已回填的日表和日级用户明细汇总而来。如果需要后台“全部”展示完整历史，必须先完成历史日数据回填；否则 `ALL` 只代表当前已经进入预聚合表的数据范围。

手动回填入口：

```bash
curl -X POST 'http://127.0.0.1:{console_port}/console/datav/country-dashboard/backfill?startDate=2026-04-08&endDate=2026-05-07&sysOrigin=LIKEI'
```

该入口默认回填 `Asia/Riyadh`、`UTC`、`Asia/Shanghai` 三个统计时区。需要指定时区时可以传：

```bash
curl -X POST 'http://127.0.0.1:{console_port}/console/datav/country-dashboard/backfill?startDate=2026-04-08&endDate=2026-05-07&sysOrigin=LIKEI&statTimezones=Asia/Riyadh,UTC,Asia/Shanghai'
```

该入口会按天重算日表，并按每个 `stat_timezone` 重算 `DAY` 周期，再刷新受影响的 `WEEK`、`MONTH` 和 `ALL`。

## 验证 SQL

检查某天是否已刷新：

```sql
SELECT stat_date, sys_origin, COUNT(1)
FROM country_dashboard_daily_metric
WHERE sys_origin = 'LIKEI'
GROUP BY stat_date, sys_origin
ORDER BY stat_date DESC;
```

检查周期汇总：

```sql
SELECT stat_timezone, period_type, period_key, sys_origin, COUNT(1)
FROM country_dashboard_period_metric
WHERE sys_origin = 'LIKEI'
GROUP BY stat_timezone, period_type, period_key, sys_origin
ORDER BY stat_timezone, period_type, period_key DESC;
```

检查用户去重明细：

```sql
SELECT stat_timezone, period_type, period_key, metric_type, COUNT(1)
FROM country_dashboard_period_user_metric
WHERE sys_origin = 'LIKEI'
GROUP BY stat_timezone, period_type, period_key, metric_type
ORDER BY stat_timezone, period_type, period_key DESC, metric_type;
```
