# 小程序评价 - `/mp/review/target` 接口 targetId 改为可选（支持"全部评论"页）

- **日期**: 2026-04-20
- **PR**: [#951](https://git.1814.love:8443/wx/HL/pulls/951) (Closes #949)
- **类型**: FEATURE（兼容性增强，请求参数收紧 → 放宽）
- **服务**: hl-user-service（review 模块合并在 user 库 / user 服务，路由 `/mp/review/**`）
- **前端是否需要改动**: **无强制改动**（旧调用方式继续可用），新页面"全部评论"可直接复用本接口

---

## 一、背景

小程序新增"全部评论"列表页：在某个 `targetType`（产品 / 攻略 / 酒店 / 景点 / ...）下展示**全部已通过审核的评价**，不限定具体的目标对象 ID。

原接口 `GET /mp/review/target` 此前要求 `targetId` 必填，前端无法用同一个接口实现"全部评论"页，只能等后端再开一个新接口或自己拼。

本次后端把 `targetId` 由必填改为可选，**同接口同时支持两种语义**，前端不用再要新接口。

---

## 二、变更接口

| # | 方法 | 路径 | 变更类型 |
|---|------|------|---------|
| 1 | GET | `/mp/review/target` | 请求参数 `targetId` 由必填改为可选；响应结构不变 |

---

## 三、请求参数变化

| 参数 | 类型 | 之前 | 现在 | 说明 |
|------|------|------|------|------|
| `targetType` | String | **必填** | **必填**（不变） | 评价目标类型，字典 `review_target_type`（如 `PRODUCT` / `STRATEGY` / `HOTEL` / `SCENIC` / ...） |
| `targetId` | Long | **必填** | **可选** | 不传 → 返回该 targetType 下所有已通过评价；传 → 按该 ID 过滤（行为不变） |
| `pageNo` / `pageSize` | Integer | 可选（分页默认值） | 同左 | 不变 |

---

## 四、行为对照

| 调用方式 | 返回内容 |
|---|---|
| `GET /mp/review/target?targetType=PRODUCT&targetId=1001` | 商品 1001 的全部已通过评价（**行为完全不变**） |
| `GET /mp/review/target?targetType=PRODUCT` | 全部商品的所有已通过评价（**新支持**） |
| `GET /mp/review/target`（缺 targetType） | 仍返回参数校验错误（targetType 仍必填） |

---

## 五、响应结构（不变）

`PageResult<MpReviewVO>`，字段沿用既有结构：

```json
{
  "code": 200,
  "data": {
    "total": 128,
    "records": [
      {
        "id": 9001,
        "targetType": "PRODUCT",
        "targetId": 1001,
        "userId": 88,
        "nickname": "***",
        "avatar": "https://...",
        "rating": 5,
        "content": "服务很好，下次还来",
        "images": ["https://..."],
        "createTime": "2026-04-15 12:30:00"
      }
    ]
  }
}
```

> 字段名、类型、嵌套结构与原接口**完全一致**，前端拿到的 records 结构无变化。

---

## 六、前端使用建议

### 场景 A：商品/攻略详情页"评价"模块（旧场景）

**不用改**。原本就传 `targetType + targetId`，行为不变。

### 场景 B：小程序"全部评论"列表页（新场景）

```js
// 全部商品评论（按 targetType 过滤）
const res = await request({
  url: '/mp/review/target',
  method: 'GET',
  data: {
    targetType: 'PRODUCT',   // 必填
    pageNo: 1,
    pageSize: 20
    // targetId 不传
  }
});
// res.data.records 即整个 targetType 下的所有评价
```

### ⚠️ 注意

- `targetType` 仍**必填**，不传会被参数校验拦截
- 不传 `targetId` 时，返回的 records 里每条都有自己的 `targetId`，前端可据此跳到对应详情
- 评价审核状态过滤（已通过 / 待审核）逻辑后端处理，前端无感

---

## 七、不兼容变更

**无**。仅放宽请求参数约束（必填 → 可选），既有调用方式继续按原语义工作。

---

## 八、回归验证

测试环境部署完成后，用 mp token 调用：

```bash
# 1. 旧用法（带 targetId）行为不变
curl "https://api.test.1814.love/mp/review/target?targetType=PRODUCT&targetId=1001&pageNo=1&pageSize=10" \
  -H "Authorization: Bearer {mp-token}" \
  | jq '.data | {total, sample: .records[0]}'

# 2. 新用法（不带 targetId，全部评论）
curl "https://api.test.1814.love/mp/review/target?targetType=PRODUCT&pageNo=1&pageSize=10" \
  -H "Authorization: Bearer {mp-token}" \
  | jq '.data | {total, sample: .records[0]}'

# 3. 缺 targetType（应报参数错误）
curl "https://api.test.1814.love/mp/review/target?pageNo=1&pageSize=10" \
  -H "Authorization: Bearer {mp-token}"
```

**预期**：场景 1 与改动前一致；场景 2 返回的 total 应 ≥ 场景 1；场景 3 返回参数校验错误。
