Bläddra i källkod

打码量与冻结金额文档改为中文

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doge 4 dagar sedan
förälder
incheckning
5b8456c602

+ 55 - 55
docs/前端交付/bot-28/后台/打码量与冻结金额-后台接口.md

@@ -1,77 +1,77 @@
 # 打码量与冻结金额:后台接口
 
-All endpoints in this doc are served by the bot-28 admin service and use admin authentication. A successful call returns `code=0`. All amounts are strings with 2 decimal places.
+本文档接口均由 bot-28 后台服务提供,使用管理员认证,成功返回 `code=0`。金额字段均为两位小数字符串。
 
-## Business rules
+## 业务规则
 
-| Item | Rule |
+| 项目 | 规则 |
 |---|---|
-| 打码量 (wagering) | Each user has one current cycle. **Required wagering** is the sum of every requirement: deposit/人工充值 × the platform multiple, the wagering configured on each activity, and wagering entered by admins. **Current wagering** (当前打码量) is the effective bets placed in this cycle. **Remaining wagering** (剩余打码量) is required minus current, floored at 0. |
-| Withdrawal | Withdrawal is blocked while remaining wagering is above 0, so a single unfinished activity requirement is enough to block it. After a successful withdrawal request, the cycle resets: required and current both go back to 0. |
-| Effective bet | Same rule as activity turnover: settled, non-voided bets, counted as min(stake, \|profit/loss\|). The cache refreshes every minute. |
-| Platform multiple | Config field `withdraw_wager_multiple` (提现打码倍数), default `1`. Edit it in the existing system config list. |
-| Activity rewards | All rewards go straight into **available balance** and no longer use frozen balance. Rewards with a configured wagering requirement add it; rewards without one can be withdrawn immediately and do not use the platform multiple. |
-| Withdrawal freeze | On a withdrawal request, available balance moves into frozen balance. If the request is rejected or fails, the money returns to available balance. If it succeeds, the money is deducted from frozen balance. |
+| 打码量 | 每个会员有一个当前打码周期。**所需打码量**为各项要求之和:充值/人工充值 × 平台倍数、各活动配置的打码量、后台填写的打码量。**当前打码量**为本周期的有效投注。**剩余打码量** = 所需 − 当前,最低为 0。 |
+| 提现 | 剩余打码量大于 0 时不能提现,只要有一项活动打码未完成就会拦截。提现申请成功后重置周期:所需与当前打码量都归 0。 |
+| 有效投注 | 与活动流水同一口径:已结算、未作废的注单,按 min(本金, \|输赢\|) 计算。缓存每分钟刷新一次。 |
+| 平台倍数 | 配置项 `withdraw_wager_multiple`(提现打码倍数),默认 `1`,在现有系统配置列表中修改。 |
+| 活动赠送 | 所有赠送直接进入**可用余额**,不再走冻结金额。配置了打码量的累加打码量;未配置的可直接提现,也不走平台倍数。 |
+| 提现冻结 | 申请提现时,可用余额转入冻结余额。审核拒绝或失败退回可用余额;成功则从冻结余额扣除。 |
 
-## 1. 会员列表 new fields
+## 1. 会员列表新增字段
 
-`GET /admin/user` (existing endpoint). Each `data.data[]` row gains:
+`GET /admin/user`(现有接口),`data.data[]` 每行新增:
 
-| Field | Meaning |
+| 字段 | 含义 |
 |---|---|
-| `wager_required` | Required wagering in the current cycle |
-| `wager_completed` | 当前打码量 (current wagering) |
-| `wager_remaining` | 剩余打码量 (remaining wagering); the user can withdraw when this is 0 |
-| `total_consume` | Unchanged: lifetime bet volume (the old 打码量 column) |
-| `wallet.frozen_balance` | Frozen balance: manual freezes plus withdrawals under review |
+| `wager_required` | 本周期所需打码量 |
+| `wager_completed` | 当前打码量 |
+| `wager_remaining` | 剩余打码量,为 0 时可以提现 |
+| `total_consume` | 不变:历史总投注额(原“打码量”列) |
+| `wallet.frozen_balance` | 冻结余额:人工冻结 + 审核中的提现 |
 
-Each `data.data[]` row in the 人工稽查 list `GET /admin/manualAudit` gains a `member_wager` object with the same three fields, plus `wager_amount`, the signed wagering change made by that operation.
+人工稽查列表 `GET /admin/manualAudit` 的 `data.data[]` 每行新增 `member_wager` 对象(同上三个字段),以及 `wager_amount`:该次操作的打码量变动(有符号)。
 
