# LaraLog

基于 **Laravel 12 + Filament 5 + Livewire 4 + Spatie 生态** 的现代化博客系统，从 SaBlog-X 1.6 完整迁移而来：

- **脚本迁移**：`php artisan sablog:import` 一键从老库迁入全部数据（文章/分类/评论/标签/友链/用户/附件），保留原始 ID，老 URL 全部 301 兼容
- **多主题**：`themes/` 目录即皮肤，后台一键切换/上传 ZIP；内置 sablog 经典风 + modern 简约风两套
- **插件系统**：`plugins/` 目录 + 钩子系统（action/filter），ZIP 安装 + 可选远程市场
- **内置插件**：SEO & GEO（JSON-LD/OG/llms.txt/Markdown 导出）、AI 评论审核 + 内容润色（Workerman 异步）、支付（支付宝/微信/沙箱）、会员（套餐订阅 + 付费文章）
- **附件全走 S3**：R2 / COS / OSS 兼容，新上传零落盘，`attachments:sync-s3` 一键同步老附件（大文件 Multipart）

## 技术栈

| 组件 | 版本 | 说明 |
|------|------|------|
| Laravel | ^12.0 | 应用框架 |
| Filament | ^5.7 | 后台面板（zh_CN） |
| Livewire | ^4.4 | 前台交互 |
| Workerman | ^5.2 | 常驻进程：队列消费者 + WebSocket 进度推送 |
| yansongda/pay | ^3.7 | 支付宝 / 微信支付 |
| spatie 库 | — | permission / medialibrary / sitemap / robots / tags / backup |
| league/commonmark | ^2.9 | Markdown 渲染（GFM） |
| league/html-to-markdown | ^5.1 | 老 HTML 内容转换 |
| MySQL | 8.x | 数据库 |

## 快速开始

```bash
# 1. 安装依赖
composer install
cp .env.example .env
php artisan key:generate

# 2. 配置 .env（MySQL 8）
DB_DATABASE=laralog
DB_USERNAME=root
DB_PASSWORD=

# 3. 迁移 + 种子（创建 admin@laralog.test / password）
php artisan migrate --seed

# 4. 启动（Herd 或 artisan serve）
php artisan serve

# 5. 后台：http://laralog.test/admin
```

## 迁移老 Sablog

```bash
# 原始导入（内容保持 HTML，[attach=xx] 渲染时解析）
php artisan sablog:import \
  --database=sablog --host=127.0.0.1 --port=3306 \
  --username=root --password=xxx \
  --attachments-dir=/path/to/old/attachments --sync-attachments

# 同时把内容转换为 Markdown（[attach=xx] 转为 {{attach:xx}} 令牌）
php artisan sablog:import ... --convert-markdown

# 事后单独转换 / 同步附件到 S3
php artisan content:convert --all
S3_ACCESS_KEY=... S3_BUCKET=... S3_ENDPOINT=... php artisan attachments:sync-s3 --legacy-dir=/path/to/old/attachments
```

> 不迁移的过时功能：trackback（引用通告，垃圾来源）、搜索缓存表、验证码（由 AI 审核 + 蜜罐 + 频控取代）、WAP、广告位变量。

## 主题

- 目录：`themes/{name}/`，含 `theme.json`（标题/版本/描述/作者）+ `views/**` + `assets/`
- 激活主题的 views 目录前置到全局视图路径：主题覆盖同名视图，缺失自动回退默认视图
- 资产开发期由 `/themes/{name}/assets/...` 流式返回；生产可 `php artisan theme:publish`（见 docs/deploy.md）
- 后台「主题管理」可激活 / 上传 ZIP / 删除

## 插件

- 目录：`plugins/{vendor}.{name}/`，含 `plugin.json` + `src/ServiceProvider.php`（继承 `App\Blog\Support\PluginServiceProvider`）+ 可选 `database/migrations`、`routes/web.php`、`views/`
- 钩子：`addAction(hook, cb)` / `addFilter(hook, cb, priority)`，核心钩子：
  - `comment.created`（AI 审核）、`payment.paid`（按 payable 分发订阅激活/解锁）、`post.rendered`（付费内容过滤/paywall）、`seo.structured_data`
  - `filament.post_form` / `filament.post_table`（后台文章表单/表格注入）
