ok
This commit is contained in:
138
plans/RTP-ffd1b7b6.plan.md
Normal file
138
plans/RTP-ffd1b7b6.plan.md
Normal file
@@ -0,0 +1,138 @@
|
||||
<!-- ffd1b7b6-16e7-4f41-9f8b-a22ab65c6a00 -->
|
||||
---
|
||||
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`](slot-pwa) 统一处理(游戏回调 → 调 wallet 的入口)
|
||||
- **不做**:wallet 侧改表、移除 `TrialValidBetNotifyService`、activity 同步(后续再定)
|
||||
|
||||
---
|
||||
|
||||
## 表结构(原样采用)
|
||||
|
||||
库:`s_common`(与 [`game_launch_session`](slot-pwa/db/game_launch_session.sql) 同库)
|
||||
|
||||
```sql
|
||||
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/db/game_user_recharge_rtp_stat.sql)
|
||||
|
||||
---
|
||||
|
||||
## 为什么在 slot-pwa
|
||||
|
||||
所有厂商下注/派奖最终都走 [`WalletService::bet()`](slot-pwa/app/service/WalletService.php) / `win()`(pop / quick / jsgame / jdb / self / gasea / bbgt 等),与现有 [`UserProfitService`](slot-pwa/app/service/user/UserProfitService.php) 挂载点一致,**一处写入、全覆盖**。
|
||||
|
||||
```mermaid
|
||||
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`](slot-wallet/app/service/wallet/RechargeExchangeService.php)):
|
||||
|
||||
- Key:`recharge:exchange:{uid % 10}`,field = `uid`,`recharge > 0` → **充值后**
|
||||
- 否则 → **充值前**
|
||||
|
||||
在 slot-pwa 新增薄封装 `UserRechargeStatusService`(读 default Redis,key 规则与 wallet 一致),**不新增表字段**。
|
||||
|
||||
---
|
||||
|
||||
## 写入口径
|
||||
|
||||
挂载点:[`WalletService::bet()`](slot-pwa/app/service/WalletService.php) / `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_accumulate` MQ → **下一阶段**,本计划不改动
|
||||
|
||||
---
|
||||
|
||||
## 验收
|
||||
|
||||
1. 未充值用户游戏:只涨 `before_*`,RTP 随 bet/win 更新
|
||||
2. 首充后:只涨 `after_*`
|
||||
3. `is_end=0` 的中间 win 不计入 payout
|
||||
4. wallet 调用失败时不写表
|
||||
5. 风控可按 `idx_before_rtp_bet` / `idx_after_rtp_bet` 扫异常 RTP
|
||||
Reference in New Issue
Block a user