diff --git a/order/src/main/java/com/baiyue/order/service/MerchantBatchImportService.java b/order/src/main/java/com/baiyue/order/service/MerchantBatchImportService.java index b8cb031e9..473289d63 100644 --- a/order/src/main/java/com/baiyue/order/service/MerchantBatchImportService.java +++ b/order/src/main/java/com/baiyue/order/service/MerchantBatchImportService.java @@ -34,6 +34,7 @@ import java.util.function.Function; @Service public class MerchantBatchImportService { private static final int MAX_ROWS = 50; + private static final int WAREHOUSE_PAGE_SIZE = 200; private static final long MAX_BYTES = 5L * 1024 * 1024; @Resource private MerchantBatchDraftService drafts; @Resource private OrderBatchFileClient files; @@ -216,14 +217,7 @@ public class MerchantBatchImportService { private Catalog catalog(long tenantId, String authorization) { if (!StringUtils.hasText(authorization)) throw new BizException(ResultCode.UNAUTHORIZED, "登录凭证缺失"); try { - var warehouseResponse = warehouses.page(authorization, 1, 200, "ACTIVE"); - if (warehouseResponse == null || !warehouseResponse.successful() || warehouseResponse.data() == null) { - throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项暂不可用"); - } - List allWarehouses = warehouseResponse.data().records(); - if (warehouseResponse.data().pages() > 1) { - throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "可用仓库超过模板支持范围,请联系平台处理"); - } + List allWarehouses = activeWarehouses(authorization); var receiving = optionMap(allWarehouses.stream().filter(value -> "DOMESTIC".equals(value.warehouseType())).toList(), value -> label(value.warehouseName(), value.warehouseCode())); if (receiving.isEmpty()) throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "暂无可用国内收货仓"); @@ -255,6 +249,57 @@ public class MerchantBatchImportService { } } + private List activeWarehouses(String authorization) { + Map unique = new LinkedHashMap<>(); + Long expectedTotal = null; + Long expectedPages = null; + long fetchedRows = 0; + for (long requestedPage = 1; ; requestedPage++) { + var response = warehouses.page(authorization, requestedPage, WAREHOUSE_PAGE_SIZE, "ACTIVE"); + if (response == null || !response.successful() || response.data() == null) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项第" + requestedPage + "页暂不可用"); + } + var page = response.data(); + if (page.page() != requestedPage || page.size() != WAREHOUSE_PAGE_SIZE) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页响应异常"); + } + long calculatedPages = page.total() == 0 ? 0 : ((page.total() - 1) / page.size()) + 1; + if (page.pages() != calculatedPages) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页统计异常"); + } + if (expectedTotal == null) { + expectedTotal = page.total(); + expectedPages = page.pages(); + } else if (expectedTotal != page.total() || expectedPages != page.pages()) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页数据发生变化,请重试"); + } + if (requestedPage > Math.max(1L, expectedPages)) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页响应异常"); + } + List records = page.records(); + if (expectedPages > 0 && records.isEmpty()) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页数据不完整"); + } + fetchedRows += records.size(); + for (var warehouse : records) { + if (warehouse == null || warehouse.id() == null || warehouse.id() <= 0 + || !"ACTIVE".equals(warehouse.status())) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项数据异常"); + } + var previous = unique.putIfAbsent(warehouse.id(), warehouse); + if (previous != null && !previous.equals(warehouse)) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页数据发生变化,请重试"); + } + } + if (requestedPage >= expectedPages) { + if (fetchedRows != expectedTotal) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "仓库选项分页数据不完整"); + } + return List.copyOf(unique.values()); + } + } + } + private static Map addressOptions(List values) { return optionMap(values, value -> String.join("|", value.addressName(), value.contactName(), value.city(), value.addressLine())); } diff --git a/order/src/test/java/com/baiyue/order/service/MerchantBatchImportServiceTest.java b/order/src/test/java/com/baiyue/order/service/MerchantBatchImportServiceTest.java index cb5971871..e7d236377 100644 --- a/order/src/test/java/com/baiyue/order/service/MerchantBatchImportServiceTest.java +++ b/order/src/test/java/com/baiyue/order/service/MerchantBatchImportServiceTest.java @@ -13,10 +13,13 @@ import org.springframework.test.util.ReflectionTestUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class MerchantBatchImportServiceTest { @@ -86,6 +89,52 @@ class MerchantBatchImportServiceTest { } } + @Test + void templateReadsEveryActiveWarehousePageWithoutApplyingBusinessLimit() throws Exception { + List firstPage = new ArrayList<>(); + for (long id = 1000; id < 1200; id++) { + firstPage.add(new MerchantWarehouseCatalogClient.Warehouse(id, "US-" + id, "海外仓" + id, + "OVERSEAS", "US", "ACTIVE")); + } + var lastWarehouse = new MerchantWarehouseCatalogClient.Warehouse(21L, "CN-YW", "义乌收货仓", + "DOMESTIC", "CN", "ACTIVE"); + when(warehouses.page("Bearer test", 1, 200, "ACTIVE")) + .thenReturn(ApiResponse.success(new PageResult<>(firstPage, 1, 200, 201, 2))); + when(warehouses.page("Bearer test", 2, 200, "ACTIVE")) + .thenReturn(ApiResponse.success(new PageResult<>(List.of(lastWarehouse), 2, 200, 201, 2))); + + byte[] content = service.template(88L, "Bearer test"); + + try (var workbook = new XSSFWorkbook(new ByteArrayInputStream(content))) { + var options = workbook.getSheet("选项数据"); + assertThat(hasCellValue(options, "海外仓1000|US-1000")).isTrue(); + assertThat(hasCellValue(options, "海外仓1199|US-1199")).isTrue(); + assertThat(hasCellValue(options, "义乌收货仓|CN-YW")).isTrue(); + } + verify(warehouses).page("Bearer test", 2, 200, "ACTIVE"); + } + + @Test + void templateFailsClosedWhenLaterWarehousePageCannotBeRead() { + List firstPage = new ArrayList<>(); + for (long id = 1000; id < 1200; id++) { + firstPage.add(new MerchantWarehouseCatalogClient.Warehouse(id, "US-" + id, "海外仓" + id, + "OVERSEAS", "US", "ACTIVE")); + } + when(warehouses.page("Bearer test", 1, 200, "ACTIVE")) + .thenReturn(ApiResponse.success(new PageResult<>(firstPage, 1, 200, 201, 2))); + when(warehouses.page("Bearer test", 2, 200, "ACTIVE")).thenReturn(null); + + assertThatThrownBy(() -> service.template(88L, "Bearer test")) + .isInstanceOf(com.baiyue.common.core.exception.BizException.class) + .hasMessage("仓库选项第2页暂不可用"); + } + + private static boolean hasCellValue(org.apache.poi.ss.usermodel.Sheet sheet, String expected) { + for (var row : sheet) for (var cell : row) if (expected.equals(cell.getStringCellValue())) return true; + return false; + } + private static CustomerAddressDtos.Response address(long id, String name, String type) { return new CustomerAddressDtos.Response(id, type, name, "测试公司", "张三", "13800000000", null, "CN", "广东", "深圳", "南山", "科技园1号", "518000", true);