task-84: 优化店铺抓取队列状态合并,消除 historyItems 的线性重复查找

新增纯 TS 合并模块 merge-history-items:以 taskId→行列表 二级索引替代
原 mergeProgress 的 [...map.values()].find() 全表扫描(O(n×m)→O(n+m))。
命中语义与原实现一致(taskId 相同且 resultId 相同或 incoming 无
resultId),命中后删除旧 key 写入新 key,shopName 变化不再留下幽灵
重复条目,同批内后到条目可命中先到新增。BrandShopDataCrawlTab 的
mergeProgress 改用该模块。9 个测试覆盖默认、批量、幂等、空、单元素、
maxItems 限流、非法输入、keyOf 故障零变更与幽灵条目场景。
This commit is contained in:
2026-08-30 22:47:41 +08:00
parent 5f062df3cc
commit 5caca86ad1
3 changed files with 323 additions and 7 deletions
@@ -127,6 +127,7 @@ import { getPywebviewApi } from '@/shared/bridges/pywebview'
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
import { createCategorizedTimers } from '@/shared/utils/categorized-timers'
import { saveUrlWithProgress } from '@/shared/utils/download-progress'
import { mergeHistoryItems } from '@/shared/merge-history-items'
import {
addShopDataCrawlCandidate,
createShopDataCrawlTask,
@@ -357,13 +358,9 @@ function mergeProgress(detail: ShopDataCrawlTaskDetailVo) {
if (!taskId) return
taskSnapshots.value = { ...taskSnapshots.value, [taskId]: detail }
const incoming = (detail.items || []).map((item) => ({ ...item, taskId: item.taskId || taskId, taskStatus: item.taskStatus || detail.task?.status, error: item.error || detail.task?.errorMessage }))
const map = new Map(historyItems.value.map((item) => [historyKey(item), item]))
for (const item of incoming) {
const existing = [...map.values()].find((row) => row.taskId === item.taskId && (row.resultId === item.resultId || !item.resultId))
if (existing) map.set(historyKey(existing), { ...existing, ...item })
else map.set(historyKey(item), item)
}
historyItems.value = [...map.values()]
historyItems.value = mergeHistoryItems(historyItems.value, incoming, {
keyOf: historyKey,
}).items
saveQueueState()
}
function isTaskDetail(row: ShopDataCrawlTaskDetailVo | ShopDataCrawlHistoryItem): row is ShopDataCrawlTaskDetailVo {