MpQuoteResultVO 新增 5 个字段: earlyBirdDiscount / earlyBirdPlanId / earlyBirdPlanName / finalBalanceAmount / finalGrandTotal - 定金模式: 早鸟扣尾款 → finalBalanceAmount = balanceAmount - discount - 全款模式: 早鸟扣全款 → finalGrandTotal = grandTotal - discount - 未命中: earlyBirdDiscount=0, finalXxx=原值
5.9 KiB
5.9 KiB
小程序产品报价 - /mp/product/{id}/quote 接口补早鸟优惠字段
- 日期: 2026-04-21
- PR: #1052 (Closes #1047)
- 类型: FEATURE(响应 VO 新增字段,向前兼容)
- 服务: hl-product-service-v2(路由
/mp/product/**) - 前端是否需要改动: 建议改动(展示早鸟优惠金额 + 最终应付金额给用户)
一、背景
之前的报价接口只返回「原价合计」与「原尾款/定金」,但订单创建时后端会自动扣早鸟优惠(OrderCreateService.tryApplyEarlyBirdDiscount()),导致:
- 小程序报价页显示
¥10,510尾款 - 用户提交订单实付却是
¥10,410(已扣 100 元早鸟) - 用户困惑 / 客服被投诉报价不准
本次修复让报价接口同步返回早鸟优惠信息,与订单实扣完全一致。
二、变更接口
| # | 方法 | 路径 | 变更类型 |
|---|---|---|---|
| 1 | POST | /mp/product/{id}/quote |
响应 VO 新增 5 个字段;请求参数不变 |
三、响应 VO 新增字段(MpQuoteResultVO)
| 字段 | 类型 | 含义 | 示例 |
|---|---|---|---|
earlyBirdDiscount |
BigDecimal | 早鸟优惠金额(未命中为 0) | 100.00 |
earlyBirdPlanId |
Long (字符串) | 命中的早鸟计划 ID(未命中为 null) | "2046080027564318721" |
earlyBirdPlanName |
String | 命中的早鸟计划名称(展示用,未命中为 null) | "暑期早鸟" |
finalBalanceAmount |
BigDecimal | 定金模式下最终应付尾款 = balanceAmount - earlyBirdDiscount;全款模式为 null |
10410.00 |
finalGrandTotal |
BigDecimal | 最终应付总价 = grandTotal - earlyBirdDiscount |
11410.00 |
其它字段不变:grandTotal / balanceAmount / depositAmount 仍是「原始价」,仅新增的 finalXxx 是「扣优惠后最终应付」。
四、业务规则(与订单实扣对齐)
- 早鸟优惠按下单日期生效(调用报价的当下,等于订单创建时的日期)
- 定金模式 (DEPOSIT):定金原价收,早鸟优惠从尾款扣
finalBalanceAmount = balanceAmount - earlyBirdDiscountfinalGrandTotal = grandTotal - earlyBirdDiscount(整单一致)
- 全款模式 (FULL):早鸟优惠从全款扣
finalBalanceAmount = nullfinalGrandTotal = grandTotal - earlyBirdDiscount
- 未命中 / 查询失败 →
earlyBirdDiscount = 0,earlyBirdPlanId = null,finalXxx = 原值 - 人数门槛:总人数(成人 + 儿童 + 小童 + 幼童)< 计划
minPeople→ 不命中 - 金额封顶:早鸟优惠 ≤ 对应金额池(定金模式下不超过尾款;全款模式下不超过总价),不会让尾款 / 全款为负
五、响应示例
定金模式 + 命中早鸟(2 成人)
{
"code": 200,
"success": true,
"data": {
"adultUnitPrice": 5755.00,
"totalAdultPrice": 11510.00,
"grandTotal": 11510.00,
"paymentType": "DEPOSIT",
"depositAmount": 1000.00,
"balanceAmount": 10510.00,
"earlyBirdDiscount": 100.00,
"earlyBirdPlanId": "2046080027564318721",
"earlyBirdPlanName": "BUG20测试-2人",
"finalBalanceAmount": 10410.00,
"finalGrandTotal": 11410.00
}
}
全款模式 + 命中早鸟
{
"data": {
"grandTotal": 8000.00,
"paymentType": "FULL",
"balanceAmount": null,
"earlyBirdDiscount": 500.00,
"earlyBirdPlanId": "1234567890",
"earlyBirdPlanName": "全款早鸟",
"finalBalanceAmount": null,
"finalGrandTotal": 7500.00
}
}
未命中早鸟
{
"data": {
"grandTotal": 5000.00,
"balanceAmount": 4000.00,
"earlyBirdDiscount": 0,
"earlyBirdPlanId": null,
"earlyBirdPlanName": null,
"finalBalanceAmount": 4000.00,
"finalGrandTotal": 5000.00
}
}
六、前端使用建议
场景 A:报价页展示早鸟优惠
{data.earlyBirdDiscount > 0 && (
<View className="discount-row">
<Text>早鸟优惠 {data.earlyBirdPlanName}</Text>
<Text>-¥{data.earlyBirdDiscount}</Text>
</View>
)}
场景 B:定金模式下拆分展示
<View>订金(立付):¥{data.depositAmount}</View>
{data.earlyBirdDiscount > 0 ? (
<>
<View className="original">原尾款:<s>¥{data.balanceAmount}</s></View>
<View className="final">最终尾款:<b>¥{data.finalBalanceAmount}</b></View>
</>
) : (
<View>尾款:¥{data.balanceAmount}</View>
)}
场景 C:全款模式下拆分展示
{data.paymentType === 'FULL' && data.earlyBirdDiscount > 0 ? (
<>
<View>原价:<s>¥{data.grandTotal}</s></View>
<View className="final">最终应付:<b>¥{data.finalGrandTotal}</b></View>
</>
) : (
<View>应付:¥{data.grandTotal}</View>
)}
⚠️ 注意
earlyBirdPlanId是 Long,JSON 序列化为字符串防精度丢失(与项目其它 ID 字段一致)- 未命中早鸟时
finalBalanceAmount/finalGrandTotal等于原值,可以无脑展示finalXxx(无需判断) earlyBirdDiscount一定是BigDecimal形式的数字(0或>0),不会是 null- 全款模式下
finalBalanceAmount固定为null(与balanceAmount语义保持一致)
七、不兼容变更
无。仅响应 VO 新增字段,既有字段语义与取值完全不变。旧客户端(没更新适配的)继续按原逻辑渲染,只是看不到早鸟优惠而已。
八、回归验证
# 测试产品(班期内有早鸟"BUG20测试-2人")
curl -sk -X POST "https://api.test.1814.love:9443/mp/product/2044603865881333762/quote" \
-H "Content-Type: application/json" \
-d '{"departureDate":"2026-05-10","adultCount":2,"childCount":0,"tierSeq":1}' | jq .data
预期:
earlyBirdDiscount: 100.00earlyBirdPlanName: "BUG20测试-2人"finalBalanceAmount = balanceAmount - 100finalGrandTotal = grandTotal - 100