# 二期 v3：新建订单向导「选主题/选产品」接通真实接口（替换 mockProducts.js）

> **服务**: hl-product-service-v2（+ hl-order-service-v3 内部统计）
> **PR**: #3155
> **Issue**: #3142
> **日期**: 2026-05-28
> **影响**: 🟡 新建订单页 `order-v2/new` 第 1 步「选主题」可改用真接口；第 2 步「选产品」用已有接口；地区筛选删除

---

## ⚠️ 关键变化（前端 mmg 必读）

`order-v2/new` 第 1、2 步当前用 `src/views/order-v2/new/_shared/mockProducts.js` 写死假数据。现可替换为真实接口。

### 1. 第 1 步「选主题」— 新增接口

```
GET /admin/product/line/order-picker
```
- 无 query 参数，**不分页**，按排序号升序返回
- 仅返回「**启用(ACTIVE) + 在售(该产品线下挂≥1个已上架产品)**」的主题
- **数据权限**：和产品管理一致——超管看全部、核心(CORE)/小蒙马(GROUP)主题全员可见、私人定制(CUSTOM)仅创建人本人可见。**地区(region)筛选不需要,产品线无此字段,请删掉那排 chips。**

返回结构：
```jsonc
{
  "code": 200,
  "data": [
    {
      "lineId": 2056937785918369794,   // 主题ID(雪花,前端按字符串透传)
      "name": "测试核心产品",            // 主题名
      "description": "...",            // 描述(原 mock 的 desc/tagline 都用它)
      "coverImageUrl": "https://...",  // 真实封面图(替代 mock 的 emoji cover + coverColor)
      "productType": "CORE",           // CORE=核心 / GROUP=小蒙马
      "skuCount": 6,                   // 该主题下已上架产品的档位总数(原 mock 的 skuCount)
      "fromPrice": 3105.00,            // 起价/人(已上架产品最低成人起价;未设价为 null,请显示 ¥-- 或灰)
      "hot": true                     // 热门标识 = 该主题近90天下单量较多(前30%且>0)
    }
  ]
}
```

字段映射建议（mock → 真接口）：
- `id` → `lineId`（字符串透传，勿 Number()）
- `cover`/`coverColor`（emoji 装饰）→ 改用 `coverImageUrl` 真实图；如仍要色块可前端按 lineId 取色
- `region`/`regionKey` → **删除**（无后端字段，地区 chips 去掉）
- `tagline` → 并入 `description` 或省略
- `skuCount` / `fromPrice` / `hot` → 同名直取
- 顶部统计「在售主题数」= 列表长度；「SKU总数」= Σ`skuCount`；「最低人均」= min(`fromPrice`)

### 2. 第 2 步「选产品」— 新增专用接口（PR #3164 / #3159，已测试服验证）

> 注：原先说复用 `/admin/product/item/list`，但 SKU 卡还要「热销」「发团日期」两个字段，通用列表没有；为不拖慢产品管理列表，**改为新建专用选产品 picker**（与选主题对称）。**前端 Step2 改调下面这个**，不要用 `/item/list`。

```
GET /admin/product/item/order-picker?lineId={lineId}&page=1&pageSize=20&keyword=
```
- `lineId` 必填（选定主题的 `lineId`）；只返该产品线**已上架**产品+档位；返回 `PageResult<OrderPickerProductVO>`（`data.records[]` + `data.total`）
- 数据权限同选主题（@DataScope：超管全部 / CORE·GROUP 全员 / CUSTOM 仅本人）
- 下单提交的 `productId + tierSeq` 从这里取（雪花 `productId` 按字符串透传，勿 Number()）

返回 `records[]` 实测字段（与 Step2 SKU 卡片元素映射）：
```jsonc
{
  "productId": 2043595351268519937,  // 产品ID(字符串透传)
  "name": "6天5晚旷野版",              // 卡片大标题
  "subtitle": "装甲车穿越·帐篷营地…",   // 副标题
  "tags": ["亲子","露营"],            // 标签行
  "tripDays": 6, "tripNights": 5,    // "6天5晚"
  "coverImageUrl": "https://...",
  "startPrice": 10.00,               // 产品级最低起价(可能 null → 显示 ¥--)
  "tierPrices": [                    // 档位数组(档次过滤 chips = 去重 tierName)
    {"tierSeq":1, "tierName":"舒适", "tierDescription":"…", "startPrice":10.00},
    {"tierSeq":2, "tierName":"豪华", "tierDescription":"…", "startPrice":null}
  ],
  "hot": true,                       // 【新】热销徽标 = 该产品近90天下单量较多(本主题内前30%且>0)
  "nextSaleDate": "2026-07-01"       // 【新】发团日期 = 价格日历/班期里最近的未来可售日(无可售为 null)
}
```

**SKU 卡渲染**：mock 里每张卡=一个产品的一个档位；真实是一个产品(`productId`)含多档位(`tierPrices[]`)。
建议按「产品 × 档位」平铺成卡（每个 `tierPrices[i]` 一张卡），卡上：
- 档位标签 ← `tierPrices[i].tierName`（"尊享档/挑战档"）
- 价格 ← `tierPrices[i].startPrice`（**null 显示 ¥-- / 灰**，别显示 0）
- 「热销」徽标 ← 产品级 `hot`（同一产品的各档位卡都显示）
- 「发团日期」← `nextSaleDate`（最近可售日；null 则不显示该行）
- 提交带 `productId` + `tierPrices[i].tierSeq`

### 3. 提交（不变）
`POST /v3/admin/order`，入参 `productId` + `tierSeq` 等，已就绪。

---

## 验证
测试服 `web.test.1814.love:9443` 真 admin token 实测：
- 选主题 `line/order-picker`：返 9 个在售主题(库内 ACTIVE 29 → 在售 9 精确吻合)，字段齐全，主题级 hot 跨服务统计生效
- 选产品 `item/order-picker`：HTTP 200，字段齐全；产品级 `hot`（热门线下 4 产品命中 1 个 hot=true）、`nextSaleDate`（价格日历最近可售日，如 2026-07-01）均生效