-## 2. 上下分: wagering params
+## 2. 上下分:打码量参数
 
 ### 人工稽查
 
-`POST /admin/manualAudit/topUp`, adding to the existing params:
+`POST /admin/manualAudit/topUp`,在现有参数基础上新增:
 
-| Param | Required | Notes |
+| 参数 | 必填 | 说明 |
 |---|---|---|
-| `wager_amount` | No | Only used when `change_type` is not `人工充值`. Wagering to add, ≥0; empty means 0 (withdrawable immediately). With `人工充值`, the system adds amount × platform multiple and ignores this param. |
+| `wager_amount` | 否 | 仅 `change_type` 不是 `人工充值` 时生效:要增加的打码量,≥0,留空为 0(可直接提现)。`人工充值` 由系统按 金额 × 平台倍数 自动增加,忽略此参数。 |
 
-`POST /admin/manualAudit/debit`, adding to the existing params:
+`POST /admin/manualAudit/debit`,在现有参数基础上新增:
 
-| Param | Required | Notes |
+| 参数 | 必填 | 说明 |
 |---|---|---|
-| `recharge_type` | No | Credit type this debit corresponds to: `人工充值` / `注册赠送` / `优惠活动`. Omit it to leave wagering unchanged. |
-| `wager_amount` | No | Used only when `recharge_type` is not `人工充值`: the wagering to deduct, ≥0. With `人工充值`, the system deducts amount × platform multiple. |
+| `recharge_type` | 否 | 本次下分对应的充值类型:`人工充值` / `注册赠送` / `优惠活动`。不传则不调整打码量。 |
+| `wager_amount` | 否 | 仅 `recharge_type` 不是 `人工充值` 时生效:要扣除的打码量,≥0。`人工充值` 由系统按 金额 × 平台倍数 扣除。 |
 
-The list endpoint `options` gains `platform_wager_type` (`人工充值`) and `withdraw_wager_multiple` (the current multiple), so the form can preview the result. The balance log for a debit still uses change type `人工扣款`.
+列表接口的 `options` 新增 `platform_wager_type`(`人工充值`)和 `withdraw_wager_multiple`(当前倍数),表单可据此预览。下分的资金日志类型仍为 `人工扣款`。
 
-### Member list: legacy 上下分
+### 会员列表(旧版上下分)
 
-`POST /admin/wallet/topUp` takes `wager_amount`, and `POST /admin/wallet/debiting` takes `recharge_type` and `wager_amount`. Both follow the same rules as above.
+`POST /admin/wallet/topUp` 支持 `wager_amount`,`POST /admin/wallet/debiting` 支持 `recharge_type` 和 `wager_amount`,规则同上。
 
-## 3. Freeze / unfreeze
+## 3. 冻结 / 解冻
 
-Same params and response for both. The member list and 人工稽查 each have their own permission URI:
+两个操作参数与返回相同,会员列表和人工稽查各有独立的权限 URI:
 
-| Action | Member list | 人工稽查 |
+| 操作 | 会员列表 | 人工稽查 |
 |---|---|---|
-| Freeze | `POST /admin/user/freeze` | `POST /admin/manualAudit/freeze` |
-| Unfreeze | `POST /admin/user/unfreeze` | `POST /admin/manualAudit/unfreeze` |
+| 冻结 | `POST /admin/user/freeze` | `POST /admin/manualAudit/freeze` |
+| 解冻 | `POST /admin/user/unfreeze` | `POST /admin/manualAudit/unfreeze` |
 
-| Param | Required | Notes |
+| 参数 | 必填 | 说明 |
 |---|---|---|
-| `request_id` | Yes | UUID generated by the front end per submission. Resubmitting the same ID returns the original result; submitting it with different content is rejected. |
-| `member_id` / `account` | One of the two | Same as 人工稽查 |
-| `amount` | Yes | Amount, ≥0.01 |
-| `wager_amount` | Yes | Wagering, ≥0. Freezing **reduces** wagering by this amount; unfreezing **increases** it. |
-| `reason` | Yes | Reason, at most 255 characters |
+| `request_id` | 是 | 前端每次提交生成的 UUID。同一 ID 重复提交返回原结果;内容不同则拒绝。 |
+| `member_id` / `account` | 二选一 | 同人工稽查 |
+| `amount` | 是 | 金额,≥0.01 |
+| `wager_amount` | 是 | 打码量,≥0。冻结时**减少**该打码量,解冻时**增加**该打码量。 |
+| `reason` | 是 | 原因,最多 255 字 |
 
