Skip to content

Plugin Extension Points

worable edited this page Oct 12, 2026 · 1 revision

扩展点

扩展点是内核和插件之间的能力注入机制。插件通过 ctx.registerExtension() 向扩展点注册实现,内核或其他插件通过 ctx.getExtensions() 读取。

内核扩展点

下表的「消费方」指谁调用 ctx.getExtensions(pointId):内核自己消费的即 KERNEL_EXTENSION_POINTS,其余由 store 插件(或预留)消费。

扩展点 ID 用途 接口类型 消费方
auth.provider 认证提供者 AuthProvider 内核
payment.provider 支付提供者 PaymentProvider 内核
payment.settlement 支付结算 PaymentSettlementHandler 内核
commerce.operations 商业域操作(商品/订单/交付物) CommerceOperations 内核
seo.provider 站点 SEO 输送(sitemap/feed 等) SeoProvider 内核
web.nav 导航项 NavItem 内核
ui.admin.dashboard 管理面板 Widget AdminDashboardWidgetMeta 内核
fulfillment.provider 履约/交付物提供方 FulfillmentProvider store
store.productType 商品类型插件(类型定义+本地履约) ProductTypeProvider store
upstream.product.source 上游商品数据源(商品表单关联) UpstreamProductSource store
notification.channel 通知外部渠道(预留,无消费方) 插件实现,监听 notification.created —
routes 预留(路由已改为声明式 routes) PluginRoute —
event.listener 预留(监听已改为声明式 eventListeners) PluginEventListener —

注册与读取

注册扩展点实现

onActivate(ctx) {
  // 注册导航项
  ctx.registerExtension('web.nav', {
    surface: 'admin',
    label: '商品管理',
    href: '/admin/my-plugin',
  });

  // 注册 Dashboard Widget
  ctx.registerExtension('ui.admin.dashboard', {
    id: 'my-widget',
    title: '销售统计',
    description: '销售数据概览',
  });
}

registerExtension 返回一个取消注册函数。通常不需要手动调用——插件停用时会自动清理。

读取其他插件的扩展

onActivate(ctx) {
  const providers = ctx.getExtensions<AuthProvider>('auth.provider');
  for (const provider of providers) {
    ctx.logger.info(`认证提供者: ${provider.id} (${provider.name})`);
  }
}

AuthProvider

第三方认证提供者(如 WowID):

interface AuthProvider {
  readonly id: string;
  readonly name: string;
  authorizeUrl?(
    state: string,
    options?: { codeChallenge?: string; nonce?: string },
  ): string | undefined;
  authenticate(credential: unknown): Promise<AuthIdentity | null>;
}

interface AuthIdentity {
  externalId: string;
  email?: string;
  displayName?: string;
}

内核编排 OAuth 舞蹈:authorizeUrl 构建授权 URL,authenticate 交换凭证并返回身份。内核负责创建/匹配本地用户和签发 session。

PaymentProvider

外部支付提供者(如易支付):

interface PaymentProvider {
  readonly id: string;
  readonly name: string;
  readonly mode: 'gateway' | 'manual';
  readonly callbackPath: string; // 如 '/callbacks/epay'
  getMethods(): PaymentMethod[];
  createPayment(
    request: ExternalPaymentRequest,
    config?: unknown,
  ): Promise<ExternalPaymentInitiationResult>;
  closePayment?(request: { merchantOrderNo: string; externalId?: string | null }): Promise<boolean>;
}

createPayment 返回 ExternalPaymentInitiationResult:网关型返回 { externalId, paymentUrl, expiresAt? },手工型返回 { mode: 'manual', instructions }(银行转账/二维码等买家指引)。callbackPath 是提供者插件自己注册的回调路由路径,用于接收支付网关的异步通知。

PaymentSettlementHandler

支付结算处理器,由业务插件(如 store 的订单域)注册,内核在结算时同步调度:

interface PaymentSettlementHandler {
  settle(
    payment: ResolvedPaymentSettlement,
  ): Promise<{ applied: boolean; purpose: 'ORDER' | 'TOP_UP' }>;
  release?(payment: ResolvedPaymentSettlement): Promise<{ applied: boolean }>;
}
  • 调度方是内核 PaymentService(ctx.payments.settle),不再是支付提供者插件。
  • 网关插件(epay/zjmf)回调验签后调 ctx.payments.settle,不直接调用本处理器。
  • TOP_UP 目的由内核直接入账钱包,不经过处理器;ORDER 目的按注册顺序调度, 取第一个成功者。
  • 处理器必须幂等——同一笔结算重复调用不应产生副作用。
  • 内核还提供 ctx.payments 的 create/release/cancel/sweepExpired 与 ctx.wallet 的 credit/debit/adjust/listLedger 等平台能力,供任意插件使用。
  • 支付与钱包属于内核服务,不由某个插件实现:网关插件只负责发起支付与回调验签, 结算由内核 PaymentService 统一编排。

NavItem

导航项注入到平台导航:

interface NavItem {
  surface: 'public' | 'account' | 'admin';
  label: string;
  href: string;
}

GET /nav/:surface 端点会聚合内核默认导航和插件注入的导航项。

AdminDashboardWidgetMeta

管理面板 Dashboard Widget:

interface AdminDashboardWidgetMeta {
  id: string;
  title: string;
  description?: string;
}

GET /admin/ui/widgets 端点返回所有激活插件贡献的 Widget,包含 pluginId。Widget 的渲染组件需在插件 frontend 包的 ui.adminWidgets 中以 Widget id 为键声明。

provides / consumes 声明

插件可以在 manifest 中声明 provides 和 consumes,用于激活时的依赖校验:

{
  "provides": ["my-plugin.widget"],
  "consumes": [{ "pluginId": "store", "extensionPoint": "payment.provider", "optional": true }]
}
  • provides:本插件向其他插件提供的扩展点 ID
  • consumes:本插件需要消费的扩展点。optional: true 表示即使目标未提供也不阻止激活

详见 插件依赖。

Clone this wiki locally