# 国家数据大屏口径对账与修复计划

## 基准说明

- 对账日期：`2026-05-27`
- 统计时区：`Asia/Riyadh`
- 统计窗口：`2026-05-27 00:00:00` 到 `2026-05-28 00:00:00`
- 系统来源：`LIKEI`
- 对账方式：旧 Java 源表口径结果 vs 当前 `country_dashboard_*` 预聚合结果。
- 注意：日内数据会继续增长，金额和人数会随时间变化。本文中的“之前数据”来自本轮对账快照；后续修复验证必须重新跑同一窗口源表对账。

## 总体结论

当前大屏切到预聚合后，差异主要来自三类问题：

1. 旧 Java 是直接按 Mongo/SQL 源表字段聚合，CDC worker 有些字段改成了用户国家表、wallet 事件或部分表事件，口径没有完全复刻。
2. 部分 Mongo 源表没有进入 CDC：日活、礼物流水、幸运礼物、工资转移等需要单独的 reconcile/增量任务。
3. 留存基数与留存命中当前使用 worker 自己的用户国家表和活跃集合，和旧 Java 的注册 cohort + active log 口径不一致。

## 字段对账表

| 字段 | 旧 Java 口径 | 旧口径源表结果 | 当前预聚合结果 | 差异 | 差异原因 | 修复方案 | 状态 |
| --- | --- | ---: | ---: | ---: | --- | --- | --- |
| 新增 | 按 admin 用户列表展示口径统计 `user_base_info` 创建时间，展示日等于 `create_time + 8h` 后的日期；按用户国家归组。 | 之前用户列表显示约 57，部署期间增长到 60 | 修复前明显少算 | 修复前不稳定 | worker 原先按数据库原始时间截日，和 admin 用户列表展示日不一致。 | 已改 worker 的新用户统计窗口为 `create_time + 8h` 展示日；回填目标日期并重建 `DAY/ALL`。 | 已修 |
| 日活 | Mongo `user_daily_active_log`，按 `activeDate + sysOrigin` 过滤；先按 `registerCountryCode,userId` 去重，再按 `registerCountryCode` 计数。 | 984 | 844 | -140 | worker 原先读取 active log 后，又强依赖 `dashboard_cdc_user_country` 做国家归类，用户国家缺失或删除标记会把活跃用户过滤掉；旧 Java 不查用户国家表。 | 已改 worker：日活直接使用 active log 的 `sysOrigin/registerCountryCode`；只有 active log 国家为空才 fallback 用户国家表。 | 已修 |
| 新增充值 | 旧 Java 订单口径中，新注册用户在窗口内产生的充值。 | 2 | 2 | 0 | 当前已对齐。 | 保持现状；后续只做回归验证。 | 正常 |
| Google 充值 | Google 订单成功支付金额，按订单完成时间落入统计窗口。 | 63 | 63 | 0 | 当前已对齐。 | 保持现状；后续只做回归验证。 | 正常 |
| MifaPay 充值 | MifaPay 订单成功支付金额，按订单完成时间落入统计窗口。 | 0 | 0 | 0 | 当前已对齐。 | 保持现状；后续只做回归验证。 | 正常 |
| 币商充值 | 币商/代理充值流水，旧 Java 从币商充值相关 SQL 表聚合。 | 0 | 0 | 0 | 当前已对齐。 | 保持现状；后续只做回归验证。 | 正常 |
| 总充值 | Google + MifaPay + 币商等充值汇总。 | 63 | 63 | 0 | 当前已对齐。 | 保持现状；后续只做回归验证。 | 正常 |
| 工资兑换 | 旧 Java 包含 SQL 工资账户流水和 Mongo bank 流水中的工资兑换口径。 | 6.75 | 0 | -6.75 | worker 当前没有完整接入工资兑换相关 SQL/Mongo 流水。 | 增加工资兑换 reconcile：读取旧 Java 使用的 SQL 表和 Mongo `user_bank_running_water` 条件，按用户国家归组写 `salary_exchange`；补回填。 | 待修 |
| 工资转移 | 旧 Java 来自 Mongo `user_bank_running_water` 的工资转移口径。 | 38 | 0 | -38 | worker 当前未接入 Mongo bank 转移流水。 | 增加 Mongo bank reconcile：复刻旧 Java 的转移类型、状态、时间字段、金额单位，写 `salary_transfer`；补回填。 | 待修 |
| 礼物消耗 | Mongo `gift_give_running_water`；旧 Java 条件包含 `refunded != true`、`userId != null`、窗口内 `createTime`、指定 `sysOrigin`、`luckyGift != true`、`originId` 为数字、`dynamicContentId` 为空或不存在；金额取 `giftValue.actualAmount`。 | 7,276,368 | 3,327,288 | -3,949,080 | worker 当前没有完整复刻 Mongo `gift_give_running_water` 条件，可能只覆盖部分礼物事件或国家映射。 | 新增/修正 gift reconcile，直接按旧 Java Mongo 条件聚合普通礼物消耗，写 `gift_consume`；不要从 wallet `LUCKY_GIFT` 反推普通礼物。 | 待修 |
| 幸运礼物流水 | 旧 Java 从 `game_lucky_gift_count` 聚合幸运礼物总流水。 | 2,279,050 | 0 | -2,279,050 | `game_lucky_gift_count` 有源数据，但 worker/预聚合没有写入 `lucky_gift_total_flow`。 | 新增 lucky gift reconcile：按旧 Java 查询条件读取 `game_lucky_gift_count`，写 `lucky_gift_total_flow`，同时写用户去重表 `LUCKY_GIFT_USER`。 | 待修 |
| 幸运礼物人数 | 旧 Java 从 `game_lucky_gift_count` 按用户去重统计参与人数。 | 101 | 0 | -101 | 同幸运礼物流水，用户去重表未写。 | lucky gift reconcile 写 `country_dashboard_period_user_metric.metric_type='LUCKY_GIFT_USER'`，再重建汇总人数。 | 待修 |
| 幸运礼物返奖 | 旧 Java 从 `game_lucky_gift_count` 聚合返奖金额。 | 1,892,900 | 0 | -1,892,900 | 同幸运礼物流水，返奖字段未写。 | lucky gift reconcile 写 `lucky_gift_payout`。 | 待修 |
| 幸运礼物利润 | `幸运礼物流水 - 幸运礼物返奖`。 | 386,150 | 0 | -386,150 | 流水和返奖都缺失导致利润为 0。 | 修复幸运礼物流水和返奖后自动恢复；利润不应单独写源数据。 | 待修 |
| 游戏流水 | 旧 Java 从 `wallet_gold_asset_record_*` 和游戏统计表按游戏下注/消耗事件聚合。 | 109,688,935 | 74,509,547 | -35,179,388 | worker 当前游戏聚合少覆盖部分 `event_type` 或表来源，且用户去重也随之少算。 | 按旧 Java 的 game event_type 列表逐项核对，补齐 consume 事件映射；对每类事件做源表 vs 预聚合差异拆分。 | 待修 |
| 游戏人数 | 旧 Java 按游戏流水参与用户去重。 | 176 | 113 | -63 | 游戏流水事件少覆盖，导致用户去重同步少算。 | 游戏流水修复时同步写 `GAME_USER` 去重表；重建 `DAY/ALL`。 | 待修 |
| 游戏返奖 | 旧 Java 从游戏返奖/中奖事件聚合。 | 107,776,247 | 72,064,472 | -35,711,775 | worker 当前 payout 事件覆盖不完整，返奖少算幅度大于流水少算。 | 按旧 Java payout event_type 和 `game_lucky_box_count` 等来源补齐映射；验证 consume/payout 各自差值。 | 待修 |
| 游戏利润 | `游戏流水 - 游戏返奖`。 | 1,912,688 | 2,445,075 | +532,387 | 流水和返奖都少算，但返奖少算更多，所以利润虚高。 | 修复游戏流水和游戏返奖后重算；利润不单独写源数据。 | 待修 |
| 次日留存 | 旧 Java：注册 cohort 来自用户注册表；命中来自 Mongo `user_daily_active_log` 的目标活跃日期。 | 0 / 0 | 84 / 127 | +84 / +127 | 当前留存基数和命中口径与旧 Java 不一致，使用了 worker 用户国家表和当前 active 集合。 | 重写 retention reconcile：注册 cohort 复刻旧 Java `listNewUserCohorts`，活跃命中直接查 active log；按国家分别写 base/user。 | 待修 |
| 七日留存 | 同上，目标日期为注册日 + 7。 | 0 / 0 | 25 / 60 | +25 / +60 | 同次日留存。 | 同 retention reconcile，支持 D7。 | 待修 |
| 30 日留存 | 同上，目标日期为注册日 + 30。 | 0 / 0 | 5 / 7 | +5 / +7 | 同次日留存。 | 同 retention reconcile，支持 D30。 | 待修 |

