初始化
This commit is contained in:
69
.cursor/skills/hyperf-service/SKILL.md
Normal file
69
.cursor/skills/hyperf-service/SKILL.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: hyperf-service
|
||||
version: 2.0.0
|
||||
description: "生成 Hyperf 后端服务模块脚手架(Controller/Service/Repository/Model)。当需要新建后端业务模块时使用。支持 DI 注入和事务管理。"
|
||||
---
|
||||
|
||||
> ⚠️ 核心执行流程已在 `.cursor/rules/skill-backend-scaffold.mdc` 中由 Cursor 自动注入。
|
||||
> 本文件提供完整模板、代码示例和边缘场景处理,供 Agent 按需深入 Read。
|
||||
|
||||
# Hyperf Service Module Scaffold
|
||||
|
||||
## 触发条件
|
||||
|
||||
用户要求创建后端服务模块、业务 Service 类、或完整的后端业务层。
|
||||
|
||||
## 执行流程
|
||||
|
||||
### 0. 加载规范(⚠️ 必须最先执行)
|
||||
|
||||
依次读取 `.cursor/rules/013-backend.mdc`、`016-swoole.mdc`、`019-modular.mdc`,提取 DI、事务、协程安全、依赖方向、事件解耦。
|
||||
|
||||
### 1. 确认模块规格
|
||||
|
||||
| 字段 | 必填 | 默认值 |
|
||||
|------|------|--------|
|
||||
| 模块名称 | ✅ | — |
|
||||
| 所属业务域 | ❌ | 推断 |
|
||||
| 需要 Repository? | ❌ | 复杂查询时 true |
|
||||
| 需要事件? | ❌ | false |
|
||||
| 需要数据权限? | ❌ | true |
|
||||
| 需要缓存? | ❌ | false |
|
||||
|
||||
### 2. 生成文件结构
|
||||
|
||||
Controller/Admin、Service、Repository、Model、Request、Event、Listener(可选)。
|
||||
|
||||
### 3. Service / Repository
|
||||
|
||||
Service 使用 `Db::transaction` 包裹写操作,Repository 实现 `applyFilters`、`applyDataScope`、分页上限。完整模板见 **Tier 3**。
|
||||
|
||||
### 4. 结构化日志
|
||||
|
||||
Service 集成 HasLogger Trait,create/update/delete 至少 info 级别。RequestId 中间件全局注入 request_id。
|
||||
|
||||
### 5. 重试机制
|
||||
|
||||
外部 API 调用使用 RetryHelper 指数退避重试,retryOn 指定可重试异常类型。
|
||||
|
||||
### 6. 遵循项目约定
|
||||
|
||||
扫描 `app/Service/`、AbstractService、BusinessException、数据权限、事件注册、HasLogger、RetryHelper。
|
||||
|
||||
## 验证
|
||||
|
||||
1. [ ] `php -l` 无错误
|
||||
2. [ ] 依赖注入正确
|
||||
3. [ ] 写操作使用 `Db::transaction()`
|
||||
4. [ ] 分页有 page_size 上限
|
||||
5. [ ] 异常使用 BusinessException
|
||||
6. [ ] applyDataScope 已实现(如需)
|
||||
7. [ ] 关键操作有结构化日志
|
||||
8. [ ] 外部 API 用 RetryHelper
|
||||
9. [ ] Logger channel 使用类名
|
||||
|
||||
## Tier 3 深度参考
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| `references/service-templates.md` | Service/Repository/Event/Logger/Retry 完整代码 |
|
||||
120
.cursor/skills/hyperf-service/references/service-templates.md
Normal file
120
.cursor/skills/hyperf-service/references/service-templates.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Hyperf Service — 代码模板
|
||||
|
||||
> 主流程见 SKILL.md,本文档为 Service/Repository/Event/Logger/Retry 完整实现。
|
||||
|
||||
## Service 模板
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace App\Service\{{Domain}};
|
||||
|
||||
use App\Model\{{Domain}}\{{Module}};
|
||||
use App\Repository\{{Domain}}\{{Module}}Repository;
|
||||
use App\Exception\BusinessException;
|
||||
use Hyperf\DbConnection\Db;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
class {{Module}}Service
|
||||
{
|
||||
#[Inject]
|
||||
protected {{Module}}Repository $repository;
|
||||
|
||||
public function getPageList(array $params): array { return $this->repository->getPageList($params); }
|
||||
|
||||
public function getById(int $id): {{Module}} {
|
||||
$record = {{Module}}::find($id);
|
||||
if (!$record) throw new BusinessException(404, '{{Module}} not found');
|
||||
return $record;
|
||||
}
|
||||
|
||||
public function create(array $data): {{Module}} {
|
||||
return Db::transaction(function () use ($data) {
|
||||
$record = {{Module}}::create($data);
|
||||
return $record;
|
||||
});
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): {{Module}} {
|
||||
$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);
|
||||
Db::transaction(fn () => $record->delete());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Repository 模板(含 applyFilters / applyDataScope)
|
||||
|
||||
```php
|
||||
class {{Module}}Repository
|
||||
{
|
||||
public function getPageList(array $params): array {
|
||||
$query = {{Module}}::query();
|
||||
$this->applyFilters($query, $params);
|
||||
$this->applyDataScope($query);
|
||||
$page = (int) ($params['page'] ?? 1);
|
||||
$pageSize = min((int) ($params['page_size'] ?? 10), 100);
|
||||
$total = $query->count();
|
||||
$items = $query->with($this->getDefaultRelations())->orderByDesc('id')
|
||||
->offset(($page - 1) * $pageSize)->limit($pageSize)->get();
|
||||
return ['items' => $items, 'total' => $total];
|
||||
}
|
||||
|
||||
protected function applyFilters(Builder $query, array $params): void {
|
||||
if (!empty($params['keyword'])) $query->where('name', 'like', "%{$params['keyword']}%");
|
||||
if (!empty($params['status'])) $query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
protected function applyDataScope(Builder $query): void { /* 数据权限 */ }
|
||||
protected function getDefaultRelations(): array { return []; }
|
||||
}
|
||||
```
|
||||
|
||||
## Event + Listener 模板
|
||||
|
||||
```php
|
||||
// Event
|
||||
class {{Module}}Created { public function __construct(public readonly {{Module}} $record) {} }
|
||||
|
||||
// Listener
|
||||
#[Listener]
|
||||
class {{Module}}CreatedListener implements ListenerInterface
|
||||
{
|
||||
public function listen(): array { return [{{Module}}Created::class]; }
|
||||
public function process(object $event): void { /* 发送通知、更新缓存等 */ }
|
||||
}
|
||||
```
|
||||
|
||||
## HasLogger Trait
|
||||
|
||||
```php
|
||||
trait HasLogger {
|
||||
protected ?LoggerInterface $logger = null;
|
||||
protected function logger(): LoggerInterface { /* 按类名获取 channel */ }
|
||||
protected function logContext(array $extra = []): array { return array_merge(['request_id' => Context::get('request_id'), 'user_id' => Context::get('current_user_id')], $extra); }
|
||||
}
|
||||
```
|
||||
|
||||
## RetryHelper
|
||||
|
||||
```php
|
||||
public static function withRetry(callable $fn, int $maxRetries = 3, int $baseDelayMs = 1000, array $retryOn = []): mixed {
|
||||
for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
|
||||
try { return $fn(); }
|
||||
catch (\Throwable $e) {
|
||||
if ($attempt < $maxRetries) Coroutine::sleep(($baseDelayMs * (2 ** $attempt) + jitter) / 1000);
|
||||
else throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RequestIdMiddleware
|
||||
|
||||
在 process 中:`Context::set('request_id', $requestId)`,响应头加 `X-Request-ID`。
|
||||
Reference in New Issue
Block a user