docs: 23日累计 3 条 changelog (banner 字段补全 + SMS info hotfix + compare 抹空)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
父节点
fece5d472f
当前提交
b5ceb15d50
@ -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<String>` 字段, 来自 `sys_banner.tags`。
|
||||||
|
|
||||||
|
之前精简掉了 tags 字段, Feign 反序列化下游 `BannerVO.tags` 被 Jackson 自动丢弃。
|
||||||
|
|
||||||
|
### 2. PRODUCT 类型 linkTarget 补 tags + seasons fallback
|
||||||
|
|
||||||
|
- `InternalProductSimpleVO` (product-v2 + user-service 镜像) 加 `tags: List<String>` 字段
|
||||||
|
- `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<br>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) 评论区留言。
|
||||||
@ -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
|
||||||
@ -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) 评论区留言。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户