6.8 KiB
新建订单「出发日期」选择器需限制为可售日期(前端待修) — 前端待修 — 管理后台
变更类型:🐛 前端 UX 缺陷(无后端代码变更、零 DDL;后端所需接口已存在且测试服可用,本文档给出对接方式) 端类型:管理后台(新建订单向导 → 第 3 步「基本信息」→「出发日期」选择器) 日期:2026-06-18 服务:hl-product-service-v2(价格日历查询接口,已上线);hl-order-service-v3(创单服务端兜底,已上线) 责任端:前端 hl-ui(mmg)
⚠️ 关键说明
现象:新建订单第 3 步的「出发日期」选择器是一个不受限的普通日期控件,任意日期都可选(截图里日历停在 2026 年 11 月 且全部可点)。
应有行为:只允许选择该产品 + 该档位下的可售日期(与产品卡「最近可出发」、小程序端下单日历口径一致)。
根因 = 前端未把日期选择器与已有的「价格日历」接口打通,不是后端缺接口:
- 限制可选范围所需的接口已存在且测试服可用:
GET /admin/product/item/{productId}/pricing-calendar,逐日返回sellable(是否可售,后端算好)。前端在第 3 步已持有productId(第 2 步选品)+tierSeq(第 2 步选档),直接调它把非可售日期置灰即可(详见 §2)。 - 后端已在服务端兜底:即便选了非可售日期提交,创单也会被拦下、不会落库(详见 §3)。所以这是纯前端的「提前拦截 / 体验」问题——当前用户能选到注定失败的日期,提交后才报错,且报错文案误导(见 §3)。
实测样本(测试服 9443 + 真实 admin token):产品「游牧的森林-短途版」(productId=
2045345825172639746,CORE 类型) 可售日仅2026-06-18~2026-06-30共 13 天;其余 78 天(含整个 11 月)sellable=false。截图里 11 月全部可选属错误,应整月禁选。
1. 复现
- 新建订单 → 选主题「游牧的森林」→ 选产品「游牧的森林-短途版」→ 选档位「轻奢」。
- 第 3 步「基本信息」点「出发日期」,日历弹出。
- 现状:所有日期都能点(截图停在 2026-11,全月可选)。
- 期望:只有
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 | 档位序号 / 价格类型 |
前端对接建议:
- 进入第 3 步(或用户在日历翻月)时,按
productId+tierSeq拉日历(可不传 month 一次性取全部,数据量小)。 - 用
items里sellable === true的date构造「可选日期集合」,其余日期一律 disabled;列表里没有的日期也按不可选处理。 - 默认打开月份用产品卡已有的「最近可出发」(
nextSaleDate) 所在月,避免落到空月份。 - 用户在第 2 步改了档位再回到第 3 步时,需按新
tierSeq重新拉取。
# 拉「游牧的森林-短途版」轻奢档(tierSeq=1)的可售日历
curl -k "https://api.test.1814.love:9443/admin/product/item/2045345825172639746/pricing-calendar?tierSeq=1" \
-H "Authorization: Bearer <adminToken>"
实测返回(节选,已上线测试服):
{
"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(不可售) |
被拦截,不落库 | 581041 | 所选出发日期不可售或未配置价格,请重新选择出发日期 |
2026-06-01(早于今天) |
被拦截,不落库 | 581011 | 出发日期不能早于今天 |
✅ 已优化(PR #4008,已合 dev-v3 + 部署测试服双实例实测):非可售日期之前误报
581014「拉取产品信息失败,请稍后重试」(像系统故障),现已改为581041「所选出发日期不可售或未配置价格,请重新选择出发日期」,前端可直接透传该 message 给用户。581014现仅表示真正的产品服务不可达。前端按 §2 限制好选择器后,正常路径不会触发 581041,它是用户绕过限制时的兜底提示。
4. 实测确认
- 价格日历接口
GET /admin/product/item/2045345825172639746/pricing-calendar?tierSeq=1:200,data.items共 91 条,sellable=true13 条(2026-06-18~2026-06-30)✓ - 创单非可售日期
2026-11-15:被拦截581041「所选出发日期不可售或未配置价格…」(PR #4008 优化后),无新增订单 ✓ - 创单过去日期
2026-06-01:被拦截581011✓ - 均经测试服网关 9443 + 真实 admin token 验证。
备注
- 本文为前端缺陷通知 + 现有接口对接说明,无后端代码变更、零 DDL、无新增/改动接口。
pricing-calendar接口为产品管理「价格日历」长期已上线端点,CORE/CUSTOM 走product_price_calendar逐日价、GROUP 走group_tour_batch班期(按出发日),前端按data.productType区分渲染。- 小程序端下单日历亦是同口径(按可售日置灰),管理后台对齐即可。