--- name: 中间派奖余额可见化 overview: 在保持 Final Settlement 真账口径不变的前提下,让 is_end=0 中间派奖实时反映到三方读取的余额(接口返回和钱包查询)。 todos: - id: pending-win-keys content: 设计并接入 pending round/total 与 dedupe Redis key status: pending - id: win-is-end-branch content: 实现 is_end=0 累计展示、is_end=1 合并结算与清理 status: pending - id: wallet-query-overlay content: 让 wallet 查询叠加 pending total 返回最新展示余额 status: pending - id: dto-validator-update content: 补齐 is_end DTO 与 win round_id 校验 status: pending - id: regression-cases content: 验证多次派奖余额可见、最终结算、幂等与失败重试场景 status: pending isProject: false --- # 中间派奖余额可见化(is_end)改造计划 ## 目标 解决“多次派奖时三方读取余额不更新”的问题,同时保持 `win.md` 的核心原则: - 真正入账、Lot 回流、转化/解锁只在 `is_end=1`(Final Settlement)执行 - `is_end=0` 仅做中间派奖累计与展示余额更新 ## 核心思路 在 Redis 维护“待结算派奖池(Pending Win)”,并把它叠加到对外返回余额: - DB 里的 `withdraw/deposit/bonus` 仍保持真实账 - 对外给三方的余额 = 真实账 + pendingWin 这样能保证: - 三方实时看到余额变化 - 不破坏现有 `WinService` 的 Round Final Settlement 账务逻辑 ## 具体改造 ### 1) 新增 Pending Win 缓存层 修改 [app/service/RedisKeyManagerService.php](app/service/RedisKeyManagerService.php):新增 key - `wallet:win:pending:round:{uid}:{currency}:{round_id}`(每局中间派奖累计) - `wallet:win:pending:total:{uid}:{currency}`(用户币种维度累计,供快速读余额) - `wallet:win:dedupe:{uid}:{currency}:{round_id}:{biz_id}`(中间派奖严格幂等) ### 2) win 分支行为 修改 [app/api/logic/WalletLogic.php](app/api/logic/WalletLogic.php) 的 `win()`: - `is_end=0`: - 命中 dedupe 直接返回 - 未命中则 `INCRBY pending:round` 与 `INCRBY pending:total` - 返回余额时把 `pending:total` 叠加到 `withdraw`(仅对外展示) - 不调用 `WinService::execute`、不写 `BIZ_TYPE_WIN` - `is_end=1`: - 读取 `pending:round`,`finalWin = fee + pendingRound` - 用 `finalWin` 调用现有 [app/service/wallet/WinService.php](app/service/wallet/WinService.php) - 成功后清理 `pending:round` 并从 `pending:total` 扣除对应值 - 返回余额按真实账(此时 pending 已回收) ### 3) 钱包查询返回也要可见化 修改 [app/api/logic/WalletLogic.php](app/api/logic/WalletLogic.php) 的 `query()` 返回: - 将 `pending:total` 叠加到返回字段 `w/withdraw` - 这样第三方无论读 `win` 返回还是调用 `wallet` 查询,都看到“最新展示余额” ### 4) DTO/校验补齐 - 在 [app/api/dto/request/wallet/WalletUpdateRequestDTO.php](app/api/dto/request/wallet/WalletUpdateRequestDTO.php) 增加 `is_end`(默认 1) - 在 [app/validator/Wallet2Validator.php](app/validator/Wallet2Validator.php) 增加: - `is_end` 仅允许 `0/1` - `win` 场景强制 `round_id` 必传 ## 一致性与幂等 - `is_end=0`:Redis dedupe(biz_id 级)防重复累计 - `is_end=1`:延续现有 `wallet_log(uid,biz_id,biz_type=win)` 幂等 - 若 Final 失败,不清 pending,便于重试恢复 ## 风险说明(需明确) - 该方案是“展示余额实时、真账延后”:若上游把展示余额当可立即可下注余额,可能出现下注时余额校验不一致。 - 若你需要“中间派奖立即可下注”,则要走另一套方案(中间账临时入库 + Final 对冲重分配),改动会显著更大。 ## 改动文件 - [app/service/RedisKeyManagerService.php](app/service/RedisKeyManagerService.php) - [app/api/dto/request/wallet/WalletUpdateRequestDTO.php](app/api/dto/request/wallet/WalletUpdateRequestDTO.php) - [app/validator/Wallet2Validator.php](app/validator/Wallet2Validator.php) - [app/api/logic/WalletLogic.php](app/api/logic/WalletLogic.php) ## 验收场景 - 多次 `is_end=0` 后,`win` 返回余额递增 - 多次 `is_end=0` 后,`wallet` 查询余额递增 - `is_end=1` 触发后,final 入账金额 = 中间累计 + 最后一笔 - 重放同一 `is_end=0` `biz_id` 不重复累计 - 重放同一 `is_end=1` `biz_id` 不重复结算