## 已完成修复记录

### 新增

- 修改点：`dashboard-cdc-worker` 新增用户统计使用 admin 用户列表展示日，即 `create_time + 8h` 后的日期。
- 验证结果：`2026-05-27` 修复后源数据和预聚合从 57 增长到 60，增长来自部署期间新增的 3 个用户。
- 结论：新增已与用户列表显示口径对齐。

### 日活

- 修改点：`dashboard-cdc-worker/internal/storage/active_reconcile.go`
- 提交：`0b5e687 Align daily active metric with active log`
- 部署镜像：`dashboard-cdc-worker:daily-active-log-country-20260527-0b5e687`
- 验证结果：`2026-05-27`
  - Mongo 旧 Java 口径：`1054`
  - `country_dashboard_period_metric` 预聚合：`1054`
  - 差值：`0`
- 结论：日活已与旧 Java active log 口径对齐。

## 后续修复顺序建议

1. 工资兑换、工资转移：影响字段少，数据源明确，先补 Mongo/SQL reconcile。
2. 礼物消耗：复刻 `gift_give_running_water` 条件，避免继续从 wallet 侧错误推导。
3. 幸运礼物：直接接 `game_lucky_gift_count`，同时写金额和人数去重。
4. 游戏流水、游戏返奖、游戏人数：按 event_type 拆分差异后逐类补齐。
5. 留存：最后统一重写，因为它依赖新增口径和日活口径都稳定。

## 每项修复后的验证模板

每修一个字段，都必须跑同一套对账：

```sql
-- 预聚合结果
SELECT
  SUM(<metric_column>) AS precomputed_value
FROM country_dashboard_period_metric
WHERE period_type = 'DAY'
  AND period_key = '2026-05-27'
  AND stat_timezone = 'Asia/Riyadh'
  AND sys_origin = 'LIKEI';
```

源表侧按旧 Java 条件单独查一遍，并输出：

| 字段 | 旧 Java 源表结果 | 预聚合结果 | 差值 | 是否通过 |
| --- | ---: | ---: | ---: | --- |
| 待验证字段 | 0 | 0 | 0 | 是/否 |

