初始化
This commit is contained in:
41
.cursor/skills/api-scaffold/references/api-response.md
Normal file
41
.cursor/skills/api-scaffold/references/api-response.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# API 统一响应格式
|
||||
|
||||
## 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"data": { ... },
|
||||
"meta": {
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"total": 100,
|
||||
"totalPages": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Human-readable error message",
|
||||
"code": "VALIDATION_ERROR",
|
||||
"details": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP 状态码
|
||||
|
||||
| 状态码 | 场景 |
|
||||
|--------|------|
|
||||
| 200 | 查询/更新成功 |
|
||||
| 201 | 创建成功 |
|
||||
| 204 | 删除成功(无响应体) |
|
||||
| 400 | 请求参数验证失败 |
|
||||
| 401 | 未认证 |
|
||||
| 403 | 已认证但无权限 |
|
||||
| 404 | 资源不存在 |
|
||||
| 409 | 资源冲突(如重复创建) |
|
||||
| 422 | 业务逻辑错误 |
|
||||
| 429 | 请求频率超限 |
|
||||
| 500 | 服务器内部错误 |
|
||||
156
.cursor/skills/api-scaffold/references/code-templates.md
Normal file
156
.cursor/skills/api-scaffold/references/code-templates.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# API Scaffold — 代码模板与异常体系
|
||||
|
||||
> 主流程见 SKILL.md,本文档为 Controller/Service/FormRequest/路由 的完整代码模板。
|
||||
|
||||
## Hyperf 分层目录约定
|
||||
|
||||
| 文件 | 路径 |
|
||||
|------|------|
|
||||
| Controller | `app/Controller/Admin/<Resource>Controller.php` |
|
||||
| Service | `app/Service/<Module>/<Resource>Service.php` |
|
||||
| FormRequest | `app/Request/<Module>/Create<Resource>Request.php` |
|
||||
| Model | `app/Model/<Module>/<Resource>.php`(如不存在) |
|
||||
| Route | `app/Http/Admin/Router/<resource>.php` |
|
||||
|
||||
## Controller 模板
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Request\{{Module}}\Create{{Resource}}Request;
|
||||
use App\Service\{{Module}}\{{Resource}}Service;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
#[Controller(prefix: '/admin/{{route-path}}')]
|
||||
class {{Resource}}Controller extends AbstractController
|
||||
{
|
||||
#[Inject]
|
||||
protected {{Resource}}Service $service;
|
||||
|
||||
#[RequestMapping(path: '', methods: ['GET'])]
|
||||
public function list(RequestInterface $request): ResponseInterface {
|
||||
$params = $request->all();
|
||||
$result = $this->service->getPageList($params);
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
#[RequestMapping(path: '{id:\d+}', methods: ['GET'])]
|
||||
public function detail(int $id): ResponseInterface {
|
||||
$result = $this->service->getById($id);
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
#[RequestMapping(path: '', methods: ['POST'])]
|
||||
public function create(Create{{Resource}}Request $request): ResponseInterface {
|
||||
$data = $request->validated();
|
||||
$result = $this->service->create($data);
|
||||
return $this->success($result, 201);
|
||||
}
|
||||
|
||||
#[RequestMapping(path: '{id:\d+}', methods: ['PUT'])]
|
||||
public function update(int $id, Create{{Resource}}Request $request): ResponseInterface {
|
||||
$data = $request->validated();
|
||||
$result = $this->service->update($id, $data);
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
#[RequestMapping(path: '{id:\d+}', methods: ['DELETE'])]
|
||||
public function delete(int $id): ResponseInterface {
|
||||
$this->service->delete($id);
|
||||
return $this->success(null, message: 'Deleted');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Service 模板
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace App\Service\{{Module}};
|
||||
|
||||
use App\Model\{{Module}}\{{Resource}};
|
||||
use App\Exception\BusinessException;
|
||||
use Hyperf\DbConnection\Db;
|
||||
|
||||
class {{Resource}}Service
|
||||
{
|
||||
public function getPageList(array $params): array {
|
||||
$query = {{Resource}}::query();
|
||||
if (!empty($params['status'])) $query->where('status', $params['status']);
|
||||
$page = (int) ($params['page'] ?? 1);
|
||||
$pageSize = (int) ($params['page_size'] ?? 10);
|
||||
$total = $query->count();
|
||||
$items = $query->orderByDesc('id')->offset(($page - 1) * $pageSize)->limit($pageSize)->get();
|
||||
return ['items' => $items, 'total' => $total];
|
||||
}
|
||||
|
||||
public function getById(int $id): {{Resource}} {
|
||||
$record = {{Resource}}::find($id);
|
||||
if (!$record) throw new BusinessException(404, '{{Resource}} not found');
|
||||
return $record;
|
||||
}
|
||||
|
||||
public function create(array $data): {{Resource}} {
|
||||
return Db::transaction(fn () => {{Resource}}::create($data));
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): {{Resource}} {
|
||||
$record = $this->getById($id);
|
||||
return Db::transaction(function () use ($record, $data) {
|
||||
$record->update($data);
|
||||
return $record->refresh();
|
||||
});
|
||||
}
|
||||
|
||||
public function delete(int $id): void {
|
||||
$record = $this->getById($id);
|
||||
$record->delete();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## FormRequest 模板
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace App\Request\{{Module}};
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
|
||||
class Create{{Resource}}Request extends FormRequest
|
||||
{
|
||||
public function authorize(): bool { return true; }
|
||||
public function rules(): array {
|
||||
return [ /* Define validation rules based on resource fields */ ];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 路由注册
|
||||
|
||||
```php
|
||||
// app/Http/Admin/Router/{{resource}}.php
|
||||
use App\Controller\Admin\{{Resource}}Controller;
|
||||
use Hyperf\HttpServer\Router\Router;
|
||||
|
||||
Router::addGroup('/admin/{{route-path}}', function () {
|
||||
Router::get('', [{{Resource}}Controller::class, 'list']);
|
||||
Router::get('/{id:\d+}', [{{Resource}}Controller::class, 'detail']);
|
||||
Router::post('', [{{Resource}}Controller::class, 'create']);
|
||||
Router::put('/{id:\d+}', [{{Resource}}Controller::class, 'update']);
|
||||
Router::delete('/{id:\d+}', [{{Resource}}Controller::class, 'delete']);
|
||||
}, ['middleware' => [AccessTokenMiddleware::class, PermissionMiddleware::class]]);
|
||||
```
|
||||
|
||||
## 统一响应格式
|
||||
|
||||
```php
|
||||
protected function success(mixed $data = null, int $code = 200, string $message = 'ok'): ResponseInterface {
|
||||
return $this->response->json(['code' => $code, 'message' => $message, 'data' => $data]);
|
||||
}
|
||||
protected function error(string $message, int $code = 500, mixed $data = null): ResponseInterface {
|
||||
return $this->response->json(['code' => $code, 'message' => $message, 'data' => $data]);
|
||||
}
|
||||
```
|
||||
100
.cursor/skills/api-scaffold/references/exception-handling.md
Normal file
100
.cursor/skills/api-scaffold/references/exception-handling.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# API Scaffold — 分级异常体系
|
||||
|
||||
> 主流程见 SKILL.md,本文档为异常分级与 AppExceptionHandler 完整实现。
|
||||
|
||||
## 异常分级
|
||||
|
||||
| 异常类 | 场景 | HTTP 状态码 | 日志级别 | 是否通知运维 |
|
||||
|---|---|---|---|---|
|
||||
| `BusinessException` | 可预期的业务错误 | 400/403/404/409/422 | warning | ❌ |
|
||||
| `ValidationException` | 参数校验失败 | 422 | info | ❌ |
|
||||
| `SystemException` | 不可预期的系统故障 | 500/502/503 | error | ✅ |
|
||||
|
||||
## BusinessException / SystemException 定义
|
||||
|
||||
```php
|
||||
// app/Exception/BusinessException.php
|
||||
class BusinessException extends ServerException
|
||||
{
|
||||
public function __construct(int $code = 400, string $message = 'Business error', public readonly bool $isOperational = true)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
|
||||
// app/Exception/SystemException.php
|
||||
class SystemException extends ServerException
|
||||
{
|
||||
public function __construct(string $message = 'System error', int $code = 500, public readonly bool $isOperational = false, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AppExceptionHandler
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
use App\Exception\BusinessException;
|
||||
use App\Exception\SystemException;
|
||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||
use Hyperf\Validation\ValidationException;
|
||||
|
||||
class AppExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
public function handle(\Throwable $throwable, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$this->stopPropagation();
|
||||
$body = match (true) {
|
||||
$throwable instanceof ValidationException => ['code' => 422, 'message' => 'Validation failed', 'data' => $throwable->validator->errors()->toArray()],
|
||||
$throwable instanceof BusinessException => ['code' => $throwable->getCode(), 'message' => $throwable->getMessage(), 'data' => null],
|
||||
$throwable instanceof SystemException => $this->handleSystemException($throwable),
|
||||
default => $this->handleUnexpected($throwable),
|
||||
};
|
||||
$statusCode = ($body['code'] >= 100 && $body['code'] < 600) ? $body['code'] : 500;
|
||||
return $response->withStatus($statusCode)->withHeader('Content-Type', 'application/json')
|
||||
->withBody(new SwooleStream(json_encode($body, JSON_UNESCAPED_UNICODE)));
|
||||
}
|
||||
|
||||
private function handleSystemException(SystemException $e): array {
|
||||
$this->logger->error('System exception', ['message' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
||||
return ['code' => 500, 'message' => 'Internal server error', 'data' => null];
|
||||
}
|
||||
|
||||
private function handleUnexpected(\Throwable $e): array {
|
||||
$this->logger->critical('Unexpected exception', ['class' => get_class($e), 'message' => $e->getMessage()]);
|
||||
return ['code' => 500, 'message' => 'Internal server error', 'data' => null];
|
||||
}
|
||||
|
||||
public function isValid(\Throwable $throwable): bool { return true; }
|
||||
}
|
||||
```
|
||||
|
||||
## 异常选择决策
|
||||
|
||||
```
|
||||
抛出异常?
|
||||
├─ 用户输入/请求导致的?
|
||||
│ ├─ 参数格式错误 → ValidationException(FormRequest 自动抛出)
|
||||
│ ├─ 资源不存在 → BusinessException(404)
|
||||
│ ├─ 无权限 → BusinessException(403)
|
||||
│ └─ 业务规则冲突 → BusinessException(409/422)
|
||||
└─ 系统/环境导致的?
|
||||
├─ 数据库连接失败 → SystemException(503)
|
||||
├─ 第三方 API 超时 → SystemException(502)
|
||||
└─ 未知异常 → AppExceptionHandler 兜底
|
||||
```
|
||||
|
||||
## Service 使用示例
|
||||
|
||||
```php
|
||||
// Predictable: resource not found
|
||||
throw new BusinessException(404, 'Order not found');
|
||||
|
||||
// Unpredictable: external system failure
|
||||
throw new SystemException(message: 'ERP sync failed: connection timeout', previous: $e);
|
||||
```
|
||||
Reference in New Issue
Block a user