[私人测试版]{class="badge informative"}

Event Transformer 指南 event-transformer-guide

目录

忠诚度挑战入门

AVAILABILITY
此功能当前处于​ 私人测试版 ​中。 有关Journey Optimizer中发行周期和可用性阶段的完整详细信息,请参阅发行周期

在将客户交易记录应用于忠诚度质询之前,它必须采用质询服务理解的​ Adobe忠诚度事件 ​格式。 客户事件(来自POS系统、移动应用程序、电子商务平台或任何其他源)通常使用客户自己的数据架构。 事件转换器​无需对上游系统进行任何更改即可弥合此间隙。

概述

事件定义​告知平台以下两点:

  • 要声明的事件 — 如何识别传入的事件属于此定义(匹配)
  • 如何重新设置其形状 — 将客户的字段映射到忠诚度事件格式(转换)的JSONata表达式

每个组织可以配置多个事件定义。 平台会按顺序评估它们,并应用匹配的第一个。 不符合任何定义的事件会落入本机引入的陷阱(请参阅后备 — 本机忠诚度事件)。

Adobe忠诚度事件格式

每个事件定义都必须生成一个采用以下格式的JSON对象。 这是挑战服务流程的输入。

{
  "_id":              "string — optional; used for duplicate detection if enabled",
  "event_name":       "string — used for internal metrics and reporting only (e.g. 'purchase', 'visit')",
  "timestamp":        "ISO 8601 date-time string — when the event occurred",
  "utc_offset":       "string — UTC offset of the store or device (e.g. '-07:00'); required for daypart matching",
  "location_id":      "string — optional; store or location identifier",
  "transaction_id":   "string — optional; dedup key for the transaction",
  "loyalty_identity": {
    "id": "string — the member's loyalty ID"
  },
  "item_list": [
    {
      "item_set":   ["string", "..."],  // one or more identifiers — SKU, category, event code, etc.
      "item_name":  "string — optional human-readable label",
      "quantity":   1,                  // integer; how many units
      "unit_price": 4.99,               // float; price per unit
      "sub_total":  4.99                // float; line total (quantity × unit_price)
    }
  ]
}

字段注释

字段
必需
注释
loyalty_identity
必须包含id — 成员的忠诚度ID。
item_list
必须具有≥1个项目;空的item_list被拒绝。
item_set
(每项)
标识符任务包含/排除列表匹配项。
timestamp
用于日期窗口评估。 必须为ISO 8601。
utc_offset
推荐
白天配对和连续天数计数所需。
_id
如果组织启用了重复检测,则用于重复数据删除。
sub_total
支出阈值任务使用它;忽略则意味着零支出。

事件定义字段

字段
类型
必需
描述
guid
字符串
否(系统分配)
系统分配的唯一ID;只读。
name
字符串
易于用户识别的标签,例如"Starbucks POS Purchase"
xdmSchemaId
字符串
按XDM架构ID匹配事件(请参阅匹配的工作方式)。
schema
字符串
JSON架构 (作为字符串)以验证传入事件。
transformer
字符串
将事件映射到忠诚度格式的JSONata表达式。

匹配的工作方式

通过数据收集核心服务(DCCS)到达的事件在其信封中包含XDM模式引用。 平台从/body/xdmMeta/schemaRef/id中读取架构ID,并将其与每个定义的xdmSchemaId进行比较。

平台按​ 的顺序遍历组织的事件定义 ​并应用第一个匹配。 找到匹配项后,xdmEntity正文将传递到转换器。

编写转换器

transformer字段是JSONata表达式。 它接收传入事件JSON作为其输入,并且必须返回有效的Adobe会员事件对象。

基本映射模式

将目标格式的每个顶级字段映射到源事件中的相应路径:

