Skip to content

v0.0.2 插件化基础设施升级说明 ​

后续新插件必须遵循 插件开发规范。 本文记录 v0.0.2 当时的升级内容,仅用于历史版本维护。新插件必须基于核心 v0.0.4 开发,当前分支、分发和安装流程参见 插件开发、分发、安装与更新指南。

版本目标 ​

本版本在不改变现有核心业务行为的前提下,引入源码级插件基础设施,为后续商城、新闻资讯等功能按插件独立开发做准备。

当前版本只提供插件契约、状态校验、迁移校验、路由接入、生命周期和管理端页面解析能力,不包含在线插件市场,也不支持运行时加载来源未知的 Go 动态库或远程 JavaScript。

主要变更 ​

后端 ​

  • 新增 internal/common/plugin:
    • 插件清单 Manifest;
    • 管理端和用户端受控路由组;
    • 插件数据库迁移声明;
    • 插件注册中心;
    • 依赖、版本和循环依赖检查;
    • 插件启动与反向停止生命周期。
  • 新增稳定注册入口和自动生成的 register_gen.go,安装器扫描插件目录生成静态注册代码。
  • 管理端插件只能注册到宿主提供的以下路由组:
    • Public:公开接口,必须由插件自行完成签名、防重放等保护;
    • Auth:只校验登录态;
    • Permission:登录、接口权限和操作日志完整链路。
  • 用户端插件只能注册到 Public 或 Auth 路由组。
  • 启用插件按依赖顺序注册和启动,停止时按相反顺序执行。
  • 已启用但未编译进程序、版本不一致、迁移缺失或依赖异常时,服务拒绝启动并返回明确错误。
  • CheckTables 只维护核心表清单;插件业务表由插件迁移记录校验,不再写入核心静态表清单。
  • 服务版本更新为 v0.0.2。

数据库 ​

新增核心表:

表作用
sys_plugin保存插件 ID、名称、已安装版本、启停状态及清单快照
sys_plugin_migration保存插件迁移版本、SHA256 摘要、状态、耗时和错误信息
sys_plugin_menu保存插件菜单业务键与系统菜单自增 ID 的映射
sys_plugin_install_log保存插件安装、升级、发行包摘要及执行结果

sys_plugin_menu.parent_source 记录菜单父级来源:1 表示使用插件清单默认层级,2 表示管理员已在菜单管理中自定义层级。插件升级会保留管理员自定义的 parent_id,同时继续更新名称、路径、接口、图标等声明字段。插件按钮必须保留在所属页面下,不能单独移动。

增量脚本会比较现有菜单父级与插件映射中的默认 parent_key,自动识别并标记功能上线前已经手工移动过的插件目录和页面;按钮不会被标记为自定义父级。

插件状态枚举:

值含义
1启用
2停用

插件迁移状态枚举:

值含义
1成功
2失败

管理端 ​

  • 新增 src/plugins/registry.ts。
  • 新增“系统维护 → 插件管理”页面,可查看安装版本、程序编译版本、插件状态、清单及数据库迁移记录。
  • 插件信息增加 Logo、作者、插件主页和描述字段;Logo 只保存相对路径并动态补全地址,管理页面可以上传和维护这些展示信息,描述使用多行文本域。
  • 插件管理支持启用和停用;操作前校验编译状态、版本、迁移和插件依赖,状态在管理端 API 与用户端 API 重启后生效。
  • 动态路由除核心 src/views 外,也会解析编译进项目的插件页面。
  • 插件菜单路径使用 /plugin/{plugin_id}/{view_path}。
  • 插件页面使用 src/plugins/{plugin_id}/views/{view_path}/index.vue。
  • 未编译页面仍然进入现有 404 兜底,不影响核心页面解析。

升级前准备 ​

  1. 备份 MySQL 数据库。
  2. 备份当前后端二进制、管理端构建产物和配置文件。
  3. 确认当前基础版本数据库已经执行 sql/v0.0.1/cf_backend_frame.sql。
  4. 在测试环境先执行升级并完成启动验证。

本版本没有修改现有业务表数据,但新版本启动时会要求两张插件核心表存在。

数据库升级 ​

在 server_api 目录执行:

bash
mysql -uroot -p 数据库名 < sql/v0.0.2/plugin_base.sql

已经执行过早期 v0.0.2 脚本的数据库,还需要执行一次增量脚本:

bash
mysql -uroot -p 数据库名 < sql/v0.0.2/plugin_menu_parent_custom.sql

脚本使用 CREATE TABLE IF NOT EXISTS,不会删除现有表或业务数据。执行后检查:

