From f4682fc56e9f29923710e88a365875bcd65db1ff Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 6 May 2026 21:26:22 +0800 Subject: [PATCH] =?UTF-8?q?frontend-notice(v3):=20=E8=AF=84=E4=BB=B7?= =?UTF-8?q?=E4=B8=BE=E6=8A=A5=E9=98=9F=E5=88=97=E6=8E=A5=E5=85=A5=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E6=94=B9=E5=86=99=E4=B8=BA=E5=89=8D=E7=AB=AF=E5=8F=8B?= =?UTF-8?q?=E5=A5=BD=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mmg 反馈前两版看不懂, 改写为: - 一句话需求 + ASCII 改之前/之后图 - 完整 axios 调用 + 真实测试服 response JSON 示例 - 操作按钮按 reportStatus 状态机表格 - 字典翻译用法 + 实测返回 - 验收清单 8 条具体到点 - 联调环境 + 测试数据 reviewId 直接给 后端 0 改动, 所有接口测试服已就绪, mmg 可直接联调。 --- ...tend_notice_review_report_queue_pending.md | 312 +++++++++++------- 1 file changed, 198 insertions(+), 114 deletions(-) diff --git a/changelogs/2026-05/06_frontend_notice_review_report_queue_pending.md b/changelogs/2026-05/06_frontend_notice_review_report_queue_pending.md index a3250e1..4f65813 100644 --- a/changelogs/2026-05/06_frontend_notice_review_report_queue_pending.md +++ b/changelogs/2026-05/06_frontend_notice_review_report_queue_pending.md @@ -1,182 +1,266 @@ -# frontend-notice: 「人工复审队列」页面加 REVIEW tab(融合评价举报,后端 #1668 已就绪) +# 前端任务: 「人工复审队列」加一个 tab 显示评价举报 -**通知对象**: @mmg -**关联**: [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 融合**,以下为最终方案 +@mmg —— **此文件已是第 3 版,前两版你说看不懂,这版尽量直白。如果还有不清楚的当面问我。** --- -## 用户产品语义 +## 一句话需求 -进「内容安全 → 人工复审队列」页面就能看到所有需要复审的内容(包括出行人姓名、用户昵称、头像,**也包括评价举报**)+ 在同页面就能处理(隐藏/恢复/忽略/警告)。**不要新建独立的"评价举报处理"二级菜单**。 +「内容安全 → 人工复审队列」页面 **顶部加一个 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 -POST /admin/review/report-queue/{reviewId}/warn-author +┌─────────────────────────────────────────────────────┐ +│ 人工复审队列 │ +├─────────────────────────────────────────────────────┤ +│ [出行人姓名] [用户昵称] [用户头像] [评价举报] │ ← 加一个新 tab +├─────────────────────────────────────────────────────┤ +│ 切到"评价举报"时: │ +│ - 列表调 /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}` | -| **REVIEW** | `GET /admin/review/report-queue/page` | `/admin/review/report-queue/{reviewId}/{hide,restore,warn-author}` + `report/{reportId}/ignore` | +| 评价内容 | `content` | 超过 100 字省略号,鼠标 hover tooltip 看全文 | +| 作者 | `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 字省略号) | -| 作者 | `userNickname`(超链接到用户详情用 `userId`) | -| 关联产品 | `productName` | -| 举报数 | `reportCount` | -| 状态 | `reportStatus` 中文化(详见下文枚举) | -| 举报原因统计 | `reasonStats`(图表或 `广告 3 / 辱骂 1` 格式),通过字典 `review_report_reason` 翻译 label | -| 隐藏时间 | `hiddenAt`(状态 = HIDDEN 时显示) | -| 创建时间 | `createTime` | -| 操作 | 按 `reportStatus` 显示不同按钮(下文) | +| `NORMAL` | `警告作者` `忽略全部` | +| `PENDING_REVIEW` | **`隐藏评价`(主)** `警告作者` `忽略全部` | +| `HIDDEN` | **`恢复评价`(主)** | -### 4. 操作按钮(按 reportStatus 路由) +#### 各按钮对应的接口 -| reportStatus | 中文 | 显示按钮 | -|---|---|---| -| `NORMAL` | 正常 | 警告作者 / 忽略全部 | -| `PENDING_REVIEW` | 待复审 | **隐藏评价**(主按钮) / 警告作者 / 忽略全部 | -| `HIDDEN` | 已隐藏 | **恢复评价**(主按钮) | +```js +// 隐藏评价(弹个框让 admin 输 reason) +axios.post(`/admin/review/report-queue/${reviewId}/hide`, { + reason: '违规广告' // 必填 +}) -> "忽略全部" = 列表内每条 `reporters[]` 行循环调 `POST /admin/review/report-queue/report/{reportId}/ignore`(批处理) -> "警告作者" → `POST /admin/review/report-queue/{reviewId}/warn-author`(后端目前仅落日志,站内信 SDK 待集成) -> "隐藏评价" 弹框输入 reason → `POST /admin/review/report-queue/{reviewId}/hide` body `{reason}` -> "恢复评价" → `POST /admin/review/report-queue/{reviewId}/restore` +// 恢复评价 +axios.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: -| 字段 | 类型 | 说明 | -|---|---|---| -| `reviewId` | Long (`@JsonFormat(shape=STRING)`) | 评价 ID | -| `content` | String | 评价正文 | -| `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` | 各 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=违规广告 (可选,模糊匹配评价内容/作者昵称) +```js +// 拿字典(整个项目通用接口) +axios.get('/admin/system/dict-data/by-type', { + params: { dictType: 'review_report_reason' } +}) ``` -### 字典 `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` | 垃圾营销 | -| `OFFENSIVE` | 不友善/辱骂(注: 后端实际入库 dictValue 也可能是 `INSULT` 见 V20260507_001) | -| `FRAUD` | 欺诈/虚假 | -| `OTHER` | 其他 | +| 建议(下拉) | 改成 `reportStatus` 下拉(全部 / 正常 / 待复审 / 已隐藏),传 `reportStatus` 参数 | +| 场景 | **隐藏**(评价举报无 scene) | +| 关键词 | 保留,传 `keyword` 参数 | -> 字典值后端权威以 `/admin/system/dict-data/by-type` 实际返回为准,前端动态渲染不要硬编码。 +切回前 3 个 tab 时恢复原本的筛选字段。 --- -## 列表排序与分页 +## 你不用关心的(后端已搞定的) -后端默认按 `reportCount DESC, create_time DESC` 排序,**前端不要重排**。 -分页用标准 `page` / `pageSize`,响应 `total` / `records`。 +- 用户从小程序点举报(`POST /mp/review/report`)的数据已经 100% 落到后端 DB,实测 `reviewId=2048001001001001005` 已经有 1 条 SPAM 举报数据,你接入后能直接看到。 +- 网关路由、JWT 认证、字典初始化 全部就绪。 +- 排序后端默认 `reportCount DESC, createTime DESC`,**前端不要 sort 重排**。 +- `reporterOpenid` 已经脱敏(`oK****n8`),**不要再 mask**。 --- -## 兼容现有 3 类的 UX +## 验收清单(全部对了再说做完) -切换 tab 时: -- TRAVELER/USER_NICKNAME/USER_AVATAR tab 保留现有页面的 列定义/操作按钮(approve/reject)不动。 -- REVIEW tab 是**完全独立的列定义 + 操作按钮**(因为 VO 字段完全不同)。 -- 顶部筛选区 "建议 / 场景 / 关键词" 在 REVIEW tab 时: - - "建议" 字段 → 复用为 `reportStatus` (NORMAL / PENDING_REVIEW / HIDDEN) - - "场景" 字段 → REVIEW tab 隐藏(评价举报无 scene 概念) - - "关键词" 字段 → 走后端 `keyword` 参数,模糊评价内容/作者昵称 +- [ ] 「人工复审队列」页面顶部 4 个 tab,默认还是"出行人姓名" +- [ ] 点"评价举报" tab,列表能加载,看到 reviewId=2048001001001001005 的那条数据 +- [ ] 评价内容/作者昵称/产品名 都是中文显示 +- [ ] `reasonStats` 的 SPAM 翻译成 "广告/营销" +- [ ] 状态显示"正常"(因为目前 reportCount=1 < 3) +- [ ] 操作按钮显示"警告作者 / 忽略全部"(NORMAL 状态) +- [ ] 展开行能看到 1 条 reporters,openid `oK****n8`,原因 "广告/营销" +- [ ] 切回"出行人姓名" tab,原列表还能正常加载(没把老 tab 弄坏) --- -## 验收 +## 工作量 -1. 「内容安全 → 人工复审队列」页面顶部 tab 出现 "评价举报",权限正常 -2. 默认 tab 仍为出行人姓名(不影响现有用户习惯) -3. 切到 "评价举报" tab,列表能加载,字段渲染对齐上方表格 -4. `reasonStats` 字典 label 翻译正确,中文显示 "广告/营销" "辱骂/人身攻击" 等 -5. 操作按钮按 `reportStatus` 显示且功能调通,操作完列表自动刷新 -6. 展开行显示 `reporters[]`,每条"忽略本条"按钮调 `report/{reportId}/ignore` 通 -7. 测试服 `reviewId=2048001001001001005` 已有 1 条 SPAM 举报数据,接入后能直接看到 +预计 1.5 天。 + +后端 0 改动。**所有接口都已部署到测试服,你可以直接联调。** --- -## 不在本次范围 +## 联调环境 -- 站内信通知作者(`warnAuthor` 后端 TODO) -- 后端跨服务统一聚合(Phase 2D-B,已评估优先级低) -- 评价举报独立的二级菜单(已废弃,统一走 tab) +- 测试服域名: `https://api.test.1814.love:9443` +- admin 账号: `admin / Admin@123456` +- 已有测试数据: `reviewId=2048001001001001005`(粉色风蚀古堡产品的一条评价,被 SPAM 举报 1 次) +- 想再造数据: 用小程序登录,POST `/mp/review/report` body `{"reviewId":"...","reason":"INSULT"}` --- -## 工作量预估 +## 历史 changelog 注意 -前端 ~1.5 天(VO 适配 + tab 切换 + 操作按钮按状态路由 + 展开行 + 字典翻译)。**后端 0 改动**。 +之前的 2 版让你"新建独立菜单 评价举报处理",**那个方向作废**。最终方向就是这版: **现有页面加 tab**。