多渠道 SKU 事件 Kafka 消费方案

结论

方案结论:PIM 与 Backend 共用一条 SKU 事件消费路径:按 store_code 取渠道配置,匹配商品一次,再保存两张表。无内部 Kafka 通知。京东首批接入;其他渠道按统一契约映射后复用。

  1. channelCode 从 auth_config 获取;消费者不写渠道名分支。
  2. 各渠道外部 SKU / item ID 映射到 channel_sku_id / 可空 page_id。
  3. page_id 为空时,两表 item_id 都使用 channel_sku_id。
  4. 当前仍为设计稿;每个渠道上线前需协调其旧写入任务,首批京东需关闭 ODTS JD Job。

适用于多渠道共用的 SKU 事件消费京东首批接入尚未实施

1

事件如何从 PIM 到达数据库?

PIM 发布事件。Backend Listener 解析后调用 Service。Service 按 store_code 查询授权配置,处理可空 page_id 并匹配一次商品,然后直接保存两张表和清理缓存;不发布内部 Kafka 消息。

PIM → Backend → 共享数据库
图的结构
  • PIM pim
  • Kafka topic kafka
  • Backend Consumer backend
  • odts 共享库 database
  • pim → kafka · 发布
  • kafka → backend · 消费
  • backend → database · 查配置 / 匹配 / 保存两表
2

消息携带什么?

Topic 为 offer-pim-channel-sku-basic-publish。分区键 store_code-channel_sku_id 由 PIM 控制;消费者不生成消息 Key。

字段用途落库或处理
schema_version消息字段不落库,不做版本校验,也不作为拒收消息的条件
event_id日志关联不做幂等去重;重复消息按最后写入覆盖
event_type事件路由UPSERT、DELETED、ON_SHELF、OFF_SHELF
event_time上游事件时间转成 Instant 写入 modified
store_code店铺识别码查询 auth_config,取得 storeId、channelCode、authCode
page_id渠道商品 / item 外部 ID,可空两表 item_id:优先使用 page_id,为空时使用 channel_sku_id
channel_sku_id当前渠道 SKU 外部 IDchannel_sku.sku_id
internal_code外部商品编码outer_id;同时作为中台匹配输入
channel_sku_nameSKU 名称channel_sku.sku_name;暂用于 item 名称
priceSKU 价格字符串转成 BigDecimal 写 product_price
changed_fields上游变更字段指示增量更新字段,不单独落库
3

四种事件分别改变什么?

event_type写库动作后续动作
UPSERT按 item / SKU 查询;存在则更新,不存在则新增。字段更新按 changed_fields保存两表并清理缓存
DELETED将 SKU 软删除;按业务规则同步父 item保存两表并清理缓存
ON_SHELFis_onsale = 1;同步父 item 状态保存两表并清理缓存
OFF_SHELFis_onsale = 0;不软删除保存两表并清理缓存

所有事件均直接写数据库;不构造 before/after,也不发送内部 Kafka 消息。

price 更新为 product_price;internal_code 更新 outer_id,并触发一次商品匹配;同一匹配结果回填 item 与 SKU。channel_sku_name 更新 sku_name。

4

channel_item:42 列映射

Backend 直接写 odts.channel_item。下表逐列列出本事件链路写入方式;“不触碰”表示 JPA 不映射或本消息不提供该字段。

