4.4 KiB
4.4 KiB
统一价格日历接口 /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 忽略
响应
{
"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 | 已预订房间数 |
前端渲染建议
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 响应
{
"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 响应
{
"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,纯接口聚合。