hl-api-changelog/changelogs/2026-04/2026-04-24_mp_favorite-footprint-fields.md

119 行
5.7 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# mp 收藏 / 浏览足迹接口: 字段全回填 + 新增产品类型字段 + 修复浏览记录保存后查不出
> **服务**: hl-mp-service (端口 8085)
> **PR**: #1338
> **Issue**: 无关联工单Gitea API 异常未能建单,已对齐口头确认)
> **日期**: 2026-04-24
> **影响范围**: 小程序 收藏列表 + 浏览足迹列表 + 浏览记录保存接口
---
## ⚠️ 关键变化
1. **前一版 `/mp/user/favorite` 和 `/mp/user/footprint` 的 `records` 中 `targetType=PRODUCT`(或 `resourceType=PRODUCT`)的行,`name`/`coverUrl`/`tags` 全是 `null`**。本版修复后全部正确回填。
2. `FavoriteItemVO``FootprintItemVO` **新增** `productType` + `productTypeLabel` 两字段(仅 PRODUCT 类型有值,其它类型 `null`)。前端若展示收藏/足迹卡片带产品类型(核心/小蒙马/私人定制)角标,直接用 `productTypeLabel` 即可,不用自己查字典。
3. 保存足迹接口 `POST /mp/user/footprint` **修复隐性失败**:原实现下,用户"清空浏览记录"后再次访问同一资源时,接口返 200 但 DB 实际未保存记录,导致"保存成功却查不出来"。现在会正确复活已软删的记录。
4. `footprintId` / `favoriteId` 在响应 JSON 中**全栈改为 String 序列化**(此前是 number,19 位雪花 ID 会被 JS 精度截断)。前端若以字符串接收,无感知;若以 number 运算需调整。
---
## 一、背景
`MpProductFeignClient.getProductBrief` Feign 路径历史写错,指向 `/internal/product/batch-brief`(班期摘要接口,返回 `BatchBriefVO` 只有 batchId/batchCode/departureDate 等班期字段),**没有 name/coverUrl/tags**。导致 mp BFF `FavoriteAggregationService` 拿到空 list → 字段全 null。资源类型SCENIC/RESTAURANT/ACTIVITY的三个 brief 端点无此问题。
浏览记录的问题则是 `addFootprint` 服务方法缺软删复活逻辑:清空浏览走 `@TableLogic``deleted_at` 置 NOW,下次访问时 `getOne` 自动加 `WHERE deleted_at IS NULL` 查不到 existing,进入 `save` 分支后撞上 DB 唯一索引(不含 deleted_at冲突被静默吞。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 收藏列表 | GET | `/mp/user/favorite` | 响应字段补全 + 新增 | PRODUCT 行 `name`/`coverUrl`/`tags` 不再为 null;新增 `productType` + `productTypeLabel` |
| 2 | 浏览足迹列表 | GET | `/mp/user/footprint` | 响应字段补全 + 新增 | 同上 |
| 3 | 保存浏览记录 | POST | `/mp/user/footprint` | 行为修复 | 清空浏览后再次访问同资源不再静默失败 |
路径/方法/参数**无变化**。
---
## 三、字段变化详情
### 3.1 `/mp/user/favorite` 响应 `records[*]` 字段
| 字段 | 类型 | 之前 | 之后 | 备注 |
|------|------|------|------|------|
| `favoriteId` | String | String | String | 无变 |
| `targetType` | String | String | String | 无变,枚举 `PRODUCT` / `SCENIC` / `RESTAURANT` / `ACTIVITY` |
| `targetId` | String | String | String | 无变 |
| `name` | String | PRODUCT 时为 `null` | **全类型正确返回** | PRODUCT 取产品名称;其它类型取资源名称 |
| `coverUrl` | String | PRODUCT 时为 `null` | **全类型正确返回** | PRODUCT 取产品封面图;其它类型取资源封面图 |
| `tags` | List\<String\> | PRODUCT 时为 `null` | **全类型正确返回** | 仅存在标签时有值 |
| `productType` | String | **不存在** | **新增** | 仅 PRODUCT 有值,枚举 `CORE` / `GROUP` / `CUSTOM`(字典 `product_type` |
| `productTypeLabel` | String | **不存在** | **新增** | 仅 PRODUCT 有值,中文,如 `核心产品` / `小蒙马` / `私人定制` |
| `createdAt` | String | String | String | 无变 |
### 3.2 `/mp/user/footprint` 响应 `records[*]` 字段
`/mp/user/favorite`,除主键字段叫 `footprintId`、业务类型字段叫 `resourceType`/`resourceId`
### 3.3 `POST /mp/user/footprint` 响应
响应结构未变(仍为 `Result<void>``Result<Footprint>`,视既有契约)。仅内部实现行为修复。
---
## 四、示例
### 4.1 修复前(已观察实际响应)
```json
{
"code": 200,
"data": {
"records": [
{
"favoriteId": "2047265158492815361",
"targetType": "PRODUCT",
"targetId": "2044306857534636034",
"name": null,
"coverUrl": null,
"tags": null,
"createdAt": "2026-04-23 18:43:45"
}
],
"total": 1
}
}
```
### 4.2 修复后
```json
{
"code": 200,
"data": {
"records": [
{
"favoriteId": "2047265158492815361",
"targetType": "PRODUCT",
"targetId": "2044306857534636034",
"name": "冻干粉发短信给",
"coverUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/xxx.jpg",
"tags": ["亲子", "研学", "露营"],
"productType": "GROUP",
"productTypeLabel": "小蒙马",
"createdAt": "2026-04-23 18:43:45"
}
],
"total": 1
}
}
```
---
## 五、前端建议
1. 卡片 UI 如展示产品类型角标(核心/小蒙马/定制),直接用 `productTypeLabel`,不用前端再拉字典翻译。
2. `productType`/`productTypeLabel` 在非 PRODUCT 类型SCENIC/RESTAURANT/ACTIVITY上是 `null`,渲染需判空。
3. 原本用 `name?.trim() || "加载中..."` 等兜底文案的地方可以简化;字段现在一定不为 null除非产品已被删除,此时 brief 返空 → 仍为 null,需保留兜底
4. `productTypeLabel` 来自字典 `product_type`,字典 value→label 映射:`CORE→核心产品``GROUP→小蒙马``CUSTOM→私人定制`。若字典中文名称后续调整,后端会自动跟随,前端无需改动。