Files
cursor/plans/充值用户_redis_集合_a31b1665.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

203 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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: walletRedisKeyManagerService + RechargedUidSetServiceshare 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曾充值用户 uidwallet 维护)
```
该文件已通过 [`ShareConfigController::all()`](slot_center/app/innerapi/controller/ShareConfigController.php) 下发为 `sharerediskey.*`,各服务经 `ShareConfigService::get('sharerediskey.userRecharged')` 读取。
**部署顺序**:先发布 center再发布 wallet否则 wallet 会落到 fallback 默认值key 不一致)。
---
## 2. WalletKey 解析与写入封装
### 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. 发布 walletService + 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` | 新建一次性脚本 |