ok
This commit is contained in:
202
plans/充值用户_redis_集合_a31b1665.plan.md
Normal file
202
plans/充值用户_redis_集合_a31b1665.plan.md
Normal file
@@ -0,0 +1,202 @@
|
||||
---
|
||||
name: 充值用户 Redis 集合
|
||||
overview: 在 center 注册共享 Redis key `userRecharged`,wallet 侧一次性脚本从分片 `wallet_stat` 回填历史充值用户,并在 `recharge` / `rechargeSign` 成功后实时 SADD 到共享 Redis 集合。
|
||||
todos:
|
||||
- id: center-key
|
||||
content: slot_center sharerediskey.php 增加 userRecharged => user:recharged
|
||||
status: completed
|
||||
- id: wallet-service
|
||||
content: wallet:RedisKeyManagerService + RechargedUidSetService(share Redis SADD)
|
||||
status: completed
|
||||
- id: wallet-hook
|
||||
content: WalletLogic::recharge / rechargeSign 成功后调用 RechargedUidSetService::add
|
||||
status: completed
|
||||
- id: backfill-cmd
|
||||
content: 新建 backfillRechargedUidSet 命令:扫 wallet_stat 分片、--dry-run/--rebuild/--batch-size
|
||||
status: completed
|
||||
- id: tests
|
||||
content: 补充 Service/集成测试或 dry-run 验证说明
|
||||
status: completed
|
||||
isProject: false
|
||||
---
|
||||
|
||||
# Wallet 充值用户共享 Redis 集合
|
||||
|
||||
## 目标
|
||||
|
||||
维护一个**全局、持久**的共享 Redis Set,成员为「曾成功充值」的用户 uid(口径:`wallet_stat.total_deposit > 0`,与 [每日返水需求](file:///Users/ray/.cursor/plans/每日返水需求文档_20a03147.plan.md) 中「充值用户」一致)。供后续活动(如每日返水结算)用 `SISMEMBER` 快速判断,避免逐用户查库。
|
||||
|
||||
你已确认:
|
||||
- Key:`userRecharged` → Redis `user:recharged`
|
||||
- `recharge` 与 `rechargeSign` 均写入集合
|
||||
|
||||
---
|
||||
|
||||
## 架构与数据流
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph center [slot_center]
|
||||
Sharerediskey["config/sharerediskey.php"]
|
||||
ShareConfigAPI["/innerapi/share-config/all"]
|
||||
end
|
||||
subgraph wallet [slot_wallet]
|
||||
BackfillCmd["BackfillRechargedUidSet command"]
|
||||
WalletLogic["WalletLogic::recharge / rechargeSign"]
|
||||
RechargedSvc["RechargedUidSetService"]
|
||||
StatDB["wallet_XX.wallet_stat_YY"]
|
||||
end
|
||||
ShareRedis["Redis connection share"]
|
||||
Sharerediskey --> ShareConfigAPI
|
||||
ShareConfigAPI --> RechargedSvc
|
||||
BackfillCmd --> StatDB
|
||||
BackfillCmd --> RechargedSvc
|
||||
WalletLogic --> RechargedSvc
|
||||
RechargedSvc --> ShareRedis
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Center:注册共享 Key
|
||||
|
||||
**文件**:[`slot_center/config/sharerediskey.php`](slot_center/config/sharerediskey.php)
|
||||
|
||||
在数组末尾增加(含注释):
|
||||
|
||||
```php
|
||||
'userRecharged' => 'user:recharged', // set:曾充值用户 uid(wallet 维护)
|
||||
```
|
||||
|
||||
该文件已通过 [`ShareConfigController::all()`](slot_center/app/innerapi/controller/ShareConfigController.php) 下发为 `sharerediskey.*`,各服务经 `ShareConfigService::get('sharerediskey.userRecharged')` 读取。
|
||||
|
||||
**部署顺序**:先发布 center,再发布 wallet(否则 wallet 会落到 fallback 默认值,key 不一致)。
|
||||
|
||||
---
|
||||
|
||||
## 2. Wallet:Key 解析与写入封装
|
||||
|
||||
### 2.1 `RedisKeyManagerService`
|
||||
|
||||
**文件**:[`slot_wallet/app/service/RedisKeyManagerService.php`](slot_wallet/app/service/RedisKeyManagerService.php)
|
||||
|
||||
新增方法(与现有 `sourceRealDataKey` 一致,从 center 取 key):
|
||||
|
||||
```php
|
||||
public static function getUserRechargedSetKey(): string
|
||||
{
|
||||
return ShareConfigService::get('sharerediskey.userRecharged', 'user:recharged');
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 新建 `RechargedUidSetService`
|
||||
|
||||
**路径**:`slot_wallet/app/service/wallet/RechargedUidSetService.php`
|
||||
|
||||
职责(公共能力,避免 Logic 直接拼 Redis):
|
||||
|
||||
- `add(int $uid): void` — `Redis::connection('share')->sAdd($key, (string)$uid)`
|
||||
- key 来自 `RedisKeyManagerService::getUserRechargedSetKey()`
|
||||
- 失败只 `LoggerService::error`,不抛异常(与 `WalletLogic::recharge()` 内 `sendConsoleBus` / `createTask` 等非阻断副作用一致)
|
||||
- **不设 expire**(成员为永久「曾充值」标记;`SADD` 幂等)
|
||||
|
||||
参考:[`slot_risk`](slot_risk/app/service/api/BlackUserService.php) 对 `connection('share')->sAdd` 的用法;[`config/redis.php`](slot_wallet/config/redis.php) 已配置 `share` 连接。
|
||||
|
||||
---
|
||||
|
||||
## 3. 充值链路:实时写入
|
||||
|
||||
**文件**:[`slot_wallet/app/api/logic/WalletLogic.php`](slot_wallet/app/api/logic/WalletLogic.php)
|
||||
|
||||
在以下两处、`total_deposit` 递增且 `RechargeExchangeService::recharge()` 之后,调用 `RechargedUidSetService::add($uid)`:
|
||||
|
||||
| 方法 | 约行号 | 说明 |
|
||||
|------|--------|------|
|
||||
| `recharge()` | ~782 后 | 正常支付充值 |
|
||||
| `rechargeSign()` | ~820 后 | 签到购,累加统计 |
|
||||
|
||||
放在现有 `try/catch` 块内即可,无需单独 try。
|
||||
|
||||
**不扩展范围**:全库仅这两处递增 `total_deposit`(已 grep 确认),无需 hook `inc()` 或其它 type。
|
||||
|
||||
---
|
||||
|
||||
## 4. 一次性回填脚本
|
||||
|
||||
### 4.1 Webman Command
|
||||
|
||||
**路径**:`slot_wallet/app/command/BackfillRechargedUidSet.php`
|
||||
|
||||
- `$defaultName = 'backfillRechargedUidSet'`
|
||||
- 结构参考 [`CleanupWalletLog.php`](slot_wallet/app/command/CleanupWalletLog.php):`LoggerService::initGenerateTraceId`、stdout 汇总、异常返回 `FAILURE`
|
||||
|
||||
**选项**:
|
||||
|
||||
| Option | 作用 |
|
||||
|--------|------|
|
||||
| `--dry-run` | 只统计 uid 数量,不写 Redis |
|
||||
| `--rebuild` | 执行前 `DEL` 目标 set(全量重建;默认增量 SADD,可重复跑) |
|
||||
| `--batch-size` | 每批 `SADD` 成员数,默认 500 |
|
||||
|
||||
### 4.2 扫描逻辑
|
||||
|
||||
按 [`InitDB::initWalletStat`](slot_wallet/app/command/InitDB.php) 的分片循环:
|
||||
|
||||
```php
|
||||
for ($i = 1; $i <= $shardInfo['database_num']; $i++) {
|
||||
$db = sprintf('wallet_%02d', $i);
|
||||
for ($j = 1; $j <= $shardInfo['table_num']; $j++) {
|
||||
$table = "{$db}.wallet_stat_" . sprintf('%02d', $j);
|
||||
// SELECT DISTINCT uid FROM {$table} WHERE total_deposit > 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 使用 `DISTINCT uid`(表主键 `(uid, currency)`,多币种各一行,同一 uid 只入 set 一次)
|
||||
- 分批 `sAdd($key, ...$uids)` 写入 `connection('share')`
|
||||
- 输出:每表行数、总 uid 数、最终 `SCARD`
|
||||
|
||||
### 4.3 执行命令(Docker)
|
||||
|
||||
```bash
|
||||
docker exec -w /app/www/slot/slot_wallet php82 php webman backfillRechargedUidSet --rebuild
|
||||
# 预检
|
||||
docker exec -w /app/www/slot/slot_wallet php82 php webman backfillRechargedUidSet --dry-run
|
||||
```
|
||||
|
||||
**建议上线步骤**:
|
||||
|
||||
1. 发布 center(`userRecharged` 配置)
|
||||
2. 发布 wallet(Service + Logic + Command)
|
||||
3. 低峰执行 `backfillRechargedUidSet --rebuild`(或先 `--dry-run` 核对数量)
|
||||
4. 之后新充值由 Logic 自动 `SADD`
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试(wallet 仓库)
|
||||
|
||||
| 项 | 方式 |
|
||||
|----|------|
|
||||
| `RechargedUidSetService` | 单元测试 mock `Redis::connection('share')`,或集成测试用真实 share Redis(若 CI 有) |
|
||||
| `WalletLogic` | 在现有 [`WalletControllerUpdateTest`](slot_wallet/tests/Integration/WalletControllerUpdateTest.php) / recharge 相关用例后断言 set 含 uid(可选,依赖 share Redis) |
|
||||
| Command | 本地 `--dry-run` + 小环境 `--rebuild` 后 `SCARD` 抽样 `SISMEMBER` |
|
||||
|
||||
至少保证:Service 在 key 存在时调用 `sAdd` 且不抛异常;Command SQL 与分片配置可读。
|
||||
|
||||
---
|
||||
|
||||
## 6. 不在本次范围(供后续)
|
||||
|
||||
- **消费方**(如 `slot_console` 每日返水 cron)改为 `SISMEMBER`:需在对应服务增加 `ShareConfigService::get('sharerediskey.userRecharged')` + `connection('share')`,本次仅 wallet 写入。
|
||||
- center 本地 [`ShareRedisKeyManagerService`](slot_center/app/service/ShareRedisKeyManagerService.php) 可不增方法(wallet 直接用 `RedisKeyManagerService` + `ShareConfigService`,与现有 `sourceReal` 一致)。
|
||||
|
||||
---
|
||||
|
||||
## 涉及文件一览
|
||||
|
||||
| 仓库 | 文件 | 变更 |
|
||||
|------|------|------|
|
||||
| slot_center | `config/sharerediskey.php` | +1 key |
|
||||
| slot_wallet | `app/service/RedisKeyManagerService.php` | +`getUserRechargedSetKey()` |
|
||||
| slot_wallet | `app/service/wallet/RechargedUidSetService.php` | 新建 |
|
||||
| slot_wallet | `app/api/logic/WalletLogic.php` | recharge / rechargeSign 各 +1 行调用 |
|
||||
| slot_wallet | `app/command/BackfillRechargedUidSet.php` | 新建一次性脚本 |
|
||||
Reference in New Issue
Block a user