2026-04-21 主题卡片跳转改路径直跳 + PRODUCT 存 productId+productType (#1085)
这个提交包含在:
父节点
f791edb0ff
当前提交
f18e187aa5
@ -0,0 +1,146 @@
|
|||||||
|
# 主题卡片跳转类型改路径直跳 + PRODUCT 存 productId+productType
|
||||||
|
|
||||||
|
**日期**:2026-04-21
|
||||||
|
**PR**:#1085
|
||||||
|
**合并到 dev commit**:55d601d7
|
||||||
|
**后端服务**:hl-user-service(需重启)
|
||||||
|
**影响**:
|
||||||
|
- 管理端「首页配置 → 品牌故事 → 主题卡片」跳转类型下拉 + 指定产品选择
|
||||||
|
- 小程序「首页第二屏」点击主题卡片跳转逻辑
|
||||||
|
|
||||||
|
> 本次是对 #1081(2026-04-21 早上)的迭代。#1081 建立了跳转类型字段,本次调整字典 value 语义 + PRODUCT 的 payload 存储方式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 为什么改
|
||||||
|
|
||||||
|
#1081 之前把 4 种跳转类型的 `dict_value` 定为枚举 code(`SEASON` / `CORE_LIST` / `MENGMA` / `PRODUCT`),前端还要维护一张 code → 路径的 switch 表。改完后三种列表类直接把路径放到 `dict_value`,前端拿到直接跳,不用 switch。只有「指定产品」保留枚举 `PRODUCT`,因为路径需要运行时拼产品 id/类型。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 字典改动(已同步本地 + 测试服 192.168.100.236)
|
||||||
|
|
||||||
|
**dict_type**:`theme_card_link_type`(不变,dict_type_id=8090,category=BUSINESS)
|
||||||
|
|
||||||
|
**dict_data**:
|
||||||
|
|
||||||
|
| dict_value(变化) | dict_label | sort_order |
|
||||||
|
|-------------------|-----------|-----------|
|
||||||
|
| `packages/product/season/list/list` | 季节之旅 | 10 |
|
||||||
|
| `packages/product/core/list/list` | 核心产品 | 20 |
|
||||||
|
| `packages/product/mengma/list/list` | 亲子游学 | 30 |
|
||||||
|
| `PRODUCT`(保留枚举) | 指定产品 | 40 |
|
||||||
|
|
||||||
|
**迁移**:本地 + 测试服 DB 已通过 UPDATE 同步;`sys_topic.link_type` 从 VARCHAR(20) 扩到 VARCHAR(200)(因路径超长)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 接口签名变化
|
||||||
|
|
||||||
|
### 1. `TopicRequest`(POST /admin/topic/create、PUT /admin/topic/update 入参)
|
||||||
|
|
||||||
|
**新增两个字段**:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 是否必填 | 说明 |
|
||||||
|
|------|------|---------|------|
|
||||||
|
| `productId` | `Long` | **PRODUCT 类型必填** | 指定的产品 ID |
|
||||||
|
| `productType` | `String` | **PRODUCT 类型必填** | 产品类型(字典 `product_type`:CORE / GROUP / CUSTOM) |
|
||||||
|
|
||||||
|
**`linkType` / `linkTarget` 语义调整**:
|
||||||
|
|
||||||
|
| linkType 值 | linkTarget 怎么传 | productId / productType 怎么传 |
|
||||||
|
|-------------|------------------|---------------------------------|
|
||||||
|
| `packages/product/season/list/list` | 不传(后端会忽略) | 不传 |
|
||||||
|
| `packages/product/core/list/list` | 不传 | 不传 |
|
||||||
|
| `packages/product/mengma/list/list` | 不传 | 不传 |
|
||||||
|
| `PRODUCT` | **不要传**(后端会忽略并合成 JSON) | **必填** productId + productType |
|
||||||
|
|
||||||
|
> ⚠️ 校验:`linkType=PRODUCT` 时 productId 或 productType 缺失,后端直接抛 `{"code":400,"message":"指定产品跳转必须填写产品ID和产品类型"}`。
|
||||||
|
|
||||||
|
**请求示例(路径直跳)**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "季节之旅",
|
||||||
|
"coverUrl": "https://oss.../cover.png",
|
||||||
|
"linkType": "packages/product/season/list/list"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求示例(指定产品)**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "亲子系列",
|
||||||
|
"coverUrl": "https://oss.../cover.png",
|
||||||
|
"linkType": "PRODUCT",
|
||||||
|
"productId": 2045345825172639746,
|
||||||
|
"productType": "CORE"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `TopicVO`(GET /admin/topic/{id} 或列表返回)
|
||||||
|
|
||||||
|
**新增两个回显字段**:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 何时有值 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `productId` | `Long` | 仅 `linkType=PRODUCT` 时非空 |
|
||||||
|
| `productType` | `String` | 仅 `linkType=PRODUCT` 时非空 |
|
||||||
|
|
||||||
|
`linkTarget` 字段行为:
|
||||||
|
- `linkType=PRODUCT`:仍然是 JSON 字符串(`{"productId":xxx,"productType":"CORE"}`),**前端直接读新的 productId/productType 字段即可**,不用再解析 linkTarget
|
||||||
|
- 其它 linkType:目前后端写入时为空字符串,前端忽略即可
|
||||||
|
|
||||||
|
**响应示例(指定产品)**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "2046xxx",
|
||||||
|
"title": "亲子系列",
|
||||||
|
"linkType": "PRODUCT",
|
||||||
|
"linkTarget": "{\"productId\":2045345825172639746,\"productType\":\"CORE\"}",
|
||||||
|
"productId": 2045345825172639746,
|
||||||
|
"productType": "CORE"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 管理端前端要做的事
|
||||||
|
|
||||||
|
1. **跳转类型下拉**:拉 `/admin/dict/data?dictType=theme_card_link_type`,直接用 `dict_label` 显示 + `dict_value` 作为 linkType
|
||||||
|
2. **选中 PRODUCT 时弹产品选择器**:用户选完产品后拿到 productId + productType,保存时塞进 `TopicRequest.productId` / `productType`(不要再传 linkTarget)
|
||||||
|
3. **选中非 PRODUCT(路径)时**:直接把字典 value 作为 linkType 提交,不用管 linkTarget / productId / productType
|
||||||
|
4. **回显**:编辑页如果 topic.linkType='PRODUCT',用 `topic.productId` + `topic.productType` 反查产品名展示;否则按路径下拉项高亮对应项即可
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 小程序前端要做的事
|
||||||
|
|
||||||
|
点击主题卡片时:
|
||||||
|
- `linkType=PRODUCT` → 按 `productId` + `productType` 拼产品详情路径(具体格式由前端约定)
|
||||||
|
- 其它(路径) → `wx.navigateTo({ url: '/' + linkType })` 直接跳
|
||||||
|
|
||||||
|
老小程序(未感知此次变化):`linkType` 拿到字符串路径也能直接 navigate,只有 PRODUCT 类型会走不到详情(但原本 #1081 就约定 PRODUCT 依赖 target 字段,老端本来就没处理),不属于回归。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 兼容性与老数据
|
||||||
|
|
||||||
|
- 本 PR 合并前数据库里 `sys_topic` 是空表,没有历史数据要迁移
|
||||||
|
- TopicVO.toVO 对 legacy 纯数值字符串 linkTarget(形如 `"12345"`)做了 `log.warn` 容忍,不会 500,productId/productType 返 null;如未来真有脏数据可手工修
|
||||||
|
- 老版本小程序拿到新的路径型 linkType 仍能直接 navigateTo,不会崩
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
- 单测 `TopicServiceTest` **31 个用例全绿**
|
||||||
|
- 新增 6 个:`createTopic_productType_serializesLinkTargetAsJson` / `createTopic_product_missingProductId` / `createTopic_product_missingProductType` / `createTopic_seasonPath_keepsLinkTargetAsIs` / `updateTopic_product_rebuildsLinkTarget` / `toVO_product_withLegacyLinkTarget_tolerated`
|
||||||
|
- 测试服字典已同步(dict_type + 4 条 dict_data + link_type 列宽)
|
||||||
|
- 测试服 hl-user-service 已触发 Deploy Panel 重新部署
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重启
|
||||||
|
|
||||||
|
- **hl-user-service** 必须重启(新代码)
|
||||||
|
- 其它服务无依赖,不用重启
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户