Files
cursor/plans/首充剩余分档规则_509262bd.plan.md
ray zhou ad623aad91 ok
2026-05-29 18:16:34 +08:00

179 lines
8.3 KiB
Markdown
Raw 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.

---
name: 首充剩余分档规则
overview: 将 Free Credits「剩余金额定格额 第一档 $20」从固定 package_amount 均分,改为按 10/20/30/50/100 元阶梯瀑布拆分;后续档解锁改为「累计充值 ≥ 每档门槛(标准档=面额−$1尾档 unlock 按大单位整数向下取整,如 15340qf→15000qf」。
todos:
- id: split-algorithm
content: 实现 buildReleasePackages 瀑布拆分,替换 splitReleasePackageAmounts + createPackages
status: pending
- id: db-unlock-qf
content: free_credits_package 增加 unlock_recharge_qfinstall.sql + 迁移脚本
status: pending
- id: advance-unlock
content: advanceByRecharge 按累计充值+unlock_recharge_qf 解锁;存量 unlock=0 回退旧逻辑
status: pending
- id: admin-config
content: 废弃 package_amount/subsequent_min 后台必填;更新 ActivityValidate
status: pending
- id: tests-docs
content: 单测/集成测 + 需求文档 §5.7/5.8 更新
status: pending
isProject: false
---
# 首充剩余定格 — 分档与解锁规则变更
## 背景与现状
核心逻辑在 [`slot_console/app/api/logic/FreeCreditsLogic.php`](slot_console/app/api/logic/FreeCreditsLogic.php)
- **定格剩余**`leftAmount = frozenAmount - firstCashAmount`(第一档默认 $20来自 `ext_config.first_cash_amount`
- **当前拆分**`splitReleasePackageAmounts($left, package_amount)` — 每档 `min(package_amount, 剩余)` 循环切分(默认 $10/档)
- **当前解锁**:第一档看 `totalRecharge >= recharge_unlock_amount`(默认 $50后续档看 **本笔** `rechargeAmount >= subsequent_min_recharge`(默认 $10且受 `max_unlock_per_recharge` 限制
需求文档旧版 §5.7 见 [`docs/requirements/首充前免费余额定格与分档释放需求文档.md`](docs/requirements/首充前免费余额定格与分档释放需求文档.md)。
## 新规则(产品口径)
**基数**`剩余 = 定格金额 第一档免打码提现额($20`
按顺序「吃掉」剩余金额(瀑布式,非按总额选一档):
| 阶段 | 单档面额 | 本阶段最多档数 | 累计充值解锁门槛(每档) |
|------|---------|---------------|------------------------|
| 1 | $10 | 3 | ≥ $9 |
| 2 | $20 | 5 | ≥ $19 |
| 3 | $30 | 10 | ≥ $29 |
| 4 | $50 | 10 | ≥ $49 |
| 5 | $100 | 直到剩余 < $100 | ≥ $99 |
| 6 | 尾档 | 剩余 < $100 的余额,**一整档**`amount_qf` 可为小数大单位,如 $15.34 | **累计充值门槛 = 面额按大单位整数向下取整**(见下) |
金额在代码中为 **千分位整数qf**$10 → `10000`
### 示例(定格 $78.50,第一档 $20剩余 $58.50
```
阶段1: 3×$10 = $30 → 剩 $28.50
阶段2: 1×$20 = $20 → 剩 $8.50
阶段35: 不足整档,跳过
阶段6: 1×$8.50 → 剩 $0unlock_recharge_qf = 8000即 $8
```
共 5 个 release 档:`10,10,10,20,8.5`(旧规则为 6 档均 $10 + 尾档)。
### 尾档解锁门槛:大单位整数(不允许小数)
尾档 **`amount_qf` 仍保留实际剩余**(可含「分」级精度,如 `15340` qf = $15.34),但写入 `unlock_recharge_qf` 时必须 **向下取整到大单位整数美元**
```text
unlock_recharge_qf = intdiv(amount_qf, 1000) * 1000 // 去掉 qf 余数,等价 floor 到整元
```
| amount_qf小单位/千分位) | 档位展示面额 | unlock_recharge_qf累计充值门槛 |
|-------------------------|-------------|-----------------------------------|
| `15340` | $15.34 | `15000`$15非 $15.34 |
| `8500` | $8.50 | `8000`$8 |
| `10000` | $10.00 | `10000`$10 |
实现时抽私有方法,例如 `calcTailUnlockRechargeQf(int $amountQf): int`,仅用于阶段 6标准档10/20/30/50/100仍为 `amount - 1000` qf本身已是整元。
```mermaid
flowchart TD
left[剩余金额 leftQf]
b1["阶段1: 最多3档 x 10元"]
b2["阶段2: 最多5档 x 20元"]
b3["阶段3: 最多10档 x 30元"]
b4["阶段4: 最多10档 x 50元"]
b5["阶段5: 整百100元直到剩小于100"]
b6["阶段6: 尾档=剩余金额"]
left --> b1 --> b2 --> b3 --> b4 --> b5 --> b6
```
## 实现方案
### 1. 拆分算法(替换 `splitReleasePackageAmounts`
`FreeCreditsLogic` 中新增结构化方法,例如:
```php
/**
* @return list<array{amount_qf: int, unlock_recharge_qf: int}>
*/
public static function buildReleasePackages(int $leftAmountQf): array
```
- 用常量表描述 4 个固定阶段 `{amount: 10000|20000|30000|50000, maxCount: 3|5|10|10, unlock: amount-1000}`
- 阶段 5`while ($left >= 100000)` 追加 `{100000, 99000}`
- 阶段 6`if ($left > 0)` 追加 `{amount_qf: left, unlock_recharge_qf: calcTailUnlockRechargeQf(left)}`**unlock 为整元,不等于 left**
- 每阶段只取 `min(maxCount, floor(left / amount))` 个**整档**,余数进入下一阶段
`createPackages()` 改为遍历上述结构写入 `free_credits_package`,不再读 `package_amount` 配置。
### 2. 库表:每档解锁门槛
[`free_credits_package`](slot_console/db/install.sql) 当前无解锁门槛字段。建议新增:
```sql
unlock_recharge_qf bigint unsigned NOT NULL DEFAULT 0 COMMENT '解锁本档所需累计真实充值,千分位'
```
- 定格写入时一并落库
- **存量用户**(默认策略):已有行 `unlock_recharge_qf=0` 时,`advanceByRecharge` 回退旧逻辑(`subsequent_min_recharge` + 单笔充值),避免行为突变
- **新定格**:一律写入新门槛
(若产品确认「存量也迁移」,另做一次性 SQL/脚本按 player 重算 package 行 — 需单独评估已解锁/已领取档。)
### 3. 解锁逻辑(`advanceByRecharge`
后续 release 档变更:
| 项 | 旧 | 新 |
|----|----|-----|
| 条件 | `rechargeAmount >= subsequent_min` | `totalRecharge >= package.unlock_recharge_qf` |
| 顺序 | 仍按 `package_no` 逐档 | 不变 |
| 单笔上限 | `max_unlock_per_recharge` | **保留**(大额充值仍最多解锁 N 档) |
第一档逻辑不变(仍用 `recharge_unlock_amount`,默认 $50
### 4. 配置与后台
- [`package_amount`](backend/slot_admin_vue/src/views/game/activity/edit.vue) / [`subsequent_min_recharge`](backend/slot_admin_vue/src/views/game/activity/edit.vue):对新定格**不再参与拆分/解锁**;后台表单项可标为「已废弃」或隐藏,校验 [`ActivityValidate::checkFreeCreditsExt`](backend/slot_admin/app/game/validate/ActivityValidate.php) 改为非必填(避免运营误填)
- `max_unlock_per_recharge``first_cash_amount``recharge_unlock_amount` 继续有效
### 5. C 端 API可选增强
[`FreeCreditsController::status`](slot_console/app/api/controller/FreeCreditsController.php) 当前 `packages``id/amount/status`。若前端要展示「再充 $X 解锁下一档」,可在每项增加 `unlock_recharge_amount`(大单位 float**非必须**help 文案可先说明规则。
### 6. 测试
更新/新增 [`FreeCreditsLogicAmountTest.php`](slot_console/tests/Unit/FreeCreditsLogicAmountTest.php)
- DataProvider`58500` qf 剩余 → `[10000×3, 20000, 8500]`,尾档 unlock=`8000`(非 8500
- 尾档取整:`15340` → amount=`15340`, unlock=`15000`
- 大额:`1000000` qf 剩余 → 验证各阶段档数上限与总和守恒
- 边界:`left=0`、整阶段边界30/100/500…
集成测 [`FreeCreditsFirstRechargeFreezeTest`](slot_console/tests/Integration/FreeCreditsFirstRechargeFreezeTest.php) 断言 package 行数与金额。
解锁:在 harness 中 mock `totalRecharge`,验证 `unlock_recharge_qf` 达标后变 `ready`
### 7. 文档
更新 [`docs/requirements/首充前免费余额定格与分档释放需求文档.md`](docs/requirements/首充前免费余额定格与分档释放需求文档.md) §5.7、§5.8 与示例表。
## 影响范围(不改)
- `slot_wallet` 定格/冻结、Claim 入账
- `slot_pay` 第一档提现
- 第一档 $20 / 累计 $50 解锁第一档 — **本次需求未改**
## 风险与验收
- **金额守恒**`sum(release.amount_qf) === frozen - first_cash`
- **幂等**:定格仍按 `first_recharge_order_id` 幂等,不重复拆档
- **存量**:默认 grandfather上线前确认是否有在途「旧档位」用户
- 验收脚本:`verify-slot-backend.sh` + phpunit `FreeCreditsLogicAmountTest`
```bash
docker exec -w /app/www/slot/slot_console php82 ./vendor/bin/phpunit tests/Unit/FreeCreditsLogicAmountTest.php
```