# 主题卡片跳转类型改路径直跳 + 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** 必须重启（新代码）
- 其它服务无依赖，不用重启
