测试方式:浏览器实测(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。
上传(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。govern_tree 负数 id 节点 → /aiClean/data/page 分流取数(不写 table_info/table_field,
AiCleanStore 有意为之,防被既有匹配器命中)。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=[]) | ✅ |
line_no,StreamingEtlListener:120 首行 NPE,
数据零落库但批次仍报 DONE。修复:ExistingHit 携带表头行号,dispatchExisting/ExistingTemplateDispatcher
回填 lineNo;复测 21 条全部入库。⚠️ 已知限制:doClean 阶段失败仍不会回写批次计数(与旧手动清洗行为一致)。judge 无候选分支改为按文件自身表头建兜底动态表(全 TEXT),
loadViaAiTemplate 兼容 draft 为空;4级只留给硬失败。needsStructure 提前到无候选判定之前(结构问题优先定性)。ai_clean_rule,零模型调用),
不再新建模板撞 uk_ai_clean_tpl_md5;0 行失败不删复用表。handleViewResult 清理;
「查看错误日志」「改为手动清洗」为条件功能按钮,暂保留。SelfHeaderPlanTest(3 用例):兜底绑定逐列自绑/全 TEXT 计划/空表头转人工。AiSmartCleanServiceTest:原「合并一次 doClean」断言改为「1级静默趟 → 2级收口趟」时序断言
(cleansByLevelInOrder),并断言出口 A 必须回填 lineNo。CallDataCleaner 对个别列报 FunException(清洗函数 pattern 为 null,列级被清洗器内部消化,
failNum=0 数据完整入库)——既有 34 策略的函数配置问题,非本轮引入,建议后续核查对方号码列的清洗函数装配。truncate govern_tree 重建后 AI 节点补挂的实测闭环)未执行,由代码走查
GovernService:380 与 calcAiTree 幂等性保障;日常治理后可观察树上 AI 表节点确认。清洗完成后没有跳转到结果统计页。根因:完成判定 isComplete 只看 SSE 清洗进度 progress,
而纯 AI/4级批次没有 doClean,SSE 进度永远不会到 100%(进度环显示的 100% 是另一个合成值);
此前靠「查看导入结果」按钮兜底,按钮移除后即卡死在「任务执行中」。
AiSmartCleanService 每判定一张表、每个阶段切换、每张 AI 表入库、批次终态都推送
状态快照(cmd 103,SeeDataDTO.payload 携带 SmartCleanStatusVO);终态推送顺序为
统计(cmd 101,100%)→ 快照(cmd 103)→ 治理宣告(cmd 102,开启治理时),
保证治理启动出现在全部清洗动作之后。DmService 各趟 doClean 全静默(收口消息统一由终态推送发出,
清理副作用照常执行)。aiStageFinished(TDZ)导致页面白屏。/data/index?tab=files&mode=smart,列表显示「导入成功 16 条」。✅