关于 ce-catalog
为使用 Commerce Engine storefront SDK 目录方法的技能指南,涵盖商品与变体查询、搜索、定价、库存及店铺展示模式,面向构建电商体验的开发者。
主要功能
- 01列出商品、变体和 SKU
- 02按筛选、排序与分面搜索
- 03获取商品详情和评价
- 04处理变体选项与 URL 状态
- 05处理库存和客户组定价
适用场景
- 01构建带筛选的商品列表页
- 02实现商品详情与变体选择
- 03展示库存状态和价格
- 04在店铺中加入商品评价
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ commercengine/ skills --skill ce-catalog安装后在 Claude Code 中输入/ce-catalog确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.claude-plugin/plugin.json.cursor-plugin/plugin.jsonreferences/pdp-option-model.md详细介绍
LLM 文档头部:所有发送到
https://llm-docs.commercengine.io的请求都必须包含Accept: text/markdown请求头(或在 URL 路径末尾添加.md)。否则,响应会返回 HTML,而不是可解析的 Markdown。
产品与目录
前置条件:已初始化 SDK 并完成匿名身份验证。请参阅
setup/。
快速参考
| 任务 | SDK 方法 |
|---|---|
| 列出产品 | sdk.catalog.listProducts({ page, limit, category_id }) |
| 获取产品详情 | sdk.catalog.getProductDetail({ product_id }) |
| 列出变体 | sdk.catalog.listProductVariants({ product_id }) |
| 获取变体详情 | sdk.catalog.getVariantDetail({ product_id, variant_id }) |
| 列出 SKU(扁平列表) | sdk.catalog.listSkus() |
| 列出分类 | sdk.catalog.listCategories() |
| 搜索产品 | sdk.catalog.searchProducts({ query: searchTerm, filter?, sort?, facets? }) |
| 获取评价 | sdk.catalog.listProductReviews({ product_id }) |
| 提交评价 | sdk.catalog.createProductReview({ product_id }, { ... }) |
| 相似产品 | sdk.catalog.listSimilarProducts({ product_id }) |
| 向上销售产品 | sdk.catalog.listUpSellProducts({ product_id }) |
| 交叉销售产品 | sdk.catalog.listCrossSellProducts({ product_id }) |
产品层级关系
理解产品 → 变体 → SKU 之间的关系至关重要:
Product (has_variant: false)
└─ A simple product with one SKU, one price
Product (has_variant: true)
├─ Variant A (Color: Red, Size: M) → SKU: "RED-M-001"
├─ Variant B (Color: Red, Size: L) → SKU: "RED-L-001"
└─ Variant C (Color: Blue, Size: M) → SKU: "BLU-M-001"
| 概念 | 返回类型 | 说明 | 适用场景 |
|---|---|---|---|
| 产品 | Product | 包含嵌套 variants 数组的基础商品 | 适用于希望每个产品显示一张卡片的 PLP(例如,显示颜色/尺码选择器的“T 恤”卡片) |
| 变体 | — | 特定的选项组合(颜色 + 尺码) | PDP、购物车商品——通过 listProductVariants() 访问,或从 Product 中嵌套获取 |
| SKU/商品 | Item | 可直接销售的扁平商品单位——每个变体都是一条独立记录 | 适用于希望显示扁平网格的 PLP(每种颜色/尺码组合对应一张卡片),或任何带有筛选、排序或搜索功能的页面 |
决策树
User Request
│
├─ "Show products" / "Product list"
│ ├─ With filters/sorting/search? → sdk.catalog.searchProducts({ query, filter, sort, facets })
│ │ → Returns Item[] (flat SKUs) + facet_distribution + facet_stats
│ ├─ Flat grid (no filters)? → sdk.catalog.listSkus()
│ │ → Returns Item[] (flat SKUs)
│ └─ One card per product (group variants)? → sdk.catalog.listProducts()
│ → Returns Product[] (with nested variants)
│
├─ "Product detail page"
│ ├─ sdk.catalog.getProductDetail({ product_id })
│ └─ If has_variant → sdk.catalog.listProductVariants({ product_id })
│
├─ "Search" / "Filter" / "Sort"
│ └─ sdk.catalog.searchProducts({ query, filter, sort, facets })
│ → Returns Item[] + facet_distribution + facet_stats
│
├─ "Categories" / "Navigation"
│ └─ sdk.catalog.listCategories()
│
├─ "Reviews"
│ ├─ Read → sdk.catalog.listProductReviews({ product_id })
│ └─ Write → sdk.catalog.createProductReview({ product_id }, body)
│
└─ "Recommendations"
├─ Similar → sdk.catalog.listSimilarProducts()
├─ Upsell → sdk.catalog.listUpSellProducts()
└─ Cross-sell → sdk.catalog.listCrossSellProducts()
关键模式
产品列表页(PLP)
对于带筛选、排序或搜索功能的 PLP,请使用 searchProducts(推荐)。它会返回 Item[](扁平的 SKU 列表),以及用于构建筛选 UI 的 facet_distribution 和 facet_stats:
const { data, error } = await sdk.catalog.searchProducts({
query: "running shoes",
filter: "pricing.selling_price 50 TO 200 AND categories.name = footwear",
sort: ["pricing.selling_price:asc"],
facets: ["categories.name", "product_type", "tags"],
page: 1,
limit: 20,
});
// data.skus → Item[] (flat list — each variant is its own record)
// Each Item includes product_slug and variant_slug for building SEO-friendly URLs
// data.facet_distribution → { [attribute]: { [value]: count } }
// data.facet_stats → { [attribute]: { min, max } } (e.g. price range)
// data.pagination → { page, limit, total, total_pages }
// filter also accepts arrays — conditions are AND'd:
// filter: ["product_type = physical", "rating >= 4"]
//
// Nested arrays express OR within AND:
// filter: ["pricing.selling_price 50 TO 200", ["categories.name = footwear", "categories.name = apparel"]]
对于不带筛选功能、且希望每个产品显示一张卡片的 PLP(变体归入同一张卡片):
const { data, error } = await sdk.catalog.listProducts({
page: 1,
limit: 20,
category_id: ["cat_123"], // Optional: filter by category
});
// data.products → Product[] (each product may contain a variants array)
// Check product.has_variant to know if options exist
对于不带筛选功能的扁平网格(每个变体显示为单独的卡片):
const { data, error } = await sdk.catalog.listSkus();
// Returns Item[] — same flat type as searchProducts
// Each Item includes product_slug and variant_slug for building SEO-friendly URLs
产品详情页(PDP)
const { data, error } = await sdk.catalog.getProductDetail({
product_id: "blue-running-shoes", // Accepts product ID or slug
});
const product = data?.product;
// Prefer product.variants from getProductDetail.
// Fetch separately only if variants are missing or you specifically need the variants endpoint.
if (product?.has_variant && (!product.variants || product.variants.length === 0)) {
const { data: variantData } = await sdk.catalog.listProductVariants({
product_id: product.id,
});
// variantData.variants contains all options with pricing and stock
}
从 PDP 将产品添加到购物车
在 PDP 上确定变体后,请使用 getProductDetail 响应中的 product.id 和 variant.id:
// product = data.product from getProductDetail()
// resolvedVariant = the variant matched from product.variants via option selection
if (product.has_variant) {
// Variant product — must specify both product_id and variant_id
await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: product.id, variant_id: resolvedVariant.id, quantity: 1 }
);
} else {
// Simple product — variant_id is null
await sdk.cart.addDeleteCartItem(
{ id: cartId },
{ product_id: product.id, variant_id: null, quantity: 1 }
);
}
使用 Hosted Checkout 时,请改用 addToCart:
const { addToCart } = useCheckout();
addToCart(product.id, product.has_variant ? resolvedVariant.id : null, 1);
PDP 的规范变体 URL 状态
对于变体商品,使用选项查询参数作为唯一事实来源:?size=large&color=blue。
- 查询键应为普通的
option.key值(不使用自定义前缀)。 - 按
variant_options顺序渲染选项组(不要通过遍历变体来推导选项组)。 - 仅当所有选项键都与
variant.associated_options匹配时,才匹配变体。 - 在比较前以一致的方式规范化选项值:
color→ 使用option.value.name进行比较(使用option.value.hexcode显示色板 UI)single-select→ 使用option.value进行比较
variant查询参数是可选的派生状态:- 当选项解析为一个变体时,设置或更新该参数。
- 当选择不完整或无效时,移除该参数。
- 如果 URL 中只有
variant,则从该变体回填选项参数。 - 如果 URL 中有部分选项和有效的
variant,则仅从该变体填充缺失的选项。 - 如果
variant无效,则回退到默认变体(is_default,否则使用第一个变体)。
- 只有在所需选项均已选中且解析出的变体可购买(
stock_available或backorder)时,才启用“加入购物车”。 - 禁用在当前部分选择下无法导向可购买变体的选项值。
直接使用规范的 SDK 类型:
import type {
AssociatedOption,
Product,
Variant,
VariantOption,
} from "@commercengine/storefront";
不要从 OpenAPI 架构推导这些应用级类型。
参考实现:
references/pdp-option-model.md(URL 状态 + 变体匹配 + 属性/variantOption UI 统一)
Attribute 与 VariantOption(PDP 一致性)
将 variant_options 视为驱动变体的属性键(通常为 single-select 和 color),而不是完全独立的展示类别。
- 非变体商品仍可为相同的键提供
ProductAttribute值。 - 即使商品没有变体,品牌也可能希望 PDP 中这些键采用相同的 UI 样式。
- 使用一个经过规范化的选项展示模型:
- 变体商品:从
variant_options+variant.associated_options获取可选选项。 - 非变体商品:从
attributes中读取品牌希望以选项样式展示的键/类型,并以只读方式显示选项组。
- 变体商品:从
- 如果变体商品上的
attribute.key与variant_option.key重叠,则仅渲染一次 variant-option UI,避免重复显示属性行。 - 将
attribute.key对齐以及属性type(single-select/color)作为主要信号。 - 购物车规则保持严格:只有变体商品在“加入购物车”前需要解析变体。
商品类型
| 类型 | 描述 | 关键字段 |
|---|---|---|
physical | 需要配送的有形商品 | shipping、inventory |
digital | 可下载或虚拟商品 | 无需配送 |
bundle | 组合销售的多个商品 | 包含子商品 |
库存与可售状态
stock_available—— 布尔值,在 Product、Variant 和 SKU 架构中始终存在。值为false时,用它禁用“加入购物车”或显示“缺货”。backorder—— 布尔值,在 Admin 中按商品设置。值为true时,即使缺货也接受订单。如果业务允许延期交货,则在stock_available为false但backorder为true时,保持按钮启用。- 库存数量 —— 商品目录 API(
listProducts、listSkus等)支持在响应中包含库存数据。使用此数据在 UI 中显示库存数量。
客户群组与定价
面向 B2B 商店的高级功能,适用于管理员已配置客户群组(例如零售商、备货商、分销商)的情况。在 API 请求中发送 customer_group_id 后,返回的商品列表、定价和促销信息将针对该特定群组。
不要在每次调用时传递此请求头。 在 SDK 配置中通过 defaultHeaders 设置一次(参见 setup/ §“默认请求头”)。用户登录后,使用其群组 ID 更新 SDK 实例——后续所有 SDK 调用都会自动包含该 ID。
愿望清单
Commerce Engine 支持通过 SDK 方法管理愿望清单(添加、移除、获取)。这些技能涵盖主要的店面流程;有关愿望清单和其他次要功能,请参阅 LLM API 参考 或 CE 文档。
常见陷阱
| 级别 | 问题 | 解决方案 |
|---|---|---|
| CRITICAL | 使用 listProducts() 构建带筛选条件的 PLP | 使用 searchProducts({ query, filter, sort, facets })——它会返回 data.skus(Item[])+ data.facet_distribution + data.facet_stats。listProducts() 返回不含 facet 的 Product[]。使用 Meilisearch 筛选语法(例如 "rating > 4 AND product_type = physical")。 |
| CRITICAL | 混淆 Product 和 Item 类型 | listProducts() 返回 Product[](分组数据,包含 variants 数组)。listSkus() 和 searchProducts() 返回 Item[](扁平数据——每个 variant 都是独立记录)。 |
| CRITICAL | 将 variant 用作多选项商品 PDP URL 的唯一真实来源 | 将选项参数(size、color 等)作为规范来源;variant 仅用于派生或回填。 |
| HIGH | 仅根据单个选项或 variant_options 来解析 variants | 根据 所有 选项键匹配 variant.associated_options。 |
| HIGH | 在应用代码中从生成的 schema 推导选项/variant 类型 | 直接从 @commercengine/storefront 导入 SDK 类型(AssociatedOption、VariantOption、Variant、Product)。 |
| MEDIUM | 将 attributes 和 variant_options 视为互不相关的 PDP UI | 构建统一的选项展示模型,使共享键(例如 metal)能在 variant 和非 variant 商品中保持一致的呈现方式。 |
| HIGH | 忽略 has_variant 标志 | 尝试访问 variant 数据前,务必检查 has_variant。 |
| HIGH | 将商品而非 variant 加入购物车 | 当 has_variant: true 时,必须添加特定的 variant,而不是商品。 |
| MEDIUM | 未使用 slug 作为 URL | 使用 slug 字段构建适合 SEO 的 URL——目录端点中的 product_id 参数既接受 ID,也接受 slug。 |
| MEDIUM | 缺少分页 | 所有列表端点都会返回 pagination——使用 page 和 limit 参数。 |
| LOW | 重复获取分类 | 分类很少变化,可在客户端缓存。 |
另请参阅
setup/- 必须先初始化 SDKcart-checkout/- 将商品加入购物车orders/- 订单场景中的商品ssr-patterns/- 商品页面的 SSG/预渲染(Next.jsgenerateStaticParams()、TanStack Start 预渲染)