关于 product-information-management
面向电商与商品运营团队的产品信息管理集成技能,说明如何将 Akeneo 或 Salsify 作为商品数据源,并同步至销售平台。涵盖字段映射、增量同步和渠道格式转换。
主要功能
- 01将 PIM 设为商品数据源
- 02同步至 Shopify、WooCommerce 等平台
- 03映射属性与渠道字段
- 04增量或事件触发同步
- 05转换商品数据格式
适用场景
- 01统一网站与市场平台的商品信息
- 02将商品内容从 PIM 推送至店铺
- 03为无头店面接入丰富商品数据
- 04检查目录中缺失的商品属性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill product-information-management安装后在 Claude Code 中输入/product-information-management确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/akeneo-client-auth-and-incremental-sync/criteria.jsontask.mdevals/product-transformation-attribute-options/criteria.jsontask.mdevals/salsify-integration-and-data-quality-rep/criteria.jsontask.md详细介绍
产品信息管理
概述
产品信息管理(PIM)系统是所有渠道(网站、市场平台、印刷目录)产品数据(名称、描述、图片、属性和数字资产)的唯一可信来源。Akeneo 和 Salsify 是主流的 PIM 平台。本技能涵盖如何将 PIM 连接为产品信息完善的权威来源、如何在 PIM 与电商平台之间实现同步,以及如何构建将 PIM 数据转换为特定渠道格式的管道。
何时使用此技能
- 当网站、市场平台商品信息和内部系统中的产品数据不一致时
- 当商品团队在 PIM 中管理产品内容,而电商平台需要反映这些内容时
- 当构建需要丰富产品数据来源的新无头店面时
- 当新增需要特定渠道产品数据的销售渠道(市场平台、B2B 门户)时
- 当审查产品数据质量并找出目录中缺失的属性时
核心说明
步骤 1:确定平台和 PIM 集成方式
| 平台 | PIM 集成选项 | 同步内容 |
|---|---|---|
| Shopify | Akeneo 官方 Shopify connector(免费,可在 Akeneo Marketplace 获取)或 Salsify Syndication for Shopify | 产品名称、描述、图片和属性 → Shopify 产品元字段;Shopify 变体映射到 Akeneo 产品模型 |
| WooCommerce | Akeneo WooCommerce Connector(开源,GitHub)或自定义 REST API 同步 | 通过 Products REST API 将产品数据推送到 WooCommerce;图片上传到 WordPress 媒体库 |
| BigCommerce | Akeneo BigCommerce Connector(Akeneo Marketplace)或 Salsify for BigCommerce | 将产品属性推送到 BigCommerce 自定义字段和变体;将图片同步到 BigCommerce CDN |
| Custom / Headless | 直接通过 REST API 集成 Akeneo 或 Salsify | 完全控制数据映射;通过 updated 筛选器增量同步;同步期间将图片上传到你的 CDN |
步骤 2:特定平台的 PIM 集成
Shopify
使用官方连接器将 Akeneo 连接到 Shopify:
- 在 Akeneo 实例中,前往 Connect → Marketplace 并安装 Shopify Connector(由 Akeneo 提供,免费)
- 在 Akeneo 中配置 Connection(位于 Connect → Connections 下),并授予 Products、Media files 和 Attribute options 的读取权限
- 在连接器设置中,将 Akeneo 渠道/区域语言映射到 Shopify 市场(例如,Akeneo
en_US作用域 → Shopify 默认语言) - 将 Akeneo 属性代码映射到 Shopify 字段:
name→ Shopify 产品标题description→ Shopify 正文 HTMLprice→ Shopify 变体价格- 自定义属性 → Shopify 产品元字段(在 Shopify 管理后台的 Settings → Custom data 下配置)
- 运行一次初始全量同步,然后安排增量同步——连接器仅拉取
updated > last_sync_at的产品
验证同步:
- 在 Shopify 管理后台打开一个产品,检查标题、描述和图片是否与 Akeneo 中的内容一致
- 在 Shopify 产品页面的 Metafields 部分检查元字段
WooCommerce
使用 REST API 将 Akeneo 同步到 WooCommerce:
开源 Akeneo WooCommerce Connector(可在 github.com/akeneo/woocommerce-connector 获取)可作为起点,但许多商家会编写自定义同步脚本:
-
设置一个 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 产品
- 使用
-
将 Akeneo 字段映射到 WooCommerce 字段:
- Akeneo
name(en_US)→ WooCommercename - Akeneo
description→ WooCommercedescription - Akeneo
images→ 下载并上传到 WordPress 媒体库,然后设置为 WooCommerce 产品图片 - Akeneo 自定义属性 → WooCommerce 产品属性或 ACF 自定义字段
- Akeneo
-
每次同步后,通过 WP-CLI 执行
wp transient delete-all清除 WooCommerce 的瞬态缓存,确保更新后的产品立即显示
BigCommerce
使用 BigCommerce 连接器连接 Akeneo:
- 在 Akeneo Marketplace 中安装 BigCommerce Connector,并配置 BigCommerce API 凭据(从 Advanced Settings → API Accounts 获取 Client ID、Client Secret 和 Access Token)
- 在连接器配置中,将 Akeneo 品类映射到 BigCommerce 产品类型
- 配置属性映射:
- Akeneo 属性 → BigCommerce 自定义字段或变体选项集
- Akeneo 分类 → BigCommerce 分类树
- 在连接器设置中安排定期增量同步
自定义 / 无头
连接 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