From 6b76333159b7975342fa8303e543d1fafbba4ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 29 Aug 2026 23:46:22 +0800 Subject: [PATCH] =?UTF-8?q?task-39:=20=E5=BA=97=E9=93=BA=20Excel=20?= =?UTF-8?q?=E6=8C=89=E5=9B=BD=E5=AE=B6=E5=A2=9E=E9=87=8F=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=8C=E6=9C=AA=E6=8F=90=E4=BA=A4=E5=9B=BD=E5=AE=B6=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E6=97=A7=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rowsByCountry 由同国行累加改为覆盖语义:按成员顺序累积, 后面的结果覆盖前面同名国家的行(最新任务胜出),本次未提交的 国家从旧累计对象/成员累积中保留,不再被清空。 新增回归测试:两个成员快照(英德法 + 仅德国新行)写入 workbook 后, 英国/法国保留旧行、德国被新任务覆盖、未提交国家保持空表。 --- .../ShopDataCrawlExcelAssemblyService.java | 6 +++- ...ShopDataCrawlExcelAssemblyServiceTest.java | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyService.java index aa025498..e88544d3 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyService.java @@ -368,7 +368,11 @@ public class ShopDataCrawlExcelAssemblyService { if (item == null || Boolean.FALSE.equals(item.getSuccess()) || item.getCountryResults() == null) continue; for (ShopDataCrawlCountryResultDto countryResult : item.getCountryResults()) { String country = countryResult == null || countryResult.getCountry() == null ? "" : countryResult.getCountry().trim().toUpperCase(); - if (result.containsKey(country) && countryResult.getItems() != null) result.get(country).addAll(countryResult.getItems()); + // 同国覆盖:按成员顺序累积,后面的结果覆盖前面的同名国家行(最新任务胜出), + // 本次未提交的国家由调用方从旧累计对象/模板保留,不在这里清空。 + if (result.containsKey(country) && countryResult.getItems() != null) { + result.put(country, new ArrayList<>(countryResult.getItems())); + } } } return result; diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyServiceTest.java index c0ba34e7..6b65aa4c 100644 --- a/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyServiceTest.java +++ b/backend-java/src/test/java/com/nanri/aiimage/modules/shopdatacrawl/service/ShopDataCrawlExcelAssemblyServiceTest.java @@ -104,6 +104,41 @@ class ShopDataCrawlExcelAssemblyServiceTest { assertEquals(2, total); } + @Test + void writeWorkbookLatestMemberWinsPerCountryOthersPreserved() throws Exception { + // 增量语义:两个成员快照(第一次英德法、第二次只更新德国)写入 workbook 时, + // 德国 sheet 以第二个成员(新任务)的行走覆盖,英国/法国保留第一个成员的行。 + ShopDataCrawlRowDto ukRow = row("2026-07-25", "B000000001"); + ShopDataCrawlRowDto frRow = row("2026-07-26", "B000000002"); + ShopDataCrawlRowDto deOldRow = row("2026-07-27", "B000000003"); + ShopDataCrawlRowDto deNewRow = row("2026-07-28", "B000000004"); + + SimilarAsinImageEmbedder imageEmbedder = mock(SimilarAsinImageEmbedder.class); + when(imageEmbedder.fetchAndResizeForCache(ukRow.getCommodityImage())) + .thenReturn(new SimilarAsinImageEmbedder.ResizedImage(jpegBytes(), 2, 2)); + when(imageEmbedder.fetchAndResizeForCache(frRow.getCommodityImage())) + .thenReturn(new SimilarAsinImageEmbedder.ResizedImage(jpegBytes(), 2, 2)); + when(imageEmbedder.fetchAndResizeForCache(deOldRow.getCommodityImage())) + .thenReturn(new SimilarAsinImageEmbedder.ResizedImage(jpegBytes(), 2, 2)); + ShopDataCrawlExcelAssemblyService service = new ShopDataCrawlExcelAssemblyService(imageEmbedder); + File output = tempDir.resolve("incremental.xlsx").toFile(); + + // 第一个成员:英德法三国有行;第二个成员:只带德国新行 + service.writeWorkbook(output, List.of( + item("UK", ukRow), item("DE", deOldRow), item("FR", frRow), + item("DE", deNewRow))); + + try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(output))) { + assertEquals("B000000001", workbook.getSheet("英国").getRow(1).getCell(1).getStringCellValue(), "英国保留旧行"); + assertEquals("B000000002", workbook.getSheet("法国").getRow(1).getCell(1).getStringCellValue(), "法国保留旧行"); + assertEquals(1, workbook.getSheet("英国").getLastRowNum(), "英国 sheet 只有一行旧数据"); + assertEquals(1, workbook.getSheet("法国").getLastRowNum(), "法国 sheet 只有一行旧数据"); + assertEquals(1, workbook.getSheet("德国").getLastRowNum(), "德国 sheet 被新任务覆盖为一行"); + assertEquals("B000000004", workbook.getSheet("德国").getRow(1).getCell(1).getStringCellValue(), "德国显示新任务的行"); + assertEquals(0, workbook.getSheet("西班牙").getLastRowNum(), "从未提交的国家保持空表"); + } + } + private ShopDataCrawlResultItemVo item(String countryCode, ShopDataCrawlRowDto row) { ShopDataCrawlCountryResultDto country = new ShopDataCrawlCountryResultDto(); country.setCountry(countryCode);