# 小蒙马（GROUP）团期房间库存管理 - 前端对接指南

> **日期**: 2026-03-18
> **后端状态**: ✅ 已完成，测试环境已部署
> **涉及模块**: 拼团批次管理、小蒙马下单

---

## 功能说明

小蒙马（GROUP）团期新增**房间库存管理**功能：

**核心变更**：
1. 创建团期时**必须设置总房间数**（maxRooms）
2. 用户/管理员下单时，系统自动**扣减房间库存**
3. 房间不足时，下单直接报错"房间不足，剩余X间，需要Y间"
4. 订单取消/退款/超时/散团，系统自动**恢复房间库存**
5. 团期列表、日历、详情均返回房间相关信息

**并发安全**：使用MySQL乐观锁（`WHERE booked_rooms + count <= max_rooms`），防止超卖。

---

## 变更接口清单

| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 创建主批次 | POST | `/admin/product/item/{productId}/group-batch` | 请求新增字段 | 新增必填参数 `maxRooms` |
| 2 | 创建子批次(溢出) | POST | `/admin/product/item/group-batch/{parentBatchId}/sub` | 请求新增字段 | 同上 |
| 3 | 更新批次 | PUT | `/admin/product/item/group-batch/{batchId}` | 请求新增字段 | 新增可选参数 `maxRooms` |
| 4 | 批次列表(树形) | GET | `/admin/product/item/{productId}/group-batches` | 响应新增字段 | VO新增3个房间字段 |
| 5 | 批次详情 | GET | `/admin/product/item/group-batch/{batchId}` | 响应新增字段 | 同上 |
| 6 | 团期日历(C端) | GET | `/internal/product/{productId}/batches/calendar` | 响应新增字段 | 日历VO新增3个房间字段 |
| 7 | GROUP报价 | GET | `/admin/product/item/{productId}/group-quote` | 无变更 | - |

---

## 接口 1：创建主批次（请求新增字段）

**使用场景**：管理员在拼团批次管理页面创建新团期时，需要额外填写"总房间数"。

```
POST /admin/product/item/{productId}/group-batch
```

### 请求参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| batchName | String | ✅ | 批次名称，如"7月20日第1批" |
| departureDate | String(date) | ✅ | 出发日期，如"2026-07-20" |
| enrollmentDeadline | String(date) | ✅ | 报名截止日期，必须早于出发日期 |
| maxParticipants | Integer | ✅ | 最大参团人数（≥1） |
| **maxRooms** | **Integer** | **✅ 新增必填** | **总房间数（≥1），下单时扣减，取消时恢复** |
| adultsPerRoom | Integer | 否 | 每间房入住成人数（默认2，用于计算单房差） |
| minParticipants | Integer | 否 | 最低成团人数（默认0=不限制） |
| remark | String | 否 | 备注说明 |
| sortOrder | Integer | 否 | 排序序号（默认0） |

### 请求示例

```json
{
  "batchName": "7月20日第1批",
  "departureDate": "2026-07-20",
  "enrollmentDeadline": "2026-07-15",
  "maxParticipants": 30,
  "maxRooms": 15,
  "adultsPerRoom": 2,
  "minParticipants": 10,
  "remark": "暑期特别批次",
  "sortOrder": 0
}
```