sql
SHOW TABLES LIKE 'sys_plugin';
SHOW TABLES LIKE 'sys_plugin_migration';
SHOW TABLES LIKE 'sys_plugin_menu';
SHOW TABLES LIKE 'sys_plugin_install_log';
SHOW CREATE TABLE sys_plugin;
SHOW CREATE TABLE sys_plugin_migration;
SHOW CREATE TABLE sys_plugin_menu;
SHOW CREATE TABLE sys_plugin_install_log;

如果在插件管理功能加入前已经执行过本脚本,可以安全地重新执行一次。脚本中的建表语句和菜单写入均做了重复执行保护。重新登录管理端后,“系统维护”下会显示“插件管理”;普通角色还需要由管理员分配该菜单及“插件启停”按钮权限。

所有字符字段使用 utf8mb4_general_ci,所有表和字段均带有注释。

插件安装器 ​

校验发行包:

bash
go run . plugin validate /path/plugin.zip

同时安装后端、管理端并自动生成注册文件:

bash
go run . plugin install /path/plugin.zip --server-root . --admin-root ../admin_client

显式应用数据库迁移、菜单业务键和安装记录:

bash
go run . plugin install /path/plugin.zip \
  --server-root . \
  --admin-root ../admin_client \
  --config config.yaml \
  --apply-database

插件升级使用 plugin upgrade。安装器不自动启用插件,完成两端检查和构建后仍需在插件管理页面启用,并重启两个 API 服务。

推荐部署顺序 ​

  1. 停止旧版管理端 API 和用户端 API。
  2. 备份数据库。
  3. 执行 sql/v0.0.2/plugin_base.sql。
  4. 发布并启动新版管理端 API。
  5. 发布并启动新版用户端 API。
  6. 发布新版 admin_client 静态资源。
  7. 检查两个服务日志和核心页面。

不要先发布新版后端再执行 SQL,否则启动检查会提示缺少 sys_plugin 和 sys_plugin_migration。

回滚说明 ​

如果尚未安装任何业务插件,代码可以回滚到旧版本,两张新增表保留不会影响旧版运行。

不建议在普通代码回滚时删除插件表。只有确认没有任何插件状态、迁移审计或业务依赖后,才可以人工清理:

sql
-- 危险操作:必须确认无插件数据后手工执行
DROP TABLE sys_plugin_migration;
DROP TABLE sys_plugin;

后端插件开发流程 ​

1. 创建插件目录 ​

推荐结构:

text
server_api/internal/plugins/news/
├── plugin.go
├── admin/
│   ├── controller/
│   ├── logic/
│   ├── param/
│   └── resp/
├── api/
│   ├── controller/
│   ├── logic/
│   ├── param/
│   └── resp/
├── enums/
├── model/
└── migrations/

插件内部仍需遵循项目分层规范:Controller 只绑定参数和响应,Logic 处理业务与事务,Param/Resp 按功能拆分,Logic 不直接返回 Model。

2. 实现插件契约 ​

插件需要实现:

go
type Plugin interface {
    Manifest() Manifest
    Migrations() []Migration
    RegisterAdminRoutes(ctx *Context, groups AdminRouteGroups) error
    RegisterAPIRoutes(ctx *Context, groups APIRouteGroups) error
    Start(ctx context.Context, service ServiceType) error
    Stop(ctx context.Context) error
}

本历史版本对应的核心兼容版本为 0.0.2。新插件不再以该版本作为开发基线,必须切换到 v0.0.4;Manifest.CoreVersion 必须与实际宿主版本精确一致,当前不解析语义化版本范围。

插件 ID 规则:

  • 长度 2 至 64 位;
  • 必须以小写字母开头;
  • 只能包含小写字母、数字和下划线;
  • 发布后不得修改。

3. 注册编译期插件 ​

插件必须导出 New(),通过命令自动生成注册文件:

go
go run . plugin generate --server-root .

开发者不再手工编辑 register.go。数据库不能启用未编译的代码。

4. 编写插件 SQL ​

插件 SQL 必须独立放在自己的迁移目录,表名建议使用:

text
plg_{plugin_id}_{table_name}

例如:

text
plg_news_article
plg_news_category
plg_shop_product
plg_shop_order

要求:

  • 表和字段都有中文注释;
  • 使用 utf8mb4_general_ci;
  • 数值枚举从 1 开始;
  • 文件字段保存相对路径;
  • 并发唯一性由唯一索引保证;
  • 不使用 AutoMigrate;
  • 迁移只向前执行,不自动执行危险回滚。

迁移文件通过插件安装器执行;安装器验证摘要后写入 sys_plugin_migration。插件声明、发行包清单和数据库记录的摘要必须一致。

