> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maple.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# 菜单模型

> Maple 菜单的结构、如何将您自己的商品目录映射到该结构，以及避免错误假设的规则。

菜单是对顾客在某个门店可以点的所有内容的结构化描述：菜品、定价方式、如何定制，以及适用哪些税费。您以一份 JSON 文档发布菜单，Maple 以**您分配的外部 ID** 作为所有对象的键。本页介绍该模型，以及如何把您自己的商品目录映射到它。[发布菜单](/zh/developer-api/guides/publish-menu)指南介绍请求流程，[菜单示例](/zh/developer-api/guides/menu-recipes)提供完整示例，[接口参考](/zh/developer-api/api-reference)列出所有字段。

## 每个门店一份菜单

每个门店只有**一份**菜单。一个门店不存在多份命名菜单（例如分开的“早餐菜单”和“晚餐菜单”）的概念，不同时段的差异是在这唯一的菜单\_内部\_通过[供应时段](#供应时段与分时段菜单)来表达的。

发布会整体替换这份菜单（以差异方式应用，见[下文](#发布会替换整份菜单)）。Maple 在内部保留版本历史，并始终提供当前版本，但从您这一侧看，模型很简单：**一个门店，一份菜单，由您完整发布。**

<Note>
  如果您自己的系统为每个门店建模了多份菜单，请在发布之前把它们合并成一份 Maple 菜单，通常做法是拆成各自带有供应时段的独立分类。
</Note>

## 树结构

菜单是由三层嵌套对象组成的树，其中引用了共享的定义：

```mermaid theme={null}
flowchart TD
  Cat[Category] --> Item[Item]
  Item --> Var[Variation · carries the price]
  Item -.modifierGroups.-> MG[Modifier group]
  Var -.modifierGroups.-> MG
  MG --> Opt[Modifier option]
  Opt -.nested.-> MG
  Item -.taxCategoryId / taxRateIds.-> Tax[Tax category & rates]
  Item -.feeIds.-> Fee[Fees]
```

* **分类（Category）**：菜单中的一个板块（饮品、主菜）。可以设置分时段，使其只在特定时间窗口内显示。
* **菜品（Item）**：一个菜品。有一个或多个规格，并可携带加料组、税和费用。
* **规格（Variation）**：菜品的一种可购买形态（小杯 / 大杯，或只是“常规”）。**价格在这里。** 每个菜品至少有一个规格。
* **加料组（Modifier group）**：一组带有选择规则的选项（牛奶、加料选项）。可以定义一次后共享，也可以内联在菜品上。选项本身还可以带有嵌套的加料组。

## 映射您自己的商品目录

大多数集成方已经有自己的菜单模型。下表是常见概念的映射方式，一开始就做对可以避免最常见的错误：

| 您的概念 | 映射到 | 说明 |
| - | - | - |
| 菜单板块 / 标题 | **分类（Category）** | 顺序有意义；分类可以设置分时段。 |
| 产品 / 菜品 | **菜品（Item）** | 一个名称和描述；价格不在这里。 |
| 改变基础价格的大小 / 选项（小 / 大） | 菜品的**规格（Variation）** | 不是单独的菜品。每个菜品至少需要 1 个规格，即使只是“常规”。 |
| 加料选项 / 定制（加一份浓缩、不要洋葱） | 加料组中的**加料选项（Modifier option）** | 加料组承载选择规则；选项承载任何价格变化。 |
| 在多个菜品上复用的选项集合（牛奶） | 通过 `groupId` 引用的**共享加料组** | 定义一次，到处引用。 |
| 带子选项的套餐 / 自选组合 | **嵌套加料组** | 选项可以带有自己的加料组。 |

<Warning>
  请避免以下常见的错误假设：价格**不**在菜品上（它在**规格**上）；大小**不是**单独的菜品（它是规格）；每个门店只有**一份**菜单（分时段使用的是供应时段，而不是多份菜单）。
</Warning>

## 外部 ID 归您所有

每个对象（分类、菜品、规格、加料组、选项、税率、费用）都带有一个由**您**分配并拥有的 `externalId`。Maple 绝不会更改它。这是整个模型的支柱：

* 使用相同的 `externalId` 重新发布，会更新同一个对象，而不是创建重复对象。
* 订单行项目通过这些 ID（`menu_entity_id`）引用菜单，因此传入的订单可以直接映射回您的商品目录。
* Maple 也会在响应中生成自己的 ID（`item_…`、`var_…`、`ctg_…`）供参考，但您始终通过自己的 `externalId` 来寻址一切。
* **对象之间同样通过 `externalId` 相互引用。** 当加料组引用使用 `groupId`、菜品通过 `taxRateIds` 指向税率，或促销通过 `discountId` 指定折扣时，这些值都是您分配的 `externalId`，而不是 Maple 生成的 ID。

请使用在您自己系统中稳定的 ID（SKU、主键），而不是可能变化的显示名称。

## 定价

价格是对象，而不是单纯的数字，因此模型可以表达的不止一个固定金额。基本结构是以**整数最小货币单位**表示的金额加上币种：

```json theme={null}
{ "amount": { "amountMinor": 450, "currency": "USD" } }
```

价格对象还支持价格类型（`fixed`、`variable` 开放金额、用于称重商品的 `per_unit`、按数量分档的 `bulk`）、含税定价、按币种覆盖，以及划线价（compare-at）。对于常见情况，即单一币种的固定价格，您只需要 `amount`。请参阅[金额与货币](/zh/developer-api/concepts/money)。

## 加料选项

加料组包含选择规则和一组选项。规则比简单的最小/最大值更丰富，这让您可以表达诸如“最多选 3 个，但同一种不超过 2 个”之类的要求：

| 字段 | 控制内容 |
| - | - |
| `minSelections` / `maxSelections` | 允许的总选择数（按数量计算）。 |
| `minUniqueSelections` / `maxUniqueSelections` | 可选择的\_不同\_选项的数量。 |
| `freeSelectionCount` | 开始收费之前免费的选择数量。 |
| `allowedQuantities` | 每个选项允许的数量。 |
| 选项 `minQuantity` / `maxQuantity` | 单个选项的数量上下限。 |

通过 `groupId` 从菜品或规格中引用共享加料组，或者内联定义一个。在同一个条目中，这两种形式互斥：引用只带 `groupId`；定义则带 `name` 和 `options`。混用会被拒绝，而不是被悄悄截断。选项可以带有自己的嵌套加料组，用于自选组合流程。请参阅[菜单示例](/zh/developer-api/guides/menu-recipes)了解完整示例。

<Note>
  订单资源目前会在每个行项目上展示直接选择的第一级加料选项。更深层的嵌套选择暂时不会展开到订单资源中。
</Note>

## 税费

这些是顶层定义，您通过 ID 将它们挂接到菜品上：

* **税类别和税率**：税率可以是 `percentage`、`flat` 或基于 `bracket`，计税基数可配置，可含税或不含税。通过 `taxCategoryId` / `taxRateIds` 挂接。
* **费用**：附加费和押金（瓶装押金或 CRV），通过 `feeIds` 挂接。
* **折扣、促销和优惠券**：用于降价的可选顶层集合。

大多数菜单只需要一个税率且没有费用；当门店需要时，可以使用更丰富的结构。

## 供应时段与分时段菜单

供应时段是您表达分时段菜单的方式，**它取代了多份菜单的想法。**

* 设置了供应时段的**分类**只会在该时段内显示（早餐供应到 11:00）。
* **菜品**也可以带有自己的时段。
* 没有设置供应时段的对象始终可供应。

每个时段由星期几加本地时间范围组成：

* `dayOfWeek`：`0`（星期日）到 `6`（星期六）。每个供应日添加一个时段。
* `startTime` / `endTime`：本地 `HH:MM`，24 小时制。

时段按**菜单的时区**计算，该时区由顶层的 `timezone` 字段设置（IANA，例如 `America/New_York`）。未设置时默认为 UTC，因此请在首次发布时设置，请参阅[为供应时段设置时区](/zh/developer-api/guides/publish-menu)。

“每日特供”或早餐板块，就是一个设置了相应时段、位于唯一那份菜单中的分类（或菜品）。请参阅[每日特供示例](/zh/developer-api/guides/menu-recipes)。

## 库存状态

库存状态为 `in_stock`、`out_of_stock` 或 `low_stock`。发布时，**省略该字段会保留**当前值；全新的对象默认为 `in_stock`。您可以用它给某个菜品“下架”（86），而无需重新发布整份菜单的结构。

## 发布会替换整份菜单

您始终需要把**整份菜单**作为期望状态发送。Maple 按 `externalId` 以差异方式应用：

* 校验是全有或全无：`400` 会列出所有问题，并且不做任何更改。
* 相同内容的重新发布不会产生任何变化，对象标识保持稳定，因此您可以在每次菜单变化时发布，而不会产生波动。

因此，最简单的集成方式是在您的数据源每次变化时重新发布完整菜单。请参阅[发布菜单](/zh/developer-api/guides/publish-menu)。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.