code language-jsonata
{
  "_id":            sourceEvent._id,
  "event_name":     sourceEvent.eventType,
  "timestamp":      sourceEvent.timestamp,
  "utc_offset":     sourceEvent.storeInfo.utcOffset,
  "location_id":    sourceEvent.storeInfo.storeId,
  "transaction_id": sourceEvent.transaction.id,
  "loyalty_identity": {
    "id": sourceEvent.member.loyaltyId
  },
  "item_list": sourceEvent.transaction.items.{
    "item_set":   [itemSku, itemCategory],
    "item_name":  itemDescription,
    "quantity":   quantity,
    "unit_price": unitPrice,
    "sub_total":  lineTotal
  }
}
对事件名称进行硬编码

如果与此定义匹配的所有事件都表示相同的逻辑活动,请对event_name进行硬编码:

code language-jsonata
{
  "event_name": "in-store-purchase",
  ...
}

event_name用于内部量度和报告。 未用作任务筛选器 — 任务资格由item_set内容决定,而不是由事件名称决定。

映射DCCS/XDM事件的标识

对于通过DCCS路由到达的事件,成员的身份通常包含在标准XDM identityMap字段中,而不是自定义租户属性中。 identityMap是一个由命名空间键控的映射 — 键本身就是命名空间名称,值是一个标识对象数组。

code language-jsonata
"loyalty_identity": {
  "id": identityMap.Email[0].id
}
  • 命名空间替换:​使用您的组织用于忠诚度会员的任何命名空间替换EmailLoyaltyECIDCRMID等。始终从包含主要会员配置文件标识的命名空间中读取。

  • 始终使用[0] identityMap.Email是一个数组。 如果没有索引,如果存在多个标识,则JSONata将返回序列而不是单个值,并且loyalty_identity.id将变为列表。 将其固定到带有[0]的第一个元素。

  • 避免自定义租户标识字段:​自定义字段组有时会公开电子邮件形式的字段(例如_yourtenant.identification.core.email)。 在示例数据中,这将返回一个值并看起来是正确的,但在生产事件中,它通常为空。 可靠的身份来源始终为identityMap

正在生成item_set

item_set是字符串标识符的数组。 包括您的挑战任务可能过滤的每个字段:

code language-jsonata
"item_set": [itemSku, productCategory, departmentCode]

对于非事务性事件(签入、调查完成、自定义触发器),单个标识符就足够了:

code language-jsonata
"item_set": [eventName]
映射unit_price

unit_price应为单价。 某些源架构存储的是行总计(价格×数量)。 如果来源字段为行合计,则除以数量以获取单价:

code language-jsonata
"unit_price": priceTotal / quantity

仅当源字段为行总计时才相除。 如果它已经存储了单位价格,则直接映射它 — 将单位价格除以数量将会自动产生错误的值。

派生transaction_id

如果源事件不包括事务标识符,则可以从时间戳派生稳定标识符:

code language-jsonata
"transaction_id": "txn_" & $string($toMillis(timestamp))

这会将ISO时间戳转换为epoch毫秒,并为给定事件生成确定性值。 使用您平台自己的ID生成函数(如果可用)。

使用JSONata函数

提供了完整的JSONata函数库。 有用的示例:

code language-jsonata
/* String concatenation */
"item_set": [skuId & ':' & categoryId]

/* Number formatting */
"item_set": ["spend:" & $formatNumber(totalAmount, '0.00')]

/* Conditional field */
"event_name": eventType ? eventType : "unknown"

/* Array transformation */
"item_list": items.{ "item_set": [sku], "quantity": qty, "sub_total": price * qty }

示例

示例1 — 简单自定义事件(非事务性)

方案:​移动应用发送签入事件。 没有行项目 — 事件本身是符合条件的活动。

传入事件:

code language-json
{
  "_id":       "evt-001",
  "eventName": "store-checkin",
  "timestamp": "2025-10-15T14:22:00Z",
  "storeId":   "STORE-042",
  "member": {
    "loyaltyId": "LM-8827361"
  }
}

事件定义:

code language-json
{
  "name":        "Mobile Store Check-In",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/store-checkin-v1",
  "transformer": "{\"_id\": _id, \"event_name\": eventName, \"timestamp\": timestamp, \"location_id\": storeId, \"loyalty_identity\": {\"id\": member.loyaltyId}, \"item_list\": [{\"item_set\": [eventName], \"quantity\": 1}]}"
}

