# 智能清洗端到端测试用例(smart-clean-test-cases) > 测试方式:浏览器实测(ZCode in-app browser),逐条执行、记录实际结果;发现 bug 即修复并重跑该条 + 相关回归条目。 > 测试账号:admin / admin123;前端 http://localhost:3100;后端 http://localhost:8980(`/js/a`)。 > 每次重启后端后必须:案件管理 → 打开案件(数据测试)→ 再继续。 > > 测试文件: > - **文件 A**:`周海--车辆登记信息.xls`(5.6KB,sheet「机动车信息」模板匹配失败 → AI 判定 → 待人工) > - **文件 B**:`周海--通信18905810269.xls`(92KB,通信记录可匹配模板 → 系统既有清洗) | # | 场景 | 步骤 | 预期 | 结果 | |---|---|---|---|---| | ① | 上传解析 A | 上传页注入文件 A | 解析树出现、sheet「机动车信息」模板匹配失败、「智能」可用 | ✅ / N/A | | ② | 上传解析 B | 上传页注入文件 B | 解析树出现、sheet 匹配模板成功、「智能」可用 | ✅ / N/A | | ③ | 混合上传 | 同批注入 A+B | 树中两文件、状态各自正确 | ✅ / N/A | | ④ | MD5 去重回归 | 再次上传文件 A(不删记录) | 后端跳过解析返回 null(原行为),页面无新树 | ✅ / N/A | | ⑤ | AI 判定推进 | 选 A 点「智能」→ 进度页 | AI 卡判定 0/1→1/1,阶段推进 | ✅ / N/A | | ⑥ | 待人工结论 | 判定完成 | 待人工 1、失败 0、阶段 DONE | ✅ / N/A | | ⑦ | 处理日志 | 查看处理日志 | 「融合清洗已启动」「开始处理:文件 A」两条 | ✅ / N/A | | ⑧ | AI 卡列表布局 | 待人工 ≥4 条时查看 | 卡内 ≤3 条 + 「查看全部 N」抽屉;抽屉含原因说明文案 | ✅ / N/A | | ⑨ | 判定详情 | 抽屉点「详情」 | 弹窗:原因/置信度五维/抽样统计/逐列校验/预览样本/模型草稿 | ✅ / N/A | | ⑩ | AI 重新判定 | 抽屉点「AI 重判」 | 受理成功提示;约几十秒后清单刷新出新结论 | ✅ / N/A | | ⑪ | 既有路径推进 | 选 B 点「智能」 | 无 AI 表 → doClean 推进 → 100% → 完成态 | ✅ / N/A | | ⑫ | 既有入库验证 | 完成后查数据 | 通信记录表有数据 | ✅ / N/A | | ⑬ | 混合批次 | A+B 点「智能」 | B 交既有清洗一次 doClean、A 待人工、批次 DONE | ✅ / N/A | | ⑭ | 混合互不影响 | 验证数据 | B 已入库、A 待人工,互不影响 | ✅ / N/A | | ⑮ | 清洗页有数据(核心 bug) | 进度页点「去人工确认」 | 清洗页树出现、sheet 被选中、预览数据加载 | ✅ / N/A | | ⑯ | 清洗页清单可见 | 看左栏 | 「待人工确认」面板含该表、原因、置信度 | ✅ / N/A | | ⑰ | 清单「处理」定位 | 点「处理」 | 树定位到目标 sheet、选中、预览加载 | ✅ / N/A | | ⑱ | 仅洗当前表 | 配模板规则后「导入数据」 | 头部出现「仅清洗当前表」徽标;提交树只含该 sheet;清洗完成 | ✅ / N/A | | ⑲ | 清单消项不丢 | 清洗后回清洗页 | 该项已消项(SUPERSEDED),其余待人工仍在 | ✅ / N/A | | ⑳ | 清洗页内详情 | 清单点「详情」 | 详情弹窗正常 | ✅ / N/A | | ㉑ | 清洗页内重判 | 清单点「AI 重判」 | 受理成功、清单轮询刷新 | ✅ / N/A | | ㉒ | 上传页行级手动清洗回归 | 上传页行级按钮 | 清洗页正常(原 sessionStorage 路径) | ✅ / N/A | | ㉓ | 上传页整页手动清洗回归 | 上传页「手动清洗」按钮 | 正常跳转清洗页 | ✅ / N/A | | ㉔ | importList 重新清洗回归 | 导入管理「重新清洗」 | reClean=1&fileId 路径正常 | ✅ / N/A | | ㉕ | 超时出口改手动清洗 | 融合页「改为手动清洗」 | 带 batchId → 清洗页按批次重建树 | ✅ / N/A | | ㉖ | Skip 入明细 | 构造 Skip(如删源文件后清洗) | 清单出现 Skip 项、计数与条数一致 | ✅ / N/A | | ㉗ | jobs 案件隔离 | 切换案件调 /aiClean/jobs | 只返回当前案件的 job | ✅ / N/A | | ㉘ | 重跑不重判待人工 | 有待人工时再点「智能」 | 日志显示沿用待人工、无新的模型调用 | ✅ / N/A | | ㉙ | 重判命中既有模板 | 重判后结论变化 | ExistingHit → dispatchExisting 走 doClean | ✅ / N/A | | ㉚ | SSE 断连/刷新恢复 | 进度页刷新 | restoreImportProgress 恢复进度继续 | ✅ / N/A | ## 执行记录 **测试共执行 3 轮混合批次 + 多轮单项验证(浏览器实测)**。后因需求调整(移除人工确认逻辑),与待人工闭环相关的条目(⑧⑨⑩⑮-㉑㉖㉙ 及部分回归)按最终形态重新验证或标记 N/A。 ### 已修复并验证的问题 1. **AI 判定线程丢案件上下文**(AiSmartCleanService):CompletableFuture 派发的判定线程无 ScopedValue 绑定 → 全部判定 400 失败。修复:判定任务包 CaseContextHolder.runWith。✅ 2. **模型调用总时长无上限**(LlmService):Reactor timeout 是片间超时,本地模型慢速流式输出 35 分钟不触发。修复:blockLast 总时长兜底(匹配调用 180s)+ 提示词要求简短推理(判定从 35min+ 降到 ~60s)。✅ 3. **/aiClean/jobs 跨案件越权**:加 caseId 过滤。✅ 4. **Skip 结论计数与明细不符**:Skip 也写入明细。✅ 5. **清洗页树兜底**:sessionStorage 被流程消费后可按 focusKey/batchId 从 /dm/getFiles 重建树(修复「人工确认页无数据」同源问题,超时出口「改为手动清洗」也依赖它)。✅ ### 最终形态(移除人工确认后)验证 - 混合批次(A 车辆登记 + B 通信记录):B 三表走系统清洗成功(272 条),A 判定后进入「无法清洗」;批次 DONE、失败 0。✅ - AI 卡指标更名「无法清洗(表)」、列表纯展示(无任何跳转/操作按钮),展示原因。✅ - 清洗页不再有待人工工作清单面板。✅ - 上传页行级/整页「手动清洗」、importList 重新清洗等旧入口不受影响。✅ - MD5 去重还原后重复上传仍被拦截。✅ ### 统一展示改造(AI 链路并入主卡,浏览器实测 ✅) - 取消独立「AI 识别」卡片:进度环按系统清洗/AI 各自处理的 sheet 数加权合成一个总进度; AI 判定期间步骤条推进到「清洗入库」。 - AI 事件全部并入处理日志:AI 识别完成并入系统清洗、AI 新建表入库中、逐条「AI 无法清洗:表名(原因)」、 阶段完成统计;混合批次实测日志顺序与内容正确。 - 完成态描述带出「另有 N 个表 AI 无法清洗」提示;超时出口「改为手动清洗」与 「查看导入结果」并入主卡展示。 - 实测混合批次(B 系统清洗 272 条 + A 无法清洗 1 条):单卡从 0% 推进到完成态,全程无独立 AI 卡。 ### 待人工确认逻辑(已按需求移除) - 相关代码(清单面板/详情弹窗/重判/resolve 接口/消项钩子/仅清洗当前表模式)已全部移除; git 历史中保留可随时恢复。 - ai_clean_job 仍以 NEED_REVIEW 状态记录「无法清洗」结论(原因可查),仅不再引导人工处理。 --- ## 4 级降级专项验证(2026-10-11) ### 逻辑梳理(已逐行核实代码) **上传(L1 判定)**:`upload.vue` → `/dm/preFileUpload` → `PreDataListener`:每 sheet 建预览表 `raw_{sheetId}`、默认「模板匹配失败」;前 20 行内 `matchTemplate`(模板全字段包含 + 必填字段包含)→ 命中打 `templateId/mainId/funcRegx/lineNo`,「未命中」留给 AI 链路。 **智能清洗(`AiSmartCleanService.run`,按降级顺序串行)**:只接管 `templateId==null` 的 sheet。 | 级别 | 判定 | 落地 | |---|---|---| | 1级 | 上传时已匹配模板 | 静默趟 doClean → 业务表 | | 2级 | AI 三腿匹配(表头指纹 → 归一化打分≥0.90 → LLM top-12 置信≥0.6)命中**既有模板**且 STRONG | 等待 1级 Latch → 收口趟 doClean → 业务表 | | 3级 | 命中 **AI 模板库**(复用已有 ai_t 表,零模型)或完全无候选(按**文件自身表头**建表,全 TEXT)或 WEAK 弱匹配 | `loadViaAiTemplate` 建表/并入 → 流式写数 → ONLINE | | 4级 | 硬失败:需结构归一(合并单元格/多级表头)/ 无可用列绑定 / 置信度不过放行线 / 表头为空 | 只标记 NEED_REVIEW,不写任何数据表,原因进 `need_review_detail` + 处理日志 | - 日志绑定:行级失败/未配模板 → `clean_error_log(fileId+sheetId+batchId)`;每 sheet → `ai_clean_job`;批次 → `ai_clean_batch`。 - 治理结果:AI 表经 `govern_tree` 负数 id 节点 → `/aiClean/data/page` 分流取数(不写 `table_info/table_field`, `AiCleanStore` 有意为之,防被既有匹配器命中)。 - AI 表同表头二次清洗:header_md5 唯一键下**复用**已有模板与动态表(存档规则零模型),数据按 job 并入/可撤销。 ### 测试数据(`ai-electron/frontend/public/__test__/`) | 文件 | 构造 | 预期 | |---|---|---| | C-短信表头同义改写.xlsx | 文件B短信表头改3列同义词、表头在第1行 | 2级:AI 语义命中既有模板 → 出口A | | D-人员信息含噪声列.xlsx | 人员信息+3个噪声列 | 观察:LLM 置信≥0.6→出口A,<0.6→出口B | | E-星际航运清单.xlsx | 全新表头(星际航运) | 修复前4级无候选 → 修复后3级自建表 | | E2/E3-星际航运清单续/三.xlsx | 同表头不同内容 | 3级:指纹直通复用已有 ai_t 表 | | F-合并单元格表头.xlsx | 合并单元格标题+第2行表头 | 4级:需结构归一 | | G-短信必填空值.xlsx | 同义表头+本机号码80%空值 | 4级:置信度不过放行线 | ### 用例与结果(浏览器实测 + 接口断言) | # | 场景 | 预期 | 结果 | |---|---|---|---| | A1 | 文件B上传 | 3 sheet 模板匹配成功(tpl 23/21/1) | ✅ | | A2/A3 | 文件A+C混合上传 | A 匹配失败、C(改表头)匹配失败,各自状态正确 | ✅ | | B1 | 仅文件B智能清洗 | 批次 DONE、matched=3、**aiJudged=0**(守卫:已匹配的绝不插手) | ✅ | | B2 | 清洗日志绑定 | `cleanLog/list` 按 batchId 查,记录含 fileId/sheetId/batchId | ✅ | | C1 | 2级 AI 语义匹配 | job:exit=A、by=LLM、STRONG、置信0.78、refTpl=21;**落库 21条 failNum=0** | ✅(修复后) | | D3 | 完全无候选 | 修复前:NEED_REVIEW「没有合适的模板」(缺陷确认);修复后:3级自建表,动态表 5行×6列(文件自身表头,全 TEXT) | ✅(修复后) | | D2 | 同表头二次清洗 | 修复前:指纹直通仍新建模板 → **撞 uk_ai_clean_tpl_md5 唯一键 FAIL**(缺陷确认);修复后:HEADER_CACHE 复用同模板、**llmCalls=0**、数据并入(5+4=9行) | ✅(修复后) | | E1 | 4级·结构归一 | 文件A/文件F → NEED_REVIEW「该表块需要结构归一…」,处理日志可见,不写任何表 | ✅ | | E2 | 4级·置信度闸门 | 文件G → NEED_REVIEW「置信度未达出口 A 的放行线」(perColErrorRate=0.8/rowYield=0.2) | ✅ | | E3 | 4级落地断言 | job 状态/批次计数/need_review_detail/前端日志三处一致;aiLoad=0 | ✅ | | F1 | templateId 非空零 AI 判定 | 同 B1(aiJudged=0) | ✅ | | F3 | 治理结果链路 | 治理树含 AI 节点(负id,type=ai_table,count 正确);`/aiClean/data/page` 取数带表头行;治理任务重建后由 `GovernService:380` calcAiTree 补挂(代码走查) | ✅ | | 串行 | 按级 1→2→3 串行 | 后端日志「按降级顺序串行清洗 1级表=N → 2级表=M」;单测断言先 doClean(true) 后 doClean(false) 再 LOADING_AI | ✅ | | 噪音 | 无「未配置模板」误报 | 修复前混合批次会为 NEED_REVIEW 表记「未配置模板!」;修复后 0 条 | ✅ | | 按钮 | 进度页无按钮 | 完成态页面无「查看导入结果」(截图目视 + DOM 断言 buttons=[]) | ✅ | ### 本轮修复(已随验证闭环) 1. **出口 A 落库断链(P0)**:AI 命中既有模板的表上传时没有 `line_no`,`StreamingEtlListener:120` 首行 NPE, **数据零落库但批次仍报 DONE**。修复:`ExistingHit` 携带表头行号,`dispatchExisting`/`ExistingTemplateDispatcher` 回填 `lineNo`;复测 21 条全部入库。⚠️ 已知限制:doClean 阶段失败仍不会回写批次计数(与旧手动清洗行为一致)。 2. **完全匹配不到→自建表(需求)**:`judge` 无候选分支改为按文件自身表头建兜底动态表(全 TEXT), `loadViaAiTemplate` 兼容 draft 为空;4级只留给硬失败。needsStructure 提前到无候选判定**之前**(结构问题优先定性)。 3. **同表头指纹复用(P1)**:指纹直通命中 AI 模板库时复用已有模板与动态表(读回存档 `ai_clean_rule`,零模型调用), 不再新建模板撞 `uk_ai_clean_tpl_md5`;0 行失败不删复用表。 4. **按降级顺序串行清洗(需求)**:拆分 1级/2级两趟 doClean,趟间 Latch 排队;首趟静默(不推终态/不关 SSE/不触发治理), 末趟收口;NEED_REVIEW/Skip 表不进任何一趟(顺带消除「未配置模板!」噪音日志)。 5. **进度页移除「查看导入结果」按钮(需求)**:两处入口删除,`handleViewResult` 清理; 「查看错误日志」「改为手动清洗」为条件功能按钮,暂保留。 ### 新增/调整单测 - `SelfHeaderPlanTest`(3 用例):兜底绑定逐列自绑/全 TEXT 计划/空表头转人工。 - `AiSmartCleanServiceTest`:原「合并一次 doClean」断言改为「1级静默趟 → 2级收口趟」时序断言 (`cleansByLevelInOrder`),并断言出口 A 必须回填 lineNo。 - aiclean 模块 76 用例全绿(5 跳过为真连 LLM 的活测试)。 ### 遗留观察 - 出口 A 清洗中 `CallDataCleaner` 对个别列报 FunException(清洗函数 pattern 为 null,列级被清洗器内部消化, failNum=0 数据完整入库)——既有 34 策略的函数配置问题,非本轮引入,建议后续核查对方号码列的清洗函数装配。 - 治理任务全量跑一遍(验证 `truncate govern_tree` 重建后 AI 节点补挂的实测闭环)未执行,由代码走查 `GovernService:380` 与 calcAiTree 幂等性保障;日常治理后可观察树上 AI 表节点确认。 --- ## 完成跳转改造:进度全 SSE 推送,去轮询(2026-10-11 下午) ### 问题 清洗完成后没有跳转到结果统计页。根因:完成判定 `isComplete` 只看 SSE 清洗进度 `progress`, 而**纯 AI/4级批次没有 doClean,SSE 进度永远不会到 100%**(进度环显示的 100% 是另一个合成值); 此前靠「查看导入结果」按钮兜底,按钮移除后即卡死在「任务执行中」。 ### 改造(按需求「不要轮询,全部 SSE 推送」) - **后端**:`AiSmartCleanService` 每判定一张表、每个阶段切换、每张 AI 表入库、批次终态都推送 状态快照(cmd 103,`SeeDataDTO.payload` 携带 `SmartCleanStatusVO`);终态推送顺序为 统计(cmd 101,100%)→ 快照(cmd 103)→ 治理宣告(cmd 102,开启治理时), 保证治理启动出现在全部清洗动作之后。`DmService` 各趟 doClean 全静默(收口消息统一由终态推送发出, 清理副作用照常执行)。 - **前端**:删除 2 秒轮询;cmd 103 驱动进度环/日志/完成判定;打开页面(含刷新恢复) 仅做**一次性**状态同步对齐初始值;SSE 断连且批次未终态时给出「改为手动清洗」出口。 - 修复过程中发现并修复:完成判定/自动跳转 watch 在 setup 早期急切求值 `aiStageFinished`(TDZ)导致页面白屏。 ### 验证(浏览器实测,无轮询) - **纯 AI 批次**(D-人员信息含噪声列):提交后进度页 SSE 推进 → 判定完成约 130s → 页面**未刷新**自动跳转到 `/data/index?tab=files&mode=smart`,列表显示「导入成功 16 条」。✅ - **刷新恢复**:停留在已完成的进度页刷新 → 一次性同步拿到终态 → 自动跳转。✅ - **秒完批次**(C+E3,均为头部指纹直通复用 AI 模板):提交 → 进度页 → 自动跳回统计页。✅ - **指纹复用跨批次**:C 与 G 同表头,G 批次建的 AI 模板被 C 秒级复用(HEADER_CACHE,零模型调用)。✅ - 回归:aiclean 模块 76 个单测全绿(静默趟断言更新为「两趟全静默」)。