Files
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

84 lines
142 KiB
JSON
Raw Permalink 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.

{"role":"user","message":{"content":[{"type":"text","text":"<timestamp>Thursday, Jun 11, 2026, 1:27 PM (UTC+8)</timestamp>\n<user_query>\n评审docs下面\"钱包系统v2\" 需求\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" I need to locate and review the wallet system v2 requirements document in the docs folder."},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/docs/**/*钱包*"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"I'll review this requirements doc against the existing wallet system. Let me look at the related background docs and actual wallet code.\n\n I should review the existing wallet system documentation and code structure to understand the current implementation before diving into my analysis."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/background/充值活动-优惠券-钱包全链路背景知识.md"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"slot-wallet/app/model/**/*.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me examine the actual wallet models and existing betting/recharge logic to ground the review in real code.\n\n I'm reviewing the requirements documentation rather than implementing code. I need to check the wallet implementation against the doc's claims—there's a discrepancy where the background describes the betting consume order as \"Withdrawable → Locked Deposit → Bonus Credits,\" but the V2 doc states it as \"Deposit → Bonus → ... → Cash,\" which appears to be reversed. I should also verify how bonus credits are actually credited in the system.\n\nLet me examine the actual wallet models and the existing betting and recharge logic to validate these claims against the real implementation."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/model/multi/WalletAccountModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/model/multi/WalletFundLotModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/model/multi/WalletFundLotGroupModel.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me look at the existing betting/settlement logic to understand current consume order and payout routing.\n\n I need to examine the existing betting and settlement logic to see how the current system handles order consumption and payout routing, since the V2 changes will modify these flows significantly."},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"slot-wallet/app/**/*.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/service/wallet/BetService.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now let me look at the current payout/win logic to compare with the V2 payout rules.\n\n Let me check the WinService implementation to see how payouts are currently being routed."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/service/wallet/WinService.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me verify the recharge入账 behavior in actual code to confirm the bonus-bucket discrepancy.\n\n I'm reviewing the V2 documentation against the current implementation to identify inconsistencies, risks, and gaps. I've spotted a major discrepancy around bonus deposit bucket conflicts between what the V2 spec describes and how it's actually implemented.\n\nThe V2 doc claims to deposit both the deposit amount and bonus together into `deposit_balance`, but the current system keeps them separate—deposit goes to `deposit_balance` while bonus goes to `frozen_bonus`. This creates a semantic conflict because the V2 approach would essentially deprecate the `frozen_bonus` field in recharge scenarios, which contradicts how other bonus-granting services work and how balance displays are calculated across the platform.\n\nThere's also a complete reversal in the bet deduction order between what V2 specifies and what the current implementation does. V2 wants to deduct from locked funds first (progress_group Deposit → Bonus → subsequent groups → Cash last), while the current system deducts from withdrawable cash first. This is a fundamental product strategy shift that needs explicit confirmation, especially since it affects risk management and how players experience their available balance.\n\nThe award attribution logic in V2 introduces a technical challenge—it requires querying the bet deduction details at payout time to determine which progress group a win should be credited to, but the current win service doesn't track this level of detail. This means adding a new bet deduction ledger table or extending wallet logs, which conflicts with the stated goal of not modifying existing table structures. Additionally, since bet and win are independent requests, there's a timing gap where the progress group could change between when the bet is placed and when the win is processed, but the spec doesn't clarify whether to use the group snapshot from bet time or re-query at payout time.\n\nWhen a payout is attributed to a progress group that's already completed, accumulating winnings to its deposit lot would add funds to an already-cleared lot, violating the invariant that completed lots have zero remaining balance. The spec also doesn't address how to handle rollbacks or reconciliation when payouts are directly merged into the principal lot's remaining amount, making it impossible to distinguish between original principal and winnings without a detailed ledger.\n\nThe current network implementation consumes all deposit lots across groups by time priority, then all bonus lots, whereas V2 requires strict group-by-group ordering: current group deposits → current group bonuses → next group deposits → next group bonuses. The spec's recommended deduction order in §7.3 confirms this group-first approach, but the sorting logic needs adjustment to match it.\n\nBeyond just affecting which lot gets consumed first, this also impacts how wager progress is attributed. Currently, valid bets are allocated to their source lot's group, but V2 requires all wagers to accumulate solely to the progress_group regardless of which lots were actually consumed. This is a significant shift—the network distributes progress across multiple groups based on funding source, while V2 centralizes everything to one group. The accumulateGroupWager logic needs to be refactored, and there's ambiguity about whether lot-level wager tracking still matters in V2 or if only group-level progress matters now.\n\nOn the PlayedOut determination timing, the spec says not to judge immediately after deduction, but the current system separates bet and win events—deduction happens without judgment, while finalizeActiveGroups only triggers on win callbacks. If a round has only a losing bet with no win event, when does PlayedOut get judged? The system still calls finalizeActiveGroups even for zero payouts, but it's unclear whether V2 guarantees every round returns a settlement callback, including losses. Without that confirmation, groups that exhaust their funds could remain Active indefinitely.\n\nThere's also a conflict between the goal of not modifying existing table structures and the requirement for detailed deduction logs with fields like progress_group_no, deduct_group_no, lot_no, and deduct_order. The current wallet_log only stores this as unstructured JSON remarks, so either the table needs expansion or a new detail table is required—both violate the \"no schema changes\" constraint. This needs clarification on whether redefining field semantics permits adding new tables.\n\nFinally, the current system only creates a group when both principal and bonus exist together; pure recharges without bonus skip group creation entirely and go directly to a Deposit lot with an empty group_no. V2's approach to this scenario isn't fully specified yet. 前端展示口径需要调整,因为 deposit_balance 同时承载多种资金类型会导致展示冲突。派奖的幂等性也很关键——win 操作必须用 provider_tx_id 或 biz_id 作为幂等键来防止重复累加到 Lot remaining但文档第 7 节没有明确强调这一点。现在我需要整理成一份结构化的评审,涵盖总体评价、与现网实现的关键冲突、逻辑漏洞、待澄清问题和文档质量。\n\n我先快速验证一下充值逻辑中 bonus 是否确实流向 frozen_bonus确保我的结论准确。"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/service/wallet/RechargeFundLotService.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" Now I need to check the WalletRechargeLogic to see how the bonus is actually applied—whether it goes into the frozen_bonus field or the deposit_balance."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/api/logic/WalletRechargeLogic.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"我已通读 V2 需求文档,并对照了背景知识文档与现网 `slot-wallet` 实际代码(`BetService`、`WinService`、`WalletRechargeLogic`、`RechargeFundLotService` 及三张 Model。下面是评审结论。\n\n---\n\n## 一、总体评价\n\n文档把\"Deposit + Bonus 整组打码\"这件事讲清楚了FIFO 推进、组内 Deposit→Bonus 消耗、达标整组转 Cash、PlayedOut 这套**组级模型本身自洽**,而且现网已经有 `wallet_fund_lot_group` + `lockEarliestActiveGroup` + `finalizeActiveGroups` 等基础设施,方向是对的。\n\n但文档有一个**致命的隐含前提没有成立**:它在 §2.11 / §17 反复强调\"不修改现有表结构,仅重新定义字段语义\",然而文档描述的多条核心规则**与现网代码的既有行为直接冲突**,且**至少有两处必须新增表/字段才能实现**。如果按现状直接交付实现,会与现网充值入账、下注扣款、派奖路由三条主链路都打架。\n\n下面按严重度排序。\n\n---\n\n## 二、与现网实现的根本性冲突(必须先拍板)\n\n### 🔴 C1. Bonus 入账落桶:文档说进 `deposit_balance`,现网进 `frozen_bonus`\n\n- 文档 §5.1 / §14.1`deposit_balance += deposit + bonus`150 全进 deposit_balance并称 `frozen_bonus` 只是\"冻结奖励池,不参与本期下注主链路\"。\n- 现网 `WalletRechargeLogic::applyIncBalanceChange` 充值走 `inc(0, fee, bonus)` → `WalletAccountModel::incBalance` 把 **bonus 加到 `frozen_bonus`**\n\n```231:245:slot-wallet/app/model/multi/WalletAccountModel.php\n public function incBalance(int $withdraw, int $deposit, int $bonus, int $oldVersion, string $currency = 'TGO')\n {\n ...\n 'frozen_bonus' => Db::raw(\"frozen_bonus + {$bonus}\"),\n```\n\n这是最核心的语义反转。现网 `frozen_bonus` = Bonus Credits可下注不可提是**下注主链路的一部分**`decBalance` / `BetService` 都在扣它);文档却把它降级为\"不参与下注\",并把 Bonus 合并进 `deposit_balance`。\n\n影响面余额展示口径前端\"可玩/可提现/Bonus Credits\"分桶)、提现校验、首充 Trial Pool 清零(`deductTrialPoolInRechargeTransaction` 读的是 `frozen_bonus`)、以及其它发奖链路(`GrantBonusService`、`available_bonus → deposit`)全部受影响。**文档完全没提这些既有用途如何迁移**,所谓\"仅重定义语义\"实际是废弃一个在用字段。\n\n### 🔴 C2. 下注扣款顺序完全反了\n\n- 文档 §7.3 / §17.7`progress_group Deposit → progress_group Bonus → 后续组 Deposit → 后续组 Bonus → Cash`**Cash 最后扣**。\n- 现网 `BetService::splitBetByFundPriority``Withdrawable(Cash) → Deposit → Bonus`**Cash 最先扣**\n\n```44:44:slot-wallet/app/service/wallet/BetService.php\n * 执行下注:扣款顺序 Withdrawable -> Deposit -> Bonus并把各来源有效投注累加到对应打码组。\n```\n\n背景知识 §0.6 也白纸黑字写\"下注顺序 Withdrawable → Locked Deposit → Bonus Credits不是 Bonus 优先)\"。文档把这条策略整个倒过来了。这是产品策略级决定(先消耗锁定资金推进打码 vs 先消耗可提现现金),不是实现细节,**需要产品明确确认是有意推翻**。\n\n### 🔴 C3. 扣款\"组优先\"而现网\"类型优先\"\n\n文档要求严格的组内顺序第一组 D→第一组 B→第二组 D→第二组 B。现网 `loadPlayableLotsByType` 是先把**所有组**的 Deposit 按 `consume_priority_at` 扣完,再扣所有组的 Bonus类型优先于组。两者对同一笔跨组下注产生的扣减结果不同会直接改变后续每组的 PlayedOut 判定与派奖命中。需要改排序维度。\n\n### 🔴 C4. 有效投注进度归属:文档\"全归 progress_group\",现网\"各归各组\"\n\n- 文档 §7.1:本笔有效投注额**只**累计到 progress_group即便资金跨了多组/Cash。\n- 现网 `BetService::accumulateGroupWager`Deposit/Bonus 计入**各自 `group_no`**,只有 Withdrawable 部分计入 FIFO 最早组:\n\n```186:186:slot-wallet/app/service/wallet/BetService.php\n * 把各来源有效投注累加到对应打码组Deposit/Bonus 计入自身 group_noWithdrawable 计入 FIFO 最早未完成组。\n```\n\n后果举例一笔下注消耗了第二组的 Deposit现网会推进第二组进度文档却要求这部分算到第一组。这又是反转需要重写 `accumulateGroupWager`。另外文档让组进度与 Lot 自身 `current_wager``consumeForBet` 仍按 Lot 累加)口径不一致,需明确 Lot 级打码进度在 V2 是否还保留。\n\n### 🔴 C5. 派奖归属:文档要\"按本局下注命中关系\",现网完全没有这个机制\n\n- 文档 §8 / §11.2:派奖要\"查询本局下注扣款明细\",判断本局是否命中 progress_group 的 Deposit/Bonus再决定派奖累加到哪侧 Lot。\n- 现网有**两套并存且都不是这个逻辑**\n - `WinService::decideWinBucket`:纯按**账户状态**路由(`withdraw_balance>0` 或无打码任务 → withdraw否则 → deposit 并回流 FIFO 最早组 Deposit Lot不看本局 bet。\n - `WalletRechargeLogic::getWinAllocationByRound`:用 Redis `bet_split` 缓存**按比例拆**派奖到 bonus/deposit/withdraw —— 而这恰恰是文档 §8.1 / §8.2 明令禁止的\"按比例拆\"。\n\n也就是说现网已有的派奖比例拆分逻辑与 V2 直接矛盾V2 又引入第三种模型。**必须明确哪套作准、旧逻辑如何下线**,否则三套并存必然账不平。\n\n---\n\n## 三、逻辑漏洞与边界缺口\n\n### 🟠 H1. progress_group 在 bet 与 win 之间会漂移,文档自相矛盾\n§7.1 下注时锁定 progress_group§11.2 step2 派奖时又\"确定 progress_group\"。但 bet 与 win 是两个独立请求,之间该组可能已被别的结算推到 Completed/PlayedOut导致两次取到的组不同。§12 明明保存了 `progress_group_no`下注快照§11.2 却要重新查。**必须统一为:派奖只认下注明细里快照的 `progress_group_no`**,否则归属会错乱。\n\n### 🟠 H2. 派奖累加到\"已 Completed/已清零\"的 Lot 会破坏不变量\n若 progress_group 在派奖前已达标 Completed其 Lot 已被 `completeLot` 置 `remaining_amount=0, status=COMPLETED`。此时文档 §8.2 仍要求\"累加到该组对应 Lot 的 `remaining_amount` + `deposit_balance += payout`\",会把钱加到一个已结束的 Lot 上,且 `deposit_balance` 凭空增长却无法再解锁。文档对\"归属组已不是 Active\"这一分支**完全没定义**。\n\n### 🟠 H3. `remaining_amount += 派奖` 让 remaining > original回滚/对账无依据\n文档 §3.3 有意不区分本金与 winnings直接把派奖累加进本金 Lot。代价`remaining_amount` 会超过 `original_amount`,对账上看像异常;更关键的是**游戏回滚rollback win时无法知道该扣回多少**,只能依赖明细表反查。文档没有定义 win 回滚 / bet 回滚(现网 Lot 有 `STATUS_REVERSED`)在新模型下怎么走。\n\n### 🟠 H4. PlayedOut 触发依赖\"每局都有结算回调\"\n§10.2 强调\"扣款后不立即判 PlayedOut要等派奖处理后再判\"。但纯输的局(无派奖)若游戏侧不回 settle/win=0资金耗尽的组将永远停在 Active。需要明确**每一局(含输)都有结算回调来触发组终态判定**,否则 PlayedOut 永远不发生。现网 `WinService` 在 `fee=0` 时仍执行 `finalizeActiveGroups`,但前提是 win 回调被调用。\n\n### 🟠 H5. win 幂等没写\n派奖累加到 Lot `remaining` 是**非幂等操作**,重复 win 回调会重复加钱。文档 §11.2 流程里没有 win 幂等判重步骤(只在 §7 笼统提幂等)。必须明确 win 幂等键(`provider_tx_id` / `biz_id`),且累加前先判重。\n\n### 🟡 H6. \"无 Bonus 纯充值\"未覆盖\n文档默认充值都带 Bonus 建组。现网纯充值bonus=0**不建组**`resolveRechargeGroupNo` 返回 `''`Deposit Lot 仍 1x 打码且 `deposit_balance += deposit`。V2 没说纯充值是否也进 deposit_balance、是否要打码、走不走组。需补充。\n\n### 🟡 H7. 注册赠送组source_type=1是纯 Bonus 组\n注册赠送通常 `deposit_amount=0`,组内 Deposit 侧为空。派奖命中判定\"未命中 Deposit → 命中 Bonus\"对它恒成立,逻辑上 OK但文档应显式说明纯 Bonus 组的处理。\n\n### 🟡 H8. 与多阶段游戏 / Trial / `bet_locked_balance` 的交互完全缺失\n现网有 `bet_locked_balance`多阶段结算预留、Trial Pool、free spin 等。V2 一字未提与这些的交互,存在遗漏风险。\n\n---\n\n## 四、与\"不改表结构\"目标§2.11)矛盾之处\n\n- §12 要求记录下注扣款明细:`progress_group_no / deduct_group_no / lot_no / lot_type / deduct_amount / deduct_order` 等。现网 `wallet_log` 只有 `remark`(JSON),无这些结构化列;`BetService::buildRemark` 把 fundings 塞进 JSON**无法按 round 高效反查、不可索引**。要支撑 C5 的派奖归属、H3 的回滚,几乎必须**新增下注明细表或扩展 wallet_log 加列**——这与\"不改表结构\"直接冲突。建议把 §2.11 改成\"不改 account/lot/group 三张核心表结构,但允许新增下注明细表\"。\n- C1 若坚持 Bonus 进 deposit_balance则 `frozen_bonus` 字段语义被废弃,这本身就是一种\"破坏性重定义\",下游消费方需要同步改,并非零成本。\n\n---\n\n## 五、需要产品/技术明确拍板的问题\n\n1. **下注扣款顺序**到底是\"Cash 先扣\"(现网)还是\"Cash 最后扣\"V2这是策略反转。\n2. **Bonus 入账**进 `deposit_balance`V2还是保留 `frozen_bonus`(现网)?`frozen_bonus` / `available_bonus` 现有用途如何迁移?\n3. **派奖归属**采用\"按本局命中\"V2还是现网\"按账户状态\"/\"按比例\"?旧 `getWinAllocationByRound` 比例拆逻辑何时下线?\n4. **有效投注进度**全归 progress_groupV2还是各归各组现网Lot 级 `current_wager` 是否保留?\n5. 是否允许**新增下注明细表**(否则 C5/H3 无法实现)。\n6. 每局(含输局)是否都有结算回调以触发 PlayedOut\n\n---\n\n## 六、文档质量\n\n- 通篇用 ` ```text ` 伪代码罗列规则,**没有一张状态机/时序图**bet→settle→win 的事务边界、幂等键、并发(乐观锁 version只字未提而这些恰是钱包系统最容易出错的地方。建议补①bet/win 时序与事务边界图;②幂等键表;③并发冲突处理(现网用 `version` 乐观锁 + maxRetry。\n- §11 流程与 §8/§10 规则有重复且口径略有出入H1。建议规则只写一处流程引用。\n- 金额单位(现网 ×1000 存储)、币种维度未在文档体现。\n- 缺\"回滚 / 取消 / 对账\"章节(现网已有 `STATUS_CANCELLED/REVERSED`)。\n\n## 七、可取之处\n\n- 组级 FIFO 模型与现网基础设施吻合度高,`lockEarliestActiveGroup`、`finalizeActiveGroups`、组级 `required_wager/current_wager` 都已存在,落地不需要从零造。\n- \"整组达标统一转 Cash、不区分本金/winnings\"作为**简化版**是合理的产品取舍。\n- §16 明确划了本期不做的边界Casino Credits、放弃 Bonus、过期、分游戏权重等范围控制清晰。\n\n---\n\n## 结论\n\n文档**作为产品意图是清楚的,但作为可实现的技术规格不达标**:核心三条链路(入账落桶 / 下注顺序 / 派奖归属)都与现网行为冲突且自称\"不改表结构\",存在 5 个根本性冲突 + 多个未定义边界。建议**先就第五节 6 个问题与产品/钱包负责人对齐**,再据此修订文档,补齐时序图、幂等、并发、回滚章节后才进入开发。\n\n需要我接着做哪一项\n\n I'm seeing a critical discrepancy in how bonuses are handled during recharge—they're being added to frozen_bonus rather than deposit_balance, which conflicts with the documented specifications. There are also two separate code paths for processing wins: one through WinService with its new Final Settlement logic, and another legacy path in WalletRechargeLogic that determines win allocation by round.\n\nThe current implementation also uses Redis bet-split caching to allocate payouts proportionally across bonus and deposit buckets—a \"按比例拆派奖\" approach that V2 explicitly forbids. This means there are already three competing payout models in play, which is a fundamental conflict I need to highlight in the review.\n\nI have enough material now to write a comprehensive review in Chinese covering the core conflicts with the live system, logical gaps, contradictions with the no-schema-change constraint, and areas needing product or technical clarification."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"next","prompt":"评审已完成,下一步希望我做什么?","options":[{"id":"writeback","label":"按评审意见修订《钱包系统V2.md》补齐冲突说明、时序/幂等/并发/回滚章节)"},{"id":"conflict_table","label":"产出一份「V2 规则 vs 现网代码行为」逐条差异对照表(供和产品对齐)"},{"id":"impl_plan","label":"基于现网代码产出 V2 落地改造方案与改动清单(不写代码)"},{"id":"nothing","label":"暂不处理,只看评审结论"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"明白,我们一条一条过。先从最底层、影响面最大的那条开始。\n\n**问题 1 / 充值时 Bonus 落哪个余额桶?**\n\n- 文档 V2`deposit + bonus` 一起进 `deposit_balance``frozen_bonus` 弃用。\n- 现网代码:`deposit_balance += deposit``bonus` 进 `frozen_bonus`(且 `frozen_bonus` 现在是下注链路的一部分,首充 Trial Pool 清零也读它)。\n\n这决定了余额展示口径、提现校验、以及 `frozen_bonus`/`available_bonus` 现有用途要不要迁移。\n\n I need to work through the key decision points systematically, starting with the foundational ones. Let me begin by asking about the first critical choice."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q1_bonus_bucket","prompt":"充值赠送的 Bonus 入账时,余额落哪个桌?","options":[{"id":"deposit","label":"按 V2Bonus 也进 deposit_balance废弃 frozen_bonus需同步处理现有 frozen_bonus 用途)"},{"id":"keep_frozen","label":"保持现网Bonus 进 frozen_bonusV2 文档改为适配现网分桶"},{"id":"discuss","label":"我拉不准,先听你分析两种方案优劣再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q1 = Bonus 进 `deposit_balance`,充值场景废弃 `frozen_bonus`**(现有 `frozen_bonus`/Trial Pool 等用途的迁移后面单独再问)。\n\n**问题 2 / 下注扣款顺序?**\n\n- 文档 V2`progress_group Deposit → 该组 Bonus → 后续组 Deposit → 后续组 Bonus → Cash`**Cash 最后扣**,优先消耗锁定资金推进打码)。\n- 现网代码:`Cash(Withdrawable) → Deposit → Bonus`**Cash 最先扣**)。\n\n 我决定在充值场景中将 Bonus 并入 deposit_balance同时废弃 frozen_bonus。现在要处理的是现有 frozen_bonus 的下游使用情况,但用户希望逐个问题讨论,所以接下来转向第二个问题:下注时的扣款顺序。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q2_bet_order","prompt":"下注时资金扣款顺序按哪个?","options":[{"id":"v2_cash_last","label":"按 V2Cash 最后扣(当前组 Deposit→当前组 Bonus→后续组…→Cash"},{"id":"keep_cash_first","label":"保持现网Cash 最先扣Withdrawable→Deposit→BonusV2 文档改回来"},{"id":"discuss","label":"先听你分析两种顺序的业务/风控影响再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q2 = 按 V2Cash 最后扣,且组优先(当前组 Deposit→当前组 Bonus→后续组…→Cash**,这同时确定了 C3 改为\"组优先\"。\n\n**问题 3 / 本笔有效投注进度算给谁?**\n\n- 文档 V2本笔有效投注额**全部**只累计到 progress_group即便资金来自后续组或 Cash。\n- 现网代码:各 Lot 的有效投注计入**各自所属组**Cash 部分才计入 FIFO 最早组。\n\n附带现网每个 Lot 自身还有 `current_wager``consumeForBet` 按 Lot 累加)。\n\n Q2 already establishes the group-first deduction order (current group Deposit → current group Bonus → subsequent groups), so that resolves the group-priority versus type-priority question in C3. I can move forward to the next key issue: determining which group the valid betting progress belongs to (C4)."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q3_wager_attr","prompt":"有效投注进度归属和 Lot 级打码怎么算?","options":[{"id":"v2_all_progress","label":"按 V2全部计入 progress_group以组级 current_wager 为唯一权威进度,弱化/不再用 Lot 级 current_wager"},{"id":"v2_keep_lot","label":"按 V2 组级全计 progress_group但 Lot 级 current_wager 仍保留(供展示/对账)"},{"id":"keep_per_group","label":"保持现网各资金计入各自组V2 文档改回来"},{"id":"discuss","label":"先听你分析再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q3 = 组级有效投注全计 progress_group 作权威进度Lot 级 `current_wager` 保留用于展示/对账**(不作解锁判据)。\n\n**问题 4 / 派奖归属规则?**(现网有两套都与 V2 冲突)\n\n- 文档 V2按\"本局下注命中关系\"——命中 progress_group 的 Deposit 则整笔进该组 Deposit 侧;否则命中 Bonus 进 Bonus 侧;纯 Cash 下注则进 `withdraw_balance`。**不按比例拆**。\n- 现网两套:①`WinService` 按账户状态路由;②`WalletRechargeLogic::getWinAllocationByRound` 用 Redis 缓存**按 bet 比例拆**(正是 V2 禁止的)。\n\n Now I'm moving to Q3, which consolidates the group-level totals for progress tracking while preserving the lot-level wager amounts for reconciliation purposes. After that comes Q4, which handles the prize distribution attribution—that's going to be the complex part."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q4_payout","prompt":"派奖归属采用哪套?旧逻辑怎么办?","options":[{"id":"v2_hit","label":"按 V2“本局命中归属”全面推行下线 WinService 状态路由 + getWinAllocationByRound 比例拆分"},{"id":"v2_keep_winservice","label":"按 V2 命中归属,但保留 WinService 整组解锁框架,仅改派奖落桶逻辑;只下线比例拆分"},{"id":"keep_state","label":"保持现网账户状态路由V2 文档改回来"},{"id":"discuss","label":"先听你分析三套的账平/复杂度再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q4 = 全面采用 V2\"本局命中归属\",下线现网 `WinService` 状态路由与 `getWinAllocationByRound` 比例拆分**。\n\n**问题 5 / 是否允许新增\"下注扣款明细\"表?**\n\nQ4 选了\"命中归属\",派奖时必须能按 `round_id` 反查本局下注命中了哪个组/哪侧、以及回滚时知道扣回多少。现网 `wallet_log` 只有 `remark`(JSON),不可索引、不可靠反查。文档 §2.11 又写\"不改表结构\"。\n\n Q4 推行命中归属全面方案,需要下线 WinService 状态路由和比例拆分逻辑。接下来要确认 Q5 是否新增下注明细表,由于 Q4 的命中归属方案依赖这个表,所以基本上是必需的。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q5_detail_table","prompt":"下注扣款明细怎么存?","options":[{"id":"new_table","label":"新增专用下注明细表bet_order_no/round_id/progress_group_no/lot_no/deduct_amount… 带索引),改 §2.11 措辞"},{"id":"extend_log","label":"不建新表,扩展 wallet_log 加结构化列 + 索引"},{"id":"redis","label":"沿用现网 Redis bet_split 缓存思路(仅缓存,不落库)"},{"id":"discuss","label":"先听你分析再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"三种存法的分析(在 Q4 已定\"命中归属\"前提下):\n\n先明确这份明细要支撑什么——①win 回调按 `round_id` 反查本局命中了哪个组/哪侧/哪个 `lot_no`决定派奖累加目标②bet/win 回滚时精确知道每个 Lot 扣了多少、派奖加了多少;③对账与风控。**这是资金正确性数据,要求持久、可索引、可靠反查、支持分表。**\n\n**A. 新增专用下注明细表(`wallet_bet_detail_xx`**\n- 优点:一笔下注命中多个 Lot跨组+Cash天然用多行表达`(uid,currency,round_id)` 建索引可靠反查;持久——延迟 win、重启、人工补单都能查回滚/对账/风控共用同一事实源;与 `wallet_log`(账变流水)职责清晰分离。\n- 代价:要建表+分表+DDL与 §2.11 冲突,但 §2.11 和 §12 本就自相矛盾,改措辞即可);下注是高频路径,每笔多写 N 行N=命中 Lot 数,通常 13需评估写入量并做热表+归档(可参考 `recharge_order` 的 archive 思路)。\n\n**B. 扩展 `wallet_log` 加结构化列 + 索引**\n- 优点:不建新表。\n- 代价:`wallet_log` 现在是\"一次账变一条\",幂等键 `uniq_uid_bizid_type`。命中多 Lot 要么塞回 JSON不可索引等于没解决要么一笔下注拆多行——会撞幂等键、破坏流水语义还要给高频大表加列+加 `(uid,round_id)` 索引,影响现有写入与体积。**本质是把明细硬塞进不合适的表,不推荐。**\n\n**C. 沿用 Redis `bet_split` 缓存(现网思路)**\n- 优点:零 DDL写得快。\n- 代价:**易失**——TTL 过期/驱逐/Redis 重启后丢失,命中归属拿不到只能 fallback 落错桶且无法补偿;延迟结算、跨天 win、人工补单查不到回滚/对账无持久事实源。**与\"钱包以 DB 为准、Redis 仅加速\"的财务级要求相悖,不能作唯一事实源。**\n\n**我的建议:选 A**,作为命中归属/回滚/对账的唯一持久事实源;如担心下注热路径延迟,可在 A 之上叠加 C 仅作读加速(命中缓存就用,未命中回查表)。同时把 §2.11 改为\"不改 account/lot/group 三张核心表,允许新增下注明细表\",并在文档里写清新表与 `wallet_log` 的职责边界、win 关联幂等键(`round_id`+`provider_tx_id`)、分表与归档策略。\n\n请定夺。\n\n Looking at the three options for storing bet deduction details, I need to understand the context from Q4 about win attribution. When a payout callback happens, the system needs to know which progress_group the bet hit, whether it was against Deposit or Bonus funds, and which lot_no was affected—this determines which Lot side accumulates the payout. The same information is critical for rollback scenarios.\n\nOption A proposes a dedicated bet detail table with structured columns that can be indexed by (uid, currency, round_id) for fast lookups. Since a single bet can hit multiple Lots across different groups and cash types, storing each hit as a separate row handles that naturally with proper ordering. This approach provides a reliable persistent source of truth that survives process restarts and delayed wins, and works for rollbacks, reconciliation, and risk control. The main drawbacks are that it requires schema changes (which conflicts with the stated no-schema-change principle, though that constraint itself seems contradictory with other requirements), and it creates write amplification since each bet generates multiple rows depending on how many Lots it hits—typically 1-3 rows per bet, which could be significant given that bet placement is a high-frequency operation.\n\nOption B tries to extend the existing wallet_log table with structured columns and indexes, but this breaks the semantic meaning of wallet_log as a single account change per transaction. A bet hitting multiple Lots would either require storing the details as unindexable JSON (recreating the original problem) or splitting into multiple log rows, which violates the existing uniqueness constraint on (uid, bizid, type). Adding indexes to this high-frequency table also impacts write performance and storage.\n\nOption C relies on Redis caching of bet splits, which is what the current system uses. It's fast and requires no schema changes, but loses data on TTL expiration, Redis failures, or evictions—and without the persistent record, the system falls back to defaults like putting everything into withdrawals, which breaks the strict hit allocation model needed for V2.\n\nThe recommendation is to go with Option A: create a dedicated bet details table as the single source of truth for win hit allocation, rollbacks, and reconciliation. This aligns with the financial requirement that the database is authoritative and Redis only accelerates reads. We can optionally layer Option C on top for hot-path caching. I should also clarify the boundary between wallet_log and the new table, define idempotency keys using round_id and provider_tx_id, and plan for partitioning and archival."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q5_decide","prompt":"听完分析,下注扣款明细最终怎么存?","options":[{"id":"a","label":"选 A新增专用明细表作唯一事实源推荐"},{"id":"a_plus_c","label":"选 A + C明细表为准Redis 仅读加速"},{"id":"b","label":"选 B扩展 wallet_log"},{"id":"c","label":"选 C只用 Redis 缓存"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q5 = 新增专用下注明细表,作为唯一持久事实源**。\n\n**问题 6 / 每一局(包括纯输、无派奖的局)是否都有结算回调?**\n\nV2 §10.2 规定 PlayedOut 要在\"处理派奖之后\"判定。若输光的局游戏侧不回结算/win=0 回调,资金耗尽的组会永远停在 ActivePlayedOut 永不触发。现网 `WinService` 在 `fee=0` 时也会跑 `finalizeActiveGroups`,但前提是 win 回调被调用。\n\n 决定采用新增专用明细表作为唯一事实源接下来还需要确认几个关键问题每局是否都会触发结算回调、progress_group的漂移处理方案、派奖归属组完成后的处理逻辑以及回滚机制。\n\n现在需要明确回滚时是针对赢局还是下注同时处理无Bonus的纯充值场景还要迁移frozen_bonus的现有用途。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q6_settle_callback","prompt":"输局(无派奖)是否有结算回调来触发组终态判定?","options":[{"id":"win_zero","label":"有:每局都有 settle/win 回调(输局 win=0在 win 里统一判定 Completed/PlayedOut"},{"id":"bet_side","label":"不一定:输局只有 bet 没 win需在 bet 结束后也补一次终态判定(要处理“派奖可能救活”的时序)"},{"id":"unknown","label":"我不确定游戏侧回调机制,你先按两种都兼容的方案出"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"我去查现网 bet/win 的实际调用与结算触发点。\n\n 我需要查看现有的逻辑是如何处理投注和赢的回调的特别是要理解WalletBetWinLogic和控制器中的流程看看每一局是否都会触发win回调以及finalize在什么时候被调用。"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/api/logic/WalletBetWinLogic.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"查清了现网的局生命周期,关键在 `win` 的 `is_end` 字段:\n\n- **`bet`**:扣款 + 记 `BIZ_TYPE_BET` 流水,幂等键 `biz_id`。不做组终态判定。\n- **`win` 且 `is_end=0`(中间派奖)**:只把金额累加进 Redis pending`getWinPendingRoundKey`**不落账、不跑结算**。\n- **`win` 且 `is_end=1`(本局最终结算)**:把 pending + 本次 fee 累加为本局总派奖,跑 `WinService::execute` → `finalizeActiveGroups` / `finalizeUngroupedLots`**这里才判定 Completed/PlayedOut**。\n\n关键点`WinService::execute` 里即使 `payout=0`(输局),只要 `is_end=1` 被调到,仍会跑 finalize、判 PlayedOut。\n\n```57:77:slot-wallet/app/service/wallet/WinService.php\n public function execute(WalletUpdateRequestDTO $requestDTO, WalletAccountModel $wallet): array\n {\n ...\n $payoutAmount = (int)$requestDTO->fee;\n $bucket = self::WIN_BUCKET_WITHDRAW;\n if ($payoutAmount > 0) {\n $bucket = $this->decideWinBucket($wallet, $requestDTO->currency);\n $wallet = $this->creditPayout($requestDTO, $wallet, $payoutAmount, $bucket);\n }\n $wallet = $this->finalizeActiveGroups($requestDTO, $wallet);\n $wallet = $this->finalizeUngroupedLots($requestDTO, $wallet);\n```\n\n**结论**:现网设计**已经依赖\"每局结束都有一个 `is_end=1` 的结算回调\"**slot 聚合标准——每个 round 都有结束结算)。输光的局就是 `is_end=1` + `fee=0` 的 win它会照常触发 PlayedOut 判定。中间多次派奖用 `is_end=0` 累计,最后 `is_end=1` 收口。所以 H4 在现网其实**不是漏洞**,只要 V2 沿用这个生命周期即可。\n\n那么 Q6 就变成确认/收口的问题:\n\n I'm seeing how the round lifecycle works: bets trigger a deduction with an idempotent key, and wins have an `is_end` flag that determines whether it's an intermediate payout (cached to Redis without finalizing) or a final settlement (accumulating pending fees and running the full win service with group finalization).\n\nThe key insight is that PlayedOut and Completed status checks only happen during the `is_end=1` win callback, even when the fee is zero. This means the current system relies on every round having a final settlement callback—losing rounds are just `is_end=1` wins with zero payout. So for V2, I should confirm they're adopting this same round lifecycle pattern and centralizing the terminal state logic in that final settlement callback."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q6_confirm","prompt":"PlayedOut/Completed 终态判定的触发点是否沿用现网“is_end=1 结算回调”机制?","options":[{"id":"reuse","label":"沿用V2 明确按 bet → 多次 is_end=0 中间 win → 一次 is_end=1 结算(含输局 fee=0的生命周期终态判定只在 is_end=1 做"},{"id":"doc_only","label":"沿用,但文档要补一句“依赖游戏侧每局必发 is_end=1”的前提与兼容若缺失的兼底"},{"id":"discuss","label":"我还有疑问,先讨论"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q6 = 沿用 `bet → 多次 is_end=0 中间 win → 一次 is_end=1 结算(含输局 fee=0`生命周期Completed/PlayedOut 只在 `is_end=1` 判定**。\n\n**问题 7 / 派奖归属的 progress_group 取哪个时点?**\n\n并发多局时下注与本局 `is_end=1` 结算之间,原 progress_group 可能已被别的局结算推进。文档 §7.1(下注时定)与 §11.2派奖时重查口径不一致。Q5 已建明细表,下注时本就会快照 `progress_group_no`。\n\n Q6 采用 is_end=1 来界定结算生命周期,终态判定只在 is_end=1 时执行。还需要处理几个问题H1 是 progress_group 的漂移问题,应该用下注快照里的 progress_group_no因为命中归属来自明细表所以自然就用 bet 时记录的值H2 是派奖归属组已完成或已出局的情况H3 是回滚流程。\n\n另外还要考虑无 Bonus 的纯充值场景,以及 frozen_bonus 的现有用途迁移——Trial 大量使用 frozen_bonus如果为充值 Bonus 弃用它,需要特殊处理。\n\n对于 H2在单轮内 bet 记录 progress_group_no 快照后,到 is_end=1 结算时,这个组可能已被其他并发轮的结算完成或出局。派奖时有几个选项:按 bet 快照的组号归属但组已转为现金且额度清零(有问题)、若快照组已非活跃则派奖改进可提现余额、或重新取当前 FIFO 最早的活跃组归属。\n\n先确认 H1——应该用 bet 时快照的 progress_group_no来自明细表还是 win 时重查 FIFO。根据 Q4+Q5 的命中归属逻辑,自然答案是用 bet 快照。确认 H1 后再处理 H2 的边界情况。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q7_progress_snapshot","prompt":"派奖归属认哪个 progress_group","options":[{"id":"snapshot","label":"认下注明细里快照的 progress_group_no推荐与命中归属一致文档 §11.2 改为不重查"},{"id":"requery","label":"结算时重查当前 FIFO 最早 Active 组"},{"id":"discuss","label":"先讨论"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q7 = 派奖归属认下注明细快照的 `progress_group_no`,文档 §11.2 改为不重查**。\n\n**问题 8 / 快照的 progress_group 在结算时已经不是 Active已 Completed 或 PlayedOut怎么办**H2资金正确性边界\n\n并发下本局结算时快照组可能已被别的局推到 Completed资金已转 Cash、Lot 已 `completeLot` 清零)或 PlayedOut。此时 §8.2\"累加到该组对应 Lot 的 `remaining_amount` + `deposit_balance`\"会把钱加到一个已结束、已清零的 Lot 上,破坏不变量、且这笔钱再也无法解锁。\n\n 我需要用快照中的 progress_group_no 来处理 Q7然后考虑一个关键的边界情况当快照的 progress_group 在结算时已经是 Completed 或 PlayedOut 状态时,该如何正确处理资金问题。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q8_group_closed","prompt":"快照组已终态Completed/PlayedOut这笔派奖怎么落","options":[{"id":"to_withdraw","label":"组已 Completed派奖直接进 withdraw_balance可提现组已 PlayedOut也进 withdraw_balance"},{"id":"next_active","label":"转归当前 FIFO 最早 Active 组的 Deposit 侧(跟随新组继续锁定打码)"},{"id":"split","label":"Completed 进 withdrawPlayedOut 转下一个 Active 组"},{"id":"discuss","label":"先听你分析再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q8 = 快照组已 Completed 或 PlayedOut 时,派奖直接进 `withdraw_balance`**备注PlayedOut 组派奖进可提现属产品取舍,文档里我会标一句风控提示)。\n\n下一条是回滚H3。我先确认现网有没有 bet/win 回滚入口,再问你怎么定。\n\n 我在思考Q8的派奖逻辑——当组已完成时派奖进入提现当组已用尽资金未达标时任务失效。这里有个问题需要澄清如果派奖在任务失效后仍进入提现这对玩家来说是有利的但我需要确认这是否真的是预期的行为。\n\n用户已经做了这个产品决策我会记录下来但也应该在文档中标注这个潜在风险。\n\n接下来要处理的是回滚逻辑——由于派奖是累加到剩余额度的非幂等操作回滚需要详细的记录。我需要向用户确认回滚应该如何处理以及是否已经有现有的回滚机制。"},{"type":"tool_use","name":"Grep","input":{"pattern":"rollback|reverse|cancel|refund|冲正|回滚|REVERSED","path":"/Users/ray/Documents/project/www/ray/slot-wallet/app","output_mode":"files_with_matches"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" I'm looking for the bet and win rollback handling in WalletLogic and the controller to see if there's a specific endpoint for canceling or rolling back bets and wins."},{"type":"tool_use","name":"Grep","input":{"pattern":"function (rollback|cancel|reverse|refund|betRollback|winRollback|rollbackBet)","path":"/Users/ray/Documents/project/www/ray/slot-wallet/app","output_mode":"content","-i":true,"-n":true}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"查清了:现网 slot-wallet **没有游戏下注/派奖回滚入口**——只有提现回滚(`rollbackWithdraw`)和 DB 事务回滚。即 provider 取消某局cancel round这条链路在钱包侧**目前未实现**。\n\n但 V2 §12 把\"支持游戏回滚\"列为下注明细表的用途之一§13.2 的 Lot 状态也保留了 `7=Reversed`。所以这里要定调。\n\n**问题 9 / 游戏回滚cancel round在 V2 怎么处理?**\n\n难点Q4 派奖累加到 Lot `remaining`(非幂等加法),且组可能已 Completed/转 Cash回滚要精确冲回很复杂。\n\n The system only supports withdrawal rollback and database transaction rollback, but there's no actual game round rollback endpoint implemented in slot-wallet—meaning when a provider needs to cancel a round or reverse bets/payouts, that flow doesn't exist yet. This is a gap since V2 §12 lists \"game rollback support\" as a purpose for the detail table, so either this feature is explicitly out of scope or it needs to be addressed."},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q9_rollback","prompt":"游戏回滚cancel round在本期怎么定","options":[{"id":"out_of_scope","label":"本期不做:列入 §16 不处理内容,明细表仅为后续回滚/对账预留字段(推荐,先上核心链路)"},{"id":"design_now","label":"本期要做需补完整回滚设计bet 回滚退资金、win 回滚冲回派奖、已解锁组的补偿)"},{"id":"discuss","label":"先听你分析回滚难点与最小可行方案"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q9 = 游戏回滚本期不做,列入 §16明细表保留回滚/对账预留字段**。\n\n**问题 10 / 无 Bonus 的纯充值怎么处理?**\n\n现网纯充值bonus=0`deposit_balance += deposit`、建 Deposit Lot 走 1x 打码、**不建组**`group_no=''`),由 `finalizeUngroupedLots` 单独解锁。V2 文档默认充值都带 Bonus没覆盖纯充值。\n\n Q9 的回滚先放到后面作为第16部分的内容详细表里保留预留字段。接下来要处理的是H6的纯充值场景以及Q1后续中frozen_bonus的迁移——Trial大量使用frozen_bonus需要把available_bonus转向deposit并通过GrantBonusService处理。现在先看H6的纯充值逻辑目前是直接增加deposit_balance打码倍数为1x。\n\n但这里有个问题需要澄清在Q1的决策下bonus进deposit_balance废弃frozen_bonus和V2模型的框架里纯充值没有参与任何bonus活动那它是否还需要锁定和打码要求现在的代码仍然把纯充值放进deposit_balance且要求1x打码但从V2 §5.1的逻辑来看锁定是因为这笔deposit参与了bonus活动纯充值既然没参与bonus是否应该直接可提现而不需要打码"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q10_pure_deposit","prompt":"无 Bonus 纯充值怎么走?","options":[{"id":"keep_ungrouped","label":"保持现网:进 deposit_balance、1x 打码、不建组,走不分组 Lot 解锁V2 文档补这一节)"},{"id":"single_group","label":"纯充值也建“单边组”deposit_amount>0, bonus_amount=0统一走组模型"},{"id":"to_withdraw","label":"纯充值直接进 withdraw_balance、不打码仅参与 Bonus 活动才锁)"},{"id":"discuss","label":"先讨论"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q10 = 无 Bonus 纯充值保持现网(进 `deposit_balance`、1x 打码、不建组、走不分组 Lot 解锁),文档补这一节**。\n\n回到 Q1 的后续——`frozen_bonus` 的废弃范围。现网 `frozen_bonus` 不止充值 Bonus 在用,还有:①**Trial 体验金**`executeTrialBet`/`executeTrialWin` 直接扣/加它);②`available_bonus ↔ frozen_bonus` 转换、`bonusToDeposit`;③其它直接发奖(`GrantBonusService`);④余额展示 `b` 字段。而且现网下注扣 Bonus Lot 时,金额是记到 `frozen_bonus` 桶的(`decBalance` 的 bonus 参数)。\n\n**问题 11 / `frozen_bonus` 废弃到什么范围?**\n\n 我在处理Q10的纯充值场景需要让存款进入deposit_balance、应用1x打码、不创建组、使用不分组的Lot解锁机制还要补充相关文档。同时需要解决Q1后续的问题——frozen_bonus原本用于试玩阶段的扣款和加款操作但现在要把充值奖金迁移到deposit_balance这意味着需要重新处理frozen_bonus在试玩流程中的使用方式。\n\n关键问题是frozen_bonus的废弃范围——是只在充值场景中不再使用还是全局废弃试玩体验金显然仍需要一个不可提现的账户而且现网的BetService通过decBalance函数确实会扣frozen_bonus当消费Bonus Lot时所以如果充值奖金移到deposit_balance需要明确充值产生的Bonus Lot余额如何处理。\n\n这涉及到BetService的实现改动——消费Bonus Lot时应该扣deposit_balance而不是frozen_bonus同时frozen_bonus仅保留给试玩体验金和其他纯奖励发放场景。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"q11_frozen_scope","prompt":"Q1 定了充值 Bonus 进 deposit_balance那 frozen_bonus 怎么处理?","options":[{"id":"recharge_only","label":"仅充值 Bonus 不再进 frozen_bonus改进 deposit_balance、下注扣 Bonus Lot 记 deposit 桶Trial/其它发奖仍用 frozen_bonus推荐"},{"id":"full_deprecate","label":"全局废弃 frozen_bonusTrial 体验金、发奖等全部改造到其它桶"},{"id":"discuss","label":"先听你分析影响面再定"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"记下:**Q11 = 仅充值 Bonus 不再进 `frozen_bonus`(改进 `deposit_balance`,下注扣充值 Bonus Lot 记 deposit 桶Trial 体验金与其它发奖仍用 `frozen_bonus`**。\n\n主要决策点都过完了汇总如下\n\n| # | 决策 |\n|---|---|\n| Q1 | 充值 Bonus 进 `deposit_balance`;仅充值场景废弃 `frozen_bonus` |\n| Q2 | Cash 最后扣,组优先(当前组 D→B→后续组→Cash |\n| Q3 | 组级 `current_wager` 全计 progress_group 作权威进度Lot 级保留仅展示/对账 |\n| Q4 | 派奖按\"本局命中归属\";下线 `WinService` 状态路由 + `getWinAllocationByRound` 比例拆分 |\n| Q5 | 新增专用下注明细表,作唯一持久事实源 |\n| Q6 | 沿用 `bet → is_end=0 中间 win → is_end=1 结算`生命周期;终态只在 `is_end=1` 判 |\n| Q7 | 派奖归属认下注明细快照的 `progress_group_no`(不重查) |\n| Q8 | 快照组已 Completed/PlayedOut 时派奖进 `withdraw_balance` |\n| Q9 | 游戏回滚本期不做,列 §16明细表留预留字段 |\n| Q10 | 无 Bonus 纯充值保持现网(进 deposit、1x 打码、不建组) |\n| Q11 | `frozen_bonus` 仅充值场景废弃Trial/其它发奖保留 |\n\n还有几个较小项我可以按默认处理并在文档标注注册纯 Bonus 组、`bet_locked_balance`/多阶段游戏交互声明本期不变、win 幂等沿用 `biz_id`、金额 ×1000 单位/币种维度、补状态机与时序图)。\n\n现在产出什么\n\n 充值Bonus不再进frozen_bonus只有Trial和其他奖励才用frozen_bonus。这意味着BetService需要区分Bonus Lot属于充值组时扣deposit_balance而Trial体验金直接扣frozen_bonus但Trial下注走单独的executeTrialBet路径不经过BetService。\n\n所以在BetService中消耗任何Bonus Lot都记到deposit桶这样保持一致性。\n\n我已经梳理了主要决策点Bonus进deposit_balance、frozen_bonus仅充值场景废弃、Cash最后扣、组级progress_group权威、命中归属和下线状态路由、新增明细表作为唯一事实源、沿用is_end=1生命周期、用快照progress_group_no、快照组已终态进withdraw、回滚本期不做、纯充值保持现网、Trial保留frozen_bonus。还有一些次要问题需要关注比如注册赠送纯Bonus组、多阶段和bet_locked_balance的交互、win幂等性、金额单位和币种等细节。\n\n现在主要决策都已确定我应该总结这些决策然后问用户接下来要生成什么交付物。"},{"type":"tool_use","name":"AskQuestion","input":{"questions":[{"id":"deliverable","prompt":"基于以上 11 项决策,接下来产出什么?","options":[{"id":"rewrite_doc","label":"改写《钱包系统V2.md》按 11 项决策修订冲突条款,补明细表/生命周期/幂等/边界/单位,加状态机与时序说明"},{"id":"impl_plan","label":"产出落地改造方案与改动清单(按现网文件逐个列要改什么,不写代码)"},{"id":"both","label":"两份都要:先改文档,再出改造清单"},{"id":"conflict_table","label":"只要「V2 vs 现网」差异对照表(含本次 11 项结论)供评审留档"}]}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"明白,我按 11 项决策重写整份文档。\n\n I need to rewrite the wallet system documentation comprehensively, incorporating the 11 key decisions and adding the missing sections on detailed ledger, lifecycle, idempotency, boundaries, and units. I'll also need to add state machine diagrams and sequence diagrams to clarify the system behavior. The main structural changes include fixing the deposit_balance definition in section 3.1 and ensuring all the specifications are internally consistent throughout the document. I'm structuring the full document with clear sections covering background, design goals, amount and concurrency conventions, table responsibilities with updated field semantics, balance field mappings, deposit and bonus accounting rules, pure recharge without bonus handling, multi-deposit scenarios with progress groups, bet lifecycle management, betting rules with deduction order and detail logging, prize attribution based on snapshot progress groups, completion and expiration rules, settlement flow with proper sequencing, account field updates, idempotency and concurrency control, state machines for groups and lots, example workflows, items deferred to future phases, and a final rules summary—all aligned with existing codebase naming conventions like biz_id, is_end, round_id, version-based optimistic locking, and group locking strategies."},{"type":"tool_use","name":"Write","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","contents":"# 钱包 Deposit + Bonus 打码与解锁需求文档V2\n\n> 本版已对照现网 `slot-wallet` 代码(`BetService`、`WinService`、`WalletBetWinLogic`、`WalletRechargeLogic`、`RechargeFundLotService` 及 `wallet_account` / `wallet_fund_lot` / `wallet_fund_lot_group` 三张 Model评审修订。\n> 凡与现网行为不一致之处,本文档已标注「与现网差异」并给出本期最终口径。\n\n---\n\n## 1. 背景\n\n平台钱包需要支持类似 DraftKings 的 Deposit Bonus 玩法。\n\n用户充值时可能同时获得 Bonus例如\n\n```text\n充值 100\n赠送 Bonus 50\n打码要求 250\n```\n\n该笔 `Deposit + Bonus` 不拆成普通 Cash 和普通 Bonus 两个独立余额,而是作为一个整体打码任务(打码组)处理。\n\n- 任务未完成前,组内资金可下注、不可提现。\n- 任务完成后,组内剩余资金整体转为 Cash可提现。\n\n---\n\n## 2. 设计目标\n\n1. 单笔 `Deposit + Bonus` 作为一个整体打码组。\n2. 多笔 `Deposit + Bonus` 按 FIFO 顺序推进。\n3. 下注流水优先贡献当前组(`progress_group`)。\n4. 组内资金消耗顺序:先 Deposit再 Bonus。\n5. 单笔下注可跨组、跨 Cash 补足。\n6. 单笔下注的打码进度只归属 `progress_group`。\n7. 派奖只归属下注时记录的 `progress_group`。\n8. 派奖不新增 Lot累加到该组已有 Deposit / Bonus Lot。\n9. 组完成后整组剩余金额转入可提现 Cash。\n10. 组内资金消耗完且未达标时任务失效PlayedOut。\n11. **不修改 `wallet_account` / `wallet_fund_lot` / `wallet_fund_lot_group` 三张核心表结构**,仅重新定义字段业务语义;**允许新增一张下注扣款明细表**(见 §4.4),用于派奖归属、回滚预留与对账。\n\n> 与现网差异(本期最终口径,已与产品确认):\n> - **充值 Bonus 落桶**:进 `deposit_balance`,不进 `frozen_bonus`§6、§11。\n> - **下注扣款顺序**Cash 最后扣、组优先§10.3与现网「Cash 先扣、类型优先」相反。\n> - **派奖归属**按本局下注命中关系§11下线现网「账户状态路由」与「按 bet 比例拆分」两套旧逻辑。\n> - **有效投注进度**:整笔只计 `progress_group`§10.1),不再按资金来源分摊到各自组。\n\n---\n\n## 3. 通用约定(金额 / 币种 / 并发 / 幂等)\n\n```text\n金额单位\n= 最小货币单位,存储放大 1000 倍\n= 19000 表示 $19.00\n\n币种\n= 所有表、查询、流水均带 currency 维度,按币种独立结算\n\n分表\n= wallet 库按 uid 路由分表:\n wallet_account_xx / wallet_fund_lot_xx / wallet_fund_lot_group_xx\n / wallet_log_xx / wallet_bet_detail_xx\n```\n\n并发控制\n\n```text\n1. 钱包聚合行操作使用行锁lockWallet+ 乐观锁 version。\n2. 打码组、资金 Lot 更新均带 version 乐观锁,冲突时由 maxRetry 重试。\n3. 一次下注 / 一次最终结算各为一个数据库事务。\n```\n\n幂等\n\n```text\nbet 幂等键 = biz_id + biz_type(bet) wallet_log 唯一)\nwin 幂等键 = biz_id + biz_type(win) wallet_log 唯一)\n中间 win(is_end=0) 去重 = round_id + biz_idRedis dedupe\n派奖累加到 Lot remaining 为非幂等加法,必须在 win 幂等判重通过后才执行。\n```\n\n---\n\n## 4. 现有表职责\n\n### 4.1 `wallet_account_xx`\n\n用户钱包聚合余额表快照事实来源是 Lot + Group + 流水 + 下注明细)。\n\n字段业务语义\n\n```text\nwithdraw_balance\n= Cash Balance / 现金余额\n= 可下注、可提现\n\ndeposit_balance\n= Locked Play Balance / 锁定可玩余额\n= 未完成打码组内的 Deposit + Bonus + 归属该组的派奖\n= 可下注、不可提现\n= 不变量deposit_balance = Σ(进行中 Lot.remaining_amount)(含组内 Deposit/Bonus 与已回流派奖)\n```\n\n其他字段\n\n```text\nfrozen_bonus\n= 仅承载 Trial 体验金与「非充值」直接发奖 Bonus\n= 本期充值 Bonus 不再写入此字段(见 §6\n\navailable_bonus\n= 待领取奖励池,不参与下注主链路\n\nbet_locked_balance\n= 下注冻结余额,多阶段结算游戏预留(本期不变更)\n\nwithdraw_lock\n= 提现审核冻结金额\n```\n\n> 与现网差异:现网充值 Bonus 写入 `frozen_bonus`;本期改为写入 `deposit_balance`。\n> `frozen_bonus` **仅在充值场景**停用Trial 体验金(`executeTrialBet/Win`)及其它纯 Bonus 发奖仍使用,不全局废弃。\n\n用户侧余额展示\n\n```text\n可玩余额 = withdraw_balance + deposit_balance + frozen_bonus\n可提现余额 = withdraw_balance\n锁定可玩余额 = deposit_balance\nBonus Credits如展示= frozen_bonus\n```\n\n---\n\n### 4.2 `wallet_fund_lot_group_xx`\n\n钱包打码组表。一笔 `Deposit + Bonus` 对应一个打码组。\n\n核心字段语义\n\n```text\ngroup_no = 打码组编号(充值 order_no / 注册 biz_id对外唯一\nsource_type = 建组来源类型1 注册4 充值\ndeposit_amount = 组内 Deposit 本金合计\nbonus_amount = 组内 Bonus 合计\nrequired_wager = 整组所需有效投注额(建组时定,= 各成员所需之和)\ncurrent_wager = 整组已完成有效投注额(权威解锁进度)\nstatus = 2 Active / 4 Completed / 5 PlayedOut\ncreated_at = FIFO 排序依据\nversion = 乐观锁\n```\n\n`required_wager` 计算(与现网一致):\n\n```text\ndeposit_required = deposit_amount × wager_rate wager_rate 默认 1\nbonus_required = bonus_amount × bonus_wagering_multiplier倍数为 0 时取 bonus_amount\nrequired_wager = deposit_required + bonus_required\n```\n\n---\n\n### 4.3 `wallet_fund_lot_xx`\n\n钱包资金 Lot 表。`lot_type` 保持两值:\n\n```text\nlot_type = 1 Deposit 侧资金 = 原始充值本金 + 归属 Deposit 侧的派奖\nlot_type = 2 Bonus 侧资金 = Bonus 额度 + 归属 Bonus 侧的派奖\n```\n\n不新增 `3 Deposit Winning` / `4 Bonus Winning`。原因:整组完成后 Deposit 侧与 Bonus 侧剩余统一转 Cash无需区分本金与 winnings。\n\n字段说明与派奖相关\n\n```text\nremaining_amount = 当前剩余金额;派奖累加进此字段,可能大于 original_amount\nrequired_wager = 该 Lot 所需有效投注额(建组时拆分)\ncurrent_wager = 该 Lot 自身已完成有效投注额\n = 本期仅用于玩家任务页展示与对账,不作为整组解锁判据\ngroup_no = 所属打码组编号;纯充值无 Bonus 时为空串(不分组)\nstatus = 见 §13.2\n```\n\n> 与现网差异:现网整组/单 Lot 解锁同时看 Lot 级与组级进度;本期统一以**组级** `current_wager` 为权威解锁判据§12Lot 级 `current_wager` 仅展示。\n\n---\n\n### 4.4 `wallet_bet_detail_xx`(本期新增)\n\n下注扣款明细表。用于①派奖按本局命中关系归属②回滚 / 对账 / 风控(回滚本期不实现,仅预留)。\n\n建议字段\n\n```text\nid\nuid\ncurrency\nbet_order_no = 本次下注业务单号(= bet 的 biz_id\nround_id = 游戏局号(派奖按此反查本局命中)\nprovider_tx_id = 三方交易号(对账用)\nprogress_group_no = 下注时快照的 progress_group 编号(派奖归属依据,见 §11\ndeduct_group_no = 本条扣款命中的资金所属组Cash 为空串)\nlot_no = 本条扣款命中的 Lot 编号Cash 为空串)\nlot_type = 1 Deposit / 2 BonusCash 为 0\nfund_source = 1 Deposit / 2 Bonus / 3 Cash\ndeduct_amount = 本条扣款金额\ndeduct_order = 本笔下注内的扣款顺序号(从 1 递增)\ncreated_at\n```\n\n索引建议`KEY idx_uid_round (uid, currency, round_id)`、`KEY idx_bet_order (bet_order_no)`。\n\n```text\n一笔下注命中多个资金来源跨组 + Cash按命中顺序写多条明细。\n派奖结算时按 (uid, currency, round_id) 反查这些明细判断命中关系。\n```\n\n---\n\n## 5. 余额字段对应关系\n\n```text\nCash Balance → wallet_account.withdraw_balance\nLocked Play Balance → wallet_account.deposit_balance\nOriginal Deposit → wallet_fund_lot.lot_type = 1\nCasino Bonus Funds → wallet_fund_lot.lot_type = 2\nDeposit 侧派奖 → 累加到 lot_type = 1 的 remaining_amount\nBonus 侧派奖 → 累加到 lot_type = 2 的 remaining_amount\nPlay-through Requirement → wallet_fund_lot_group.required_wager\nPlay-through Progress → wallet_fund_lot_group.current_wager\n```\n\n---\n\n## 6. Deposit + Bonus 入账规则\n\n用户充值并获得 Bonus例如 `充值 100 / Bonus 50 / 打码要求 250`。\n\n### 6.1 更新账户聚合余额\n\n```text\nwallet_account.deposit_balance += 150 = deposit 100 + bonus 50\n```\n\n不更新 `withdraw_balance`,也不更新 `frozen_bonus`。\n\n原因\n\n```text\n这笔 Deposit 已参与 Bonus 活动,打码完成前不是可提现 Cash而是 Locked Play Balance\n整组Deposit + Bonus统一锁定在 deposit_balance。\n```\n\n> 与现网差异:现网 `incBalance(0, deposit, bonus)` 把 bonus 加到 `frozen_bonus`;本期 bonus 改加到 `deposit_balance`。\n\n### 6.2 创建打码组\n\n写入 `wallet_fund_lot_group_xx`\n\n```text\ngroup_no = 充值订单号\nsource_type = 4 充值\ndeposit_amount = 100\nbonus_amount = 50\nrequired_wager = 250\ncurrent_wager = 0\nstatus = 2 Active\n```\n\n### 6.3 创建资金 Lot\n\n```text\nlot_type = 1 original_amount = 100 remaining_amount = 100 group_no = 当前 group_no status = Active/Waiting\nlot_type = 2 original_amount = 50 remaining_amount = 50 group_no = 当前 group_no status = Active/Waiting\n```\n\n初始状态遵循现网 `resolveInitialLotStatus`:全局仅一个 Active 子任务,其余 Waiting。\n\n---\n\n## 7. 无 Bonus 纯充值规则\n\n`bonus_amount = 0` 的纯充值(未参与 Bonus 活动)保持现网处理:\n\n```text\nwallet_account.deposit_balance += deposit_amount\n建 Deposit Lotlot_type = 1required_wager = deposit × wager_rate默认 1x\ngroup_no = 空串(不建打码组)\n```\n\n解锁路径达标后由不分组 Lot 收敛(现网 `finalizeUngroupedLots`)直接转入 `withdraw_balance`,不参与 §6 的组模型。\n\n---\n\n## 8. 多笔 Deposit + Bonus 与 progress_group\n\n每笔充值赠送创建独立打码组系统不合并\n\n```text\nA组Deposit 100 + Bonus 50\nB组Deposit 200 + Bonus 100\nC组Deposit 50 + Bonus 10\n```\n\n推进顺序A 完成/失效 → B → C。\n\n当前打码组`progress_group`)查询:\n\n```sql\nSELECT *\nFROM wallet_fund_lot_group_xx\nWHERE uid = ? AND currency = ? AND status = 2\nORDER BY created_at ASC, id ASC\nLIMIT 1;\n```\n\n对应现网 `WalletFundLotGroupModel::lockEarliestActiveGroup`。\n\n---\n\n## 9. 局生命周期(沿用现网)\n\n终态判定Completed / PlayedOut只在**最终结算**触发:\n\n```text\n1. bet 扣款、写下注明细、累加 progress_group.current_wager\n2. win 且 is_end = 0 中间派奖,仅累计到 Redis pending不落账、不结算\n3. win 且 is_end = 1 最终结算:本局总派奖 = pending + 本次 fee\n → 处理派奖归属\n → 判定整组 Completed / PlayedOut\n```\n\n要求**每一局结束都有一个 `is_end = 1` 的结算回调(输局为 `is_end = 1` 且 `fee = 0`**。\n输局即资金耗尽的组靠此回调触发 PlayedOut 判定;若三方未发 `is_end = 1`,组将停留 Active依赖三方协议保证本期不另设兜底。\n\n---\n\n## 10. 下注规则\n\n### 10.1 progress_group 与进度归属\n\n下注时取 FIFO 最早 Active 组为本笔 `progress_group`,并写入本次下注的所有明细行(`progress_group_no`)。\n\n本笔有效投注额**整笔只计 progress_group**,即使资金来自后续组或 Cash\n\n```text\nprogress_group.current_wager += valid_bet_amount\n```\n\n> 与现网差异:现网把各资金来源的有效投注计入各自所属组,仅 Cash 部分计入最早组;本期改为整笔只计 `progress_group`。\n> Lot 级 `current_wager` 仍按命中各 Lot 累加,仅用于展示/对账,不影响解锁。\n\n### 10.2 组内资金消耗顺序\n\n```text\n1. Deposit 侧资金lot_type = 1\n2. Bonus 侧资金lot_type = 2\n```\n\n### 10.3 多资金来源扣款顺序组优先Cash 最后)\n\n当本笔下注金额大于当前组剩余时按下列顺序补足\n\n```text\n1. progress_group 的 Deposit\n2. progress_group 的 Bonus\n3. 后续组的 Deposit\n4. 后续组的 Bonus\n5. Cashwithdraw_balance\n```\n\n> 与现网差异现网为「Cash → 所有组 Deposit → 所有组 Bonus」Cash 先扣、类型优先本期改为「组优先、Cash 最后」。\n\n示例\n\n```text\n第一组Deposit 5 / Bonus 1\n第二组Deposit 8 / Bonus 2\nCash 4\n下注 20\n\n扣款第一组 Deposit 5 → 第一组 Bonus 1 → 第二组 Deposit 8 → 第二组 Bonus 2 → Cash 4 = 20\nprogress_group = 第一组;有效投注 20 全部计入第一组\n```\n\n### 10.4 下注落库\n\n每条扣款写一条 `wallet_bet_detail`(含 `progress_group_no`、`deduct_group_no`、`lot_no`、`lot_type`、`fund_source`、`deduct_amount`、`deduct_order`)。账户聚合按桶扣减:\n\n```text\n扣 Deposit / Bonus Lot → deposit_balance -= 该部分合计\n扣 Cash → withdraw_balance -= 该部分合计\n```\n\n---\n\n## 11. 派奖归属规则\n\n### 11.1 命中归属(不按比例拆)\n\n本笔派奖整笔归属依据**下注明细快照**的 `progress_group_no` 及本局命中关系:\n\n```text\n按 (uid, currency, round_id) 反查本局下注明细:\n\n1. 若本局命中了 progress_group 的 Deposit存在 progress_group_no = X 且 lot_type = 1 的扣款)\n → 整笔派奖归属该组 Deposit 侧。\n\n2. 否则若命中了 progress_group 的 Bonuslot_type = 2\n → 整笔派奖归属该组 Bonus 侧。\n\n3. 否则(本局为纯 Cash 下注,无 progress_group_no\n → 整笔派奖进 withdraw_balance。\n```\n\n> 与现网差异:下线现网两套旧逻辑——\n> ① `WinService::decideWinBucket` 按账户状态路由;\n> ② `WalletRechargeLogic::getWinAllocationByRound` 按 bet 比例拆分。\n> 本期统一改为「命中归属」。\n\n### 11.2 归属组已终态时的处理\n\n下注与本局最终结算之间快照的 `progress_group` 可能已被其它并发局推进:\n\n```text\n若快照组 status = Completed已转 Cash、Lot 已清零)→ 本笔派奖进 withdraw_balance。\n若快照组 status = PlayedOut已失效 → 本笔派奖进 withdraw_balance。\n```\n\n> 风控提示PlayedOut 组派奖进可提现属本期产品取舍,需在风控侧关注「失效组仍产出可提现派奖」的异常下注模式。\n\n### 11.3 派奖入账方式(组仍 Active\n\n派奖不新增 Lot累加到 `progress_group` 下已有的对应 Lot\n\n```text\n命中 Depositprogress_group 下 lot_type = 1 的 remaining_amount += payout\n命中 Bonus progress_group 下 lot_type = 2 的 remaining_amount += payout\n同时 wallet_account.deposit_balance += payout\n```\n\n要求\n\n```text\n1. 不新增派奖 Lot不新增 Deposit/Bonus Winning Lot。\n2. 不按比例拆派奖。\n3. 派奖只累加到 progress_group 下已有的对应 Lot。\n4. win 幂等判重通过后才执行累加(避免重复派奖)。\n```\n\n### 11.4 示例\n\n```text\n第一组Deposit 5 / Bonus 1 第二组Deposit 8 / Bonus 2 Cash 4 ;下注 20\n扣款命中第一组 Depositprogress_group = 第一组。\n本局派奖 50 → 全部归属第一组 Deposit 侧:\n 第一组 lot_type=1 的 remaining_amount += 50\n wallet_account.deposit_balance += 50\n```\n\n---\n\n## 12. 打码完成规则\n\n最终结算`is_end = 1`)后判断当前 Active 组是否达标:\n\n```text\ncurrent_wager >= required_wager\n```\n\n达标则\n\n```text\nwallet_fund_lot_group.status = 4 Completed\nwallet_fund_lot_group.completed_at = now()\n```\n\n整组剩余资金转入 Cash\n\n```text\ngroup_remaining = Σ(组内 Lot.remaining_amount)\nwallet_account.deposit_balance -= group_remaining\nwallet_account.withdraw_balance += group_remaining\n组内所有有效 Lot 状态 → Completedremaining 清零\n```\n\n完成后该组不再参与下注下一笔下注推进下一个 Active 组。\n\n> 解锁顺序沿用现网 `unlockGroupMembers`:成员按 Deposit 在前、Bonus 在后依次解锁。\n\n---\n\n## 13. 任务失效规则与状态定义\n\n### 13.1 失效条件PlayedOut\n\n```text\ngroup_remaining = 0 AND current_wager < required_wager\n→ wallet_fund_lot_group.status = 5 PlayedOut\n 组内 Bonus 成员 Lot → PlayedOutDeposit 成员保持原状态)\n```\n\n含义资金用尽但未达标任务失效不再参与后续下注与打码。\n\n### 13.2 最终结算判断顺序\n\n每个 `is_end = 1` 结算按此顺序:\n\n```text\n1. bet 阶段已扣资金、已累加 current_wager、已写明细\n2. 处理派奖归属§11\n3. 判断是否 Completed§12\n4. 未 Completed 再判断是否 PlayedOut§13.1\n```\n\n不在扣款后立即判 PlayedOut——本局可能有派奖回流救活该组。\n\n### 13.3 Group 状态\n\n```text\n2 = Active 进行中,参与 FIFO 打码排序\n4 = Completed 已达标,组内剩余已转 withdraw_balance\n5 = PlayedOut 资金用尽未达标,任务失效\n```\n\n### 13.4 Lot 状态(沿用现网)\n\n```text\n1 Waiting / 2 Active / 3 PendingConversion / 4 Completed / 5 PlayedOut / 6 Cancelled / 7 Reversed\n```\n\n本期主要使用`2 Active`(可扣减)、`4 Completed`(所属组已完成转 Cash、`5 PlayedOut`(资金用尽)。`6 Cancelled` / `7 Reversed` 为回滚预留(本期不触发,见 §18。\n\n---\n\n## 14. 结算流程\n\n### 14.1 用户下注bet\n\n输入`uid / currency / bet_amount / round_id / biz_id / provider_tx_id`\n\n```text\n1. 锁定 wallet_account 行bet 幂等判重biz_id + bet。\n2. 查询 FIFO 最早 Active 组,作为 progress_group。\n3. 按 §10.3 扣款顺序拆分扣减 Lot 与 Cash。\n4. 写 wallet_bet_detail每条扣款一行含 progress_group_no。\n5. 更新 deposit_balance / withdraw_balance。\n6. progress_group.current_wager += valid_bet_amount整笔只计该组。\n7. 写 bet 流水,提交事务。\n```\n\n### 14.2 游戏派奖win\n\n输入`uid / currency / payout_amount / round_id / biz_id / provider_tx_id / is_end`\n\n```text\nis_end = 0仅累计 Redis pending返回不落账、不结算。\n\nis_end = 1最终结算\n1. 锁定 wallet_account 行win 幂等判重biz_id + win。\n2. payout = pending + 本次 fee。\n3. 按 (uid, currency, round_id) 反查下注明细,取快照 progress_group_no。\n4. 按 §11 决定派奖落桶(命中 Deposit/Bonus 累加对应 Lot纯 Cash 或组已终态 → withdraw_balance。\n5. 判断该组 Completed§12未完成再判 PlayedOut§13.1)。\n6. 写 win 流水,提交事务,清理 Redis pending。\n```\n\n---\n\n## 15. Account 字段更新规则\n\n```text\n15.1 Deposit + Bonus 入账 deposit_balance += deposit_amount + bonus_amount\n15.2 锁定组下注扣款 deposit_balance -= 命中 Lot 扣款合计\n15.3 Cash 下注扣款 withdraw_balance -= Cash 扣款合计\n15.4 锁定组派奖(组 Activedeposit_balance += payout并累加对应 Lot.remaining_amount\n15.5 Cash 派奖 / 组已终态 withdraw_balance += payout\n15.6 打码组完成 deposit_balance -= group_remainingwithdraw_balance += group_remaining\n15.7 打码组 PlayedOut 组内资金已为 0deposit_balance 无需额外扣减group.status = 5\n```\n\n---\n\n## 16. 幂等与并发要点\n\n```text\n1. bet / win 各自 biz_id 幂等,重复请求直接返回当前余额。\n2. 派奖累加 Lot.remaining 为非幂等加法,必须在 win 幂等判重之后执行。\n3. 钱包行 lockWallet + version 乐观锁Lot / Group 更新带 version冲突 maxRetry 重试。\n4. 中间派奖is_end = 0按 round_id + biz_id 在 Redis 去重累计。\n5. 不变量deposit_balance 始终等于 Σ(进行中 Lot.remaining_amount)。\n```\n\n---\n\n## 17. 示例流程\n\n### 17.1 入账\n\n```text\n充值 Deposit 100 / Bonus 50 / Required 250\n账户withdraw_balance = 0 deposit_balance = 150\nGroupdeposit_amount=100 bonus_amount=50 required_wager=250 current_wager=0 status=Active\nLot lot_type=1 remaining=100 lot_type=2 remaining=50\n```\n\n### 17.2 下注并派奖\n\n```text\n下注 20 → 扣 lot_type=1 20\n账户deposit_balance -= 20= 130\n组 current_wager += 20\n明细1 行progress_group_no=本组, lot_type=1, deduct_amount=20\n\nis_end=1 派奖 40命中本组 Deposit → 本组 lot_type=1 remaining += 40deposit_balance += 40= 170\n不新增派奖 Lot。\n```\n\n### 17.3 完成打码\n\n```text\ncurrent_wager >= required_wagergroup_remaining = 170\ndeposit_balance -= 170 → 0\nwithdraw_balance += 170 → 170\ngroup.status = Completed\n```\n\n### 17.4 PlayedOut\n\n```text\n组内 remaining = 0 且 current_wager < required_wager\ngroup.status = PlayedOut\n下一笔下注推进下一个 Active 组\n```\n\n---\n\n## 18. 本期不处理内容\n\n```text\n1. Casino Credits 独立玩法\n2. 用户主动放弃 Bonus\n3. Bonus 过期自动失效\n4. 分游戏权重打码\n5. 部分解锁\n6. 多币种复杂汇率\n7. Casino Credits 开关 / 指定游戏使用\n8. 游戏回滚cancel roundbet 回滚退资金、win 回滚冲派奖、已解锁组补偿\n —— 本期不实现wallet_bet_detail 已预留回滚/对账字段Lot 保留 Cancelled/Reversed 状态供后续扩展。\n```\n\n---\n\n## 19. 最终规则总结\n\n```text\n1. withdraw_balance = Cash可下注可提现。\n2. deposit_balance = Locked Play可下注不可提现= Σ 进行中 Lot.remaining。\n3. 充值 Bonus 进 deposit_balance不进 frozen_bonusfrozen_bonus 仅 Trial / 非充值发奖用)。\n4. 单笔 Deposit + Bonus 建一个独立打码组;无 Bonus 纯充值不建组、走不分组解锁。\n5. 多组按 created_at ASC, id ASC FIFO 推进。\n6. 下注扣款顺序:当前组 Deposit → 当前组 Bonus → 后续组 → CashCash 最后、组优先)。\n7. 本笔有效投注整笔只计 progress_group组级 current_wager 为权威进度)。\n8. 下注每条扣款写 wallet_bet_detail快照 progress_group_no。\n9. 派奖按下注明细命中关系归属,不按比例拆:命中 Deposit→该组 Deposit 侧;命中 Bonus→该组 Bonus 侧;纯 Cash→withdraw。\n10. 快照组已 Completed/PlayedOut 时派奖进 withdraw_balance。\n11. 派奖不新增 Lot累加到 progress_group 下已有对应 Lot 的 remaining_amount。\n12. 终态判定Completed/PlayedOut只在 is_end=1 最终结算执行;判断顺序:派奖 → Completed → PlayedOut。\n13. 打码完成后整组剩余 Deposit + Bonus 统一转入 withdraw_balance。\n14. 组内资金用尽未达标 → PlayedOut后续不再参与下注。\n15. 下线现网 win 状态路由与比例拆分;不修改三张核心表结构,新增 wallet_bet_detail。\n```\n"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"已按 11 项决策重写 `docs/requirements/钱包系统V2.md`。主要改动:\n\n**修订冲突条款(标注「与现网差异」)**\n- §4.1 / §6充值 Bonus 改进 `deposit_balance``frozen_bonus` 仅充值场景停用、Trial 与其它发奖保留Q1、Q11。\n- §10.3扣款顺序改为「组优先、Cash 最后」Q2。\n- §10.1:有效投注整笔只计 `progress_group`Lot 级 `current_wager` 降级为展示/对账Q3。\n- §11派奖改「本局命中归属」明确下线 `WinService` 状态路由与 `getWinAllocationByRound` 比例拆分Q4。\n\n**新增/补全**\n- §4.4:新增 `wallet_bet_detail_xx` 下注明细表(字段+索引),作派奖归属/回滚预留/对账唯一事实源§2.11 措辞改为「不改三张核心表、允许新增明细表」Q5。\n- §9补局生命周期`bet → is_end=0 → is_end=1` 结算),终态只在 `is_end=1` 判Q6。\n- §11.1:派奖认下注快照 `progress_group_no`、不重查Q7§11.2:快照组已 Completed/PlayedOut → 进 `withdraw_balance`并加风控提示Q8。\n- §7补无 Bonus 纯充值口径Q10。\n- §18游戏回滚明确列入本期不做、明细表/Lot 状态预留Q9。\n- §3 通用约定(金额 ×1000、币种、分表、并发乐观锁、§16 幂等与并发要点、§13.2 判断顺序、`deposit_balance = Σ 进行中 Lot.remaining` 不变量。\n\n附带项注册纯 Bonus 组、`bet_locked_balance` 本期不变、win 幂等沿用 `biz_id`)已并入相应章节。\n\n说明本次只改了 `.md` 文档,未改动任何 `app/**/*.php`,故后端 verify 门禁/`php -l` 不适用(无 PHP 变更)。\n\n需要我接着出「落地改造方案与改动清单」吗\n\n The documentation file is complete, and since no PHP code was modified, the backend verification script won't be triggered by this change."}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<timestamp>Thursday, Jun 11, 2026, 2:27 PM (UTC+8)</timestamp>\n<user_query>\n本期不新增 wallet_bet_detail_xx 这个表,直接在wallet_log_01新增一个json字段记录简化一下\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" The user wants to store bet deduction details as JSON within the `wallet_log_01` table instead of creating a separate `wallet_bet_detail_xx` table. I need to examine the current WalletLogModel structure to see how to integrate this JSON field."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/slot-wallet/app/model/multi/WalletLogModel.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"明白,去掉新表,改为在 `wallet_log` 加一个 JSON 字段记录。我来改文档对应章节。\n\n The user wants to consolidate the design by storing bet detail information directly in a JSON column on the `wallet_log_01` table rather than creating a separate detail table. Instead of adding `wallet_bet_detail_xx`, I'll add a dedicated JSON field like `fund_detail` or `detail_json` to capture the bet deduction breakdown, which currently gets stored in the `remark` field through the buildRemark function.\n\nFor the payout settlement flow, when a win needs to look up the corresponding bet log to retrieve its detail, there's a challenge: `wallet_log` doesn't have a `round_id` column indexed, so querying by round_id would require either adding `round_id` as an indexed column or searching through the JSON field itself, which is inefficient. I need to figure out the simplest approach that aligns with the user's goal of minimizing complexity. So the user wants to keep it simple — just add the JSON field to wallet_log without creating a separate table. For the win反查 flow, I'll specify that it locates the bet's wallet_log row using round_id, and I should note that including round_id in the JSON field itself enables this lookup. I'll update the documentation to remove the wallet_bet_detail table section and clarify that this is just a wallet_log extension, not a structural change to the core tables. The tension here is that persisting to wallet_log JSON alone doesn't give us an efficient reverse lookup by round_id without adding an index. But the user explicitly wants simplification with just one JSON field. I'll go with the pragmatic approach: store the round_id inside the JSON as part of the persistent record, use Redis as the primary lookup layer for speed (keyed by round_id with a TTL), and fall back to account state or conservative withdrawal if Redis misses during a restart. This keeps the schema change minimal while acknowledging that the JSON serves as the durable audit trail rather than the hot path. I'll stick with the user's explicit constraint: one JSON field in wallet_log containing round_id, hit details, and progress_group_no. For lookups, I'll rely on Redis as the index layer (round_id → bet log reference), with the JSON field as the persistent fallback; if Redis misses, I'll gracefully degrade to a conservative withdrawal attribution and log an alert for reconciliation. Now I need to update the doc systematically—removing references to the new detail table, replacing them with wallet_log's JSON extension, and adjusting the lookup and attribution logic throughout the relevant sections. Updating the documentation..."},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"11. **不修改 `wallet_account` / `wallet_fund_lot` / `wallet_fund_lot_group` 三张核心表结构**,仅重新定义字段业务语义;**允许新增一张下注扣款明细表**(见 §4.4),用于派奖归属、回滚预留与对账。","new_string":"11. **不修改 `wallet_account` / `wallet_fund_lot` / `wallet_fund_lot_group` 三张核心表结构**,也**不新增明细表**;仅重新定义字段业务语义,并**给流水表 `wallet_log` 增加一个 JSON 字段**`fund_detail`,见 §4.4)记录下注命中明细,用于派奖归属、回滚预留与对账。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"分表\n= wallet 库按 uid 路由分表:\n wallet_account_xx / wallet_fund_lot_xx / wallet_fund_lot_group_xx\n / wallet_log_xx / wallet_bet_detail_xx\n```","new_string":"分表\n= wallet 库按 uid 路由分表:\n wallet_account_xx / wallet_fund_lot_xx / wallet_fund_lot_group_xx / wallet_log_xx\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now replace the §4.4 table section with the `wallet_log` JSON-field design:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"### 4.4 `wallet_bet_detail_xx`(本期新增)\n\n下注扣款明细表。用于①派奖按本局命中关系归属②回滚 / 对账 / 风控(回滚本期不实现,仅预留)。\n\n建议字段\n\n```text\nid\nuid\ncurrency\nbet_order_no = 本次下注业务单号(= bet 的 biz_id\nround_id = 游戏局号(派奖按此反查本局命中)\nprovider_tx_id = 三方交易号(对账用)\nprogress_group_no = 下注时快照的 progress_group 编号(派奖归属依据,见 §11\ndeduct_group_no = 本条扣款命中的资金所属组Cash 为空串)\nlot_no = 本条扣款命中的 Lot 编号Cash 为空串)\nlot_type = 1 Deposit / 2 BonusCash 为 0\nfund_source = 1 Deposit / 2 Bonus / 3 Cash\ndeduct_amount = 本条扣款金额\ndeduct_order = 本笔下注内的扣款顺序号(从 1 递增)\ncreated_at\n```\n\n索引建议`KEY idx_uid_round (uid, currency, round_id)`、`KEY idx_bet_order (bet_order_no)`。\n\n```text\n一笔下注命中多个资金来源跨组 + Cash按命中顺序写多条明细。\n派奖结算时按 (uid, currency, round_id) 反查这些明细判断命中关系。\n```","new_string":"### 4.4 `wallet_log` 扩展:新增 `fund_detail` JSON 字段(本期唯一表变更)\n\n本期**不新增明细表**,仅给现有流水表 `wallet_log_xx` 增加一个 JSON 字段记录本笔下注命中明细,用于派奖归属、回滚预留与对账。\n\n```sql\nALTER TABLE wallet_log_01\n ADD COLUMN fund_detail JSON DEFAULT NULL COMMENT '下注命中明细(仅 bet 行写入)';\n-- 各分表同步 ALTER\n```\n\n写入时机仅 `biz_type = bet` 的流水行写 `fund_detail`;其它行保持 NULL。\n\n`fund_detail` 结构:\n\n```json\n{\n \"round_id\": \"本局局号\",\n \"progress_group_no\": \"下注时快照的 progress_group 编号(纯 Cash 下注为空串)\",\n \"deductions\": [\n {\n \"fund_source\": 1, \"// 1 Deposit / 2 Bonus / 3 Cash\",\n \"deduct_group_no\": \"命中资金所属组Cash 为空串)\",\n \"lot_no\": \"命中 Lot 编号Cash 为空串)\",\n \"lot_type\": 1, \"// 1 Deposit / 2 BonusCash 为 0\",\n \"deduct_amount\": 20,\n \"deduct_order\": 1 \"// 本笔下注内扣款顺序,从 1 递增\"\n }\n ]\n}\n```\n\n```text\n一笔下注命中多个资金来源跨组 + Cashdeductions 写多条,按命中顺序排列。\nfund_detail 是派奖归属与对账的「持久事实源」。\n```\n\n派奖反查关键约束\n\n```text\n1. wallet_log 现无 round_id 列、不便按 round_id 走索引反查 fund_detail。\n2. 本期为反查加一条 Redis 命中摘要bet 时按 round_id 写\n { progress_group_no, hit_side }hit_side = deposit / bonus / cashTTL 与一局结算窗口匹配)。\n3. win 最终结算时优先读 Redis 摘要决定归属§11。\n4. Redis 摘要缺失(过期 / 重启 / 驱逐)时,派奖归属保守降级为进 withdraw_balance\n 并记告警fund_detail 仍持久落库,供事后对账与(后续)回滚。\n```\n\n> 取舍说明相比独立明细表本方案以「Redis 摘要为反查主路径 + wallet_log JSON 为持久事实源」简化实现,代价是 Redis 摘要缺失时归属降级(保守进可提现)。此为本期产品选择。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now update §10.4 (下注落库), §11 (反查), and §14 (流程):"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"### 10.4 下注落库\n\n每条扣款写一条 `wallet_bet_detail`(含 `progress_group_no`、`deduct_group_no`、`lot_no`、`lot_type`、`fund_source`、`deduct_amount`、`deduct_order`)。账户聚合按桶扣减:\n\n```text\n扣 Deposit / Bonus Lot → deposit_balance -= 该部分合计\n扣 Cash → withdraw_balance -= 该部分合计\n```","new_string":"### 10.4 下注落库\n\n本笔下注命中明细写入 bet 流水行的 `fund_detail` JSON含 `progress_group_no` 与各条 `deductions`,见 §4.4),并按 round_id 写一条 Redis 命中摘要供派奖反查。账户聚合按桶扣减:\n\n```text\n扣 Deposit / Bonus Lot → deposit_balance -= 该部分合计\n扣 Cash → withdraw_balance -= 该部分合计\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"本笔派奖整笔归属,依据**下注明细快照**的 `progress_group_no` 及本局命中关系:\n\n```text\n按 (uid, currency, round_id) 反查本局下注明细:\n\n1. 若本局命中了 progress_group 的 Deposit存在 progress_group_no = X 且 lot_type = 1 的扣款)\n → 整笔派奖归属该组 Deposit 侧。\n\n2. 否则若命中了 progress_group 的 Bonuslot_type = 2\n → 整笔派奖归属该组 Bonus 侧。\n\n3. 否则(本局为纯 Cash 下注,无 progress_group_no\n → 整笔派奖进 withdraw_balance。\n```","new_string":"本笔派奖整笔归属,依据**下注时记录**的 `progress_group_no` 及本局命中关系(主路径读 Redis 命中摘要,持久事实源为 bet 流水的 `fund_detail`\n\n```text\n按 round_id 读取本局命中摘要 { progress_group_no, hit_side }\n\n1. hit_side = deposit本局命中了 progress_group 的 Deposit\n → 整笔派奖归属该组 Deposit 侧。\n\n2. hit_side = bonus未命中 Deposit但命中 progress_group 的 Bonus\n → 整笔派奖归属该组 Bonus 侧。\n\n3. hit_side = cash纯 Cash 下注,无 progress_group_no\n → 整笔派奖进 withdraw_balance。\n\n4. 摘要缺失Redis 过期 / 重启)\n → 保守进 withdraw_balance并记告警fund_detail 仍可供事后对账。\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"1. 锁定 wallet_account 行bet 幂等判重biz_id + bet。\n2. 查询 FIFO 最早 Active 组,作为 progress_group。\n3. 按 §10.3 扣款顺序拆分扣减 Lot 与 Cash。\n4. 写 wallet_bet_detail每条扣款一行含 progress_group_no。\n5. 更新 deposit_balance / withdraw_balance。\n6. progress_group.current_wager += valid_bet_amount整笔只计该组。\n7. 写 bet 流水,提交事务。","new_string":"1. 锁定 wallet_account 行bet 幂等判重biz_id + bet。\n2. 查询 FIFO 最早 Active 组,作为 progress_group。\n3. 按 §10.3 扣款顺序拆分扣减 Lot 与 Cash。\n4. 更新 deposit_balance / withdraw_balance。\n5. progress_group.current_wager += valid_bet_amount整笔只计该组。\n6. 写 bet 流水,命中明细落入该行 fund_detail JSON按 round_id 写 Redis 命中摘要。\n7. 提交事务。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"is_end = 1最终结算\n1. 锁定 wallet_account 行win 幂等判重biz_id + win。\n2. payout = pending + 本次 fee。\n3. 按 (uid, currency, round_id) 反查下注明细,取快照 progress_group_no。\n4. 按 §11 决定派奖落桶(命中 Deposit/Bonus 累加对应 Lot纯 Cash 或组已终态 → withdraw_balance。\n5. 判断该组 Completed§12未完成再判 PlayedOut§13.1)。\n6. 写 win 流水,提交事务,清理 Redis pending。","new_string":"is_end = 1最终结算\n1. 锁定 wallet_account 行win 幂等判重biz_id + win。\n2. payout = pending + 本次 fee。\n3. 按 round_id 读 Redis 命中摘要,取 progress_group_no 与 hit_side缺失则降级见 §11。\n4. 按 §11 决定派奖落桶(命中 Deposit/Bonus 累加对应 Lot纯 Cash / 组已终态 / 摘要缺失 → withdraw_balance。\n5. 判断该组 Completed§12未完成再判 PlayedOut§13.1)。\n6. 写 win 流水,提交事务,清理 Redis pending 与命中摘要。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now update §16, §17.2, §18, §19 references to the table:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"明细1 行progress_group_no=本组, lot_type=1, deduct_amount=20","new_string":"明细bet 流水 fund_detail = {progress_group_no:本组, deductions:[{lot_type:1, deduct_amount:20, ...}]}"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"8. 游戏回滚cancel roundbet 回滚退资金、win 回滚冲派奖、已解锁组补偿\n —— 本期不实现wallet_bet_detail 已预留回滚/对账字段Lot 保留 Cancelled/Reversed 状态供后续扩展。","new_string":"8. 游戏回滚cancel roundbet 回滚退资金、win 回滚冲派奖、已解锁组补偿\n —— 本期不实现bet 流水 fund_detail 已记录命中明细供后续回滚/对账Lot 保留 Cancelled/Reversed 状态供后续扩展。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"8. 下注每条扣款写 wallet_bet_detail快照 progress_group_no。","new_string":"8. 下注命中明细写入 bet 流水 fund_detail JSON快照 progress_group_no并写 Redis 命中摘要供派奖反查。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"15. 下线现网 win 状态路由与比例拆分;不修改三张核心表结构,新增 wallet_bet_detail。","new_string":"15. 下线现网 win 状态路由与比例拆分;不修改三张核心表结构、不新增明细表,仅给 wallet_log 增加 fund_detail JSON 字段。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me verify no stale references to the old table remain:"},{"type":"tool_use","name":"Grep","input":{"pattern":"wallet_bet_detail|明细表","path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","output_mode":"content","-n":true}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"已按要求简化:去掉 `wallet_bet_detail_xx` 新表,改为在 `wallet_log` 加一个 `fund_detail` JSON 字段。剩余出现的「不新增明细表」都是有意表述。\n\n改动汇总\n- **§2.11 / §19.15**:口径改为「不新增明细表,仅给 `wallet_log` 加一个 `fund_detail` JSON 字段」。\n- **§3**:分表清单移除 `wallet_bet_detail_xx`。\n- **§4.4**:重写为「`wallet_log` 扩展」——给出 `ALTER ... ADD COLUMN fund_detail JSON`、JSON 结构(`round_id` + `progress_group_no` + `deductions[]`),仅 `bet` 行写入。\n- **派奖反查**:明确 `wallet_log` 无 `round_id` 索引,故主路径用 **Redis 命中摘要**bet 时按 `round_id` 写 `{progress_group_no, hit_side}``fund_detail` 作持久事实源Redis 摘要缺失时归属**保守降级进 `withdraw_balance` 并告警**(取舍说明已写明)。\n- **§10.4 / §11.1 / §14.1 / §14.2**:落库与结算流程同步改为写/读 `fund_detail` + Redis 摘要。\n- **§17.2 示例、§18 回滚、§19 总结**:引用同步更新。\n\n注本次仅改 `.md`,无 PHP 变更,后端 verify 门禁/`php -l` 不适用。\n\n需要我继续出「落地改造方案与改动清单」吗\n\n All the references are intentional—no stale table references to clean up. Since this is just a documentation change, the verification gate doesn't apply."}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<timestamp>Thursday, Jun 11, 2026, 2:35 PM (UTC+8)</timestamp>\n<user_query>\n充值还是走 deposit_balance + frozen_bonus 。 对功能有没有影响?\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"I'm in Plan mode now, so I'll analyze the impact and propose a plan rather than edit. Let me re-read the affected sections of the current doc to be precise.\n\n The user is reconsidering the recharge flow design—they want to keep bonuses in frozen_bonus rather than merging them into deposit_balance, which aligns with the current production system. I need to assess whether this approach impacts the V2 design's functionality.\n\nLooking at the core V2 mechanics: the group model relies on deposit_balance representing locked play amounts, betting deductions pull from either deposit or bonus lots, bonus accumulation adds to remaining amounts, and playthrough completion redistributes the group's total remaining balance across deposit_balance, frozen_bonus, and withdrawable funds. The key insight is that the Lot and Group models—which serve as the source of truth—remain completely unchanged; only the aggregate account-level bookkeeping shifts. Instead of consolidating everything into deposit_balance, the deposit portion stays in deposit_balance while the bonus portion lives in frozen_bonus. This is purely a mapping layer difference that doesn't alter the underlying functionality or game mechanics.\n\nThe core operations like FIFO progression, intra-group consumption order, progress tracking, bonus hit attribution, and completion/playedout determination all operate directly on Lots and Groups, so they're unaffected by this change. What does shift is which aggregate bucket each operation increments or decrements—but this maps cleanly to the existing production functions that already handle deposit, bonus, and withdrawal splits. However, there's a conceptual tension: frozen_bonus becomes a mixed bucket containing both bonus lots from active groups and trial bonuses or other promotional awards, whereas deposit_balance remains clean with only deposit-side locked amounts. This mixing creates some ambiguity about what frozen_bonus actually represents.\n\nWhen a payout hits the bonus side, frozen_bonus increases instead of deposit_balance, and when it hits the deposit side, deposit_balance increases—the production system's incWinBalance function already supports this split. For playthrough completion, the group's remaining balance splits into depositRemaining and bonusRemaining, then unlocks by decrementing each from their respective buckets and moving both to withdraw. The production codebase already has separate moveDepositToWithdraw and moveBonusToWithdraw functions that handle exactly this scenario.\n\nKeeping frozen_bonus aligns perfectly with how the production system works—BetService already routes bonus fund sources to the frozen_bonus bucket, and Trial pool clearing just reads from frozen_bonus without needing changes. For balance display, playable balance stays as withdraw + deposit + frozen_bonus, but the locked playable portion needs clarification on whether it should combine deposit_balance and frozen_bonus, or show them separately. Since frozen_bonus contains both bonus lot and trial/other rewards mixed together, we can't isolate just the bonus lot portion from the aggregated bucket alone—we'd need to calculate it from the lot level. 这其实是文档表示的变化而非逻辑变化。关键风险点在于frozen_bonus混入Trial体验金时扣款顺序能否正确处理——但BetService通过Bonus Lots来扣款Trial阶段独立执行非Trial阶段的Trial残留体验金早已清零所以只要保证\"frozen_bonus里所有钱都有对应Active Lot\"这个不变量,就不会有新增风险,现网本身就是这样运行的。\n\n对核心业务功能打码组推进、解锁、派奖归属、PlayedOut没有影响因为这些都基于Lot和Group事实表只影响聚合余额的记账映射反而更贴近现网实现。不过需要调整几处文档和口径——包括不变量说明、派奖落桶、解锁分桶、前端展示逻辑和frozen_bonus的混合桶定义。\n\n我应该先给出这个影响分析然后创建一个计划来更新钱包系统V2.md的相关章节§2、§4.1、§6.1、§10.4、§11.3、§12、§13.1、§15、§16等把充值Bonus从deposit_balance改回frozen_bonus的拆分方式。\n\n现在让我重新检查一下关键章节特别是§4.1和§15来确保计划的准确性。"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":42,"limit":120}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" I should double-check a few specific sections to make sure I'm citing them accurately, particularly §15, §11.3, §12, and §19. I already have a sense of what they contain from my earlier review, but let me verify §15 to get the precise language."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":330,"limit":130}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"## 直接结论\n\n**对核心业务功能没有影响。** 充值改回 `deposit_balance + frozen_bonus`(本金进 `deposit_balance`、Bonus 进 `frozen_bonus`)只改变\"聚合余额落哪个桶\"的记账映射,**不触碰打码组的任何业务逻辑**,而且反而更贴近现网代码、改动更小。\n\n## 为什么不影响功能\n\nV2 的所有核心机制都建立在 **Lot + Group 事实表**上,聚合余额只是快照:\n\n- FIFO 推进、组内 Deposit→Bonus 消耗顺序、`progress_group` 进度、派奖命中归属、Completed/PlayedOut 判定 —— 全部读写 `wallet_fund_lot` / `wallet_fund_lot_group`**与聚合桶无关**。\n- 聚合桶(`deposit_balance` / `frozen_bonus` / `withdraw_balance`)只是这些 Lot 余额的\"分桶快照\"。把 Bonus 放 `deposit_balance` 还是 `frozen_bonus`,只是同一笔钱贴在哪个标签上。\n\n而且现网已有的分桶函数天然支持这种拆分**保持 `frozen_bonus` 比合并进 `deposit_balance` 更省代码**\n\n```231:245:slot-wallet/app/model/multi/WalletAccountModel.php\n public function incBalance(int $withdraw, int $deposit, int $bonus, ...)\n```\n- 下注扣 Bonus Lot → `decBalance` 的 bonus 参数扣 `frozen_bonus`(现网行为,不用改 `BetService`)。\n- 整组解锁时Deposit 侧走 `moveDepositToWithdraw`、Bonus 侧走 `moveBonusToWithdraw`(现网 `WinService` 已是这么写的,完全契合)。\n\n## 映射对比\n\n```mermaid\nflowchart LR\n subgraph fact [事实源 不变]\n DLot[\"Deposit Lot remaining\"]\n BLot[\"Bonus Lot remaining\"]\n Grp[\"Group current_wager\"]\n end\n subgraph v2old [原V2: 合并]\n DLot -->|进| DepA[\"deposit_balance\"]\n BLot -->|进| DepA\n end\n subgraph keep [改回: 分桶 同现网]\n DLot -->|进| DepB[\"deposit_balance\"]\n BLot -->|进| Frz[\"frozen_bonus\"]\n end\n```\n\n## 需要相应调整的点(都是记账/口径层,非逻辑层)\n\n1. **不变量改两条**`deposit_balance = Σ(进行中 Deposit Lot.remaining)``frozen_bonus ⊇ Σ(进行中组内 Bonus Lot.remaining)``frozen_bonus` 还含 Trial 体验金/其它发奖,是超集)。\n2. **派奖落桶**:命中 Deposit 侧 → `deposit_balance += payout`;命中 Bonus 侧 → `frozen_bonus += payout`(之前文档统一写 `deposit_balance`)。\n3. **整组解锁转 Cash**`group_remaining` 拆成 Deposit 侧(从 `deposit_balance` 扣)+ Bonus 侧(从 `frozen_bonus` 扣),合并进 `withdraw_balance`。\n4. **`frozen_bonus` 成为混合桶**(充值组 Bonus + Trial + 其它发奖)。功能上靠 Lot/Group 区分没问题;但**前端要把\"Locked Play / 锁定可玩\"作为整体展示时,不能直接取单个聚合桶**,需从 Lot/Group 聚合算\"组内 Bonus\"(因 `frozen_bonus` 混入了 Trial 等)。\n5. 一个隐含不变量要守住:**非 Trial 阶段 `frozen_bonus` 里每一分钱都应有对应的 Active Bonus Lot**,否则聚合桶与 Lot 对不上(现网已如此,无新增风险)。\n\n注这与 Q4「命中归属、下线比例拆分」**不冲突**——命中 Bonus 侧时把 payout 记到 `frozen_bonus` 即可,不会复活按比例拆分。\n\n这本质是把之前的 Q1/Q11 决策回退为\"同现网分桶\",需要回改文档若干小节。下面是修订计划。\n\n I'm ready to provide a comprehensive answer now. The key insight is that redefining recharge as the sum of deposit_balance and frozen_bonus doesn't disrupt the core mechanics—those all depend on Lot and Group data. The real impact is on how we aggregate and map the bucket accounting, which actually aligns better with the existing system in production. However, this change requires updates across several documentation sections and adjustments to some of the bookkeeping rules.\n\nI should structure my response to first establish that core functionality remains unaffected, then explain why this approach is actually less invasive, identify all the areas that do need adjustment—including how frozen_bonus becomes a mixed bucket and how the front-end \"Locked Play\" display must aggregate from individual Lots rather than a single bucket—and finally propose a concrete plan for updating the documentation. There's one clarifying question I should ask about the front-end display convention for locked balances, since that's a genuine product decision arising from this change. I can default the display format to match the current system's three-bucket approach (withdrawable, locked playable deposit, and bonus credits) without needing to ask — that's already well-defined in production. So I'll move straight to documenting the plan: updating §2 to remove the bonus-into-deposit difference since we're reverting to the current split, redefining §4.1 to clarify that deposit_balance holds only deposit-side principal and payouts while frozen_bonus covers bonus and trial credits, adjusting §5 to map each payout side to its corresponding bucket, and updating §6.1 to reflect the deposit-side logic.\n\nContinuing through the remaining sections: §10.4 splits deductions by lot type into their respective buckets, §11.3 routes payouts to the matching bucket, §12 splits the playthrough completion into separate deposit and bonus remaining amounts that move to withdrawable, §13.1 confirms bonus lots transition to zero when played out, and §15-16 update the account rules and invariants to reflect the two-bucket model. I'll present the analysis and then lay out these doc edits as a structured plan."},{"type":"tool_use","name":"CreatePlan","input":{"name":"钱包V2恢复frozen_bonus分桶","overview":"将《钱包系统V2.md》中\"充值 Bonus 进 deposit_balance\"的记账口径回退为现网的\"本金进 deposit_balance、Bonus 进 frozen_bonus\"分桶。仅改文档的聚合余额记账/不变量/展示口径,不改任何打码组业务逻辑。","plan":"# 钱包系统V2.md恢复 frozen_bonus 分桶记账\n\n仅修订 [docs/requirements/钱包系统V2.md](docs/requirements/钱包系统V2.md) 中与\"聚合余额落桶\"相关的章节。打码组推进、消耗顺序、派奖命中归属、Completed/PlayedOut、`fund_detail` JSON、Redis 命中摘要等核心逻辑**保持不变**。\n\n## 修订内容\n\n- 三处不变量/语义:`deposit_balance = Σ 进行中 Deposit Lot.remaining``frozen_bonus ⊇ Σ 进行中组内 Bonus Lot.remaining`(含 Trial/其它发奖,超集)。\n- 派奖落桶:命中 Deposit → `deposit_balance += payout`;命中 Bonus → `frozen_bonus += payout`。\n- 整组解锁:`group_remaining` 拆 Deposit 侧(扣 `deposit_balance`+ Bonus 侧(扣 `frozen_bonus`),合并进 `withdraw_balance`(对应现网 `moveDepositToWithdraw` + `moveBonusToWithdraw`)。\n- 前端\"锁定可玩/Locked Play\"如需整体展示,须从 Lot/Group 聚合算组内 Bonus不能直接取 `frozen_bonus`(默认口径:展示为 Locked Deposit + Bonus Credits 两项,可玩 = 三桶和)。\n\n## 待改小节(逐节)\n\n- 第 42 行 §2 差异 bullet删除\"充值 Bonus 进 deposit_balance\"这条差异(改回后与现网一致)。\n- §4.1`deposit_balance` 语义改为\"仅 Deposit 本金 + Deposit 侧派奖\"`frozen_bonus` 语义改为\"组内 Bonus + Trial 体验金 + 其它发奖\";不变量拆两条;移除第 120-121 行\"与现网差异\"note展示口径调整为三桶。\n- §5 对应关系补注聚合桶映射Deposit 侧→`deposit_balance`、Bonus 侧→`frozen_bonus`)。\n- §6.1:改为 `deposit_balance += 100`、`frozen_bonus += 50`;删除\"不更新 frozen_bonus\"与差异 note。\n- §10.4:扣款分桶改为 扣 Deposit Lot→`deposit_balance--`、扣 Bonus Lot→`frozen_bonus--`、扣 Cash→`withdraw_balance--`。\n- §11.3:命中 Deposit→`deposit_balance += payout`、命中 Bonus→`frozen_bonus += payout`(各自累加对应 Lot remaining。\n- §12 打码完成:`group_remaining` 拆 Deposit/Bonus 分桶扣减后合并进 `withdraw_balance`。\n- §13.1 PlayedOutBonus Lot 置 PlayedOut 时 `frozen_bonus` 对应部分已为 0无需额外扣。\n- §15 Account 更新规则15.1 / 15.2 / 15.4 / 15.6 改为分桶表述。\n- §16 不变量、§19 总结条款 2、3同步为分桶口径。\n\n## 不改动\n\n- §9 局生命周期、§10.1-10.3 进度归属与扣款顺序、§11.1-11.2 命中归属与终态降级、§4.4 `fund_detail` JSON、§18 回滚不做。\n- Q4「下线 win 状态路由与比例拆分」结论不变(命中 Bonus 记 `frozen_bonus` 不复活比例拆分)。","todos":[{"id":"sec2-4","content":"改 §2 差异 bullet 与 §4.1deposit_balance/frozen_bonus 语义、不变量拆两条、移除差异 note、展示口径三桶"},{"id":"sec5-6","content":"改 §5 对应关系补聚合桶映射§6.1 入账改为 deposit_balance += 本金、frozen_bonus += bonus"},{"id":"sec10-11","content":"改 §10.4 扣款分桶、§11.3 派奖按命中侧落 deposit_balance / frozen_bonus"},{"id":"sec12-13","content":"改 §12 整组解锁拆 Deposit/Bonus 分桶转 Cash§13.1 PlayedOut 表述"},{"id":"sec15-19","content":"改 §15 Account 更新规则分桶、§16 不变量、§19 总结条款 2/3"}]}}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<timestamp>Thursday, Jun 11, 2026, 2:39 PM (UTC+8)</timestamp>\n<user_query>\n钱包系统V2.md恢复 frozen_bonus 分桶记账\n\nImplement the plan as specified, it is attached for your reference. Do NOT edit the plan file itself.\n\nTo-do's from the plan have already been created. Do not create them again. Mark them as in_progress as you work, starting with the first one. Don't stop until you have completed all the to-dos.\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"I'll implement the plan. Let me start with the first todo and read the current state of the affected sections."},{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec2-4","status":"in_progress"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now let me make the edits. Starting with §2 difference bullet:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"> 与现网差异(本期最终口径,已与产品确认):\n> - **充值 Bonus 落桶**:进 `deposit_balance`,不进 `frozen_bonus`§6、§11。\n> - **下注扣款顺序**Cash 最后扣、组优先§10.3与现网「Cash 先扣、类型优先」相反。","new_string":"> 与现网差异(本期最终口径,已与产品确认):\n> - **充值 Bonus 落桶**:本金进 `deposit_balance`、Bonus 进 `frozen_bonus`(与现网一致,不合并)。\n> - **下注扣款顺序**Cash 最后扣、组优先§10.3与现网「Cash 先扣、类型优先」相反。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now §4.1 - semantics, invariant, difference note, display:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"deposit_balance\n= Locked Play Balance / 锁定可玩余额\n= 未完成打码组内的 Deposit + Bonus + 归属该组的派奖\n= 可下注、不可提现\n= 不变量deposit_balance = Σ(进行中 Lot.remaining_amount)(含组内 Deposit/Bonus 与已回流派奖)\n```\n\n其他字段\n\n```text\nfrozen_bonus\n= 仅承载 Trial 体验金与「非充值」直接发奖 Bonus\n= 本期充值 Bonus 不再写入此字段(见 §6\n\navailable_bonus","new_string":"deposit_balance\n= Locked Deposit / 锁定充值本金\n= 未完成打码组内的 Deposit 本金 + 归属 Deposit 侧的派奖\n= 可下注、不可提现\n= 不变量deposit_balance = Σ(进行中 Deposit Lot.remaining_amount)(含已回流 Deposit 侧派奖)\n```\n\n其他字段\n\n```text\nfrozen_bonus\n= Bonus Credits / 奖励额度,可下注、不可提现\n= 未完成打码组内的 Bonus + 归属 Bonus 侧的派奖 + Trial 体验金 + 其它非充值发奖 Bonus\n= 不变量frozen_bonus ⊇ Σ(进行中组内 Bonus Lot.remaining_amount)(超集,另含 Trial / 其它发奖)\n\navailable_bonus"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"> 与现网差异:现网充值 Bonus 写入 `frozen_bonus`;本期改为写入 `deposit_balance`。\n> `frozen_bonus` **仅在充值场景**停用Trial 体验金(`executeTrialBet/Win`)及其它纯 Bonus 发奖仍使用,不全局废弃。\n\n用户侧余额展示\n\n```text\n可玩余额 = withdraw_balance + deposit_balance + frozen_bonus\n可提现余额 = withdraw_balance\n锁定可玩余额 = deposit_balance\nBonus Credits如展示= frozen_bonus\n```","new_string":"> 落桶口径与现网一致:充值本金进 `deposit_balance`、充值 Bonus 进 `frozen_bonus`(不合并)。\n> 注意 `frozen_bonus` 为混合桶(组内 Bonus + Trial + 其它发奖),用途区分靠 Lot / Group聚合桶本身无法区分。\n\n用户侧余额展示\n\n```text\n可玩余额 = withdraw_balance + deposit_balance + frozen_bonus\n可提现余额 = withdraw_balance\n锁定充值 = deposit_balance\nBonus Credits = frozen_bonus\n```\n\n> 如需把「Deposit + Bonus 打码组」作为一个整体「Locked Play」展示须从 Lot / Group 聚合算组内 Bonus\n> 不能直接取 `frozen_bonus`(其中混入了 Trial 等非组内 Bonus。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec2-4","status":"completed"},{"id":"sec5-6","status":"in_progress"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me read §5 and §6.1 to edit precisely:"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":238,"limit":70}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\nCash Balance → wallet_account.withdraw_balance\nLocked Play Balance → wallet_account.deposit_balance\nOriginal Deposit → wallet_fund_lot.lot_type = 1\nCasino Bonus Funds → wallet_fund_lot.lot_type = 2\nDeposit 侧派奖 → 累加到 lot_type = 1 的 remaining_amount\nBonus 侧派奖 → 累加到 lot_type = 2 的 remaining_amount\nPlay-through Requirement → wallet_fund_lot_group.required_wager\nPlay-through Progress → wallet_fund_lot_group.current_wager\n```","new_string":"```text\nCash Balance → wallet_account.withdraw_balance\nLocked Deposit聚合桶 → wallet_account.deposit_balance仅 Deposit 侧)\nBonus Credits聚合桶 → wallet_account.frozen_bonus含组内 Bonus + Trial 等)\nOriginal Deposit → wallet_fund_lot.lot_type = 1聚合到 deposit_balance\nCasino Bonus Funds → wallet_fund_lot.lot_type = 2聚合到 frozen_bonus\nDeposit 侧派奖 → 累加 lot_type = 1 的 remaining_amountdeposit_balance += payout\nBonus 侧派奖 → 累加 lot_type = 2 的 remaining_amountfrozen_bonus += payout\nPlay-through Requirement → wallet_fund_lot_group.required_wager\nPlay-through Progress → wallet_fund_lot_group.current_wager\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"### 6.1 更新账户聚合余额\n\n```text\nwallet_account.deposit_balance += 150 = deposit 100 + bonus 50\n```\n\n不更新 `withdraw_balance`,也不更新 `frozen_bonus`。\n\n原因\n\n```text\n这笔 Deposit 已参与 Bonus 活动,打码完成前不是可提现 Cash而是 Locked Play Balance\n整组Deposit + Bonus统一锁定在 deposit_balance。\n```\n\n> 与现网差异:现网 `incBalance(0, deposit, bonus)` 把 bonus 加到 `frozen_bonus`;本期 bonus 改加到 `deposit_balance`。","new_string":"### 6.1 更新账户聚合余额\n\n```text\nwallet_account.deposit_balance += 100 Deposit 本金)\nwallet_account.frozen_bonus += 50 Bonus\n```\n\n不更新 `withdraw_balance`。\n\n原因\n\n```text\n这笔 Deposit 已参与 Bonus 活动,打码完成前不是可提现 Cash\n本金锁定在 deposit_balanceBonus 锁定在 frozen_bonus二者同属一个打码组group_no 关联)。\n```\n\n> 落桶与现网一致:`incBalance(0, deposit, bonus)` 本金进 `deposit_balance`、bonus 进 `frozen_bonus`。\n> 「整组」概念由 `wallet_fund_lot_group` 维护,不依赖聚合桶合并。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec5-6","status":"completed"},{"id":"sec10-11","status":"in_progress"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"§10.4 already maps deposit/bonus to deposit_balance; need to split bonus to frozen_bonus:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\n扣 Deposit / Bonus Lot → deposit_balance -= 该部分合计\n扣 Cash → withdraw_balance -= 该部分合计\n```","new_string":"```text\n扣 Deposit Lot → deposit_balance -= Deposit 侧合计\n扣 Bonus Lot → frozen_bonus -= Bonus 侧合计\n扣 Cash → withdraw_balance -= Cash 合计\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now §11.3. Let me read it:"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":456,"limit":55}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\n命中 Depositprogress_group 下 lot_type = 1 的 remaining_amount += payout\n命中 Bonus progress_group 下 lot_type = 2 的 remaining_amount += payout\n同时 wallet_account.deposit_balance += payout\n```","new_string":"```text\n命中 Depositprogress_group 下 lot_type = 1 的 remaining_amount += payoutdeposit_balance += payout\n命中 Bonus progress_group 下 lot_type = 2 的 remaining_amount += payoutfrozen_bonus += payout\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Also update §11.4 example (deposit hit, so deposit_balance is correct there—leave as is). Now mark sec10-11 done and move to sec12-13:"},{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec10-11","status":"completed"},{"id":"sec12-13","status":"in_progress"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":506,"limit":70}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\ngroup_remaining = Σ(组内 Lot.remaining_amount)\nwallet_account.deposit_balance -= group_remaining\nwallet_account.withdraw_balance += group_remaining\n组内所有有效 Lot 状态 → Completedremaining 清零\n```\n\n完成后该组不再参与下注下一笔下注推进下一个 Active 组。\n\n> 解锁顺序沿用现网 `unlockGroupMembers`:成员按 Deposit 在前、Bonus 在后依次解锁。","new_string":"```text\ndeposit_remaining = Σ(组内 Deposit Lot.remaining_amount)\nbonus_remaining = Σ(组内 Bonus Lot.remaining_amount)\ngroup_remaining = deposit_remaining + bonus_remaining\n\nwallet_account.deposit_balance -= deposit_remaining moveDepositToWithdraw\nwallet_account.frozen_bonus -= bonus_remaining moveBonusToWithdraw\nwallet_account.withdraw_balance += group_remaining\n组内所有有效 Lot 状态 → Completedremaining 清零\n```\n\n完成后该组不再参与下注下一笔下注推进下一个 Active 组。\n\n> 解锁顺序沿用现网 `unlockGroupMembers`:成员按 Deposit 在前、Bonus 在后依次解锁;\n> Deposit 侧从 `deposit_balance` 扣、Bonus 侧从 `frozen_bonus` 扣,二者均转入 `withdraw_balance`。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\ngroup_remaining = 0 AND current_wager < required_wager\n→ wallet_fund_lot_group.status = 5 PlayedOut\n 组内 Bonus 成员 Lot → PlayedOutDeposit 成员保持原状态)\n```\n\n含义资金用尽但未达标任务失效不再参与后续下注与打码。","new_string":"```text\ngroup_remaining = 0 AND current_wager < required_wager\n→ wallet_fund_lot_group.status = 5 PlayedOut\n 组内 Bonus 成员 Lot → PlayedOutDeposit 成员保持原状态)\n```\n\n含义资金用尽但未达标任务失效不再参与后续下注与打码。\n资金已为 0Deposit 侧已从 deposit_balance、Bonus 侧已从 frozen_bonus 扣减完毕),无需额外扣聚合桶。"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec12-13","status":"completed"},{"id":"sec15-19","status":"in_progress"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now §15, §16, §17 example, §19. Let me read them:"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":596,"limit":120}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\n15.1 Deposit + Bonus 入账 deposit_balance += deposit_amount + bonus_amount\n15.2 锁定组下注扣款 deposit_balance -= 命中 Lot 扣款合计\n15.3 Cash 下注扣款 withdraw_balance -= Cash 扣款合计\n15.4 锁定组派奖(组 Activedeposit_balance += payout并累加对应 Lot.remaining_amount\n15.5 Cash 派奖 / 组已终态 withdraw_balance += payout\n15.6 打码组完成 deposit_balance -= group_remainingwithdraw_balance += group_remaining\n15.7 打码组 PlayedOut 组内资金已为 0deposit_balance 无需额外扣减group.status = 5\n```","new_string":"```text\n15.1 Deposit + Bonus 入账 deposit_balance += deposit_amountfrozen_bonus += bonus_amount\n15.2 锁定组下注扣款 扣 Deposit Lot → deposit_balance -= 该部分;扣 Bonus Lot → frozen_bonus -= 该部分\n15.3 Cash 下注扣款 withdraw_balance -= Cash 扣款合计\n15.4 锁定组派奖(组 Active命中 Deposit → deposit_balance += payout命中 Bonus → frozen_bonus += payout并累加对应 Lot.remaining_amount\n15.5 Cash 派奖 / 组已终态 withdraw_balance += payout\n15.6 打码组完成 deposit_balance -= deposit_remainingfrozen_bonus -= bonus_remainingwithdraw_balance += group_remaining\n15.7 打码组 PlayedOut 组内资金已为 0各桶已扣完无需额外扣减group.status = 5\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"5. 不变量deposit_balance 始终等于 Σ(进行中 Lot.remaining_amount)。\n```","new_string":"5. 不变量deposit_balance = Σ(进行中 Deposit Lot.remaining)frozen_bonus ⊇ Σ(进行中组内 Bonus Lot.remaining)。\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now §17 examples (入账 and 完成打码 show merged 150/170):"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\n充值 Deposit 100 / Bonus 50 / Required 250\n账户withdraw_balance = 0 deposit_balance = 150\nGroupdeposit_amount=100 bonus_amount=50 required_wager=250 current_wager=0 status=Active\nLot lot_type=1 remaining=100 lot_type=2 remaining=50\n```","new_string":"```text\n充值 Deposit 100 / Bonus 50 / Required 250\n账户withdraw_balance = 0 deposit_balance = 100 frozen_bonus = 50\nGroupdeposit_amount=100 bonus_amount=50 required_wager=250 current_wager=0 status=Active\nLot lot_type=1 remaining=100 lot_type=2 remaining=50\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\ncurrent_wager >= required_wagergroup_remaining = 170\ndeposit_balance -= 170 → 0\nwithdraw_balance += 170 → 170\ngroup.status = Completed\n```","new_string":"```text\ncurrent_wager >= required_wager\ndeposit_remaining = 100 + 40(派奖) = 140 bonus_remaining = 50 group_remaining = 190\ndeposit_balance -= 140 → 0\nfrozen_bonus -= 50 → 0\nwithdraw_balance += 190 → 190\ngroup.status = Completed\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me correct §17.2 to match the split buckets, then fix §17.3 accordingly:\n\n Let me trace through the deposit and bonus lot calculations across these sections. In §17.2, a 20-unit deposit wager reduces the deposit balance from 100 to 80, then a 40-unit payout adds back to both the deposit lot remaining (now 120) and deposit balance (now 120). The bonus lot remaining stays at 50, and I'm working through how these balances carry into §17.3 where the deposit remaining is 120 and bonus remaining is 50. I see the issue now—I was using 150 as the starting deposit_balance, but it should be 100. Let me recalculate §17.2: after the 20 deduction, deposit_balance drops to 80, then the 40 payout brings it back to 120. I need to correct the section to reflect this proper sequence."},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\n下注 20 → 扣 lot_type=1 20\n账户deposit_balance -= 20= 130\n组 current_wager += 20\n明细bet 流水 fund_detail = {progress_group_no:本组, deductions:[{lot_type:1, deduct_amount:20, ...}]}\n\nis_end=1 派奖 40命中本组 Deposit → 本组 lot_type=1 remaining += 40deposit_balance += 40= 170\n不新增派奖 Lot。\n```","new_string":"```text\n下注 20 → 扣 lot_type=1Deposit20\n账户deposit_balance -= 20100 → 80frozen_bonus = 50 不变\n组 current_wager += 20\n明细bet 流水 fund_detail = {progress_group_no:本组, deductions:[{lot_type:1, deduct_amount:20, ...}]}\n\nis_end=1 派奖 40命中本组 Deposit → 本组 lot_type=1 remaining += 40deposit_balance += 4080 → 120\n不新增派奖 Lot。\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"```text\ncurrent_wager >= required_wager\ndeposit_remaining = 100 + 40(派奖) = 140 bonus_remaining = 50 group_remaining = 190\ndeposit_balance -= 140 → 0\nfrozen_bonus -= 50 → 0\nwithdraw_balance += 190 → 190\ngroup.status = Completed\n```","new_string":"```text\ncurrent_wager >= required_wager\ndeposit_remaining = 120= 本金100 - 下注20 + 派奖40bonus_remaining = 50 group_remaining = 170\ndeposit_balance -= 120 → 0\nfrozen_bonus -= 50 → 0\nwithdraw_balance += 170 → 170\ngroup.status = Completed\n```"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now §19 summary clauses 2 and 3:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"1. withdraw_balance = Cash可下注可提现。\n2. deposit_balance = Locked Play可下注不可提现= Σ 进行中 Lot.remaining。\n3. 充值 Bonus 进 deposit_balance不进 frozen_bonusfrozen_bonus 仅 Trial / 非充值发奖用)。","new_string":"1. withdraw_balance = Cash可下注可提现。\n2. deposit_balance = Locked Deposit仅 Deposit 侧frozen_bonus = Bonus Credits组内 Bonus + Trial + 其它发奖);均可下注不可提现。\n3. 充值本金进 deposit_balance、充值 Bonus 进 frozen_bonus与现网一致不合并「整组」由 wallet_fund_lot_group 维护。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Let me do a final scan for any stale references to the merged-bucket model:\n\n I'm checking section 19 clause 13 to confirm the deposit and bonus handling after wagering is complete, then searching through the document for any remaining references to \"Locked Play\" or outdated balance field names that might need updating."},{"type":"tool_use","name":"Grep","input":{"pattern":"Locked Play|deposit_balance \\+= 150|deposit_balance = 150|Bonus 进 deposit_balance|含组内 Deposit/Bonus|deposit_balance \\+ frozen_bonus","path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","output_mode":"content","-n":true}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Both remaining matches are intentional (the 可玩余额 sum and the Locked Play display note). All edits are consistent.\n\n I'm verifying that the intentional changes are correct—the playable balance calculation on line 127 and the locked play display note on line 133 are both as intended. I'm also checking that the section headings are consistent, confirming that §4.1 now says \"Locked Deposit\" instead of \"Locked Play Balance\" and that §1 doesn't contradict this since it doesn't mention the buckets. The example in §11.4 correctly shows a deposit hit adding to the deposit balance.\n\nAll todos are done. Since this is only a markdown change with no PHP files modified, the verification gate that scans PHP changes doesn't apply here."},{"type":"tool_use","name":"TodoWrite","input":{"merge":true,"todos":[{"id":"sec15-19","status":"completed"}]}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"全部 5 个 todo 已完成。已将《钱包系统V2.md》的充值记账口径从\"合并进 `deposit_balance`\"改回现网的\"本金进 `deposit_balance`、Bonus 进 `frozen_bonus`\"分桶。\n\n改动一览\n- **§2 差异 bullet**:落桶口径改为与现网一致(本金 deposit_balance / Bonus frozen_bonus。\n- **§4.1**`deposit_balance` = Locked Deposit仅 Deposit 侧);`frozen_bonus` = Bonus Credits组内 Bonus + Trial + 其它发奖,混合桶);不变量拆两条;展示口径三桶;补\"整体 Locked Play 展示须从 Lot/Group 聚合\"提示。\n- **§5**对应关系补聚合桶映射Deposit 侧→deposit_balance、Bonus 侧→frozen_bonus。\n- **§6.1**:入账改为 `deposit_balance += 100`、`frozen_bonus += 50`,删旧差异 note。\n- **§10.4 / §15.2**下注扣款分桶Deposit Lot→deposit_balance、Bonus Lot→frozen_bonus、Cash→withdraw。\n- **§11.3 / §15.4**:派奖命中 Deposit→`deposit_balance += payout`、命中 Bonus→`frozen_bonus += payout`。\n- **§12 / §15.6**:整组解锁拆 `deposit_remaining`/`bonus_remaining` 分桶扣减,合并进 `withdraw_balance`(对应 `moveDepositToWithdraw` + `moveBonusToWithdraw`)。\n- **§13.1**PlayedOut 表述补\"各桶已扣完\"。\n- **§16**:不变量改两条。\n- **§17 示例**:入账/下注/派奖/完成的金额按分桶重算deposit_balance 100→80→120、frozen_bonus 50、完成转 withdraw 170。\n- **§19**:总结条款 2、3 同步。\n\n功能影响确认核心打码组逻辑FIFO、消耗顺序、进度归属、命中归属、Completed/PlayedOut、`fund_detail`均未改动Q4「下线 win 状态路由与比例拆分」结论不变。\n\n说明本次仅改 `.md` 文档,未改动任何 `app/**/*.php`,后端 verify 门禁/`php -l` 不适用(无 PHP 变更)。"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<timestamp>Thursday, Jun 11, 2026, 2:49 PM (UTC+8)</timestamp>\n<user_query>\nTrial 体验金的逻辑还是之前的不能变\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":" I need to verify that the V2 documentation doesn't inadvertently change how Trial体验金 works, especially since my edits made frozen_bonus a mixed bucket that now contains组内Bonus, Trial, and other rewards together. Let me check what the doc currently says about Trial to ensure the logic remains intact."},{"type":"tool_use","name":"Grep","input":{"pattern":"Trial|体验金|frozen_bonus","path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","output_mode":"content","-n":true}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"You're right—I need to make explicit that Trial 体验金 stays on its existing independent path and is not touched by the V2 group logic. Let me verify the current现网 Trial behavior and then add a clear scoping note. The doc currently only mentions Trial as a co-resident of `frozen_bonus` but doesn't state it's out of scope. Let me read the area after §2 to insert a scoping note.\n\n I need to clarify that Trial 体验金 operates independently from the V2 group betting and payout logic, even though both reside in the frozen_bonus bucket — the new changes to deduction, unlocking, and payout flows only apply to V2 group funds, not to Trial funds which maintain their own separate mechanics. I need to clarify the scope by explicitly stating that the group mechanics, bet deduction, payout, and unlock logic only apply to the real-money phase—Trial bonus funds follow the existing independent path (executeTrialBet/Win, Trial Pool) and aren't affected by these rules. I should add this scoping statement early in the document, perhaps in §2 or as a dedicated section, and reinforce it in §4.1 when discussing the frozen_bonus invariant, so readers understand that Trial funds are completely separate from the Lot/Group system."},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","offset":40,"limit":10}}]}}
{"role":"assistant","message":{"content":[{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"> 与现网差异(本期最终口径,已与产品确认):\n> - **充值 Bonus 落桶**:本金进 `deposit_balance`、Bonus 进 `frozen_bonus`(与现网一致,不合并)。\n> - **下注扣款顺序**Cash 最后扣、组优先§10.3与现网「Cash 先扣、类型优先」相反。\n> - **派奖归属**按本局下注命中关系§11下线现网「账户状态路由」与「按 bet 比例拆分」两套旧逻辑。\n> - **有效投注进度**:整笔只计 `progress_group`§10.1),不再按资金来源分摊到各自组。\n\n---","new_string":"> 与现网差异(本期最终口径,已与产品确认):\n> - **充值 Bonus 落桶**:本金进 `deposit_balance`、Bonus 进 `frozen_bonus`(与现网一致,不合并)。\n> - **下注扣款顺序**Cash 最后扣、组优先§10.3与现网「Cash 先扣、类型优先」相反。\n> - **派奖归属**按本局下注命中关系§11下线现网「账户状态路由」与「按 bet 比例拆分」两套旧逻辑。\n> - **有效投注进度**:整笔只计 `progress_group`§10.1),不再按资金来源分摊到各自组。\n\n### 适用范围(重要)\n\n本文档的打码组、下注扣款、派奖归属、解锁 / PlayedOut 规则**仅适用于真金阶段(非 Trial**。\n\n**Trial 体验金逻辑完全沿用现网,本期不做任何改动:**\n\n```text\n1. Trial 阶段判定isTrialPhase与分支保持不变\n bet / win 入口先判 isTrialPhaseTrial 阶段走 executeTrialBet / executeTrialWin 独立路径,\n 不进入 BetService / WinService不走本文档的组 / Lot 逻辑。\n2. Trial 下注:仅扣 frozen_bonus体验金不建 Lot、不挂打码组、不可提现。\n3. Trial 派奖:整笔回 frozen_bonus不路由可提现 / 锁定充值,不跑解锁与 PlayedOut。\n4. 首充 Trial Pool 清零deductTrialPoolInRechargeTransaction保持不变\n 充值入账同事务内扣减充值前 frozen_bonus体验金转 Trial Pool。\n5. frozen_bonus 虽为混合桶,但本文档的组扣款 / 解锁均由 Bonus Lotlot_type=2驱动\n Trial 体验金无对应 Lot故不会被组逻辑扣减或解锁。\n```\n\n---"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Now reinforce in §4.1 that the Trial portion of `frozen_bonus` is not driven by group logic:"},{"type":"tool_use","name":"StrReplace","input":{"path":"/Users/ray/Documents/project/www/ray/docs/requirements/钱包系统V2.md","old_string":"> 落桶口径与现网一致:充值本金进 `deposit_balance`、充值 Bonus 进 `frozen_bonus`(不合并)。\n> 注意 `frozen_bonus` 为混合桶(组内 Bonus + Trial + 其它发奖),用途区分靠 Lot / Group聚合桶本身无法区分。","new_string":"> 落桶口径与现网一致:充值本金进 `deposit_balance`、充值 Bonus 进 `frozen_bonus`(不合并)。\n> 注意 `frozen_bonus` 为混合桶(组内 Bonus + Trial + 其它发奖),用途区分靠 Lot / Group聚合桶本身无法区分。\n> 其中 **Trial 体验金部分由现网 Trial 独立路径增减,不参与本文档的组扣款 / 解锁**(见 §2 适用范围)。"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"已明确把 Trial 体验金圈定为\"现网不变、独立路径\",避免被 V2 组逻辑误读。改动两处:\n\n- **§2 新增「适用范围(重要)」**:声明本文档的打码组/下注扣款/派奖归属/解锁/PlayedOut 规则**仅适用真金阶段(非 Trial**;并逐条列明 Trial 沿用现网不变——\n 1. `isTrialPhase` 判定与分支不变Trial 走 `executeTrialBet`/`executeTrialWin` 独立路径,不进 `BetService`/`WinService`、不走组/Lot 逻辑;\n 2. Trial 下注仅扣 `frozen_bonus`、不建 Lot/组、不可提现;\n 3. Trial 派奖整笔回 `frozen_bonus`、不路由、不跑解锁/PlayedOut\n 4. 首充 Trial Pool 清零(`deductTrialPoolInRechargeTransaction`)保持不变;\n 5. 组扣款/解锁均由 Bonus Lot 驱动Trial 体验金无 Lot故不会被组逻辑动到。\n- **§4.1 `frozen_bonus` 注**:补一句 Trial 部分由现网独立路径增减、不参与组扣款/解锁。\n\n关键点现网 `frozen_bonus` 是混合桶,但 V2 的组扣款/解锁只认 `lot_type=2` 的 Bonus Lot——Trial 体验金没有对应 Lot因此机制上天然不会被触碰逻辑保持原样。\n\n说明仅改 `.md` 文档,无 PHP 变更,后端 verify 门禁/`php -l` 不适用。\n\n I'm checking whether section 18 needs updating about unhandled features, but the scope section already covers the trial behavior appropriately since it's not part of the core flow. The document reads consistently now, and since this is documentation-only with no code changes, no gating is needed."}]}}