# 接口变更记录 — 2026-04-13

> **变更类型**：接口合并 + 定价模型说明
> **在线文档**：
> - 局域网：http://192.168.100.236:8080/doc.html → `1. 管理端 - 产品管理`
> - 公网：https://api.test.1814.love:9443/doc.html（需认证）

---

## 一、创建产品和Step1基础信息合并为一个接口

### 旧接口（已删除）

| 接口 | 说明 |
|------|------|
| ~~POST /admin/product/item~~ | ~~独立创建接口，已删除~~ |
| ~~PUT /admin/product/item/{id}/basic~~ | ~~独立Step1保存，已删除~~ |

### 新接口

**`POST /admin/product/item/basic`** — 创建+更新合一

- **productId 不传** = 创建新产品（草稿），返回新 productId
- **productId 传了** = 更新已有产品，返回原 productId

### 创建时请求示例（productId不传）

```json
{
  "productType": "CORE",
  "name": "额吉的故乡·亲子版",
  "tripDays": 6,
  "lineId": 100,
  "coverImageUrl": "https://...",
  "seasons": ["summer"],
  "tags": ["亲子"]
}
```

- `productType` **创建时必传**（CORE/GROUP/CUSTOM）
- `version` **创建时不传**
- 其他字段可选，创建时可以只传最少信息，也可以一次性填完全部

### 更新时请求示例（productId传已有ID）

```json
{
  "productId": 2043246204711550977,
  "version": 0,
  "name": "额吉的故乡·亲子版（修改）",
  "tripDays": 6,
  "lineId": 100,
  "coverImageUrl": "https://...",
  "seasons": ["summer", "autumn"],
  "tags": ["亲子", "研学"],
  "tiers": [
    {"tierSeq": 1, "tierName": "舒适"},
    {"tierSeq": 2, "tierName": "豪华"}
  ]
}
```

- `version` **更新时必传**（从详情接口获取，乐观锁校验）
- `productType` 更新时传了也会被忽略（创建后不可改）

### 响应

```json
{
  "code": 200,
  "message": "成功",
  "data": 2043246204711550977
}
```

返回 `Long` 类型的产品ID（创建时是新ID，更新时是原ID）。

---

## 二、定价模型：哪种产品用哪个接口

### 核心规则

| 产品类型 | 定价模型 | 用户选什么 | 用哪组接口 |
|---------|---------|-----------|-----------|
| **CORE**（核心产品） | 价格日历 | 用户从日历选出发日期 | `price-calendar` 系列 |
| **CUSTOM**（私人定制） | 价格日历 | 同上 | `price-calendar` 系列 |
| **GROUP**（小蒙马） | 班期制 | 用户从班期列表选第N期 | `schedule` 系列 |

### 前端判断逻辑

```
if (productType === 'CORE' || productType === 'CUSTOM') {
    // Step4 显示【价格日历】界面
    // 用 price-calendar 系列接口
} else if (productType === 'GROUP') {
    // Step4 显示【班期管理】界面
    // 用 schedule 系列接口
}
```

### 价格日历接口（CORE / CUSTOM 产品用）

| 场景 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 查询价格日历 | GET | `/admin/product/item/{id}/price-calendar?month=2026-07&tierSeq=1` | 按月+档位查询 |
| 批量设置价格 | POST | `/admin/product/item/{id}/price-calendar/batch` | 按日期范围设价 |
| 删除价格区间 | DELETE | `/admin/product/item/{id}/price-calendar` | 按日期范围删除 |

**批量设置请求示例**：

```json
{
  "productId": 123,
  "version": 3,
  "startDate": "2026-07-01",
  "endDate": "2026-07-31",
  "tierSeq": 1,
  "priceType": "PEAK",
  "adultSellPrice": 4980,
  "childSellPrice": 3980,
  "toddlerDiscount": -500,
  "infantPrice": 0,
  "dailyStock": 10
}
```

**价格日历查询响应**：

```json
[
  {
    "id": 1,
    "productId": 123,
    "tierSeq": 1,
    "date": "2026-07-01",
    "priceType": "PEAK",
    "adultSellPrice": 4980,
    "childSellPrice": 3980,
    "toddlerDiscount": -500,
    "infantPrice": 0,
    "dailyStock": 10,
    "sold": 2
  }
]
```

### 班期接口（GROUP 小蒙马产品用）

