5.9 KiB
5.9 KiB
todos:
- id: "ddl" content: "新增 slot-pwa/db/game_user_recharge_rtp_stat.sql(用户 DDL 原样)" status: pending
- id: "service-model" content: "GameUserRechargeRtpStatModel + GameUserRechargeRtpStatService + UserRechargeStatusService" status: pending
- id: "wire-wallet-service" content: "WalletService bet/win 成功后挂载累计与 RTP 重算" status: pending
- id: "redis-key" content: "RedisKeyManagerService 补充 recharge:exchange key" status: pending isProject: false
game_user_recharge_rtp_stat(slot-pwa)
变更范围
- 表:按你提供的 DDL,不做额外字段
- 服务:
slot-pwa统一处理(游戏回调 → 调 wallet 的入口) - 不做:wallet 侧改表、移除
TrialValidBetNotifyService、activity 同步(后续再定)
表结构(原样采用)
库:s_common(与 game_launch_session 同库)
CREATE TABLE `game_user_recharge_rtp_stat` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户ID',
`before_recharge_bet_amount` BIGINT NOT NULL DEFAULT 0 COMMENT '充值前累计下注金额,金额扩大1000倍',
`before_recharge_payout_amount` BIGINT NOT NULL DEFAULT 0 COMMENT '充值前累计派奖金额,金额扩大1000倍',
`before_recharge_rtp_rate` DECIMAL(10, 6) NOT NULL DEFAULT 0.000000 COMMENT '充值前RTP,payout / bet',
`after_recharge_bet_amount` BIGINT NOT NULL DEFAULT 0 COMMENT '充值后累计下注金额,金额扩大1000倍',
`after_recharge_payout_amount` BIGINT NOT NULL DEFAULT 0 COMMENT '充值后累计派奖金额,金额扩大1000倍',
`after_recharge_rtp_rate` DECIMAL(10, 6) NOT NULL DEFAULT 0.000000 COMMENT '充值后RTP,payout / bet',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_uid` (`uid`),
KEY `idx_before_rtp_bet` (`before_recharge_rtp_rate`, `before_recharge_bet_amount`),
KEY `idx_after_rtp_bet` (`after_recharge_rtp_rate`, `after_recharge_bet_amount`),
KEY `idx_before_bet` (`before_recharge_bet_amount`),
KEY `idx_after_bet` (`after_recharge_bet_amount`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='用户充值前后下注派奖RTP累计统计表';
DDL 文件:slot-pwa/db/game_user_recharge_rtp_stat.sql
为什么在 slot-pwa
所有厂商下注/派奖最终都走 WalletService::bet() / win()(pop / quick / jsgame / jdb / self / gasea / bbgt 等),与现有 UserProfitService 挂载点一致,一处写入、全覆盖。
flowchart LR
Provider[厂商回调] --> PwaCash[pop/quick/... CashLogic]
PwaCash --> WalletSvc[WalletService bet/win]
WalletSvc --> WalletApi[slot-wallet API]
WalletSvc --> RtpStat[GameUserRechargeRtpStatService]
RtpStat --> Db[(game_user_recharge_rtp_stat)]
阶段判定(充值前 / 充值后)
读 wallet 同款 Redis 充值标记(RechargeExchangeService::isRecharge):
- Key:
recharge:exchange:{uid % 10},field =uid,recharge > 0→ 充值后 - 否则 → 充值前
在 slot-pwa 新增薄封装 UserRechargeStatusService(读 default Redis,key 规则与 wallet 一致),不新增表字段。
写入口径
挂载点:WalletService::bet() / win(),在 updateWallet() 成功返回非 null 之后 写统计(比现有 UserProfitService 更早调用更安全,避免 wallet 失败仍累加)。
| 动作 | 条件 | 更新字段 |
|---|---|---|
| bet 成功 | 未充值 | before_recharge_bet_amount += fee |
| bet 成功 | 已充值 | after_recharge_bet_amount += fee |
| win 成功 | is_end === 1 且未充值 |
before_recharge_payout_amount += fee |
| win 成功 | is_end === 1 且已充值 |
after_recharge_payout_amount += fee |
派奖仅计 is_end=1:与 wallet 实际入账一致(中间派奖 is_end=0 只写 Redis pending,不落库)。
RTP 重算(同事务内 SQL 更新):
before_recharge_rtp_rate = before_bet > 0
? ROUND(before_payout / before_bet, 6) : 0
after_recharge_rtp_rate = after_bet > 0
? ROUND(after_payout / after_bet, 6) : 0
幂等:依赖 slot-pwa 已有 provider_tx 幂等(同一 biz 不会重复调 wallet);不在本表再存 biz_id。
fee=0:跳过累加(zero win 等场景)。
代码落点
| 文件 | 职责 |
|---|---|
slot-pwa/db/game_user_recharge_rtp_stat.sql |
建表 |
slot-pwa/app/model/game/GameUserRechargeRtpStatModel.php |
ThinkORM Model + 完整 PHPDoc |
slot-pwa/app/service/game/GameUserRechargeRtpStatService.php |
accumulateBet / accumulatePayout、RTP 重算、upsert by uid |
slot-pwa/app/service/user/UserRechargeStatusService.php |
读 Redis 判断是否已充值 |
slot-pwa/app/service/WalletService.php |
bet/win 成功后调用 Service |
slot-pwa/app/service/RedisKeyManagerService.php |
补充 getRechargeExchangeKey(与 wallet 一致) |
与 wallet / activity 的关系(后续)
- 本表成为 充值前后 bet/payout/RTP 权威数据源
- activity 的
valid_bet_amount/ 4→5 解锁、移除trial_valid_bet_accumulateMQ → 下一阶段,本计划不改动
验收
- 未充值用户游戏:只涨
before_*,RTP 随 bet/win 更新 - 首充后:只涨
after_* is_end=0的中间 win 不计入 payout- wallet 调用失败时不写表
- 风控可按
idx_before_rtp_bet/idx_after_rtp_bet扫异常 RTP