插件清单
在 AuraBoot 中,插件是交付的最小单元。无论你交付一个小型字典表、一个端到端的 CRM,还是一套完整的行业解决方案,包的形态都是一致的:一个目录,内含 plugin.json 清单文件以及一组待平台导入的资源文件。
清单不仅仅是名字和版本号。它是一份类型化声明,描述了插件贡献的每一项资源 —— 模型、字段、命令、权限、菜单、页面、命名查询等 —— 同时声明了插件向他人提供的能力,以及对他人的依赖。正是这份声明,让插件具备了升级安全、可组合、可被工具和 Agent 分析的属性。
为什么需要清单
没有清单,插件不过是一堆代码和 JSON。两个问题会立刻浮现:
- 升级不再安全。当新版本到来,平台无法分清哪些资源归插件所有,哪些是客户或别的插件加进来的,差异分析变成靠猜。
- 组合变得随意。一个想扩展 CRM 的插件,没有任何机器可读的方式声明"我需要
crm_account模型"或"我提供mfg:scrap命令",跨插件契约只能依赖人类描述。
插件清单同时解决这两个问题。它充当贡献与能力的类型化注册表,平台在安装时读取它,并在升级、依赖解析、校验、打包、授权门控时反复使用它。
清单同时也是下游工具依赖的真值源:
- 插件加载器使用
pluginType、dependencies、resourceDirs决定加载顺序和导入哪些文件。 - DSL 设计器使用
provides和requires提示引用、在目标模型缺失时给出告警。 - 权限系统读取
requiredPermissions为角色预填权限。 - 许可层根据
licenseMode、plans、features、planFeatures控制行为。 - AI Agent 通过清单推断如何建议配置,或者直接生成新插件。
因此,完备的清单不是打包的细节,而是让其他每一层都能把插件当作一等对象处理的契约。
三种插件类型
AuraBoot 在 pluginType 字段中识别三种插件类型。在选型阶段就明确类型,可以让打包、部署、评审都更简单。
config 插件只包含 JSON 资源文件。它通过声明式 DSL 定义模型、字段、命令、页面、权限、菜单、字典、命名查询。没有 Java 代码,没有编译产物。AuraBoot 中绝大多数插件 —— 包括大部分业务模块和模板 —— 都是 config 类型。只要你的行为可以通过命令、校验规则、副作用、BPM 流程声明式表达,就应选 config。
hybrid 插件在声明式 JSON 之上叠加由 PF4J 运行时加载的 Java 代码。当你需要自定义命令处理器调用外部系统、需要数据提供者对接非关系型数据源、或者需要一个无法通过标准执行模式表达的专用动作时,就选这种类型。Hybrid 插件依然带有 config/ 目录;Java 代码以 JAR 形式提供,并在清单的 backend 块中通过 entryClass 指向入口类。只有当 JSON 表达不了相关操作时,才选 hybrid。
solution 插件是行业垂直解决方案包。Solution 通常不引入自己的模型;取而代之的是声明对若干 config 或 hybrid 插件的依赖,补充跨插件的粘合资源,并为某个行业(制造、资产管理、项目交付等)交付预先调好的配置。当你要把一组精挑细选的插件打包成可部署的行业栈,而不是构建单个功能时,才选 solution。
如果一个插件从 config 起步,后来需要自定义 Java 行为,可以通过添加 backend 块和 entryClass 提升为 hybrid。从 config 到 solution 的转换很少见;solution 通常一开始就按 solution 设计。
清单顶层字段
当前官方 hybrid 插件示例使用 backend.jarPath 和 backend.entryClass。plugin-manifest.schema.json 中仍保留的旧字段名是待统一的 schema 债务;新文档和新插件应按实际官方插件写法 author。最重要的字段如下:
| 字段 | 类型 | 必填 | 用途 |
|---|---|---|---|
pluginId | string | 是 | 反向域名形式的全局唯一标识(例:com.auraboot.asset-management)。 |
namespace | string | 是 | 用于资源隔离的短前缀,在模型 code、命令 code、权限 code 中复用。小写,允许下划线。 |
version | string | 是 | 语义化版本号,格式 MAJOR.MINOR.PATCH,可带预发布或构建元数据。 |
pluginType | enum | 否 | config、hybrid 或 solution,默认 config。 |
displayName | string | 否 | 默认显示名;配合 displayName:zh-CN 与 displayName:en 做本地化。 |
description | string | 否 | 面向人的简介。 |
author | string | 否 | 作者或组织名。 |
homepage | string | 否 | 文档或产品主页 URL。 |
minPlatformVersion | string | 否 | 所要求的 AuraBoot 平台最低版本。 |
dslVersion | integer | 否 | 插件页面定义所使用的 DSL schema 版本,默认 1。 |
dependencies | array | 否 | 必须先于本插件安装的其他插件列表,见下文。 |
provides | array | 否 | 本插件向他人提供的能力。 |
requires | array | 否 | 本插件依赖他人提供的能力。 |
resourceDirs | object | 否 | 各类资源(模型、字段、命令等)的文件路径映射。 |
backend.jarPath | string | hybrid 时是 | 插件包内 JAR 路径。 |
backend.entryClass | string | hybrid 时是 | Java 类全限定名,hybrid 插件入口类。 |
backend | object | 否 | hybrid 插件的 JAR 路径与入口类。 |
client | object | 否 | 前端插件配置,包含暴露的组件。 |
importOptions | object | 否 | 导入行为开关(冲突策略、校验、自动发布)。 |
requiredPermissions | array | 否 | 插件安装时需要授予的权限 code 列表。 |
providedModels | array | 否 | 插件将注册的模型 code 列表。 |
providedCommands | array | 否 | 插件将注册的命令 code 列表。 |
licenseMode | enum | 否 | free、platform、vendor,驱动授权行为。 |
plans | array | 否 | 本插件可用的订阅方案。 |
features | array | 否 | 可被方案门控的能力开关。 |
planFeatures | object | 否 | 方案 code 到启用能力 key 的映射。 |
清单还允许在顶层直接内联各类资源数组(models、fields、commands、permissions、roles、menus、pages、processes、namedQueries、dicts、modelFieldBindings、bindingRules)。大多数插件把资源放在专用文件中,由 resourceDirs 指向;内联数组只适合极小的插件或测试夹具。
资源目录
resourceDirs 对象把"逻辑资源类型"映射到插件包内的文件或目录路径。加载器会遍历每个声明项并导入其中的资源。schema 识别以下类别:
models— 模型定义,描述业务实体、分类、是否抽象以及父模型继承。fields— 可复用的元字段定义:数据类型、约束、UI 提示、查询与校验 schema。modelFieldBindings— 将字段绑定到模型,并附带上下文级覆盖(必填、可见、可编辑、顺序)。commands— 命令定义,覆盖 CRUD、状态流转、批量操作和自定义动作,以及处理器、副作用、校验规则与后置动作。processes— BPM 流程定义,可以是 BPMN 文件,也可以是工作流引擎消费的设计器 JSON。permissions— 权限 code,含分类、资源类型、动作、模块和数据范围。roles— 预置 RBAC 角色,聚合一组权限 code。menus— 导航树节点:路径、图标、父级 code、权限门控、页面绑定。pages— DSL 驱动的页面定义:list、form、detail、dashboard 或 custom 类型,带有完整 schema。reports— 报表子系统所用的报表定义。namedQueries— 已存储的参数化查询,声明字段、默认排序、允许的算子。dicts— 字典(枚举集合)定义,由dict类型字段引用。data— 与元数据一并导入的种子数据。
每一项既可以是单个 JSON 文件(config/commands.json),也可以是 JSON 目录(config/commands/),凡是支持目录的类别均会递归遍历。
能力声明:dependencies、provides、requires
AuraBoot 区分两种表达跨插件关系的方式:
dependencies 是插件对插件的链接。它表达"插件 B 必须先于我安装"。可以是纯插件 ID 列表(匹配任意版本),也可以是带语义化版本范围的对象列表:
{
"dependencies": [
"com.auraboot.org-management",
{ "pluginId": "com.auraboot.crm", "version": ">=1.2.0" }
]
}provides 与 requires 是能力级声明,表达"我提供这个模型 / 命令 / 查询 / 自动化 / api","我需要别人提供这一项"。能力有类型(model、command、query、automation、api)和 code:
{
"provides": [
{ "type": "model", "code": "asset_unit" },
{ "type": "command", "code": "asset:retire" }
],
"requires": [
{ "type": "model", "code": "org_employee", "optional": true }
]
}解析器在安装与升级时使用这些信息:按 dependencies 做拓扑排序,校验每项必需能力都有某个已安装插件提供,任何非可选要求缺失即拒绝加载。可选要求缺失时,仅停用相关功能,不会让安装失败。
能力声明对工具也有帮助:DSL 设计器只在已安装的提供方之间建议引用,AI Agent 也能推断"哪个插件缺口阻断了端到端工作流"。
插件生命周期
无论首次安装还是升级,插件都会走过相同的阶段:
上传安装包
-> 校验清单
-> 解析依赖与能力
-> 加载 Java 入口(仅 hybrid)
-> 导入字典
-> 导入字段与模型
-> 导入模型-字段绑定
-> 导入命令与 binding rules
-> 导入权限与角色
-> 导入命名查询
-> 导入页面与菜单
-> 导入流程(autoDeploy 时部署)
-> 注册前端组件(若 client.enabled)
-> 激活升级时,平台会对比新清单与已安装状态。归插件所有的资源按 importOptions.conflictStrategy 协调处理:
error— 如果本地版本被改过,直接中止。skip— 保留本地版本并记录冲突。overwrite— 用新清单的版本覆盖本地版本。
新版本中被移除的资源不会被无条件删除;平台会标记为孤立项,由管理员决定是否退役。这样可以避免在引用仍存在时丢失数据。
走查:一个最小 config 插件
设想一个微型 Notes 插件,只含一个模型、一个命令、一个页面、一个菜单。
目录结构:
com.acme.notes/
plugin.json
config/
models.json
commands.json
permissions.json
pages/
notes-list.json
menus.jsonplugin.json:
{
"pluginId": "com.acme.notes",
"namespace": "note",
"version": "1.0.0",
"pluginType": "config",
"displayName": "Notes",
"displayName:en": "Notes",
"displayName:zh-CN": "便签",
"description": "Lightweight personal notes",
"author": "Acme",
"minPlatformVersion": "1.0.0",
"provides": [
{ "type": "model", "code": "note_item" },
{ "type": "command", "code": "note:create" }
],
"resourceDirs": {
"models": "config/models.json",
"commands": "config/commands.json",
"permissions": "config/permissions.json",
"menus": "config/menus.json",
"pages": "config/pages"
},
"importOptions": {
"conflictStrategy": "overwrite",
"validateReferences": true,
"autoPublishPages": true
}
}config/models.json 声明一个模型:
[
{
"code": "note_item",
"displayName:en": "Note",
"displayName:zh-CN": "便签",
"modelType": "entity",
"modelCategory": "document"
}
]config/commands.json 注册创建命令:
[
{
"code": "note:create",
"modelCode": "note_item",
"type": "create",
"displayName:en": "Create Note",
"displayName:zh-CN": "新建便签",
"permissions": ["note.note_item.create"]
}
]config/permissions.json 声明权限:
[
{
"code": "note.note_item.create",
"name:en": "Create Note",
"name:zh-CN": "新建便签",
"resourceType": "MODEL",
"resourceCode": "note_item",
"action": "CREATE",
"module": "note"
}
]config/pages/notes-list.json 承载 list 页的 DSL schema(此处略),config/menus.json 把菜单挂上这页:
[
{
"code": "note.menu.list",
"name:en": "Notes",
"name:zh-CN": "便签",
"path": "/notes",
"type": 1,
"pageKey": "note.notes-list",
"permissionCode": "note.note_item.create",
"orderNo": 100
}
]导入该包后,平台已经具备一个可从侧边栏抵达的可用 list 页,背后是一个类型化模型与一个创建命令。没有任何 Java 代码,也没有 React 代码。其它 CRUD 命令(update、delete、view)都可以通过在 commands.json 中追加条目并绑定到页面来添加。
Hybrid 插件扩展
有时 JSON 不够用。一个要调用外部计价服务的命令、一个从传感器网关流式取数的数据提供者、一个要产出二进制文件的动作,都没法靠声明式表达。Hybrid 插件用 Java JAR 与 JSON 并行的方式解决这个问题。
Hybrid 插件的清单增加 backend 块:
{
"pluginType": "hybrid",
"backend": {
"jarPath": "backend/notes-extras-1.0.0.jar",
"entryClass": "com.acme.notes.NotesExtrasPlugin"
}
}平台通过 PF4J 加载 JAR。entryClass 类声明插件的生命周期钩子(start、stop)。JAR 内部可以注册 PF4J extensions,实现平台定义的 extension point —— 最常见的是 CommandHandler、DataProvider,或者自定义动作接口。
JSON 命令与其 Java handler 之间的桥梁是 binding rule。commands.json 中的命令带有 executionConfig.handler 字段,指向 handler 的 bean 或类名;bindingRules.json 文件(与 commands 一起在 resourceDirs 中注册)声明额外的绑定元信息,例如字段映射、事件 handler、触发条件。
一条关键打包纪律:binding rules 必须放在自己的文件里。commands.json 内联的 bindingRules 不会被导入。独立的 bindingRules.json 文件是唯一支持的位置,且必须经由 resourceDirs 注册。
Hybrid 插件启动时,平台会:
- 在隔离 classloader 中加载 JAR。
- 调用入口类的
start()钩子。 - 发现
@Extension注解类并向宿主注册。 - 通过 binding rules 把 JSON 命令的 handler 与 Java 实现连起来。
- 通过 SPI 缝(background accessor、credential resolver、tenant resolver 等)把插件的服务暴露给宿主代码。
停用与卸载则反向执行:拆除 classloader、反注册 extensions、清掉宿主中以插件资源为键的缓存。
解决方案包
Solution 插件把其他插件打成可部署的行业栈。清单将 pluginType 设为 solution,并用 dependencies 列出组成它的插件:
{
"pluginId": "com.auraboot.solution.pcba",
"namespace": "pcba",
"version": "1.0.0",
"pluginType": "solution",
"displayName": "PCBA 制造解决方案",
"dependencies": [
{ "pluginId": "com.auraboot.bom-standardization", "version": "^1.0.0" },
{ "pluginId": "com.auraboot.manufacturing-execution", "version": "^1.0.0" },
{ "pluginId": "com.auraboot.quality-management", "version": "^1.0.0" }
],
"resourceDirs": {
"menus": "config/menus.json",
"permissions": "config/permissions.json",
"pages": "config/pages",
"namedQueries": "config/named-queries.json"
}
}Solution 依然带有自己的 resourceDirs,但这些资源通常是跨插件的粘合层:一个精心安排的着陆 dashboard、把各模块串起来的顶层菜单、跨多个模型的命名查询,以及为行业典型角色调好的权限与角色。
Solution 与普通插件有三点差异:
- 部署。安装 solution 会传递安装它所有依赖,卸载亦对称。
- 升级工具。Solution 升级会对所有依赖统一编排,平台拒绝半成品状态。
- 授权姿态。Solution 通常是售卖授权的单位,
plans、features、planFeatures一般声明在 solution 这一层,而非每个组成插件上。
如果你只是要交付单个功能,做 config 或 hybrid 插件即可。只有当你确实在打包一组精选的行业栈时,才动用 solution。
校验与门禁
每个插件导入都必须通过两道门。
静态清单校验是预检。平台用 JSON Schema 解析 plugin.json、遍历 resourceDirs 确认每个引用的文件存在、检查内联资源是否合规,并暴露常见的编写错误:
S-PAGE-LABEL—— 某按钮或列的 label 不规范或缺失,可能在 UI 上泄漏 raw code。S-PAGE-FORM-REQUIRED—— 某个必填字段没有在绑定和 form schema 中双重声明。S-PAGE-TABLE-DICT—— list 列引用了一个未声明的字典 code。S-PAGE-BUTTONS为空 —— 工具栏 block 中没有按钮。S-EXT-HANDLER—— binding rule 指向了插件并未注册的 Java handler。
静态检查通过是必要但不充分的。
服务端导入是权威门禁。平台的插件导入 API 解析包、解决依赖与能力、在单个事务内按拓扑顺序导入每项资源、运行跨资源校验(引用、权限完整性、页面-模型一致性、命令-权限覆盖)。只有当导入 API 返回 success: true 时,插件才算真正安装成功。
日常开发中,尽早、频繁跑静态检查,把导入 API 当作最终门禁。静态检查能拦下大部分书写错误;导入 API 能拦下只有当所有资源一起加载时才暴露的结构性错误。
企业版扩展
AuraBoot 企业版发行在清单之上为"把插件当作产品交付"的组织提供额外能力:带签名包与多版本通道的私有 Marketplace、按租户对 plans 与 features 做门控的 Entitlement 服务、带分批灰度和一键回滚的解决方案升级工具,以及多租户部署中谁可发布、谁可安装、谁可更新插件的策略管控。插件清单本身保持不变,这些行为在其之上叠加。