# fix: 小蒙马（GROUP）班期 maxParticipants 改回可选（0/不传 = 不限名额）

- **日期**: 2026-04-20
- **PR**: [#953](https://git.1814.love:8443/wx/HL/pulls/953) (Closes #952)
- **类型**: FIX（请求参数约束放宽）
- **服务**: hl-product-service-v2
- **前端是否需要改动**: **建议改动**（去掉前端必填校验，新增"不限名额"选项）

---

## 一、背景

PR #927（2026-04-19）将班期保存接口的 `maxParticipants` 改为 **必填**，目的是把"约束模糊报错"前置到字段级。

但实际业务反馈：**小蒙马（GROUP）部分团允许"不限名额"**（按需开班，不卡上限），强制必填导致这类班期无法创建。

本次回退到**可选**，并明确"0 / 不传 = 不限名额"的语义；前端可据此提供"不限名额"开关或留空。

---

## 二、变更接口清单

| # | 方法 | 路径 | 影响 |
|---|------|------|------|
| 1 | POST | `/admin/product/v2/schedule/save` | 请求体字段 `maxParticipants` 由必填改回**可选** |
| 2 | POST | `/admin/product/v2/schedule/batch-create` | 同上 |

---

## 三、字段约束变化

| 字段 | 之前 (PR #927) | 现在 (本次) | 允许值 |
|------|---------------|-------------|--------|
| `maxParticipants` | **必填**（null 报"最大参与人数不能为空"） | **可选** | 不传 / 0 / null = **不限名额**；>0 = 上限人数 |

DB 侧：不传或传 0 均落库为 `0`，业务校验报名人数时按"0 视为不限"处理。

---

## 四、请求示例

### 不限名额班期（新支持）

```json
POST /admin/product/v2/schedule/save
{
  "productId": 123,
  "batchName": "五一不限人数团",
  "departureDate": "2026-05-01",
  "adultPrice": 5160,
  "childPrice": 4580
  // 不传 maxParticipants → 0 → 不限
}
```

### 显式限名额班期（行为不变）

```json
POST /admin/product/v2/schedule/save
{
  "productId": 123,
  "batchName": "五一限 30 人精品团",
  "departureDate": "2026-05-01",
  "adultPrice": 5160,
  "childPrice": 4580,
  "maxParticipants": 30
}
```

---

## 五、报名上限校验逻辑

| `maxParticipants` 落库值 | 报名时校验 |
|---|---|
| `0`（不传 / 显式传 0） | **不校验**，可无限报名 |
| `> 0` | 报名累计人数不得超过该值；超出报"班期已满"错误 |

> 现有"按 maxParticipants 校验"代码路径完整保留，仅在 `maxParticipants > 0` 时生效。

---

## 六、前端改动建议

### 班期创建/编辑表单

1. **去掉"最大人数必填"前端校验**
2. 在最大人数输入框旁加 **"不限名额"开关 / 复选框**：
   - 勾选 → 提交时**不传 `maxParticipants`**（或显式传 `0`）
   - 不勾 → 输入框可填具体数字（建议前端做 `>0` 校验，避免误填负数）
3. **回显逻辑**：编辑场景拉到 `maxParticipants=0` 时，开关默认勾选，输入框置灰

### 批量创建表单

同上，"不限名额"开关对批量生成的所有班期统一生效。

### 列表展示

| 后端字段值 | 建议展示 |
|---|---|
| `0` | "不限" / "不限名额" |
| `> 0` | "上限 N 人" / 进度条（已报 / N） |

---

## 七、不兼容变更

| 调用方 | 影响 |
|---|---|
| 旧前端版本（**必传 `maxParticipants`**） | **不受影响**，传具体数字时行为完全一致 |
| 旧前端版本（**显式传 0** 想表达"不限"） | 之前会因必填校验直接 400；本次起正常落库为"不限" |
| 新前端版本（**不传 `maxParticipants`**） | 落库为 0 = 不限，符合预期 |

---

## 八、回归验证

测试环境部署完成后，用管理端 token 调用：

```bash
# 1. 不限名额班期（不传字段）
curl -X POST "https://api.test.1814.love/admin/product/v2/schedule/save" \
  -H "Authorization: Bearer {admin-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": 123,
    "batchName": "回归-不限名额",
    "departureDate": "2026-06-01",
    "adultPrice": 5160,
    "childPrice": 4580
  }'

# 预期：code=200，落库 maxParticipants=0

# 2. 显式限名额班期（传具体数字）
curl -X POST "https://api.test.1814.love/admin/product/v2/schedule/save" \
  -H "Authorization: Bearer {admin-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": 123,
    "batchName": "回归-限 30 人",
    "departureDate": "2026-06-02",
    "adultPrice": 5160,
    "childPrice": 4580,
    "maxParticipants": 30
  }'

# 预期：code=200，落库 maxParticipants=30
```
