From 3d208ea0e5fe768804211b645a86394a824752c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sun, 13 Sep 2026 15:16:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E4=BB=BB=E5=8A=A1=E4=B8=AD=E6=96=AD):=20?= =?UTF-8?q?=E6=A0=87=E5=A4=B1=E8=B4=A5=E6=97=B6=E5=90=8C=E6=AD=A5=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=BC=93=E5=AD=98=E4=B8=8E=E8=BF=9B=E5=BA=A6=E5=BF=AB?= =?UTF-8?q?=E7=85=A7=EF=BC=8C=E4=BF=AE"=E5=A4=84=E7=90=86=E4=B8=AD"?= =?UTF-8?q?=E5=8D=A1=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 客户端重启上报中断只改了 file_task,模块缓存/进度快照仍是 RUNNING, 导致 progress/batch 继续回报 RUNNING:前端任务面板永远"处理中"并阻塞该工具后续任务 (2026-09-13 真机:店铺数据采集 28094 history=FAILED 但 progress=RUNNING,界面卡住)。 markInterrupted 现在同时刷新模块缓存(saveFileTaskCache)与进度快照(新增 markTerminal)。 --- .../task/service/TaskHeartbeatService.java | 13 ++++++++++ .../service/TaskProgressSnapshotService.java | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskHeartbeatService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskHeartbeatService.java index 70f17c8c..debe652b 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskHeartbeatService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskHeartbeatService.java @@ -54,6 +54,7 @@ public class TaskHeartbeatService { private static final String MODULE_BRAND = "BRAND"; private final FileTaskMapper fileTaskMapper; + private final TaskProgressSnapshotService taskProgressSnapshotService; private final BrandCrawlTaskMapper brandCrawlTaskMapper; private final ProductRiskTaskCacheService productRiskTaskCacheService; private final PublishTaskService publishTaskService; @@ -142,6 +143,18 @@ public class TaskHeartbeatService { if (updated > 0) { log.warn("[task-interrupted] file task marked failed by client restart taskId={} moduleType={} reason={}", taskId, fileTask.getModuleType(), safeReason); + // 同步模块缓存与进度快照:progress/batch 读缓存/快照,不刷新会让前端 + // 一直按 RUNNING 渲染(任务面板永远"处理中"并阻塞该工具后续任务)。 + try { + fileTask.setStatus("FAILED"); + fileTask.setErrorMessage(safeReason); + fileTask.setFinishedAt(LocalDateTime.now()); + saveFileTaskCache(fileTask.getModuleType(), fileTask); + taskProgressSnapshotService.markTerminal(taskId, fileTask.getModuleType(), "FAILED", safeReason); + } catch (Exception cacheEx) { + log.warn("[task-interrupted] refresh cache/snapshot failed taskId={} err={}", + taskId, cacheEx.getMessage()); + } return TaskHeartbeatVo.notAlive(fileTask.getModuleType(), "FAILED", "marked failed"); } log.info("[task-interrupted] file task not in RUNNING, skipped taskId={} status={}", taskId, status); diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskProgressSnapshotService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskProgressSnapshotService.java index 92fcf07c..959f3a44 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskProgressSnapshotService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/task/service/TaskProgressSnapshotService.java @@ -157,6 +157,31 @@ public class TaskProgressSnapshotService { lastWriteAtMillis.remove(cacheKey(taskId, moduleType)); } + /** + * 任务被外部置为终态(客户端中断上报 / stale 兜底修复)时同步进度快照。 + * 不同步的话,progress/batch 仍按快照里的 RUNNING 回放,前端任务面板永远"处理中" + * 并阻塞该工具的后续任务(2026-09-13 真机:客户端重启后店铺数据采集一直显示 28094 处理中)。 + */ + @Transactional + public void markTerminal(Long taskId, String moduleType, String status, String message) { + if (taskId == null || taskId <= 0 || isBlank(moduleType) || isBlank(status)) { + return; + } + TaskProgressSnapshotEntity existing = find(taskId, moduleType); + if (existing == null) { + return; + } + if (Objects.equals(existing.getStatus(), status) && Objects.equals(existing.getMessage(), message)) { + return; + } + taskProgressSnapshotMapper.update(null, new LambdaUpdateWrapper() + .eq(TaskProgressSnapshotEntity::getId, existing.getId()) + .set(TaskProgressSnapshotEntity::getStatus, status) + .set(TaskProgressSnapshotEntity::getMessage, message) + .set(TaskProgressSnapshotEntity::getUpdatedAt, LocalDateTime.now())); + lastWriteAtMillis.remove(cacheKey(taskId, moduleType)); + } + private String writeJson(Object value) { try { return objectMapper.writeValueAsString(value);