2026-04-30: fee-deduction 按名字去重 + name 字段填具体名 (PR #1577)
这个提交包含在:
父节点
85f27e93fd
当前提交
6d26531198
@ -0,0 +1,123 @@
|
||||
# 修复: 费用包含按名字去重 + name 字段填具体景区/活动/酒店名
|
||||
|
||||
**类型**: 后端 BUG 修复(PR #1568 follow-up)
|
||||
**关联**: 工单 #1576 / PR #1577
|
||||
**日期**: 2026-04-30
|
||||
**前端处理者**: yst / mmg
|
||||
**影响范围**: 管理后台「产品编辑 → 补充信息 → 费用说明 →『根据行程自动生成』」按钮
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
PR #1568 已修复「自动生成」漏景区/活动/酒店的问题(每节点 1 条 + 追加酒店),但用户使用后反馈 2 个新问题:
|
||||
|
||||
### 问题 1: 同名条目重复
|
||||
|
||||
同一酒店多天住就出现多次 — 产品 `2045018534152478721`「游牧的森林·南线经典」自动生成 35 条结果中:
|
||||
- 「海拉尔洲季酒店」出现 **11 次**(11 天住同一酒店)
|
||||
- 「呼伦贝尔香格里拉大酒店」出现 **4 次**
|
||||
- 「额尔古纳玖成万豪水汇智能酒店」出现 **4 次**
|
||||
- 27 条住宿真实只有 **9 家不同酒店**
|
||||
|
||||
### 问题 2: `name` 字段无信息量
|
||||
|
||||
三种类型(景区/活动/酒店)的 `name` 字段都填 `feeType` 标签 — 即:
|
||||
- 6 条门票项 `name` 都是 `"门票"`(实际景区名是黑山头/白桦林景区/莫日格勒河等)
|
||||
- 27 条住宿项 `name` 都是 `"住宿"`(实际酒店名各不同)
|
||||
|
||||
前端中间 label 区域显示重复的 chip 文字,无信息量。
|
||||
|
||||
## 修复
|
||||
|
||||
`hl-product-service-v2` `ProductValidationService.feeDeductionPreview()`:
|
||||
|
||||
### 1. 按名字去重
|
||||
|
||||
```java
|
||||
LinkedHashSet<String> seenNodeNames = new LinkedHashSet<>();
|
||||
for (ProductItineraryNodeDO node : sortedNodes) {
|
||||
String nodeName = node.getNodeName();
|
||||
if (nodeName == null || nodeName.isBlank()) continue;
|
||||
if (!seenNodeNames.add(nodeName)) continue; // ← 按名字去重
|
||||
result.add(...);
|
||||
}
|
||||
|
||||
LinkedHashSet<String> seenHotelNames = new LinkedHashSet<>();
|
||||
for (ProductDayHotelDO hotel : sortedHotels) {
|
||||
String hotelName = hotel.getHotelName();
|
||||
if (hotelName == null || hotelName.isBlank()) continue;
|
||||
if (!seenHotelNames.add(hotelName)) continue;
|
||||
result.add(...);
|
||||
}
|
||||
```
|
||||
|
||||
`LinkedHashSet` 保留**首次出现**(行程顺序里第一次见到该名字的那条)。
|
||||
|
||||
### 2. `name` 字段填具体名
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|---|---|---|
|
||||
| `feeType` | 门票/活动/住宿/餐饮/交通 | **不变**(前端 chip 用) |
|
||||
| `name` | 同 feeType | **节点名/酒店名** |
|
||||
| `sourceNodeName` | 节点名/酒店名 | **不变**(与 name 同值) |
|
||||
| `icon` | null | **不变**(PR #1573 行为) |
|
||||
|
||||
## 影响接口
|
||||
|
||||
| 方法 | 路径 | 改动 |
|
||||
|------|------|------|
|
||||
| GET | `/admin/product/item/{id}/fee-deduction-preview` | 数组**长度变短**(去重),每项 `name` 改为具体名,其他字段语义不变 |
|
||||
|
||||
请求签名完全不变,响应字段不变。
|
||||
|
||||
## 响应示例
|
||||
|
||||
```jsonc
|
||||
GET /admin/product/item/2045018534152478721/fee-deduction-preview
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
/* 6 门票 */
|
||||
{"feeType": "门票", "name": "黑山头", "source": "AUTO", "sourceNodeName": "黑山头", "icon": null},
|
||||
{"feeType": "门票", "name": "白桦林景区", "source": "AUTO", "sourceNodeName": "白桦林景区", "icon": null},
|
||||
/* ... 共 6 条 */
|
||||
/* 2 活动 */
|
||||
{"feeType": "活动", "name": "ATV全地形越野车穿越", "source": "AUTO", "sourceNodeName": "ATV全地形越野车穿越", "icon": null},
|
||||
{"feeType": "活动", "name": "驯鹿拉车体验", "source": "AUTO", "sourceNodeName": "驯鹿拉车体验", "icon": null},
|
||||
/* 9 住宿(去重后,不再有重复) */
|
||||
{"feeType": "住宿", "name": "海拉尔洲季酒店", "source": "AUTO", "sourceNodeName": "海拉尔洲季酒店", "icon": null},
|
||||
/* ... 共 9 条 */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
旧返回 35 条,新返回 **17 条**(6 门票 + 2 活动 + 9 住宿)。
|
||||
|
||||
## 前端要做的改动
|
||||
|
||||
**0 改动**。前端模板 `{{ item.name }}` 直接渲染就显示具体名字了。
|
||||
|
||||
`sourceNodeName` 字段值与 `name` 同值,如果前端模板里有 `← {{ item.sourceNodeName }}` 来源标签,会出现"门票 chip + 黑山头 + ← 黑山头"的视觉重复。前端**可选**:
|
||||
- 保留来源标签作为视觉标识(用户已习惯)
|
||||
- 或加 `v-if="item.sourceNodeName !== item.name"` 隐藏冗余
|
||||
|
||||
## 测试覆盖
|
||||
|
||||
`mvn test -pl hl-product-service-v2`: **1045/1045 全绿**。
|
||||
|
||||
新增 3 单测:
|
||||
- `feeDeductionPreview_duplicateNodeNames_dedupedByName` — 5 个同名 SCENIC 节点 → result.size=1
|
||||
- `feeDeductionPreview_sameHotelMultipleDays_dedupedByName` — 11 个同酒店多天 → result.size=1
|
||||
- `feeDeductionPreview_nullOrBlankName_skipped` — null/空名字跳过
|
||||
|
||||
测试服 round-trip(产品 `2045018534152478721`)实测:
|
||||
- 总计 **17 条**(6 + 2 + 9)
|
||||
- 全部 `name` unique(去重生效)
|
||||
- 全部 `name` 是具体名字(黑山头/ATV 全地形越野车穿越/海拉尔洲季酒店 等)
|
||||
- 全部 `icon` null(PR #1573 行为不变)
|
||||
|
||||
## 部署状态
|
||||
|
||||
- ✅ Deploy Panel 已部署测试服 hl-product-service-v2 双实例(2026-04-30 16:56)
|
||||
- 正式环境暂未发布(等 dev → main release PR)
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户