diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeRunService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeRunService.java index 946d945d..1c7669ec 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeRunService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/dedupe/service/DedupeRunService.java @@ -47,6 +47,7 @@ public class DedupeRunService { private static final String HEADER_SPLIT_MARKER = "idASIN国家状态价格变体数量"; private static final String HEADER_STOP_COLUMN = "缩略图地址8"; + private static final List EXPORT_COLUMN_PRIORITY = List.of("id", "ASIN", "国家", "价格", "品牌"); private final FileTaskMapper fileTaskMapper; private final FileResultMapper fileResultMapper; @@ -254,6 +255,7 @@ public class DedupeRunService { private void cleanExcelByLegacyRules(File inputFile, File outputFile, List selectedColumns, boolean keepIntegerIds, boolean keepUnderscoreIds, boolean keepIntegerMainIdsWhenNoSubIds) throws Exception { + selectedColumns = reorderExportColumns(selectedColumns); long readStartNs = System.nanoTime(); DedupeReadResult readResult = readDedupeRows( inputFile, @@ -310,13 +312,14 @@ public class DedupeRunService { } long writeNs = elapsedNs(writeStartNs); log.info( - "dedupe clean stages file={} scannedRows={} keptRows={} uniqueAsins={} matchedAsins={} outputRows={} readFilterMs={} dbMs={} writeMs={} totalCleanMs={}", + "dedupe clean stages file={} scannedRows={} keptRows={} uniqueAsins={} matchedAsins={} outputRows={} filteredFbaRows={} readFilterMs={} dbMs={} writeMs={} totalCleanMs={}", inputFile.getName(), readResult.scannedRows(), readResult.rows().size(), candidateAsinValues.size(), matchedAsinValues.size(), outputRows, + readResult.filteredFbaRows(), toMs(readNs), toMs(dbNs), toMs(writeNs), @@ -324,6 +327,24 @@ public class DedupeRunService { ); } + private List reorderExportColumns(List selectedColumns) { + if (selectedColumns == null || selectedColumns.isEmpty()) { + return selectedColumns; + } + List ordered = new ArrayList<>(selectedColumns.size() + EXPORT_COLUMN_PRIORITY.size()); + for (String priority : EXPORT_COLUMN_PRIORITY) { + if (selectedColumns.contains(priority)) { + ordered.add(priority); + } + } + for (String column : selectedColumns) { + if (!ordered.contains(column)) { + ordered.add(column); + } + } + return ordered; + } + private DedupeReadResult readDedupeRows(File inputFile, List selectedColumns, boolean keepIntegerIds, boolean keepUnderscoreIds, boolean keepIntegerMainIdsWhenNoSubIds) throws Exception { @@ -356,14 +377,23 @@ public class DedupeRunService { } Integer idColumnIndex = headerMap.get("id"); Integer asinColumnIndex = headerMap.get("ASIN"); + Integer shippingTypeColumnIndex = headerMap.get("发货类型"); int scannedRows = 0; + int filteredFbaRows = 0; for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { Row row = sheet.getRow(rowNum); if (row == null) { continue; } scannedRows++; + if (shippingTypeColumnIndex != null) { + String shippingType = normalizeCellText(formatter.formatCellValue(row.getCell(shippingTypeColumnIndex))); + if ("FBA".equalsIgnoreCase(shippingType)) { + filteredFbaRows++; + continue; + } + } if (idColumnIndex == null) { rows.add(buildCandidateRow(row, selectedIndexes, asinColumnIndex, formatter)); continue; @@ -383,7 +413,7 @@ public class DedupeRunService { } pendingMainIdGroup.flush(rows); - return new DedupeReadResult(sheet.getSheetName(), rows, scannedRows); + return new DedupeReadResult(sheet.getSheetName(), rows, scannedRows, filteredFbaRows); } } @@ -641,7 +671,7 @@ public class DedupeRunService { return nanos / 1_000_000L; } - private record DedupeReadResult(String sheetName, List rows, int scannedRows) { + private record DedupeReadResult(String sheetName, List rows, int scannedRows, int filteredFbaRows) { } private record DedupeCandidateRow(List selectedValues, String asinValue) { diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinResolveService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinResolveService.java index 42406332..c1b4e181 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinResolveService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/queryasin/service/QueryAsinResolveService.java @@ -24,6 +24,7 @@ import org.springframework.transaction.support.TransactionTemplate; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -136,16 +137,33 @@ public class QueryAsinResolveService { if (ordered.isEmpty()) { throw new BusinessException("shop_names 无有效店铺名"); } - List allAsins = queryAsinMapper.selectList( - new LambdaQueryWrapper().orderByAsc(QueryAsinEntity::getId)); - List preloadedQueryAsins = toCountryAsins(allAsins); + Map> rowsByShop = loadAsinsGroupByShop(new ArrayList<>(ordered)); ProductRiskMatchShopsVo vo = new ProductRiskMatchShopsVo(); for (String shopName : ordered) { - vo.getItems().add(matchOneShop(shopName, preloadedQueryAsins)); + vo.getItems().add(matchOneShop(shopName, + toCountryAsins(rowsByShop.getOrDefault(shopName, List.of())))); } return vo; } + private Map> loadAsinsGroupByShop(List shopNames) { + LambdaQueryWrapper wrapper = shopNames.size() == 1 + ? new LambdaQueryWrapper() + .eq(QueryAsinEntity::getShopName, shopNames.get(0)) + .orderByAsc(QueryAsinEntity::getId) + : new LambdaQueryWrapper() + .in(QueryAsinEntity::getShopName, shopNames) + .orderByAsc(QueryAsinEntity::getId); + Map> rowsByShop = new HashMap<>(); + for (QueryAsinEntity row : queryAsinMapper.selectList(wrapper)) { + if (row.getShopName() == null) { + continue; + } + rowsByShop.computeIfAbsent(row.getShopName(), k -> new ArrayList<>()).add(row); + } + return rowsByShop; + } + public List loadQueryAsinsForShop(Long userId, String shopName) { validateUserId(userId); String normalizedShopName = ziniaoShopSwitchService.normalizeShopName(shopName); @@ -153,7 +171,9 @@ public class QueryAsinResolveService { return List.of(); } List rows = queryAsinMapper.selectList( - new LambdaQueryWrapper().orderByAsc(QueryAsinEntity::getId)); + new LambdaQueryWrapper() + .eq(QueryAsinEntity::getShopName, normalizedShopName) + .orderByAsc(QueryAsinEntity::getId)); return toCountryAsins(rows); } diff --git a/frontend-vue/src/pages/brand/components/BrandQueryAsinTab.vue b/frontend-vue/src/pages/brand/components/BrandQueryAsinTab.vue index 23459c62..8d3deb1f 100644 --- a/frontend-vue/src/pages/brand/components/BrandQueryAsinTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandQueryAsinTab.vue @@ -548,8 +548,7 @@ function fallbackQueryAsinsForShop(shopName?: string) { ...matchedItems.value, ]; const exact = candidates.find((item) => (item?.shopName || "").trim() === normalizedShopName && hasQueryAsins(item)); - const any = candidates.find((item) => hasQueryAsins(item)); - return readQueryAsins(exact || any); + return readQueryAsins(exact); } function withQueryAsinFallback(