mcpField
C
ce-catalog

ce-catalog

github.com/commercengine其他065 次安装9 周前更新
代码仓库

关于 ce-catalog

为使用 Commerce Engine storefront SDK 目录方法的技能指南,涵盖商品与变体查询、搜索、定价、库存及店铺展示模式,面向构建电商体验的开发者。

主要功能

  1. 01列出商品、变体和 SKU
  2. 02按筛选、排序与分面搜索
  3. 03获取商品详情和评价
  4. 04处理变体选项与 URL 状态
  5. 05处理库存和客户组定价

适用场景

  1. 01构建带筛选的商品列表页
  2. 02实现商品详情与变体选择
  3. 03展示库存状态和价格
  4. 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.json
references/
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/ - 必须先初始化 SDK
  • cart-checkout/ - 将商品加入购物车
  • orders/ - 订单场景中的商品
  • ssr-patterns/ - 商品页面的 SSG/预渲染(Next.js generateStaticParams()、TanStack Start 预渲染)

文档

评论

支持 Markdown