feat(product-v2): 统一价格日历接口契约 (PR #1290)
这个提交包含在:
父节点
178fe3d5a4
当前提交
83d251e4f5
@ -0,0 +1,166 @@
|
||||
# 统一价格日历接口 /admin/product/item/{id}/pricing-calendar
|
||||
|
||||
**日期**: 2026-04-23
|
||||
**PR**: #1290 (Closes #1286)
|
||||
**服务**: hl-product-service-v2
|
||||
**类型**: feat(新增接口,零破坏性)
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
之前 admin 查价格日历需要分场景调两个不同接口:
|
||||
- CORE/CUSTOM → `GET /admin/product/item/{id}/price-calendar`
|
||||
- GROUP(小蒙马) → `GET /admin/product/item/{id}/schedule/list`
|
||||
|
||||
**根因**:GROUP 定价真相源是 `group_tour_batch.adult_price`(班期表),不走 `product_price_calendar`。前端要按 `productType` 分流调用。
|
||||
|
||||
本次新增**统一入口**,后端按 productType 聚合两种数据源,前端一个接口搞定。
|
||||
|
||||
---
|
||||
|
||||
## 新接口
|
||||
|
||||
### `GET /admin/product/item/{id}/pricing-calendar`
|
||||
|
||||
**Query 参数**
|
||||
- `month`(可选):`yyyy-MM`,过滤月份。不传返全部
|
||||
- `tierSeq`(可选):档位序号,**仅 CORE/CUSTOM 生效**,GROUP 忽略
|
||||
|
||||
**响应**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"productType": "CORE | GROUP | CUSTOM",
|
||||
"items": [
|
||||
{ ... 统一条目 VO ... }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 统一条目 VO 字段表
|
||||
|
||||
| 字段 | 类型 | CORE/CUSTOM | GROUP |
|
||||
|------|------|-------------|-------|
|
||||
| **通用** |
|
||||
| `date` | LocalDate | 每日价格日 | 出发日(departureDate)|
|
||||
| `adultPrice` | BigDecimal | adultSellPrice | adultPrice |
|
||||
| `childPrice` | BigDecimal | childSellPrice | childPrice |
|
||||
| `toddlerDiscount` | BigDecimal | ✅ | ✅ |
|
||||
| `infantPrice` | BigDecimal | ✅ | ✅ |
|
||||
| `stock` | Integer | dailyStock | productStockLimit(无则 maxParticipants)|
|
||||
| `sold` | Integer | sold | enrolledCount |
|
||||
| `sellable` | Boolean | dailyStock 空或 sold<stock | batchStatus∈{ENROLLING,NEARLY_FULL} |
|
||||
| **CORE/CUSTOM 专属** |
|
||||
| `id` | Long | 日历记录主键 | null |
|
||||
| `tierSeq` | Integer | 档位序号 | null |
|
||||
| `priceType` | String | NORMAL/PEAK/HOLIDAY/SPECIAL | null |
|
||||
| `rangeId` | Long | 批量区间标识 | null |
|
||||
| **GROUP 专属** |
|
||||
| `batchId` | Long | null | 班期ID |
|
||||
| `batchNo` | String | null | 班期编号 |
|
||||
| `batchName` | String | null | 班期名称 |
|
||||
| `batchStatus` | String | null | ENROLLING/NEARLY_FULL/FULL/FINISHED/CANCELLING/CANCELLED |
|
||||
| `endDate` | LocalDate | null | 班期结束日 |
|
||||
| `enrollmentDeadline` | LocalDate | null | 报名截止日 |
|
||||
| `maxRooms` | Integer | null | 总房间数 |
|
||||
| `bookedRooms` | Integer | null | 已预订房间数 |
|
||||
|
||||
---
|
||||
|
||||
## 前端渲染建议
|
||||
|
||||
```javascript
|
||||
const resp = await api.getUnifiedPricingCalendar(productId, { month, tierSeq });
|
||||
switch (resp.data.productType) {
|
||||
case 'CORE':
|
||||
case 'CUSTOM':
|
||||
// 矩阵日历: 按 tierSeq × date 渲染档位 × 日期表格
|
||||
renderMatrixCalendar(resp.data.items);
|
||||
break;
|
||||
case 'GROUP':
|
||||
// 列表日历: 按出发日渲染班期卡片,显示 batchName/batchStatus/endDate
|
||||
renderBatchCalendar(resp.data.items);
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 示例
|
||||
|
||||
### CORE 响应
|
||||
```json
|
||||
{
|
||||
"productType": "CORE",
|
||||
"items": [
|
||||
{
|
||||
"date": "2026-05-01",
|
||||
"adultPrice": 1999.00,
|
||||
"childPrice": 999.00,
|
||||
"id": 1780000000000000001,
|
||||
"tierSeq": 1,
|
||||
"priceType": "NORMAL",
|
||||
"stock": 50,
|
||||
"sold": 10,
|
||||
"sellable": true,
|
||||
"batchId": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GROUP 响应
|
||||
```json
|
||||
{
|
||||
"productType": "GROUP",
|
||||
"items": [
|
||||
{
|
||||
"date": "2026-07-06",
|
||||
"adultPrice": 4580.00,
|
||||
"childPrice": 2290.00,
|
||||
"batchId": 2043697199761584129,
|
||||
"batchNo": "Q202607062043697199757389826",
|
||||
"batchName": "7月暑期团",
|
||||
"batchStatus": "ENROLLING",
|
||||
"endDate": "2026-07-10",
|
||||
"stock": 25,
|
||||
"sold": 0,
|
||||
"sellable": true,
|
||||
"tierSeq": null,
|
||||
"priceType": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 向后兼容
|
||||
|
||||
✅ **老接口保持不变**:
|
||||
- `GET /admin/product/item/{id}/price-calendar` — 不动
|
||||
- `GET /admin/product/item/{id}/schedule/list` — 不动
|
||||
|
||||
前端可**逐步迁移**到新接口,无需一次性全量改造。
|
||||
|
||||
---
|
||||
|
||||
## 验证
|
||||
|
||||
测试服网关 7 场景全过:
|
||||
- CORE 全量 → 92 条 tierSeq/priceType 齐 ✅
|
||||
- GROUP 全量 → 4 条班期 batchId/batchStatus/endDate 齐 ✅
|
||||
- CUSTOM → 62 条走 CORE 分支 ✅
|
||||
- CORE/GROUP ?month=2026-05 → 正确过滤 ✅
|
||||
- CORE ?tierSeq=1 → 从 92 条过滤到 79 条 ✅
|
||||
- 老接口 /price-calendar → 92 条不变零回归 ✅
|
||||
|
||||
---
|
||||
|
||||
## 部署
|
||||
|
||||
测试服已部署: task `a1ca3b50` success。
|
||||
正式环境上线前**无需任何 DDL**,纯接口聚合。
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户