-- Freeze: moves available balance into frozen balance. Fails if available balance is too low.
-- Unfreeze: moves frozen balance back to available balance. The cap is the user's **outstanding manual freeze amount**; money frozen for a withdrawal under review cannot be unfrozen manually. When over the cap, the error says `可解冻金额不足,最多可解冻:X`.
-- Each operation writes a balance log of type `人工冻结` (−amount) or `人工解冻` (+amount).
+- 冻结:可用余额转入冻结余额,可用余额不足则失败。
+- 解冻:冻结余额退回可用余额。上限为该会员**尚未解冻的人工冻结金额**,审核中提现冻结的资金不能人工解冻。超出时提示 `可解冻金额不足,最多可解冻:X`。
+- 每次操作写一条资金日志:`人工冻结`(−金额)或 `人工解冻`(+金额)。
 
-Response `data` (a single freeze record; same fields as the list below):
+返回 `data`(单条冻结记录,字段同下方列表):
 
 ```json
 { "id": 12, "member_id": "6001", "account": "tg123", "type": "manual_freeze", "type_text": "人工冻结",
@@ -81,26 +81,26 @@ Response `data` (a single freeze record; same fields as the list below):
   "finished_at": null, "created_at": "2026-11-04 15:00:00" }
 ```
 
-## 4. Freeze record list
+## 4. 冻结记录列表
 
 `GET /admin/walletFreeze`
 
-| Param | Notes |
+| 参数 | 说明 |
 |---|---|
-| `page`, `limit` | `limit` ≤200, default 20 |
-| `member_id`, `account` | Filter by member |
+| `page`、`limit` | `limit` ≤200,默认 20 |
+| `member_id`、`account` | 按会员筛选 |
 | `type` | `manual_freeze` 人工冻结 / `manual_unfreeze` 人工解冻 / `withdraw_hold` 提现冻结 |
 | `status` | `frozen` 已冻结 / `done` 已解冻 / `holding` 提现审核中 / `released` 已退回余额 / `settled` 提现成功已扣除 |
-| `start_date`, `end_date` | Y-m-d, pass both together |
+| `start_date`、`end_date` | Y-m-d,须同时传 |
 
-Response: `data.total`, `data.data[]` (fields as in section 3), and `data.options.types` / `data.options.statuses` (each item has `label` and `value`). For a withdrawal freeze, `related_type` is `withdraws` (USDT) or `payment_orders` (RMB) and `related_id` is the withdrawal order ID.
+返回:`data.total`、`data.data[]`(字段同第 3 节)、`data.options.types` / `data.options.statuses`(每项含 `label`、`value`)。提现冻结的 `related_type` 为 `withdraws`(USDT)或 `payment_orders`(RMB),`related_id` 为提现单 ID。
 
-## 5. Permissions and menu
+## 5. 权限与菜单
 
-The migration adds a 会员管理 → 冻结记录 menu (front-end route key `walletFreeze`) and these buttons:
+迁移新增 会员管理 → 冻结记录 菜单(前端路由 key `walletFreeze`)及以下按钮:
 
 - `admin/walletFreeze`
-- `admin/user/freeze`, `admin/user/unfreeze`
-- `admin/manualAudit/freeze`, `admin/manualAudit/unfreeze`
+- `admin/user/freeze`、`admin/user/unfreeze`
+- `admin/manualAudit/freeze`、`admin/manualAudit/unfreeze`
 
-After launch, assign these buttons to the roles that need them. Roles that don't have them cannot call these endpoints; the super admin is not restricted.
+上线后请给需要的角色分配这些按钮。未分配的角色无法调用这些接口;超级管理员不受限制。

+ 36 - 37
docs/前端接口/提现打码量与冻结记录-前台接入.md

@@ -1,59 +1,58 @@
 # 提现打码量与冻结记录:前台接入
 
-The endpoints in this doc are served by `melbet_sport-api` and require member login.
+接口项目:`melbet_sport-api`,均需会员登录。
 
-## Rules
+## 规则
 
