# feat(评价): review_source 改为系统字典 + 产品详情精选评价补 source/sourceLabel

> **类型**: feat (后端 + 前端均需改动)
> **关联 PR/Issue**: PR #2989 (Closes #2988) — 承接 #2978 (source 字段) + #2984 (reviewSummary 链路修)
> **日期**: 2026-05-25
> **影响范围**: admin 端「评价审核」筛选区可改用通用字典接口 + `/mp/product/{id}` 精选评价新增 source 字段
> **接收方**: mmg

---

## 🎯 背景

承接两个已合 PR:
- **#2981 (source 字段)** — 加了 enum,前端要拉「来源」下拉必须硬编码两个选项
- **#2984 (reviewSummary 链路修)** — 把 Map 改成强类型 DTO,但 DTO.Review 没加 source 字段,产品详情精选评价虽然有数据但拿不到来源

本次补两个尾巴。

---

## 📊 改动 1:`review_source` 系统字典

字典 type:`review_source`
字典 data 2 条:

| dict_value | dict_label | sort_order | status |
|------------|-----------|------------|--------|
| MINIPROGRAM | 小程序 | 10 | ACTIVE |
| YOUZAN | 有赞 | 20 | ACTIVE |

### admin 端使用

原本「来源」下拉前端要硬编码:
```js
const options = [{value:'MINIPROGRAM', label:'小程序'}, {value:'YOUZAN', label:'有赞'}]
```

现在可改用通用字典接口拉(与其他下拉一样的处理方式):
```
GET /admin/dict/data/review_source
```

返回:
```json
{
  "code": 200,
  "data": [
    {"dictValue":"MINIPROGRAM", "dictLabel":"小程序", "sortOrder":10, "status":"ACTIVE"},
    {"dictValue":"YOUZAN",      "dictLabel":"有赞",   "sortOrder":20, "status":"ACTIVE"}
  ]
}
```

后端 `ReviewSourceEnum` 保留(代码层兜底),业务写值仍走 enum;字典只给前端下拉用。后续要加新来源(如美团/抖音)需要后端加 enum + 字典 INSERT 双改。

---

## 📊 改动 2:`/mp/product/{id}` reviewSummary.topReview 加 source

之前 (`#2984` 修后) 返:
```json
"reviewSummary": {
  "avgScore": 5.0,
  "totalCount": 159,
  "topReview": {
    "reviewId": "...",
    "nickname": "...",
    "content": "...",
    "score": 5.0,
    "images": [...],
    "likeCount": 12,
    "reviewTime": "..."
  }
}
```

本次后返(多 2 个字段):
```diff
"topReview": {
  ...
+ "source": "MINIPROGRAM",
+ "sourceLabel": "小程序"
}
```

### 前端期望

小程序产品详情页的精选评价区,如果是有赞评价可以加角标(如「来自有赞」)。小程序评价默认就是「小程序」可不显示标识。

---

## ✅ 后端验证

### 本地 (gateway 8080)

```
GET /admin/dict/data/review_source → 2 条字典
GET /mp/product/2045716022098264066(159 条评价) → topReview.source=MINIPROGRAM sourceLabel=小程序
```

### 测试服 (api.test.1814.love:9443)

```
GET /admin/dict/data/review_source → 200, 2 条
  MINIPROGRAM / 小程序
  YOUZAN / 有赞

GET /mp/product/2045716022098264066 → 200
  reviewSummary.avgScore=5.0 totalCount=159
  reviewSummary.topReview.source=MINIPROGRAM sourceLabel=小程序
```

rolling-deploy-all.sh 滚动部署 user-service / order-v2 / product-v2 共 ~3 分钟,Flyway V20260525_001 落库(测试服在本地启动时已落,部署时跳过 history.success=1)。

---

## 🗂️ Schema

```sql
INSERT INTO sys_dict_type (dict_type_id, dict_name, dict_type, category, status, remark) VALUES
  (10110, '评价来源', 'review_source', 'BUSINESS', 'ACTIVE', '与 ReviewSourceEnum 枚举一一对应');

INSERT INTO sys_dict_data (dict_data_id, dict_type, dict_label, dict_value, sort_order, status) VALUES
  (101101, 'review_source', '小程序', 'MINIPROGRAM', 10, 'ACTIVE'),
  (101102, 'review_source', '有赞',   'YOUZAN',      20, 'ACTIVE');
```

Flyway `V20260525_001__add_review_source_dict.sql`(hl-user-service),`INSERT IGNORE` 幂等。

---

## 🔗 关联

- `25_feat_review_source_field.md` (#2981) — 评价表加 source 字段(本字典对应的 enum 出处)
- `25_fix_mp_product_review_summary_contract.md` (#2984) — reviewSummary 链路修(本次补 source 字段挂载点)
- `25_feat_review_revert_status_switch.md` (#2983) — 已通过↔已拒绝互转(独立功能,跟本次无直接关联)