- 插件依赖 `requires` 强制校验：启用需依赖已启用，停用/卸载会被依赖者阻止；启动时依赖不满足的插件跳过
- 订单商品化：`Payment` 多态 `payable` 关联购买实体，实体实现 `payableLabel()` / `payableUrl()` 接入订单展示（详见 docs/plugins.md）
- 后台「插件管理」：启停 / 上传 ZIP 安装 / 卸载 / 市场拉取（配置 `MARKET_URL` 后可用）
- 插件自带迁移由 `migrate` 自动加载（`app/Providers/AppServiceProvider` 注册 plugins/*/database/migrations）

## 内容格式

- 每篇文章有 `content_format`（markdown / html），新文章默认 Markdown
- 老数据导入标记为 html 原样保留；`content:convert` 批量转 Markdown
- 附件短代码：HTML 内容用 `[attach=1]` / `[img=1]`，Markdown 内容用 `{{attach:1}}` / `{{img:1}}`（`[]` 是 Markdown 保留字符），渲染时统一解析
- 付费内容：`[paid]...[/paid]` 短代码，非会员/未解锁访客隐藏（会员插件）

## 异步处理（Workerman）

```bash
php artisan workerman:serve start      # 队列消费者 × N + WebSocket :8787
php artisan workerman:serve stop
```

- 消费者复用 Laravel `Queue\Worker`，**database / redis 队列统一支持**，常驻进程内框架只启动一次（省去每个任务的启动开销）
- 任务统一实现 `App\Blog\Jobs\AiJob` 接口（`handle(LlmClient)`），依赖由容器自动注入
- 队列选择：
  - database（默认）：`WORKERMAN_QUEUE_CONNECTION=database`
  - redis：`WORKERMAN_QUEUE_CONNECTION=redis` 并确保 `QUEUE_CONNECTION=redis`、`REDIS_*` 配置正确；`redis` 连接的 `block_for` 建议设为 `0`（配合 1s 轮询，避免阻塞 Workerman event loop）
- 消费队列：`WORKERMAN_QUEUES=default,ai`（ai 队列为 AI 审核/润色任务）
- 失败重试：`WORKERMAN_MAX_TRIES`（默认 3），超时 `WORKERMAN_TIMEOUT`（默认 60s），重试耗尽进 `failed_jobs` 表
- 降级路径：`php artisan queue:work` 照常可用（同一队列）

## 前台缓存

- 匿名 GET 页面整页缓存（首页/列表/文章/归档/标签等），TTL 默认 300 秒（`PAGE_CACHE_TTL`），命中时零数据库查询
- 文章浏览量由独立 `/track-view` 端点统计，不受整页缓存影响（实时）
- 后台「缓存管理」：清空页面/侧边栏/设置/全部缓存、重建编译缓存
- 侧边栏数据与站点设置另有 1 小时缓存

## 对外 API

- 交互式文档（OpenAPI/Swagger 风格）：`GET /docs/api`
- 端点：`/api/site`、`/api/posts`、`/api/posts/{slug}`、`/api/categories`、`/api/tags`、`POST /api/comments`、`/api/me`（Bearer Token）
- 供小程序 / 第三方平台接入，详见 `docs/api.md`

## 测试

```bash
php artisan test    # 84 个用例：迁移/老路由 301/主题/插件/会员支付流/AI 审核/商城购买
```

## 开发者文档

| 文档 | 内容 |
|------|------|
| [docs/development.md](docs/development.md) | 开发规范（代码/插件/主题/队列/测试/提交） |
| [docs/tutorial-plugin.md](docs/tutorial-plugin.md) | 插件开发教程（10 分钟上手，配套 demo） |
| [docs/tutorial-theme.md](docs/tutorial-theme.md) | 主题开发教程（10 分钟上手，配套 demo） |
| [docs/plugins.md](docs/plugins.md) | 插件 API 详解（钩子/依赖/商品化/市场契约） |
| [docs/themes.md](docs/themes.md) | 主题 API 详解（视图解析/注入点/变量） |
| [docs/architecture.md](docs/architecture.md) | 架构与设计决策 |
| [docs/api.md](docs/api.md) | 对外 API（小程序/第三方） |
| [docs/deploy.md](docs/deploy.md) | 部署（nginx/systemd/workerman/cron） |

**上手示例**：
- 插件：`plugins/demo.hello-world/`（短代码 / 钩子 / 路由 / 后台页 / 迁移 / 表单注入，全演示）
- 主题：`themes/demo/`（最小可运行主题，绿色系，覆盖 partials 即可换肤）

## 目录结构

```
app/
  Blog/            # 前台域模块（Controllers/Jobs/Services/Support/View）
  Filament/        # 后台资源与页面
  Console/Commands # sablog:import / attachments:sync-s3 / workerman:serve / content:convert
themes/            # 主题（sablog / modern / demo 示例）
plugins/           # 内置插件（blog-seo / ai-moderation / payment / membership）+ demo.hello-world 示例
docs/              # 全部文档（见上文表格）
```

## 许可

MIT
