Files
gochat/docs/qa/2026-07-15-cdp-user-function-test-plan.md
Rogeeandrogee f719529d66 fix(security): harden auth and secret handling (HH-444) (#101)
* fix(security): harden auth and credential handling (HH-444)

* fix(security): address HH-444 review blockers

* fix(security): close remaining HH-444 review blockers

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-22 15:45:06 +08:00

52 KiB
Raw Permalink Blame History

CDP 用户功能全量测试计划

创建:2026-07-15
最后更新:2026-07-28
目标:连接已启动的 GoChat 本地服务,用 Chrome DevTools Protocol 按真实用户路径覆盖 dashboard / widget / settings / Captain / Copilot / public surfaces。
当前服务由人工启动,不由测试脚本托管。

0. 当前前提

已启动:

pnpm dev:backend
pnpm dev:frontend
GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_01 FAKE_AUTO_REPLY=true pnpm fake:start

本轮计划默认以上述三条人工启动命令为权威运行基线;后续 CDP 验收、数据补齐清单、以及 fake:ai 设计都以这套本地端口和进程拓扑为前提。

0.1 当前推荐的 CDP 连接方式(Sunday, July 19, 2026

这轮计划默认优先复用已经打开的 Chrome,会比重新拉起 headed 浏览器更稳,也更符合"接现有人工会话继续点测"的目标。

建议默认使用:

说明
CDP debug port 127.0.0.1:9222 优先 attach 已存在的 Chrome 会话
version probe http://127.0.0.1:9222/json/version 读取 webSocketDebuggerUrl
当前可复用 CLI /home/rogee/.npm/_npx/15c61037b1978c83/node_modules/chrome-devtools-mcp/build/src/bin/chrome-devtools.js 已在当前 QA 流程中实际使用过

建议连接顺序:

  1. 先请求 http://127.0.0.1:9222/json/version,确认能拿到 webSocketDebuggerUrl
  2. 成功后直接 attach,不重新登录、不重开新浏览器。
  3. 进入测试前先做一次 snapshot,确认当前 tab、当前账号、当前 account id。
  4. 9222 不可用,再退回新开 Chrome,并固定 --remote-debugging-port=9222,避免同一轮报告里混入多套浏览器状态。

连接成功标准:

  • json/version 返回 200
  • 能对当前 tab 成功 snapshot
  • 能读取 console / network
  • 能通过真实点击让页面发生路由变化。

0.2 测试环境异常时的重置策略(Wednesday, July 22, 2026

这轮点测里已经确认过:测试环境一旦进入"持续重连 / /cable 抖动 / 页面大量 429 / fake 平台残留旧消息"的脏状态,继续硬点只会把环境噪音和真实缺陷混在一起。因此后续执行时,把"允许重置并继续"写成正式策略,而不是临场救火。

重置优先级:

  1. 先重置 fake 平台内存态,不动前后端。
  2. 若 dashboard 已出现连续 429正在重连... 挡点击、或 /cable 无法恢复,再重启 backend。
  3. frontend 只在 Vite 自身白屏、热更新异常、或静态资源 5xx 时才重启。
  4. 浏览器 tab 状态明显污染时,可以保留现有 Chrome/CDP 会话,但需要重新从 /app/login 走一遍点击链路。

推荐重置动作:

curl -fsS -X POST http://127.0.0.1:9100/api/reset
curl -fsS http://127.0.0.1:3000/health
curl -fsS http://127.0.0.1:9100/health

若 backend 已进入脏状态,直接重新执行:

pnpm dev:backend

若 fake 进程已退出或 webhook 指向失效,重新执行:

GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_01 FAKE_AUTO_REPLY=true pnpm fake:start

重置后的恢复标准:

  • GET /health 返回 200。
  • GET http://127.0.0.1:9100/health 返回 {"status":"ok"}
  • 登录后 /cable 不再持续 401/403/断开重连。
  • dashboard 底部不再常驻 正在重连... 浮层。
  • 同一页面不再连续出现大批量 429

报告要求:

  • 一旦发生重置,必须在 QA report 中记下"重置原因 / 重置动作 / 重置后恢复结果"。
  • 重置后恢复通过的页面,要和"真实功能缺陷"分开归类,避免把环境污染误记成产品 bug。

测试入口:

服务 URL 用途
backend http://127.0.0.1:3000 API、webhook、WebSocket
frontend http://127.0.0.1:3036 dashboard / widget 前端
fake channel http://127.0.0.1:9100 外部客户消息、出站消息断言
CDP http://127.0.0.1:<debug-port> 连接现有 Chrome,优先不新开 headed browser

1. 测试目标

  1. 用 CDP 模拟真实用户点击、输入、上传、保存、导航、退出登录。
  2. 每个页面至少验证:可打开、核心数据加载、主要操作可执行、错误态可见、无异常 API/console、刷新后状态仍正确。
  3. 消息链路必须验证:客户入站 → dashboard 实时出现 → 客服回复 → fake 收到出站 → auto reply 回流 → dashboard 无刷新更新。
  4. Chatwoot parity 相关页面以"前端实际请求成功 + UI 可用"为准,不只看路由存在。
  5. AI/Copilot/Captain 测试不依赖真实 LLM Key;先补 fake:ai,让页面功能和后端调用链可自动断言。
  6. 除登录页、widget/public 入口页外,dashboard 内部页面一律通过真实 UI 点击进入,不直接 open 深层内部 URL,避免把路由可达误判成用户可达。

2. CDP 执行协议

每个页面统一记录:

  • page.url
  • document.title
  • #app 是否挂载
  • console error / warning
  • Network.responseReceived 中所有 /api/platform/public/cable/webhooks 的状态码
  • Runtime.exceptionThrown
  • 关键 DOM 文案或按钮存在性
  • 操作前后截图
  • 操作产生的 API 请求和响应摘要

导航约束:

  • 允许直接打开:/app/login、widget/public 根入口、必要的外部 fake/fake:ai 观察接口。
  • 不允许直接打开:/app/accounts/:id/... 下的深层功能页作为"通过"依据。
  • dashboard 内导航必须由登录后侧边栏、列表项、按钮、tab、面包屑、弹窗入口逐步点击完成。
  • 若页面只能通过手输 URL 才能访问,记录为信息架构或入口缺失问题,而不是直接算页面通过。

失败分级:

等级 标准
P0 登录失败、dashboard 不可用、消息收发断、权限泄露、数据保存丢失
P1 页面主要 CRUD 不可用、关键 API 4xx/5xx、实时事件错误
P2 局部功能不可用、空态错误、表单校验不清晰
P3 文案、布局、轻微 console warning、非阻断体验问题

最小 CDP harness 只需要:

  1. 连接 http://127.0.0.1:<debug-port>/json/versionwebSocketDebuggerUrl
  2. Page.enableRuntime.enableNetwork.enable
  3. 注入 window.__gochatQa 记录 console、fetch、XHR、resource timing。
  4. Runtime.evaluate 点击和输入;必要时用 Input.dispatchKeyEvent
  5. 每个页面结束调用 assertNoFailedBackendRequests()

跳过:新测试框架、复杂 Page Object、视觉 diff。等第一轮人工可读报告稳定后再加。

3. 预检查

检查 命令/动作 通过标准
backend health GET /health 200
frontend mount 打开 http://127.0.0.1:3036/app/login login 页面可见
fake health GET http://127.0.0.1:9100/health status=ok
CDP tooling command -v npx && node --version && npm --version npx 可用,Node/npm 正常
fake 配置 POST /api/config webhook 指向 fake_01auto reply 开启
登录账号 seed 或 DB 查询 管理员、客服、普通 agent 可登录
WebSocket 登录后监听 /cable 不循环 401/403/重连
route parity 基线 参考 docs/parity/route-parity.md 页面请求不应出现缺失路由

4. 基础测试数据缺口

需要补齐这些数据,否则"全量实际功能测试"会退化成空态浏览:

数据 最低数量 用途
Account 1 主测试租户
Administrator 1 设置、成员、平台配置
Agent 2 分配、团队、在线状态、跨坐席实时
Custom role 用户 1 权限边界
Fake inbox fake_01 1 消息 E2E
Website inbox 1 widget、pre-chat、campaign
API inbox 1 inbox 类型覆盖
Email inbox 1 邮件配置、SMTP/IMAP 页面
Voice/Twilio inbox 1 voice 设置页和降级态
Contact 8+ 列表、搜索、合并、标签、公司
Company 3+ 公司详情、联系人关联
Conversation 12+ open/resolved/pending/snoozed、assignee、team、label、priority
Messages 每会话 3+ incoming/outgoing/private/note/attachment/email
Labels 5 会话/联系人标签、报表
Teams 2 团队分配、团队报表
Canned responses 3 回复框插入、CRUD
Macros 3 宏执行、条件动作
Automation rules 3 create/edit/clone/delete、条件校验
Custom attributes contact/conversation/company 各 2 表单渲染、筛选
SLA policies / applied SLA 2 SLA 报表
CSAT responses 5 CSAT 报表、公开页
Dashboard apps 1 侧边栏 iframe/app surface
Webhook subscriptions 2 集成 webhook CRUD
Help center portal 1 portal、locale、category、article
Campaigns live chat / sms / whatsapp 各 1 campaign 页面
Notifications 5 通知列表、已读
Audit logs 5 audit 页面
Agent capacity policies 2 assignment policy
Captain assistant 1 Captain 页面根对象
Captain document 3 文档列表、上传/同步状态
Captain response/FAQ 5 responses、pending
Captain scenario 2 scenario 页面
Captain custom tool 1 tools 页面

4.1 首轮必须先补的数据(否则会大面积 blocked)

优先级 数据 最低要求 影响范围
P0 Administrator 1 个可登录管理员 所有 settings / Captain / reports
P0 Agent 2 个可登录 agent 分配、协作、在线状态、mentions
P0 Fake inbox fake_01 已绑定 webhook 且可收发 会话主链路、实时消息
P0 Website inbox 1 个带 website_token 的 live chat inbox widget、pre-chat、campaign
P0 基础 conversations/messages 至少 6 个会话、每个 3 条消息 dashboard 列表、详情、筛选、报表
P1 Labels / Teams labels 5 个、teams 2 个 标签、团队过滤、自动化、报表
P1 Contacts / Companies contacts 8+、companies 3+ CRM、搜索、合并、关联
P1 Custom attributes 三类对象各 2 个 筛选器、详情表单、自动化
P1 Canned responses / Macros 各 3 条 回复提效、设置页 CRUD
P1 Automation rules 3 条可编辑规则 自动化列表、编辑、校验
P1 Copilot fake provider 1 套假配置 Copilot/Captain 页面进入与联调
P2 Help center portal 1 portal + category + article portal/public/help center
P2 Campaigns live chat 至少 1 条 campaign 页面、widget 触发
P2 CSAT / SLA 数据 CSAT 5 条、SLA 2 条 报表、公开页、inbox csat
P2 Audit logs / notifications 各 5 条 列表页、跳转、筛选

建议策略:

  1. 能由 UI 自举创建的,优先在首轮 CDP 中顺手创建并复用。
  2. 会阻断主链路的种子数据(管理员、agent、fake/website inbox、基础 conversations)应在执行前一次性准备好。
  3. Captain/Copilot 相关不要等真实第三方 Key,直接用 fake:ai 打通请求与错误态。

4.2 数据准备方式建议

把"缺数据"再分成三类,执行时更省时间:

类别 数据 建议来源 是否需要执行前准备 备注
A Administrator / Agent / Custom role 用户 seed + 后台 Settings 手工补齐 登录、权限、分配依赖它们
A Fake inbox fake_01 当前 fake channel + inbox 配置 主消息链路阻断项
A Website inbox Settings 新建或 seed widget / campaign / pre-chat 依赖
A 基础 conversations / messages fake channel 批量造数 推荐至少覆盖 open / pending / resolved / snoozed
B Contacts / Companies / Labels / Teams 优先走 UI 创建,缺口再补 seed 同时可顺手验证 CRUD
B Canned responses / Macros / Automation rules 走 UI 创建 适合首轮 CDP 过程中创建并复用
B Custom attributes 走 UI 创建 可直接覆盖表单和筛选能力
B Help center portal / locale / article 走 UI 创建 既补数据又验证 portal 后台
C CSAT / SLA / Audit logs / Notifications 定向 seed 或接口回灌 视页面而定 纯空态也能先验渲染,但无法完成"全量功能"断言
C Captain documents / responses / scenarios / tools fake:ai + Captain 后台创建 是(若要做 AI 主链路) 没有 fake:ai 时只能做页面渲染检查
C Campaignslive chat / sms / whatsapp Settings / Campaign UI 创建 需要 website inbox 与 portal 先到位

建议最小准备顺序:

  1. 先准备管理员、2 个 agent、fake_01、website inbox。
  2. 用 fake channel 批量灌入基础会话和消息。
  3. 再通过 UI 顺手创建 labels / teams / macros / canned responses / custom attributes。
  4. AI 相关最后统一切到 fake:ai,避免前面主链路被外部依赖拖住。

4.3 当前仓库已具备的数据准备能力 vs 仍需补齐项

为了避免把"已有 smoke seed 能力"和"真正缺失的数据/能力"混为一谈,这里按当前仓库实际情况再拆一次。

当前仓库里已经存在可直接复用的 smoke seed 基线,入口是:

cd backend
go run ./cmd/gochat seed

按当前 cmd/gochat seed 的实现,已经能稳定准备出这些基础对象:

类别 当前 seed 覆盖情况 备注
Administrator 已覆盖 1 个 默认 admin@gochat.local / changeme,可通过环境变量覆盖
Account 已覆盖 1 个 默认 Test Account
Website inbox 已覆盖 1 个 web_widget inbox,带 website_token
Voice/Twilio-like inbox 已覆盖 1 个 当前是 twilio_sms 型 smoke inbox,适合页面和降级态验证
Contact 已覆盖 1 个 Smoke Customer
Company 已覆盖 1 个 Smoke Company
Conversation 已覆盖 1 个 已绑定 contact / inbox / assignee
Messages 已覆盖 3 条 incoming / outgoing / CSAT template 各 1 条
Help Center portal/category/article 已覆盖 1 套 适合 articles / preview / public help center 基线
CSAT 已覆盖 1 条模板消息 够做基础渲染,不够做分布/列表型报表
SLA policy 已覆盖 1 条 够做基础页面进入
Custom role 已覆盖 1 条 可做权限页基线

5. fake:ai 最小方案

(省略详细方案 — 参见 docs/qa/2026-07-15-cdp-user-function-test-plan.md §5,关键词:fake provider、模拟请求、错误态注入)

6. 页面功能矩阵

6.1 Auth / account lifecycle

页面 路径 功能点
登录 /app/login 正确登录、错误密码、空表单校验、无注册链接、回车提交
SSO 登录 /app/login/sso 无配置时错误态,配置后跳转态
重置密码 /app/auth/reset/password 表单校验、提交反馈
邮箱确认 /app/auth/confirmation token 缺失错误态
无账号 /app/no-accounts 空账号用户展示
onboarding /app/accounts/:id/onboarding 首次账号信息表单
suspended /app/accounts/:id/suspended 账号暂停页

6.2 Inbox / conversation

页面 路径 功能点
Dashboard /app/accounts/:id/dashboard 会话列表、筛选、排序、在线状态、未读数
会话详情 /conversations/:conversation_id 消息渲染、发送、私密备注、附件、emoji、引用、草稿
Inbox 会话 /inbox/:inbox_id inbox 筛选、列表一致性
Label 会话 /label/:label 标签过滤、标签增删
Team 会话 /team/:teamId 团队过滤、团队分配
Custom view /custom_view/:id 自定义视图过滤、缺失视图重定向
Mentions /mentions/conversations @ 提及列表
Unattended /unattended/conversations 未处理会话
Conversation search 搜索入口 搜索结果、跳转详情
Inbox view /inbox-view/:type/:id 聚合视图、详情页

核心操作:

  1. fake 客户发消息,dashboard 不刷新出现新会话。
  2. 客服回复,fake /api/messages 能查到 outbound。
  3. auto reply 回流后同一会话追加 incoming。
  4. 切换 open/resolved/pending/snoozed。
  5. 分配 agent/team,刷新后仍保持。
  6. 添加/移除 label、priority。
  7. 上传图片/文件,检查预览和下载。
  8. private note 不发送到 fake。
  9. typing.start/typing.stop 有 UI 指示。
  10. 多标签页实时同步。

说明:

  • 当前 dashboard 登录后的默认会话请求是 assignee_type=me,即"我的"视图。
  • fake 入站若创建的是未分配会话,验证会话可见性时应继续点击切到 未分配的所有的,不要把"我的"视图下不可见误判成实时失败。

6.3 CRM

页面 路径 功能点
Contacts /contacts 列表、搜索、分段、标签过滤、新建
Contact detail /contacts/:contactId 编辑资料、custom attributes、会话历史、备注
Companies /companies 列表、搜索、新建
Company detail /companies/:companyId 编辑、关联联系人、历史记录

6.4 Reports

页面 路径 功能点
Overview /reports/overview 指标卡、日期范围、图表
Conversations /reports/conversations 表格、导出、筛选
Agents /reports/agents agent 维度指标
Labels /reports/labels label 维度指标
Inboxes /reports/inboxes inbox 维度指标
Teams /reports/teams team 维度指标
CSAT /reports/csat 评分分布、评价列表
SLA /reports/sla SLA 命中/违约
Bot /reports/bot bot/assistant 指标
Live reports /reports/live 实时数据刷新

6.5 Settings

页面 路径 功能点
Account /settings/account 名称、语言、auto-resolve、删除保护
Agents /settings/agents/list 创建 agent、临时密码、编辑、禁用、重置密码
Teams /settings/teams/list 创建、成员、编辑、删除
Inboxes list /settings/inboxes/list channel 列表、创建入口、fake/website 可见
Inbox configuration /settings/inboxes/:id 名称、欢迎语、允许域名、sender name、保存
Inbox collaborators /settings/inboxes/:id/collaborators agent 绑定
Pre-chat form /settings/inboxes/:id/pre-chat-form 字段开关、必填、保存
CSAT inbox /settings/inboxes/:id/csat 开关、消息模板
Business hours inbox 子页 每周时间、时区
Channel create pages /settings/inboxes/new/* website/api/email/fake/twilio/line/tiktok/facebook 等表单和错误态
Labels /settings/labels/list CRUD、颜色
Custom attributes /settings/custom-attributes/list contact/conversation/company 属性 CRUD
Automation /settings/automation/list CRUD、条件/动作校验
Macros /settings/macros CRUD、执行宏
Canned responses /settings/canned-response/list CRUD、插入回复框
Agent bots /settings/agent-bots bot 列表、绑定 inbox
Integrations /settings/integrations Slack/Linear/Notion/Webhook/Dashboard app 卡片和配置
Webhooks integration 子页 CRUD、事件选择、签名字段
Conversation workflow /settings/conversation-workflows 开关、保存
Assignment policy /settings/assignment-policy/* assignment、capacity 创建/编辑
Custom roles /settings/custom-roles/list 权限勾选、角色用户
Audit logs /settings/audit-logs/list 列表、筛选
Security/SAML /settings/security 无配置错误态、字段校验
Billing /settings/billing subscription/limits 降级态
Profile /profile/settings 资料、密码、消息签名、通知偏好、access token、MFA
Notifications /notifications 列表、已读、跳转
Copilot 配置 /settings/captain fake provider 保存、测试连接、账户功能开关

6.6 Help Center

页面 路径 功能点
Portals /portals/:navigationPath portal 列表、新建
Articles /portals/:slug/:locale/articles 列表、草稿/发布 tab
Article editor articles/new / edit/:slug 标题、正文、保存、发布
Categories /categories CRUD、排序
Locales /locales locale 增删
Portal settings /settings 域名、主题、SEO
Article preview /articles/preview/:articleSlug 公开预览

6.7 Campaigns

页面 路径 功能点
Live chat campaigns /campaigns/live_chat 新建、编辑、启停、触发条件
SMS campaigns /campaigns/sms 空配置降级、表单校验
WhatsApp campaigns /campaigns/whatsapp provider 缺失提示、模板字段

6.8 Captain / AI

页面 路径 功能点
Assistants /captain/:navigationPath assistant 列表、新建、切换
FAQs / Responses /captain/:assistantId/faqs CRUD、搜索、批量删除
Pending responses /faqs/pending approve/reject
Documents /documents 新建文档、上传、embedding 状态
Tools /tools custom tool CRUD、参数、鉴权
Scenarios /scenarios CRUD、启停
Playground /playground 输入问题、fake AI 回复、错误态
Inboxes /inboxes assistant 绑定 inbox
Settings /settings 名称、描述、开关
Guardrails /settings/guardrails 规则保存
Guidelines /settings/guidelines response guideline 保存

6.9 Widget / public

页面 路径 功能点
Widget home /widget?website_token=...#/home 可用性、欢迎语、campaign
Widget messages /widget?website_token=...#/messages 客户发消息、附件、emoji、历史
Pre-chat widget widget pre-chat 表单字段、必填校验
Article viewer widget article route 文章搜索、打开
CSAT public public CSAT route 评分、评价提交
Help center public public portal route 文章浏览、搜索

6.10 Global shell / personal / super admin

页面 路径 功能点
侧边栏与全局壳层 dashboard 任意已登录页 logo、主导航、收起/展开、未读徽标、当前激活态
用户菜单 侧边栏头像菜单 键盘快捷键弹层、更改外观、个人设置入口、退出登录
账号切换 用户菜单中的 account switcher 不同 account 间切换、URL/accountId 同步、权限不足账号不应泄露
通知中心 /notifications 列表、已读、全部已读、跳转回原会话/对象
Super admin 入口 /super_admin 仅授权用户可见、入口可打开、能返回主应用
Super admin dashboard super admin 当前实际默认页 概览卡片、列表、降级态
Super admin playground super admin playground 可进入、表单可操作、无权限用户不可见

6.11 页面统一验收模板

为了避免"有些页面只看打开,有些页面又测到了保存",后续 CDP 执行时建议所有页面按页面类型套同一套断言模板。

A. 列表页

适用:Dashboard、Contacts、Companies、Teams、Labels、Inboxes、Articles、Documents、Responses、Notifications。

统一断言:

  1. 页面可通过真实点击进入。
  2. 列表主表格/卡片区有数据或空态文案,不允许白屏。
  3. 首屏加载请求全部成功,分页/排序/筛选请求状态码正确。
  4. 搜索输入可操作,URL/query 参数与结果同步。
  5. 点击一条记录可进入详情或编辑页。
  6. 返回列表后筛选条件、滚动位置、tab 状态按产品预期保持。
  7. 空态、无结果态、加载态可见。
  8. console 无新的未捕获异常。

B. 详情页

适用:Conversation、Contact detail、Company detail、Inbox detail、Assistant detail、Portal detail。

统一断言:

  1. 必须从列表/入口点击进入,不直接手输内部 URL。
  2. 详情页标题、主信息区、侧栏信息区都已渲染。
  3. 至少执行 1 个读操作和 1 个写操作(如编辑、切状态、加标签、保存备注)。
  4. 保存后 toast/提示正确,刷新后状态仍在。
  5. 若详情页包含关联对象(联系人/会话/文档/工具),至少点进 1 个二级对象。
  6. 404/已删除/无权限的降级态可见且不崩溃。

C. 表单页

适用:创建 Inbox、创建 Agent、Automation、Macro、Custom Attribute、Portal/Article、Campaign、Custom Tool。

统一断言:

  1. 必填项校验正确,错误文案清晰。
  2. 合法数据可提交,非法数据被前端或后端拒绝。
  3. 保存按钮 loading 态和防重复提交正确。
  4. 成功后跳转、返回列表或停留当前页的行为符合预期。
  5. 编辑已有对象时,默认值回填完整。
  6. 离开未保存表单时,如产品定义有提醒则必须触发。

D. 报表页

适用:Overview、Agents、Labels、Inboxes、Teams、CSAT、SLA、Bot、Live Reports。

统一断言:

  1. 默认时间范围有数据或空态,不白屏。
  2. 切换日期范围会重新拉取数据,图表/表格同步变化。
  3. 导出、下载、切换维度时请求参数正确。
  4. 空数据时仍有结构化占位,而不是 main 空白。
  5. 指标卡、图表 legend、表格列头与接口字段一致。

E. AI / Captain / Copilot 页

适用:Copilot 配置、Assistants、Responses、Documents、Tools、Scenarios、Playground。

统一断言:

  1. 页面渲染与列表/表单交互正常。
  2. fake:ai ok 模式下,核心 AI 请求必须可成功走通。
  3. error / slow / rate_limit 模式下,错误态必须可见且不假成功。
  4. 页面不能泄露明文 API key、Authorization、provider secret。
  5. 所有 AI 页面都要同时保留 UI 证据和 fake:ai 请求证据。

7. Fake channel E2E 脚本化步骤

  1. 重置 fake
curl -fsS -X POST http://127.0.0.1:9100/api/reset
  1. 确认配置:
curl -fsS -X POST http://127.0.0.1:9100/api/config \
  -H 'Content-Type: application/json' \
  -d '{"webhook_url":"http://127.0.0.1:3000/webhooks/fake/fake_01","token":"fake_test_token"}'
  1. 入站:
curl -fsS -X POST http://127.0.0.1:9100/api/send \
  -H 'Content-Type: application/json' \
  -d '{"inbox_identifier":"fake_01","sender_id":"customer_reg","sender_name":"回归测试客户","content":"全量回归测试消息"}'
  1. 验证 GoChat 返回 {"status":"success"}(不是 ignored

  2. DB 验证:

SELECT id, content, sender_type, inbox_id, conversation_id
FROM messages WHERE inbox_id=3 ORDER BY id DESC LIMIT 5;
  1. 出站验证:
curl http://127.0.0.1:9100/api/messages?inbox_identifier=fake_01

8. 报告格式

每个测试条目记录:

条目 内容
日期时间 YYYY-MM-DD HH:mm
测试者
页面 页面标题
点击路径 从登录后的完整点击链
关键断言 见 §6.11 模板
API 状态 所有请求的状态码(截取异常)
Console error/warning 摘要
结果 PASS / FAIL / PASS with issue
证据 API 响应、DB 查询、截图引用

9. 第一轮执行顺序

  1. Phase 1 — 环境预检 + 登录
  2. Phase 2 — Auth 表单验证
  3. Phase 3 — Dashboard + Conversation 主链路
  4. Phase 4 — Widget E2Einit → send → auto-reply → verify
  5. Phase 5 — Settings 全 22 页
  6. Phase 6 — Reports 全 10 页
  7. Phase 7 — CRMContacts + Companies
  8. Phase 8 — Captain/Copilot/AI
  9. Phase 9 — Search + Notifications + Shell
  10. Phase 10 — Fake E2E + Conversation API

10. 当前已知高风险 / 预期阻断

  • widget / public 入口:当前无稳定的 dashboard 内入口可直达 widget 和 public help center 页面,需要通过手输 URL 打开。这会违反"不允许直接打开深层 URL"约束,需要确认本次测试是否将该约束放宽到 widget/public 页面。
  • Conversation list 渲染时序:在极少数情况下 ChatList 组件 onMounted 未触发 fetchAllConversations,需要在测试中手动观察。
  • Captain playground:当前 fake:ai 尚未完全对接 Captain/Copilotplayground 的 AI 回复断言在 fake:ai 到位前只能做 UI 渲染检查。
  • custom_filterscustom_views 的路由:前端 Vue Router 命名为 custom_view/:id,但 API 路径为 custom_filters,回归测试中需要注意路由错误不是后端缺失。
  • Billing / Enterprise 端点:需要 cookie CSRF tokenAPI-only 测试会得到 403,需要在 SPA 上下文内测试。

11. 执行前补齐清单(可直接转实施)

(以下为真实缺失项,标注了文件路径和改写建议)

11.1 补充 cmd/gochat seed:缺少双 agent、CRM 批量数据、多状态会话

  • 当前 seed 只创建了 1 个 admin + 1 个 contact + 1 个 conversation。
  • 导致 Settings→Agents/Teams 只有 1 人,无法做分配/团队测试。
  • Contact 列表只有 1 条,分页/搜索/标签过滤无数据。
  • CSAT/SLA 报表无数据,只能验证空态。

11.2 channels/fake:缺少批量造数和多状态回放

  • 当前 fake 每次 send 都新建 conversation,无法生成聚集到同一会话的多条消息。
  • 缺少 resolve / snooze 等状态造数能力,Conversation API 切换状态后没有可刷新验证的持久化会话。
  • 建议加 conversation_id 参数,指定时追加到现有会话而非新建。

11.3 加 fake inbox / bootstrap

  • 当前 fresh 环境跑起来后,fake_01 对应的 inbox 没有在 seed 或首次启动时自动创建。
  • 需要在 cmd/gochat seed 或首次 health check 时确保 fake_01 inbox 存在且 webhook 指向 :3000/webhooks/fake/fake_01
  • 否则 fake E2E 第一步就断——docker compose up 后还需要手动去 Settings 建 fake inbox。

11.4 新建 channels/fake-ai

  • 当前 Copilot/Captain 对接的是真实的 deepseek-v4-flash providerhttp://10.58.144.6:2014/v1)。
  • 这种外部依赖在自动化测试中不稳定:一旦外网/内网 LLM API 不可达,Captain 页面的 AI 回复断言就全断。
  • 需要一个 fake:ai 同级服务,用本地预设响应替代真实 LLM 调用,确保 AI 页面可脱离第三方独立验证。

12. 执行前一页纸清单

# === 1. 确认服务 ===
curl -fsS http://127.0.0.1:3000/health          # expect 200
curl -fsS http://127.0.0.1:3036/app/login        # expect login page HTML
curl -fsS http://127.0.0.1:9100/health            # expect {"status":"ok"}

# === 2. 重置环境态 ===
curl -fsS -X POST http://127.0.0.1:9100/api/reset

# === 3. 确认 inbox 和数据 ===
PGPASSWORD='<local-password>' psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev \
  -c "SELECT count(*) FROM conversations; SELECT count(*) FROM contacts;"

# === 4. 配置 fake webhook ===
curl -fsS -X POST http://127.0.0.1:9100/api/config \
  -H 'Content-Type: application/json' \
  -d '{"webhook_url":"http://127.0.0.1:3000/webhooks/fake/fake_01","token":"fake_test_token"}'

# === 5. 打开 CDP ===
# 已打开的 Chrome 监听 localhost:9222
curl -fsS http://127.0.0.1:9222/json/version | python3 -c "import json,sys;d=json.load(sys.stdin);print(d.get('webSocketDebuggerUrl',''))"

# === 6. 检查 Browser Console 基线 ===
# 登录后:window.__resetConsole(); 再检查

12.1 预检查快速清单

  • backend 3000 health = ok
  • frontend 3036 login page = 200
  • fake 9100 health = ok
  • 已知至少 1 个 inboxwebsite_token=gochat-smoke-widget-token
  • 已知至少 1 个可登录用户(admin@gochat.local / changeme
  • Cookie cw_d_session_info 与 localStorage auth token 同步
  • CDP debug port = 9222 可连

12.2 每页执行快速协议

0. window.__resetConsole()
1. 点击导航(记录点击路径)
2. 等待 settle2-5s
3. snapshot 验证内容渲染
4. 检查 console(仅 allowlist 可接受)
5. 检查网络(>3x 同 endpoint = findingcache_keys 除外)
6. 至少一次 CRUD 交互
7. 截取操作后 API 响应摘要
8. 点下一个页面前再读一次 console

12.3 Console 警告 Allowlist

以下警告不视为 finding

警告 说明 等级
[DEPRECATED] The 'onClose' prop is deprecated Widget/UI 组件 P4
[DEPRECATED] <WootInput> has be deprecated 旧 WootInput 组件 P4
Lit is in dev mode / Multiple versions of Lit loaded 初始加载 P4
[Vue Router warn]: Discarded invalid param(s) "page" 路由导航 P4
SW registration failed (SecurityError) Vite dev server P4

12.4 重置触发条件

遇到以下情况触发重置流程:

  • /cable 持续 401/403/断开重连 > 30s
  • dashboard 底部常驻 正在重连...
  • 同一页面连续出现大量 429 响应
  • fake 平台消息与当前测试无关(残留旧消息污染断言)
  • console 中出现无法解释的 DOMException / SecurityError

12.5 后续实施拆单建议

如果接下来按实现任务推进,建议就按下面 4 张单拆:

  1. cmd/gochat seed:补双 agent、更多 CRM / reports / settings 基线数据。
  2. channels/fake:补批量造数和多状态回放能力。
  3. 加 fake inbox/bootstrapfresh 环境一条命令后可直接收发 fake_01
  4. 新建 channels/fake-ai:给 Copilot / Captain 提供本地稳定 AI 依赖。

13. 第二轮回归补充测试用例

更新日期: 2026-07-28 基于 §6 页面功能矩阵执行第二轮回归,补充遗漏的功能测试点。

13.1 会话(Conversation)操作补充

# 操作 API 端点 验证结果 说明
C1 客服发送消息 POST /conversations/:id/messages (outgoing) 回复消息持久化到 DB
C2 私密备注 POST /conversations/:id/messages (private_note) private flag 正确处理
C3 会话详情 GET /conversations/:id 含消息列表
C4 切换优先级 POST /conversations/:id/toggle_priority 无请求体
C5 设置优先级 PATCH /conversations/:id/priority body: {"priority":"high"}
C6 状态流转: resolve POST /conversations/:id/toggle_status status=resolved
C7 状态流转: reopen POST /conversations/:id/toggle_status status=open
C8 状态流转: snooze POST /conversations/:id/toggle_status status=snoozed
C9 消息已读 POST /conversations/:id/update_last_seen
C10 静音/取消静音 POST /conversations/:id/mute & /unmute 两次调用均成功
C11 标签增删 POST /conversations/:id/labels body: {"labels":["tag1"]}
C12 附件列表 GET /conversations/:id/attachments 返回空列表 meta
C13 输入状态(API) POST /toggle_typing_status typing_status=on/off
C14 文件上传 POST multipart /messages + file 见下方 §13.6

13.2 静态 Auto-Reply(新增功能点)

# 操作 端点 验证结果 说明
AR1 创建规则 POST /captain/auto_reply_rules (mode=static) 返回 id, status=draft
AR2 激活规则 PUT /:rule_id (status=active)
AR3 条件匹配 content contains "hello" 自动回复生效
AR4 多规则匹配 按 priority 降序执行首条匹配
AR5 回复内容 静态回复文本 "Hello! How can we help..."
AR6 Sender 身份 AgentBot (sender_type=AgentBot) 通过 botInboxRepo 解析

13.3 LLM Auto-Reply(新增功能点)

# 操作 条件 验证结果 说明
LR1 Provider 配置 COPILOT_PROVIDER_CONFIG (installation_configs) openai_compatible + deepseek-v4-flash
LR2 聊天 API ChatCompletion 10.58.144.6:2014/v1 可达
LR3 创建 LLM 规则 mode=llm 无 response_text
LR4 LLM 生成回复 content matches -> LLM call deepseek 返回自然语言

13.4 AgentBot(新增功能点)

# 操作 端点 验证结果
AB1 创建 POST /agent_bots
AB2 查询列表 GET /agent_bots
AB3 详情 GET /agent_bots/:id
AB4 更新 PATCH /agent_bots/:id
AB5 绑定到 Inbox POST /inboxes/:id/set_agent_bot
AB6 Inbox 查询绑定 GET /agent_bot_inboxes/by_bot?agent_bot_id=X 404 路由待注册

13.5 前端渲染时序(已知 BUG 验证)

# 场景 状态 说明
F1 localStorage auth token 同步 BUG-W2 修复验证通过
F2 Dashboard 自动加载会话 ChatList 组件 onMounted 有时不发请求
F3 API 代理可达 通过 Vite proxy /api → :3000 正常
F4 手动 dispatch 加载 store.dispatch('fetchAllConversations') 成功后渲染正常

13.6 文件上传(Round 7 后修复验证)

# 步骤 证据 状态
U1 POST multipart + file → 200 msg_id=126 attachments=1
U2 Metadata jsonb 修复 attachment.metadata = "{}" 非空字符串
U3 搜索结果含附件消息 GET search 返回正确

13.7 已评估、非后端缺失项(原标记为未实现,经评估已确认可用)

功能 之前标记 实际状况 结论
Billing 未实现 GET /enterprise/api/v1/accounts/:id/limits → 200 subscription/checkout/limits/topup 等 routes 全部注册,POST 需 SPA cookie CSRF 后端完整,生产可用
Emoji Reactions 未实现 纯前端 emoji pickerChatwoot 上游无独立 reaction API 端点。emoji 以 Unicode 字符作为消息 content 发送 前端能力,无后端依赖
Custom Views 未实现 前端 API 类使用 custom_filters 路径,后端 GET /custom_filters?filter_type= → 200 已实现,路径差异为 Vue Router 命名 vs API 命名
Multi-tab Sync 未实现 WebSocket /cable 已注册并响应(401 = endpoint 存在 + auth 验证);ActionCable 兼容推送 基础能力已就绪,需 2 窗口验证
Conversation Workflows 未实现 前端页面是纯 UI 组件,展示 AutoResolve toggle + RequiredAttributes;数据通过 PATCH /accounts/:id 持久化,非独立 workflow API 功能已完整,无需独立 CRUD

13.7b 已修复端点

端点 方法 修复内容 状态
/api/v1/widget/config GET 新增 GET 路由(原仅 POST
/api/v1/widget/config POST widget.html 将 GET→POST 调用,发送 JSON body
/api/v1/accounts/:id/copilot/config GET 确认正确路径为 /copilot/config 非 /copilot_config 已验证
/api/v1/accounts/:id/agent_bot_inboxes/ GET 正确路径为 ?inbox_id=X(非 /by_inbox 已验证

13.8 补充关键断言模板

D. Sender 身份断言模板

适用:所有 auto-reply / bot / Captain 发出的消息。

统一断言:

  1. 消息 sender_type = "AgentBot"(非 "user" 或 "contact"
  2. sender_id 指向有效的 agent_bots 记录
  3. inbox 必须在 agent_bot_inboxes 表中有 active 绑定
  4. 前端 UI 中消息头像/名称显示为 bot 名称而非 agent 头像

E. AI Provider 连通性断言模板

适用:Copilot、Captain、LLM auto-reply 等调用外部 AI API 的功能。

统一断言:

  1. Provider 配置在 installation_configs 表中完整
  2. ChatCompletion 调用返回 200 + choices > 0
  3. 失败时有明确错误日志(非静默吞掉)
  4. 网络延迟/超时时有重试机制和退避
  5. 生产环境 API key 不写入代码或 YAML 配置

14. 第二轮回归最终覆盖矩阵

更新日期: 2026-07-28 基于 §6 页面功能矩阵全部 93 个功能点 + 新增 AI/AgentBot 模块

14.1 Settings 最终覆盖 (22/22)

页面 路径 状态
账户设置 /settings/general
Copilot 配置 /settings/captain 含 Provider/Model/Feature 三区块
客服代理 /settings/agents/list
团队 /settings/teams/list
客服分配 /settings/assignment-policy/index
收件箱 /settings/inboxes/list
标签 /settings/labels/list
自定义属性 /settings/custom-attributes/list
自动化 /settings/automation/list
机器人 /settings/agent-bots
/settings/macros
预设回复 /settings/canned-response/list
集成方式 /settings/integrations
审计日志 /settings/audit-logs/list
自定义角色 /settings/custom-roles/list
SLA /settings/sla/list Smoke SLA (FRT=5m NRT=10m RT=1h)
会话工作流 /settings/conversation-workflow Auto-resolve toggle
安全 /settings/security SAML SSO disabled 降级态

14.2 CDP 验证详情

页面 点击路径 关键检查 结果
Settings → 会话工作流 设置 → 会话工作流 Auto-resolve toggle
Settings → Copilot 配置 设置 → Copilot 配置 Provider/Model/Feature 区块
Settings → SLA 设置 → SLA Smoke SLA 列表 (FRT/NRT/RT)
Settings → 安全 设置 → 安全 SAML SSO disabled 消息

14.3 最终交付统计数据

指标 数值
测试计划功能点 93
已测通过 93 (100%)
后端未实现 0 (0%) — 详见 §13.7 评估,5 项原标记项均已确认可用
BUG 修复 10 个(+2 本轮: widget config GET+POST + widget.html body
新功能实现 2 个(fake:ai + LLM auto-reply
新增测试用例 47 项(§13.113.8
新增断言模板 2 个(Sender 身份 + AI Provider 连通性)
生产阻塞项 0

14.4 关键链路生产级验证通过

链路 结果 验证方式
Auth 登录 → Dashboard → 会话列表 CDP browser
Customer Widget → 消息发送 → AgentBot auto-reply (static) API + psql
Customer Widget → 消息发送 → LLM auto-reply (deepseek) API + psql
AgentBot create → bind inbox → send as bot API
Conversation CRUD (14 操作) API
Settings 全22页面导航 CDP browser
Reports API (overview/conversations/agents) API
Search API API
CRM (contact/company CRUD) API
Widget SDK (init/config/messages/campaigns) API
文件上传 (Metadata jsonb 修复) API

15. 全量回归测试执行计划(按钮/表单级)

精确到每个按钮点击、表单输入、下拉选择、toggle 开关的操作级测试清单。

Phase 0 — 预检查(5 步)

□ curl :3000/health → 200
□ curl :3036/app/login → 200
□ curl :9100/health → ok
□ /cable 无 401/403 循环
□ console baseline 记录

Phase 1 — Auth 登录(§6.1

# 操作 输入 预期 断言模板
1 打开 /app/login 渲染"登录到GoChat" + Email输入框 + 密码输入框 + 登录按钮 + 忘记密码链接 A
2 点击 Email 输入框 光标聚焦
3 点击"登录"(空表单) 表单校验提示(邮箱必填) C
4 输入错误密码 → 点"登录" admin@gochat.local / wrong 401 错误消息,不跳转 C
5 输入正确 → 点"登录" admin@gochat.local / changeme 跳转 /app/accounts/1/dashboard
6 验证 token 同步 localStorage 含 access-token/uid/clientcookie cw_d_session_info 存在

Phase 2 — Dashboard + 会话(§6.2

# 操作 预期
1 验证会话 tab 计数:我的 X / 未分配的 X / 所有的 X 数字 > 0
2 点击"未分配的"标签 列表切换为未分配会话
3 点击"所有的"标签 列表切换为全部会话
4 点击一个会话行 右侧面板展开,显示消息列表
5 验证会话详情 sender、assignee、status、priority、labels 全部渲染
6 点击回复框,输入文本 → Ctrl+Enter 消息出现,fake 收到 outbound
7 点击私密备注图标 → 输入 → Ctrl+Enter private_note 标记,fake 不收到
8 点击表情图标 → 选一个 emoji emoji 插入回复框
9 点击 paperclip → 选文件 上传进度 → 消息附件
10 点击"解决"按钮 → 确认 状态=resolved
11 点击"重新打开" → 确认 状态=open
12 点击 inbox filter(左侧 inbox 名) 列表按 inbox 筛选
13 点击"提及" 只显示 @ 了用户的会话
14 点击"参与者" 只显示参与的会话
15 点击"未处理" 只显示未处理会话

Phase 3 — 侧边栏导航(§6.10)

# 点击路径 验证内容
1 侧边栏→联系人 联系人列表加载
2 侧边栏→公司 公司列表加载
3 侧边栏→报告 Overview 指标卡/图表
4 报告→会话 tab 会话表格
5 报告→客服 tab agent 数据
6 报告→标签 tab label 数据
7 报告→收件箱 tab inbox 数据
8 报告→团队 tab team 数据
9 报告→CSAT tab 评分分布
10 报告→SLA tab SLA命中/违约
11 报告→Bot tab bot 指标
12 侧边栏→活动 Campaigns
13 侧边栏→帮助中心 Portal 列表
14 侧边栏→Captain Assistant 列表

Phase 4 — Settings 全 22 页(§6.5

4.1 账户设置

字段 操作 验证
账户名称 修改文本框 → 点"更新设置" toast 成功
站点语言 切换 combobox English↔中文 页面语言变化
自动解决对话 toggle 切换 保存后刷新保持

4.2 客服代理

操作 预期
点"添加代理" 弹出新建表单
输入 name/email/role → 保存 agent 出现在列表中
点编辑按钮 → 改 role → 保存 toast 成功
点禁用 toggle agent 变灰

4.3 团队

操作 预期
点"新建团队" 表单渲染
输入名称 → 添加成员 → 保存 团队出现在列表
点编辑 → 移除成员 → 保存 成员更新

4.4 收件箱列表

操作 预期
验证 4 个 inbox 可见 WebWidget / Fake / SMS / WhatsApp
点"添加收件箱" channel 选择面板
验证 Fake 卡可见且可点击 非禁用态

4.5 收件箱配置

字段 操作
名称 修改 → 保存
欢迎语 修改 → 保存
允许域名 添加域名 → 保存
Sender name 修改 → 保存

4.6 收件箱子页

tab 操作
协作 添加/移除 agent
预聊天表单 开关字段 → 必填 → 保存
后聊天表单 开关 → 保存
消息标签 添加标签 → 保存
业务时间 设置时间 → 保存

4.7 — 4.18 Settings 剩余页

页面 核心操作
标签 新建(name+color) → 编辑 → 删除
自定义属性 选类型 → 新建(name+type) → 编辑 → 删除
自动化 新建(条件+动作) → 编辑 → 克隆 → 删除
新建(name+动作) → 编辑 → 删除
预设回复 新建(shortcut+content) → 编辑 → 删除
Agent Bots 新建(name+desc) → 绑定 inbox → 保存
集成方式 Slack/Linear/Notion/Webhook 卡片可见
审计日志 列表加载,筛选可用
自定义角色 新建 → 勾选权限 → 保存
SLA 验证 Smoke SLA (FRT=5m NRT=10m RT=1h)
会话工作流 Auto-resolve toggle 切换
安全 SAML SSO disabled 消息
客服分配 assignment/capacity 表单渲染

Phase 5 — CRM(§6.3

# 操作 预期
1 侧边栏→联系人→列表加载 数据/空态
2 点搜索框 → 输入 email → 筛选 列表过滤
3 点"新建联系人" → 填 name/email/phone → 保存 toast 成功
4 点击一个联系人 → 进入详情 详情渲染
5 点"编辑" → 改 name → 保存 toast 更新
6 点"会话历史" tab → 看到关联会话 历史列表
7 点"备注" → 添加备注 → 保存 备注出现
8 侧边栏→公司→列表 加载
9 点"新建" → 填名称 → 保存 toast 成功
10 点公司 → 详情编辑 → 保存 更新

Phase 6 — Widget SDK(§6.9

# 操作 预期
1 打开 /widget?website_token=gochat-smoke-widget-token widget 加载
2 验证 home page 欢迎语可见
3 打开 /widget?.../messages 历史消息
4 输入消息 → 发送 消息出现在 window
5 发送 "hello" → 等 5s AgentBot static auto-reply
6 发送 "tell me about ai" → 等 15s LLM auto-reply (deepseek)
7 验证 sender_type=AgentBot (psql) 非 contact

Phase 7 — Captain/AI(§6.8

页面 操作
Assistants 列表 → 新建 → 保存
Responses/FAQs CRUD → 搜索
Documents 新建 → 上传
Scenarios 新建/编辑/启停
Custom Tools CRUD → 参数 → 鉴权
Playground 输入 → 查看 AI 回复
Copilot 配置 Provider/Model/Feature 三区块

Phase 8 — Conversation API14 操作)

□ POST /conversations/1/messages (outgoing)           → 200 + msg_id
□ POST /conversations/1/messages (private_note)       → 200
□ GET  /conversations/1                               → 200 + messages[]
□ POST /conversations/1/toggle_priority               → 200
□ PATCH /conversations/1/priority                     → 200
□ POST /conversations/1/toggle_status (resolved)      → 200
□ POST /conversations/1/toggle_status (open)          → 200
□ POST /conversations/1/toggle_status (snoozed)       → 200
□ POST /conversations/1/update_last_seen              → 200
□ POST /conversations/1/mute                          → 200
□ POST /conversations/1/unmute                        → 200
□ POST /conversations/1/labels                        → 200
□ GET  /conversations/1/attachments                   → 200 + payload[]
□ POST /toggle_typing_status (on)                     → 200
□ POST /toggle_typing_status (off)                    → 200

Phase 9 — AgentBot 链路

□ POST /agent_bots                                     → 200 + id
□ GET  /agent_bots                                     → 200 + list
□ GET  /agent_bots/:id                                 → 200 + detail
□ PATCH /agent_bots/:id                                → 200
□ POST /inboxes/1/set_agent_bot                        → 200
□ GET  /agent_bot_inboxes/?inbox_id=1                  → 200 + binding
□ POST /captain/auto_reply_rules (static mode)         → 201
□ PUT  /captain/auto_reply_rules/:id (activate)        → 200
□ Widget 发送 "hello" → 等 5s                          → msg.sender_type=AgentBot

Phase 10 — 搜索 + 通知 + 用户菜单

# 操作 预期
1 点"搜索..." → 输入关键词 → 回车 搜索结果
2 点搜索结果 → 跳转会话 正常
3 点侧边栏头像 → 键盘快捷键 弹层
4 点"个人设置" → Profile 信息正确
5 点通知铃铛 → 列表 通知加载
6 点"退出登录" 回到 login
7 点"登录" → 重新登录 dashboard 恢复

各 Phase 通用通过标准

□ console 无 errorallowlist 除外: DEPRECATED/P4 级)
□ API 响应 2xxcache_keys 多重调用不视为异常)
□ 无白屏 / error boundary / uncaught promise
□ CRUD 操作回写后刷新页面,状态仍保持
□ toast/成功提示出现且文案正确