# 甲方新需求文档（2026-04-15）

## 来源

2026-04-15 下午，甲方补充需求，已与大头确认。

---

## 需求内容

### 需求 1：多座位单订单
一个订单可包含多个座位，每个座位生成独立核销码。

**技术要求**：
- 每个座位 = 一个 ShopXO SKU（spec_base_id），stock=1
- 一次购买多个不同座位 = 多个 goods_params 条目（同一 goods_id + 不同 spec_base_id）
- ShopXO 原生支持多 spec_base_id 同单购买，各生成独立 order_goods 行 ✅

### 需求 2：核销码卡夹展示（订单详情页）
订单详情展示多个 QR 核销码，交互如下：
- 手动滑切换（类似轮播，但手动）
- 每个 QR 独立状态：已核销 → 灰掉
- 自动切换到下一张未核销的 QR
- 买了 N 个座位 → 显示 N 个 QR

**技术要求**：
- 多行 `order_goods` → 多张 `vr_tickets` QR
- 前端轮播组件（uni-app）
- Realtime 订阅：核销状态变更 → 前端自动更新

### 需求 3：商品级必填信息配置
商品 `ext` 字段声明购买时用户需填写的必填信息。

**字段设计（建议）**：
```json
{
  "required_fields": ["id_card", "phone"],
  "field_labels": {
    "id_card": "身份证号",
    "phone": "手机号"
  }
}
```

**逻辑**：
- `ext` 为空 → 下单不弹窗，直接购买
- `ext` 有内容 → 弹窗要求填写，填写后附在订单备注

### 需求 4：手机号自动填充（订单级）
- 默认自动填微信认证手机号（wx.getPhoneNumber），可编辑
- 手机号是**购买凭据**（售后定位用）
- **同一订单多个座位/核销码 → 只需填一份联系信息**（订单级，非座位级）

---

## ShopXO 多 Spec_base_id 同单购买验证

### 源码分析结论

**问题**：ShopXO 能否在同一次购买中，用同一个 `goods_id` + 不同 `spec_base_id`，各买 1 个？

**结论：✅ 支持**

### 源码证据

**BuyService.php 关键路径**：

```
goods_params = [
  { goods_id: 112, spec_base_id: 1001, stock: 1 },  ← 座位A1
  { goods_id: 112, spec_base_id: 1002, stock: 1 }   ← 座位B2
]
```

1. **BuyGoods()**：`foreach($params['goods_data'] as $v)` → 每个 goods_params 条目 → 一个 `$data[]` 元素
2. **OrderSplitService::Run()**：按 warehouse 分组（非 goods_id 合并）→ 不同 spec_base_id 保留为不同 goods_items[]
3. **OrderInsert()**：`foreach($v['goods_items'] as $vs)` → 每个 goods_items 条目 → **一行 order_goods**

```php
// BuyService.php:786
foreach($v['goods_items'] as $vs)
{
    $order['detail_data'][] = [
        'goods_id'    => $vs['goods_id'],
        'price'       => $vs['price'],
        'buy_number'  => intval($vs['stock']),  // = 1
        // ...
    ];
}
```

**结果**：
- goods_id=112, spec_base_id=1001 → order_goods 第1行（座位A1）
- goods_id=112, spec_base_id=1002 → order_goods 第2行（座位B2）
- 两个座位，同一订单，各生成独立 vr_tickets QR ✅

### 与需求对应关系

| 甲方需求 | 技术实现 | 状态 |
|---------|---------|------|
| 多座位单订单 | 每座位 = 独立 spec_base_id，stock=1 | ✅ |
| 多核销码 | 多行 order_goods → 多张 vr_tickets QR | ✅ |
| ext 必填字段 | extension_data.required_fields | ✅ |
| 手机号订单级 | 联系信息挂 order 备注，非 goods_params | ✅ |

---

## 当前数据库状态（已验证）

```sql
-- 商品 112 票务商品
is_exist_many_spec = 0   -- ShopXO 认为无多规格
spec_base 表 = 空的      -- 没有任何 SKU

-- vr_seat_templates.spec_base_id_map
-- {"A": 1001, "B": 1002, "C": 1003} ← 这些 ID 在 DB 里不存在！
```

**问题**：ShopXO 防超卖机制完全未启用，购买走裸商品逻辑。

---

## spec_value 绑定方案（$vr- 前缀）

### 方案已确认

ShopXO spec name 允许特殊字符（$，-，中文），无字符过滤。

### 插件专用规格命名

```
$vr-场馆   → 场馆名称（如 $vr-场馆 = "鸟巢"）
$vr-分区   → 座位分区（Zone）
$vr-时段   → 场次时间
```

### 为什么不会与用户规格冲突

- 插件票务商品使用自定义模板 `ticket_detail.html`
- 前端 UI 不走 ShopXO 默认规格选择器
- 用户无法通过默认界面触碰到 `$vr-` 规格

---

## 方案 A（每个座位一个 SPEC）兼容性

**结论：方案 A 完全兼容甲方全部 4 项新需求**

| 需求 | 方案 A 如何满足 |
|-----|---------------|
| 多座位单订单 | 每座位 = SKU，ShopXO 原生支持多 SKU 同单 ✅ |
| 核销码卡夹 | order_goods × N → vr_tickets × N → N 张 QR ✅ |
| ext 必填字段 | goods.extension_data.required_fields ✅ |
| 手机号订单级 | 联系信息不写在 goods_params，写在 order 备注 ✅ |

---

## 待办事项

- [ ] Issue #9：方案 A vs B 最终决策
- [ ] 紧急修复：is_exist_many_spec → 1 + 正确生成每个座位的 SKU
- [ ] 后台批量创建 SKU 实现（方案 A 关键路径）
- [ ] ext.required_fields 前端弹窗实现
- [ ] 订单详情核销码卡夹组件
- [ ] 微信手机号自动填充 API 集成