| 场景 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 班期列表 | GET | `/admin/product/item/{id}/schedule/list` | 返回全部班期 |
| 创建班期 | POST | `/admin/product/item/{id}/schedule` | 不传batchId |
| 修改班期 | PUT | `/admin/product/item/{id}/schedule` | 传batchId |
| 删除班期 | DELETE | `/admin/product/item/{id}/schedule/{scheduleId}` | 有订单不可删 |
| 取消班期 | POST | `/admin/product/item/{id}/schedule/{scheduleId}/cancel` | 触发退款 |
| 批量创建 | POST | `/admin/product/item/{id}/schedule/batch-create` | 按重复模式 |
| 班期团队查询 | GET | `/admin/product/item/{id}/schedule/team?batchId=xxx` | 团队成员 |
| 班期团队保存 | PUT | `/admin/product/item/{id}/schedule/team?batchId=xxx` | 全量替换 |

**创建班期请求示例**：

```json
{
  "productId": 123,
  "batchName": "第1期",
  "departureDate": "2026-07-01",
  "enrollmentDeadline": "2026-06-30",
  "adultPrice": 4980,
  "childPrice": 3980,
  "toddlerDiscount": -500,
  "infantPrice": 0,
  "maxParticipants": 20,
  "maxRooms": 10,
  "productStockLimit": 20
}
```

**班期列表响应**：

```json
[
  {
    "batchId": 456,
    "productId": 123,
    "batchNo": "B20260701001",
    "batchName": "第1期",
    "departureDate": "2026-07-01",
    "endDate": "2026-07-06",
    "enrollmentDeadline": "2026-06-30",
    "adultPrice": 4980,
    "childPrice": 3980,
    "toddlerDiscount": -500,
    "infantPrice": 0,
    "maxParticipants": 20,
    "enrolledCount": 8,
    "maxRooms": 10,
    "bookedRooms": 4,
    "productStockLimit": 20,
    "batchStatus": "ENROLLING",
    "version": 0
  }
]
```

**批量创建班期请求示例**：

```json
{
  "productId": 123,
  "startDate": "2026-07-01",
  "endDate": "2026-09-30",
  "repeatMode": "WEEKLY",
  "dayOfWeek": 1,
  "adultPrice": 4980,
  "childPrice": 3980,
  "maxParticipants": 20,
  "maxRooms": 10
}
```

### 价格日历 vs 班期对比

| 维度 | 价格日历（CORE/CUSTOM） | 班期（GROUP） |
|------|----------------------|-------------|
| 定价粒度 | 每天独立价格 | 每期一个固定价格 |
| 库存 | 按天（dailyStock=每天可接几单） | 按期（maxParticipants=总人数+maxRooms=总房间数） |
| 档位 | 支持多档（tierSeq区分） | 单一档位 |
| 用户选择 | C端日历选日期 | C端列表选第N期 |
| 团队配置 | 无 | 每期可配领队/摄影师等 |
| 价格类型 | 平日/旺季/节假日/特价（颜色区分） | 无（每期统一价） |

### 班期状态说明

| 状态 | 中文 | 说明 |
|------|------|------|
| ENROLLING | 报名中 | 正常接受报名 |
| NEARLY_FULL | 即将满员 | 剩余房间≤阈值（默认2间） |
| FULL | 已满 | 剩余房间=0 |
| FINISHED | 已结束 | 出发日期已过 |
| CANCELLING | 取消中 | 有订单，退款处理中 |
| CANCELLED | 已取消 | 取消完成 |

---

## 三、后端与前端名词对照

| 后端术语 | 后端字段 | 前端术语 | 说明 |
|---------|---------|---------|------|
| 产品线 | lineId / lineName | **主题** | 如"额吉的故乡""草原环线" |
| 产品 | productId / name | **版本** | 如"亲子版""旷野版""舒适版" |
| 档位 | tierSeq / tierName / tierCount | **规格** | 如"舒适""豪华"，tierCount=规格数量 |

**前端展示映射**：
- 主题列表 → 调 `/admin/product/line/list`
- 版本列表 → 调 `/admin/product/item/list`（tierCount=该版本有几个规格）
- 规格详情 → 在产品详情的 `tiers` 字段中（`[{tierSeq:1, tierName:"舒适"}, {tierSeq:2, tierName:"豪华"}]`）
