# Research: 商品规格移植

**Feature**: 013-food-spec | **Date**: 2026-07-15

本研究基于对源项目 `cte_server` 规格功能与目标项目 `foodie_server` 商品体系的代码勘察，记录关键技术决策。无 NEEDS CLARIFICATION（移植任务，源代码即参考答案）。

## 决策 1：数据模型 — 规格加价模型（非笛卡尔积 SKU）

- **Decision**: 采用"规格组 + 规格值 + 商品-规格关联"三表模型；规格值带"加价"，顾客选规格后加价累加到商品基础价。
- **Rationale**: 源项目 cte_server 即此模型，需求是"移植"；满足餐饮/夜市"甜度/加料/辣度"场景；实现简单，无需笛卡尔积组合管理。
- **Alternatives**: 电商式 SKU（每规格组合独立价格/库存/编码）——拒绝：当前无独立库存/编码需求，且与现有 specPrice/otherPrice 加价口径冲突，复杂度不划算。

## 决策 2：规格归属 — 门店级共享

- **Decision**: 规格组按门店(`md_id`) + 语言(`language`)维度管理，本门店多商品复用。
- **Rationale**: 与源项目一致；餐饮场景同一门店的"甜度"规格被多杯饮品复用，避免每商品重复录入。
- **Alternatives**: 商品级独立规格——拒绝：录入成本高、无复用价值。

## 决策 3：food_sku JSON 与结构化关联双写

- **Decision**: 保留 `pos_food.food_sku` JSON 字段，与 `food_spec_relation` 结构化关联双写；查询时以结构化关联为权威，`food_sku` 作兼容冗余。
- **Rationale**: foodie 现有前端与订单链路（`PromotionCalc.specPrice`、`PosOrder.food` 快照 `otherPrice`）读取 `food_sku` JSON；直接废弃会破坏存量链路。双写保证渐进兼容。
- **Alternatives**: 仅结构化关联、废弃 JSON——拒绝：需同步改前端 + 订单读取，范围过大。

## 决策 4：命名统一

- **Decision**: 消除源项目命名不一致（其 `foodSku` JSON 内规格值明细叫 `objects`，`foodSpecs` 内叫 `foodSpecsItems`）。foodie 移植版统一：结构化规格 `FoodSpecs.foodSpecsItems` 用 `foodSpecsItems`；`food_sku` JSON 内明细若 foodie 前端已有约定则从之，否则统一为 `foodSpecsItems`。最终命名在 [contracts/api.md](contracts/api.md) 明确，前后端对齐。
- **Rationale**: 源项目命名不一致是已知坑，移植时必须消除，否则前后端字段对不上。
- **Alternatives**: 照搬两套命名——拒绝：维护负担大。

## 决策 5：价格类型与口径统一

- **Decision**: 规格值加价 `price` 与商品 `price` 同口径。foodie `PosFood.price` 为 BigDecimal（DB decimal），故规格加价列也用 decimal/BigDecimal，**不照搬源项目的 Long(分)**。下单实付单价 = 商品基础价 + 所选规格值加价之和；该和即写入订单快照 `otherPrice` 与促销 `specPrice`。
- **Rationale**: 保持与 foodie 现有价格体系一致，避免单位换算 bug；统一 otherPrice/specPrice/规格加价三国口径。
- **Alternatives**: 用整数分——拒绝：与商品价类型不一致，引入换算风险。

## 决策 6：后台通道统一收口（FR-011）

- **Decision**: 一并修复后台 `add`/`edit`（PosFoodController 后台段）不写规格的问题，使两条保存通道行为一致；抽取公共的"保存规格关联 + 写 food_sku"逻辑供 `setposfood` 与 `add`/`edit` 复用。
- **Rationale**: spec FR-011 要求；否则后台编辑商品会丢规格，造成数据不一致。
- **Alternatives**: 仅改商家端 setposfood——拒绝：留隐患。

## 决策 7：查询性能 — 批量注入避免 N+1

- **Decision**: 商品列表注入规格采用"一次查该批商品所有关联 → 一次查相关规格组 → 一次查相关规格值 → 内存分组"，不逐商品查库（沿用源项目模式）。
- **Rationale**: 列表接口商品数多，逐条查会 N+1。
- **Alternatives**: 逐商品查——拒绝：性能差。

## 依赖与集成

- 技术栈均为 foodie 既有，无需引入新依赖。
- 唯一需对齐的集成点：订单快照 `otherPrice`、促销 `specPrice` 的取值来源（决策 5 已统一为"所选规格值加价之和"）。
