# 插件开发指南

## 目录结构

```
plugins/{vendor}.{name}/
├── plugin.json            # 插件清单（必填）
├── src/
│   ├── ServiceProvider.php # 插件入口（继承基类）
│   ├── ...                 # 业务代码
│   └── Filament/           # 可选：后台页面/资源
├── routes/web.php         # 可选：前台路由（boot 时自动加载）
├── database/migrations/   # 可选：迁移（migrate 时自动加载）
└── views/                 # 可选：视图（命名空间 plugin.{vendor}.{name}）
```

## plugin.json

```json
{
    "title": "插件标题",
    "version": "1.0.0",
    "description": "插件描述",
    "author": "作者",
    "type": "core",
    "provider": "Plugins\\Vendor\\Name\\ServiceProvider",
    "requires": ["neatstudio.payment"],
    "filament_pages": ["Plugins\\Vendor\\Name\\Filament\\Pages\\SettingsPage"],
    "filament_resources": ["Plugins\\Vendor\\Name\\Filament\\Resources\\OrderResource"]
}
```

| 字段 | 说明 |
|------|------|
| `provider` | 入口类 FQCN；缺省时自动推断 `src/ServiceProvider.php` |
| `requires` | 依赖的其他插件（如会员依赖支付），按 `vendor.name` 引用 |
| `filament_pages` / `filament_resources` | 注册到后台的页面/资源（由 `PluginPages` 汇总，统一归入「插件」导航分组） |

## ServiceProvider

```php
<?php

declare(strict_types=1);

namespace Plugins\Vendor\Name;

use App\Blog\Support\PluginManager;
use App\Blog\Support\PluginServiceProvider;

class ServiceProvider extends PluginServiceProvider
{
    protected function boot(PluginManager $manager): void
    {
        // 加载前台路由（可选）
        $this->loadRoutes(__DIR__.'/../routes/web.php');

        // 注册视图命名空间（可选）
        $this->loadViews(__DIR__.'/../views', 'plugin.vendor.name');

        // 注册动作/过滤器
        $manager->addAction('comment.created', function ($comment) { ... }, 10);
        $manager->addFilter('post.rendered', fn (string $html, $post) => $html, 10);
    }
}
```

> 注意：插件入口类**不要**命名为 `PluginServiceProvider`（与基类短名冲突会导致 PHP 声明错误），统一用 `ServiceProvider`。

## 钩子系统

### addAction(hook, callback, priority) — 无返回值

| Hook | 参数 | 用途 |
|------|------|------|
| `comment.created` | `Comment` | 新评论创建（AI 审核在此监听） |
| `payment.paid` | `Payment` | 支付成功（订阅激活/解锁在此监听） |

### addFilter(hook, callback, priority) — 返回值传给下一个过滤器

| Hook | 签名 | 用途 |
|------|------|------|
| `post.rendered` | `(string $html, Post $post): string` | 文章渲染后处理（付费内容过滤） |
| `seo.structured_data` | `(array $data): array` | 扩展 JSON-LD 结构化数据 |
| `payment.gateway` | `(array $gateways): array` | 注册支付渠道 |

## 迁移

插件迁移放在 `database/migrations/`，`app/Providers/AppServiceProvider` 启动时自动注册，`php artisan migrate` 会一并执行（无需手动 --path）。

## 打包与安装

- 将插件目录打成 ZIP（根目录含 plugin.json）
- 后台「插件管理」→ 上传 ZIP 安装，或配置远程市场 `MARKET_URL`
- 内置插件（config/plugins.php enabled 列表）不可卸载，只能停用

## 异步任务（配合 Workerman）

实现 `App\Blog\Jobs\AiJob` 接口并在 Workerman 常驻进程执行：

```php
use App\Blog\Jobs\AiJob;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class MyAiJob implements AiJob, ShouldQueue
{
    use Queueable;

    public function __construct(public int $id) {}

    public function handle(\App\Blog\Services\LlmClient $llm): void
    {
        // LlmClient 由容器自动注入
    }
}
```

入队：`dispatch(new MyAiJob($id))->onQueue('ai')`（Workerman 默认消费 `default,ai` 队列）。
