Skip to content

插件开发规范 ​

新开发者从核心版本建立插件仓库、制作发行包与安装升级的完整流程,参见插件安装与更新。

适用范围 ​

本文档适用于基于当前项目开发的源码级业务插件,例如商城、新闻资讯、内容管理和营销活动等插件。

插件基线为核心 v0.0.4(最低要求,推荐使用最新的版本开发),采用源码级、编译期注册模式:

  • 不支持上传 Go 文件后动态加载,不支持运行中热启停;
  • 插件安装、升级、启停后必须重新构建并重启管理端 API 和用户端 API;
  • 插件后端和管理端页面都进入主项目构建流程。

强制原则 ​

  1. 插件 ID 发布后不得修改:小写字母开头,仅含小写字母、数字和下划线,2~64 位;
  2. 插件间只能通过公开服务接口或明确声明的依赖协作,禁止直接修改其他插件的业务表;
  3. Controller 只负责参数绑定、调用 Logic 和统一响应,业务规则及事务放在 Logic;
  4. Logic 不直接返回 Model,接口响应必须在 resp 中重新定义;
  5. Model、Param、Resp 的字段必须添加 JSON、GORM 或校验 Tag,并写明字段注释;
  6. 表关联关系在 Resp 中定义,不在 Model 中定义;优先使用 GORM Preload;
  7. 管理端业务接口默认注册到 Permission 路由组,禁止绕过登录、权限和操作日志;
  8. 数据库表和字段必须有注释,字符集 utf8mb4,排序规则 utf8mb4_general_ci;
  9. 枚举值从 1 开始,并在 Go 枚举文件和前端枚举文件中集中定义;
  10. 业务表中的文件字段只保存相对路径,展示时统一补全访问地址;
  11. 不使用 GORM AutoMigrate,结构变化通过增量 SQL 交付;
  12. 前端使用 TypeScript、ES6 与箭头函数,不得用 any 绕过类型检查;
  13. 插件页面复用现有组件、主题变量与交互规范,不得覆盖全局样式;
  14. 富文本编辑必须复用宿主 src/components/RichTextEditor.vue。

目录结构 ​

后端 ​

text
server_api/internal/plugins/{plugin_id}/
├── plugin.go          # 清单、迁移声明、路由注册与生命周期
├── enums/             # 插件业务枚举(从 1 开始)
├── model/             # 插件自身的数据库表模型
├── service/           # 管理端、用户端共用的插件公开能力
├── admin/             # 管理端接口:controller / logic / param / resp
└── api/               # 用户端接口:controller / logic / param / resp

Param 与 Resp 按业务功能拆分文件(如 admin/param/article_create.go),禁止整个插件堆放在单一 param.go / resp.go。只有单端能力时可省略 admin 或 api 目录,但路由注册方法仍需实现并返回 nil。

管理端 ​

text
admin_client/src/plugins/{plugin_id}/
├── api/               # 接口封装
├── types/             # 请求与响应类型
├── enums/             # 前端枚举
├── components/        # 表单等组件(与列表页拆分)
└── views/{view_path}/index.vue

页面路径约定:数据库菜单路径 /plugin/{plugin_id}/{view_path} 对应 src/plugins/{plugin_id}/views/{view_path}/index.vue。

迁移 SQL ​

text
database/migrations/
├── v1.0.0.sql
└── v1.1.0.sql

插件契约 ​

插件必须导出 New() 并实现 internal/common/plugin.Plugin:

go
package news

import (
    "context"

    commonplugin "server_api/internal/common/plugin"
)

type Plugin struct{}

func New() *Plugin { return &Plugin{} }

func (p *Plugin) Manifest() commonplugin.Manifest {
    return commonplugin.Manifest{
        PluginID:    "news",
        Name:        "新闻资讯",
        Version:     "1.0.0",
        CoreVersion: commonplugin.CoreVersion,
        Dependencies: []commonplugin.Dependency{},
    }
}

func (p *Plugin) Migrations() []commonplugin.Migration {
    return []commonplugin.Migration{
        {Version: "1.0.0", Description: "创建新闻资讯基础表", Checksum: "SQL 文件 SHA256"},
    }
}

func (p *Plugin) RegisterAdminRoutes(ctx *commonplugin.Context, groups commonplugin.AdminRouteGroups) error {
    groups.Permission.GET("/plugin/news/article/list", controller.List)
    return nil
}

func (p *Plugin) RegisterAPIRoutes(ctx *commonplugin.Context, groups commonplugin.APIRouteGroups) error {
    return nil
}

func (p *Plugin) Start(ctx context.Context, service commonplugin.ServiceType) error { return nil }
func (p *Plugin) Stop(ctx context.Context) error { return nil }

清单要求:

  • plugin.json.version、后端 Manifest.Version、Git 标签与 ZIP 文件名保持一致,遵循 SemVer;
  • CoreVersion 必须与实际宿主 commonplugin.CoreVersion 精确一致,不解析版本范围;
  • 所有依赖在 Dependencies 中声明,精确匹配版本,禁止循环依赖;
  • Version 每次对外发布必须提升,禁止同版本重新打包。

安装器通过以下命令扫描插件并生成注册文件 internal/plugins/register_gen.go(禁止手工编辑):

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

数据库启用但未编译进程序的插件会导致服务拒绝启动。

数据库规范 ​

建表要求 ​

