mcpField
P
product-information-management

product-information-management

github.com/finsilabs数据与数据库6080 次安装6 天前更新
代码仓库

关于 product-information-management

面向电商与商品运营团队的产品信息管理集成技能,说明如何将 Akeneo 或 Salsify 作为商品数据源,并同步至销售平台。涵盖字段映射、增量同步和渠道格式转换。

主要功能

  1. 01将 PIM 设为商品数据源
  2. 02同步至 Shopify、WooCommerce 等平台
  3. 03映射属性与渠道字段
  4. 04增量或事件触发同步
  5. 05转换商品数据格式

适用场景

  1. 01统一网站与市场平台的商品信息
  2. 02将商品内容从 PIM 推送至店铺
  3. 03为无头店面接入丰富商品数据
  4. 04检查目录中缺失的商品属性

安装

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

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill product-information-management

安装后在 Claude Code 中输入/product-information-management确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/akeneo-client-auth-and-incremental-sync/
criteria.json
task.md
evals/product-transformation-attribute-options/
criteria.json
task.md
evals/salsify-integration-and-data-quality-rep/
criteria.json
task.md

详细介绍

产品信息管理

概述

产品信息管理(PIM)系统是所有渠道(网站、市场平台、印刷目录)产品数据(名称、描述、图片、属性和数字资产)的唯一可信来源。Akeneo 和 Salsify 是主流的 PIM 平台。本技能涵盖如何将 PIM 连接为产品信息完善的权威来源、如何在 PIM 与电商平台之间实现同步,以及如何构建将 PIM 数据转换为特定渠道格式的管道。

何时使用此技能

  • 当网站、市场平台商品信息和内部系统中的产品数据不一致时
  • 当商品团队在 PIM 中管理产品内容,而电商平台需要反映这些内容时
  • 当构建需要丰富产品数据来源的新无头店面时
  • 当新增需要特定渠道产品数据的销售渠道(市场平台、B2B 门户)时
  • 当审查产品数据质量并找出目录中缺失的属性时

核心说明

步骤 1:确定平台和 PIM 集成方式
平台PIM 集成选项同步内容
ShopifyAkeneo 官方 Shopify connector(免费,可在 Akeneo Marketplace 获取)或 Salsify Syndication for Shopify产品名称、描述、图片和属性 → Shopify 产品元字段;Shopify 变体映射到 Akeneo 产品模型
WooCommerceAkeneo WooCommerce Connector(开源,GitHub)或自定义 REST API 同步通过 Products REST API 将产品数据推送到 WooCommerce;图片上传到 WordPress 媒体库
BigCommerceAkeneo BigCommerce Connector(Akeneo Marketplace)或 Salsify for BigCommerce将产品属性推送到 BigCommerce 自定义字段和变体;将图片同步到 BigCommerce CDN
Custom / Headless直接通过 REST API 集成 Akeneo 或 Salsify完全控制数据映射;通过 updated 筛选器增量同步;同步期间将图片上传到你的 CDN
步骤 2:特定平台的 PIM 集成

Shopify

使用官方连接器将 Akeneo 连接到 Shopify:

  1. 在 Akeneo 实例中,前往 Connect → Marketplace 并安装 Shopify Connector(由 Akeneo 提供,免费)
  2. 在 Akeneo 中配置 Connection(位于 Connect → Connections 下),并授予 Products、Media files 和 Attribute options 的读取权限
  3. 在连接器设置中,将 Akeneo 渠道/区域语言映射到 Shopify 市场(例如,Akeneo en_US 作用域 → Shopify 默认语言)
  4. 将 Akeneo 属性代码映射到 Shopify 字段:
    • name → Shopify 产品标题
    • description → Shopify 正文 HTML
    • price → Shopify 变体价格
    • 自定义属性 → Shopify 产品元字段(在 Shopify 管理后台的 Settings → Custom data 下配置)
  5. 运行一次初始全量同步,然后安排增量同步——连接器仅拉取 updated > last_sync_at 的产品

验证同步:

  • 在 Shopify 管理后台打开一个产品,检查标题、描述和图片是否与 Akeneo 中的内容一致
  • 在 Shopify 产品页面的 Metafields 部分检查元字段

WooCommerce

使用 REST API 将 Akeneo 同步到 WooCommerce:

