From 0af1ec232635bb30178628d4d19c39fa880a4c76 Mon Sep 17 00:00:00 2001 From: wx Date: Thu, 30 Apr 2026 17:26:40 +0800 Subject: [PATCH] =?UTF-8?q?2026-04-30:=20fee-deduction=20=E6=94=B9?= =?UTF-8?q?=E5=90=8D=20=E6=99=AF=E5=8C=BA/=E6=B8=B8=E7=8E=A9=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=20+=20name=20=E5=8E=BB=E5=89=8D=E7=BC=80=20(PR=20#158?= =?UTF-8?q?1=20+=20#1583)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...t_fee-deduction-display-label-no-prefix.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 changelogs/2026-04/30_fix_product_fee-deduction-display-label-no-prefix.md diff --git a/changelogs/2026-04/30_fix_product_fee-deduction-display-label-no-prefix.md b/changelogs/2026-04/30_fix_product_fee-deduction-display-label-no-prefix.md new file mode 100644 index 0000000..ab3058f --- /dev/null +++ b/changelogs/2026-04/30_fix_product_fee-deduction-display-label-no-prefix.md @@ -0,0 +1,126 @@ +# 修复: 费用包含 feeType 改名 景区/游玩项目 + name 去前缀 + +**类型**: 后端 BUG 修复(PR #1579 双 follow-up) +**关联**: 工单 #1580 / PR #1581 + 工单 #1582 / PR #1583 +**日期**: 2026-04-30 +**前端处理者**: yst / mmg +**影响范围**: 管理后台「产品编辑 → 补充信息 → 费用说明 →『根据行程自动生成』」按钮 + +--- + +## 演进链(完整 5 PR) + +| PR | 行为 | 产品 2045018534152478721 返回 | +|---|---|---| +| #1568 | 修复漏景区/活动/酒店,每节点 1 条 | 35 条 | +| #1577 | 按名字去重,name 改具体名 | 17 条 | +| #1579 | 按 feeType 合并,name="门票:xxx" | 3 条(有 feeType 前缀) | +| **#1581** | feeType 改名 门票→景区,活动→游玩项目 | 3 条(name="景区:xxx") | +| **#1583** | name 去 feeType 前缀,只剩名字拼接 | 3 条(name="黑山头,...") | + +## 用户反馈 + +- 「怎么没分为 景区 游玩项目」(→ PR #1581:feeType 改名) +- 「不要我圈出来的直接显示名字就行」(→ PR #1583:name 去前缀) + +## 字段语义 + +| 字段 | 值 | +|---|---| +| `feeType` | **景区 / 游玩项目 / 住宿 / 餐饮 / 交通**(展示名,前端 chip 用) | +| `name` | `"黑山头,白桦林景区,莫日格勒河,..."` 仅名字拼接(中文「,」分隔) | +| `sourceNodeName` | null | +| `icon` | null(PR #1573 行为) | + +## 实现要点 + +```java +// PR #1581: 加 displayLabel 私有静态映射 +private static final Map FEE_LABEL_DISPLAY = Map.of( + "门票", "景区", // 用户期望 + "活动", "游玩项目", // 用户期望 + "住宿", "住宿", + "餐饮", "餐饮", + "交通", "交通" +); + +// PR #1583: name 去前缀,直接 join +.map(e -> { + String displayLabel = toDisplayLabel(e.getKey()); + String joinedNames = String.join(",", e.getValue()); + return FeeDeductionPreviewVO.builder() + .feeType(displayLabel) // chip 用展示名 + .name(joinedNames) // 仅名字列表,无 feeType 前缀 + .source(SOURCE_AUTO) + .sourceNodeName(null) + .build(); +}) +``` + +**底层逻辑保持**: +- 分组 key 仍是 `feeLabel`("门票"/"活动"/"住宿"等) +- 排序 key 仍按 `feeLabel`(门票 → 活动 → 住宿 → 餐饮 → 交通) +- `feeIgnoredTypes` 过滤仍按 `feeLabel`(保护历史数据兼容) +- `NodeTypeEnum.getFeeLabel()` **不动**(防止影响别处) + +## 影响接口 + +| 方法 | 路径 | 改动 | +|------|------|------| +| GET | `/admin/product/item/{id}/fee-deduction-preview` | `feeType` 字段值改为展示名(景区/游玩项目),`name` 字段去掉前缀 | + +请求签名完全不变,响应字段不变。 + +## 响应示例 + +```jsonc +GET /admin/product/item/2045018534152478721/fee-deduction-preview +{ + "code": 200, + "data": [ + { + "feeType": "景区", + "name": "黑山头,白桦林景区,莫日格勒河,莫日格勒河断崖,呼伦湖,阿尔山火车站", + "source": "AUTO", + "sourceNodeName": null, + "icon": null + }, + { + "feeType": "游玩项目", + "name": "ATV全地形越野车穿越,驯鹿拉车体验", + "source": "AUTO", + "sourceNodeName": null, + "icon": null + }, + { + "feeType": "住宿", + "name": "海拉尔洲季酒店,呼伦贝尔香格里拉大酒店,额尔古纳玖成万豪水汇智能酒店,额尔古纳豪星酒店,额尔古纳白桦大酒店,海拉尔海棠酒店,满洲里饭店(百年俄式),敖根栖息地,阿尔善国际维景度假温泉酒店", + "source": "AUTO", + "sourceNodeName": null, + "icon": null + } + ] +} +``` + +(注:实际中文逗号是「,」非半角) + +## 前端要做的改动 + +**0 改动**。前端 chip `{{ item.feeType }}` 显示「景区/游玩项目/住宿」,文字 `{{ item.name }}` 显示纯名字列表,UI 无重复无冗余。 + +## 测试覆盖 + +- PR #1581:`mvn test` 1047/1047 全绿,加 `feeDeductionPreview_displayLabelTranslation` 验证 SCENIC→景区 / ACTIVITY→游玩项目 映射 +- PR #1583:21 处 name 断言批量去前缀,`ProductValidationServiceTest` 53/53 全绿 + +测试服 round-trip(产品 `2045018534152478721`)实测: +- 3 条返回 +- feeType: 景区 / 游玩项目 / 住宿(顺序固定) +- name: 黑山头,白桦林景区,... / ATV全地形越野车穿越,驯鹿拉车体验 / 海拉尔洲季酒店,呼伦贝尔香格里拉大酒店,...(无前缀) +- sourceNodeName / icon 全 null + +## 部署状态 + +- ✅ Deploy Panel 已部署测试服 hl-product-service-v2 双实例(2026-04-30 17:25) +- 正式环境暂未发布(等 dev → main release PR)