mcpField
S
spree-data-model

spree-data-model

github.com/spree数据与数据库10122 次安装4 周前更新
代码仓库

关于 spree-data-model

spree-data-model 是面向 Spree 开发者的模型参考技能,梳理目录、订单、结账、商店、频道和市场之间的关系,并指出影响查询与 API 使用的实现细节。

主要功能

  1. 01目录到订单的模型关系
  2. 02商店、频道与市场结构
  3. 03结账与支付模型概览
  4. 04API 前缀 ID 约定
  5. 05状态字段与请求上下文说明

适用场景

  1. 01追踪商品到购物车和订单的模型关系
  2. 02按销售频道查询商品
  3. 03区分进行中的购物车与已完成订单
  4. 04在代码中使用模型 ID 和请求上下文

安装

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

$npx skills add https://github.com/spree/agent-skills --skill spree-data-model

安装后在 Claude Code 中输入/spree-data-model确认已加载。

详细介绍

Spree 数据模型

最常被问及的 Spree 模型关系图。字段级文档位于已安装的 @spree/docs 包中的 node_modules/@spree/docs/dist/developer/core-concepts/。

商品目录 → 购物车流程

Product → Variant → LineItem → Order
  • Product 是品牌级实体(名称、slug、描述、类别)。
  • Variant 是可售 SKU。每个 Product 至少有一个 Variant。Variant 包含 SKU、价格、尺寸,并关联库存。
  • LineItem 将 Variant 关联到 Order,包含 quantity,并在添加时冻结价格。
  • Order 是客户的交易记录——进行中的购物车,以及结账后的已完成交易(同一条记录,不同的 state)。

Variant 通过 StockItem(每个 Variant 在每个 StockLocation 中各有一个)和 StockMovement 历史记录关联到库存。

主变体与默认变体

Product 有一个 master variant(旧版概念,is_master: true),以及一个计算得出的 default_variant 方法:当 Spree::Config[:track_inventory_levels] 开启时,返回第一个可购买的 variant;否则返回按 position 排序的第一个非 master variant;只有当商品没有其他 variant 时,才会使用 master 作为后备。product.default_variant_id 只会返回该计算所得 variant 的 id。在 5.5 中,两者都不是数据库列——不要针对 default_variant_id 进行查询或迁移(计划在 6.0 中为 spree_products 添加 default_variant_id 外键,但尚未开始实现;参见 docs/plans/6.0-remove-master-variant.md)。非 master 的可售变体请使用 product.variants;只有确实需要包含 master 行时,才使用 product.variants_including_master。

多渠道/多商店维度

Store → Channel → ProductPublication → Product

自 Spree 5.5 起提供。

  • Store 是顶层品牌(通常一个组织对应一个 Store)。
  • Channel 是 Store 内的销售界面:在线商店、线下 POS、市场集成(Amazon、eBay)、B2B 批发、移动应用。每个 Store 至少有一个名为 "Online Store" 的默认 Channel。
  • ProductPublication 是关联记录:指定哪些 Product 在哪些 Channel 上可见,并可选用 published_at / unpublished_at 时间窗口进行排期。
  • Order 有 channel_id,因此可以按渠道归属收入。

Store API 会根据每个请求中的 X-Spree-Channel 标头解析渠道(与 channels.code 或带 ch_… 前缀的 ID 匹配);如果没有该标头,则使用该商店的默认渠道。Admin API 不会读取 X-Spree-Channel——管理端查询会返回当前商店所有渠道的数据。

Markets(区域配置)

Market has_many :countries
Market  columns:  currency (string), default_locale (string)
Order belongs_to :market

Market 是一种区域配置,包含其国家/地区集合、货币和默认语言区域。通常会在创建 Store 时(已知默认国家/地区的情况下)自动创建默认 Market,但 Market 并非必需——请检查 store.has_markets?;如果没有 Market,货币和语言区域会回退到商店级默认值。Order 会在某个 Market 中创建——它决定客户看到的货币以及适用的税务规则。

完整的 Market 文档请参阅 node_modules/@spree/docs/dist/developer/core-concepts/markets.md。

购物车与 Order

在 Spree 中,Spree::Order 既表示进行中的购物车,也表示已完成的交易。state 列会记录所处阶段:cart、address、delivery、payment、confirm、complete。可按状态筛选以区分:

Spree::Order.where(state: 'cart')      # in-progress carts
Spree::Order.where(state: 'complete')  # finalized orders
Spree::Order.complete                  # named scope — NOT equivalent: defined as where.not(completed_at: nil), so it matches any order that ever completed checkout, including ones later canceled or returned

Order#token(has_secure_token :token, length: 35)用于在多个请求间标识匿名购物车。已登录用户的购物车通过 user_id 外键归属用户。

结账相关模型

Order → Payment → PaymentMethod
Order → Shipment → ShippingRate → ShippingMethod
Order → Address (bill_address, ship_address)
  • Payment 有自己的状态机(checkout → processing → pending → completed,以及 failed、void 和 invalid)。对应列为 state。
  • Shipment 有自己的状态机(pending → ready → shipped,以及 canceled)。对应列为 state。
  • ShippingRate 是每个 Shipment 提供的选项(例如 UPS Ground $5.99、USPS Priority $8.99)。客户从中选择一项。

客户 / 用户

Spree.user_class (typically Spree::User)
  ↓
Address (many, via spree_addresses)
CreditCard (many)
GiftCard (many)
StoreCredit (many)

使用 Spree.user_class 和 Spree.admin_user_class 引用用户模型,切勿直接使用 Spree::User。应用可以通过配置替换为自己的用户模型。

调整项(多态)

Adjustable (Order, LineItem, Shipment) ← Adjustment

Adjustment 是多态的:它通过 adjustable_type + adjustable_id 关联到任意订单、行项目或配送。每个 Adjustment 都有一个 source(创建它的对象,例如 TaxRate、PromotionAction、ReturnAuthorization 等),并内置了可按来源类型筛选的作用域:

order.adjustments.tax                     # source_type: 'Spree::TaxRate'
order.adjustments.promotion               # source_type: 'Spree::PromotionAction'
order.adjustments.return_authorization
order.all_adjustments                     # adjustments on order + its line_items + shipments

带前缀的 ID

通过 v3 API 暴露的每个 Spree 模型都有类似 Stripe 的带前缀 ID:

product.prefixed_id  # => "prod_86Rf07xd4z"
order.prefixed_id    # => "or_m3Rp9wXz"
variant.prefixed_id  # => "variant_k5nR8xLq"

ID 根据整数主键通过 Sqids 计算得出,不对应数据库列。前缀通过模型上的 has_prefix_id :<prefix> 按类声明。v3 API 在所有位置都接受并返回带前缀的 ID;find_by_prefix_id! 会将它们解析回整数主键。

前缀约定:

  • 部分资源使用较长形式:prod(Product)、variant(Variant)
  • 大多数其他资源使用短代码:or(Order)、py(Payment,与 Stripe 保持一致)、adj(Adjustment)、li(LineItem)、ctg(Category/Taxon)、cus(客户,与 Stripe 保持一致)、ch(Channel)、mkt(Market)

切勿在 API 响应中暴露原始整数主键。

state 与 status(5.5 中混用)

不同模型使用不同的列名,具体取决于它们的引入时间:

  • Order.state、Payment.state、Shipment.state — 较早的状态机
  • OrderApproval.status — 较新的状态列
  • Channel 不使用状态机,而是使用 active 布尔值

编写模型代码时,遵循该模型实际拥有的列所采用的约定。查询时,如果不确定,请查看模型的源代码。

Spree::Current(每个请求的上下文)

避免将店铺 / 货币 / 地区设置作为参数传递。使用当前上下文:

Spree::Current.store      # The store handling this request
Spree::Current.currency   # The currency to display prices in
Spree::Current.locale     # The locale for translations
Spree::Current.channel    # The resolved sales channel (falls back to the store's default channel)
Spree::Current.market     # The resolved market (falls back to the store's default market)

模型、控制器、作业和服务中均可使用。API 上的控制器会通过 before_action 自动设置(Spree::Current 内置了回退到店铺默认值的机制);在需要针对特定店铺的作业和 rake 任务中,则需手动设置。

延伸阅读

  • 字段级文档: 每个模型对应的 node_modules/@spree/docs/dist/developer/core-concepts/<topic>.md。
  • OpenAPI 规范: node_modules/@spree/docs/dist/api-reference/store.yaml 列出了每个 API 字段及其类型,比根据模型源代码猜测更可靠。
  • 添加新模型 / API 资源: 使用 spree-resource 技能。
  • 扩展现有 Spree 模型(通过装饰器添加关联、验证、作用域、方法):使用 spree-decorators 技能。

评论

支持 Markdown