feat(support): in-app feedback & complaints with admin handling; persist reply ratings

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-30 09:42:11 +08:00
co-authored by Claude Opus 5.5
parent 8f58eccecf
commit 3c8123d912
24 changed files with 1244 additions and 1 deletions
@@ -0,0 +1,31 @@
# PROGRESS · 反馈与投诉 + 回复评价持久化 · 2026-09-30
> 合规轮第四项之一(产品 2026-09-30「合规与法律的前四个你可以帮我做吗」,选定「站内反馈表单 + 后台处理」)。执行:Claude fork 子代理 C。分支 `codex/feedback-complaints-20260930`,基于 `codex/compliance-base-20260930`(`2cd37720`,含 `lib/legal-entity.ts` 占位)。
## 复用与新增
| 项 | 复用 | 新增 |
|---|---|---|
| 账户菜单 | `app-sidebar.tsx` 的 Menu(只加一行「反馈与投诉」) | `components/feedback-dialog.tsx`(侧栏自持状态,任何页面原地打开,不经 `/?account=`、不动 Home) |
| 写入与限流 | 服务端 `createAdminSupabaseClient().rpc` 模式(同 telemetry) | 迁移 `20260930030000_user_feedback.sql`:`user_feedback` 表、`submit_user_feedback()`(五次 / 滚动一小时、会话归属校验) |
| 回复评价 | 页面既有的 `messageFeedback` 状态与 `toggleChatMessageFeedback`;术语沿用 CONTEXT「回复评价」 | `reply_ratings` 表(库里原先没有评价表)、`set_reply_rating()` / `list_reply_ratings()`、`/api/reply-ratings`、`lib/reply-ratings.ts`、`hooks/use-reply-ratings-sync.ts`(Home 的 useState / useRef 数不变) |
| 后台 | RBAC 表、`admin_has_permission`、`audit.admin_audit_logs`、`ResourceTable`、refine 资源权限映射 | 权限 `support.feedback.read/write`(owner / operations / support 读写,auditor 只读);`admin_list_user_feedback()` / `admin_update_user_feedback()`(带审计);`/api/admin/feedback`、`/admin/feedback` |
## 决定
- 评价按「会话 + 第几条消息」存,附回答文本 SHA-256 前 16 位;重新打开对话时只恢复文本哈希一致的评价,重新生成的回答不继承旧评价。未保存的本地会话(非 UUID)不写库。
- 反馈只存用户自己写的内容、选填联系方式、(勾选时)会话编号;表上没有任何对话原文列。
- 两张表都开 RLS、对所有运行时角色撤销表权限,只能经 SECURITY DEFINER 函数读写;随账号(auth.users)与会话级联删除。
- 新内容举报在后台排最前、标红。
## 验证(Linux,Node 22.14;基线 = `origin/staging` `5208f19b` 的全量结果)
| 项 | 结果 |
|---|---|
| `tsc --noEmit` | 0 错 |
| `npm run lint` | 0 error,126 warning(同基线) |
| `npm test` 全量 | 4,427 / fail 24;失败名单与基线逐条相同(无 Docker 的数据库 / 部署套件);新增 9 条,消失 0 条 |
| 新测试 `feedback-complaints-20260930.test.tsx` | 9 / 9:校验、错误映射(限流 429)、弹窗提交与成功态、限流提示、无会话时不出现附带勾选、菜单只有一个入口、评价保存与按文本哈希恢复、SQL 限流 / 归属 / 撤权 / 审计、RBAC 映射 |
| `next build` | `/` 仍 `○ Static` |
| 首屏 gzip-9 | 646,480 B → 648,901 B(+0.37%;侧栏首屏多了反馈弹窗组件) |
| 真实数据库 / 真机 | 未验证,见 `BLOCKED.md`「反馈与投诉 + 回复评价」 |