feat: expose dashboard recharge user metrics

This commit is contained in:
hy001 2026-05-27 21:33:56 +08:00
parent 8feecc423c
commit 7541ba77a6
9 changed files with 338 additions and 0 deletions

View File

@ -438,11 +438,14 @@ public class CountryDashboardDailyMetricService {
for (CountryDashboardMetricDTO metric : metrics) {
CountryDashboardDailyMetricDTO row = getRow(rows, metric, statDate, sysOrigin);
row.setCountryNewUser(safeLong(row.getCountryNewUser()) + safeLong(metric.getCountryNewUser()));
row.setDailyRechargeUser(safeLong(row.getDailyRechargeUser()) + safeLong(metric.getDailyRechargeUser()));
row.setNewUserRecharge(add(row.getNewUserRecharge(), metric.getNewUserRecharge()));
row.setOfficialRecharge(add(row.getOfficialRecharge(), metric.getOfficialRecharge()));
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
@ -469,6 +472,8 @@ public class CountryDashboardDailyMetricService {
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
@ -489,6 +494,7 @@ public class CountryDashboardDailyMetricService {
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
statTimezone);
row.setCountryNewUser(safeLong(metric.getCountryNewUser()));
row.setDailyRechargeUser(safeLong(metric.getDailyRechargeUser()));
row.setLuckyGiftUser(safeLong(metric.getLuckyGiftUser()));
row.setGameUser(safeLong(metric.getGameUser()));
}

View File

@ -383,11 +383,14 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
metric.getPeriodKey(), metric.getPeriodName());
row.setCountryNewUser(row.getCountryNewUser() + safeLong(metric.getCountryNewUser()));
row.setDailyActiveUser(row.getDailyActiveUser() + safeLong(metric.getDailyActiveUser()));
row.setDailyRechargeUser(row.getDailyRechargeUser() + safeLong(metric.getDailyRechargeUser()));
row.setNewUserRecharge(add(row.getNewUserRecharge(), metric.getNewUserRecharge()));
row.setOfficialRecharge(add(row.getOfficialRecharge(), metric.getOfficialRecharge()));
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
row.setSalaryTransfer(add(row.getSalaryTransfer(), metric.getSalaryTransfer()));
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
@ -784,11 +787,14 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
for (CountryDashboardMetricCO row : records) {
total.setCountryNewUser(total.getCountryNewUser() + safeLong(row.getCountryNewUser()));
total.setDailyActiveUser(total.getDailyActiveUser() + safeLong(row.getDailyActiveUser()));
total.setDailyRechargeUser(total.getDailyRechargeUser() + safeLong(row.getDailyRechargeUser()));
total.setNewUserRecharge(add(total.getNewUserRecharge(), row.getNewUserRecharge()));
total.setOfficialRecharge(add(total.getOfficialRecharge(), row.getOfficialRecharge()));
total.setMifapayRecharge(add(total.getMifapayRecharge(), row.getMifapayRecharge()));
total.setGoogleRecharge(add(total.getGoogleRecharge(), row.getGoogleRecharge()));
total.setDealerRecharge(add(total.getDealerRecharge(), row.getDealerRecharge()));
total.setUserRecharge(add(total.getUserRecharge(), row.getUserRecharge()));
total.setNewDealerUserRecharge(add(total.getNewDealerUserRecharge(), row.getNewDealerUserRecharge()));
total.setSalaryExchange(add(total.getSalaryExchange(), row.getSalaryExchange()));
total.setSalaryTransfer(add(total.getSalaryTransfer(), row.getSalaryTransfer()));
total.setGiftConsume(add(total.getGiftConsume(), row.getGiftConsume()));

View File

@ -28,10 +28,16 @@ public class CountryDashboardMetricCO implements Serializable {
private Long dailyActiveUser = 0L;
private Long dailyRechargeUser = 0L;
private BigDecimal newUserRecharge = BigDecimal.ZERO;
private BigDecimal dealerRecharge = BigDecimal.ZERO;
private BigDecimal userRecharge = BigDecimal.ZERO;
private BigDecimal newDealerUserRecharge = BigDecimal.ZERO;
private BigDecimal mifapayRecharge = BigDecimal.ZERO;
private BigDecimal googleRecharge = BigDecimal.ZERO;

View File

@ -24,6 +24,8 @@ public class CountryDashboardDailyMetricDTO {
private Long countryNewUser;
private Long dailyRechargeUser;
private BigDecimal newUserRecharge;
private BigDecimal officialRecharge;
@ -34,6 +36,10 @@ public class CountryDashboardDailyMetricDTO {
private BigDecimal dealerRecharge;
private BigDecimal userRecharge;
private BigDecimal newDealerUserRecharge;
private BigDecimal salaryExchange;
private BigDecimal salaryTransfer;

View File

@ -23,6 +23,8 @@ public class CountryDashboardMetricDTO {
private Long dailyActiveUser;
private Long dailyRechargeUser;
private BigDecimal newUserRecharge;
private BigDecimal officialRecharge;
@ -33,6 +35,10 @@ public class CountryDashboardMetricDTO {
private BigDecimal dealerRecharge;
private BigDecimal userRecharge;
private BigDecimal newDealerUserRecharge;
private BigDecimal salaryExchange;
private BigDecimal salaryTransfer;

View File

@ -34,6 +34,8 @@ public class CountryDashboardPeriodMetricDTO {
private Long dailyActiveUser;
private Long dailyRechargeUser;
private BigDecimal newUserRecharge;
private BigDecimal officialRecharge;
@ -44,6 +46,10 @@ public class CountryDashboardPeriodMetricDTO {
private BigDecimal dealerRecharge;
private BigDecimal userRecharge;
private BigDecimal newDealerUserRecharge;
private BigDecimal salaryExchange;
private BigDecimal salaryTransfer;

View File

@ -20,11 +20,14 @@
`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 '国家名称',
`country_new_user` bigint NOT NULL DEFAULT 0 COMMENT '新增用户数',
`daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT '当日充值用户数',
`new_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增用户充值',
`official_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '官方充值',
`mifapay_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'MifaPay充值',
`google_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'Google充值',
`dealer_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '代理充值',
`user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户充值',
`new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增币商用户充值',
`salary_exchange` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资兑换',
`salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资转移',
`gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '礼物消耗',
@ -59,11 +62,14 @@
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
`country_new_user` bigint NOT NULL DEFAULT 0 COMMENT '新增用户数',
`daily_active_user` bigint NOT NULL DEFAULT 0 COMMENT '当日活跃用户数',
`daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT '当日充值用户数',
`new_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增用户充值',
`official_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '官方充值',
`mifapay_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'MifaPay充值',
`google_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'Google充值',
`dealer_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '代理充值',
`user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户充值',
`new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增币商用户充值',
`salary_exchange` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资兑换',
`salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资转移',
`gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '礼物消耗',
@ -486,6 +492,21 @@
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_user_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'daily_recharge_user'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `daily_active_user`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_user_columns_schema_lock = 1
AND NOT EXISTS (
@ -598,6 +619,81 @@
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'daily_recharge_user'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `country_new_user`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'user_recharge'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'user_recharge'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'new_dealer_user_recharge'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'new_dealer_user_recharge'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
@country_dashboard_value_columns_schema_lock = 1
AND NOT EXISTS (
@ -1084,11 +1180,14 @@
MAX(t.country_name) AS countryName,
<include refid="PrecomputedPeriodColumns"/>,
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,
@ -1143,11 +1242,14 @@
MAX(t.period_name) AS periodName,
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
IFNULL(SUM(t.daily_active_user), 0) AS dailyActiveUser,
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,
@ -1228,11 +1330,14 @@
country_code,
country_name,
country_new_user,
daily_recharge_user,
new_user_recharge,
official_recharge,
mifapay_recharge,
google_recharge,
dealer_recharge,
user_recharge,
new_dealer_user_recharge,
salary_exchange,
salary_transfer,
gift_consume,
@ -1254,11 +1359,14 @@
#{item.countryCode},
#{item.countryName},
IFNULL(#{item.countryNewUser}, 0),
IFNULL(#{item.dailyRechargeUser}, 0),
IFNULL(#{item.newUserRecharge}, 0),
IFNULL(#{item.officialRecharge}, 0),
IFNULL(#{item.mifapayRecharge}, 0),
IFNULL(#{item.googleRecharge}, 0),
IFNULL(#{item.dealerRecharge}, 0),
IFNULL(#{item.userRecharge}, 0),
IFNULL(#{item.newDealerUserRecharge}, 0),
IFNULL(#{item.salaryExchange}, 0),
IFNULL(#{item.salaryTransfer}, 0),
IFNULL(#{item.giftConsume}, 0),
@ -1275,11 +1383,14 @@
ON DUPLICATE KEY UPDATE
country_name = VALUES(country_name),
country_new_user = VALUES(country_new_user),
daily_recharge_user = VALUES(daily_recharge_user),
new_user_recharge = VALUES(new_user_recharge),
official_recharge = VALUES(official_recharge),
mifapay_recharge = VALUES(mifapay_recharge),
google_recharge = VALUES(google_recharge),
dealer_recharge = VALUES(dealer_recharge),
user_recharge = VALUES(user_recharge),
new_dealer_user_recharge = VALUES(new_dealer_user_recharge),
salary_exchange = VALUES(salary_exchange),
salary_transfer = VALUES(salary_transfer),
gift_consume = VALUES(gift_consume),
@ -1316,11 +1427,14 @@
country_name,
country_new_user,
daily_active_user,
daily_recharge_user,
new_user_recharge,
official_recharge,
mifapay_recharge,
google_recharge,
dealer_recharge,
user_recharge,
new_dealer_user_recharge,
salary_exchange,
salary_transfer,
gift_consume,
@ -1353,11 +1467,14 @@
#{item.countryName},
IFNULL(#{item.countryNewUser}, 0),
IFNULL(#{item.dailyActiveUser}, 0),
IFNULL(#{item.dailyRechargeUser}, 0),
IFNULL(#{item.newUserRecharge}, 0),
IFNULL(#{item.officialRecharge}, 0),
IFNULL(#{item.mifapayRecharge}, 0),
IFNULL(#{item.googleRecharge}, 0),
IFNULL(#{item.dealerRecharge}, 0),
IFNULL(#{item.userRecharge}, 0),
IFNULL(#{item.newDealerUserRecharge}, 0),
IFNULL(#{item.salaryExchange}, 0),
IFNULL(#{item.salaryTransfer}, 0),
IFNULL(#{item.giftConsume}, 0),
@ -1384,11 +1501,14 @@
country_name = VALUES(country_name),
country_new_user = VALUES(country_new_user),
daily_active_user = VALUES(daily_active_user),
daily_recharge_user = VALUES(daily_recharge_user),
new_user_recharge = VALUES(new_user_recharge),
official_recharge = VALUES(official_recharge),
mifapay_recharge = VALUES(mifapay_recharge),
google_recharge = VALUES(google_recharge),
dealer_recharge = VALUES(dealer_recharge),
user_recharge = VALUES(user_recharge),
new_dealer_user_recharge = VALUES(new_dealer_user_recharge),
salary_exchange = VALUES(salary_exchange),
salary_transfer = VALUES(salary_transfer),
gift_consume = VALUES(gift_consume),
@ -1636,6 +1756,7 @@
t.period_key AS periodKey,
MAX(t.period_name) AS periodName,
SUM(CASE WHEN t.metric_type = 'COUNTRY_NEW_USER' THEN 1 ELSE 0 END) AS countryNewUser,
SUM(CASE WHEN t.metric_type = 'RECHARGE_USER' THEN 1 ELSE 0 END) AS dailyRechargeUser,
SUM(CASE WHEN t.metric_type = 'LUCKY_GIFT_USER' THEN 1 ELSE 0 END) AS luckyGiftUser,
SUM(CASE WHEN t.metric_type = 'GAME_USER' THEN 1 ELSE 0 END) AS gameUser
FROM country_dashboard_period_user_metric t
@ -1657,11 +1778,14 @@
'全部' AS periodName,
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
IFNULL(SUM(t.daily_active_user), 0) AS dailyActiveUser,
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,

View File

@ -0,0 +1,93 @@
# 国家数据大屏口径对账与修复计划
## 基准说明
- 对账日期:`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 | 是/否 |

View File

@ -0,0 +1,85 @@
SET @current_schema = DATABASE();
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'daily_recharge_user'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `country_new_user`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'daily_recharge_user'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `daily_active_user`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'user_recharge'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'user_recharge'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_daily_metric'
AND COLUMN_NAME = 'new_dealer_user_recharge'
),
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @ddl = IF(
NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @current_schema
AND TABLE_NAME = 'country_dashboard_period_metric'
AND COLUMN_NAME = 'new_dealer_user_recharge'
),
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
'SELECT 1'
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;