DDL 列类型Backend 映射 / 赋值说明
idbigint新增 RedisHelper.getEntityId();更新保留原 ID主键 NOT NULL
auth_codevarcharauth_config.auth_code按 store_code 查询
item_idvarchar解析后的 itemIdpage_id 非空用其值;否则用 channel_sku_id
item_namevarcharmsg.channel_sku_namePIM 未提供独立 item 名称
outer_idvarcharmsg.internal_code外部商品编码
product_codevarchar匹配商品后写 ProductEntity.code中台匹配结果
product_cnamevarchar匹配商品后写 ProductInfoEntity.chineseName中台匹配结果
product_pricedecimal不设置PIM 无 item 级价格
mp_idbigint匹配商品后写 ProductEntity.merchantProductId中台匹配结果
store_mp_idbigint匹配商品后写 ProductEntity.id中台匹配结果
modifiedtimestampmsg.event_time毫秒转时间
last_query_timetimestamp不设置ODTS JD 同步字段
ext_infolongtext不设置写入 NULL / 保持默认
stock_auto_push_statustinyint不设置ODTS 推送字段
stock_push_error_remarkvarchar不设置ODTS 推送字段
channel_codevarcharauth_config.channel_code同一次授权查询
merchant_idbigintcommonProp.getWarehouseMerchantId()—
store_idbigintauth_config.store_id同一次授权查询
match_statustinyint单 SKU 匹配后设 3;未匹配设 1ALL_MATCH / UN_MATCH
price_auto_push_statustinyint不设置ODTS 推送字段
price_push_error_remarkvarchar不设置ODTS 推送字段
is_availabletinyintDTO of() 显式设 1避免 JPA NULL 覆盖 DDL 默认值
is_deletedbigintDELETED 时设 ID;其他事件设 0软删除标记
version_noint新增设 0;更新用旧值加一,旧值 NULL 则 0手动赋值,不用 @Version
create_useridbigint不设置ODTS 审计字段
create_usernamevarchar不设置ODTS 审计字段
create_useripvarchar不设置ODTS 审计字段
create_usermacvarchar不设置ODTS 审计字段
create_timetimestamp@CreatedDateBackend 审计时间
create_time_dbtimestamp数据库默认值不触碰
server_ipvarchar不设置ODTS 系统字段
update_useridbigint不设置ODTS 审计字段
update_usernamevarchar不设置ODTS 审计字段
update_useripvarchar不设置ODTS 审计字段
update_usermacvarchar不设置ODTS 审计字段
update_timetimestamp@LastModifiedDateBackend 审计时间
update_time_dbtimestamp数据库自动更新不触碰
client_versionnovarchar不设置ODTS 系统字段
company_idbigintBizConstant.COMPANY_IDBackend 常量
is_onsaleint按 event_type 映射上架 1;下架 / 删除 0;UPSERT 不改变
type_of_producttinyint匹配商品后写 ProductEntity.typeOfProduct中台匹配结果
5

channel_sku:38 列映射

Backend 直接写 odts.channel_sku。新增 SKU 的 channel_item_id 使用保存 item 后返回的 ID;其余字段逐列映射如下。

DDL 列类型Backend 映射 / 赋值说明
idbigint新增 RedisHelper.getEntityId();更新保留原 ID主键 NOT NULL
auth_codevarcharauth_config.auth_code按 store_code 查询
channel_item_idbigint保存 channel_item 后返回的主键父商品主键
item_idvarchar解析后的 itemIdpage_id 非空用其值;否则用 channel_sku_id
sku_idvarcharmsg.channel_sku_id当前渠道 SKU 外部 ID
sku_namevarcharmsg.channel_sku_name消息字段已补充
outer_idvarcharmsg.internal_code外部商品编码
product_codevarchar匹配商品后写 ProductEntity.code中台匹配结果
product_cnamevarchar匹配商品后写 ProductInfoEntity.chineseName中台匹配结果
product_pricedecimalnew BigDecimal(msg.price)SKU 价格
mp_idbigint匹配商品后写 ProductEntity.merchantProductId中台匹配结果
store_mp_idbigint匹配商品后写 ProductEntity.id中台匹配结果
modifiedtimestampmsg.event_time毫秒转时间
ext_infotext不设置PIM 消息不提供原始 SKU JSON
properties_namevarchar不设置PIM 消息无规格属性
channel_codevarcharauth_config.channel_code同一次授权查询
merchant_idbigintcommonProp.getWarehouseMerchantId()—
store_idbigintauth_config.store_id同一次授权查询
is_availabletinyintDTO of() 显式设 1避免 JPA NULL 覆盖 DDL 默认值
is_deletedbigintDELETED 时设 ID;其他事件设 0软删除标记
version_noint新增设 0;更新用旧值加一,旧值 NULL 则 0手动赋值,不用 @Version
create_useridbigint不设置ODTS 审计字段
create_usernamevarchar不设置ODTS 审计字段
create_useripvarchar不设置ODTS 审计字段
create_usermacvarchar不设置ODTS 审计字段
create_timetimestamp@CreatedDateBackend 审计时间
create_time_dbtimestamp数据库默认值不触碰
server_ipvarchar不设置ODTS 系统字段
update_useridbigint不设置ODTS 审计字段
update_usernamevarchar不设置ODTS 审计字段
update_useripvarchar不设置ODTS 审计字段
update_usermacvarchar不设置ODTS 审计字段
update_timetimestamp@LastModifiedDateBackend 审计时间
update_time_dbtimestamp数据库自动更新不触碰
client_versionnovarchar不设置ODTS 系统字段
company_idbigintBizConstant.COMPANY_IDBackend 常量
is_onsaleint按 event_type 映射上架 1;下架 / 删除 0;UPSERT 不改变
type_of_producttinyint匹配商品后写 ProductEntity.typeOfProduct中台匹配结果
6

