fix(采集数据): 品牌检测移出任务锁 + 失败任务仍产出可下载的部分结果
taskId 28599 事故根因:分片回传在任务锁内同步做品牌检测,上游 16890 卡死时 单次持锁 103.5 秒(costMs=103556),期间该任务的心跳与客户端重试全部撞 40902「任务正在处理」,客户端 5 次重试预算耗尽后中止了整个采集。 - submitResult:行归一化 / 去重过滤 / 品牌检测移到任务锁外,锁内只保留落库与终态 - BrandCheckClient:整批加 90 秒总耗时上限(超时按查询失败降级,首轮始终执行), 单次回传时延从「无上界」收敛到约 90 秒 + 一次读超时 - 失败且已收到分片时照常组装部分结果;任务已 FAILED 则保留失败态与真实失败原因, 只补结果文件(乐观写结果行 + 条件更新不匹配再补偿写回,避免崩溃时留下「成功但无下载」) - productrisk / shopmatch 按 pricetrack 的 preserveFailure 样板补齐,含组装落地时 不再把失败行「洗成成功」 - 前端:采集 / 跟价 / 产品风险 / 店铺匹配的下载按钮放宽到失败任务
This commit is contained in:
@@ -596,7 +596,9 @@ function taskKeywordProgress(item: CollectDataHistoryItem) {
|
||||
|
||||
function canDownload(item: CollectDataHistoryItem) {
|
||||
const status = normalizeTaskStatus(item)
|
||||
return Boolean(item.downloadUrl && (item.success || status === 'SUCCESS'))
|
||||
// 失败任务只要已生成结果文件也允许下载:失败时后端按已收到的分片组装部分结果,
|
||||
// 已采集的数据不应随任务失败一起不可用(taskId 28599 事故)。
|
||||
return Boolean(item.downloadUrl && (item.success || status === 'SUCCESS' || status === 'FAILED'))
|
||||
}
|
||||
|
||||
async function downloadResult(item: CollectDataHistoryItem) {
|
||||
|
||||
@@ -1658,7 +1658,8 @@ function statusClass(item: PriceTrackHistoryItem) {
|
||||
function canDownload(item: PriceTrackHistoryItem) {
|
||||
if (!item.resultId || (!item.fileReady && !item.downloadUrl)) return false
|
||||
const st = resolvedTaskStatus(item)
|
||||
return st === 'SUCCESS' || item.success === true
|
||||
// 失败任务也可能有已跑出来的部分结果文件(后端按已收分片组装),有文件就允许下载
|
||||
return st === 'SUCCESS' || item.success === true || st === 'FAILED'
|
||||
}
|
||||
|
||||
async function downloadResult(item: PriceTrackHistoryItem) {
|
||||
|
||||
@@ -1018,7 +1018,8 @@ function statusClass(item: ProductRiskHistoryItem) {
|
||||
function canDownload(item: ProductRiskHistoryItem) {
|
||||
if (!item.resultId || (!item.fileReady && !item.downloadUrl)) return false
|
||||
const st = resolvedTaskStatus(item)
|
||||
return st === 'SUCCESS' || item.success === true
|
||||
// 失败任务也可能有已跑出来的部分结果文件(后端按已收分片组装),有文件就允许下载
|
||||
return st === 'SUCCESS' || item.success === true || st === 'FAILED'
|
||||
}
|
||||
|
||||
async function downloadResult(item: ProductRiskHistoryItem) {
|
||||
|
||||
@@ -531,7 +531,8 @@ function currentTaskStageText(item: ShopMatchHistoryItem) { const snapshot = tas
|
||||
function nextScheduledDisplay(item: ShopMatchHistoryItem) { const snapshot = taskSnapshotOf(item); const task = snapshot?.task; const stages = task?.scheduleStages || []; if (typeof task?.currentStageIndex === 'number') { const stage = stages.find((entry) => entry.stageIndex === task.currentStageIndex); if (stage?.scheduledAt) return formatMonthDayTime(stage.scheduledAt) } if (item.scheduledAt) return formatMonthDayTime(item.scheduledAt); return '' }
|
||||
function statusText(item: ShopMatchHistoryItem) { const status = resolvedTaskStatus(item); if (status === 'SCHEDULED') return '待执行'; if (status === 'RUNNING') return '执行中'; if (status === 'SUCCESS' || status === 'COMPLETED') return '已完成'; if (status === 'FAILED') return '失败'; return item.success ? '已完成' : '未知' }
|
||||
function statusClass(item: ShopMatchHistoryItem) { const status = resolvedTaskStatus(item); return status === 'SUCCESS' || status === 'COMPLETED' ? 'success' : status === 'FAILED' ? 'failed' : 'running' }
|
||||
function canDownload(item: ShopMatchHistoryItem) { const status = resolvedTaskStatus(item); return !!item.resultId && (!!item.fileReady || !!item.downloadUrl) && (status === 'SUCCESS' || status === 'COMPLETED') }
|
||||
// 失败任务也可能有已跑出来的部分结果文件(后端按已收分片组装),有文件就允许下载
|
||||
function canDownload(item: ShopMatchHistoryItem) { const status = resolvedTaskStatus(item); return !!item.resultId && (!!item.fileReady || !!item.downloadUrl) && (status === 'SUCCESS' || status === 'COMPLETED' || status === 'FAILED') }
|
||||
async function downloadResult(item: ShopMatchHistoryItem) { if (!item.resultId) return; const url = getShopMatchResultDownloadUrl(item.resultId); const filename = item.outputFilename || `${item.shopName || 'result'}.xlsx`; const result = await saveUrlWithProgress(url, filename, `shop-match:${item.resultId}`); if (result.success) ElMessage.success(`已保存: ${result.path || filename}`); else if (result.error && result.error !== '用户取消') ElMessage.error(result.error) }
|
||||
async function deleteTaskRecord(item: ShopMatchHistoryItem) {
|
||||
const taskId = normalizeTaskId(item.taskId)
|
||||
|
||||
Reference in New Issue
Block a user