5. 写入插件状态 ​

插件安装完成但尚未启用时:

sql
INSERT INTO sys_plugin
  (created_at, updated_at, plugin_id, name, version, logo, author, homepage, description, status, manifest)
VALUES
  (NOW(3), NOW(3), 'news', '新闻资讯', '1.0.0', 'plugins/news/logo.png', '项目维护者',
   'https://example.com/plugins/news', '提供新闻分类、文章和发布管理能力', 2, '{}');

迁移、菜单、权限和前端均准备完成后再启用:

sql
UPDATE sys_plugin
SET status = 1, updated_at = NOW(3)
WHERE plugin_id = 'news' AND deleted_at IS NULL;

源码级插件的启停在服务启动阶段生效。修改插件状态后需要重启管理端 API 和用户端 API,不支持运行中热卸载。

6. 注册路由 ​

管理端普通业务接口应注册到:

go
groups.Permission.GET("/plugin/news/article/list", controller.List)
groups.Permission.POST("/plugin/news/article/save", controller.Save)

用户端登录后接口注册到:

go
groups.Auth.GET("/plugin/news/article/detail", controller.Detail)

公开回调只有在确实不适合登录鉴权时才允许注册到 Public,并必须实现验签、防重放、限流和幂等。

7. 菜单与权限 ​

插件菜单和按钮通过 plugin.json.menus 使用业务键描述,由安装器写入 sys_menu 和 sys_plugin_menu:

text
GET:/admin/plugin/news/article/list
POST:/admin/plugin/news/article/save

插件不得指定菜单 id 或 parent_id。普通角色默认不应自动获得新插件权限,由管理员明确授权后清理权限缓存或重新登录。

管理端插件开发流程 ​

以新闻列表为例:

text
admin_client/src/plugins/news/
├── api/
├── types/
├── enums/
├── components/
└── views/
    └── article/
        └── index.vue

数据库菜单路径配置为:

text
/plugin/news/article

注册中心会解析为:

text
src/plugins/news/views/article/index.vue

插件页面必须:

  • 使用 <script setup lang="ts">;
  • 所有函数使用箭头函数;
  • 请求统一使用项目 request<T>();
  • 类型和枚举分别存放;
  • 按钮使用 v-perm;
  • 使用现有主题变量并适配明暗主题;
  • 日期时间格式化到秒;
  • 文件业务字段只提交相对路径。

新增插件页面后必须重新执行管理端构建,当前版本不加载远程 JavaScript。

生命周期行为 ​

服务启动 ​

  1. 检查核心数据表;
  2. 注册编译期插件;
  3. 读取数据库中状态为启用的插件;
  4. 校验代码版本与数据库安装版本;
  5. 校验迁移记录和摘要;
  6. 校验依赖是否启用及版本是否一致;
  7. 检测循环依赖;
  8. 按依赖顺序注册路由;
  9. 按依赖顺序启动后台能力。

服务停止 ​

  1. 取消插件运行 Context;
  2. 按启动相反顺序调用 Stop;
  3. 停止超时使用现有 shutdown_timeout_seconds;
  4. 最后释放 Redis 和 MySQL 连接。

插件后台任务必须监听 Context,不允许启动无法停止的永久 Goroutine。

兼容性说明 ​

  • 现有核心路由、菜单路径和业务接口不变。
  • 没有安装插件时,注册中心不会增加任何路由或后台任务。
  • 核心页面仍从 src/views 加载。
  • 插件页面只识别 /plugin/{plugin_id}/... 路径,不会抢占现有页面路径。
  • 插件表不会加入核心静态表清单,停用插件不会阻止核心服务启动。
  • 插件状态表属于核心基础设施,因此新版服务启动前必须执行 v0.0.2 SQL。

验证命令 ​

后端:

bash
gofmt -l .
go vet ./...
go build ./...
go test ./...

管理端:

bash
pnpm exec vue-tsc --noEmit
pnpm build

已知边界 ​

  • 当前没有插件管理页面和在线安装接口;插件状态通过安装 SQL 或受控部署流程维护。
  • 当前不支持热启停,启停后必须重启服务。
  • 当前插件依赖版本为精确匹配,不解析 >=1.0.0 等版本范围。
  • 当前不支持远程前端模块或微前端加载。
  • 当前只在命令行显式传入 --apply-database 时执行通过安全检查的插件迁移,服务启动和管理页面不会隐式修改表结构。
  • 当前没有不可信代码沙箱,第三方插件必须完成代码审查后才能编译部署。

以上边界是有意保留的安全约束,后续应根据真实插件数量和交付方式逐步演进,不建议一次性引入在线插件市场和运行时任意代码执行。