Files
cursor/plans/dailyrebatelogic重构_50d62b71.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

133 lines
6.0 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: DailyRebateLogic重构
overview: 按 `php-code` 规范重构每日返水领取链路:`claim` 编排化、BusinessException、Model 条件更新;同步调整 Controller 异常映射。`settleDate`/`info` 仅做必要小抽取,不改业务语义。
todos:
- id: model-mark-claimed
content: DailyRebateRecordModel 增加 markClaimedIfClaimable + PHPDoc
status: pending
- id: logic-claim-refactor
content: DailyRebateLogicclaim 拆 assert/perform/formatBusinessException
status: pending
- id: logic-info-helper
content: 可选isClaimableRecord 供 info 与 assert 复用
status: pending
- id: controller-exception
content: DailyRebateControllercatch BusinessException → FAILED
status: pending
- id: verify-smoke
content: 跑 verify-slot-backend.sh确认无 RuntimeException 新增
status: pending
isProject: false
---
# DailyRebateLogic 规范重构
## 范围AskQuestion 中断,按推荐默认)
- **必做**[`claim`](slot_console/app/api/logic/DailyRebateLogic.php)、[`DailyRebateController::claim`](slot_console/app/api/controller/DailyRebateController.php)、[`DailyRebateRecordModel`](slot_console/app/model/common/DailyRebateRecordModel.php)
- **轻量**`info()` 抽取「是否可领」判断为 private`assertClaimableRecord` 复用逻辑
- **不改语义**`settleDate` / `expireDueRecords` / Redis 结算逻辑保持行为一致,仅在有重复代码时抽 1 个 private可选
## 现状问题(对照 php-code
[`claim`](slot_console/app/api/logic/DailyRebateLogic.php) L92157
- 6 段 `RuntimeException` if 墙
- CAS 写在 Logic 内联,未沉淀 Model
- Controller `catch RuntimeException` + `PARAMS_ERROR`verify 会拦新增行)
参照:[`FreeCreditsLogic::claim`](slot_console/app/api/logic/FreeCreditsLogic.php)`assert*` + `BusinessException` + 状态更新)
## 目标结构
```mermaid
sequenceDiagram
participant C as DailyRebateController
participant L as DailyRebateLogic
participant M as DailyRebateRecordModel
participant W as WalletService
C->>L: claim(uid, source, statDate)
L->>L: assertActivityEnabled
L->>L: assertUserDeposited
L->>M: findByUidAndDate
L->>L: assertClaimableRecord
L->>M: markClaimedIfClaimable
L->>W: gift(rebate, DAILY_REBATE, remark)
L-->>C: formatClaimResult
```
### 1. Model条件更新
在 [`DailyRebateRecordModel`](slot_console/app/model/common/DailyRebateRecordModel.php) 新增:
```php
/**
* 待领取状态下标记为已领取CAS
*
* @return int 影响行数1 表示成功
*/
public static function markClaimedIfClaimable(int $id): int
```
实现:`where id` + `where status = STATUS_CLAIMABLE``STATUS_CLAIMED` + `claimed_at`
### 2. Logic`claim` 拆分为编排 + assert + perform
| 方法 | 职责 |
|------|------|
| `claim()` public | 编排 ≤20 行:`resolveSource` → 默认 `statDate` → assert → `performClaim` → 返回 |
| `assertActivityEnabled(string $source)` | 活动未开 → `BusinessException` |
| `assertUserDeposited(int $uid)` | 未充值 → `BusinessException` |
| `assertClaimableRecord(?DailyRebateRecordModel $record)` | 不存在/状态/金额/过期 → 各一条 `BusinessException`(合并原 4 条 if |
| `performClaim(int $uid, DailyRebateRecordModel $record, string $statDate)` | 事务:`markClaimedIfClaimable``WalletService::gift` → commit`affected !== 1` 或 wallet 空 → `BusinessException` |
| `formatClaimResult(...)` | 返回 `stat_date` / `rebate_amount` / `display` / `balance` |
- 使用 `support\exception\BusinessException`(与 FreeCredits 一致)
- `@throws` 改为 `BusinessException``\Throwable`(钱包失败)
- **钱包 biz_id**`WalletService::gift()` 内部已 `generateOrderId`[`slot_lib/src/services/WalletService.php`](slot_lib/src/services/WalletService.php) L262265幂等依赖 **记录 CAS**remark 保持 `每日返水 {statDate}` 便于对账
### 3. Logic`info` 轻量复用(可选)
抽取 `isClaimableRecord(DailyRebateRecordModel $record): bool`status + amount + expire`info()``claimable` 块与 `assertClaimableRecord` 共用,避免两套判断漂移。
### 4. Controller异常映射
[`DailyRebateController::claim`](slot_console/app/api/controller/DailyRebateController.php)
```php
} catch (BusinessException $e) {
return $this->errorCode(ErrorCode::FAILED, $e->getMessage());
}
```
- 对齐 [`SignController`](slot_console/app/api/controller/SignController.php)(业务失败 `FAILED` + 文案)
- **API 变更**`code``40003`PARAMS_ERROR变为 `1`FAILED文案仍为中文业务提示。若 PWA 强依赖 `40003`,可在计划中改为保留 `PARAMS_ERROR`(实现时二选一,默认 `FAILED`
不采用 FreeCredits「无 catch、走全局 Handler」方式避免未捕获时变成 `SYSTEM_ERROR`50001
### 5. 不动 / 谨慎
- **事务边界**:维持「先 CAS 再 gift 再 commit」不在此 PR 改为「先 wallet 后 DB」或 Outbox
- **`settleDate`**:逻辑不变;可选抽 `settleOneUserFromRedis(...)` 降低 foreach 嵌套(非必须)
## 文件清单
| 文件 | 变更 |
|------|------|
| `slot_console/app/model/common/DailyRebateRecordModel.php` | +`markClaimedIfClaimable` |
| `slot_console/app/api/logic/DailyRebateLogic.php` | 重构 `claim`;可选 `isClaimableRecord` |
| `slot_console/app/api/controller/DailyRebateController.php` | `BusinessException` + `FAILED` |
## 验收
1. `docker exec -w /app/www/slot/slot_console php82 php webman dailyRebateSettle --date=...`(如有环境)结算后,已充值用户可领昨日返水
2. 重复领取 → 业务错误文案DB 仍为 `CLAIMED`,不重复入账
3. `~/.cursor/hooks/verify-slot-backend.sh` → PASS无新增 `RuntimeException` 业务态)
4. 最终回复含 `PHPDoc: checked`(触及符号补全 `@throws BusinessException`
## 风险与回滚
- **PWA 错误码**:若前端按 `code===40003` 分支,需同步前端或 Controller 保留 `PARAMS_ERROR`
- 回滚:还原 3 个文件即可