格式化的转换器(用于可读性):

code language-jsonata
{
  "_id":        _id,
  "event_name": eventName,
  "timestamp":  timestamp,
  "location_id": storeId,
  "loyalty_identity": {
    "id": member.loyaltyId
  },
  "item_list": [
    {
      "item_set": [eventName],
      "quantity": 1
    }
  ]
}

输出Adobe忠诚度事件:

code language-json
{
  "_id":        "evt-001",
  "event_name": "store-checkin",
  "timestamp":  "2025-10-15T14:22:00Z",
  "location_id": "STORE-042",
  "loyalty_identity": { "id": "LM-8827361" },
  "item_list": [{ "item_set": ["store-checkin"], "quantity": 1 }]
}

没有包含/排除限制的挑战任务会将此事件计为符合条件的访问 — 单个item_set条目["store-checkin"]与允许所有项目的任何任务匹配。

示例2 — 包含行项目的POS购买

方案:​销售点系统发送事务负载。 每个行项目都有一个SKU并且属于一个类别。 挑战任务使用SKU和类别来确定符合条件。

传入事件:

code language-json
{
  "_id":       "txn-20251015-4492",
  "timestamp": "2025-10-15T14:35:00Z",
  "storeInfo": {
    "storeId":   "STORE-042",
    "utcOffset": "-07:00"
  },
  "transaction": {
    "transactionId": "4492",
    "items": [
      { "sku": "COFFEE-001", "category": "BEVERAGE", "qty": 2, "unitPrice": 4.50, "lineTotal": 9.00 },
      { "sku": "MUFFIN-007", "category": "FOOD",     "qty": 1, "unitPrice": 3.25, "lineTotal": 3.25 }
    ]
  },
  "member": {
    "loyaltyId": "LM-8827361"
  }
}

事件定义:

code language-json
{
  "name":        "Retail POS Purchase",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/retail-pos-purchase-v1",
  "transformer": "{\"_id\": _id, \"event_name\": \"purchase\", \"timestamp\": timestamp, \"utc_offset\": storeInfo.utcOffset, \"location_id\": storeInfo.storeId, \"transaction_id\": transaction.transactionId, \"loyalty_identity\": {\"id\": member.loyaltyId}, \"item_list\": transaction.items.{\"item_set\": [sku, category], \"quantity\": qty, \"unit_price\": unitPrice, \"sub_total\": lineTotal}}"
}

格式化的转换器:

code language-jsonata
{
  "_id":            _id,
  "event_name":     "purchase",
  "timestamp":      timestamp,
  "utc_offset":     storeInfo.utcOffset,
  "location_id":    storeInfo.storeId,
  "transaction_id": transaction.transactionId,
  "loyalty_identity": {
    "id": member.loyaltyId
  },
  "item_list": transaction.items.{
    "item_set":   [sku, category],
    "quantity":   qty,
    "unit_price": unitPrice,
    "sub_total":  lineTotal
  }
}

输出Adobe忠诚度事件:

code language-json
{
  "_id":            "txn-20251015-4492",
  "event_name":     "purchase",
  "timestamp":      "2025-10-15T14:35:00Z",
  "utc_offset":     "-07:00",
  "location_id":    "STORE-042",
  "transaction_id": "4492",
  "loyalty_identity": { "id": "LM-8827361" },
  "item_list": [
    { "item_set": ["COFFEE-001", "BEVERAGE"], "quantity": 2, "unit_price": 4.50, "sub_total": 9.00 },
    { "item_set": ["MUFFIN-007", "FOOD"],     "quantity": 1, "unit_price": 3.25, "sub_total": 3.25 }
  ]
}

具有include: ["BEVERAGE"]的质询任务将看到咖啡行项目符合条件(其item_set包含"BEVERAGE")并累计9.00美元用于该任务。 将排除松饼行项目。

示例3 — AEP Experience Event(XDM模式匹配)