-- **Before withdrawing, all wagering must be complete.** Required wagering is the sum of: deposits × the platform multiple, plus each activity's configured wagering, plus any wagering an admin entered. It is met with effective bets. If any amount remains, withdrawal is blocked.
-- Once a withdrawal request goes through, the current wagering and remaining wagering both reset to 0, and a new cycle starts.
-- Activity rewards go straight into available balance. Rewards with no wagering requirement can be withdrawn immediately.
-- When a withdrawal is requested, the amount moves into **frozen balance**. If the request is rejected or withdrawn, the money returns to available balance. If it succeeds, the money is deducted from frozen balance.
-- The balance endpoints show current wagering refreshed about once a minute. A withdrawal request recomputes it live, but a bet only counts once it is settled and synced into the bet records (usually within a minute).
+- **提现前所有打码量必须完成。**所需打码量 = 充值 × 平台倍数 + 各活动配置的打码量 + 后台填写的打码量,由有效投注完成;只要还有剩余,就不能提现。
+- 提现申请成功后,当前打码量和剩余打码量都归 0,开始新的周期。
+- 活动赠送直接进入可用余额;没有打码要求的赠送可以直接提现。
+- 申请提现时金额转入**冻结余额**;审核拒绝或撤回后退回可用余额,成功则从冻结余额扣除。
+- 余额接口中的当前打码量约每分钟刷新一次;提现申请时会实时重算,但注单需结算并同步到投注记录后才计入(通常一分钟内)。
 
-## 1. Account balance
+## 1. 账户余额
 
-`GET /api/wallet/getUsdt` (existing endpoint). `data` gains these fields:
+`GET /api/wallet/getUsdt`(现有接口),`data` 新增字段:
 
-| Field | Meaning |
+| 字段 | 含义 |
 |---|---|
-| `money` | Available balance (existing) |
-| `frozen_money` | Frozen balance (existing): withdrawals under review plus admin freezes |
-| `total_money` | Total balance = available + frozen |
-| `wager_required` | Required wagering in the current cycle |
-| `wager_completed` | Current wagering: effective bets placed in this cycle |
-| `wager_remaining` | Remaining wagering; the user can withdraw when this is `0.00` |
+| `money` | 可用余额(原有) |
+| `frozen_money` | 冻结余额(原有):审核中的提现 + 后台冻结 |
+| `total_money` | 总余额 = 可用 + 冻结 |
+| `wager_required` | 本周期所需打码量 |
+| `wager_completed` | 当前打码量:本周期的有效投注 |
+| `wager_remaining` | 剩余打码量,为 `0.00` 时可以提现 |
 
-The user info endpoint (`UserLogic::info`, the existing endpoint) returns the same `total_money`, `wager_required`, `wager_completed` and `wager_remaining`.
+用户信息接口(`UserLogic::info`,现有接口)同样返回 `total_money`、`wager_required`、`wager_completed`、`wager_remaining`。
 
-## 2. Withdrawal
+## 2. 提现
 
-`POST /api/wallet/withdraw` (existing endpoint, parameters unchanged). When wagering is incomplete, the call returns a failure with the message `打码量未完成,剩余打码量:50.00`. Show this message as-is, or show `wager_remaining` from the balance endpoint.
+`POST /api/wallet/withdraw`(现有接口,参数不变)。打码量未完成时返回失败,提示 `打码量未完成,剩余打码量:50.00`,可直接展示该提示,或展示余额接口的 `wager_remaining`。
 
-Withdrawal cancellation, `withdrawCancel`, is unchanged; frozen money returns to available balance.
+提现撤回 `withdrawCancel` 用法不变,冻结的资金退回可用余额。
 
-## 3. Freeze records
+## 3. 冻结记录
 
 ```http
 GET /api/wallet/frozenRecords?page=1&limit=20
 ```
 
-| Parameter | Notes |
+| 参数 | 说明 |
 |---|---|
-| `page`, `limit` | Same as other list endpoints |
-| `type` | Optional: `manual_freeze` (人工冻结) / `manual_unfreeze` (人工解冻) / `withdraw_hold` (提现冻结) |
-| `start_time`, `end_time` | Optional, Y-m-d |
+| `page`、`limit` | 同其他列表接口 |
+| `type` | 可选:`manual_freeze`(人工冻结)/ `manual_unfreeze`(人工解冻)/ `withdraw_hold`(提现冻结) |
+| `start_time`、`end_time` | 可选,Y-m-d |
 
