# 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` 照常可用(同一队列) ## 外链中转(/go,内置插件) 内置插件 `neatstudio.link-tracker`(外链中转): - 正文外链、评论正文 URL、评论作者网址、友链统一改写为 `/go/{hash}`:302 跳转 + **点击统计**、可**停用拦截**(返回 410)、隐藏真实链接、为未来广告位预留 - 站内链接/相对路径/mailto/tel/锚点不中转;同一 URL 复用同一短码 - 后台「管理 → 外链中转」可查看点击量、启停、删除 - **插件化设计**:核心提供 `tracked_url()` 钩子(`link.redirect` 过滤器)+ 正文 `post.rendered` 过滤器接入点,停用插件后链接恢复直连、不影响内容显示 ## 前台缓存 - 匿名 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/install.md](docs/install.md) | 安装指南(环境/配置/验收清单) | | [docs/import.md](docs/import.md) | Sablog 老数据导入(命令/映射/附件/301 兼容) | | [docs/deploy.md](docs/deploy.md) | 部署(nginx/systemd/workerman/cron/上线清单) | | [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(小程序/第三方) | **上手示例**: - 插件:`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