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 兜底,不影响核心页面解析。
升级前准备
- 备份 MySQL 数据库。
- 备份当前后端二进制、管理端构建产物和配置文件。
- 确认当前基础版本数据库已经执行
sql/v0.0.1/cf_backend_frame.sql。 - 在测试环境先执行升级并完成启动验证。
本版本没有修改现有业务表数据,但新版本启动时会要求两张插件核心表存在。
数据库升级
在 server_api 目录执行:
mysql -uroot -p 数据库名 < sql/v0.0.2/plugin_base.sql已经执行过早期 v0.0.2 脚本的数据库,还需要执行一次增量脚本:
mysql -uroot -p 数据库名 < sql/v0.0.2/plugin_menu_parent_custom.sql脚本使用 CREATE TABLE IF NOT EXISTS,不会删除现有表或业务数据。执行后检查:
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,所有表和字段均带有注释。
插件安装器
校验发行包:
go run . plugin validate /path/plugin.zip同时安装后端、管理端并自动生成注册文件:
go run . plugin install /path/plugin.zip --server-root . --admin-root ../admin_client显式应用数据库迁移、菜单业务键和安装记录:
go run . plugin install /path/plugin.zip \
--server-root . \
--admin-root ../admin_client \
--config config.yaml \
--apply-database插件升级使用 plugin upgrade。安装器不自动启用插件,完成两端检查和构建后仍需在插件管理页面启用,并重启两个 API 服务。
推荐部署顺序
- 停止旧版管理端 API 和用户端 API。
- 备份数据库。
- 执行
sql/v0.0.2/plugin_base.sql。 - 发布并启动新版管理端 API。
- 发布并启动新版用户端 API。
- 发布新版
admin_client静态资源。 - 检查两个服务日志和核心页面。
不要先发布新版后端再执行 SQL,否则启动检查会提示缺少 sys_plugin 和 sys_plugin_migration。
回滚说明
如果尚未安装任何业务插件,代码可以回滚到旧版本,两张新增表保留不会影响旧版运行。
不建议在普通代码回滚时删除插件表。只有确认没有任何插件状态、迁移审计或业务依赖后,才可以人工清理:
-- 危险操作:必须确认无插件数据后手工执行
DROP TABLE sys_plugin_migration;
DROP TABLE sys_plugin;后端插件开发流程
1. 创建插件目录
推荐结构:
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. 实现插件契约
插件需要实现:
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 run . plugin generate --server-root .开发者不再手工编辑 register.go。数据库不能启用未编译的代码。
4. 编写插件 SQL
插件 SQL 必须独立放在自己的迁移目录,表名建议使用:
plg_{plugin_id}_{table_name}例如:
plg_news_article
plg_news_category
plg_shop_product
plg_shop_order要求:
- 表和字段都有中文注释;
- 使用
utf8mb4_general_ci; - 数值枚举从 1 开始;
- 文件字段保存相对路径;
- 并发唯一性由唯一索引保证;
- 不使用
AutoMigrate; - 迁移只向前执行,不自动执行危险回滚。
迁移文件通过插件安装器执行;安装器验证摘要后写入 sys_plugin_migration。插件声明、发行包清单和数据库记录的摘要必须一致。
5. 写入插件状态
插件安装完成但尚未启用时:
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, '{}');迁移、菜单、权限和前端均准备完成后再启用:
UPDATE sys_plugin
SET status = 1, updated_at = NOW(3)
WHERE plugin_id = 'news' AND deleted_at IS NULL;源码级插件的启停在服务启动阶段生效。修改插件状态后需要重启管理端 API 和用户端 API,不支持运行中热卸载。
6. 注册路由
管理端普通业务接口应注册到:
groups.Permission.GET("/plugin/news/article/list", controller.List)
groups.Permission.POST("/plugin/news/article/save", controller.Save)用户端登录后接口注册到:
groups.Auth.GET("/plugin/news/article/detail", controller.Detail)公开回调只有在确实不适合登录鉴权时才允许注册到 Public,并必须实现验签、防重放、限流和幂等。
7. 菜单与权限
插件菜单和按钮通过 plugin.json.menus 使用业务键描述,由安装器写入 sys_menu 和 sys_plugin_menu:
GET:/admin/plugin/news/article/list
POST:/admin/plugin/news/article/save插件不得指定菜单 id 或 parent_id。普通角色默认不应自动获得新插件权限,由管理员明确授权后清理权限缓存或重新登录。
管理端插件开发流程
以新闻列表为例:
admin_client/src/plugins/news/
├── api/
├── types/
├── enums/
├── components/
└── views/
└── article/
└── index.vue数据库菜单路径配置为:
/plugin/news/article注册中心会解析为:
src/plugins/news/views/article/index.vue插件页面必须:
- 使用
<script setup lang="ts">; - 所有函数使用箭头函数;
- 请求统一使用项目
request<T>(); - 类型和枚举分别存放;
- 按钮使用
v-perm; - 使用现有主题变量并适配明暗主题;
- 日期时间格式化到秒;
- 文件业务字段只提交相对路径。
新增插件页面后必须重新执行管理端构建,当前版本不加载远程 JavaScript。
生命周期行为
服务启动
- 检查核心数据表;
- 注册编译期插件;
- 读取数据库中状态为启用的插件;
- 校验代码版本与数据库安装版本;
- 校验迁移记录和摘要;
- 校验依赖是否启用及版本是否一致;
- 检测循环依赖;
- 按依赖顺序注册路由;
- 按依赖顺序启动后台能力。
服务停止
- 取消插件运行 Context;
- 按启动相反顺序调用
Stop; - 停止超时使用现有
shutdown_timeout_seconds; - 最后释放 Redis 和 MySQL 连接。
插件后台任务必须监听 Context,不允许启动无法停止的永久 Goroutine。
兼容性说明
- 现有核心路由、菜单路径和业务接口不变。
- 没有安装插件时,注册中心不会增加任何路由或后台任务。
- 核心页面仍从
src/views加载。 - 插件页面只识别
/plugin/{plugin_id}/...路径,不会抢占现有页面路径。 - 插件表不会加入核心静态表清单,停用插件不会阻止核心服务启动。
- 插件状态表属于核心基础设施,因此新版服务启动前必须执行 v0.0.2 SQL。
验证命令
后端:
gofmt -l .
go vet ./...
go build ./...
go test ./...管理端:
pnpm exec vue-tsc --noEmit
pnpm build已知边界
- 当前没有插件管理页面和在线安装接口;插件状态通过安装 SQL 或受控部署流程维护。
- 当前不支持热启停,启停后必须重启服务。
- 当前插件依赖版本为精确匹配,不解析
>=1.0.0等版本范围。 - 当前不支持远程前端模块或微前端加载。
- 当前只在命令行显式传入
--apply-database时执行通过安全检查的插件迁移,服务启动和管理页面不会隐式修改表结构。 - 当前没有不可信代码沙箱,第三方插件必须完成代码审查后才能编译部署。
以上边界是有意保留的安全约束,后续应根据真实插件数量和交付方式逐步演进,不建议一次性引入在线插件市场和运行时任意代码执行。