mcpField
C
commerce-app-eventing

commerce-app-eventing

github.com/adobe开发316270 次安装今天更新
代码仓库

关于 commerce-app-eventing

commerce-app-eventing 用于在已初始化的 Adobe Commerce 应用中配置 Commerce 原生或外部事件源,并将事件关联到运行时操作。它涵盖配置校验、处理器注册、安装操作生成及构建验证。

主要功能

  1. 01配置 Commerce 与外部事件
  2. 02将事件关联到运行时操作
  3. 03提取 Commerce 事件字段
  4. 04注册事件处理器
  5. 05生成安装操作并验证构建

适用场景

  1. 01响应 Commerce 原生事件
  2. 02处理 ERP 或 CRM 等外部事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/adobe/skills --skill commerce-app-eventing

安装后在 Claude Code 中输入/commerce-app-eventing确认已加载。

附带文件assets/eventing-config.tsevals/evals.json

详细介绍

配置 Commerce 应用事件

在现有的 app.commerce.config.ts 中添加或修改事件源——Commerce 原生事件或外部事件。 事件处理以外的可扩展领域(Webhook、业务配置)需通过各自的技能单独添加。

前提条件

  • 确认应用已完成脚手架搭建并初始化,而不只是配置文件存在。必须同时满足以下两项:
    • 项目根目录中存在 app.commerce.config.ts,并且
    • 项目已初始化——即生成了 src/commerce-extensibility-1/ 目录,并安装了 node_modules(包含 @adobe/aio-commerce-lib-app 依赖)。
  • 如果缺少 app.commerce.config.ts,请停止并先调用 commerce-app-init(它会写入配置,然后运行初始化)。
  • 如果配置文件存在但项目尚未初始化(缺少 src/commerce-extensibility-1/ 或 node_modules),请在继续之前运行 npx @adobe/aio-commerce-lib-app init。初始化具有幂等性——它会找到现有配置、跳过交互式提示、安装依赖并生成项目文件。
  • 只有当项目已配置 TypeScript 构建环境(webpack-config.cjs + 根目录下的 tsconfig.json)时,才能用 TypeScript 编写 Actions;init 会为 TypeScript Commerce 配置搭建该环境——详见 commerce-app-init。否则,请用 JavaScript 编写 Actions。
  • 确保在 Developer Console 工作区中已订阅 CloudIntegrationSDK(I/O Events)和 commerceeventing(Adobe Commerce 的 Adobe I/O Events):
    1. 列出当前已订阅的服务:

      aio console workspace api list --projectName <project> --workspaceName <workspace> --json
      
    2. 如果缺少任一服务,请使用合并后的完整服务代码集合(现有服务代码 + 缺少的服务代码)重新订阅。aio console workspace api add 会替换订阅列表——如果省略当前已订阅的服务,该服务就会被移除。

      aio console workspace api add \
        --projectName <project> \
        --workspaceName <workspace> \
        --service-code <existing-codes>,CloudIntegrationSDK,commerceeventing \
        --json
      

      如果命令因 commerceeventing 出现“product profile required”错误,请向用户询问配置文件名称,然后添加 --license-config commerceeventing=<profile> 重试。

第 1 步 — 了解意图

询问用户要配置 Commerce 事件、外部事件,还是两者都配置:

  • Commerce 事件(eventing.commerce):Commerce 原生事件。名称格式为 plugin.<segments> 或 observer.<segments>。
  • 外部事件(eventing.external):来自第三方系统(例如 ERP、CRM)的事件。名称格式不限([\w\-_.]+)。

对于每个事件源,收集以下信息:

  • 提供方标签、描述和可选键
  • 对于每个事件:名称、标签、描述,以及应由哪些运行时 Action 处理(格式:<package>/<action>)
  • 仅针对 Commerce 事件:从事件负载中提取的字段(空数组会捕获完整负载),以及任何可选的筛选规则
  • 可选:该事件适用的 Commerce 环境(env)

步骤 2 — 推导配置值

写入配置前,请应用以下验证规则。继续操作前,向用户说明任何问题。

字段约束
Commerce 事件名称以 plugin. 或 observer. 开头;每个段匹配 [a-z_]+;最多 180 个字符
外部事件名称[\w\-_.]+;最多 180 个字符
提供方标签最多 100 个字符
提供方描述最多 255 个字符
提供方键可选;仅限字母数字和连字符;最多 50 个字符
事件标签最多 100 个字符
事件描述最多 255 个字符
字段名称[a-zA-Z0-9_\-.[\]]+ 或 *
规则运算符greaterThan、lessThan、equal、regex、in 或 onChange
运行时操作<package>/<action>(例如 my-package/handle-order-placed)
事件环境(可选)非空的 "paas" / "saas" 数组;省略表示适用于所有环境

