GFast V4 插件 / 应用开发手册

适用版本:框架 3.4.2(internal/consts/version.go)
面向对象:需要开发、打包、安装、维护 GFast 插件或模块的后端 / 前端开发人员
目标:读完本文即可独立完成「生成骨架 → 写代码 → 打包导出 → 安装 → 卸载」全流程


1. 概述

GFast 的「插件 / 应用」本质是一段按约定组织的源码 + 元数据 + SQL + 菜单,由后台的「插件管理」模块(internal/app/plugins/manage)负责打包、安装、卸载。

它有两种布局,能力等价,只是落盘位置不同:

布局 installPath 源码落盘位置 前端组件路径 典型例子
容器插件 普通路径,缺省 plugins internal/app/<installPath>/<name>/(例如internal/app/plugins/sms) src/views/<installPath>/<name> sms、mail
一级模块(“应用”) . internal/app/<name>/(例如internal/app/cms) src/views/<name> cms
  • <name>:插件标识名,规则 ^[A-Za-z][A-Za-z0-9_]{1,63}$(字母开头,2–64 位)。
  • 一级模块的 <name> 不能取框架保留目录:system、common、boot、plugins。
  • 一级模块必须使用「原生布局」(见 §5.2),旧式 go/<name>/ 会被安装器直接拒绝。

2. 快速开始(最短路径)

步骤 0:先生成骨架包(强烈推荐)

后台「插件管理 → 已安装插件 Tab → 新建插件包」填写:标识名、名称、版本、作者、installPath、业务表、方言、依赖,点击「生成」。

后端对应接口 GET /api/v1/plugins/manage/scaffold,会产出一个 *-scaffold.zip,内含可直接编辑的:

<name>/
├── install.json          # 已填好的元数据
├── install_mysql.sql     # MySQL 建表模板(带 TODO)
├── install_pgsql.sql     # PostgreSQL 建表模板
├── menus.json            # 约定菜单树(目录 + 列表页 + 4 个按钮)
├── README.md             # 包内说明(含目录规范与注意事项)
├── go/                   # 空目录,放后端源码
└── vue/                  # 空目录,放前端源码

缺省方言为 mysql,pgsql(同时生成两套模板)。可在弹窗里改成只声明实际支持的方言。

步骤 1:解压骨架,按 §5 / §6 放入后端与前端源码

步骤 2:补全 install_mysql.sql / install_pgsql.sql 建表语句(§8)

步骤 3:调整 menus.json(§7)

步骤 4:压缩为 zip,根目录必须是 <name>/(不要多套一层目录)

<name>.zip
└── <name>/              ← zip 内第一层必须是插件名
    ├── install.json
    ├── ...

步骤 5:安装

  • 本地导入:后台「插件管理 → 在线商城 Tab → 本地导入安装」上传 zip。
  • 在线安装:需先登录插件商城,从商城安装并选择版本。

安装完成后:后端必须重新编译并重启(go run main.go);前端开发模式自动热更,生产环境需重新构建。

步骤 6:给角色授权

新菜单与按钮权限插入后,需要在「角色管理」里勾选授权,才会对非超管账号可见。


3. 插件包结构

以容器插件 sms(installPath=plugins)为例,sms-3.4.0.zip 的真实结构:

sms/
├── install.json                      # 插件元数据(必填 name、version)
├── install_mysql.sql                 # MySQL 建表脚本(按方言择优执行)
├── menus.json                        # 菜单树(无菜单行时可不生成)
├── go/                               # 后端源码(相对后端项目根,推荐原生布局)
│   ├── api/v1/plugins/sms/*.go               # 接口 DTO(g.Meta 路由标签)
│   └── internal/
│       ├── app/boot/pluginsSms.go            # 服务注册挂载点
│       ├── app/plugins/sms/                  # 业务模块
│       │   ├── controller/  service/  logic/  model/  dao/  router/
│       │   ├── consts/  driver/              # 业务自定义子包
│       │   └── register.go                   # 模块自身 init 挂载(可选)
│       ├── app/plugins/sms/logic/logic.go    # 模块级 Register 聚合
│       └── router/pluginsSms.go              # 路由绑定挂载点
└── vue/                              # 前端源码(内容相对 admin-ui/src)
    ├── api/modules/sms.ts                    # 接口封装
    └── views/plugins/sms/                    # 页面组件
        ├── index.vue  log.vue
        └── locale-zh.ts  locale-en.ts  ...

各文件的作用与生成方式

文件 作用 安装时如何处理
install.json 元数据与能力声明 解析校验;决定方言兼容、版本门槛、业务表清单、依赖清单
install_<dialect>.sql 按方言建表 按当前数据库方言择优执行唯一命中者
install.sql 通用建表(历史回落) 无方言专属脚本时才用;在非 MySQL 上会给出核对提醒
menus.json 菜单/权限树 按 name 命名空间幂等同步(复用 ID、增缺、删过期)
go/** 后端源码 解析后复制落盘
vue/** 前端源码 复制到前端 src 对应位置

⚠️ 不要在包内放 go/**/data/*.sql:安装器会自动扫描并执行该目录下的 SQL,可能与根目录的 install.sql 重复执行。导出时也会主动排除模块根 data/ 目录。


4. install.json 字段详解

结构定义见 model.InstallCfg。校验只强制 name 与 version(validateInstallCfg)。

{
  "name": "sms",
  "title": "短信发送",
  "description": "",
  "version": "3.4.0",
  "auth": "gfast01",
  "minGfastVersion": "3.4.1",
  "installPath": "plugins",
  "tables": ["plugin_sms_config", "plugin_sms_log"],
  "dialects": ["mysql"],
  "dependencies": {
    "go": [
      { "target": "github.com/alibabacloud-go/darabonba-openapi", "version": "v0.2.1", "standard": false }
    ],
    "npm": []
  }
}
字段 必填 说明
name ✅ 插件标识名,^[A-Za-z][A-Za-z0-9_]{1,63}$;同时决定模块目录名与菜单命名空间
version ✅ 插件版本
title ➖ 展示名;缺省取 name
description ➖ 介绍
auth ➖ 作者
minGfastVersion ➖ 最低框架版本;大于当前框架版本(3.4.2)时阻断安装。建议留空或填框架版本
installPath ➖ 模块相对 internal/app 的路径;. = 一级模块;缺省 plugins(骨架/导出会自动填)
tables ➖ 业务表名数组(卸载时按此精确删表)。表名规则 ^[A-Za-z][A-Za-z0-9_]{1,63}$
dialects ➖ 支持方言 mysql / pgsql;缺省视为仅 mysql
dependencies ➖ 前后端依赖声明(§9),形状 {"go":[...], "npm":[...]}

installPath 合法性

归一化规则(normalizeInstallPath):\ 统一为 /;连续/首尾 / 合并;././ 归一为 .;拒绝绝对路径、盘符、..、非法字符;空值取 plugins。


5. 后端模块开发规范

5.1 推荐:原生布局

无论容器还是一级模块,go/ 内都按宿主项目的相对路径组织,安装时原样落盘:

文件 位置 package
业务模块 internal/app/<modulePath>/(含 controller/service/logic/model/dao/router) 各子包
接口 DTO api/v1/<modulePath>/ 模块名
服务注册挂载点 internal/app/boot/<fileStem>.go boot
路由绑定挂载点 internal/router/<fileStem>.go router

<modulePath> = installPath(一级模块时为空)+ name。例如 plugins/sms。

<fileStem> 推导规则(moduleLayout.fileStem):

布局 installPath name fileStem boot 文件
容器 plugins sms pluginsSms internal/app/boot/pluginsSms.go
容器 demo demoPlugin demoDemoPlugin internal/app/boot/demoDemoPlugin.go
一级 . cms cms internal/app/boot/cms.go

一级模块布局额外兼容首字母大写命名(Ipfilter.go);容器布局只认历史命名。

三个必须的文件模板

① 服务注册挂载点 internal/app/boot/<fileStem>.go

package boot

import (
    "github.com/tiger1103/gfast/v3/internal/app/plugins/sms/logic"
    "github.com/tiger1103/gfast/v3/internal/register"
)

// 包初始化时上交本模块服务注册回调(无 init,增删本文件即插拔模块)
var _ = register.Add(logic.Register)

② 路由绑定挂载点 internal/router/<fileStem>.go

package router

import (
    "context"

    "github.com/gogf/gf/v2/net/ghttp"
    smsRouter "github.com/tiger1103/gfast/v3/internal/app/plugins/sms/router"
)

func (router *Router) BindPluginsSmsModuleController(ctx context.Context, group *ghttp.RouterGroup) {
    smsRouter.R.BindController(ctx, group)
}

方法名 = Bind + ucfirst(fileStem) + ModuleController。一级模块即 BindIpfilterModuleController。

③ 模块级 Register 聚合 internal/app/plugins/sms/logic/logic.go

package logic

import (
    pluginSmsConfig "github.com/tiger1103/gfast/v3/internal/app/plugins/sms/logic/pluginSmsConfig"
    pluginSmsLog "github.com/tiger1103/gfast/v3/internal/app/plugins/sms/logic/pluginSmsLog"
)

// Register 收集本模块所有服务注册回调(由 boot 层收集,入口文件统一执行)
func Register() {
    pluginSmsConfig.Register()
    pluginSmsLog.Register()
}

新增一个 logic 子包时,只需在 logic.go 的 Register() 里加一行。

5.2 兼容:容器旧式布局

go/ 顶层出现 go/<installPath>/<name>/... 时,安装器会把 go/<installPath> 重定向到 internal/app/<installPath>,其余顶层条目按原样复制到后端根。

一级模块(installPath=.)不接受该形式。

5.3 依赖注入与路由

  • 服务注册链路:logic 子包 Register() → 模块 logic/logic.go Register() → boot/<fileStem>.go 用 register.Add(...) 上交 → main.go 调 register.DoRegister()。
  • 全项目零 init()(个别历史插件如 sms/register.go 的 init() 属例外,新插件不必模仿)。
  • 路由前缀统一 /api/v1(internal/router/router.go),模块路由自行 group.Group(...) 后绑定。

5.4 业务模块子包职责

internal/app/plugins/sms/
├── controller/   # HTTP 处理,嵌入 systemController.BaseController
├── service/      # 自动生成的服务接口(gf gen service,勿手改)
├── logic/        # 业务实现,New() + Register()
├── model/        # 请求/响应模型(entity/ do/ 为 gf gen dao 产物)
├── dao/          # 数据访问(gf gen dao 产物)
└── router/       # 模块路由绑定

接口 DTO(api/v1/plugins/sms/plugin_sms_config.go)用 g.Meta 声明路由:

type PluginSmsConfigSearchReq struct {
    g.Meta `path:"/list" tags:"插件接口/短信配置" method:"get" summary:"短信配置列表"`
    commonApi.Author                       // 鉴权/上下文用户信息
    model.PluginSmsConfigSearchReq
}

commonApi.Author 提供登录用户上下文;commonApi.EmptyRes 用作无数据返回。

Controller 直接转发到 service:

type pluginSmsConfigController struct{ systemController.BaseController }

var PluginSmsConfig = new(pluginSmsConfigController)

func (c *pluginSmsConfigController) List(ctx context.Context, req *sms.PluginSmsConfigSearchReq) (res *sms.PluginSmsConfigSearchRes, err error) {
    res = new(sms.PluginSmsConfigSearchRes)
    res.PluginSmsConfigSearchRes, err = service.PluginSmsConfig().List(ctx, &req.PluginSmsConfigSearchReq)
    return
}

5.5 模块路由注册模板(router/router.go)

var R = new(Router)

type Router struct{}

func (router *Router) BindController(ctx context.Context, group *ghttp.RouterGroup) {
    group.Group("/plugins/sms", func(group *ghttp.RouterGroup) {
        service.GfToken().Middleware(group)                 // 登录验证
        group.Middleware(service.Middleware().Ctx, service.Middleware().Auth) // 上下文 + 权限
        group.Hook("/*", ghttp.HookAfterOutput, service.OperateLog().OperationLog) // 操作日志
        if err := libRouter.RouterAutoBind(ctx, router, group); err != nil {
            panic(err)
        }
    })
}

5.6 排障:编译 / 依赖

  • 插件引入第三方 Go module 时,必须显式在 install.json.dependencies.go 声明(§9),并在安装后执行「安装依赖」,否则重编译会报缺包。
  • 生成 service/、dao/ 用项目内的 gf CLI:
    • gf gen service -s internal/app/plugins/sms/logic -d internal/app/plugins/sms/service
    • gf gen dao -link "mysql:user:pass@tcp(host:port)/dbname"

6. 前端开发规范

vue/ 内的路径相对 admin-ui/src(前端根来自配置 gen.frontDir,缺省 ../admin-ui)。

内容 放置位置 与菜单的对应
页面组件 vue/views/<viewPath>/index.vue 菜单 component = <viewPath>/index
接口封装 vue/api/modules/<name>.ts(或 api/modules/<name>/、api/<name>.ts) 任选其一,导出/安装都支持三种形态
语言包 与页面同目录,如 locale-zh.ts / locale-en.ts 自行 registerLocaleModule 注册
  • <viewPath>:容器 = <installPath>/<name>,一级 = <name>。菜单 component 由它推导视图目录(安装器会自动从菜单 component 反推需要打包的 views/ 目录)。
  • keep-alive 命名:defineOptions({ name: 'Dyn-<component 用 - 连接>' })。例:组件 plugins/manage/index → Dyn-plugins-manage-index。不匹配会导致缓存失效。
  • 模板单根元素:与 <Transition> + <KeepAlive> 组合时,模板必须有唯一根元素(用 <div> 包裹),否则动画会报错。
  • 接口封装示例(vue/api/modules/sms.ts)用项目统一请求实例,路径以 /api/v1 为前缀。

7. 菜单与权限(menus.json)

7.1 命名空间(最重要)

所有菜单 name 必须落在本插件命名空间内:

api/v1/<modulePath>            # 命名空间前缀
api/v1/plugins/sms             # 容器示例
api/v1/ipfilter                # 一级示例

安装时按前缀做差异化同步(planMenuSync):

  • 命名空间内、本次出现的既有菜单 → 复用其 ID 做 Update(角色授权不失效);
  • 命名空间内、本次未出现的 → 删除,并清理其 Casbin 授权策略;
  • 命名空间外的既有菜单 → 一律不动。
  • 越界的 name 会被当作新菜单插入并给出告警。

这也是为什么不要复用别人插件的 name:否则会互删菜单。

7.2 节点字段(MenuNode)

字段 说明
name 权限标识 / 路由 name,必须带命名空间前缀
title 显示标题
icon Iconify Remix 图标,必须以 ri: 开头(如 ri:puzzle-line)
menuType 0 目录 / 1 菜单 / 2 按钮
path 前端路由地址(菜单/目录用)
component 组件路径;目录用 layout/routerView/parent,页面用 <viewPath>/index
weigh 排序权重
isHide 是否隐藏(隐藏菜单内部跳转需 registerLeafRoutes 注册,否则 404)
isCached 是否 keep-alive 缓存
isIframe / isLink / linkUrl 内嵌 / 外链相关
redirect / isAffix 重定向 / 固定标签
moduleType 所属模块,缺省 sys_admin
modelId 模型 ID
condition / remark 备注类字段
children 子节点数组

7.3 权限按钮与 v-auth

menuType=2 的按钮节点 name(如 api/v1/plugins/sms/pluginSmsConfig/get)就是前端 v-auth 判断用的权限串。页面按钮写法:

<el-button v-auth="'api/v1/plugins/manage/export'">导出</el-button>

7.4 骨架生成的约定菜单

buildScaffoldMenus 会生成:目录(ri:puzzle-line)→ 列表页(component=<viewPath>/index,ri:file-list-2-line)+ 4 个按钮(get/add/edit/delete)。按实际业务增删即可。

导出插件时,menus.json 由数据库现有菜单行生成(见 §10),而不是固定模板。


8. 数据库脚本规范

文件命名与择优顺序(collectSqlFiles):

  1. install_<dialect>.sql(方言专属,命中即只执行它)
  2. install.sql(通用回落,兼容历史包)
  3. go/**/data/*.sql(仅 data 目录内;同时存在方言专属时只取专属)

硬性约定

  • 仅幂等 DDL:CREATE TABLE IF NOT EXISTS ...。
  • 每条语句必须以分号 ; 结尾(安装器按 ; 拆分语句)。多表脚本漏分号会把多条 CREATE TABLE 合并成一条而整体执行失败。
  • 禁止破坏性语句:DROP TABLE/DATABASE/SCHEMA、TRUNCATE 会被安装器跳过并记警告(防止清空接收方数据)。
  • 不要写 INSERT 等数据语句:避免把本机配置、密钥随包分发;配置应由使用方在页面填写。
  • 安装时以单连接事务顺序执行,支持 LAST_INSERT_ID() 等会话变量;单条失败记为警告、不中断安装。
  • tables(install.json)应如实列出业务表:卸载时按它精确删表。

方言

  • 声明 dialects 只列包内真实提供的方言。
  • 声明了某方言却没有对应脚本时:该数据库上安装会直接报错阻断(tables 非空时)。
  • 缺省(不声明)按历史约定视为仅 mysql;在 PostgreSQL 上会被明确阻断,而不是静默缺表。

9. 依赖声明(install.json.dependencies)

"dependencies": {
  "go":  [{ "target": "github.com/x/y", "version": "v1.2.3", "standard": false }],
  "npm": [{ "target": "echarts", "version": "^5.6.0", "standard": true }]
}
  • version 留空 = 安装最新版。
  • standard 标记框架标准依赖(前端弹窗据此区分展示)。
  • 安装时安装器把声明与接收方现状比对,得到三种判定:install(缺失)/upgrade(版本偏低)/skip(已满足或不可判定)。绝不降级。
  • 是否安装后自动进入依赖安装,由宿主配置 plugin.deps.autoInstall 决定;也可在列表页用「安装依赖」手动触发。
  • 依赖安装过程有进度任务(depTaskStatus 轮询),失败/取消可重试。
  • 导出/生成骨架时,不填依赖会自动扫描源码:
    • Go:解析 go.mod(module/require/replace)与源码 import;
    • npm:匹配 from 'x' / import('x') / require('x')(宁多勿漏,可在弹窗删除误报)。

10. 打包导出

后台入口:「插件管理 → 已安装插件 Tab → 打包导出」或列表行「导出」。

两种来源:

场景 行为
已安装插件 只传 name,其余元数据取 sys_plugins 安装记录
未登记插件(有源码、无安装记录) 弹窗补齐 title/version/auth/installPath/tables/dialects/依赖;其余自动探测

后端 Export(GET /api/v1/plugins/manage/export)生成内容:

  1. install.json:minGfastVersion 统一写当前框架版本;tables = 入参 ∪ 记录 ∪ 前缀探测;dialects = 可提供的方言集合。
  2. go/:模块目录 + api 目录 + boot/router 文件;排除模块根 data/ 目录。
  3. vue/:由菜单 component 反推的 views/** 目录 + 实际存在的 api 封装(三种形态)。无菜单时回退约定路径 views/<viewPath>。
  4. install_<dialect>.sql:优先复用安装时缓存的原始脚本(保留插件原有其他方言版本);当前库方言无缓存时,按实际表结构重建幂等 DDL。多方言顺序固定为 mysql、pgsql。
  5. menus.json:从数据库读取当前菜单行构建树(按安装记录的菜单 ID 优先,缺省按 name 命名空间前缀匹配);查不到菜单行则完全不生成该文件。

自动探测说明:

  • installPath:入参 > 记录 > 源码目录探测 > 缺省 plugins。
  • tables:plugin_<name小写> 与 plugin_<name小写>_* 前缀匹配(按方言查 information_schema / pg_tables)。

导出/导入可往返:在 MySQL 主机导出的包,只要安装时缓存过 PG 脚本,就能把 PG 版一并带回(缓存位置 <后端根>/data/pluginSql/<name>/)。


11. 安装

11.1 安装流水线(installFromZip)

  1. 解压(含 Zip Slip 路径穿越校验)到 data/installPlugins。
  2. 定位包目录(按响应头/文件名期望,否则扫描唯一顶层目录)。
  3. 解析 + 校验 install.json:name/version 必填,installPath 合法性,一级模块保留名校验。
  4. 方言兼容检查(在任何写操作之前):包声明的 dialects 不含当前库方言 → 零副作用阻断。
  5. 依赖声明清洗:非法条目丢弃并记警告,不中断。
  6. 包目录名统一为 install.json.name。
  7. 收集 SQL:声明了 tables 却无任何脚本 → 报错阻断(避免“装完却没有表”)。
  8. 框架版本门槛:minGfastVersion > 当前版本 → 阻断。
  9. 覆盖闸门:force=false 且(已有安装记录 / 目标模块目录已存在)→ 返回 needConfirm,未写入任何文件;前端确认后带 force=true 重试。
  10. 复制文件:复制前建快照,失败自动回滚(DDL 无事务,不回滚数据库)。
  11. 执行 SQL(§8)。
  12. 同步菜单(§7.1)。
  13. 写 sys_plugins 记录(tables/dialects/menus/dependencies 落库)→ 此后不再回滚文件。
  14. 缓存包内方言 SQL,清菜单缓存。
  15. 依赖判定:读接收方 go.mod / package.json,返回可执行依赖项。

安装 / 卸载互斥(进程内 opMutex),同一时刻只允许一个文件操作。

11.2 安装后必做

  • 重新编译并重启后端(Go 是编译型服务):go run main.go。
  • 前端:开发模式自动热更;生产需重新构建。
  • 给角色授权新菜单/按钮,否则非超管看不到入口。
  • 若菜单/配置没刷新:清缓存 DELETE /api/v1/system/cache/remove(Redis 菜单缓存可能跨重启残留)。
  • Casbin 规则变更需重启后端才清除内存 enforcer 缓存。
  • 缺失依赖:列表行「安装依赖」或安装后弹窗确认执行。

11.3 安装失败对照表

报错关键词 原因 处理
插件名不合法 name 不符 ^[A-Za-z][A-Za-z0-9_]{1,63}$ 改名后重打包
已阻断安装(方言不匹配) dialects 不含当前库方言 换匹配版本,或补对应 install_<方言>.sql 并更新 dialects
您的gfast版本过低 minGfastVersion 高于 3.4.2 下调 minGfastVersion 或升级框架
包内未提供 install_xxx.sql 声明了 tables 但无脚本 补脚本
一级模块插件包必须使用原生布局 installPath=. 却用了 go/<name>/ 改为 go/internal/app/<name>/
以下 SQL 语句执行失败(警告) 语句语法/多表漏分号/方言不符 修脚本,确保每条 DDL 以 ; 结尾
已跳过破坏性语句(警告) 包内含 DROP/TRUNCATE 移除这些语句
needConfirm 覆盖确认 前端确认后自动带 force 重试

12. 卸载

「插件管理 → 已安装插件 → 卸载」,可选「同时删除业务数据表」。

Uninstall 流程:

  1. 读取 sys_plugins 记录,推导布局与(从菜单推导的)前端视图目录。
  2. 删除本插件命名空间菜单,并清理其 Casbin 授权策略(避免策略悬空)。
  3. 后端源码、前端视图与 api 封装备份后移除,备份位置:
    <后端根>/data/uninstallBackup/<name>-<时间戳>/
  4. 勾选删表时:对记录在案的 tables 执行 DROP TABLE IF EXISTS <方言引用>。
  5. 删除 sys_plugins 安装记录;清菜单缓存。

卸载后同样需要重新编译重启后端,并重新授权已删除菜单涉及的角色项(新装时重新授权)。


13. 注意事项与常见坑

  • SQL 分号:install_<dialect>.sql 每条语句必须以 ; 结尾。尤其导出重建的脚本曾因漏分号导致多表合并、建表失败——现已修复,但历史包需重新导出。
  • 菜单命名空间:name 必须带 api/v1/<modulePath> 前缀,避免误删他人菜单。
  • 保留目录:一级模块 name 不得为 system/common/boot/plugins。
  • 不要放 `go//data/.sql`*:会被自动执行,易与根目录脚本冲突。
  • 不写数据/凭证:包内只放幂等 DDL 与源码,配置由使用方填写。
  • 图标前缀:菜单图标必须 ri:*。
  • keep-alive 名:Dyn-<组件路径连字符>,且模板单根元素。
  • 前端视图目录推导:来自菜单 component 目录(跳过 layout/);菜单 component 写错会导致前端源码打包/卸载路径不对。
  • 缓存:菜单/配置改动不生效时先清缓存;Casbin 改动需重启。
  • 并发:安装/卸载串行;不要并发触发。
  • 临时目录:打包/生成骨架的临时目录默认 <后端根>/data/tmp,受限环境可用 plugin.stagingDir 覆盖。
  • 未登记插件导出依赖源码目录存在;元数据缺失靠弹窗补齐。

附录 A:插件管理 API

前缀 /api/v1/plugins/manage:

端点 方法 说明
/localList GET 已安装插件列表(含当前方言、兼容性、待装依赖数)
/storeList GET 在线商城插件列表
/install POST 在线安装
/installLocal POST 本地导入安装(zip 上传)
/uninstall POST 卸载(可选删表)
/export GET 导出插件包(支持未登记插件)
/scaffold GET 生成插件包骨架
/storeCaptcha GET 商城验证码
/storeLogin POST 商城登录
/scanDeps GET 扫描插件依赖
/installDeps POST 安装插件依赖
/depTaskStatus GET 依赖任务状态
/cancelDepTask POST 取消依赖任务

附录 B:相关配置(manifest/config/config.yaml)

gen:
  frontDir: "../admin-ui"     # 前端路径(与后端项目平级)

plugin:
  serverUrl: "https://plugins.g-fast.cn"  # 插件商城地址
  # backendDir: ""        # 后端项目根覆盖,缺省取主程序源码目录/进程工作目录
  # stagingDir: ""        # 打包临时目录,缺省 <后端根>/data/tmp
  deps:
    autoInstall: true     # 安装插件后是否自动进入依赖安装
    goCommand: "go"       # 后端依赖安装命令
    npmCommand: "pnpm"    # 前端依赖安装命令
    npmDir: ""            # 前端项目根覆盖,缺省 gen.frontDir 的父目录
    timeoutSeconds: 600   # 单条命令超时(秒)
    runGoModTidy: false   # go get 后是否追加 go mod tidy

附录 C:内置文件落盘位置一览(相对后端根)

用途 路径
安装临时目录 data/installPlugins/
打包/骨架临时目录 data/tmp/(可配 plugin.stagingDir)
包内方言 SQL 缓存 data/pluginSql/<name>/
卸载备份 data/uninstallBackup/<name>-<时间戳>/

附录 D:可参考的真实样例

样例 布局 位置
短信插件 容器 plugins/sms internal/app/plugins/sms/、api/v1/plugins/sms/
IP 过滤模块 一级 . → internal/app/ipfilter internal/app/ipfilter/
插件管理自身 容器 plugins/manage internal/app/plugins/manage/

一页速记

  1. 生成骨架 → 放 go/ 与 vue/ → 补 SQL(幂等 + 分号)→ 调 menus.json(命名空间)。
  2. zip 根目录必须是 <name>/。
  3. 本地导入安装 → 确认覆盖 → 按需装依赖 → 重编译重启后端 → 给角色授权。
  4. 导出用于分发;未登记插件靠弹窗补元数据,其余自动探测。
作者:管理员  创建时间:2026-10-15 15:04
最后编辑:管理员  更新时间:2026-10-19 11:30