docs(mp-product): 小程序报价接口补早鸟优惠字段 (PR #1052)
MpQuoteResultVO 新增 5 个字段: earlyBirdDiscount / earlyBirdPlanId / earlyBirdPlanName / finalBalanceAmount / finalGrandTotal - 定金模式: 早鸟扣尾款 → finalBalanceAmount = balanceAmount - discount - 全款模式: 早鸟扣全款 → finalGrandTotal = grandTotal - discount - 未命中: earlyBirdDiscount=0, finalXxx=原值
这个提交包含在:
父节点
941e3453d5
当前提交
5915b439b5
@ -0,0 +1,192 @@
|
||||
# 小程序产品报价 - `/mp/product/{id}/quote` 接口补早鸟优惠字段
|
||||
|
||||
- **日期**: 2026-04-21
|
||||
- **PR**: [#1052](https://git.1814.love:8443/wx/HL/pulls/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 - earlyBirdDiscount`
|
||||
- `finalGrandTotal = grandTotal - earlyBirdDiscount`(整单一致)
|
||||
- **全款模式 (FULL)**:早鸟优惠**从全款扣**
|
||||
- `finalBalanceAmount = null`
|
||||
- `finalGrandTotal = grandTotal - earlyBirdDiscount`
|
||||
- **未命中 / 查询失败** → `earlyBirdDiscount = 0`,`earlyBirdPlanId = null`,`finalXxx = 原值`
|
||||
- **人数门槛**:总人数(成人 + 儿童 + 小童 + 幼童)< 计划 `minPeople` → 不命中
|
||||
- **金额封顶**:早鸟优惠 ≤ 对应金额池(定金模式下不超过尾款;全款模式下不超过总价),不会让尾款 / 全款为负
|
||||
|
||||
---
|
||||
|
||||
## 五、响应示例
|
||||
|
||||
### 定金模式 + 命中早鸟(2 成人)
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 全款模式 + 命中早鸟
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"grandTotal": 8000.00,
|
||||
"paymentType": "FULL",
|
||||
"balanceAmount": null,
|
||||
|
||||
"earlyBirdDiscount": 500.00,
|
||||
"earlyBirdPlanId": "1234567890",
|
||||
"earlyBirdPlanName": "全款早鸟",
|
||||
"finalBalanceAmount": null,
|
||||
"finalGrandTotal": 7500.00
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 未命中早鸟
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"grandTotal": 5000.00,
|
||||
"balanceAmount": 4000.00,
|
||||
|
||||
"earlyBirdDiscount": 0,
|
||||
"earlyBirdPlanId": null,
|
||||
"earlyBirdPlanName": null,
|
||||
"finalBalanceAmount": 4000.00,
|
||||
"finalGrandTotal": 5000.00
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、前端使用建议
|
||||
|
||||
### 场景 A:报价页展示早鸟优惠
|
||||
|
||||
```tsx
|
||||
{data.earlyBirdDiscount > 0 && (
|
||||
<View className="discount-row">
|
||||
<Text>早鸟优惠 {data.earlyBirdPlanName}</Text>
|
||||
<Text>-¥{data.earlyBirdDiscount}</Text>
|
||||
</View>
|
||||
)}
|
||||
```
|
||||
|
||||
### 场景 B:定金模式下拆分展示
|
||||
|
||||
```tsx
|
||||
<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:全款模式下拆分展示
|
||||
|
||||
```tsx
|
||||
{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 新增字段,既有字段语义与取值完全不变。旧客户端(没更新适配的)继续按原逻辑渲染,只是看不到早鸟优惠而已。
|
||||
|
||||
---
|
||||
|
||||
## 八、回归验证
|
||||
|
||||
```bash
|
||||
# 测试产品(班期内有早鸟"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.00`
|
||||
- `earlyBirdPlanName: "BUG20测试-2人"`
|
||||
- `finalBalanceAmount = balanceAmount - 100`
|
||||
- `finalGrandTotal = grandTotal - 100`
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户