接口入参 month/startDate/endDate 全改为可选, 响应新增 month 字段 测试服 10/10 case 端到端通过 (含 CUSTOM + GROUP 缺省入参真测)
这个提交包含在:
父节点
998d90d425
当前提交
d7d93b0030
@ -0,0 +1,165 @@
|
|||||||
|
---
|
||||||
|
date: 2026-05-13
|
||||||
|
type: backend-improvement
|
||||||
|
service: hl-product-service-v2
|
||||||
|
priority: high
|
||||||
|
notify: ["@mmg"]
|
||||||
|
status: verified
|
||||||
|
---
|
||||||
|
|
||||||
|
# C端价格日历支持月份缺省回退最近可售月, 响应新增 `month` 字段 (已测试服验证)
|
||||||
|
|
||||||
|
> **PR**: [#2172](https://git.1814.love:8443/wx/HL/pulls/2172) — 已合并 dev + 部署测试服
|
||||||
|
> **Issue**: [#2167](https://git.1814.love:8443/wx/HL/issues/2167)
|
||||||
|
> **状态**: ✅ **测试服已验证** (api.test.1814.love:9443) — 10/10 case 端到端通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
### 入参契约放宽
|
||||||
|
|
||||||
|
`/mp/product/{id}/price-calendar` 接口的 `month` / `startDate` / `endDate` 三个参数**全部改为可选**:
|
||||||
|
|
||||||
|
- 三参数全空 → 后端**自动定位**"从今天起最近一个有可售日期所在月份"并返回该月日历。
|
||||||
|
- 旧调用方(detail.js / confirm.js / supplement.js 等传 month 或 startDate+endDate)**行为完全不变**。
|
||||||
|
|
||||||
|
### 响应新增字段
|
||||||
|
|
||||||
|
`MpPriceCalendarRespVO` 新增 `month: yyyy-MM` 字段,前端缺省调用时**必须**用它来渲染日历头/切换上下月。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"month": "2026-07", // 新增,服务端实际返回的月份;无可售时为 null
|
||||||
|
"days": [ ... ] // 既有结构不变
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
无可售日时 `month = null`, `days = []`,前端可展示"暂无可售日期"。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景
|
||||||
|
|
||||||
|
前端两个场景下不知道该传哪个月份:
|
||||||
|
1. 用户首次进入产品详情页,希望默认展示"最近可售月份"。
|
||||||
|
2. 已上架产品的最近可售日不一定是本月(可能近月全过期/全无库存)。
|
||||||
|
|
||||||
|
原来前端只能先调 `/earliest-available-date` 再拿月份回调 `/price-calendar`,多一次 RT。本次改造后**一次接口即可拿到最近可售月日历 + 月份字段**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | C端价格日历 | GET | `/mp/product/{id}/price-calendar` | 入参可选 + 响应新增字段 | 三日期参数全可选;响应新增 `month` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. C端价格日历 `GET /mp/product/{id}/price-calendar`
|
||||||
|
|
||||||
|
**入参 VO**: `MpPriceCalendarReqVO`
|
||||||
|
**响应 VO**: `MpPriceCalendarRespVO`
|
||||||
|
|
||||||
|
#### 入参(Query)
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| `month` | Query | String | ❌ 改为可选 | yyyy-MM | 与 startDate/endDate 三选一可全空 |
|
||||||
|
| `startDate` | Query | String | ❌ 改为可选 | yyyy-MM-dd | 若传必须与 endDate 同月配对 |
|
||||||
|
| `endDate` | Query | String | ❌ 改为可选 | yyyy-MM-dd | 同上 |
|
||||||
|
|
||||||
|
**入参组合规则**:
|
||||||
|
|
||||||
|
| 组合 | 行为 |
|
||||||
|
|------|------|
|
||||||
|
| `startDate` + `endDate` 都非空 | 解析为它们所在月份范围(必须同月,否则抛 410109) |
|
||||||
|
| 仅 `month` 非空 | 解析为该月范围 |
|
||||||
|
| **三者全空** | **新行为**:自动定位"最近可售月" |
|
||||||
|
| 单传 `startDate` 或 `endDate` | 仍抛 410108 |
|
||||||
|
| `month` 格式非法(如 `2026/07`、`abc`、`2026-13`) | 仍抛 410108 |
|
||||||
|
|
||||||
|
#### 出参 `Result<MpPriceCalendarRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `month` | String | **新增**。服务端实际返回的月份 `yyyy-MM`;无可售日时为 `null` |
|
||||||
|
| `days` | List | 既有结构不变(每日 date / priceType / adultPrice / childPrice / isSelectable / remainStock) |
|
||||||
|
|
||||||
|
#### 产品类型分流(全部 3 类覆盖)
|
||||||
|
|
||||||
|
| 类型 | 最近可售日来源 |
|
||||||
|
|------|--------------|
|
||||||
|
| CORE / CUSTOM | `product_price_calendar` 全档位齐全的最早未来日 |
|
||||||
|
| GROUP(小蒙马) | `group_tour_batch` 状态 `ENROLLING/NEARLY_FULL` 且报名未截止的最早出发日 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
**A. 缺省入参(新行为)**:
|
||||||
|
```
|
||||||
|
GET /mp/product/2045018534152478721/price-calendar
|
||||||
|
→ { "code":200, "data":{ "month":"2026-05", "days":[ {...30 条...} ] } }
|
||||||
|
```
|
||||||
|
|
||||||
|
**B. 显式 month(旧行为,兼容)**:
|
||||||
|
```
|
||||||
|
GET /mp/product/2045018534152478721/price-calendar?month=2026-07
|
||||||
|
→ { "code":200, "data":{ "month":"2026-07", "days":[ ... ] } }
|
||||||
|
```
|
||||||
|
|
||||||
|
**C. 缺省入参 + 产品完全无可售**:
|
||||||
|
```
|
||||||
|
GET /mp/product/{noSellable}/price-calendar
|
||||||
|
→ { "code":200, "data":{ "month":null, "days":[] } }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误码
|
||||||
|
|
||||||
|
| code | 触发场景 | 文案 |
|
||||||
|
|------|---------|------|
|
||||||
|
| 410108 | 单边 startDate / 单边 endDate / month 格式非法 | 参数格式错误:month 需为 yyyy-MM,或 startDate/endDate 必须同月成对且为 yyyy-MM-dd |
|
||||||
|
| 410109 | startDate / endDate 跨月 | startDate(X) 与 endDate(Y) 必须在同一个月份内 |
|
||||||
|
| 404 | 产品不存在 / 已下架 | 产品不存在或已下架 |
|
||||||
|
|
||||||
|
> **顺带的安全收益**:本次把产品可见性校验 `getVisibleProductOrThrow` 从"缓存命中后"提前到"参数解析前"。**堵住"产品下架但缓存内日历仍可读 5 分钟"的口子**。对前端无可见影响(响应内容不变),但下架产品立即返回 404。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、测试服验证(硬性凭证,10/10 端到端通过)
|
||||||
|
|
||||||
|
测试环境 `api.test.1814.love:9443`,今天 2026-05-13。
|
||||||
|
|
||||||
|
| # | 用例 | 入参 | 期望 | 实测 |
|
||||||
|
|---|------|------|------|------|
|
||||||
|
| 1 | CORE 缺省入参 | pid=2045018534152478721 | month 非 null + days > 0 | ✓ month=2026-05, days=30 |
|
||||||
|
| 2 | CORE 显式 month=2026-07 | month=2026-07 | month=2026-07 | ✓ |
|
||||||
|
| 3 | CORE startDate+endDate 同月 | 2026-07-10 ~ 2026-07-20 | month=2026-07 | ✓ |
|
||||||
|
| 4 | **CUSTOM 缺省入参** | pid=2043696016590327809 | month 非 null | ✓ month=2026-07, days=31 |
|
||||||
|
| 5 | **GROUP 缺省入参** | pid=2044306857534636034 | month 非 null | ✓ month=2026-07, days=5 |
|
||||||
|
| 6 | 跨月异常 | startDate=2026-07-25&endDate=2026-08-05 | 410109 | ✓ |
|
||||||
|
| 7 | 单边 startDate | startDate=2026-07-15 | 410108 | ✓ |
|
||||||
|
| 8 | 单边 endDate | endDate=2026-07-15 | 410108 | ✓ |
|
||||||
|
| 9 | month 格式错 | month=2026/07 | 410108 | ✓ |
|
||||||
|
| 10 | 不存在产品 + 缺省入参 | pid=999999 | 404 visible 提前 | ✓ |
|
||||||
|
|
||||||
|
本地单测(hl-product-service-v2 全模块)1192/1192 全绿。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、前端调用建议
|
||||||
|
|
||||||
|
1. **首次进入产品详情页**:直接调 `/mp/product/{id}/price-calendar` **不传任何参数**,拿响应 `month` 字段渲染日历头。
|
||||||
|
2. **用户切换上/下月**:传 `month=yyyy-MM` 显式查询(与新行为不互斥)。
|
||||||
|
3. **响应 `month === null`**:展示"该产品暂无可售日期"。
|
||||||
|
4. **旧代码不必立即改**:传 month / startDate+endDate 的旧逻辑完全兼容,按各自节奏迁移即可。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、缓存
|
||||||
|
|
||||||
|
- 内容仍按 `mp:product:price-calendar:{productId}:{yyyy-MM}` 月份 key 缓存,5 分钟 TTL。
|
||||||
|
- 缺省入参时多一次 earliest 查询(不缓存,廉价、随时间变化)。
|
||||||
|
- 无可售月(`month=null`)不写缓存。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户