跳转至

ISSUE-0075:状态列「待采集」与 HTTP 列「✓已采」视觉矛盾

现象

用户截图(v0.10.112)官网列表去重视图,多行显示:

状态 HTTP 网址
待采集 ✓ 已采 (tooltip: pool-hit emails=0 phones=27) https://bbahc.org/Aleknagik
待采集 ✓ 已采 https://www.genevawoodsbird...
待采集 ✓ 已采 https://www.unitedpt.com/

用户问"为什么后边是已采,前边还是待采?" — 视觉矛盾。

根因:两列看不同维度

数据源 维度
状态 row.scrape_status 字段 当前 row(MapTaskData 单行)
HTTP websiteLogMap.get(row.website) URL 维度(任意 row 的 page-log)

场景:连锁店共享同 URL(westernunion / bbahc 等): - 第 1 个商家 row 命中该 URL → engine 处理 → 写 emails + scrape_status=2 + page-log mstage:success - 第 2 个商家 row 也指向该 URL,仍在 queue 没轮到 → scrape_status=0 - 官网去重视图按 URL group 取第一行作为代表,碰巧拿到了 status=0 那行 - UI 显示:状态列看 row 自己 = 待采集;HTTP 列看 URL 历史 = ✓已采

代码里的「查重复用」(scraper-executor.ts:152-178)能避免重复抓,但只在 engine pick 到该 row 时才执行 —— 排队中的行 UI 仍是 status=0。

修复(v0.10.114)

A:engine 启动时批量复用(治本)

新函数 batchDedupeByUrl()engine-manager.ts

  1. 拉所有 status=2 行 → 建 url → source row 索引(emails 非空优先)
  2. 拉所有 status=0 行 → 按 source row 分组
  3. 对每组 updateByQuery(id IN [...], { emails, socials, scrape_status:2, sync:0 })

调用时机: - 每个 SW session 跑一次(dedupeRanThisSession 模块标记) - 用户「立即触发」时 markDedupeNeedsRerun() 重置标记,下次 manageQueue 再跑

收益: - 视觉矛盾消除(同 URL 多行一起变 status=2) - 节省 worker — 不再重复跑相同 URL(你的 335 商家、238 URL 意味着 ~100 行直接秒标)

B:官网去重视图代表行优先 status=2(兜底)

data-view.tsx 官网 useMemo:

const score = (r) => (emails?.length>0 ? 2 : 0) + (scrape_status===2 ? 1 : 0);
const byUrl = new Map();
for (const r of rows) {
  const k = r.website.toLowerCase();
  if (!byUrl.has(k) || score(r) > score(byUrl.get(k))) byUrl.set(k, r);
}

即便 A 还没跑完,UI 也会优先展示已采过的 row 作代表,状态列直接显示「已完成」。

设计教训

  • UI 列必须维度一致 — row 字段 + URL 维度信息混在一行,必然出现矛盾
  • engine pick 节奏不等于数据可见节奏 — 处理完毕的成果应该立即扩散到同维度的其他行
  • 类似坑:分组时取"第一个"作代表是隐性 bug — 应该按"代表性 score"选

验证

  • 装 v0.10.114 后第一次开 engine:
  • sysLog 会出现 pipeline batch-dedupe-applied { urls: N, rows: M }
  • 官网去重视图所有同 URL 行立即变 "已完成"
  • 点「立即触发」:再跑一次,覆盖刚新建任务的同 URL 行