sql
CREATE TABLE IF NOT EXISTS `plg_news_article` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '文章ID',
  `created_at` datetime(3) NOT NULL COMMENT '创建时间',
  `updated_at` datetime(3) NOT NULL COMMENT '更新时间',
  `deleted_at` datetime(3) DEFAULT NULL COMMENT '删除时间',
  `title` varchar(200) COLLATE utf8mb4_general_ci NOT NULL COMMENT '文章标题',
  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:1草稿,2已发布,3已下线',
  PRIMARY KEY (`id`),
  KEY `idx_news_article_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='新闻文章表';
  • 插件业务表统一 plg_{plugin_id}_* 前缀,禁止使用 sys_ 前缀;
  • 业务唯一性由唯一索引兜底;金额用整数最小货币单位或明确精度 decimal,禁止浮点数;
  • 迁移必须可安全重试(MySQL DDL 隐式提交,无法与元数据同事务);
  • 已发布迁移不可修改,只能新增更高版本;无数据库变化的版本不需要空迁移,但插件版本仍必须提升;
  • 每个版本必须提供 docs/database/v{version}.md:业务表、表内关系、对核心表与其他插件表的引用、文件归属、Remove/Purge 影响;没有引用也要明确写「无」——缺少声明时安装器拒绝校验、安装、升级和 Purge。

迁移允许与禁止 ​

允许:CREATE TABLE、ALTER TABLE、CREATE INDEX、对当前插件表的 INSERT(显式字段、不含自增 ID)。

拒绝:DROP、TRUNCATE、DELETE、RENAME、业务数据 UPDATE、破坏性 ALTER、存储过程/触发器、操作 sys_ 核心表或其他插件表、写入菜单与插件元数据。

路由与鉴权 ​

路由组使用场景要求
Permission管理端普通业务接口默认选择,登录 + 接口权限 + 操作日志完整链路
Auth登录即可访问的个人能力必须说明不需要角色权限的原因
Public无登录态的回调或公开能力必须自行实现验签、防重放、限流和幂等

用户端私有数据接口注册到 Auth,公开列表/详情或第三方回调注册到 Public;即使用户已登录,也必须在 Logic 中校验资源归属。

菜单与权限 ​

  • 菜单、按钮和接口权限在 plugin.json.menus 中交付,不编写 sys_menu INSERT SQL;
  • 菜单只用 key 与 parent_key 描述层级,真实 ID 由数据库生成并记录到 sys_plugin_menu;
  • 页面菜单路径必须位于 /plugin/{plugin_id}/ 下;
  • 普通角色默认不自动获得插件权限,由管理员明确授权。

版本文档 ​

每个插件版本必须提供(文件名版本与 plugin.json.version 完全一致):

  • docs/api/v{version}.md:覆盖管理端、用户端与回调接口,含参数、响应、鉴权、错误场景与版本变更记录;
  • docs/database/v{version}.md:数据库引用说明(内容要求见上);
  • docs/updates/v{version}.md:版本类型、主要变化、数据库变化、升级操作、兼容性与回滚方式。

自动生成的 OpenAPI/Swagger 可作补充,不能替代上述文档。

前端开发要点 ​

  1. 请求统一走 request<T>(),接口域名只从环境变量读取;
  2. 列表页与表单组件拆分,表单交互不写在列表页 index.vue;
  3. 按钮使用 v-perm;时间展示到秒;文件字段只提交相对路径;
  4. 明暗主题都要适配;不得定义跨模块的全局 CSS;
  5. 新增页面后执行 pnpm exec vue-tsc --noEmit 与 pnpm build 确认可解析。

生命周期与后台任务 ​

Start 只用于确实需要的后台任务、订阅或连接,普通 CRUD 插件返回 nil 即可。后台任务必须:

  • 监听传入的 context.Context,取消后及时退出;
  • 避免重复启动,共享状态加锁或使用并发安全结构;
  • 设置外部请求超时,可重试任务带退避与最大重试次数,消费任务设计幂等;
  • 在 Stop 中释放插件独占资源,不关闭宿主的数据库或 Redis 连接。

发布前检查清单 ​

后端:插件 ID / 版本 / 依赖正确;分层合规;Logic 未返回 Model;关联无 N+1;管理端接口走 Permission;Public 接口有验签防重放限流;并发写入有唯一索引或锁;日志无敏感信息;gofmt / go vet / go build / go test 通过。

数据库:表前缀正确;注释齐全;utf8mb4_general_ci;枚举从 1 开始且前后端同步;索引合理;迁移可重试;版本与 SHA256 已登记;数据库引用文档完整。

管理端:页面路径符合解析约定;TS/ES6/箭头函数;表单拆分独立组件;v-perm 生效;明暗主题正常;时间到秒;无全局样式污染。

验证通过后仍需在测试环境完成安装、启用、权限分配、停用、升级与服务重启流程。

禁止事项 ​

  • 禁止绕过注册中心直接修改核心路由初始化;
  • 禁止在插件启动时自动建表或修改表结构;
  • 禁止把 Model 直接作为接口响应,禁止在 Model 中定义业务关联结构;
  • 禁止跨插件直接改表、使用未校验的字符串拼接 SQL / 排序 / 表名;
  • 禁止在 Public 路由中信任客户端身份字段;
  • 禁止启动不能停止、没有超时或无限重试的 Goroutine;
  • 禁止把密钥、Token、证书或用户敏感信息写入日志;
  • 禁止覆盖全局 CSS、全局组件或核心菜单行为;
  • 禁止修改已发布并登记摘要的迁移 SQL。