开源 Akeneo WooCommerce Connector(可在 github.com/akeneo/woocommerce-connector 获取)可作为起点,但许多商家会编写自定义同步脚本:

  1. 设置一个 cron 作业(每天或每小时运行一次),以便:

    • 使用 GET /api/rest/v1/products?search={"updated":[{"operator":">","value":"..."}]} 获取自上次同步以来更新的产品
    • 使用 GET /wp-json/wc/v3/products?sku={sku} 检查 WooCommerce 中是否已存在该产品
    • 使用 POST 或 PUT /wp-json/wc/v3/products/{id} 创建或更新 WooCommerce 产品
  2. 将 Akeneo 字段映射到 WooCommerce 字段:

    • Akeneo name(en_US)→ WooCommerce name
    • Akeneo description → WooCommerce description
    • Akeneo images → 下载并上传到 WordPress 媒体库,然后设置为 WooCommerce 产品图片
    • Akeneo 自定义属性 → WooCommerce 产品属性或 ACF 自定义字段
  3. 每次同步后,通过 WP-CLI 执行 wp transient delete-all 清除 WooCommerce 的瞬态缓存,确保更新后的产品立即显示


BigCommerce

使用 BigCommerce 连接器连接 Akeneo:

  1. 在 Akeneo Marketplace 中安装 BigCommerce Connector,并配置 BigCommerce API 凭据(从 Advanced Settings → API Accounts 获取 Client ID、Client Secret 和 Access Token)
  2. 在连接器配置中,将 Akeneo 品类映射到 BigCommerce 产品类型
  3. 配置属性映射:
    • Akeneo 属性 → BigCommerce 自定义字段或变体选项集
    • Akeneo 分类 → BigCommerce 分类树
  4. 在连接器设置中安排定期增量同步

自定义 / 无头

连接 Akeneo REST API:

// lib/akeneo/client.ts
export class AkeneoClient {
  private accessToken: string | null = null;
  private tokenExpiry: number = 0;

  constructor(private config: {
    baseUrl: string; clientId: string; clientSecret: string;
    username: string; password: string;
  }) {}

  async getToken(): Promise<string> {
    if (this.accessToken && this.tokenExpiry > Date.now() + 60000) return this.accessToken;

    const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
    const res = await fetch(`${this.config.baseUrl}/api/oauth/v1/token`, {
      method: 'POST',
      headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ grant_type: 'password', username: this.config.username, password: this.config.password }),
    });

    const data = await res.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + data.expires_in * 1000;
    return this.accessToken!;
  }

  async getAll(path: string): Promise<any[]> {
    const items: any[] = [];
    let nextUrl: string | null = path;

    while (nextUrl) {
      const token = await this.getToken();
      const res = await fetch(`${this.config.baseUrl}${nextUrl}`, {
        headers: { 'Authorization': `Bearer ${token}` },
      });
      const page = await res.json();
      items.push(...(page._embedded?.items ?? []));
      nextUrl = page._links?.next?.href?.replace(this.config.baseUrl, '') ?? null;
    }

    return items;
  }
}

export const akeneo = new AkeneoClient({
  baseUrl: process.env.AKENEO_BASE_URL!,
  clientId: process.env.AKENEO_CLIENT_ID!,
  clientSecret: process.env.AKENEO_CLIENT_SECRET!,
  username: process.env.AKENEO_USERNAME!,
  password: process.env.AKENEO_PASSWORD!,
});

将 Akeneo 按语言环境/范围划分的属性格式转换为扁平的店面产品:

// lib/akeneo/product-transformer.ts
export function transformAkeneoProduct(akeneoProduct: any, locale = 'en_US', scope = 'ecommerce') {
  const getValue = (attrCode: string, defaultValue: any = null) => {
    const values = akeneoProduct.values[attrCode] ?? [];
    const match = values.find(v => v.locale === locale && v.scope === scope)
      ?? values.find(v => v.locale === locale && v.scope === null)
      ?? values.find(v => v.locale === null && v.scope === scope)
      ?? values.find(v => v.locale === null && v.scope === null);
    return match?.data ?? defaultValue;
  };

  return {
    sku: akeneoProduct.identifier,
    name: getValue('name', '') as string,
    description: getValue('description', '') as string,
    brand: getValue('brand', '') as string,
    categories: akeneoProduct.categories,
    attributes: {
      color: getValue('color'),
      size: getValue('size'),
      material: getValue('material'),
    },
    enabled: akeneoProduct.enabled,
  };
}

