139 lines
5.9 KiB
Markdown
139 lines
5.9 KiB
Markdown
<!-- 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
|