From 11d1951c7f838aa4207787cff39e67f03374ab88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Wed, 9 Sep 2026 10:26:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(collect-data):=20=E5=93=81=E7=89=8C?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E6=9F=A5=E8=AF=A2=E5=A4=B1=E8=B4=A5=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=86=99=E5=85=A5=E6=97=A0=E6=95=88=E5=93=81=E7=89=8C?= =?UTF-8?q?=E8=A1=A8=EF=BC=8C=E9=81=BF=E5=85=8D=E6=95=85=E9=9A=9C=E6=9C=9F?= =?UTF-8?q?=E8=AF=AF=E6=9D=80=E6=B0=B8=E4=B9=85=E6=8B=89=E9=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 线上 task-27265 采集结果 0 行的根因:.env 缺 export 导致 Java 请求 16890 不带 X-Token 全线 422 -> 整批 queryFailed,而 filterByBrandCheck 把 queryFailed 与 rejected 一并写入 biz_invalid_asin_data,故障品牌被 永久拉黑,后续任务同品牌商品全部被 invalidFiltered 误杀(9-01 起所有 采集任务均 0 行)。改为仅真正判定无效的 rejected 才写表,rejected 为空 不触发写入;新增 2 个单测固化契约。 --- .../service/CollectDataService.java | 13 ++++++---- .../CollectDataServiceTxBoundaryTest.java | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/collectdata/service/CollectDataService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/collectdata/service/CollectDataService.java index bc4b8ef9..4f8e7086 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/collectdata/service/CollectDataService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/collectdata/service/CollectDataService.java @@ -803,10 +803,15 @@ public class CollectDataService { CollectDataBrandBatchFilter.BrandBatchOutcome outcome = brandBatchFilter.filter(rows); stats.brandRejectedCount += outcome.rejected().size(); stats.brandQueryFailedCount += outcome.queryFailed().size(); - List invalidRows = new ArrayList<>(outcome.rejected().size() + outcome.queryFailed().size()); - invalidRows.addAll(outcome.rejected()); - invalidRows.addAll(outcome.queryFailed()); - invalidAsinBatchWriter.writeBatch(invalidRows); + // 只有被服务端判定为无效品牌(rejected)的行才写入无效品牌表; + // queryFailed 是品牌检测服务端到端失败(如缺 X-Token 422/超时/限流), + // 一并写入会把故障期间被误伤的品牌永久拉黑,后续同品牌商品全部被 + // invalidFiltered 过滤(task-27265 实测:通过 Python 过滤的 21 行中 + // 8 行查询失败被写表后误杀,最终结果 Excel 0 行)。rejected 为空时 + // 不触发任何写入,避免空批次无意义调用。 + if (!outcome.rejected().isEmpty()) { + invalidAsinBatchWriter.writeBatch(outcome.rejected()); + } return outcome.accepted(); } diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/collectdata/service/CollectDataServiceTxBoundaryTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/collectdata/service/CollectDataServiceTxBoundaryTest.java index 3af94eda..f83113d3 100644 --- a/backend-java/src/test/java/com/nanri/aiimage/modules/collectdata/service/CollectDataServiceTxBoundaryTest.java +++ b/backend-java/src/test/java/com/nanri/aiimage/modules/collectdata/service/CollectDataServiceTxBoundaryTest.java @@ -208,6 +208,30 @@ class CollectDataServiceTxBoundaryTest { verify(invalidAsinBatchWriter).writeBatch(any()); } + @Test + void brandQueryFailedRowsNotWrittenToInvalidWriter() { + CollectDataResultRowVo queryFailed = new CollectDataResultRowVo(); + queryFailed.setAsin("B0QUERYFAIL"); + when(brandBatchFilter.filter(any())).thenReturn( + new CollectDataBrandBatchFilter.BrandBatchOutcome(List.of(), List.of(queryFailed), List.of())); + + service.submitResult(TASK_ID, submitRequest()); + + verify(invalidAsinBatchWriter, never()).writeBatch(any()); + } + + @Test + void emptyRejectedOutcomeSkipsInvalidWriter() { + CollectDataResultRowVo accepted = new CollectDataResultRowVo(); + accepted.setAsin("B0OK"); + when(brandBatchFilter.filter(any())).thenReturn( + new CollectDataBrandBatchFilter.BrandBatchOutcome(List.of(), List.of(), List.of(accepted))); + + service.submitResult(TASK_ID, submitRequest()); + + verify(invalidAsinBatchWriter, never()).writeBatch(any()); + } + @Test void rustfsFailureAbortsWithoutPartialChunk() { when(transientPayloadStorageService.extractPointer(anyString())).thenReturn("local:/tmp/x");