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

3.7 KiB
Raw Blame History

Hyperf Service — 代码模板

主流程见 SKILL.md本文档为 Service/Repository/Event/Logger/Retry 完整实现。

Service 模板

<?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

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 模板

// 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

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

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