docs(changelog): 新建订单出发日期选择器需限制为可售日期(前端待修)——已有 pricing-calendar 接口可置灰+后端已服务端兜底
这个提交包含在:
父节点
979ecdf8f1
当前提交
6939e6d6b8
@ -0,0 +1,117 @@
|
|||||||
|
# 新建订单「出发日期」选择器需限制为可售日期(前端待修) — 前端待修 — 管理后台
|
||||||
|
|
||||||
|
> 变更类型:🐛 前端 UX 缺陷(**无后端代码变更、零 DDL**;后端所需接口已存在且测试服可用,本文档给出对接方式)
|
||||||
|
> 端类型:管理后台(新建订单向导 → 第 3 步「基本信息」→「出发日期」选择器)
|
||||||
|
> 日期:2026-06-18
|
||||||
|
> 服务:hl-product-service-v2(价格日历查询接口,已上线);hl-order-service-v3(创单服务端兜底,已上线)
|
||||||
|
> 责任端:前端 hl-ui(mmg)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键说明
|
||||||
|
|
||||||
|
**现象**:新建订单第 3 步的「出发日期」选择器是一个**不受限的普通日期控件**,任意日期都可选(截图里日历停在 `2026 年 11 月` 且全部可点)。
|
||||||
|
|
||||||
|
**应有行为**:只允许选择该产品 + 该档位下的**可售日期**(与产品卡「最近可出发」、小程序端下单日历口径一致)。
|
||||||
|
|
||||||
|
**根因 = 前端未把日期选择器与已有的「价格日历」接口打通**,不是后端缺接口:
|
||||||
|
|
||||||
|
1. **限制可选范围所需的接口已存在且测试服可用**:`GET /admin/product/item/{productId}/pricing-calendar`,逐日返回 `sellable`(是否可售,后端算好)。前端在第 3 步已持有 `productId`(第 2 步选品)+ `tierSeq`(第 2 步选档),直接调它把非可售日期置灰即可(详见 §2)。
|
||||||
|
2. **后端已在服务端兜底**:即便选了非可售日期提交,创单也会被拦下、不会落库(详见 §3)。所以这是**纯前端的「提前拦截 / 体验」问题**——当前用户能选到注定失败的日期,提交后才报错,且报错文案误导(见 §3)。
|
||||||
|
|
||||||
|
> 实测样本(测试服 9443 + 真实 admin token):产品「游牧的森林-短途版」(productId=`2045345825172639746`,CORE 类型) **可售日仅 `2026-06-18`~`2026-06-30` 共 13 天**;其余 78 天(含整个 11 月)`sellable=false`。截图里 11 月全部可选属错误,应整月禁选。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 复现
|
||||||
|
|
||||||
|
1. 新建订单 → 选主题「游牧的森林」→ 选产品「游牧的森林-短途版」→ 选档位「轻奢」。
|
||||||
|
2. 第 3 步「基本信息」点「出发日期」,日历弹出。
|
||||||
|
3. 现状:所有日期都能点(截图停在 2026-11,全月可选)。
|
||||||
|
4. 期望:只有 `2026-06-18`~`2026-06-30` 可点,其余置灰;最好默认定位到「最近可出发」所在月(2026-06)而不是空月份。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 修复方式:用 `pricing-calendar` 给选择器置灰
|
||||||
|
|
||||||
|
**`GET /admin/product/item/{productId}/pricing-calendar`**
|
||||||
|
|
||||||
|
| 参数 | 位置 | 必填 | 说明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| productId | path | 是 | 第 2 步选中的产品 ID |
|
||||||
|
| month | query | 否 | `yyyy-MM`;不传返回全部已配日历 |
|
||||||
|
| tierSeq | query | 否 | 档位序号,仅 CORE/CUSTOM 生效;**传第 2 步选中的档位**(不同档位可售日可能不同) |
|
||||||
|
|
||||||
|
响应 `data`:`{ productType, items[] }`,`items[]` 子项关键字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| date | LocalDate(yyyy-MM-dd) | 日期 |
|
||||||
|
| **sellable** | **Boolean** | **是否可售(置灰判据:`sellable !== true` 即禁选)** |
|
||||||
|
| adultPrice | String | 成人价(金额=带引号字符串) |
|
||||||
|
| childPrice / infantPrice / toddlerDiscount | String | 各人群价/优惠 |
|
||||||
|
| stock / sold | Integer | 库存上限 / 已售(stock=null 表示不限) |
|
||||||
|
| tierSeq / priceType | Integer / String | 档位序号 / 价格类型 |
|
||||||
|
|
||||||
|
**前端对接建议**:
|
||||||
|
|
||||||
|
1. 进入第 3 步(或用户在日历翻月)时,按 `productId` + `tierSeq` 拉日历(可不传 month 一次性取全部,数据量小)。
|
||||||
|
2. 用 `items` 里 `sellable === true` 的 `date` 构造「可选日期集合」,其余日期一律 disabled;列表里没有的日期也按不可选处理。
|
||||||
|
3. 默认打开月份用产品卡已有的「最近可出发」(`nextSaleDate`) 所在月,避免落到空月份。
|
||||||
|
4. 用户在第 2 步改了档位再回到第 3 步时,需按新 `tierSeq` 重新拉取。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 拉「游牧的森林-短途版」轻奢档(tierSeq=1)的可售日历
|
||||||
|
curl -k "https://api.test.1814.love:9443/admin/product/item/2045345825172639746/pricing-calendar?tierSeq=1" \
|
||||||
|
-H "Authorization: Bearer <adminToken>"
|
||||||
|
```
|
||||||
|
|
||||||
|
实测返回(节选,已上线测试服):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"productType": "CORE",
|
||||||
|
"items": [
|
||||||
|
{ "date": "2026-06-18", "adultPrice": "3.00", "childPrice": "3.00", "sellable": true, "tierSeq": 1, "priceType": "NORMAL", "stock": null, "sold": 0 },
|
||||||
|
{ "date": "2026-06-30", "adultPrice": "3.00", "sellable": true, "tierSeq": 1 },
|
||||||
|
{ "date": "2026-11-15", "sellable": false, "tierSeq": 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 本例中 `sellable=true` 仅 13 天(`2026-06-18`~`2026-06-30`),其余(含 11 月全月)`sellable=false`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 后端服务端兜底(已生效,前端可放心)
|
||||||
|
|
||||||
|
创单接口 `POST /v3/admin/order` 已对出发日期做服务端校验,**非可售日期 / 过去日期都进不去**(实测,均未落库):
|
||||||
|
|
||||||
|
| 提交的出发日期 | 结果 | code | message |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `2026-06-20`(可售) | 通过报价、正常创单 | 200 | — |
|
||||||
|
| `2026-11-15`(不可售) | **被拦截**,不落库 | 581014 | `拉取产品信息失败,请稍后重试` |
|
||||||
|
| `2026-06-01`(早于今天) | 被拦截,不落库 | 581011 | `出发日期不能早于今天` |
|
||||||
|
|
||||||
|
> ⚠️ 注意:非可售日期当前返回的是 `581014「拉取产品信息失败,请稍后重试」`,**文案对用户有误导**(真实原因是「该日期未配置价格/不可售」,并非系统故障)。前端按 §2 把选择器限制好后,正常路径不会再触发此错误;该文案是否优化由后端另行评估,不阻塞本次前端修复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 实测确认
|
||||||
|
|
||||||
|
- 价格日历接口 `GET /admin/product/item/2045345825172639746/pricing-calendar?tierSeq=1`:`200`,`data.items` 共 91 条,`sellable=true` 13 条(`2026-06-18`~`2026-06-30`)✓
|
||||||
|
- 创单非可售日期 `2026-11-15`:被拦截 `581014`,无新增订单 ✓
|
||||||
|
- 创单过去日期 `2026-06-01`:被拦截 `581011` ✓
|
||||||
|
- 均经测试服网关 9443 + 真实 admin token 验证。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
|
||||||
|
- 本文为前端缺陷通知 + 现有接口对接说明,无后端代码变更、零 DDL、无新增/改动接口。
|
||||||
|
- `pricing-calendar` 接口为产品管理「价格日历」长期已上线端点,CORE/CUSTOM 走 `product_price_calendar` 逐日价、GROUP 走 `group_tour_batch` 班期(按出发日),前端按 `data.productType` 区分渲染。
|
||||||
|
- 小程序端下单日历亦是同口径(按可售日置灰),管理后台对齐即可。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户