增量同步作业(仅获取已更新的产品):

// jobs/akeneo-sync.ts
export async function syncAkeneoProducts() {
  const lastSyncAt = await db.syncState.getLastSync('akeneo_products');
  const syncStartTime = new Date();
  const updatedAt = lastSyncAt?.toISOString() ?? '2020-01-01T00:00:00+00:00';

  const products = await akeneo.getAll(
    `/api/rest/v1/products?search={"updated":[{"operator":">","value":"${updatedAt}"}]}&limit=100&with_attribute_options=true`
  );

  let synced = 0, errors = 0;

  for (const akeneoProduct of products) {
    try {
      const storefrontProduct = transformAkeneoProduct(akeneoProduct);
      // Validate required fields before upserting
      if (!storefrontProduct.name) {
        console.warn(`Skipping ${akeneoProduct.identifier}: missing name`);
        continue;
      }
      await db.products.upsert(storefrontProduct.sku, {
        ...storefrontProduct,
        akeneoUpdatedAt: new Date(akeneoProduct.updated),
      });
      synced++;
    } catch (err: any) {
      errors++;
      await db.syncErrors.insert({ productId: akeneoProduct.identifier, error: err.message });
    }
  }

  await db.syncState.updateLastSync('akeneo_products', syncStartTime);
  console.log(`Akeneo sync complete: ${synced} synced, ${errors} errors`);
}

Akeneo 发布产品时触发的 Webhook 同步(Akeneo 的 Event API):

// Register the webhook endpoint in Akeneo under: Connect → Webhooks
// POST /api/webhooks/akeneo
export async function POST(req: NextRequest) {
  const event = await req.json();
  if (event.event_type === 'product.updated' || event.event_type === 'product.created') {
    const sku = event.data.resource.identifier;
    const akeneoProduct = await akeneo.getAll(`/api/rest/v1/products/${sku}?with_attribute_options=true`);
    const storefrontProduct = transformAkeneoProduct(akeneoProduct);
    await db.products.upsert(sku, storefrontProduct);
    // Purge CDN cache for this product's page
    await revalidateProductPage(storefrontProduct.slug);
  }
  return NextResponse.json({ received: true });
}

最佳实践

  • 将 PIM 视为唯一真实数据源 — 切勿从电商平台将产品内容写回 PIM — 数据从 PIM 流向电商平台;只有对于 PIM 明确管理的数据(例如平台生成的 SEO 元数据),才写回 PIM
  • 使用增量同步,而非全量同步 — 每 15 分钟获取所有产品的开销很大;使用 Akeneo 的 updated 筛选器,仅获取已更改的产品
  • 同步时将图片上传到自己的 CDN — Akeneo 媒体文件 URL 是需要身份验证的内部 API URL;切勿直接向客户提供这些 URL;同步时将图片上传到 S3/R2/Cloudinary
  • 在本地缓存属性选项 — 颜色、尺寸和材质的选项标签查找很少发生变化;将它们缓存在 Redis 中并每小时刷新,以避免逐个产品调用 API
  • 在同步到店面前验证必填属性 — 没有名称或主图的产品不应发布;在创建或更新前添加验证

常见问题

问题解决方案
同步缺少必填属性的产品时失败使用 try/catch 包裹每个产品的同步;记录产品标识符和错误;跳过该产品并继续,而不是中止整个同步
同步后图片不可用Akeneo 媒体 URL 需要身份验证;同步时下载图片并重新上传到 CDN — 切勿直接提供 akeneo-base-url/api/rest/v1/media-files/...
Akeneo API 速率限制使用批量请求,并在非高峰时段运行同步;在本地缓存属性选项,以减少每个产品的 API 调用次数
PIM 重组后分类映射不同步创建一个在产品同步前运行的分类同步作业;当 Akeneo 分类代码在电商平台中没有对应映射时发出提醒
Shopify 连接器未同步 metafields确保连接器配置中的 metafield 命名空间和键与 Shopify admin → Settings → Custom data → Products 中配置的内容一致

Related Skills

  • @marketplace-connectors
  • @webhook-architecture
  • @erp-integration
  • @analytics-integration

评论

支持 Markdown