-Response `data`: `count` (total), `list` (records), `extend.type_list` (type filter options). Each item in `data.list[]`:
+返回 `data`:`count`(总数)、`list`(记录)、`extend.type_list`(类型筛选项)。`data.list[]` 每项:
 
-| Field | Meaning |
+| 字段 | 含义 |
 |---|---|
-| `id` | Record ID |
-| `type` / `type_text` | Type and its display text |
-| `amount` | Amount |
-| `wager_amount` | Wagering change: an admin freeze reduces wagering, an unfreeze increases it |
-| `reason` | Reason |
-| `status` / `status_text` | `frozen` 已冻结, `done` 已解冻, `holding` 提现审核中, `released` 已退回余额, `settled` 提现成功已扣除 |
-| `related_type`, `related_id` | For withdrawal freezes, the matching withdrawal order (`withdraws` for USDT, `payment_orders` for RMB) |
-| `created_at` | Freeze time |
-| `finished_at` | When a withdrawal freeze was returned or deducted |
-
+| `id` | 记录 ID |
+| `type` / `type_text` | 类型及展示文字 |
+| `amount` | 金额 |
+| `wager_amount` | 打码量变动:后台冻结减少打码量,解冻增加打码量 |
+| `reason` | 原因 |
+| `status` / `status_text` | `frozen` 已冻结、`done` 已解冻、`holding` 提现审核中、`released` 已退回余额、`settled` 提现成功已扣除 |
+| `related_type`、`related_id` | 提现冻结对应的提现单(`withdraws` 为 USDT,`payment_orders` 为 RMB) |
+| `created_at` | 冻结时间 |
+| `finished_at` | 提现冻结退回或扣除的时间 |

+ 51 - 49
docs/后端/提现打码量与冻结金额-部署说明.md

@@ -1,58 +1,60 @@
 # 提现打码量与冻结金额:部署说明
 
-This change spans bot-28 and melbet_sport-api, which share one database. **Deploy in this order:**
-
-1. bot-28: run the migrations.
-   - `2026_11_04_140000_create_wagering_and_wallet_freezes` creates the wallet wagering columns, `wager_records`, `wallet_freeze_records`, `frozen_amount` on withdrawal orders, `manual_audits.wager_amount`, config `withdraw_wager_multiple=1`, and `updated_at` indexes on `share_bet_facts`, `bets` and `jisu_game_order`. It also backfills each wallet's cycle start to its most recent withdrawal time (or the wallet's creation time if it has none).
-   - `2026_11_04_141000_add_wallet_freeze_admin_menus` adds the 冻结记录 menu and the freeze/unfreeze buttons.
-2. Deploy the bot-28 code.
-3. Deploy melbet_sport-api. Its code checks whether the wallet has the new columns; before the migration has run, it keeps the old behaviour.
-4. Initial calculation: run `php artisan wagering:sync --all` once. After that the scheduler runs `wagering:sync` every minute.
-5. Migrate frozen rewards, dry run first:
+本次改动涉及 bot-28 和 melbet_sport-api,两者共用同一数据库。**请按以下顺序部署:**
+
+1. bot-28 执行迁移:
+   - `2026_11_04_140000_create_wagering_and_wallet_freezes`:新增钱包打码量字段、`wager_records`、`wallet_freeze_records`、提现单 `frozen_amount`、`manual_audits.wager_amount`,配置 `withdraw_wager_multiple=1`,以及 `share_bet_facts`、`bets`、`jisu_game_order` 的 `updated_at` 索引。同时把每个钱包的周期开始时间回填为最近一次提现时间(没有提现则为钱包创建时间)。
+   - `2026_11_04_141000_add_wallet_freeze_admin_menus`:新增“冻结记录”菜单和冻结/解冻按钮。
+2. 部署 bot-28 代码。
+3. 部署 melbet_sport-api。代码会检查钱包是否已有新字段,迁移前保持旧逻辑。
+4. 首次计算:执行一次 `php artisan wagering:sync --all`,之后由定时任务每分钟执行 `wagering:sync`。
+5. 迁移冻结奖励,先 dry-run:
    ```bash
    php artisan wagering:migrate-frozen-rewards --dry-run -v
+   ```
+   ```bash
    php artisan wagering:migrate-frozen-rewards
    ```