新增记录的字段如何保证完整?

Backend 写共享库时不能依赖数据库为主键生成值:两个 Entity 的 id 都是无 @GeneratedValue 的 @Id。设计要求在新增时调用 Redis ID helper;更新时沿用已查出的主键。

字段 / 依赖设计处理依据与边界
id新增 item / SKU 调用 RedisHelper.getEntityId();更新保留原 IDBackend 已有 Redis 自增 helper;年维度计数,返回 14 位 Long
auth_code、store_id、channel_code增加 AuthConfigEntity / Repository;按 store_code 查询 odts.auth_config查询结果供 item、SKU 共用;字段属于唯一键组成部分
is_availableDTO of() 显式设为 1Entity 映射该列时,JPA 的显式 NULL 会覆盖 DDL 默认值
version_no新增 Entity 字段 Integer versionNo;新增赋 0,更新赋旧值加一,旧值为空则赋 0Backend 没有此表字段的自动递增机制;参照 ODTS BasePO.beforeInsert/beforeUpdate,不使用 @Version
company_id使用 BizConstant.COMPANY_ID沿用当前 Backend 方案
中台匹配用 internal_code 和 storeId 查询商品数据,回填商品编码、名称、类型及关联 ID文档 §3.1.9 提出 Backend 查库替代 ODTS SOA

更新前先按 item / SKU 业务键查询,再按 ID 保存。Item 查询以 itemId + storeId 为条件;SKU 查询使用父 item ID。软删除记录查询策略需要保留,以支持既有记录更新。

7

Listener 与业务 Service 怎样分工?

组件职责
ChannelSkuEventMessage映射 JSON 字段,包括 snake_case 字段名
ChannelSkuEventListener绑定新 topic、反序列化消息、调用 Service;沿用 aliKafkaListener 与现有错误处理器
ChannelSkuEventServiceImpl校验必需字段、解析 itemId、按 store_code 查询授权、匹配一次商品、保存两表、清理缓存
AuthConfigEntity / Repository映射 odts.auth_config;只按 store_code 查询一次,两表共用结果
MiddlewareMatchService每条事件按 internal_code + storeId 匹配一次;item 与 SKU 共用同一结果
8

上线前必须处理哪些风险?

风险影响应对
旧写入任务与 Backend 并行共享数据被覆盖或不一致每个渠道启用前,确认并停止该渠道冲突写入任务;首批京东先关闭 ODTS JD Job
Kafka topic 尚未创建Consumer 无法接收事件上线前创建共用 topic
PIM 消息契约未锁定渠道字段可能映射错误PIM 确认 internal_code、价格精度、changed_fields 和 store_code 字典
消息乱序SKU 状态可能被旧事件覆盖同一店铺 SKU 使用同分区键,并按 event_time 处理
缓存未同步失效查询可能读到旧数据更新后清理 Backend SkuIdCache;逐渠道确认是否共享旧应用缓存
业务异常部分事件不能落库Listener 复用错误处理器重试;未知事件或店铺未匹配时记录错误并返回

首批京东:确认消息契约与 topic;关闭 ODTS JD Job;启用 Backend 共用 Consumer;先完成单店铺灰度。后续渠道按相同契约完成字段映射和旧任务切换后复用该路径。

9

多渠道接入边界

共用消费者只处理标准事件和授权配置,不按渠道名分支。PIM 将各渠道 SKU / item ID、商品编码、名称、价格和事件类型映射到统一契约。字段语义不同则先归一化或扩展契约。

每个渠道配置 store_code / channelCode,并协调旧写入任务。每条事件只匹配一次商品,item 与 SKU 共用结果。本方案不构造 ChannelSkuOnsaleDto,也不发内部 Kafka 消息。

10

交付范围是什么?

本次新增消息 DTO、事件枚举、Listener 和 Service;测试 Controller 可选。配置文件新增 Consumer group。方案不新增表、不发送内部 Kafka 消息,也不修改 ODTS 代码。

当前仍是设计稿,未落代码。上线前需完成:

  • 创建共用 Kafka topic 并确认标准消息契约。
  • 关闭京东旧 Job,完成 Backend 编码、测试和首批灰度。
  • 后续渠道逐一确认字段映射、auth_config 配置及旧任务切换。

正在打开交互图…