From b5ceb15d50c110ce49dffdf8a847cfe7bc267d25 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 23 May 2026 11:22:04 +0800 Subject: [PATCH] =?UTF-8?q?docs:=2023=E6=97=A5=E7=B4=AF=E8=AE=A1=203=20?= =?UTF-8?q?=E6=9D=A1=20changelog=20(banner=20=E5=AD=97=E6=AE=B5=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=20+=20SMS=20info=20hotfix=20+=20compare=20=E6=8A=B9?= =?UTF-8?q?=E7=A9=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- ...at_banner_product_tags_seasons_top_tags.md | 117 ++++++++++++++++++ ...ix_admin_info_token_role_sms_customizer.md | 65 ++++++++++ ..._fix_compare_strip_subtitle_intro_crowd.md | 62 ++++++++++ 3 files changed, 244 insertions(+) create mode 100644 changelogs/2026-05/23_feat_banner_product_tags_seasons_top_tags.md create mode 100644 changelogs/2026-05/23_fix_admin_info_token_role_sms_customizer.md create mode 100644 changelogs/2026-05/23_fix_compare_strip_subtitle_intro_crowd.md diff --git a/changelogs/2026-05/23_feat_banner_product_tags_seasons_top_tags.md b/changelogs/2026-05/23_feat_banner_product_tags_seasons_top_tags.md new file mode 100644 index 0000000..b69d2e3 --- /dev/null +++ b/changelogs/2026-05/23_feat_banner_product_tags_seasons_top_tags.md @@ -0,0 +1,117 @@ +# feat(banner): /mp/banner/active 顶层 tags + PRODUCT 类型补 tags / seasons fallback + +> **仓库**: HL (后端 hl-mp-service + hl-user-service + hl-product-service-v2) +> **关联 PR/Issue**: PR #2952, Closes #2950 +> **日期**: 2026-05-23 +> **影响范围**: `/mp/banner/active` 响应新增顶层 `tags` + PRODUCT 类型 linkTarget 补 tags / seasons fallback +> **接收方**: mmg (前端) +> **前端**: **需要消费新字段** + +--- + +## 🎯 wx 实测反馈 + +1. **轮播图自身标签** (admin 编辑 banner 时填的「研学证书」)在 `/mp/banner/active` 没返回 +2. **PRODUCT 类型 banner** 跳产品时, linkTarget 缺产品自身标签 (`product.tags`), seasons 字段也常为空 + +## 改动 + +### 1. Banner 顶层 tags + +`MpBannerVO` 加 `tags: List` 字段, 来自 `sys_banner.tags`。 + +之前精简掉了 tags 字段, Feign 反序列化下游 `BannerVO.tags` 被 Jackson 自动丢弃。 + +### 2. PRODUCT 类型 linkTarget 补 tags + seasons fallback + +- `InternalProductSimpleVO` (product-v2 + user-service 镜像) 加 `tags: List` 字段 +- `ProductMapper.selectSimpleInfoByIds` `.select()` 补 `ProductDO::getTags` +- `ProductHelperService.selectSimpleByIds` builder 补 `.tags(p.getTags())` +- `BannerService.enrichLinkTarget` PRODUCT 分支: + - 透传 `p.getTags()` (**产品自身标签, 不 fallback 主题** — 产品有自己的标签语义) + - **seasons fallback**: 产品自己 seasons 为空且有 line_id → 取 line.seasons + - lineIds 收集逻辑扩到 productMap.values().lineId, 复用 lineMap 一次 Feign + +### 3. PRODUCT_LINE 分支不动 + +PR #2938/#2941 已做 (description + tags), 本期不动。 + +## API 行为变化 + +### `GET /mp/banner/active` 响应 + +```json +{ + "id": "2033821591988748290", + "title": "2026 亲子游学营", + "tags": ["研学证书"], // ← 新增, banner 自身标签 (sys_banner.tags) + "linkType": "PRODUCT", + "linkId": "...", + "linkTarget": { + "type": "PRODUCT", + "id": "...", + "name": "额吉的故乡V9·7天6晚旷野版", + "subtitle": "", + "coverImageUrl": "...", + "productType": "CORE", + "tags": ["亲子"], // ← 新增, 产品自身标签 (product.tags) + "seasons": ["winter"], // ← 修复 fallback line.seasons + "lineId": ..., + "description": null, // PRODUCT 场景不返 description (对称性) + }, + "sortOrder": 1 +} +``` + +### 两个 tags 字段独立语义 + +| 字段 | 来源 | 用途 | +|---|---|---| +| `banner.tags` | sys_banner.tags | 轮播图自身标签, 例「研学证书」、「私家团 / 亲子」 | +| `banner.linkTarget.tags` | PRODUCT_LINE: product_line.tags
PRODUCT: product.tags | 跳转目标自身标签, 例「限定」「亲子」「城记」 | + +--- + +## 🚨 前端需要做的事 + +### 1. 读取顶层 tags + +```ts +const banner = response.data[0]; +const bannerTags = banner.tags; // ["研学证书"] - banner 自身标签 +const linkTargetTags = banner.linkTarget?.tags; // ["亲子"] - 跳转目标标签 +``` + +### 2. 区分两个 tags + +前端轮播图卡片可在不同位置展示: +- `banner.tags` → 卡片右上角徽章 / 顶部 chip +- `banner.linkTarget.tags` → 跳转目标信息区(产品/主题卡片) + +如果两个 tags 在 UI 上不区分, 可合并去重展示。 + +### 3. seasons fallback 透明 + +`banner.linkTarget.seasons` 现在更可靠 — 产品没填时自动 fallback 产品线的 seasons, 前端按字段渲染即可。 + +--- + +## 测试服真测记录 + +部署 user + mp + product-v2 (rolling-deploy 2026-05-23 11:20) 后真测: + +| Banner | linkType | banner.tags | linkTarget.tags | linkTarget.seasons | linkTarget.description | +|---|---|---|---|---|---| +| #2033821591988748290 | PRODUCT | ✅ ['研学证书'] | ✅ ['亲子'] | ✅ ['winter'] (fallback line) | null (对称正确) | +| #5 (额吉的故乡) | PRODUCT_LINE | ✅ ['私家团','亲子'] | ✅ ['限定'] | ✅ ['winter'] | ✅ '公司的风格的' | + +单测: BannerServiceTest **37** + ProductHelperServiceTest **18** + InternalProductControllerTest **8** + mp Banner/Home **20** 全绿。 + +--- + +## 联系人 + +后端: wx (呼籁旅行) +前端: mmg + +如有疑问可在 [#2950](https://git.1814.love:8443/wx/HL/issues/2950) 评论区留言。 diff --git a/changelogs/2026-05/23_fix_admin_info_token_role_sms_customizer.md b/changelogs/2026-05/23_fix_admin_info_token_role_sms_customizer.md new file mode 100644 index 0000000..e577280 --- /dev/null +++ b/changelogs/2026-05/23_fix_admin_info_token_role_sms_customizer.md @@ -0,0 +1,65 @@ +# hotfix(user): /admin/auth/info 尊重 token role, SMS 登录强制 CUSTOMIZER + +> **仓库**: HL (后端 hl-user-service) +> **关联 PR/Issue**: PR #2946 (#2918 跟进, 非新工单) +> **日期**: 2026-05-23 +> **影响范围**: `/admin/auth/info` 响应 role 字段 +> **接收方**: mmg (前端) +> **前端**: **无需改动** — 响应字段类型不变, 仅 role/roleName 值修正 + +--- + +## wx 实测反馈 + +SMS 登录后顶部用户信息显示「超级管理员」, 不是「定制师」。前端 admin 端登录后角色 badge 异常。 + +## 根因 + +PR #2920 (Closes #2918) 让 SMS 登录响应里 `role=CUSTOMIZER` 是对的, **但**: + +- `getAdminInfo(adminId)` (Service) 默认按 `admin.currentRoleId` 算 activeRoleKey +- token 内嵌的 forced CUSTOMIZER 角色没传给 service +- 前端调 `/admin/auth/info` 重新拿到 currentRoleId 对应的 SUPER_ADMIN + +## 修复 + +- `AdminAuthService.getAdminInfo` 新签名 `(adminId, tokenRole)`: + - tokenRole=CUSTOMIZER 且 admin 真持有 CUSTOMIZER 角色 → activeRoleKey 强制 CUSTOMIZER + - 否则按现有 currentRoleId 逻辑 + - **不污染 admin.currentRoleId 持久化** (SMS 是临时切角色) +- `AuthController.getAdminInfo` 从 `request.getAttribute("role")` 透传 token role + +## 防御 + +`tokenRole=CUSTOMIZER` 但 admin 真实没 CUSTOMIZER 角色时 **NOT 强制**, 防止 token 纂改越权显示。 + +## API 行为对照 + +| token role | admin currentRoleId | admin 有 CUSTOMIZER | response.role | 说明 | +|---|---|---|---|---| +| 任意 | SUPER_ADMIN | - | SUPER_ADMIN | 旧逻辑 | +| CUSTOMIZER | SUPER_ADMIN | ✅ | **CUSTOMIZER** | **新, SMS 登录场景** | +| CUSTOMIZER | SUPER_ADMIN | ❌ | SUPER_ADMIN | 防御, 不信任 token | + +前端无需改动, 响应字段类型不变。 + +## 测试服真测 + +部署 user-service (2026-05-23 10:58) 后: + +| 场景 | response.role | response.roleName | +|---|---|---| +| SMS 登录 wx (持 SUPER_ADMIN+CUSTOMIZER 多角色) → /admin/auth/info | ✅ **CUSTOMIZER** | ✅ **定制师** | +| 密码登录 admin (SUPER_ADMIN) → /admin/auth/info | ✅ SUPER_ADMIN (不变) | ✅ 超级管理员 | + +## 单测 + +- AdminAuthServiceTest **69** 全绿 (新加 2 个 case: SMS 强制 / 防御不强制) +- AuthControllerTest **9** 全绿 (新加 verify tokenRole 透传) + +--- + +## 联系人 + +后端: wx (呼籁旅行) +前端: mmg diff --git a/changelogs/2026-05/23_fix_compare_strip_subtitle_intro_crowd.md b/changelogs/2026-05/23_fix_compare_strip_subtitle_intro_crowd.md new file mode 100644 index 0000000..bdb30a1 --- /dev/null +++ b/changelogs/2026-05/23_fix_compare_strip_subtitle_intro_crowd.md @@ -0,0 +1,62 @@ +# fix(mp): /mp/product/compare 抹空 subtitle / introduction / crowdBenefit + +> **仓库**: HL (后端 hl-product-service-v2) +> **关联 PR/Issue**: PR #2944, Closes #2943 +> **日期**: 2026-05-23 +> **影响范围**: `/mp/product/compare` 响应 3 个字段抹空 +> **接收方**: mmg (前端) +> **前端**: **建议忽略 null 字段** — compare 页面无需展示这 3 字段 + +--- + +## 业务背景 + +wx 反馈: 产品对比接口前端只关心核心规格差异 (price / tier / itinerary / hotel 等), 简介性字段不需要返回。 + +## 改动 + +`MpProductService.compareByLine` 调完 `getDetail` 后, 循环把每个 VO 的 `subtitle` / `introduction` / `crowdBenefit` set null。 + +抽 private `stripCompareIrrelevantFields(vo)` helper, 单职责清晰。 + +## 为什么用 set null 而不删 VO 字段 + +`MpProductDetailRespVO` 是 `/mp/product/detail` 和 `/mp/product/compare` 共享的 VO。直接删字段会破坏详情接口。新建 compare 独立 VO 字段众多 (30+) 复制成本高。set null 是最小改动 + 详情接口零影响 + 前端忽略 null 即可。 + +## API 行为变化 + +### `POST /mp/product/compare` + +每个产品对象: + +- `subtitle: null` (本来有值) +- `introduction: null` (本来有值) +- `crowdBenefit: null` (本来是嵌套对象) + +其他字段不变。 + +### `/mp/product/detail` 不受影响 + +仍返完整 subtitle / introduction / crowdBenefit。 + +--- + +## 🚨 前端 + +- compare 页面渲染时忽略 subtitle / introduction / crowdBenefit (反正都是 null) +- detail 页面继续读这 3 字段, 不变 + +--- + +## 单测 + +`MpProductServiceTest` **76** 全绿, 含新加 `compareByLine_stripsCompareIrrelevantFields` 回归断言三字段都为 null。 + +--- + +## 联系人 + +后端: wx (呼籁旅行) +前端: mmg + +如有疑问可在 [#2943](https://git.1814.love:8443/wx/HL/issues/2943) 评论区留言。