步骤 3 — 更新 app.commerce.config.ts

将 eventing.commerce 和/或 eventing.external 添加到现有配置中,或与之合并,并保留所有其他域。如果配置中已有 eventing 键,请扩展它,而不要替换它。

最简示例(Commerce 事件):

eventing: {
  commerce: [{
    provider: { label: "Commerce Events Provider", description: "..." },
    events: [{
      name: "plugin.order_placed",          // plugin.<segments> or observer.<segments>
      label: "Order Placed",
      description: "Triggered when a customer places an order.",
      fields: [{ name: "order_id" }],       // empty array = full payload; Commerce events only
      runtimeActions: ["my-package/handle-order-placed"], // <package>/<action>
    }],
  }],
}

完整参考(包括外部事件源),请参阅 assets/eventing-config.ts。

创建处理程序操作

对于通过 runtimeActions 引用运行时操作的事件,请在 src/actions/ 下创建操作文件,并在 app.config.yaml 中注册。

注册操作

在 src/commerce-extensibility-1/ext.config.yaml 中,添加一个用户定义的包,与现有的 app-management 包并列。名称可任意指定,但不能使用 app-management(该名称由框架保留):

# src/commerce-extensibility-1/ext.config.yaml
# (add below the auto-generated app-management package)
runtimeManifest:
  packages:
    app-management:
      # ... auto-generated — do not edit
    my-app: # your package name — any name except "app-management"
      actions:
        handle-order-placed:
          function: actions/handle-order-placed/index.js # relative to src/commerce-extensibility-1/
          web: "no"
          runtime: nodejs:24
          annotations:
            require-adobe-auth: false

runtimeActions 中的 <package>/<action> 格式直接对应:my-app/handle-order-placed → 包 my-app,操作 handle-order-placed。

处理程序框架

事件处理程序接收 CloudEvents 格式的负载。params.data 将事件负载与元数据打包在一起,因此不要直接从 params.data 读取字段:params.data.value 是事件负载,即事件 fields 数组中声明的字段(如果 fields 为空,则为完整负载);params.data._metadata 是 Commerce 实例元数据;params.data.source 是 Commerce 事件配置中设置的商家/环境 ID 对。

负载结构因事件而异 — 在确定 fields 路径前,请查看 Adobe Commerce 事件参考中的具体事件名称。有些事件会将负载嵌套在子对象中(例如 value.order.entity_id);另一些事件则会将字段平铺到 value 上(例如 value.order_id),如下方示例所示。

// src/commerce-extensibility-1/actions/handle-order-placed/index.ts
export async function main(params: Record<string, unknown>) {
  const data = params.data as Record<string, unknown>;
  const value = data.value as Record<string, unknown>;
  // value contains the fields declared in the event's `fields` array
  // (or the full payload if fields is empty)

  const orderId = value.order_id;

  // process the event ...

  return { statusCode: 200, body: { processed: true } };
}

看起来像数字的字段不一定是数字 — Commerce 对某些字段的序列化方式不一致,甚至同一事件中也是如此(例如,ID 字段以 "3" 的形式传入,而同一负载中的总额仍为数字)。在比较或转发字段之前,请验证并转换其类型;如果无法正确转换,请跳过处理(不要抛出异常):

function toFiniteNumber(value: unknown): number {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }

  if (typeof value === "string" && value.trim() !== "") {
    const parsed = Number(value);
    if (Number.isFinite(parsed)) {
      return parsed;
    }
  }

  throw new Error(`Could not convert "${value}" to a finite number`);
}
在处理程序中调用 Commerce REST API

如果处理程序需要调用 Commerce REST API(例如获取其他订单数据),请使用 @adobe/aio-commerce-lib-app 中的 getCommerceClient,而不是自行实现配置或基于环境变量访问 Commerce 实例的方式(例如自定义 .env 变量,如 AIO_COMMERCE_API_BASE_URL,或用于存储基本 URL 的业务配置字段)。SDK 已根据应用的关联信息存储了 Commerce 基本 URL 和部署类型,因此 getCommerceClient 会为你解析这些信息:

// src/commerce-extensibility-1/actions/handle-order-placed/index.ts
import { getCommerceClient } from "@adobe/aio-commerce-lib-app";
import { resolveImsAuthParams } from "@adobe/aio-commerce-lib-auth";

export async function main(params: Record<string, unknown>) {
  const data = params.data as Record<string, unknown>;
  const value = data.value as Record<string, unknown>;
  const orderId = value.order_id;

  const client = await getCommerceClient(resolveImsAuthParams(params));
  const order = await client.get(`orders/${orderId}`).json();

  // process the event using order details ...

  return { statusCode: 200, body: { processed: true } };
}

客户端的基本 URL 已包含 REST 前缀和 API 版本(PaaS 为 rest/<store>/V1,SaaS 为 V1)——只传入资源路径(orders/${orderId},而不是 V1/orders/${orderId} 或 rest/all/V1/orders/${orderId})。

完整模式(包括如何处理未关联状态(AssociationRecordNotFoundError)),请参阅从运行时操作访问关联的 Commerce 实例。

第 4 步 — 重新生成 installation 操作

如果 eventing 是配置中第一个需要安装的域(其他域包括 webhooks、adminUi、installation.customInstallationSteps),请重新运行:

npx @adobe/aio-commerce-lib-app init

此操作会将 installation 操作添加到 ext.config.yaml 的 app-management 包中——aio app build/aio app deploy 只会读取现有文件,不会重新生成文件。如果跳过此步骤,Commerce 就没有可调用的端点,因此事件源可以正常构建和部署,但实际上永远不会订阅。即使该操作已存在,也可以安全地重新运行。

第 5 步 — 验证

构建项目,确认更新后的配置有效:

aio app build

构建失败并出现验证错误时,会直接指出有问题的配置字段。如果此事件源需要执行第 4 步,还要检查 ext.config.yaml 中的 app-management 下是否现在包含 actions.installation。

常见问题

  • 外部事件包含 fields:fields 属性仅适用于 Commerce 事件;外部事件不支持此属性。
  • runtimeActions 格式错误:必须采用 <package>/<action> 格式。两部分都只能包含小写字母、数字和连字符。
  • app-management 包名称冲突:框架每次构建时都会在 ext.config.yaml 中生成此包。请为自己的操作使用其他名称。
  • 函数路径相对于 src/commerce-extensibility-1/:不要使用 src/... 或相对于项目根目录的路径。actions/handle-order-placed/index.js 可以正确解析;src/commerce-extensibility-1/actions/handle-order-placed/index.js 则不行。
  • 找不到 defineConfig:确保已安装 @adobe/aio-commerce-lib-app,并且从 @adobe/aio-commerce-lib-app/config 导入 defineConfig。
  • 构建因缺少操作而失败:runtimeActions 中引用的运行时操作必须存在于项目中。检查 src/commerce-extensibility-1/actions/ 下的操作文件,并创建缺少的桩文件。
  • 处理程序需要 Commerce 基本 URL:请使用 getCommerceClient(@adobe/aio-commerce-lib-app),不要使用自定义 .env 变量或业务配置字段。请参阅在处理程序中调用 Commerce REST API。
  • 事件已部署,但 Commerce 从未订阅:添加第一个需要安装的域后没有重新运行 init(第 4 步)——没有 installation 操作,也就没有安装端点。
  • 数字字段以字符串形式传入:不要假设 typeof value.field === "number"——即使同一事件中的其他字段仍为数字,id/qty/total 字段也可能被序列化为字符串。使用 Number(...) 进行转换,并在使用前检查 Number.isFinite。

质量标准

  • aio app build 执行成功且无错误
  • 当此事件源需要时,ext.config.yaml 中包含 installation 操作
  • 使用从事件负载中提取的数字字段前,先进行类型转换和验证,不要假定它们已经是数字

后续扩展

aio app build 通过后:

  • 添加 Webhook 拦截 — 调用 commerce-app-webhooks,以拦截 Commerce 操作
  • 添加商家设置 — 调用 commerce-app-business-config,以在 Commerce Admin 中公开可配置的设置
  • 扩展 Admin UI — 调用 commerce-app-admin-ui,以在 Commerce Admin 中添加自定义列、批量操作或菜单项
  • 添加持久化存储 — 调用 commerce-app-storage,以通过可查询的数据库存储为事件处理程序提供支持

参考资料

评论

支持 Markdown