# 架构与开发模式

## 技术栈

| 层 | 技术 | 说明 |
|----|------|------|
| 框架 | Laravel 12.65 | PHP ^8.2，`declare(strict_types=1)` 全量启用 |
| 后台 | Filament 5.7 | admin panel，zh_CN，资源/页面/组件 |
| 前台交互 | Livewire 4 | Filament 依赖 |
| 数据库 | MySQL 8（生产）/ SQLite（测试 :memory:） | |
| 缓存/队列 | database / redis | 队列默认 database，可切 redis |
| Markdown | league/commonmark（GFM）+ html-to-markdown | 内容双格式 |
| 附件 | S3 兼容（R2/COS/OSS）+ spatie/laravel-medialibrary | 零落盘 |
| 异步 | Workerman 5 常驻（队列消费者 + WebSocket） | 复用 Laravel Queue Worker |
| 支付 | yansongda/pay（支付宝/微信） | 沙箱模式内置 |
| 权限 | spatie/laravel-permission | admin/editor/member |

## 目录分层

```
app/
  Blog/                # 前台域模块（与 Laravel 默认 app/ 分离）
    Controllers/       # 前台控制器（theme_view() 渲染）
    Jobs/              # AI 异步任务（AiJob 接口）
    Services/          # 领域服务：渲染、支付、导入、S3 同步、LLM 客户端
    Support/           # 基础设施：ThemeManager、PluginManager、SlugGenerator、MediaDisk、WorkermanBroadcaster
    Providers/         # ThemeServiceProvider、PluginManagerServiceProvider
    View/Composers/    # 侧边栏数据共享
  Filament/            # 后台（Resources/Pages/Widgets）
  Console/Commands/    # sablog:import / attachments:sync-s3 / workerman:serve / content:convert / theme:publish
  Models/              # Post/Category/Comment/Link/Setting/User...
themes/                # 皮肤（theme.json + views + assets）
plugins/               # 插件（plugin.json + ServiceProvider + 迁移/路由/视图）
```

## 核心设计决策

1. **内容双格式**：`posts.content_format`（markdown/html）。新文章默认 Markdown；老数据导入标记 html 原样保留，`content:convert` 可批量转 Markdown。渲染管线 `PostContentRenderer::render()` 统一输出 HTML，再经 `post.rendered` 过滤器（付费内容等）。

2. **附件短代码**：HTML 内容 `[attach=1]`/`[img=1]`；Markdown 内容用 `{{attach:1}}`/`{{img:1}}` 令牌（`[]` 是 Markdown 保留字符）。渲染时按**全局** legacy_attachmentid 解析，找不到媒体自动清理残留。

3. **主题系统**：激活主题的 views 目录前置到全局视图路径（View Finder），主题覆盖同名视图、缺失回退默认视图。header 打开的容器由页面视图自闭合（避免 footer 错位）。资产开发期流式返回、生产 `theme:publish`。

4. **插件系统**：目录即插件（`plugins/{vendor}.{name}/plugin.json`），钩子（action/filter）解耦。后台页面/资源通过 manifest 的 `filament_pages`/`filament_resources` 由 `PluginPages` 汇总注册。插件迁移自动加载。

5. **S3 附件**：上传磁盘 = s3（R2/COS/OSS endpoint），新上传零落盘；未配置 S3 时回退本地 public（开发友好）。`attachments:sync-s3` 幂等同步（含大文件 Multipart），损坏文件降级 pending 不中断。

6. **异步 LLM**：任务实现 `AiJob` 接口，Workerman 常驻进程复用 Laravel `Queue\Worker` 消费（database/redis 统一），`sleep=0` + 1s Timer 不阻塞 event loop；失败重试 `failed_jobs` 表。

7. **URL 兼容**：canonical 前台 URL 统一 `.shtml` 后缀（`/posts/{slug}.shtml`）；无后缀版本 301；sablog 老 URL（伪静态/查询串/PHP 入口）全部 301；trackback 类垃圾功能直接 410 废弃。

8. **迁移保 ID**：`sablog:import` 用 `DB::table()->updateOrInsert` 保留老主键（Eloquent insertGetId 会忽略显式自增 id），老 MD5 密码登录时自动升级 bcrypt。

## 开发模式

- **本地**：Herd 托管 `laralog.test`（PHP 8.2 + MySQL 8）；无 S3 时附件落本地 public
- **测试**：PHPUnit，SQLite `:memory:`（`phpunit.xml`），`RefreshDatabase` + `seed()`；MySQL 专属 SQL（syncCounters）在 sqlite 兼容
- **常用命令**：
  - `php artisan sablog:import --fresh --convert-markdown`（老库迁移）
  - `php artisan workerman:serve start|stop`
  - `php artisan test`
  - `php artisan theme:publish` / `content:convert --all` / `attachments:sync-s3`

## 配置速查

| 配置 | 说明 |
|------|------|
| `config/blog.php` | 站点/每页数/附件磁盘 |
| `config/themes.php` | 主题目录/默认主题 |
| `config/plugins.php` | 插件目录/内置启用列表 |
| `config/workerman.php` | 消费者数/队列连接/队列名/重试 |
| `config/media.php` | S3 配置（R2/COS/OSS）与 Multipart 阈值 |
| `config/market.php` | 插件/主题市场远程源 |

## 已知边界

- AI 审核/润色需要真实 LLM Key（OpenAI 兼容）；支付需真实网关密钥（沙箱已验证）
- 插件/主题市场客户端已就绪，市场服务端需另行部署（接口约定见 `App\Blog\Services\MarketplaceClient`）
- 自动配图（封面生成）为后续迭代项：封面媒体集合与主题展示已就位，生成器走队列/脚本
