本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:暂存20260913/docs/插件迁移计划 (2).md。
SoSite 1.0 — 插件迁移计划(数据库独立目录方案)
文档状态:待确认(2026-08-13)
目标:恢复插件系统(P0~P6 已暂停迁移),并将每个插件的数据库独立存放于插件自身目录
原则:插件数据不再写入全局 business.sqlite;每个插件一个独立 SQLite 文件;核心库只读访问保留
一、现状勘察(已读代码确认)
1.1 插件清单(16 个)与数据表映射
| 插件 | 中文名 | 自有数据表 | 依赖核心库 | 备注 |
|---|---|---|---|---|
announcements | 公告 | announcements | settings | activate/uninstall 建删表 |
content_review | 内容审核 | content_reviews、review_notifications | threads/posts | hook 内也有建表 |
daily_checkin | 每日签到 | daily_checkin | users(积分) | activate + init_after 双处建表 |
dice | 掷骰子 | 无表 | 无 | 纯随机,无需数据库 |
edit_info | 编辑历史 | edit_history | — | post/thread_edit_after hook 建表 |
friend_links | 友情链接 | friend_links | — | Admin/Front 双控制器 |
icon_generator | 图标生成 | 无表 | 无 | 前端工具 |
invite | 邀请注册 | invites | users | init_after 建表 |
medal | 勋章系统 | medals、user_medals、user_medal_wears | users | activate + init_after 双处建表 |
notifications | 通知系统 | notifications | — | 多 hook 读写 |
post_favorite | 帖子收藏 | post_favorites | threads | — |
quiz_duel | 答题对决 | quiz_duels 等 | — | 说明.md 列示 |
red_packet | 红包 | red_packets、red_packet_claims | users(积分) | 双表 + 索引 |
seo | SEO 优化 | 无自有表 | threads/blogs/categories/settings(只读) | 仅读核心库,无建表 |
user_profile | 用户主页 | user_follows | users | — |
1.2 现状问题(已确认)
- 全部插件使用全局库:
\app\Core\Database::getInstance()→ business.sqlite,插件表与核心表混居一库 - 建表为 MySQL 语法:
SHOW TABLES LIKE 'x'、INT AUTO_INCREMENT PRIMARY KEY、DATETIME、VARCHAR(n)、UNIQUE KEY、INDEX、NOW()等——SQLite 下无法执行 - 插件系统当前"暂停":
Plugin::init()与全部Plugin::hook()调用已被// [SplitDB] 临时注释:注释(P0~P6 阶段),需恢复 - 旧数据未迁移:migrate.php B2 只迁了 17 张核心表,插件表(announcements/daily_checkin/edit_history 等)仍在旧 MySQL fastbb 库中,dry-run 时可见(announcements 3 行、daily_checkin 36 行、edit_history 57 行…)
二、目标架构
plugins/{插件名}/
Plugin.php ← activate/deactivate/uninstall(建表/删表)
hook/*.php ← 钩子逻辑(读写改走插件库)
data/
{插件名}.sqlite ★ 本插件独立数据库(DBFactory 连接,统一 PRAGMA)
plugin.json / views / assets- 数据流:插件自有表读写 →
Plugin::db($name)(新增,经 DBFactory 连plugins/{name}/data/{name}.sqlite) - 核心库只读(seo 读 threads/blogs/categories、通知读帖子标题等)→ 保留
Database::getInstance()只读访问 - 插件间不共享数据,互不依赖
三、改造执行计划表(待确认后串行,每步自测)
| # | 任务 | 交付物 | 验证方式 |
|---|---|---|---|
| P1 | 核心支撑:Plugin.php 新增 db($name)(插件独立库连接,幂等建 data/ 目录 + DBFactory + 统一 PRAGMA);新增 Plugin::ensureSchema($name, $ddl) 助手(SQLite DDL 幂等建表) | app/Helpers/Plugin.php | 临时脚本:对任意插件名建独立库、建表、读写往返 |
| P2 | 恢复插件系统骨架:恢复 app/init.php 的 Plugin::init();全站取消 Plugin::hook 的临时注释(Controllers/Views/index.php,恢复后插件钩子开始触发) | app/init.php + 各处 hook 还原 | 页面可渲染、无插件报错(先只开 activate 幂等) |
| P3 | 逐插件改造(按依赖从简到繁):
- 无表组:
dice、icon_generator(仅改 getInstance 引用即可,无库) - 单表组:
announcements、daily_checkin、edit_info、friend_links、invite、notifications、post_favorite、user_profile、quiz_duel - 多表组:
content_review(2 表)、medal(3 表)、red_packet(2 表) - 只读组:
seo(无建表,保留核心库只读) | 各插件 Plugin.php/hook/Controller | 每插件:activate 建表成功、页面功能走通、uninstall 删表 |
| P4 | 插件数据迁移脚本:cli/migrate_plugins.php——从旧 MySQL(fastbb)按插件表清单读取 → 写入各插件独立库(列白名单 + 显式 ID 保外键) | 新脚本 | --dry-run 行数预览 + 真实迁移 + 抽查行数一致 |
| P5 | 交付:update/ 同步 + CHANGELOG.md 追加 | 交付物 | 与根目录 diff 一致;php -l 全过 |
四、关键技术设计
4.1 插件独立库连接(P1 核心)
// app/Helpers/Plugin.php 新增
public static function db(string $name): \PDO
{
$dir = self::$pluginDir . '/' . $name . '/data';
if (!is_dir($dir)) @mkdir($dir, 0755, true);
return \app\SplitDB\DBFactory::getConnection($dir . '/' . $name . '.sqlite');
}- 复用 DBFactory:统一 6 条 PRAGMA(WAL/busy_timeout 等)、LRU 池、路径归一化,零新增连接管理代码
- 幂等:data/ 目录缺失自动创建
4.2 SQLite DDL 转换规则(P3 逐插件)
| MySQL 写法 | SQLite 等价 |
|---|---|
id INT AUTO_INCREMENT PRIMARY KEY | id INTEGER PRIMARY KEY |
VARCHAR(n) / DATETIME | TEXT |
SHOW TABLES LIKE 'x' 探测 | 直接 CREATE TABLE IF NOT EXISTS(幂等)或 PRAGMA table_info |
UNIQUE KEY uk (a,b) | UNIQUE(a,b)(表内)或独立 CREATE UNIQUE INDEX IF NOT EXISTS |
INDEX idx (a) | CREATE INDEX IF NOT EXISTS idx ON t (a) |
NOW() | PHP time() / date('Y-m-d H:i:s') 参数绑定 |
4.3 数据迁移(P4)
- 数据源:旧 MySQL
fastbb(复用 migrate.php 的连接参数机制--host/--name/--user/--pass) - 表清单:P1 表中"自有数据表"列(约 18 张),按插件归属迁移到对应独立库
- 规则:列白名单(仅迁目标表存在的列)、显式 ID、
_runtime_*类跳过、迁移后各插件库行数与旧库一致
4.4 恢复钩子的范围(P2)
app/init.php:Plugin::init()恢复- Controllers / Views /
index.php:约 25+ 处// [SplitDB] 临时注释:还原(按 CHANGELOG P5/P6 记录的位置逐一恢复) - 恢复顺序注意:先恢复
init()与activate()链路(建表),再恢复业务钩子,避免钩子触发时表不存在
五、风险与红线
- 不删插件代码(沿用补充要求 #4):所有恢复均为"去掉注释前缀",原始代码保留
- 数据安全:插件表迁移失败 → 报错中止,不产生部分残留;迁移前
--dry-run预览 - 兼容:插件同时兼容"已迁移独立库"与"全新安装"两种状态(activate 幂等建表)
- seo 等只读插件:不建独立库,保留核心库只读——避免重复数据
- 全部文件 UTF-8 no BOM + LF;
php -l校验;每插件改造后立即自测
六、验收流程(用户侧)
- 后台 → 插件管理 → 逐个激活插件(announcements → medal → red_packet…)
- 每插件功能走通:公告展示、每日签到、编辑历史、收藏、通知、勋章佩戴、红包发放、答题对决…
- 检查
plugins/{插件名}/data/{插件名}.sqlite已生成且独立(不依赖 business.sqlite) - 跑
cli/migrate_plugins.php --dry-run→ 全量 → 抽查旧数据行数一致 - 卸载某插件 → 独立库删除、核心库无残留
七、待用户确认点
- 独立库路径:
plugins/{插件名}/data/{插件名}.sqlite(推荐)还是plugins/{插件名}/db.sqlite或别的? - 迁移范围:插件旧数据(fastbb 中约 18 张插件表)是否本次一并迁移?(推荐一并做,否则新装无历史数据)
- 插件激活范围:全部 16 个插件都恢复,还是先恢复核心几个(公告/签到/收藏/通知/勋章)验证后再全量?