--- todos: - id: "unify-pending-query" content: "WagerTaskModel:getUncompletedTasksVersion2 / getActiveCountVersion2 / lockedTasksV2 统一为 status IN (1,11) AND current_wager < required_wager" status: pending - id: "version2-loop-repair" content: "UpdateWagerTask::version2():needed=0 自动完成;脏 is_completed=1 时纠正并累加打码" status: pending - id: "add-unit-tests" content: "补充单测覆盖 is_completed 脏数据与 required_wager=0 边界" status: pending - id: "data-hotfix-optional" content: "(可选)uid 1876592 任务1 将 is_completed 改回 0 作为上线前救火" status: pending isProject: false --- # 修复 version2 有待打码任务却不扣除的问题 ## 根因(对应你这两条数据) 解析你贴的数据(按表字段顺序): | 字段 | 任务1 `7733765055921637` | 任务2 `7745776340511881` | |------|--------------------------|--------------------------| | required_wager | **1000** | 0 | | current_wager | **0** | 0 | | is_completed | **1** | 1 | | status | 1(等待中) | 1(等待中) | | brief | 恢复提现额数据 | 提现退回 | **下注不累加的直接原因**:[`UpdateWagerTask::version2()`](slot_wallet/app/command/UpdateWagerTask.php) 调用 [`getUncompletedTasksVersion2()`](slot_wallet/app/model/multi/WagerTaskModel.php),查询条件是: ```php is_completed = 0 AND status IN (1, 11) ``` 两条记录 **`is_completed` 都是 1**,查询结果 **为空** → `empty($tasks)` → **直接 return**,下注额不会分配。 任务1 明明 `current_wager(0) < required_wager(1000)`,本应吃打码,但因 `is_completed=1` 被排除。 ```mermaid flowchart TD betMQ[下注MQ updateWagerTask] --> version2[version2] version2 --> query[getUncompletedTasksVersion2] query --> filter{"is_completed=0 AND status IN 1,11"} filter -->|任务1 is_completed=1| empty[tasks 为空] empty --> returnEarly[return 不扣除] filter -->|正常任务| loop[按 id FIFO 累加 current_wager] ``` ## 附带不一致(同一用户会「不能提现」但「也不扣打码」) version2 提现判断走 [`WalletService::calculateWithdrawAmount()`](slot_wallet/app/service/WalletService.php): ```php $num = $wagerTaskModel->getActiveCountVersion2($uid); // 只按 status IN (1,11) 计数 if ($num > 0) return 0; ``` [`getActiveCountVersion2()`](slot_wallet/app/model/multi/WagerTaskModel.php) **不看 `is_completed`**,两条 `status=1` 都会计入 → 提现被挡。 但打码更新 **看 `is_completed=0`** → 不累加。 这是典型的 **卡死状态**:有任务、不能提现、打码也不前进。 ## 代码修复方案(推荐) ### 1. 统一 version2「待打码任务」定义 在 [`WagerTaskModel`](slot_wallet/app/model/multi/WagerTaskModel.php) 中,将 version2 待处理任务定义为: > `status IN (STATUS_ON, STATUS_PROGRESS)` **且** `current_wager < required_wager` 理由:真正决定是否还需打码的是进度,而不是可能脏写的 `is_completed`。 **改动点**: - **重写或替换** `getUncompletedTasksVersion2()`:用 `where + whereExp('current_wager', '< required_wager')`(或等价 ThinkPHP 写法),去掉对 `is_completed=0` 的硬依赖。 - **同步** `getActiveCountVersion2()`、`lockedTasksV2()`:使用同一套「待打码」条件(目前 `lockedTasksV2` 也不过滤 `is_completed`,与打码查询语义不一致)。 - **同步** [`WalletService::calculateLeftWithdrawNeedBet()`](slot_wallet/app/service/WalletService.php) 中 version2 分支(已用 `lockedTasksV2`,改 Model 后自动对齐)。 ### 2. 加强 `UpdateWagerTask::version2()` 循环 在 [`UpdateWagerTask::version2()`](slot_wallet/app/command/UpdateWagerTask.php) 的 foreach 内补充: - 当 `needed == 0` 且 `status` 仍为 ON/PROGRESS 时,调用 `markTaskCompletedVersion2()` 收尾(避免 `required_wager=0` 的脏任务永远占着 `status=1`)。 - 当 `needed > 0` 且 `is_completed=1`(脏数据)时,累加打码时顺带把 `is_completed` 纠正为 0(或在 `incrementWagerVersion2` 内一并更新),避免下次再被旧逻辑挡住。 ### 3. 单测补充 在 [`tests/Unit/UpdateWagerTaskContributionTest.php`](slot_wallet/tests/Unit/UpdateWagerTaskContributionTest.php) 或新增 Model 单测,覆盖: - `is_completed=1` 但 `current_wager < required_wager` 的任务应被纳入待打码列表; - `required_wager=0` 且 `current_wager=0` 的任务不应纳入(对应你的任务2); - `getActiveCountVersion2` 与打码查询计数一致。 ## 该用户即时数据修复(可选,上线代码前可先救火) 对 uid `1876592`、任务 `7733765055921637`: ```sql UPDATE wager_task_{shard} SET is_completed = 0 WHERE id = 7733765055921637 AND uid = 1876592; ``` (分表名按 uid 取模规则确定。) 任务2 `required_wager=0` 在代码修复后不会再挡打码;若仍 `status=1` 且 `is_completed=1`,修复 `getActiveCountVersion2` 后也不再挡提现。 ## 不纳入本次范围 - 不判断 `withdraw_back` 业务倍数应为多少;任务2 `required_wager=0` 在修复后的查询里自然不会参与打码分配。 - 不追溯任务1为何 `is_completed=1` 且 `completed_at=0`(数据脏写来源),代码修复以 **自愈脏数据** 为主。 ## 验收 1. uid 1876592 下注后,任务1 `current_wager` 递增。 2. `calculateLeftWithdrawNeedBet` 显示的剩余打码与 `current_wager` 同步下降。 3. 任务1 打满后 `status=12`、`is_completed=1`,`getActiveCountVersion2` 为 0,可提现。 4. 跑 `slot-backend-completion-report` 门禁通过。