# 测试用例 - H5 智能推荐重构 > **REQ 编号**: REQ-用户-006 > **版本**: v1.0 > **日期**: 2026-07-28 > **作者**: 宋献 (Simon) + Duckula > **状态**: 🟡 待评审(基于 PRD v1.0-Frozen + 技术方案 v1.0) > **关联文档**: > - **PRD(冻结)**:`docs/01-产品文档/05-用户端H5/PRD-REQ-用户-006-智能推荐重构-v1.0-Frozen.md` > - **技术方案**:`docs/02-技术文档/技术架构/技术方案-REQ-用户-006-智能推荐重构-v1.0.md` > - **任务说明书**:`docs/07-项目管理/任务说明书/任务说明书-REQ-用户-006-智能推荐重构.md` > - **原型图**:`docs/01-产品文档/05-用户端H5/原型-REQ-用户-006-智能推荐重构-v1.0.html` --- ## 一、基本信息 | 项目 | 内容 | |------|------| | **用例集名称** | TC-用户-006 H5 智能推荐重构 | | **关联 PRD** | REQ-用户-006 v1.0-Frozen(§4.7 已冻结) | | **用例总数** | 76 条(10 大类) | | **优先级分布** | P0 28 条 / P1 32 条 / P2 16 条 | | **关联决策** | 必须 100% 覆盖 PRD §4.7 5 个核心决策(① A / ② B / ③ B / ④ B / ⑤ B) | | **自动化目标** | 单元测试 100% 自动化 / E2E 关键路径自动化 / 灰度指标由数据埋点验证 | --- ## 二、测试范围 ### 2.1 In-scope - 后端 `asset_recommend_service.py` 重构(L1/L2/L3 + 中文匹配) - 后端新增 `recommend_progress_service.py`(T2 webhook + 轮询) - 后端新增 `topic_detector.py`(Jaccard 相似度) - 后端 `_step_assets` 改造(3s 超时 + 降级链路) - 前端 `recommendStore.ts` 状态机 - 前端 `DynamicRecommend.vue` 重构(无标题 + FIFO + 4 类卡片) - 前端 `RightPanel.vue` 适配 - WS 协议扩展(dynamic_recommend / asset_recommend / recommend_update) - localStorage 持久化 - 数据模型(recommend_progress + recommend_event) ### 2.2 Out-of-scope - 智能推荐算法升级(语义匹配/向量召回)→ v1.4+ 单独测 - 坐席端 AI 辅助 → 不在本次范围 - 多模态推荐 → PRD-REQ-AI-003 单独测 --- ## 三、测试环境 | 项 | 配置 | |----|------| | **后端** | Python 3.13 / FastAPI / pytest | | **前端** | Vue 3 / TypeScript / Vitest | | **E2E** | agent-browser(Playwright) | | **数据库** | PostgreSQL 16 + Redis 7 | | **外部 mock** | Dify 推理 / 企微审批 webhook / 火绒 API | | **环境** | dev → staging → 生产灰度 | --- ## 四、用例分类规则 | 前缀 | 模块 | 用例数 | |------|------|--------| | TC-001 | 来源 A:Dify action(T0 同步) | 8 | | TC-002 | 来源 B:L1 关键词(T1 异步) | 10 | | TC-003 | 来源 C:L2 画像(T1 异步) | 10 | | TC-004 | 来源 D:L3 角色(T1 异步) | 8 | | TC-005 | 多源合并规则(决策 ② B) | 8 | | TC-006 | 冷启动策略(决策 ① A) | 6 | | TC-007 | 触发失败降级(决策 ③ B) | 8 | | TC-008 | 话题切换检测(决策 ④ B) | 8 | | TC-009 | 跨会话持久化(决策 ⑤ B) | 6 | | TC-010 | E2E 集成 + UI 行为 | 4 | | **合计** | | **76** | --- ## 五、详细用例 ### TC-001:来源 A - Dify action(T0 同步) #### TC-001-1 [P0] 来源 A:AI 返回 action 后立即同步推送到左侧气泡 - **前置条件**:用户进入新会话,员工端 WS 已连接 - **测试步骤**: 1. 用户发送"我要申请 VPN" 2. Dify 返回 `{text: "好的", action: {approval_type: "vpn_access", title: "VPN 申请"}}` - **预期结果**: - WS `ai_reply` 立即到达(含文字 + action) - 左侧气泡显示"VPN 申请"操作卡(v2.3 后审批类只放左边) - 右侧栏 **不出现** 该审批卡(v2.3 决策) - **关联**:PRD §4.7.1 来源 A / 技术方案 §5.1 T0 #### TC-001-2 [P0] 来源 A:非审批类 action 同步推送到右侧栏 - **前置条件**:同上 - **测试步骤**: 1. 用户发送"VPN 连不上怎么办" 2. Dify 返回 `{text: "请重连", action: {type: "step_card", steps: [...]}}` - **预期结果**: - 左侧气泡显示文字 + 来源图标 - 右侧栏顶部插入操作步骤卡(蓝色边框,可展开) - **关联**:PRD §4.5 / §4.7.1 A #### TC-001-3 [P1] 来源 A:action 为空时不推送右侧栏 - **前置条件**:Dify 返回 `{text: "你好", action: null}` - **测试步骤**:触发 AI 回复 - **预期结果**: - 左侧气泡显示文字 - 右侧栏 **不增加** 任何卡片 - 不出现"暂无推荐"提示 - **关联**:技术方案 §4.1 #### TC-001-4 [P1] 来源 A:recommend_data 包含 source/trigger_timing/layer 字段 - **前置条件**:触发 AI 回复(Dify 返回非空 action) - **测试步骤**:检查 WS 数据包 - **预期结果**: - `recommend_data.source = "dify_action"` - `recommend_data.trigger_timing = "T0"` - `recommend_data.layer = "A"` - **关联**:技术方案 §5.1 #### TC-001-5 [P1] 来源 A:审批发起后立即在右侧栏顶部插入占位卡 - **前置条件**:用户发起 VPN 审批(action.approval_type = "vpn_access") - **测试步骤**: 1. 用户点击气泡中的"申请" 2. 跳转企微审批表单 3. 用户提交审批 - **预期结果**: - 提交瞬间右侧栏顶部插入审批进度占位卡(紫色边框,状态"pending") - 显示"VPN 申请 已发起,等待审批" - **关联**:技术方案 §5.3 T2 / PRD §4.7.2 #### TC-001-6 [P2] 来源 A:action.type 包含 contact_window 时只推左侧 - **前置条件**:Dify 返回 `action.type = "contact_window"` - **测试步骤**:触发 AI 回复 - **预期结果**: - 左侧气泡显示联系窗口按钮 - 右侧栏 **不出现** 该卡(PRD §4.7.1 A 限制) - **关联**:PRD §4.7.1 来源 A 补充决策 #### TC-001-7 [P2] 来源 A:action.type 包含 contact_card 时只推左侧 - **前置条件**:Dify 返回 `action.type = "contact_card"` - **测试步骤**:同上 - **预期结果**: - 左侧气泡显示联系人名片 - 右侧栏不出现 - **关联**:PRD §4.7.1 来源 A 补充决策 #### TC-001-8 [P2] 来源 A:action.type 包含 download 时只推左侧 - **前置条件**:Dify 返回 `action.type = "download"` - **测试步骤**:同上 - **预期结果**: - 左侧气泡显示下载链接 - 右侧栏不出现 - **关联**:PRD §4.7.1 来源 A 补充决策 --- ### TC-002:来源 B - L1 关键词匹配(T1 异步) #### TC-002-1 [P0] 来源 B:用户消息包含精确关键词时命中 - **前置条件**:assets.yaml 配置 `vpn` 关键词 - **测试步骤**:用户发送"VPN 连不上" - **预期结果**: - 右侧栏插入 L1 卡片(绿色边框) - 标题为"VPN 相关信息" - 置信度反映词频权重(≥ 0.5) - **关联**:PRD §4.7.1 来源 B / 技术方案 §4.2 #### TC-002-2 [P0] 来源 B:用户消息包含别名时也命中 - **前置条件**:配置 alias "虚拟专用网络" → "vpn" - **测试步骤**:用户发送"虚拟专用网络怎么用" - **预期结果**:右侧栏插入 L1 VPN 卡片 - **关联**:技术方案 §4.2 同义词表 #### TC-002-3 [P1] 来源 B:词频权重(短消息 + 高频词) - **前置条件**:用户发送"VPN"(仅 3 字) - **测试步骤**:触发 AI 回复 - **预期结果**: - L1 VPN 卡插入 - 置信度 = min(1.0, 1 × (3/3) × 10) = 1.0(短消息高权重) - **关联**:技术方案 §4.2 词频权重公式 #### TC-002-4 [P1] 来源 B:长消息中关键词被稀释 - **前置条件**:用户发送长消息(> 50 字),仅 1 次包含 "VPN" - **测试步骤**:触发 AI 回复 - **预期结果**: - L1 VPN 卡插入 - 置信度较低(< 0.5),优先级低于其他高权重推荐 - **关联**:技术方案 §4.2 长度归一化 #### TC-002-5 [P1] 来源 B:exclude_keywords 排除规则生效 - **前置条件**:配置 `exclude_keywords: ["邮箱"]` - **测试步骤**:用户发送"我的收件箱打不开,邮箱登不上" - **预期结果**: - L1 邮箱卡 **不出现**(被排除) - 仅出现其他来源推荐 - **关联**:技术方案 §4.2 排除关键词 #### TC-002-6 [P1] 来源 B:30 分钟同源抑制 - **前置条件**:5 分钟前已推送过 L1 VPN 卡 - **测试步骤**:用户再次发送"VPN 又连不上了" - **预期结果**: - L1 VPN 卡 **不重复推送**(30 分钟内同关键词抑制) - Redis 中存在 `suppress:{employee_id}:vpn` 键(TTL ≤ 30 分钟) - **关联**:PRD §4.7.3 同源抑制 / 技术方案 §6.4 #### TC-002-7 [P2] 来源 B:30 分钟后可再次推送 - **前置条件**:30 分钟前推送过 L1 VPN 卡,Redis 键已过期 - **测试步骤**:用户再次发送"VPN 连不上" - **预期结果**:L1 VPN 卡正常推送 - **关联**:技术方案 §6.4 #### TC-002-8 [P1] 来源 B:用户消息包含多个关键词时多卡 - **前置条件**:用户发送"VPN 连不上,邮箱也登不上" - **测试步骤**:触发 AI 回复 - **预期结果**: - 右侧栏插入 L1 VPN 卡 + L1 邮箱卡(2 张) - 按 layer 优先级 + confidence 排序 - 总数 ≤ 3 张 - **关联**:PRD §4.7.3 / 技术方案 §6.1 #### TC-002-9 [P2] 来源 B:消息完全不包含任何关键词时无 L1 - **前置条件**:用户发送"今天天气真好" - **测试步骤**:触发 AI 回复 - **预期结果**: - 来源 B 返回空列表 - 继续走来源 C/D 降级链路 - **关联**:技术方案 §4.2 / §6.1 #### TC-002-10 [P2] 来源 B:Dify 同步识别意图时,来源 B 可与 A 互补 - **前置条件**:Dify 返回 action(来源 A),同时用户消息包含关键词 - **测试步骤**:触发 AI 回复 - **预期结果**: - 左侧气泡显示 A 来源卡片 - 右侧栏同时显示 A 步骤卡 + B 关键词卡(不重复) - **关联**:技术方案 §6.1 去重 --- ### TC-003:来源 C - L2 画像触发(T1 异步) #### TC-003-1 [P0] 来源 C:火绒病毒库过期触发提醒 - **前置条件**:员工画像 `huorong_virusdb_date` 距今 10 天 - **测试步骤**:用户发送任意消息 - **预期结果**: - 右侧栏插入 L2 提醒卡(橙色边框) - 标题"火绒病毒库过期" - 描述包含"已 N 天未更新" - **关联**:PRD §4.7.1 来源 C / 技术方案 §4.3 #### TC-003-2 [P0] 来源 C:火绒终端版本过旧触发提醒 - **前置条件**:画像 `huorong_version = "5.0.50"`(< 5.0.73) - **测试步骤**:同上 - **预期结果**: - L2 提醒卡插入 - 描述"您的版本为 5.0.50,最新版本为 5.0.73" - **关联**:技术方案 §4.3 #### TC-003-3 [P0] 来源 C:终端防护离线触发最高优先级提醒 - **前置条件**:画像 `huorong_offline_days = 5` - **测试步骤**:同上 - **预期结果**: - L2 提醒卡插入 - priority = 100(最高) - **关联**:技术方案 §4.3 #### TC-003-4 [P1] 来源 C:联软违规项触发 - **前置条件**:画像 `unionsoft_violations = ["禁用软件", "未安装补丁"]` - **测试步骤**:同上 - **预期结果**: - L2 提醒卡插入 - extra.violations 包含违规项列表 - **关联**:技术方案 §4.3 #### TC-003-5 [P1] 来源 C:系统补丁缺失触发 - **前置条件**:画像 `unionsoft_patches_missing = 12` - **测试步骤**:同上 - **预期结果**:L2 提醒卡"系统补丁缺失" - **关联**:技术方案 §4.3 #### TC-003-6 [P0] 来源 C:画像 API 不可用时降级到 DB 缓存 - **前置条件**:`employee_profile_service.get_profile()` 超时/异常 - **测试步骤**:触发 AI 回复 - **预期结果**: - 调用 `_get_cached_profile_from_db(employee_id)` 兜底 - 如 DB 有缓存画像,按缓存触发 L2 - 如 DB 也无,走来源 D 降级 - **关联**:技术方案 §4.3 缓存画像 / PRD §4.7.5 决策 ③ B #### TC-003-7 [P1] 来源 C:画像完全为空时(API + DB 都无) - **前置条件**:新员工,无任何画像数据 - **测试步骤**:同上 - **预期结果**: - 来源 C 返回空 - 走来源 D 降级(C → D) - **关联**:PRD §4.7.5 / 技术方案 §4.3 #### TC-003-8 [P2] 来源 C:多个画像字段同时满足触发多张卡 - **前置条件**:病毒库过期 + 版本过旧 + 离线 5 天 - **测试步骤**:触发 AI 回复 - **预期结果**: - 3 张 L2 提醒卡(按 priority 排序:离线 > 版本 > 病毒库) - 总数 + 其他来源 ≤ 3 张 - **关联**:技术方案 §4.3 / §6.1 #### TC-003-9 [P2] 来源 C:画像字段格式异常时优雅跳过 - **前置条件**:`huorong_version = ""`(空字符串) - **测试步骤**:同上 - **预期结果**: - try-except 捕获 AttributeError/ValueError - 该字段跳过,其他字段继续判定 - **关联**:技术方案 §4.3 try-except #### TC-003-10 [P1] 来源 C:T1 异步触发 3s 超时保护 - **前置条件**:画像 API 响应缓慢(> 3s) - **测试步骤**:触发 AI 回复 - **预期结果**: - `asyncio.timeout(3.0)` 触发超时 - 跳过来源 C,走来源 D - 不阻塞主对话流程 - **关联**:技术方案 §5.2 / PRD §4.7.5 --- ### TC-004:来源 D - L3 角色匹配(T1 异步) #### TC-004-1 [P0] 来源 D:中文岗位名命中英文 role_assets key(精确) - **前置条件**:`profile.position = "开发工程师"` + 配置 `开发工程师: alias_of: developer` - **测试步骤**:触发 AI 回复 - **预期结果**: - 来源 D 命中 developer 资源 - L3 卡插入右侧栏(实际:按 v1.0 用户决策移到左侧) - **关联**:PRD §4.7.1 来源 D / 技术方案 §4.4 / §11 卡片归属 #### TC-004-2 [P0] 来源 D:中文岗位子串匹配 - **前置条件**:`profile.position = "前端开发工程师"` - **测试步骤**:同上 - **预期结果**: - 子串匹配命中 "developer" - confidence = 0.7(子串匹配置信度低) - **关联**:技术方案 §4.4 子串匹配 #### TC-004-3 [P1] 来源 D:别名匹配(如 "研发" → "developer") - **前置条件**:`profile.position = "研发"` + 配置 `role_aliases.developer: ["研发", ...]` - **测试步骤**:同上 - **预期结果**: - 命中 developer 资源 - confidence = 0.85 - **关联**:技术方案 §4.4 role_aliases #### TC-004-4 [P1] 来源 D:profile.position 为 None 时跳过 - **前置条件**:新员工未填写岗位 - **测试步骤**:同上 - **预期结果**: - 来源 D 返回空 - 走来源 B + C 降级(D 失败 → B + C) - **关联**:技术方案 §4.4 #### TC-004-5 [P1] 来源 D:英文 position 也能命中英文 key(兼容) - **前置条件**:`profile.position = "developer"` - **测试步骤**:同上 - **预期结果**:精确匹配 developer,confidence = 1.0 - **关联**:技术方案 §4.4 #### TC-004-6 [P2] 来源 D:双向子串匹配(key 包含 position) - **前置条件**:`profile.position = "开发"` + key = "developer" - **测试步骤**:同上 - **预期结果**: - "开发" 在 "developer" 里?否(中文不在英文里) - "developer" 在 "开发" 里?否 - 来源 D 返回空(无命中) - **关联**:技术方案 §4.4 双向子串 #### TC-004-7 [P2] 来源 D:多个角色同时命中 - **前置条件**:position = "开发"(命中 developer),但配置也有 "财务开发" - **测试步骤**:同上 - **预期结果**: - 多张 L3 卡 - 按 confidence 排序 - **关联**:技术方案 §4.4 / §6.1 #### TC-004-8 [P2] 来源 D:role_assets 配置变更热重载 - **前置条件**:调用 `asset_service.reload()` - **测试步骤**:修改 assets.yaml 后调用 reload - **预期结果**: - 配置生效,无需重启服务 - 日志输出"配置热重载完成" - **关联**:技术方案 §4.4 reload() --- ### TC-005:多源合并规则(决策 ② B) #### TC-005-1 [P0] 合并:同 recommend_id 去重,保留高 confidence - **前置条件**:A 来源产生 rec_001(confidence=0.85)+ B 来源也产生 rec_001(confidence=0.7) - **测试步骤**:触发 AI 回复 - **预期结果**: - 右侧栏仅 1 张 rec_001(保留 A 的 0.85) - **关联**:PRD §4.7.3 / 技术方案 §6.1 #### TC-005-2 [P0] 合并:layer 优先级排序 L1 > L2 > L3 - **前置条件**:A 步骤卡(layer A)+ L1 关键词卡 + L2 画像卡 + L3 角色卡 - **测试步骤**:触发 AI 回复 - **预期结果**: - 排序:A > L1 > L2 > L3 - 右侧栏显示:A 在最上 - **关联**:技术方案 §6.2 #### TC-005-3 [P0] 合并:上限 3 张 - **前置条件**:同时有 5 张推荐卡(A + 2 × L1 + L2 + L3) - **测试步骤**:触发 AI 回复 - **预期结果**: - 右侧栏仅 3 张 - 超出 2 张按排序丢弃 - **关联**:PRD §4.7.3 上限规则 #### TC-005-4 [P1] 合并:同 layer 内按 confidence DESC - **前置条件**:L1 VPN(0.85)+ L1 邮箱(0.7) - **测试步骤**:触发 AI 回复 - **预期结果**: - L1 VPN 在 L1 邮箱上方 - **关联**:技术方案 §6.1 #### TC-005-5 [P1] 合并:A 来源步骤卡优先级最高(layer A) - **前置条件**:A 步骤卡(layer A)+ L1 关键词卡 - **测试步骤**:触发 AI 回复 - **预期结果**:A 步骤卡在最上方(即使 confidence 较低) - **关联**:技术方案 §6.2 layer_priority #### TC-005-6 [P1] 合并:进度回流卡插入最上方 - **前置条件**:已有 2 张卡,审批状态变化触发 T2 - **测试步骤**:企微审批 webhook 推送 status="approved" - **预期结果**: - 进度卡插入最上方(FIFO) - 已有的 2 张卡往下排 - 超出上限时最旧的被丢弃 - **关联**:PRD §4.7.7 / 技术方案 §5.3 #### TC-005-7 [P2] 合并:merge_recommends 算法纯函数测试 - **前置条件**:单元测试 - **测试步骤**:mock 4 个来源各返回 N 张卡 - **预期结果**: - 总数 ≤ 3 - 排序符合 layer 优先级 + confidence - 同 ID 不重复 - **关联**:技术方案 §6.1 #### TC-005-8 [P2] 合并:空输入返回空列表 - **前置条件**:所有来源都返回空 - **测试步骤**:调用 `merge_recommends([], [], [], [])` - **预期结果**:返回 `[]` - **关联**:技术方案 §6.1 --- ### TC-006:冷启动策略(决策 ① A) #### TC-006-1 [P0] 冷启动:进会话 5s 内无消息 → 右侧栏完全空白 - **前置条件**:用户进入新会话 - **测试步骤**:观察右侧栏 5 秒 - **预期结果**: - 右侧栏 **完全空白** - 不显示"暂无推荐"提示 - 不显示"试试问 XXX"引导 - **关联**:PRD §4.7.4 决策 ① A #### TC-006-2 [P0] 冷启动:首条消息发出但 AI 未回复 → 右侧栏空白 - **前置条件**:用户已发消息,AI 推理中 - **测试步骤**:观察右侧栏 - **预期结果**:右侧栏空白(与决策 ① A 一致) - **关联**:PRD §4.7.4 #### TC-006-3 [P0] 冷启动:AI 回复到达瞬间 → 真实推荐填充 - **前置条件**:AI 推理完成 - **测试步骤**:观察右侧栏变化 - **预期结果**: - 按 §4.7.3 合并规则填充 - 顶部插入新卡 - **关联**:PRD §4.7.4 场景 3 #### TC-006-4 [P0] 冷启动:完全不显示任何兜底 - **前置条件**:所有来源都失败(L3 失败 + 无画像 + 无历史) - **测试步骤**:触发 AI 回复 - **预期结果**: - 右侧栏 **完全空白** - 不显示"暂无推荐,试试问 'VPN 怎么连'"提示 - **关联**:PRD §4.7.4 决策 ① A + §4.7.5 全部失败 #### TC-006-5 [P1] 冷启动:store 状态机从 empty → populated - **前置条件**:前端 recommendStore 初始化 - **测试步骤**:触发 AI 回复(A/B/C/D 任一产生卡) - **预期结果**: - state 从 `empty` → `populated` - visibleCards 非空 - **关联**:技术方案 §7.1 #### TC-006-6 [P2] 冷启动:清空后 state 回到 empty - **前置条件**:已有卡,用户清空 - **测试步骤**:调用 `store.clearAll()` - **预期结果**:state 回到 `empty`,visibleCards = [] - **关联**:技术方案 §7.1 --- ### TC-007:触发失败降级(决策 ③ B) #### TC-007-1 [P0] 降级:A 失败 → B + C + D - **前置条件**:Dify 返回非 JSON 或 action 为空 - **测试步骤**:触发 AI 回复 - **预期结果**: - 来源 A 跳过 - 来源 B/C/D 正常执行 - **关联**:PRD §4.7.5 #### TC-007-2 [P0] 降级:B 失败 → C + D - **前置条件**:用户消息不包含任何关键词(来源 B 返回空) - **测试步骤**:触发 AI 回复 - **预期结果**: - 来源 B 跳过 - 来源 C/D 正常执行 - **关联**:PRD §4.7.5 #### TC-007-3 [P0] 降级:C 失败 → D(关键降级) - **前置条件**:画像 API 不可用 + DB 无缓存 - **测试步骤**:触发 AI 回复 - **预期结果**: - 来源 C 跳过 - 来源 D 命中(如果有中文岗位) - 至少能展示 1 张卡(保证有内容) - **关联**:PRD §4.7.5 / 技术方案 §4.3 #### TC-007-4 [P0] 降级:D 失败 → 空 - **前置条件**:profile.position 为 None + 用户消息无关键词 + 无画像 - **测试步骤**:触发 AI 回复 - **预期结果**: - 所有来源失败 - 右侧栏空(按决策 ① A 走空状态) - **关联**:PRD §4.7.5 #### TC-007-5 [P0] 降级:全部失败 → 右侧栏空(与决策 ① 一致) - **前置条件**:所有来源都失败 - **测试步骤**:触发 AI 回复 - **预期结果**: - 右侧栏 **完全空白** - 不显示"暂无推荐"提示 - **关联**:PRD §4.7.4 + §4.7.5 #### TC-007-6 [P1] 降级:T1 异步触发 3s 超时 - **前置条件**:来源 B/C/D 全部响应缓慢 - **测试步骤**:模拟慢响应 - **预期结果**: - 3s 超时触发 - 不阻塞主对话 - 右侧栏空 - **关联**:技术方案 §5.2 #### TC-007-7 [P2] 降级:降级路径日志完整 - **前置条件**:触发降级(C → D) - **测试步骤**:检查后端日志 - **预期结果**:日志包含 `[AssetRecommend] 画像获取失败/超时(C → D 降级)` - **关联**:技术方案 §5.2 日志 #### TC-007-8 [P2] 降级:recommend_event 埋点记录降级触发次数 - **前置条件**:降级发生时 - **测试步骤**:检查 recommend_event 表 - **预期结果**: - 存在 `event_type='degraded'` 记录 - 包含降级链路信息(C→D / 全部失败) - **关联**:技术方案 §11.2 埋点 --- ### TC-008:话题切换检测(决策 ④ B) #### TC-008-1 [P0] 话题切换:Jaccard 相似度 < 0.3 触发清空 - **前置条件**:最近 3 条消息关键词集合 = {VPN, 重连, aTrust},新消息 = {会议室, 预定, 时间} - **测试步骤**:用户发送"帮我预定会议室" - **预期结果**: - Jaccard = 0/6 = 0 < 0.3 - 触发话题切换 - L1 推荐清空(按 layer 清除) - **关联**:PRD §4.7.6 / 技术方案 §9.1 #### TC-008-2 [P0] 话题切换:Jaccard 相似度 ≥ 0.3 不触发清空 - **前置条件**:最近 3 条 = {VPN, 重连, aTrust},新消息 = {VPN, 又连不上} - **测试步骤**:用户发送"VPN 又连不上了" - **预期结果**: - Jaccard = 1/5 = 0.2 < 0.3(边界) - L1 保留(VPN 关键词继续命中) - **关联**:技术方案 §9.1 #### TC-008-3 [P1] 话题切换:意图突变触发清空 - **前置条件**:Dify 上次返回 intent_type="vpn_query",本次 "meeting_book" - **测试步骤**:触发 AI 回复 - **预期结果**:清空对应 layer 的推荐 - **关联**:PRD §4.7.6 / 技术方案 §9.2 #### TC-008-4 [P1] 话题切换:会话分隔清空所有跨会话推荐 - **前置条件**:用户点"结束会话"或新建会话 - **测试步骤**:用户新建会话 - **预期结果**: - 所有跨会话推荐清空 - L2/L3/progress 也清(按"会话分隔"规则) - **关联**:PRD §4.7.6 #### TC-008-5 [P1] 话题切换:L2/L3/progress 不被 L1 清空影响 - **前置条件**:已有 L1 VPN + L2 病毒库 + progress VPN 申请 - **测试步骤**:用户切换话题到"会议室" - **预期结果**: - L1 VPN 清空 - L2 病毒库保留(与话题无关) - progress VPN 申请保留(跨会话持久) - **关联**:PRD §4.7.6 #### TC-008-6 [P2] 话题切换:算法纯函数测试 - **前置条件**:单元测试 - **测试步骤**:构造边界用例 - **预期结果**: - 空集合相似度 = 1.0 - 完全相同相似度 = 1.0 - 完全不同相似度 = 0 - **关联**:技术方案 §9.1 #### TC-008-7 [P2] 话题切换:阈值为 0.3 边界测试 - **前置条件**:Jaccard = 0.29 和 0.31 - **测试步骤**:分别测试 - **预期结果**: - 0.29 < 0.3 → 触发 - 0.31 ≥ 0.3 → 不触发 - **关联**:技术方案 §9.1 #### TC-008-8 [P2] 话题切换:检测失败时静默跳过(不阻塞主流程) - **前置条件**:`extract_keywords` 抛异常 - **测试步骤**:mock 异常 - **预期结果**: - 异常被捕获 - 主对话正常进行 - 不触发清空(保守处理) - **关联**:技术方案 §9.1 try-except --- ### TC-009:跨会话持久化(决策 ⑤ B) #### TC-009-1 [P0] 持久化:L1 关键词推荐不持久化 - **前置条件**:用户会话 1 产生 L1 VPN 卡 - **测试步骤**:关闭浏览器,重新打开 - **预期结果**: - L1 VPN 卡 **不出现**(localStorage 无记录) - **关联**:PRD §4.7.7 / 技术方案 §10.2 #### TC-009-2 [P0] 持久化:L2 画像提醒持久化 30 天 - **前置条件**:用户会话 1 产生 L2 火绒过期提醒 - **测试步骤**:关闭浏览器,重新打开 - **预期结果**: - L2 提醒卡 **仍在**(localStorage 有记录) - 数据包含 `ttl_days: 30` - **关联**:PRD §4.7.7 / 技术方案 §10.2 #### TC-009-3 [P0] 持久化:L3 角色资源持久化 - **前置条件**:用户会话 1 产生 L3 开发者资源 - **测试步骤**:关闭浏览器,重新打开 - **预期结果**:L3 卡仍在 - **关联**:PRD §4.7.7 #### TC-009-4 [P0] 持久化:审批进度持久化直到完结 - **前置条件**:用户发起 VPN 审批,progress 卡插入 - **测试步骤**: 1. 关闭浏览器,重新打开 2. 企微审批 webhook 推送 status="completed" - **预期结果**: - 关闭前 progress 卡仍在 - webhook 后 progress 卡标记"已完成" - 后续不再显示(已完结) - **关联**:PRD §4.7.7 / 技术方案 §5.3 #### TC-009-5 [P1] 持久化:localStorage 配额超限降级 - **前置条件**:localStorage 写入失败(QuotaExceededError) - **测试步骤**:mock QuotaExceededError - **预期结果**: - 捕获异常 - LRU 淘汰最旧数据 - 新数据正常写入 - **关联**:技术方案 §10.1 / §15 R3 #### TC-009-6 [P2] 持久化:profile.position 变更时清空 L3 - **前置条件**:用户从"开发"调到"财务" - **测试步骤**:修改 profile.position + 触发 AI 回复 - **预期结果**:原 L3 开发者资源清空,命中新 L3 财务资源 - **关联**:PRD §4.7.7 --- ### TC-010:E2E 集成 + UI 行为 #### TC-010-1 [P0] E2E:员工发"VPN 申请"完整链路 - **前置条件**:agent-browser + dev 环境 - **测试步骤**: 1. 打开 H5 员工端 2. 发送"我要申请 VPN" 3. 观察 AI 回复 + 右侧栏 4. 点击"申请" - **预期结果**: - 左侧气泡显示"VPN 申请"操作卡 - 右侧栏顶部插入审批进度占位卡 - 跳转企微审批表单 - **关联**:技术方案 §3.2 时序图 / 端到端 #### TC-010-2 [P0] E2E:话题切换 + 持久化组合 - **前置条件**:已有 L1/L2/progress 卡 - **测试步骤**: 1. 用户切换话题 2. 验证 L1 清空 3. 验证 L2/progress 保留 4. 关闭浏览器重新打开 5. 验证 L2/progress 仍在 - **预期结果**:所有规则同时生效 - **关联**:综合 #### TC-010-3 [P0] UI:右侧栏无标题 - **前置条件**:任意场景 - **测试步骤**:观察右侧栏 - **预期结果**: - 顶部无"⚡ 智能推荐"标题 - 顶部无"相关推荐"/"运维提醒"/"常用资源"分组标题 - 仅卡片内容 - **关联**:PRD §4.5 #### TC-010-4 [P0] UI:FIFO 插入验证 - **前置条件**:先产生卡 A,再产生卡 B - **测试步骤**:观察卡片顺序 - **预期结果**:B 在 A 上方(最新优先) - **关联**:PRD §4.5 / §4.7.3 #### TC-010-5 [P0] UI:边框颜色区分 layer - **前置条件**:同时有 L1/L2/L3 卡 - **测试步骤**:观察每张卡左边框 - **预期结果**: - L1 = 绿色 - L2 = 橙色 - L3 = 灰色 - A = 蓝色 - progress = 紫色 - **关联**:PRD §4.5 #### TC-010-6 [P1] UI:操作步骤可展开 - **前置条件**:A 来源步骤卡 - **测试步骤**:点击"展开 ▼" - **预期结果**:显示完整步骤列表,按钮变"收起 ▲" - **关联**:PRD §4.6 #### TC-010-7 [P1] 性能:右侧栏渲染 < 200ms - **前置条件**:10 张卡准备就绪 - **测试步骤**:使用浏览器 Performance API 测量 - **预期结果**:render 时间 < 200ms - **关联**:技术方案 §14.1 #### TC-010-8 [P1] 性能:T1 异步触发 < 3s - **前置条件**:正常响应 - **测试步骤**:测量从用户发消息到右侧栏出现卡的时间 - **预期结果**:< 3s - **关联**:技术方案 §14.1 #### TC-010-9 [P1] 性能:T0 同步触发零延迟 - **前置条件**:Dify 返回 action - **测试步骤**:测量 ai_reply 与右侧栏卡的时间差 - **预期结果**:同一 WS 包发出(0ms 延迟) - **关联**:技术方案 §14.1 #### TC-010-10 [P1] WS 断连降级 - **前置条件**:手动断开 WS - **测试步骤**: 1. 断开 WS 2. 触发 AI 回复 3. 重连 WS - **预期结果**: - 断连期间 store 保持现有卡 - 重连后增量同步 - **关联**:技术方案 §14.2 --- ## 六、验收标准对照(PRD §4.7 5 个决策) | 决策 | 测试用例覆盖 | 状态 | |------|------------|------| | ① 冷启动 A | TC-006(6 条) | ✅ 100% | | ② 多源合并 B | TC-005(8 条) | ✅ 100% | | ③ 触发失败降级 B | TC-007(8 条) | ✅ 100% | | ④ 话题切换检测 B | TC-008(8 条) | ✅ 100% | | ⑤ 跨会话持久化 B | TC-009(6 条) | ✅ 100% | | §4.7.1 4 类触发源 | TC-001~004(36 条) | ✅ 100% | | §4.7.2 3 种触发时机 | TC-001~003(28 条) | ✅ 100% | | §4.7.5 触发失败降级 | TC-007(8 条) | ✅ 100% | --- ## 七、灰度验证标准 | 阶段 | 规模 | 通过条件 | 用例支撑 | |------|------|---------|---------| | **1%** | 10 人,1 天 | 无 P0/P1 失败 | TC-010-1~10 全通过 | | **10%** | 100 人,2 天 | 点击率 > 5%(recommend_event 埋点) | TC-005/TC-009 | | **50%** | 500 人,3 天 | 点击率 > 10% | TC-005/TC-009 | | **100%** | 全量 | 点击率 > 15%,自助解决率 > 10% | 长期监控 | --- ## 八、风险与边界 | 边界场景 | 处理策略 | |---------|---------| | 浏览器禁用 localStorage | 持久化失效,仅当前会话有效 | | 用户清空浏览器数据 | 所有持久化数据丢失 | | 多设备登录同一账号 | localStorage 仅本机生效 | | Dify 升级改变 action 结构 | 兼容旧字段解析(向后兼容) | | 企微 webhook 推送频率异常 | 60s 轮询兜底 | --- ## 九、关联文档 - **PRD(冻结)**:`docs/01-产品文档/05-用户端H5/PRD-REQ-用户-006-智能推荐重构-v1.0-Frozen.md` - **技术方案**:`docs/02-技术文档/技术架构/技术方案-REQ-用户-006-智能推荐重构-v1.0.md` - **任务说明书**:`docs/07-项目管理/任务说明书/任务说明书-REQ-用户-006-智能推荐重构.md` - **原型图**:`docs/01-产品文档/05-用户端H5/原型-REQ-用户-006-智能推荐重构-v1.0.html` - **现有代码**: - `src/backend/tests/services/test_asset_recommend.py`(升级为 v2) - `src/backend/tests/services/test_recommend_progress.py`(新增) - `src/backend/tests/services/test_topic_detector.py`(新增) - `src/frontend-h5/tests/stores/recommendStore.test.ts`(新增) --- ## 十、变更日志 | 版本 | 日期 | 变更内容 | 作者 | |------|------|---------|------| | v1.0 | 2026-07-28 19:31 | 初版:76 条用例覆盖 4 类触发源 + 3 种时机 + 多源合并 + 冷启动 + 降级 + 话题切换 + 持久化 + E2E;100% 覆盖 PRD §4.7 5 个决策 | Duckula + 宋献 |