# 装备建议字段改造 — 条目列表结构化

**日期**: 2026-04-23
**PR**: #1259 (Closes #1244)
**服务**: hl-product-service-v2 / hl-order-service-v2 / hl-mp-service
**类型**: feat（新增字段，向后兼容）

---

## 背景

管理后台「产品编辑 → 补充信息 → 预订须知 → 装备建议」从纯文本 textarea 改为**条目列表**形式。小程序端目标视觉：左图标 + 右文案，一行一条。

**图标责任**：后端只存文本，图标由前端自行实现（硬编码关键词匹配 / 内置图标库 / 顺序样式），后端不提供图标字段。

---

## 字段新增契约

### 数据结构

```typescript
interface EquipmentItem {
  text: string;   // 文案，必填，1-50 字
}
```

- **条目数上限**: 20
- **单条 text 长度**: 1-50 字（非空非空白）
- **text 必填非空**
- 超限后端返 `code=400 message="装备建议不合法：..."`

### 旧字段（保留，降级兜底）

`equipmentAdvice: string`（富文本 HTML 字符串）继续返回，**不删**。

### 降级三态（前端必须实现）

| equipmentList | equipmentAdvice | 前端行为 |
|---------------|-----------------|---------|
| 非空数组 | 任意 | 渲染条目列表（图标前端自行匹配） |
| null / 空数组 | 非空字符串 | 渲染纯文本（可能含 HTML） |
| null / 空数组 | null / 空 | 不渲染"装备建议"模块 |

---

## 涉及接口（5 处响应体变化 + 1 处请求体）

### 1. Admin 保存 `POST /admin/product/supplement/save`

**请求体新增**：
```json
{
  "productId": 123,
  "equipmentList": [
    {"text": "防晒霜 SPF50+"},
    {"text": "长袖防晒衣"},
    {"text": "薄外套（早晚温差15°C+）"}
  ]
}
```

**请求体删除**：`equipmentAdvice` 字段从 `ProductSupplementSaveReqVO` 彻底移除（老 UI 已无入口）。

### 2. Admin 详情 `GET /admin/product/{id}` → `SupplementVO`

**响应新增**：
```json
{
  "supplement": {
    "equipmentAdvice": "<p>老富文本旧字段</p>",
    "equipmentList": [{"text": "防晒霜 SPF50+"}]
  }
}
```

### 3. MP 产品详情 `GET /mp/product/{id}`

**响应新增**：
```json
{
  "equipmentAdvice": "<p>...</p>",
  "equipmentList": [{"text": "防晒霜 SPF50+"}]
}
```

### 4. 订单详情 `GET /mp/order/{id}`

**响应新增** `OrderDetailVO.equipmentList`（下单时固化自产品快照）：
```json
{
  "equipmentAdvice": "...",
  "equipmentList": [{"text": "..."}]
}
```

### 5. 供应清单 `GET /mp/order/{orderId}/supplies-checklist`

**响应新增** `MpSuppliesChecklistRespVO.equipmentList`：
```json
{
  "orderId": 1001,
  "categories": [...],
  "equipmentAdvice": "...",
  "equipmentList": [{"text": "..."}]
}
```

---

## 向后兼容说明

- **老订单快照**：2026-04-23 之前下单的订单，产品快照 JSON 中不含 `equipmentList` 键 → 后端 `path("equipmentList").isArray()` 返 false → `equipmentList` 返 null → 前端按三态降级兜底渲染 `equipmentAdvice`
- **老产品**：未重新保存过的产品 DB 列 `equipment_list` 为 null → 接口返 null
- **零破坏性**：所有响应字段只新增不删除，前端不升级也不会报错

---

## 前端实现要点

1. **管理端**：
   - 移除原 `equipmentAdvice` 富文本输入框
   - 新增"+ 新增装备"条目列表 UI（一行一条，单条文案输入 + 删除按钮）
   - 保存时只传 `equipmentList: List<{text}>`，不传 `equipmentAdvice`
   - 本地做条目数 ≤20 / 单条 1-50 字前置校验，避免 400

2. **小程序端**：
   - 实现三态降级逻辑（见上表）
   - 图标硬编码或按文案关键词匹配（例: "防晒" → 太阳图标, "外套" → 衣服图标, "鞋" → 鞋图标）
   - 一行一条布局：`<Icon> <Text>`

3. **订单详情/供应清单**：老订单自然走 `equipmentAdvice` 纯文本分支，新订单渲染 `equipmentList`

---

## 硬约束提醒

- **不要传空字符串 text**：后端会 400（`@NotBlank`）
- **不要传 >20 条**：后端会 400（`@Size(max=20)`）
- **不要传 >50 字 text**：后端会 400（`@Size(max=50)`）
- **前端可以传空数组 `[]`** 表示"清空装备建议"，这是合法意图

---

## 代码位置参考（前端调试用）

- Admin VO: `hl-product-service-v2/.../vo/admin/ProductSupplementSaveReqVO.java`
- Admin 详情: `hl-product-service-v2/.../vo/admin/ProductDetailRespVO.SupplementVO`
- MP VO: `hl-product-service-v2/.../vo/mp/MpProductDetailRespVO`
- 订单详情: `hl-order-service-v2/.../controller/vo/OrderDetailVO`
- 供应清单: `hl-order-service-v2/.../internal/vo/MpSuppliesChecklistRespVO`

---

## 部署说明

测试服已执行 DDL（本次部署前必须先跑）：
```sql
ALTER TABLE product_supplement
  ADD COLUMN equipment_list JSON DEFAULT NULL
  COMMENT '装备建议条目列表([{text}]),优先于 equipment_advice';
```

正式环境上线前同步 DDL。
