ok
This commit is contained in:
132
plans/dailyrebatelogic重构_50d62b71.plan.md
Normal file
132
plans/dailyrebatelogic重构_50d62b71.plan.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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: DailyRebateLogic:claim 拆 assert/perform/format,BusinessException
|
||||
status: pending
|
||||
- id: logic-info-helper
|
||||
content: 可选:isClaimableRecord 供 info 与 assert 复用
|
||||
status: pending
|
||||
- id: controller-exception
|
||||
content: DailyRebateController:catch 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) L92–157:
|
||||
|
||||
- 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) L262–265);幂等依赖 **记录 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 个文件即可
|
||||
Reference in New Issue
Block a user