-6. In the admin role settings, assign the new buttons: `admin/walletFreeze`, `admin/user/freeze|unfreeze`, `admin/manualAudit/freeze|unfreeze`.
+6. 在后台角色设置中分配新按钮:`admin/walletFreeze`、`admin/user/freeze|unfreeze`、`admin/manualAudit/freeze|unfreeze`。
 
-## Behaviour changes
+## 行为变化
 
-| Area | Before | Now |
+| 场景 | 之前 | 现在 |
 |---|---|---|
-| Deposits (USDT, third-party, approved manual deposits, admin 人工充值) | No wagering | Adds amount × `withdraw_wager_multiple` |
-| 即充即送 / 老用户回归 | Credited to frozen balance, unfrozen at 1x of the bet amount | Credited to available balance. With a wagering multiple configured, adds bonus × multiple; without one, **adds no wagering** (the old 1x fallback is gone) |
-| New activities (first deposit, net-loss invite, wheel) | Rewards with turnover were `locked` in frozen balance | Credited to available balance and the status is set straight to `paid`. `required_flow` counts toward wagering |
-| 笔笔返 / 回水 | Credited to available balance; wagering never enforced | Unchanged, but `need_flow` now counts toward wagering |
-| Applied activities (activity_user) | Withdrawals blocked while in progress (only PC28 bets counted) | Gift wagering counts toward the unified wagering. The Telegram withdrawal check now uses the unified wagering |
-| Withdrawal request | Deducted from available balance | Checks wagering, moves available balance to frozen balance, writes a `withdraw_hold` record, resets the cycle |
-| Rejection / provider failure / user cancellation | Refunded to available balance | Returned from frozen balance to available balance |
-| Approval / provider success callback | No money movement | Deducted from frozen balance |
-| Orders pending at deploy time (`frozen_amount=0`) | — | Keep the old logic: a rejection refunds to available balance, a success deducts nothing |
-
-Balance log change types for deductions and refunds are unchanged (`提现` / `三方提现` / `人工提现`), so reporting definitions are unaffected. New types `人工冻结` and `人工解冻` are added; reports do not count them.
-
-## Related fixes
-
-- The log amount for a rejected USDT withdrawal was empty; it is now recorded correctly.
-- The QianBao payout callback had no status guard, so a repeated callback could refund twice. It now only processes orders that are in progress.
-- 人工稽查 top-ups and the legacy member-list top-up now save the wallet before writing the balance log. Previously the first-deposit reward and other deposit hooks wrote to the wallet in the same transaction and were overwritten by the caller's later save.
-- Every withdrawal request, rejection and callback path now locks the wallet row.
-- melbet bank withdrawals wrote the ledger key `transaction_type` instead of `change_type`, so the debit had no change type. It is now logged as `三方提现` (the same type bot-28 uses for payout orders and their refunds) and linked to its order through `related_id`.
-- Withdrawal requests recompute current wagering live outside the wallet lock (bot-28 with `PromotionFacts`, melbet with its port of it); only the cached value is checked under the lock.
-- If `wagering:sync` fails for some members, the time window still moves forward and those members are retried on the next run.
-
-## What the migration command does
-
-`wagering:migrate-frozen-rewards`:
-
-- Still-frozen 即充即送 / 老用户回归 logs (`type=2, frozen_status=1`): moved to available balance with paired `流水解冻` logs. Wagering added is the matching `wallet_bonus.need_flow`, or 1x the bonus if there is none, minus the user's old `wallets.flow_balance` carry-over, which is then reset to 0.
-- New-activity rewards still `locked`: moved to available balance and set to `paid`. Wagering added is `required_flow` minus effective bets since the reward was paid, floored at 0.
-- In-progress `activity_user` rows: no money moves; the remaining wagering (`betting_amount` − `effective_betting_amount`) is added.
-- Each processed source writes a `wager_records` row with `source=migration`, so running the command again does not reprocess anything. If a user's frozen balance is too low, that user is skipped with a warning and the command exits with a failure code; that user needs manual handling.
-- 余额宝利息 (Yuebao interest) is not an activity, so it stays on melbet's old 1x unlock.
-
-## Troubleshooting
-
-- Wagering changes for a user: `wager_records`, filtered by `member_id`. Values for `source`: `recharge`, `activity:*`, `manual_credit` / `manual_debit`, `freeze` / `unfreeze`, `migration`, `reset`.
-- Recalculate one user: `php artisan wagering:sync --member=<member ID>`.
-- Withdrawal freezes: `wallet_freeze_records` where `type='withdraw_hold'`, joined to the order through `related_type` and `related_id`.
+| 充值(USDT、三方、前台人工充值审核到账、后台人工充值) | 无打码量 | 增加 金额 × `withdraw_wager_multiple` |
+| 即充即送 / 老用户回归 | 进冻结余额,按投注额1倍解冻 | 进可用余额。配置了打码倍数的增加 奖金 × 倍数;未配置的**不增加打码量**(去掉了原来的1倍兜底) |
+| 新活动(首充、净亏邀请、转盘) | 带流水要求的奖励 `locked` 在冻结余额 | 进可用余额,状态直接为 `paid`,`required_flow` 计入打码量 |
+| 笔笔返 / 回水 | 进可用余额,打码量从未生效 | 不变,但 `need_flow` 现在计入打码量 |
+| 申请活动(activity_user) | 进行中时禁止提现(只统计 PC28 投注) | 赠送打码量计入统一打码量;Telegram 提现校验改为统一打码量 |
+| 提现申请 | 扣可用余额 | 校验打码量 → 可用余额转入冻结余额 → 写 `withdraw_hold` 记录 → 重置周期 |
+| 拒绝 / 三方失败 / 用户撤回 | 退回可用余额 | 从冻结余额退回可用余额 |
+| 审核通过 / 三方成功回调 | 无资金变动 | 从冻结余额扣除 |
+| 上线时仍在处理中的旧单(`frozen_amount=0`) | — | 保持旧逻辑:拒绝退回可用余额,成功不再扣款 |
+
+提现扣款与退款的资金日志类型不变(`提现` / `三方提现` / `人工提现`),报表口径不受影响。新增类型 `人工冻结`、`人工解冻`,报表不统计。
+
+## 相关修复
+
+- USDT 提现拒绝时日志金额为空,现已正确记录。
+- 钱宝代付回调没有状态校验,重复回调可能重复退款;现只处理“处理中”的订单。
+- 人工稽查上分和旧版会员列表上分改为先保存钱包再写资金日志。之前首充奖励等充值钩子在同一事务内写入钱包,会被调用方随后的保存覆盖。
+- 所有提现申请、拒绝和回调路径都锁定钱包行。
+- melbet 银行卡提现的资金日志写成了 `transaction_type` 而不是 `change_type`,扣款没有类型。现记为 `三方提现`(与 bot-28 代付单及其退款同一类型),并通过 `related_id` 关联订单。
+- 提现申请时在钱包行锁之外实时重算当前打码量(bot-28 用 `PromotionFacts`,melbet 用其移植版),锁内只用缓存值判断。
+- `wagering:sync` 部分会员刷新失败时,时间窗口照常前进,失败会员在下次运行重试。
+
+## 迁移命令说明
+
+`wagering:migrate-frozen-rewards`:
+
+- 仍冻结的 即充即送 / 老用户回归 日志(`type=2, frozen_status=1`):转入可用余额,并成对写 `流水解冻` 日志。增加的打码量为对应 `wallet_bonus.need_flow`(同一笔充值的同额奖励各自匹配一条),没有则按奖金1倍,再减去会员旧的 `wallets.flow_balance` 结余,结余随后归 0。
+- 仍为 `locked` 的新活动奖励:转入可用余额并改为 `paid`。增加的打码量为 `required_flow` 减去发奖后的有效投注,最低为 0。
+- 进行中的 `activity_user`:不动资金,增加剩余打码量(`betting_amount` − `effective_betting_amount`)。上线后按新流程发放、已记过打码量的活动会跳过。
+- 每处理一个来源写一条 `source=migration` 的 `wager_records`,重复执行不会重复处理。会员冻结余额不足时跳过并告警,命令以失败码退出,需人工处理该会员。
+- 余额宝利息不是活动,仍走 melbet 原有的1倍解冻。
+
+## 排查
+
+- 会员打码量变动:`wager_records` 按 `member_id` 查询。`source` 取值:`recharge`、`activity:*`、`manual_credit` / `manual_debit`、`freeze` / `unfreeze`、`migration`、`reset`。
+- 重算单个会员:`php artisan wagering:sync --member=<会员ID>`。
+- 提现冻结:`wallet_freeze_records` 中 `type='withdraw_hold'`,通过 `related_type`、`related_id` 关联提现单。