方案:​事件通过Adobe Journey Optimizer。 传入事件是具有已知架构ID的XDM体验事件。 平台使用架构ID进行匹配,而不是路径/值检查。

传入XDM实体主体(从AJO事件提取的xdmEntity):

code language-json
{
  "_brandname": {
    "identities": {
      "loyaltyId": "LM-8827361"
    },
    "transactions": {
      "transactionId": "TXN-9901",
      "storeNumber":   "042",
      "utcOffset":     "-07:00",
      "lineItems": [
        { "skuNumber": "11143053", "priceAmount": 345, "qty": 1, "category": "BEVERAGE" },
        { "skuNumber": "11161387", "priceAmount": 495, "qty": 1, "category": "FOOD" }
      ],
      "totalAmount": 840
    }
  },
  "_id":       "87c0cccf-5809-38e0-a703-3994e80173ab",
  "timestamp": "2025-07-04T16:03:32.000Z"
}

事件定义:

code language-json
{
  "name":        "AJO Brand Purchase",
  "xdmSchemaId": "https://ns.adobe.com/brandname/schemas/purchase-event-v1",
  "transformer":  "{\"_id\": _id, \"event_name\": \"purchase\", \"timestamp\": timestamp, \"utc_offset\": _brandname.transactions.utcOffset, \"location_id\": _brandname.transactions.storeNumber, \"transaction_id\": _brandname.transactions.transactionId, \"loyalty_identity\": {\"id\": _brandname.identities.loyaltyId}, \"item_list\": _brandname.transactions.lineItems.{\"item_set\": [skuNumber, category], \"quantity\": qty, \"unit_price\": priceAmount, \"sub_total\": priceAmount * qty}}"
}

格式化的转换器:

code language-jsonata
{
  "_id":            _id,
  "event_name":     "purchase",
  "timestamp":      timestamp,
  "utc_offset":     _brandname.transactions.utcOffset,
  "location_id":    _brandname.transactions.storeNumber,
  "transaction_id": _brandname.transactions.transactionId,
  "loyalty_identity": {
    "id": _brandname.identities.loyaltyId
  },
  "item_list": _brandname.transactions.lineItems.{
    "item_set":   [skuNumber, category],
    "quantity":   qty,
    "unit_price": priceAmount,
    "sub_total":  priceAmount * qty
  }
}

注意:​当事件按XDM架构ID匹配时,转换器仅接收事件的xdmEntity部分,而不是外部AJO信封。 转换器表达式中的所有路径都相对于XDM实体主体。

添加JSON架构验证(可选)

如果您希望平台在尝试转换之前验证传入事件的结构,请将schema字段设置为编码为JSON字符串的JSON架构文档。

架构验证失败的事件会在转换运行之前被拒绝。 错误响应包括特定的验证失败,因此很容易诊断格式错误的上游事件。

模式示例(例如上面的2个)
code language-json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": ["_id", "timestamp", "transaction", "member"],
  "properties": {
    "_id":       { "type": "string" },
    "timestamp": { "type": "string", "format": "date-time" },
    "member": {
      "type": "object",
      "required": ["loyaltyId"],
      "properties": {
        "loyaltyId": { "type": "string" }
      }
    },
    "transaction": {
      "type": "object",
      "required": ["items"],
      "properties": {
        "transactionId": { "type": "string" },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["sku", "qty", "lineTotal"],
            "properties": {
              "sku":       { "type": "string" },
              "category":  { "type": "string" },
              "qty":       { "type": "number" },
              "unitPrice": { "type": "number" },
              "lineTotal": { "type": "number" }
            }
          }
        }
      }
    }
  }
}

在事件定义的schema字段中将此架构作为缩小的JSON字符串传递。

回退 — 本机忠诚度事件

如果没有事件定义与传入事件匹配,则平台会尝试将其直接摄取为本机Adobe忠诚度事件。 如果有效负载已符合上述忠诚度事件格式,则无需转换器,并且事件会按原样应用。 这允许已预格式化其事件的客户完全绕过转换。

