# 小程序产品详情 - 住宿信息补酒店封面图 / 类型 / 城市

- **日期**: 2026-04-19
- **Commit**: [2625331f](https://git.1814.love:8443/wx/HL/commit/2625331f) (分支 `fix/0419-business-hotfixes`，批量 PR 待合)
- **类型**: FIX
- **状态**: 已 commit + push，待批量 PR 合入 dev + 部署测试环境
- **服务**: hl-product-service-v2（端口 8083，通过网关暴露 `/mp/product/**`）

---

## 一、为什么加这三个字段

原型（`小蒙马.pen` → `S6-住宿安排`）的每晚住宿卡片结构：

```
┌────────┬──────────────────────┐
│        │ 第N晚·城市名（小标签） │
│ 封面图 │ 酒店名称（粗体）      │
│ 100x100│ 一句话描述            │
└────────┴──────────────────────┘
```

此前后端返回的 `HotelInfo` **只有 hotelName + 档位字段**，前端拿不到封面图 URL、酒店类型（五星/四星等）、酒店所在城市，小程序端显示为空白图位。

根因：`MpProductDetailAssembler.fetchResourceDetails(...)` 原先只收集 `nodeType=HOTEL` 节点的 hotelId 去批量查 `ResourceDetailDTO`，而 `product_day_hotel` 表里的 hotelId **从未进入批量查参数**，所以 HotelInfo 始终拿不到酒店详情。

---

## 二、变更接口清单

| # | 方法 | 路径 | 影响 |
|---|------|------|------|
| 1 | GET | `/mp/product/{id}` | 返回的 `itinerary[].hotels[]` 每项新增 `coverUrl` / `hotelType` / `city` 三字段 |

**其他接口**（如 `/mp/product/{id}/day/{dayNumber}/hotels` 住宿详情弹窗）已有 `coverUrl`，**不变**。

---

## 三、字段定义

### `MpProductDetailRespVO.HotelInfo` 新增字段

| 字段 | 类型 | 说明 | 示例 | 可能为 null |
|---|---|---|---|---|
| `coverUrl` | String | 酒店封面图 URL（来自资源服务的酒店 cover） | `"https://oss.1814.love/hotel/9001/cover.jpg"` | ✅ 当酒店资源未配封面或 Feign 异常时为 null |
| `hotelType` | String | 酒店类型（字典 `hotel_type`：`FIVE_STAR` / `FOUR_STAR` / `BOUTIQUE` / `HOMESTAY` / ...） | `"FIVE_STAR"` | ✅ 可能为 null |
| `city` | String | 酒店所在城市 | `"海拉尔"` | ✅ 可能为 null |

### 既有字段（不变）

`hotelName` / `tierSeq` / `tierName` / `roomTypeId` / `roomTypeName` / `roomCount`。

此外，本次修复顺带加强了 `hotelName` 的补齐语义：当资源服务返回了 `name`，**会覆盖 DB 快照字段**（更新鲜），快照只作为 Feign 降级时的兜底。

---

## 四、响应示例对比

### 前

```json
{
  "code": 200,
  "data": {
    "productId": 1001,
    "name": "小蒙马·呼伦贝尔5日",
    "itinerary": [{
      "dayNumber": 1,
      "hotels": [{
        "hotelName": "海拉尔精选酒店",
        "tierSeq": 1,
        "tierName": "标准",
        "roomTypeName": "大床房",
        "roomCount": 1
      }]
    }]
  }
}
```

### 后

```json
{
  "code": 200,
  "data": {
    "productId": 1001,
    "name": "小蒙马·呼伦贝尔5日",
    "itinerary": [{
      "dayNumber": 1,
      "hotels": [{
        "hotelName": "海拉尔精选酒店",
        "tierSeq": 1,
        "tierName": "标准",
        "roomTypeName": "大床房",
        "roomCount": 1,
        "coverUrl": "https://oss.1814.love/hotel/9001/cover.jpg",
        "hotelType": "FIVE_STAR",
        "city": "海拉尔"
      }]
    }]
  }
}
```

---

## 五、前端渲染建议

原型 `S6-住宿安排` 每日住宿卡片：

```html
<div class="hotel-card" v-for="hotel in day.hotels">
  <!-- 左侧 100x100 封面图 -->
  <image
    class="hotel-cover"
    :src="hotel.coverUrl || DEFAULT_HOTEL_COVER"
    mode="aspectFill" />

  <!-- 右侧文字信息 -->
  <view class="hotel-info">
    <text class="hotel-city">第{{ day.dayNumber }}晚·{{ hotel.city }}</text>
    <text class="hotel-name">{{ hotel.hotelName }}</text>
    <!-- 房型/档位信息 -->
    <text class="hotel-room">{{ hotel.tierName }} · {{ hotel.roomTypeName }}</text>
  </view>
</div>
```

### ⚠️ 容错要求

- `coverUrl` / `hotelType` / `city` **三个字段均可能为 null**（Feign 失败 / 酒店资源未配封面 / 老数据没录入城市），前端务必做 null/空字符串判断：
  - `coverUrl` 为空时显示默认占位图（避免整个卡片布局错乱）
  - `city` 为空时可以只显示"第N晚"
  - `hotelType` 用于可选的类型标签展示，为空时整个标签隐藏即可

---

## 六、不兼容变更

**无**。纯新增字段，前端不读不受影响；既有字段（`hotelName` / 档位 / 房型）全部保留，语义不变。

---

## 七、运维注意事项（⚠️ 发布后必做）

本接口有 **5 分钟 Redis 缓存**（key 前缀 `mp:product:detail:`）。测试 / 生产部署 `hl-product-service-v2` 后**必须清一次缓存**，否则 C 端最长 5 分钟内仍返回旧缓存（无封面图）：

```bash
# 测试环境（192.168.100.236 或 1.182.108.58）
redis-cli -h <redis_host> -p 6379 --scan --pattern 'mp:product:detail:*' | xargs -r redis-cli -h <redis_host> -p 6379 del

# 或者粗暴重启服务强制清本地进程缓存（Redis 不会自动失效）
```

---

## 八、回归验证

测试环境部署完成后：

```bash
# 选一个小蒙马 GROUP 产品的 productId
curl "https://api.test.1814.love/mp/product/{productId}" \
  -H "Authorization: Bearer {mp-token}" \
  | jq '.data.itinerary[0].hotels[0] | {hotelName, coverUrl, hotelType, city}'
```

**预期**：三个新字段都在响应 JSON 中出现；若酒店资源已配封面，`coverUrl` 为完整 http(s) URL；`city` / `hotelType` 应与后台管理端该酒店的配置一致。

如果 `coverUrl` 仍为 null：
1. 先确认 Redis 缓存已清（见第七节）
2. 再排查酒店资源是否在资源服务后台配了封面图（管理端 → 酒店管理 → 编辑 → 封面图）
