初始化
This commit is contained in:
386
.cursor/skills/module-scaffold/SKILL.md
Normal file
386
.cursor/skills/module-scaffold/SKILL.md
Normal file
@@ -0,0 +1,386 @@
|
||||
---
|
||||
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)
|
||||
Reference in New Issue
Block a user