API 参考

所有事件定义操作都使用基本路径/loyalty/metadata/config/events

创建事件定义
code language-http
POST /loyalty/metadata/config/events
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":        "Retail POS Purchase",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/retail-pos-purchase-v1",
  "transformer": "{ ... }"
}
列出事件定义
code language-http
GET /loyalty/metadata/config/events
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
更新事件定义
code language-http
PUT /loyalty/metadata/config/events/{eventId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":        "Retail POS Purchase (v2)",
  "transformer": "{ ... updated expression ... }"
}
删除事件定义
code language-http
DELETE /loyalty/metadata/config/events/{eventId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}

转换器验证

在保存事件定义时,将验证JSONata表达式的语法。 如果表达式无效,则API返回包含分析失败说明的422错误。

要在部署之前测试转换器,请使用JSONata Exerciser — 将源事件粘贴为输入,并粘贴转换器表达式以验证输出是否与预期的忠诚度事件格式匹配。

常见陷阱

这些错误都在一个简单的单项测试有效载荷上无错误地运行,这正是它们滑过未被检测到的原因。 在部署之前,请始终根据包含两个或更多产品的负载测试转换器。

构建一个对象而不是在数组上映射

最常见的错误。 将单个对象文字与productListItems.SKU结合使用会将每个SKU和每个数量提取到集总序列中,而不是为每个产品生成一个行项目。

✗将所有项折叠为一个:

code language-jsonata
"item_list": [
  {
    "item_set": [ productListItems.SKU ],
    "quantity": productListItems.quantity
  }
]

使用两个产品,item_set同时保存两个SKU,quantity将变为[1, 4]等数组。

✓每个产品一个行项目:

code language-jsonata
"item_list": [
  productListItems.{
    "item_set": [SKU],
    "quantity": quantity
  }
]

每个产品运行一次.{ }映射,因此每个产品都会成为自己的条目。

忘记标识上的数组索引

identityMap.Email是一个数组。 如果没有[0],则当配置文件在该命名空间中有多个标识时,id将变为值列表而不是单个字符串。

identityMap.Email.id

identityMap.Email[0].id

从自定义租户字段获取身份
自定义字段组有时会公开电子邮件形式的字段,例如_yourtenant.identification.core.email。 在示例数据中,它会返回一个值并且看起来是正确的,但在生产事件中,它通常为空,从而导致loyalty_identity.id输出为null。 始终使用identityMap作为标识的来源。
嵌套数组泄漏到item_set

将类别字段添加到item_set看起来很简单,但如果productCategories本身是一个数组,则结果将以不可预测的方式展开。

✗生成的条目可能多于预期:

code language-jsonata
"item_set": [SKU, productCategories.categoryID]

具有三个类别的产品会生成具有四个值的item_set

✓对嵌套数组编制索引,以仅获取一个值:

code language-jsonata
"item_set": [SKU, productCategories[0].categoryID]
item_list为空或缺失

具有空或不存在item_list的事件被拒绝为无效。 对于非事务性事件(签入、自定义触发器),不存在自然的行项目,因此请生成一个合成的行项目:

code language-jsonata
"item_list": [{ "item_set": [eventName], "quantity": 1 }]
timestamp作为Unix纪元整数,而不是ISO 8601

该平台需要一个ISO 8601字符串。 如果您的源事件带有自纪元以来的毫秒数,请将其转换为:

code language-jsonata
"timestamp": $fromMillis(timestamp)
已忽略utc_offset
如果没有utc_offset,则会跳过时段匹配和连续日期连续计数。 将存储或设备UTC偏移量映射到任何可用的源事件。
DCCS事件中相对于AJO信封的转换器路径
对于DCCS事件,转换器仅接收xdmEntity主体,而不接收外部AJO信封。 所有路径都必须相对于XDM实体根。 如果表达式引用位于外部信封中的字段(例如/body/xdmMeta/...),则未找到这些字段,并将静默生成null。
recommendation-more-help
journey-optimizer-help