Files
vibe_coding/.cursor/skills/module-scaffold/SKILL.md
2026-03-05 21:27:11 +08:00

387 lines
13 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: module-scaffold
version: 2.1.0
description: "生成 Hyperf 模块化业务模块脚手架ConfigProvider/Controller/Service/Model。当需要新建业务模块、创建 module、添加模块化功能时使用。基于 ModuleLoader 自动发现 + ConfigProvider 机制,无需修改 composer.json。"
requires: [hyperf-service]
---
> ⚠️ 核心执行流程已在 `.cursor/rules/skill-module-scaffold.mdc` 中由 Cursor 自动注入。
> 本文件提供完整模板、代码示例和边缘场景处理,供 Agent 按需深入 Read。
# Hyperf Module Scaffold
## 触发条件
用户要求创建新的业务模块、在 `modules/` 下新建功能模块、或提到"模块化"、"新模块"、"module"。
## 前置条件
项目已完成模块化架构初始化:
- `Case-Database-Backend/modules/` 目录存在
- `app/Support/ModuleLoader.php` 已存在并注册到 `composer.json` `autoload.files`
- `config/autoload/annotations.php` 已包含 `BASE_PATH . '/modules'` 扫描路径
若未初始化,先引导用户完成架构搭建。
## 工作原理
`ModuleLoader.php``vendor/autoload.php` 加载时Hyperf DI 容器初始化之前)自动执行:
1. 扫描 `modules/*/composer.json`
2. 读取每个模块的 `extra.hyperf.config`
3. 通过反射注入 `Hyperf\Support\Composer::$extra`
4. 同时注册模块的 PSR-4 命名空间到 Composer ClassLoader
这使得 `ProviderConfig::load()` 能发现所有模块的 ConfigProvider**无需将模块添加到主项目 `composer.json``require` 中**。
## 执行流程
### 1. 确认模块规格
| 字段 | 必填 | 默认值 | 说明 |
|------|------|--------|------|
| 模块名称 | ✅ | — | PascalCase`Order``UserCenter` |
| 模块描述 | ❌ | 推断 | 一句话说明模块职责 |
| 接口类型 | ❌ | `api` | `api`(用户端)/ `admin`(管理端)/ `both`(两者都有) |
| 需要 Service | ❌ | true | 业务逻辑层 |
| 需要 Model | ❌ | false | 数据模型 |
| 需要 Repository | ❌ | false | 复杂查询时 |
| 需要 Middleware | ❌ | false | 模块级中间件 |
| 需要 Request | ❌ | false | 表单验证 |
| 路由前缀 | ❌ | 见下方规则 | 自动推断,也可手动指定 |
### 2. Controller 与 Request 目录结构规则(核心)
根据接口类型Controller 和 Request **均放入对应子目录**,命名空间随之变更:
| 接口类型 | 路由前缀 | Controller 目录 | Request 目录 | PHP 命名空间Controller |
|----------|----------|-----------------|--------------|--------------------------|
| 用户端 API`/api/*` | `/api/{module-kebab}` | `Http/Controller/Api/` | `Http/Request/Api/` | `Modules\{Name}\Http\Controller\Api` |
| 管理端 API`/admin/*` | `/admin/{module-kebab}` | `Http/Controller/Admin/` | `Http/Request/Admin/` | `Modules\{Name}\Http\Controller\Admin` |
| 两者都有(`both` | 各自前缀 | 两个 Controller 子目录 | 两个 Request 子目录 | 各自命名空间 |
**判断规则**
- 路由前缀包含 `/api/` → Controller 放 `Controller/Api/`Request 放 `Request/Api/`
- 路由前缀包含 `/admin/` → Controller 放 `Controller/Admin/`Request 放 `Request/Admin/`
- 用户未指定接口类型时,优先询问,或根据模块职责推断
- 若模块同时有用户端和管理端接口,两组子目录都创建
- **类名不需要加接口类型前缀**,子目录本身已区分,类名保持简洁
### 3. 生成目录结构
**仅有用户端 API最常见**
```
modules/{ModuleName}/
├── composer.json
└── src/
├── ConfigProvider.php
├── Http/
│ ├── Controller/
│ │ └── Api/ # 用户端控制器
│ │ └── {Name}Controller.php
│ ├── Request/
│ │ └── Api/ # 用户端请求验证
│ │ └── {Name}Request.php
│ └── Middleware/ # 模块中间件(按需)
├── Service/
├── Model/ # 按需
├── Repository/ # 按需
├── Event/ # 按需
├── Listener/ # 按需
└── Constants/ # 按需
```
**仅有管理端 API**
```
modules/{ModuleName}/
└── src/
├── Http/
│ ├── Controller/
│ │ └── Admin/ # 管理端控制器
│ │ └── {Name}Controller.php
│ ├── Request/
│ │ └── Admin/ # 管理端请求验证
│ │ └── {Name}Request.php
...
```
**同时有用户端 + 管理端both**
```
modules/{ModuleName}/
└── src/
├── Http/
│ ├── Controller/
│ │ ├── Api/ # 用户端控制器
│ │ │ └── {Name}Controller.php
│ │ └── Admin/ # 管理端控制器
│ │ └── {Name}Controller.php
│ ├── Request/
│ │ ├── Api/ # 用户端请求验证
│ │ │ └── {Name}Request.php
│ │ └── Admin/ # 管理端请求验证
│ │ └── {Name}Request.php
...
```
### 4. 生成 composer.json
```json
{
"name": "modules/{module-kebab}",
"description": "{模块描述}",
"type": "library",
"autoload": {
"psr-4": {
"Modules\\{ModuleName}\\": "src/"
}
},
"extra": {
"hyperf": {
"config": "Modules\\{ModuleName}\\ConfigProvider"
}
}
}
```
**命名规则**
- Composer 包名:`modules/{kebab-case}`,如 `modules/user-center`
- PHP 命名空间:`Modules\{PascalCase}`,如 `Modules\UserCenter`
如模块有独立的第三方依赖,可在模块 `composer.json``require` 中声明,`wikimedia/composer-merge-plugin` 会在下次 `composer update` 时合并到主项目。
### 5. 生成 ConfigProvider.php
```php
<?php
declare(strict_types=1);
namespace Modules\{ModuleName};
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
// Interface::class => Implementation::class,
],
'annotations' => [
'scan' => [
'paths' => [
__DIR__,
],
],
],
'publish' => [],
];
}
}
```
**ConfigProvider 职责**
- `dependencies`:注册模块的 DI 绑定(接口 → 实现)
- `annotations.scan.paths`:注册注解扫描路径(`__DIR__` 指向 `src/`
- `publish`:可发布的配置文件(可选)
### 6. 生成 Controller 模板
**用户端 API Controller**(放在 `Http/Controller/Api/`
```php
<?php
declare(strict_types=1);
namespace Modules\{ModuleName}\Http\Controller\Api;
use App\Controller\AbstractController;
use App\Support\ResponseTrait;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Modules\{ModuleName}\Service\{ModuleName}Service;
use Psr\Http\Message\ResponseInterface;
#[Controller(prefix: '/api/{module-kebab}')]
class {ModuleName}Controller extends AbstractController
{
use ResponseTrait;
#[Inject]
protected {ModuleName}Service ${moduleName}Service;
#[GetMapping(path: '')]
public function index(): ResponseInterface
{
return $this->success([]);
}
}
```
**管理端 API Controller**(放在 `Http/Controller/Admin/`
```php
<?php
declare(strict_types=1);
namespace Modules\{ModuleName}\Http\Controller\Admin;
use App\Controller\AbstractController;
use App\Middleware\JwtAuthMiddleware;
use App\Support\ResponseTrait;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Annotation\Middleware;
use Modules\{ModuleName}\Service\{ModuleName}Service;
use Psr\Http\Message\ResponseInterface;
#[Controller(prefix: '/admin/{module-kebab}')]
#[Middleware(JwtAuthMiddleware::class)]
class {ModuleName}Controller extends AbstractController
{
use ResponseTrait;
#[Inject]
protected {ModuleName}Service ${moduleName}Service;
#[GetMapping(path: '')]
public function index(): ResponseInterface
{
return $this->success([]);
}
}
```
**关键区别**
- `Api/` 下:前缀 `/api/`,命名空间含 `\Api\`
- `Admin/` 下:前缀 `/admin/`,命名空间含 `\Admin\`,自动加 `JwtAuthMiddleware`
- **两者类名相同**(均为 `{Name}Controller`),由命名空间区分,无需加接口类型前缀
### 7. 生成 Request 模板
**用户端 Request**(放在 `Http/Request/Api/`
```php
<?php
declare(strict_types=1);
namespace Modules\{ModuleName}\Http\Request\Api;
use Hyperf\Validation\Request\FormRequest;
class {Name}Request extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
// 'field' => 'required|string|max:255',
];
}
public function messages(): array
{
return [];
}
}
```
**管理端 Request**(放在 `Http/Request/Admin/`
```php
<?php
declare(strict_types=1);
namespace Modules\{ModuleName}\Http\Request\Admin;
use Hyperf\Validation\Request\FormRequest;
class {Name}Request extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
// 'field' => 'required|string|max:255',
];
}
public function messages(): array
{
return [];
}
}
```
**关键区别**
- `Api/` 下:命名空间含 `\Request\Api\`,类名 `{Name}Request`
- `Admin/` 下:命名空间含 `\Request\Admin\`,类名 `{Name}Request`
- **两者类名相同**,由命名空间区分,无需加接口类型前缀
- Controller 中 `use` 路径需对应各自子命名空间
### 8. 验证模块自动发现
创建文件后**无需任何 composer 操作**,直接验证:
```bash
docker exec hyperf-skeleton php -r "
define('BASE_PATH', '/opt/www');
require '/opt/www/vendor/autoload.php';
\$extra = Hyperf\Support\Composer::getMergedExtra('hyperf');
echo in_array('Modules\\{ModuleName}\\ConfigProvider', \$extra['config'] ?? []) ? 'OK' : 'FAIL';
"
```
## 模块间依赖
若模块 A 依赖模块 B 的类:
- 通过 DI 注入模块 B 的 Service不直接依赖 Controller/Model
- 遵循依赖方向:上层模块 → 下层模块,禁止循环依赖
- ModuleLoader 的 glob 扫描天然支持多模块并存,无需额外配置
## 已有模块示例参考
当前项目 `modules/Auth/` 的实际结构(**参考标准**
```
modules/Auth/src/Http/Controller/
├── Api/ # 用户端接口(推荐新规范)
│ ├── AuthController.php # prefix: /api/auth
│ ├── CaptchaController.php # prefix: /api/auth/captcha
│ └── SmsController.php # prefix: /api/auth/sms
└── Admin/ # 管理端接口(推荐新规范)
└── AdminAuthController.php # prefix: /admin/auth
```
> 注:当前 Auth 模块尚未按此规范重构(所有 Controller 平铺在 `Controller/` 下),新模块应从一开始遵循子目录规范。
## 验证
1. [ ] 模块目录结构完整composer.json + src/ConfigProvider.php + Http/Controller/
2. [ ] **Controller 按接口类型放入正确子目录**`/api/*``Controller/Api/``/admin/*``Controller/Admin/`
3. [ ] Controller 命名空间包含正确子层级:`...\Http\Controller\Api\``...\Http\Controller\Admin\`
4. [ ] `Admin/` 下的 Controller 自动添加 `JwtAuthMiddleware`;类名与 `Api/` 下保持一致,无需加前缀
5. [ ] **Request 按接口类型放入正确子目录**`/api/*``Request/Api/``/admin/*``Request/Admin/`
6. [ ] Request 命名空间包含正确子层级:`...\Http\Request\Api\``...\Http\Request\Admin\`
7. [ ] Controller 中 `use` 的 Request 路径与子目录一致
8. [ ] composer.json 的 `name` 使用 `modules/{kebab-case}` 格式
9. [ ] namespace 使用 `Modules\{PascalCase}` 格式
10. [ ] ConfigProvider 注册了 `__DIR__` 注解扫描路径
11. [ ] `extra.hyperf.config` 指向正确的 ConfigProvider 类
12. [ ] 无需修改主项目 `composer.json`,模块被 ModuleLoader 自动发现
13. [ ] `ProviderConfig::load()` 能发现模块 ConfigProvider
14. [ ] `php -l` 所有 PHP 文件语法正确
15. [ ] Controller 路由前缀遵循 RESTful 命名kebab-case