### 响应示例

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "batchId": "2030139162007318530",
    "productId": "2030106351179280385",
    "parentBatchId": null,
    "batchNo": "B2026072033",
    "batchLabel": "1",
    "batchName": "7月20日第1批",
    "departureDate": "2026-07-20",
    "endDate": "2026-07-26",
    "enrollmentDeadline": "2026-07-15",
    "minParticipants": 10,
    "maxParticipants": 30,
    "enrolledCount": 0,
    "adultsPerRoom": 2,
    "maxRooms": 15,
    "bookedRooms": 0,
    "remainingRooms": 15,
    "batchStatus": "PENDING",
    "sortOrder": 0,
    "remark": "暑期特别批次",
    "createBy": "2025607151170445314",
    "createTime": "2026-03-18T17:16:00",
    "updateTime": "2026-03-18T17:16:00",
    "remainingSlots": 30,
    "subBatches": []
  }
}
```

---

## 接口 3：更新批次（请求新增字段）

**使用场景**：管理员修改团期信息时，可调整总房间数（不能低于已预订数）。

```
PUT /admin/product/item/group-batch/{batchId}
```

### 新增请求参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| **maxRooms** | **Integer** | **否（新增）** | **总房间数（≥1），不能低于已预订房间数，否则报错** |

### 校验规则

- `maxRooms` 不能低于 `bookedRooms`，否则返回错误："总房间数不能低于已预订房间数（X）"

---

## 响应 VO 新增字段（影响接口 4、5、6）

以下3个字段在**批次列表、批次详情、团期日历**响应中均有返回：

### 团批次VO（GroupTourBatchVO）— 接口4、5

| 字段 | 类型 | 说明 |
|------|------|------|
| **maxRooms** | Integer | 总房间数 |
| **bookedRooms** | Integer | 已预订房间数 |
| **remainingRooms** | Integer | 剩余房间数（= maxRooms - bookedRooms） |

### 团期日历VO（GroupBatchCalendarVO）— 接口6

| 字段 | 类型 | 说明 |
|------|------|------|
| **maxRooms** | Integer | 总房间数 |
| **bookedRooms** | Integer | 已预订房间数 |
| **remainingRooms** | Integer | 剩余房间数 |

### 响应示例（批次列表中的一条）

```json
{
  "batchId": "2030139162007318530",
  "batchName": "7月20日第1批",
  "departureDate": "2026-07-20",
  "maxParticipants": 30,
  "enrolledCount": 5,
  "remainingSlots": 25,
  "maxRooms": 15,
  "bookedRooms": 3,
  "remainingRooms": 12,
  "adultsPerRoom": 2,
  "batchStatus": "ENROLLING"
}
```

---

## 下单相关变更

### 下单房间校验

用户/管理员创建GROUP订单时，系统会：
1. **前置检查**：根据批次的 `remainingRooms` 判断房间是否充足
2. **乐观锁扣减**：`booked_rooms + roomCount <= max_rooms` 时才扣减成功
3. **失败提示**：`"房间不足，剩余X间，需要Y间"`

### 取消/退款自动恢复

以下场景自动归还房间：
- 用户主动取消订单
- 管理员取消订单
- 支付超时自动取消
- 团期散团批量取消
- 退款完成

归还逻辑：`booked_rooms = booked_rooms - 订单的roomCount`

---

## 前端实现建议

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

在现有表单中新增一行：

```
┌─────────────────────────────────────────────┐
│  批次名称:  [7月20日第1批        ]           │
│  出发日期:  [2026-07-20  📅]                │
│  报名截止:  [2026-07-15  📅]                │
│  最大人数:  [30          ]                  │
│  总房间数:  [15          ]    ← 新增必填     │
│  每房成人:  [2           ]                  │
│  最低成团:  [10          ]                  │
│  备    注:  [暑期特别批次 ]                  │
└─────────────────────────────────────────────┘
```

- `maxRooms` 使用 `el-input-number`，min=1
- 编辑时需校验：不能低于 `bookedRooms`（从详情接口获取）

### 批次列表展示

在现有的"已报名/最大人数"旁边，增加房间信息：

```
第1批 | 2026-07-20出发 | 报名中
人数: 5/30 (剩余25)  |  房间: 3/15 (剩余12)
```

### 小程序端日历

日历接口已返回 `remainingRooms`，可在日历格子中展示"剩余X间"。

---

## 数据库变更

```sql
ALTER TABLE group_tour_batch
  ADD COLUMN max_rooms INT NOT NULL DEFAULT 0 COMMENT '总房间数',
  ADD COLUMN booked_rooms INT NOT NULL DEFAULT 0 COMMENT '已预订房间数';
```

> 已在本地、测试环境执行完毕。历史团期的 max_rooms=0，需要管理员编辑补充。

---

## 关联字典

| 字典类型 | 字典值 | 中文标签 | 说明 |
|----------|--------|----------|------|
| batch_status | PENDING | 待开放 | 初始状态，不接受报名 |
| batch_status | ENROLLING | 报名中 | 接受报名，可下单 |
| batch_status | CONFIRMED | 已成团 | 达到最低成团人数，仍可报名 |
| batch_status | FULL | 已满员 | 人数达到上限，自动关闭 |
| batch_status | CLOSED | 已关闭 | 管理员手动关闭报名 |
| batch_status | DISBANDED | 已解散 | 散团，触发退款 |
| batch_status | IN_PROGRESS | 进行中 | 出发日当天自动更新 |
| batch_status | FINISHED | 已结束 | 行程结束 |
