frontend-notice(v3): 评价举报队列接入说明改写为前端友好版

mmg 反馈前两版看不懂, 改写为:
- 一句话需求 + ASCII 改之前/之后图
- 完整 axios 调用 + 真实测试服 response JSON 示例
- 操作按钮按 reportStatus 状态机表格
- 字典翻译用法 + 实测返回
- 验收清单 8 条具体到点
- 联调环境 + 测试数据 reviewId 直接给

后端 0 改动, 所有接口测试服已就绪, mmg 可直接联调。
这个提交包含在:
API Changelog Bot 2026-05-06 21:26:22 +08:00
父节点 7e4b6365f1
当前提交 f4682fc56e

查看文件

@ -1,182 +1,266 @@
# frontend-notice: 「人工复审队列」页面加 REVIEW tab(融合评价举报,后端 #1668 已就绪) # 前端任务: 「人工复审队列」加一个 tab 显示评价举报
**通知对象**: @mmg @mmg —— **此文件已是第 3 版,前两版你说看不懂,这版尽量直白。如果还有不清楚的当面问我。**
**关联**: [PR #1668](https://git.1814.love:8443/wx/HL/pulls/1668)(后端 admin API 5 个)+ [PR #1755](https://git.1814.love:8443/wx/HL/pulls/1755)mp BFF 透传)
**问题反馈**: 2026-05-06 用户反馈"客户举报成功了应该在人工复审队列中展示 现在没有"
**方向修正**: ⚠️ 此前同名 changelog 让你"新建独立页面"——方向改为 **在现有「人工复审队列」页面加 tab 融合**,以下为最终方案
--- ---
## 用户产品语义 ## 一句话需求
进「内容安全 → 人工复审队列」页面就能看到所有需要复审的内容(包括出行人姓名、用户昵称、头像,**也包括评价举报**)+ 在同页面就能处理(隐藏/恢复/忽略/警告)。**不要新建独立的"评价举报处理"二级菜单**。 「内容安全 → 人工复审队列」页面 **顶部加一个 tab 叫"评价举报"**,切到这个 tab 就调下面的接口,数据展示出来,操作按钮按下面的规则做。**只改这一个页面,不要新建别的菜单。**
--- ---
## 现状 ## 改之前 vs 改之后
后台「内容安全 → 人工复审队列」页面当前调: ### 现在的页面
``` ```
GET /admin/wx-security/manual-review/page?bizType=TRAVELER ┌─────────────────────────────────────────┐
│ 人工复审队列 │
├─────────────────────────────────────────┤
│ [出行人姓名] [用户昵称] [用户头像] │ ← 这是当前已有的 tab
├─────────────────────────────────────────┤
│ 列表(调 /admin/wx-security/manual-review/page) │
└─────────────────────────────────────────┘
``` ```
支持 `bizType` ∈ {TRAVELER, USER_NICKNAME, USER_AVATAR},三类的查询/通过/驳回操作 5 个 API。 ### 改成这样
举报评价数据走另一套独立 admin API(已就绪,后端 0 改动):
``` ```
GET /admin/review/report-queue/page?reportStatus=PENDING_REVIEW ┌─────────────────────────────────────────────────────┐
POST /admin/review/report-queue/{reviewId}/hide Body: { "reason": "..." } │ 人工复审队列 │
POST /admin/review/report-queue/{reviewId}/restore ├─────────────────────────────────────────────────────┤
POST /admin/review/report-queue/report/{reportId}/ignore │ [出行人姓名] [用户昵称] [用户头像] [评价举报] │ ← 加一个新 tab
POST /admin/review/report-queue/{reviewId}/warn-author ├─────────────────────────────────────────────────────┤
│ 切到"评价举报"时: │
│ - 列表调 /admin/review/report-queue/page │
│ - 操作按钮换成 隐藏/恢复/警告作者/忽略 │
└─────────────────────────────────────────────────────┘
``` ```
前 3 个 tab 你不用动,保持现状。**只加第 4 个 tab + 这个 tab 内的逻辑。**
--- ---
## 前端融合方案 ## tab=评价举报 时调什么接口
### 1. 页面顶部 tab 加第 4 项 "评价举报" ### 1. 加载列表
``` ```js
[出行人姓名] [用户昵称] [用户头像] [评价举报] ← 新增 // GET 请求
axios.get('/admin/review/report-queue/page', {
params: {
page: 1,
pageSize: 20,
reportStatus: 'PENDING_REVIEW', // 可选: NORMAL / PENDING_REVIEW / HIDDEN
keyword: '违规' // 可选: 模糊搜评价内容/作者昵称
}
})
``` ```
对应 `bizType` 内部值: `TRAVELER` / `USER_NICKNAME` / `USER_AVATAR` / `REVIEW` ### 响应实例(测试服真数据,reviewId=2048001001001001005 的那条举报)
### 2. tab 路由不同 API ```json
{
"code": 200,
"data": {
"total": 1,
"page": 1,
"pageSize": 20,
"records": [
{
"reviewId": "2048001001001001005",
"content": "出发前一直犹豫值不值得去……(评价正文)",
"userNickname": "罗子昂",
"userId": "2031234567890123456",
"productName": "粉色风蚀古堡",
"status": "PUBLISHED",
"reportCount": 1,
"reportStatus": "NORMAL",
"hiddenAt": null,
"hiddenReason": null,
"createTime": "2026-05-05 14:23:11",
"reasonStats": { "SPAM": 1 },
"reporters": [
{
"reportId": "2052007674911895553",
"reporterUserId": "2031999888777666555",
"reporterOpenid": "oK****n8",
"reason": "SPAM",
"detail": null,
"status": "PENDING",
"createTime": "2026-05-06 20:25:42"
}
]
}
]
}
}
```
| 当前 tab | 列表接口 | 操作接口 | **注意**: `reviewId / userId / reporterUserId / reportId` 都是字符串(后端用 `@JsonFormat(shape=STRING)` 防 JS 精度丢失),前端**不要 parseInt**,直接当字符串用。
---
### 2. 表格列建议
| 列 | 字段 | 说明 |
|---|---|---| |---|---|---|
| TRAVELER / USER_NICKNAME / USER_AVATAR | `GET /admin/wx-security/manual-review/page?bizType=X` | `/admin/wx-security/manual-review/{approve,reject}` | | 评价内容 | `content` | 超过 100 字省略号,鼠标 hover tooltip 看全文 |
| **REVIEW** | `GET /admin/review/report-queue/page` | `/admin/review/report-queue/{reviewId}/{hide,restore,warn-author}` + `report/{reportId}/ignore` | | 作者 | `userNickname` | 链接到用户详情(可选,用 `userId`) |
| 关联产品 | `productName` | |
| 举报数 | `reportCount` | |
| 状态 | `reportStatus` | 见下面"状态枚举"中文化 |
| 举报原因 | `reasonStats` | 例如 `广告 ×3 / 辱骂 ×1`,通过字典翻译(下面有字典接口) |
| 隐藏时间 | `hiddenAt` | `reportStatus=HIDDEN` 时显示,否则空 |
| 创建时间 | `createTime` | 评价发布时间 |
| 操作 | (按钮) | 见下面"操作按钮规则" |
### 3. tab=REVIEW 时表格列建议 ### 状态枚举(中文化)
| 列名 | 数据来源 | | `reportStatus` | 中文 | tag 颜色建议 |
|---|---|---|
| `NORMAL` | 正常 | 灰 / 默认 |
| `PENDING_REVIEW` | 待复审 | 橙 / warning |
| `HIDDEN` | 已隐藏 | 红 / error |
---
### 3. 操作按钮规则
按当前行的 `reportStatus` 显示不同按钮:
| `reportStatus` | 显示这些按钮 |
|---|---| |---|---|
| 评价内容 | `content` (≤ 100 字省略号) | | `NORMAL` | `警告作者` `忽略全部` |
| 作者 | `userNickname`(超链接到用户详情用 `userId`) | | `PENDING_REVIEW` | **`隐藏评价`(主)** `警告作者` `忽略全部` |
| 关联产品 | `productName` | | `HIDDEN` | **`恢复评价`(主)** |
| 举报数 | `reportCount` |
| 状态 | `reportStatus` 中文化(详见下文枚举) |
| 举报原因统计 | `reasonStats`(图表或 `广告 3 / 辱骂 1` 格式),通过字典 `review_report_reason` 翻译 label |
| 隐藏时间 | `hiddenAt`(状态 = HIDDEN 时显示) |
| 创建时间 | `createTime` |
| 操作 | 按 `reportStatus` 显示不同按钮(下文) |
### 4. 操作按钮(按 reportStatus 路由) #### 各按钮对应的接口
| reportStatus | 中文 | 显示按钮 | ```js
|---|---|---| // 隐藏评价(弹个框让 admin 输 reason)
| `NORMAL` | 正常 | 警告作者 / 忽略全部 | axios.post(`/admin/review/report-queue/${reviewId}/hide`, {
| `PENDING_REVIEW` | 待复审 | **隐藏评价**(主按钮) / 警告作者 / 忽略全部 | reason: '违规广告' // 必填
| `HIDDEN` | 已隐藏 | **恢复评价**(主按钮) | })
> "忽略全部" = 列表内每条 `reporters[]` 行循环调 `POST /admin/review/report-queue/report/{reportId}/ignore`(批处理) // 恢复评价
> "警告作者" → `POST /admin/review/report-queue/{reviewId}/warn-author`(后端目前仅落日志,站内信 SDK 待集成) axios.post(`/admin/review/report-queue/${reviewId}/restore`)
> "隐藏评价" 弹框输入 reason → `POST /admin/review/report-queue/{reviewId}/hide` body `{reason}`
> "恢复评价" → `POST /admin/review/report-queue/{reviewId}/restore`
### 5. 展开行(可选,推荐) // 警告作者(后端目前只落日志,以后接站内信 SDK 再说)
axios.post(`/admin/review/report-queue/${reviewId}/warn-author`)
点击行展开看 `reporters[]` 数组,每个举报人: // 忽略全部 = 把当前行 reporters[] 数组里每个 reportId 循环调忽略接口
for (const r of row.reporters) {
await axios.post(`/admin/review/report-queue/report/${r.reportId}/ignore`)
}
```
| openid(脱敏) | 举报原因 | 详细说明 | 状态 | 时间 | 操作 | **所有操作完成后请刷新当前页列表。**
---
### 4. 展开行(可选,推荐有)
点击行旁边的展开图标,展开看 `reporters[]` 数组,每个举报人一行:
| 举报人 | 原因 | 详情 | 状态 | 时间 | 操作 |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| `oABCD***1234` | 垃圾营销 | (用户填的 detail) | PENDING / IGNORED / HANDLED | createTime | [忽略本条] | | `oK****n8` (脱敏 openid) | 垃圾营销 | (用户填的 detail,可能为空) | `PENDING` | createTime | `[忽略本条]` |
`[忽略本条]``POST /admin/review/report-queue/report/{reportId}/ignore` **`[忽略本条]` 按钮**:
```js
axios.post(`/admin/review/report-queue/report/${reportId}/ignore`)
```
--- ---
## 接口字段清单 ### 5. 字典翻译(reasonStats 里的 key)
### `AdminReportQueueItemRespVO`(列表项) `reasonStats` 的 key 是英文 value(SPAM/INSULT/...),展示时翻译成中文 label:
| 字段 | 类型 | 说明 | ```js
|---|---|---| // 拿字典(整个项目通用接口)
| `reviewId` | Long (`@JsonFormat(shape=STRING)`) | 评价 ID | axios.get('/admin/system/dict-data/by-type', {
| `content` | String | 评价正文 | params: { dictType: 'review_report_reason' }
| `userNickname` | String | 评价作者昵称 | })
| `userId` | Long (JsonString) | 评价作者 ID |
| `productName` | String | 关联产品名 |
| `status` | String | 评价本身状态(PUBLISHED 等) |
| `reportCount` | Integer | 总举报数 |
| `reportStatus` | String | NORMAL / PENDING_REVIEW / HIDDEN |
| `hiddenAt` | LocalDateTime | 隐藏时间(null 表示未隐藏) |
| `hiddenReason` | String | 隐藏原因(管理员填的) |
| `createTime` | LocalDateTime | 评价创建时间 |
| `reasonStats` | `Map<String, Integer>` | 各 reason 统计 e.g. `{"SPAM":3,"OFFENSIVE":1}` |
| `reporters` | List | 举报人列表(展开行用) |
`reporters[]` 每项: `reportId / reporterUserId(JsonString) / reporterOpenid(已脱敏) / reason / detail / status / createTime`
### `AdminReportQueuePageReqVO`(查询参数)
```
GET /admin/review/report-queue/page
?page=1
&pageSize=20
&reportStatus=PENDING_REVIEW (可选,前端 tab 内嵌"全部/待复审/已隐藏"子筛选时用)
&keyword=违规广告 (可选,模糊匹配评价内容/作者昵称)
``` ```
### 字典 `review_report_reason` 返回(测试服实测):
通过 `GET /admin/system/dict-data/by-type?dictType=review_report_reason` 拿: ```json
{
"code": 200,
"data": [
{ "value": "SPAM", "label": "广告/营销" },
{ "value": "INSULT", "label": "辱骂/人身攻击" },
{ "value": "PORN", "label": "色情低俗" },
{ "value": "POLITICS", "label": "政治敏感" },
{ "value": "FAKE", "label": "虚假评价" },
{ "value": "LEAK_PRIVACY", "label": "泄露他人隐私" },
{ "value": "OTHER", "label": "其他" }
]
}
```
| value | label | **不要硬编码这个映射**, 进页面拉一次缓存到 vuex/pinia 就行。
---
### 6. 顶部筛选区(REVIEW tab 时)
页面顶部本来已经有"建议 / 场景 / 关键词"3 个筛选,在 REVIEW tab 时:
| 原字段 | 改成 |
|---|---| |---|---|
| `SPAM` | 垃圾营销 | | 建议(下拉) | 改成 `reportStatus` 下拉(全部 / 正常 / 待复审 / 已隐藏),传 `reportStatus` 参数 |
| `OFFENSIVE` | 不友善/辱骂(注: 后端实际入库 dictValue 也可能是 `INSULT` 见 V20260507_001) | | 场景 | **隐藏**(评价举报无 scene) |
| `FRAUD` | 欺诈/虚假 | | 关键词 | 保留,传 `keyword` 参数 |
| `OTHER` | 其他 |
> 字典值后端权威以 `/admin/system/dict-data/by-type` 实际返回为准,前端动态渲染不要硬编码。 切回前 3 个 tab 时恢复原本的筛选字段
--- ---
## 列表排序与分页 ## 你不用关心的(后端已搞定的)
后端默认按 `reportCount DESC, create_time DESC` 排序,**前端不要重排**。 - 用户从小程序点举报(`POST /mp/review/report`)的数据已经 100% 落到后端 DB,实测 `reviewId=2048001001001001005` 已经有 1 条 SPAM 举报数据,你接入后能直接看到。
分页用标准 `page` / `pageSize`,响应 `total` / `records` - 网关路由、JWT 认证、字典初始化 全部就绪。
- 排序后端默认 `reportCount DESC, createTime DESC`,**前端不要 sort 重排**。
- `reporterOpenid` 已经脱敏(`oK****n8`),**不要再 mask**。
--- ---
## 兼容现有 3 类的 UX ## 验收清单(全部对了再说做完)
切换 tab 时: - [ ] 「人工复审队列」页面顶部 4 个 tab,默认还是"出行人姓名"
- TRAVELER/USER_NICKNAME/USER_AVATAR tab 保留现有页面的 列定义/操作按钮(approve/reject)不动。 - [ ] 点"评价举报" tab,列表能加载,看到 reviewId=2048001001001001005 的那条数据
- REVIEW tab 是**完全独立的列定义 + 操作按钮**(因为 VO 字段完全不同)。 - [ ] 评价内容/作者昵称/产品名 都是中文显示
- 顶部筛选区 "建议 / 场景 / 关键词" 在 REVIEW tab 时: - [ ] `reasonStats` 的 SPAM 翻译成 "广告/营销"
- "建议" 字段 → 复用为 `reportStatus` (NORMAL / PENDING_REVIEW / HIDDEN) - [ ] 状态显示"正常"(因为目前 reportCount=1 < 3)
- "场景" 字段 → REVIEW tab 隐藏(评价举报无 scene 概念) - [ ] 操作按钮显示"警告作者 / 忽略全部"(NORMAL 状态)
- "关键词" 字段 → 走后端 `keyword` 参数,模糊评价内容/作者昵称 - [ ] 展开行能看到 1 条 reporters,openid `oK****n8`,原因 "广告/营销"
- [ ] 切回"出行人姓名" tab,原列表还能正常加载(没把老 tab 弄坏)
--- ---
## 验收 ## 工作量
1. 「内容安全 → 人工复审队列」页面顶部 tab 出现 "评价举报",权限正常 预计 1.5 天。
2. 默认 tab 仍为出行人姓名(不影响现有用户习惯)
3. 切到 "评价举报" tab,列表能加载,字段渲染对齐上方表格 后端 0 改动。**所有接口都已部署到测试服,你可以直接联调。**
4. `reasonStats` 字典 label 翻译正确,中文显示 "广告/营销" "辱骂/人身攻击" 等
5. 操作按钮按 `reportStatus` 显示且功能调通,操作完列表自动刷新
6. 展开行显示 `reporters[]`,每条"忽略本条"按钮调 `report/{reportId}/ignore`
7. 测试服 `reviewId=2048001001001001005` 已有 1 条 SPAM 举报数据,接入后能直接看到
--- ---
## 不在本次范围 ## 联调环境
- 站内信通知作者(`warnAuthor` 后端 TODO) - 测试服域名: `https://api.test.1814.love:9443`
- 后端跨服务统一聚合(Phase 2D-B,已评估优先级低) - admin 账号: `admin / Admin@123456`
- 评价举报独立的二级菜单(已废弃,统一走 tab) - 已有测试数据: `reviewId=2048001001001001005`(粉色风蚀古堡产品的一条评价,被 SPAM 举报 1 次)
- 想再造数据: 用小程序登录,POST `/mp/review/report` body `{"reviewId":"...","reason":"INSULT"}`
--- ---
## 工作量预估 ## 历史 changelog 注意
前端 ~1.5 天(VO 适配 + tab 切换 + 操作按钮按状态路由 + 展开行 + 字典翻译)。**后端 0 改动** 之前的 2 版让你"新建独立菜单 评价举报处理",**那个方向作废**。最终方向就是这版: **现有页面加 tab**