diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java index d6f15198..ec5e82e0 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java @@ -653,13 +653,11 @@ public class PriceTrackTaskService { } changed = true; if (payload.getError() != null && !payload.getError().isBlank()) { - markResultFailed(fr, payload.getError()); - priceTrackTaskCacheService.removeShopMergedPayload(taskId, shopKey); + finalizeFailedShop(fr, shopKey, payload, payload.getError()); continue; } if (Boolean.FALSE.equals(payload.getSuccess())) { - markResultFailed(fr, "shop processing failed"); - priceTrackTaskCacheService.removeShopMergedPayload(taskId, shopKey); + finalizeFailedShop(fr, shopKey, payload, "shop processing failed"); continue; } handleSkipAsinDeletionSignals(shopKey, payload); @@ -815,6 +813,39 @@ public class PriceTrackTaskService { updateTaskStatusFromLatestRows(task, latest); } + /** + * 失败店铺的收尾:任务照旧标 FAILED(原因回显给用户),但已经把跑出来的行组装成 + * 部分结果文件随任务交付——否则用户只看到「失败」,却拿不到「哪些 ASIN 实际已被 + * 跟价/改价」的记录(会话掉线这类"跑了前几页才断"的场景尤其需要)。 + * + *
组装出来的行仍是失败态(success=0 + errorMessage),任务状态因此不变,
+ * 只是多了个可下载的文件;只有一行可用数据都没有时才退化成纯失败。
+ */
+ private void finalizeFailedShop(FileResultEntity fr, String shopKey,
+ PriceTrackSubmitResultRequest.ShopResult payload, String errorMessage) {
+ String finalMessage = errorMessage;
+ try {
+ PriceTrackSubmitResultRequest.ShopResult merged = mergeShopPayload(fr.getTaskId(), shopKey, payload);
+ int rows = countPayloadRows(merged);
+ if (rows > 0) {
+ markResultFailed(fr, errorMessage);
+ enqueueResultFileAssembly(fr, shopKey, merged, true);
+ priceTrackTaskCacheService.removeShopMergedPayload(fr.getTaskId(), shopKey);
+ log.info("[price-track] 失败店铺仍产出部分结果 taskId={} shop={} rows={} error={}",
+ fr.getTaskId(), shopKey, rows, errorMessage);
+ return;
+ }
+ log.info("[price-track] 失败店铺无可用行,不产出结果文件 taskId={} shop={} error={}",
+ fr.getTaskId(), shopKey, errorMessage);
+ } catch (Exception ex) {
+ log.warn("[price-track] 失败店铺部分结果排队失败,仅标记失败 taskId={} shop={} msg={}",
+ fr.getTaskId(), shopKey, ex.getMessage(), ex);
+ finalMessage = errorMessage + "(部分结果组装排队失败:" + ex.getMessage() + ")";
+ }
+ markResultFailed(fr, finalMessage);
+ priceTrackTaskCacheService.removeShopMergedPayload(fr.getTaskId(), shopKey);
+ }
+
private void markResultFailed(FileResultEntity fr, String message) {
fr.setSuccess(0);
fr.setErrorMessage(message);
@@ -841,11 +872,18 @@ public class PriceTrackTaskService {
java.io.File workRoot = cn.hutool.core.io.FileUtil.mkdir(
cn.hutool.core.io.FileUtil.file(System.getProperty("java.io.tmpdir"), "price-track-result", String.valueOf(result.getTaskId())));
java.io.File xlsx = cn.hutool.core.io.FileUtil.file(workRoot, stem + ".xlsx");
+ // 失败店铺的部分结果(errorMessage 已写明原因):组装完成时保留失败态,只挂文件。
+ // 否则组装一落地就把行"洗成成功",用户再也看不到「这个店铺其实没跑完」。
+ boolean partialFailure = result.getErrorMessage() != null && !result.getErrorMessage().isBlank();
try {
excelAssemblyService.writeWorkbook(xlsx, countries);
String objectKey = ossStorageService.uploadResultFile(xlsx, MODULE_TYPE);
- result.setSuccess(1);
- result.setErrorMessage(null);
+ if (partialFailure) {
+ result.setSuccess(0);
+ } else {
+ result.setSuccess(1);
+ result.setErrorMessage(null);
+ }
result.setResultFilename(stem + ".xlsx");
result.setResultFileUrl(objectKey);
result.setResultFileSize(xlsx.length());
@@ -885,9 +923,20 @@ public class PriceTrackTaskService {
private void enqueueResultFileAssembly(FileResultEntity result,
String shopKey,
PriceTrackSubmitResultRequest.ShopResult payload) {
+ enqueueResultFileAssembly(result, shopKey, payload, false);
+ }
+
+ /**
+ * @param preserveFailure 失败店铺的部分结果:保留 success=0 + 失败原因,只把文件挂上去,
+ * 任务状态不变(仍 FAILED),用户仍能下载已跑出来的行
+ */
+ private void enqueueResultFileAssembly(FileResultEntity result,
+ String shopKey,
+ PriceTrackSubmitResultRequest.ShopResult payload,
+ boolean preserveFailure) {
applyServerModifyCounts(result.getTaskId(), payload);
taskResultPayloadService.saveLatest(result.getTaskId(), MODULE_TYPE, shopKey, payload);
- markResultFilePending(result, shopKey, payload);
+ markResultFilePending(result, shopKey, payload, preserveFailure);
taskFileJobService.enqueueAssembleResult(result.getTaskId(), MODULE_TYPE, result.getId(), shopKey);
}
@@ -993,15 +1042,24 @@ public class PriceTrackTaskService {
private void markResultFilePending(FileResultEntity result,
String shopKey,
- PriceTrackSubmitResultRequest.ShopResult payload) {
+ PriceTrackSubmitResultRequest.ShopResult payload,
+ boolean preserveFailure) {
Map