--- name: StartDTO 去重与规范 overview: 收紧 `GameLaunchSessionStartDTO` 为仅组合两个子 DTO + accessor 派生字段,简化 `launchWithSession` 编排;并在 `php-clean-code.mdc` 增加「禁止编排层双份拷贝 / DTO 重复标量」规则。 todos: - id: refactor-start-dto content: GameLaunchSessionStartDTO:仅 2 子 DTO + uid/providerCode/gameCode accessor status: completed - id: refactor-launch-logic content: 简化 launchWithSession;findReusableLaunch 收 StartDTO;createLaunchSession 用 accessor status: completed - id: update-rules-dto-dup content: php-clean-code §3 增加「编排上下文与聚合 DTO」;§8 与 agent-completion-gate 补充自查 status: completed - id: verify-start-dto content: docker php -l + SLOT_ROOT verify-slot-backend.sh status: completed isProject: false --- # StartDTO 去重重构 + 规范加固 ## 问题 [`launchWithSession`](slot-pwa/app/api/logic/GameLaunchSessionLogic.php) 中: ```php $uid = (int) $gameLaunchDto->uid; $providerCode = $gameLaunchContext->providerCode; $gameCode = $gameLaunchDto->gameCode; // ... new GameLaunchSessionStartDTO($gameLaunchDto, $gameLaunchContext, $uid, $providerCode, $gameCode); ``` [`GameLaunchSessionStartDTO`](slot-pwa/app/api/dto/GameLaunchSessionStartDTO.php) 同时持有 `gameLaunchDto`、`gameLaunchContext` **以及** 可从二者推导的 `uid` / `providerCode` / `gameCode`,属于 **编排层双份拷贝**,违反 DRY 与 [`php-clean-code.mdc`](/Users/ray/.cursor/rules/php-clean-code.mdc)「参数应表达业务需要、聚合 DTO 不重复字段」精神。 ```mermaid flowchart LR subgraph before [当前] DTO[GameLaunchDTO] CTX[GameLaunchContextDTO] locals[uid providerCode gameCode] Start[GameLaunchSessionStartDTO 5 fields] DTO --> locals CTX --> locals DTO --> Start CTX --> Start locals --> Start end ``` ## 一、代码重构 ### 1. 收紧 `GameLaunchSessionStartDTO` **文件:** [`slot-pwa/app/api/dto/GameLaunchSessionStartDTO.php`](slot-pwa/app/api/dto/GameLaunchSessionStartDTO.php) - 构造函数仅保留: - `GameLaunchDTO $gameLaunchDto` - `GameLaunchContextDTO $gameLaunchContext` - 派生字段通过 **accessor** 提供(单一数据源): - `uid(): int` ← `(int) $gameLaunchDto->uid` - `providerCode(): string` ← `$gameLaunchContext->providerCode` - `gameCode(): string` ← `$gameLaunchDto->gameCode` ### 2. 简化 `launchWithSession` **文件:** [`slot-pwa/app/api/logic/GameLaunchSessionLogic.php`](slot-pwa/app/api/logic/GameLaunchSessionLogic.php) 目标形态(可读性优先): ```php public function launchWithSession(GameLaunchDTO $gameLaunchDto): GameLaunchSessionResultDTO { $launchStart = new GameLaunchSessionStartDTO( $gameLaunchDto, $this->resolveGameLaunchContext($gameLaunchDto) ); $reusedLaunch = $this->findReusableLaunch($launchStart); if ($reusedLaunch !== null) { return $reusedLaunch; } return $this->createLaunchSessionAndFetchUrl($launchStart); } ``` - 删除 `$gameLaunchContext` / `$uid` / `$providerCode` / `$gameCode` 四个中间局部变量。 ### 3. `findReusableLaunch` 收参为 StartDTO 将签名由 `(int $uid, string $providerCode, string $gameCode)` 改为: ```php private function findReusableLaunch(GameLaunchSessionStartDTO $launchStart): ?GameLaunchSessionResultDTO ``` 方法内使用 `$launchStart->uid()`、`providerCode()`、`gameCode()`,避免再次拆三个标量传递。 ### 4. `createLaunchSessionAndFetchUrl` 统一 accessor - `$launchStart->uid` 等改为 `$launchStart->uid()`(及 `providerCode()`、`gameCode()`) - Provider `launch()` 调用统一为 `$launchStart->uid()` + `$launchStart->gameLaunchContext->externalGameCode`(不再混用 `$launchStart->gameLaunchDto->uid`) **改动范围:** 仅上述 2 个 PHP 文件,无 API / 路由变更。 ## 二、用户级规范更新 ### [`php-clean-code.mdc`](/Users/ray/.cursor/rules/php-clean-code.mdc) 在 **§3「参数与日志」** 后增加小节 **「编排上下文与聚合 DTO」**: - **禁止**编排方法先把子 DTO/Context 字段拆成局部变量,再原样传入聚合 DTO(双份拷贝)。反例:`$uid = $dto->uid` 后 `new StartDTO($dto, $ctx, $uid, ...)`。 - **禁止**聚合 DTO 构造函数同时接收「可从已有只读字段推导」的重复标量。反例:同时存 `gameLaunchDto` 与 `gameCode`。 - 聚合上下文 DTO 应 **只组合** 子对象;派生值用 `uid()` / `providerCode()` 等 accessor 从子对象读取,或在用例方法内直接使用子 DTO 字段(二选一,不并存)。 - 子步骤 private 方法若需多字段,优先传 **一个** 聚合 DTO,而不是再拆 3 个标量参数。 **§8 Agent 自查** 增加: - 是否存在「局部变量 + 聚合 DTO」双份承载同一业务字段? - 聚合 DTO 是否包含可推导的重复标量? ### [`agent-completion-gate.mdc`](/Users/ray/.cursor/rules/agent-completion-gate.mdc) 在 Logic 自查条(现有第 5 条)中补充:**§3 编排上下文与聚合 DTO**。 ## 三、验收 1. `launchWithSession` 无仅用于组 DTO 的 `$uid` / `$providerCode` / `$gameCode` 局部变量。 2. `GameLaunchSessionStartDTO` 构造函数仅 2 个参数。 3. `docker exec -w /app/www/ray/slot-pwa php82 php -l` 对相关文件通过。 4. `SLOT_ROOT=/Users/ray/Documents/project/www/ray ~/.cursor/hooks/verify-slot-backend.sh` → `PASS`。 ## 范围外 - 不改动 `GameLaunchContextDTO` / `GameLaunchSessionCreateParams` 结构。 - 不修改 verify 脚本(仍依赖规则 + 自查)。