# 早鸟优惠改造：产品 1:N 多方案 + 阶梯文案（BUG 20）

**日期**：2026-04-20
**PR**：#964 + hotfix #967
**影响**：管理端早鸟优惠管理页 + 小程序产品详情页 + 下单报价

---

## 业务变化

### 1. 一个产品可绑定多个早鸟方案

**改造前**：产品 1:1 方案。管理端创建第 2 个绑定同一产品的方案报错「以下产品已绑定其他早鸟计划，不能重复绑定」。

**改造后**：产品 1:N。可以配阶梯：
- 2 人打 9 折 (`minPeople=2, maxPeople=2, discountType=PERCENT, discountPercent=90`)
- 3-5 人打 85 折 (`minPeople=3, maxPeople=5, discountType=PERCENT, discountPercent=85`)
- 6 人及以上打 8 折 (`minPeople=6, maxPeople=null, discountType=PERCENT, discountPercent=80`)

报价时后端按订单人数自动匹配最优方案，落 `order_discount` 表。

### 2. 优惠类型扩展（3 种）

`discount_type` 字段新增：

| 值 | 含义 | 示例 | 需填字段 |
|---|---|---|---|
| `AMOUNT_TOTAL`（**默认**） | 整单立减 | 整单减 500 元 | `discountAmount` |
| `AMOUNT_PER_PERSON` | 每人立减 | 每人减 100 元 | `discountAmount` |
| `PERCENT` | 按比例打折 | 8 折 | `discountPercent` (0-100) |

**⚠️ 重要：老数据默认为 `AMOUNT_TOTAL`**（历史上运营侧约定 `discountAmount=500` 即整单立减 500，所以迁移时存量数据全部回正为 `AMOUNT_TOTAL`）。

---

## 接口变化

### 1. 创建/更新方案（兼容老调用，新字段可选）

**`POST /admin/order/early-bird`**
**`PUT /admin/order/early-bird/{planId}`**

`EarlyBirdPlanSaveReqVO` 新增字段：

```ts
{
  "planName": "阶梯档 3-5 人 85 折",
  "startDate": "2026-04-01",
  "endDate": "2026-12-31",
  "enabled": true,
  "productIds": [123456],

  // 老字段
  "minPeople": 3,
  "discountAmount": 0,        // AMOUNT_TOTAL/PER_PERSON 必填；PERCENT 可为 null

  // 新字段
  "maxPeople": 5,             // NEW 最高人数(含)，不传=不限
  "discountType": "PERCENT",  // NEW 默认 AMOUNT_TOTAL
  "discountPercent": 85,      // NEW PERCENT 时必填 (0<v<100)
  "priority": 0               // NEW 同区间多命中决胜，默认 0
}
```

### 2. 响应 VO 新增字段

`EarlyBirdPlanVO`（列表/详情）新增后端拼好的**文案字段**：

```ts
{
  "peopleRangeText": "3-5 人",           // NEW 例："2 人"/"3-5 人"/"6 人及以上"
  "discountText": "85 折",               // NEW 例："9 折"/"每人立减 100 元"/"整单立减 500 元"
  // ... 其他 4 个新业务字段（maxPeople/discountType/discountPercent/priority）
}
```

前端可直接用 `peopleRangeText` 和 `discountText` 做展示，无需自己拼。

### 3. 不再校验产品唯一

`POST /admin/order/early-bird` 去掉 `以下产品已绑定其他早鸟计划，不能重复绑定` 校验。同一 `productId` 可出现在任意多个方案。

### 4. 小程序产品详情页新增字段

**`GET /mp/product/item/{id}`** 响应 `MpProductDetailRespVO` 新增：

```ts
{
  "earlyBirdTip": "最低 8 折起（共 3 档早鸟方案）",  // 无人数上下文的概览文案，无绑定=null
  "earlyBirdLadder": [                                // 早鸟阶梯（按 min_people 升序）
    {
      "planId": "123",
      "peopleRangeText": "2 人",
      "discountText": "9 折",
      "startDate": "2026-04-01",
      "endDate": "2026-12-31"
    },
    { "planId": "124", "peopleRangeText": "3-5 人", "discountText": "85 折", ... },
    { "planId": "125", "peopleRangeText": "6 人及以上", "discountText": "8 折", ... }
  ]
}
```

前端可按 ladder 画阶梯图。老字段 `earlyBirdTip` 语义微调为「概览性描述」，不再只拿第 1 个方案。

### 5. 小程序下单按人数本地匹配

前端可用 `earlyBirdLadder` 按用户选择的人数本地计算匹配档位，展示「您当前 X 人，命中 'Y 人 Z 折'；满 M 人可升级到 'N 折'」提示。

---

## 前端 TODO

### 管理端早鸟优惠管理页
1. 方案编辑表单加 **4 个新输入项**：
   - 最高人数（数字，可空=不限）
   - 优惠类型（单选：整单立减/每人立减/按比例打折），默认**整单立减**
   - 折扣率（0-100，仅"按比例打折"时启用）
   - 优先级（数字，默认 0）
2. 原"优惠金额 discountAmount"改为仅在"整单立减/每人立减"时启用
3. 产品绑定改为**多选 tag**（去掉 1:1 限制，可一个产品绑多个方案）
4. 建议加"阶梯预览"区块显示该产品当前所有方案的覆盖情况，帮肉眼核对重叠/漏洞
5. 列表展示：用后端返回的 `peopleRangeText + discountText` 直接显示，不用前端拼

### 小程序产品详情页
1. 读 `earlyBirdLadder` 画阶梯图（3 档示意）
2. `earlyBirdTip` 作为默认概览展示（如"最低 8 折起"）
3. 老字段 `earlyBirdText`、`earlyBirdPlanName`、`earlyBirdDiscount` 废弃（值可能为 null 或第 1 档概览），请改读 `earlyBirdLadder[0]`

### 小程序下单页
1. 用户选完人数 + 日期后本地按 `earlyBirdLadder` 匹配：
   ```
   match = ladder.find(l => totalPeople >= l.minPeople && (l.maxPeople == null || totalPeople <= l.maxPeople))
   ```
2. 匹配到方案时，提示文案可参考：`"您当前 N 人，命中 '${peopleRangeText} ${discountText}'"`
3. 可顺带提示"更优档位"：若 `ladder` 里有 `minPeople > totalPeople` 且折扣更大的，提示"满 X 人可享 Y"

---

## 兼容性

- 老数据：全部默认 `discount_type=AMOUNT_TOTAL`，行为不变（整单立减）
- 老接口：`EarlyBirdPlanSaveReqVO` 新字段全部可选，旧版管理端提交不传也可
- 老订单：已支付订单不回溯，`order_discount` 持久化的 `discount_amount` 不变
- 废弃但保留：`earlyBirdText/earlyBirdPlanName/earlyBirdDiscount` 三个老字段继续下发（值按新逻辑计算），下个大版本下架
