This commit is contained in:
ray zhou
2026-05-29 11:21:40 +08:00
parent 5d6d482efe
commit f71a5c59af
447 changed files with 32245 additions and 116 deletions

View 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: 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` | 新建一次性脚本 |