--- name: PHP用例写法规则 overview: 在用户级 Cursor 新增 `php-use-case-style.mdc`,约束 slot 后端所有 PHP 业务代码(Controller/Logic/Service/Model/Command 等)的表达力与可维护性;并扩展 verify-slot-backend.sh 对 diff 中所有 touched 的 app/**/*.php 做轻量检查。仅约束本次 diff,不强制全盘重构历史代码。 todos: - id: create-mdc content: 创建 ~/.cursor/rules/php-use-case-style.mdc(全层 MUST/禁止、BAD/GOOD、按层对照表) status: completed - id: update-gate-rule content: 在 agent-completion-gate.mdc 增加对 php-use-case-style 的引用(任意 PHP 改动) status: completed - id: extend-verify content: 扩展 verify-slot-backend.sh:所有 app/**/*.php diff 检查 + @use-case-exempt status: completed - id: smoke-verify content: 分别在 Logic/Controller/Service diff 触发自测 PASS/FAIL/豁免 status: completed - id: merge-php-code content: 用例写法合并进 php-code.mdc,修 frontmatter/强制要求/命名 status: completed - id: fix-gate-refs content: agent-completion-gate 引用改为 php-code status: completed - id: smoke-rules content: 确认规则生效 + verify PASS status: completed isProject: false --- # PHP 代码写法规则 + Verify 门禁(全层) ## 目标 让 AI 在改 **任意 slot 后端 PHP 业务代码** 时默认产出:**编排清晰、职责单一、业务异常语义正确、状态变更有条件更新与幂等键**,并对齐仓库内参照实现。 适用范围(规则 + verify 一致): - `app/api/controller/**` - `app/api/logic/**`、`app/innerapi/logic/**`、`app/napi/logic/**` - `app/service/**` - `app/model/**` - `app/command/**` - 其它 `app/**` 下业务 PHP(`validate`、`entity` 等按条适用) **不覆盖**:`vendor/`、`tests/`(除非 diff 触及且单独约定)、纯配置/SQL 文件。 ## 默认范围 - **规则文件**:用户级 [`~/.cursor/rules/php-use-case-style.mdc`](~/.cursor/rules/php-use-case-style.mdc) - **生效方式**:`alwaysApply: true`(任意对话均注入;与 `backend-layering` 同级,避免只开 Logic 文件时漏规则) - **门禁脚本**:[`~/.cursor/hooks/verify-slot-backend.sh`](~/.cursor/hooks/verify-slot-backend.sh)(仅 `git diff` + 路径 `app/`) - **不在本阶段**:强制重构历史代码(如 `DailyRebateLogic::claim`);可另开样板 PR ## 1. 新增规则 `php-use-case-style.mdc` **Frontmatter**: ```yaml --- description: Slot PHP 写法——表达力、可维护、全层通用(Controller/Logic/Service/Model/Command) alwaysApply: true --- ``` ### 1.1 全层通用 MUST | 条目 | 要求 | |------|------| | 单一职责 | 一个 public 方法只做一件事;编排方法目标 ≤25 行,超出拆 private | | if 墙 | 禁止同一方法内连续 ≥4 个 `if (...) throw/return error`;合并为 `assert*` / `ensure*` | | 业务异常 | 可预期业务失败用 `support\exception\BusinessException`;禁止用 `RuntimeException` 表示业务态(未开启、不可领、已过期等) | | 魔法值 | 状态/类型用 Model 常量或 enum;禁止裸 `1/2/3` 散落(diff 新增代码) | | 命名 | 方法名表达意图(`assertClaimable`、`markClaimedIfClaimable`),禁止 `doClaim`、`handle` 等空泛名(新增代码) | | 参照 | 领取/入账编排:[`FreeCreditsLogic::claim`](slot_console/app/api/logic/FreeCreditsLogic.php);Controller:[`FreeCreditsController`](slot_console/app/api/controller/FreeCreditsController.php) | ### 1.2 按层补充(与 backend-layering 互补,不重复分层表) | 分层 | 写法 MUST | 禁止 | |------|-----------|------| | **Controller** | 仅:Validate → 调 Logic/Service → `success/errorCode`;`catch BusinessException` 映射业务码 | 业务 if 墙、直接 `Db::`、直接 `gift()` | | **Logic** | 用例编排:`buildContext` → `assert*` → `perform*` → `format*`;事务边界在此 | 纯转发 Model 无编排、跨服务裸 curl | | **Service** | 可复用能力封装;外部系统(wallet/sdk)隔离;稳定 `biz_id` | Logic 型 Service(单用例整条流水线) | | **Model** | 查询/写入 + `scope`/`mark*If*` 条件更新;金额字段注释单位(厘) | 业务编排、调 Wallet | | **Command** | 薄入口:参数解析 → 调 Logic;日志 phase 结构化 | 复制 Logic 大段 if 墙 | | **Validate** | 格式/必填/枚举 | 业务规则(「是否可领」应在 Logic assert) | ### 1.3 领取 / 入账 / 状态变更(凡涉及处均适用) - MUST:条件更新(`where status = expected` 再 `update`),鼓励 Model 方法 `mark*If*` - MUST:钱包/外部入账带稳定 `biz_id`(如 `daily_rebate:{uid}:{statDate}`) - Controller:禁止 `catch (\RuntimeException)` 后一律 `PARAMS_ERROR` ### 1.4 BAD / GOOD(规则内各一段,≤8 行) - BAD:Controller `catch RuntimeException` + Logic 5 连 throw + Service 仅 `return Model::find()` - GOOD:Controller 捕 `BusinessException`;Logic `assert*` + 条件更新;Service 封装 wallet + biz_id ### 1.5 与现有规则关系 - 分层职责:仍服从 [`backend-layering.mdc`](~/.cursor/rules/backend-layering.mdc) - 文档:仍服从 [`php-doc.mdc`](~/.cursor/rules/php-doc.mdc) - [`agent-completion-gate.mdc`](~/.cursor/rules/agent-completion-gate.mdc) 增加:**任意改动 `app/**/*.php` 须遵守 `php-use-case-style`** ## 2. 扩展 `verify-slot-backend.sh` 对 **`CHANGED_PHP` 且路径匹配 `*/app/*`** 的文件检查(不再限定 `/logic/`): ```bash # 1) diff 新增行含 throw new \RuntimeException(业务态误用) # 豁免:文件含 @use-case-exempt # 2) Controller(*/app/**/controller/*)diff 新增行: # catch (\RuntimeException 且同文件/邻近行 PARAMS_ERROR → fail # 3) 已有:BaseController、banlist、deleted class — 保持不变 ``` **原则**: - 只 FAIL **diff 新增行**(`git diff -U0` / `git diff --cached -U0`),不扫历史行 - `@use-case-exempt` 在文件前 5 行内则跳过该文件全部 use-case 检查 - 首期不 FAIL:方法行数、biz_id 参数名(写在规则 MUST,verify 二期) ## 3. 数据流 ```mermaid flowchart TB subgraph agent [Agent 改任意 app PHP] rule[php-use-case-style alwaysApply] layers[Controller Logic Service Model Command] end subgraph gate [完成前] verify[verify-slot-backend app/** diff] hook[stop hook followup] end rule --> layers layers --> verify verify -->|FAIL| hook verify -->|PASS| done[可声称完成] ``` ## 4. 验收 1. 打开任意 `app/service/*.php` 或 `DailyRebateController.php`,规则均应生效(alwaysApply) 2. 在 Controller diff 新增 `catch (\RuntimeException` + `PARAMS_ERROR` → verify FAIL 3. 在 Service diff 新增 `throw new \RuntimeException('活动未开')` → verify FAIL 4. 加 `@use-case-exempt` → 该文件跳过 5. `~/.cursor/hooks/verify-slot-backend.sh` 输出 PASS ## 5. 后续可选(本计划不含) - 样板 PR:重构 `DailyRebateLogic::claim` + `DailyRebateController::claim` - verify 二期:`gift(` / wallet 调用邻近 biz_id 检测;public 方法行数 WARN --- ## 6. 阶段二:合并进 `php-code.mdc`(待执行) 用户已将 PHPDoc 并入 [`php-code.mdc`](~/.cursor/rules/php-code.mdc);`php-use-case-style.mdc` 已不存在。verify 已具备 use-case 检查,但规则与 gate 引用断裂。本阶段只做规则对齐,不改业务代码。 ### 6.1 修改 `php-code.mdc` **Frontmatter**(删空 `globs:`): ```yaml --- description: PHP 全局工程规范(PHPDoc + 用例写法 + AI 纪律) alwaysApply: true --- ``` **在「代码原则」后插入新章节「用例写法(app/**)」**(约 35 行): - MUST:业务失败 `support\exception\BusinessException`;禁止 `RuntimeException` 表业务态 - MUST:编排 public 方法 ≤25 行;同一方法禁止连续 ≥4 个 `if (...) throw` → 抽 `assert*` - MUST:状态变更条件更新;钱包入账稳定 `biz_id`(`daily_rebate:{uid}:{date}`) - 按层表(与 backend-layering 互补):Controller 禁止业务 if 墙 / 直接 gift;Logic 编排结构;禁止 Logic 型 Service - 参照:`slot_console/app/api/logic/FreeCreditsLogic.php` :: `claim()`;`FreeCreditsController.php` - BAD/GOOD 各一段(RuntimeException vs BusinessException;Controller catch) - 豁免:文件前 5 行 `@use-case-exempt`(与 verify 一致) **改写「强制要求」**(L84–92)为 diff 范围: ```markdown ## 强制要求(本次 diff 新增/修改须符合) - PHP 8+;新增/修改的方法须有 typed parameter 与 return - 新增类属性须 typed property - 新建文件或本次 diff 触及的文件顶部可加 `declare(strict_types=1);`,禁止为达标改无关历史文件 ``` **微调「命名规范」**: - 禁止 `$tmp`、`$a`、`$b`、无业务含义的 `$data`/`$list` - **删除** blanket 禁止 `$info`(与 `UserInfoEntity` 等冲突);改为禁止「无上下文的 `$info` 临时变量」 **文末增加「与 verify 对齐」**: - diff 新增 `throw new RuntimeException`(业务态)→ verify FAIL - Controller diff 新增 `catch RuntimeException` + `PARAMS_ERROR` → verify FAIL ### 6.2 修改 `agent-completion-gate.mdc` ```diff - 3. 修改 PHP 后,最终回复含 `PHPDoc: checked`(见 php-doc 规则)。 - 4. 改动 `app/**/*.php` 须遵守 `php-use-case-style`(表达力、BusinessException、按层写法)。 + 3. 修改 PHP 后,最终回复含 `PHPDoc: checked`(见 php-code 规则 PHPDoc 章节)。 + 4. 改动 `app/**/*.php` 须遵守 `php-code` 用例写法章节(BusinessException、按层写法)。 ``` ### 6.3 不改动 - `verify-slot-backend.sh`(已含 use-case 检查) - `backend-layering.mdc`、`cross-service-sdk.mdc` ### 6.4 验收 1. `~/.cursor/rules/` 仅一份 PHP 总规范 `php-code.mdc`,无悬空 `php-doc` / `php-use-case-style` 引用 2. 新开 Agent 对话,改 `app/**` PHP 时应看到用例写法 + PHPDoc 3. `~/.cursor/hooks/verify-slot-backend.sh` → PASS ### 6.5 执行 todos | id | 内容 | |----|------| | merge-php-code | 按 6.1 更新 php-code.mdc | | fix-gate-refs | 按 6.2 更新 agent-completion-gate.mdc | | smoke-rules | 打开 DailyRebateLogic + 跑 verify |