Files
vibe_coding/.cursor/skills/hyperf-service/references/service-templates.md
2026-03-05 21:27:11 +08:00

121 lines
3.7 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.
# 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`