diff --git a/changelogs/2026-05/09_fix_mp_order_detail_single_room_and_early_bird_fields.md b/changelogs/2026-05/09_fix_mp_order_detail_single_room_and_early_bird_fields.md new file mode 100644 index 0000000..a9379e4 --- /dev/null +++ b/changelogs/2026-05/09_fix_mp_order_detail_single_room_and_early_bird_fields.md @@ -0,0 +1,115 @@ +# mp 订单详情单房差展示修复 + 5 字段独立暴露 + +> **服务**: hl-order-service-v2(8094) +> **PR**: #1906 + #1909 (Refs #1900) +> **日期**: 2026-05-09 +> **影响范围**: 小程序订单详情页 + +--- + +## ⚠️ 关键变化 + +`GET /mp/order/{orderId}` 接口: + +1. **修复 GROUP 订单 priceBreakdown JSON 漏字段**(#1906):创建 GROUP 订单时 `priceBreakdownJson` 没写 `singleRoomDiff` / `singleRoomSurcharge`,导致明细面板看不到"单房差"行 +2. **新增 5 个独立字段**(#1909):对齐 `POST /mp/product/{id}/quote` 接口 + +--- + +## 一、新增字段(`MpOrderDetailVO`) + +| 字段 | 类型 | 说明 | +|------|------|------| +| `singleRoomSurcharge` | BigDecimal | 单房差实际加价金额(下单时按 D1: `adultCount==1` 触发);老订单/不触发 = `null` | +| `earlyBirdDiscount` | BigDecimal | 早鸟优惠金额(从 `order_discount` 表 EARLY_BIRD 取);无早鸟 = `null` | +| `earlyBirdPlanName` | String | 命中的早鸟计划名称(格式 `"早鸟优惠:xxx"`);无早鸟 = `null` | +| `finalGrandTotal` | BigDecimal | **应付总额** = `priceBreakdown.totalPayable` = `totalPrice - 优惠总额 + 附加费总额` | +| `finalBalanceAmount` | BigDecimal | **应付尾款**:DEPOSIT 模式 = `balanceAmount - earlyBirdDiscount`;FULL 模式 = `null` | + +字段位置:`discountAmount` 之后、`priceBreakdown` 之前。 + +## 二、响应示例 + +订单 1 大 1 小、班期单房差 500、命中早鸟优惠 200、DEPOSIT 模式(订金 1000): + +```json +{ + "code": 200, + "data": { + "orderId": "2052941051093520386", + "adultCount": 1, + "childCount": 1, + "totalPrice": 5850.00, + "depositAmount": 1000.00, + "balanceAmount": 5850.00, + "discountAmount": 200.00, + "singleRoomSurcharge": 500.00, + "earlyBirdDiscount": 200.00, + "earlyBirdPlanName": "早鸟优惠:暑期早鸟", + "finalGrandTotal": 5650.00, + "finalBalanceAmount": 5650.00, + "priceBreakdown": { + "items": [ + { "label": "成人", "unitPrice": 2925, "quantity": 1, "amount": 2925 }, + { "label": "儿童", "unitPrice": 2425, "quantity": 1, "amount": 2425 }, + { "label": "单房差", "unitPrice": null, "quantity": null, "amount": 500 } + ], + "subtotal": 5850.00, + "discounts": [ + { "tag": "早鸟优惠", "description": "早鸟优惠:暑期早鸟", "amount": -200.00 } + ], + "totalDiscount": -200.00, + "totalPayable": 5650.00 + } + } +} +``` + +## 三、字段使用建议(mmg) + +订单详情页价格区域可用**两种展示风格**任选其一: + +### 风格 A:简洁(推荐,与 quote 接口一致) + +``` +团费总价 ¥5,850.00 ← totalPrice (含单房差) +单房差 +¥500.00 ← singleRoomSurcharge (条件展示, null 隐藏) +早鸟优惠 -¥200.00 ← earlyBirdDiscount (条件展示, null 隐藏) +应付总额 ¥5,650.00 ← finalGrandTotal (粗体,主视觉) +其中订金 ¥1,000.00 ← depositAmount +应付尾款 ¥5,650.00 ← finalBalanceAmount (DEPOSIT 模式) +``` + +### 风格 B:展开明细(用现有 priceBreakdown 数组渲染) + +继续用 `priceBreakdown.items` + `priceBreakdown.discounts` + `priceBreakdown.surcharges` 列表渲染明细,**总价显示用 `finalGrandTotal`**(等同 `priceBreakdown.totalPayable`)。 + +## 四、PR #1906 修复内容 + +**问题**:`GroupOrderStrategy.calculatePrice` 序列化 `priceBreakdownJson` 时手动 copy 字段,**漏 set `singleRoomDiff` 和 `singleRoomSurcharge`**。导致 mp 端 `priceBreakdown.items` 解析时 `isMissingNode` 跳过,GROUP 订单详情明细面板看不到"单房差"行。 + +**修复**:加 2 行 `q.setSingleRoomDiff(quote.getSingleRoomDiff()); q.setSingleRoomSurcharge(quote.getSingleRoomSurcharge());` + +`CoreOrderStrategy.calculatePrice` 直接 `objectMapper.writeValueAsString(quote)` 序列化整个 SimpleQuoteVO,**无此问题**。 + +## 五、向后兼容 + +- 老订单 `priceBreakdown` JSON 缺 `singleRoomSurcharge` → 5 字段中 `singleRoomSurcharge=null`,其他正常 +- 无早鸟订单 → `earlyBirdDiscount=null`,`earlyBirdPlanName=null`,`finalBalanceAmount=balanceAmount`(无优惠减) +- FULL 模式 → `finalBalanceAmount=null`(全款无尾款概念) +- 老前端不读新字段不报错(Jackson `fail-on-unknown-properties=false`) +- 未改 `totalPrice` / `discountAmount` / `priceBreakdown` 现有字段语义 + +## 六、测试覆盖 + +- `GroupOrderStrategyTest +1`(快照含 singleRoomDiff/Surcharge,#1906) +- `MpOrderDetailAssemblerTest +4`(单房差+早鸟+老订单+FULL,#1909) +- 共 +5 用例,**全绿** + +## 七、@mmg 关注点 + +1. 订单详情页价格区域改造:加"单房差"行 + "早鸟优惠"行(都是条件展示,null 隐藏) +2. 总价用 `finalGrandTotal`(应付总额)替代 `totalPrice`(后者含单房差但不减优惠,继续保留作为"团费原价") +3. 应付尾款 DEPOSIT 模式用 `finalBalanceAmount`,FULL 模式用 `totalPrice` +4. 老前端不动也能跑(只是看不到新字段拆分) +5. 已存在的"老订单"(单房差功能上线前下的)`singleRoomSurcharge=null` 仍按原 totalPrice 展示,行为不变