diff --git a/changelogs-v2/2026-07/58_新建订单预估价改用产品报价接口-前端待处理-管理后台.md b/changelogs-v2/2026-07/58_新建订单预估价改用产品报价接口-前端待处理-管理后台.md new file mode 100644 index 0000000..fc9193e --- /dev/null +++ b/changelogs-v2/2026-07/58_新建订单预估价改用产品报价接口-前端待处理-管理后台.md @@ -0,0 +1,120 @@ +# 新建订单预估价改用产品报价接口 - 前端待处理 - 管理后台 + +> 日期:2026-07-13 +> 端类型:管理后台(新建订单向导,第 3 步基本信息及第 4 步确认创建) +> 服务:`hl-product-service-v2`(现有接口,无后端代码变更、无 DDL) +> 责任端:`hl-ui` +> 关联说明:`2026-06/18_新建订单出发日期选择器需限制可售日期-前端待修-管理后台.md` + +## 1. 结论 + +新建订单的预估总价必须在用户选择出发日期后,通过产品服务报价接口计算。禁止继续使用产品起价 `startPrice` 或 `儿童数 * 0.7` 在前端估算。 + +两个接口职责不同: + +| 接口 | 职责 | +|---|---| +| `GET /admin/product/item/{productId}/pricing-calendar` | 获取可售日期;日期选择器按 `sellable` 禁用不可售日期,同时保留该日价格/班期信息。 | +| `POST /admin/product/item/{productId}/quote` | 按已选出发日期、档位及四类人数执行权威算价;预估总价直接展示响应 `grandTotal`。 | + +`quote` 后端会按产品类型读取正确价格源:CORE/CUSTOM 读取所选日期的 `product_price_calendar`,GROUP 读取所选日期的 `group_tour_batch`。前端不要复制价格日历计算规则。 + +## 2. 当前缺陷 + +只读检查当前 `hl-ui`: + +| 位置 | 当前行为 | 问题 | +|---|---|---| +| `src/views/order-v2/new/components/Step2Info.vue` | `startPrice * (adultCount + childCount * 0.7)` | 起价不是所选日期价格,儿童价也不是成人价的固定比例。 | +| `src/views/order-v2/new/components/Step3Confirm.vue` | 再次使用相同本地公式 | 确认页会重复展示错误金额。 | +| `src/api/product/pricing.js` | 已有统一价格日历方法,无管理端报价方法 | 新建订单流程没有调用现有报价接口。 | + +截图样本的测试环境实测: + +| 条件 | 值 | +|---|---:| +| 产品 ID | `2056944461216100353` | +| 出发日期 / 档位 | `2026-07-14` / `tierSeq=1` | +| 价格日历 | 成人 `4000.00`,儿童 `3000.00` | +| 人数 | 2 成人、1 儿童 | +| 当前前端本地公式 | `10800.00`(错误) | +| 后端 `quote.grandTotal` | `11000.00`(正确) | + +## 3. 报价接口契约 + +### 请求 + +`POST /admin/product/item/{productId}/quote` + +```json +{ + "departureDate": "2026-07-14", + "adultCount": 2, + "childCount": 1, + "toddlerCount": 0, + "infantCount": 0, + "tierSeq": 1, + "batchId": null +} +``` + +| 请求字段 | 必填 | 新建订单页面来源 | +|---|---|---| +| `productId` | 是 | URL path,选中的 `draft.product.productId`,雪花 ID 按字符串透传。 | +| `departureDate` | 是 | `draft.departureDate`;未选择时禁止发起报价。 | +| `adultCount` | 是 | `draft.adultCount`,最少 1。 | +| `childCount` | 否 | `draft.childCount`,默认 0。 | +| `toddlerCount` | 否 | `draft.youngChildCount`,默认 0。 | +| `infantCount` | 否 | `draft.babyCount`,默认 0。 | +| `tierSeq` | 否 | `draft.product.tierSeq`,默认 1。 | +| `batchId` | GROUP 时建议传 | 从所选日期对应的 `pricing-calendar.items[].batchId` 取得;CORE/CUSTOM 不传。 | + +### 响应 + +```json +{ + "code": 200, + "data": { + "adultUnitPrice": "4000.00", + "childUnitPrice": "3000.00", + "toddlerUnitPrice": "0", + "infantUnitPrice": "0.00", + "totalAdultPrice": "8000.00", + "totalChildPrice": "3000.00", + "totalToddlerPrice": "0", + "totalInfantPrice": "0.00", + "grandTotal": "11000.00", + "singleRoomDiff": "0", + "singleRoomSurcharge": "0" + } +} +``` + +金额字段按字符串处理和展示,禁止先转 JS `Number` 后再长期保存。预估总价使用 `data.grandTotal`,不要由各单价在前端重新相加。 + +## 4. 前端处理要求 + +1. 未选择出发日期时,预估总价显示“请选择出发日期”或“待计算”,不得展示基于起价的金额。 +2. 选择出发日期后调用 `quote`;产品、档位、出发日期、成人数、儿童数、小童数、幼童数任一变化,都要重新报价。 +3. 请求期间展示明确加载态;连续修改人数时做短防抖,并用请求序号或取消旧请求防止旧响应覆盖新结果。 +4. 报价失败时清空旧报价,不允许退回 `startPrice`/`0.7` 本地估算;展示后端错误信息并阻止带着过期报价进入确认页。 +5. 第 4 步确认页复用与当前输入完全匹配的最新报价;若报价依赖项已变化或无有效报价,进入确认页前重新调用一次。 +6. 删除 `Step2Info.vue` 和 `Step3Confirm.vue` 中 `childCount * 0.7` 及 `startPrice` 预估总价逻辑。 +7. `pricing-calendar` 仍用于可售日期限制,不要删除;`quote` 是其后的权威算价步骤。 + +## 5. 验收清单 + +- [ ] 未选出发日期时不展示金额,也不调用报价接口。 +- [ ] 选择 `2026-07-14`、`tierSeq=1`、2 成人 1 儿童后,样本产品预估总价显示 `11000.00`,不再显示 `10800.00`。 +- [ ] 成人/儿童/小童/幼童任一人数变化均重新报价,页面结果与最后一次请求一致。 +- [ ] 切换产品、档位或日期后,旧报价立即失效并重新获取。 +- [ ] CORE/CUSTOM 按所选日期价格日历算价;GROUP 携带所选班期 `batchId` 后报价正确。 +- [ ] 报价接口失败时不展示旧金额、不使用本地比例兜底。 +- [ ] 第 3 步与第 4 步显示同一份有效 `grandTotal`。 + +## 6. 验证证据 + +- 当前源码已确认 `POST /admin/product/item/{id}/quote` 为管理端正式报价接口。 +- Knife4j 测试环境聚合文档已确认该接口存在。 +- 2026-07-13 经测试环境网关实调:价格日历与报价接口均返回 `code=200`;上述样本 `grandTotal=11000.00`。 +- 本通知只修改 `hl-api-changelog`,未修改 `hl-ui` 或后端业务代码。