From d736fdba1b99952609d3025babccf1ed14afc868 Mon Sep 17 00:00:00 2001 From: wujinliang <2318309077@qq.com> Date: Wed, 26 Aug 2026 18:12:55 +0800 Subject: [PATCH] 1 --- .../service/UnifiedOrderQueryService.java | 36 +++++++++++++++---- .../service/UnifiedOrderQueryServiceTest.java | 21 +++++++++++ .../warehouse/dto/WarehouseRequests.java | 12 +++---- .../dto/WarehouseSummaryResponse.java | 2 +- .../service/WarehouseImportService.java | 2 +- .../service/WarehouseSaveNormalizer.java | 6 ++-- warehouse/src/main/resources/db/warehouse.sql | 2 +- 7 files changed, 63 insertions(+), 18 deletions(-) diff --git a/order/src/main/java/com/baiyue/order/service/UnifiedOrderQueryService.java b/order/src/main/java/com/baiyue/order/service/UnifiedOrderQueryService.java index b50c0d987..ffc055646 100644 --- a/order/src/main/java/com/baiyue/order/service/UnifiedOrderQueryService.java +++ b/order/src/main/java/com/baiyue/order/service/UnifiedOrderQueryService.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.time.LocalDateTime; import java.util.Comparator; import java.util.EnumSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -186,7 +187,7 @@ public class UnifiedOrderQueryService { null, null, null, null, order.getPickupMethod(), order.getTaxMode(), order.getContainerMode(), order.getCustomsInspection(), order.getDestinationType(), order.getAmazonWarehouseCode(), null, null, null, null, netWeightKg(cargo), null, null, order.getExpressCompany(), order.getExpressTrackingNo(), - string(order.getCreatedBy()), skuNos(cargo), totalQuantity(cargo), order.getShipmentId(), order.getReferenceId(), + string(order.getCreatedBy()), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), null, null, null, null, null, order.getCustomerRemark(), latestInternalNote, order.getCreatedAt(), order.getUpdatedAt(), order.getRequestedPickupAt()); @@ -205,7 +206,7 @@ public class UnifiedOrderQueryService { inbound.chargeableWeightKg(), inbound.chargeableVolumeCbm(), inbound.volumeWeightKg(), inbound.actualNetWeightKg() != null ? inbound.actualNetWeightKg() : netWeightKg(cargo), inbound.measureCompletedAt(), inbound.inboundedAt(), inbound.expressCompanyName(), inbound.expressTrackingNo(), - inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), order.getShipmentId(), order.getReferenceId(), measurement(inbound), + inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), measurement(inbound), preload(summary.preload()), loading(summary.loading()), delivery(summary.delivery()), transport(inbound), order.getCustomerRemark(), latestInternalNote, order.getCreatedAt(), order.getUpdatedAt(), inbound.requestedPickupAt()); @@ -231,8 +232,8 @@ public class UnifiedOrderQueryService { inbound.chargeableWeightKg(), inbound.chargeableVolumeCbm(), inbound.volumeWeightKg(), inbound.actualNetWeightKg() != null ? inbound.actualNetWeightKg() : netWeightKg(cargo), inbound.measureCompletedAt(), inbound.inboundedAt(), inbound.expressCompanyName(), inbound.expressTrackingNo(), - inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), order == null ? null : order.getShipmentId(), - order == null ? null : order.getReferenceId(), measurement(inbound), preload(summary.preload()), loading(summary.loading()), + inbound.createdByName(), skuNos(cargo), totalQuantity(cargo), shipmentId(order, cargo), referenceId(order, cargo), + measurement(inbound), preload(summary.preload()), loading(summary.loading()), delivery(summary.delivery()), transport(inbound), order == null ? null : order.getCustomerRemark(), latestInternalNote, inbound.createdAt(), inbound.updatedAt(), inbound.requestedPickupAt()); } @@ -281,6 +282,21 @@ public class UnifiedOrderQueryService { return cargo == null || cargo.skuNos().isEmpty() ? null : cargo.skuNos(); } + private String shipmentId(OrderEntity order, CargoSummary cargo) { + return joinIdentifiers(order == null ? null : order.getShipmentId(), cargo == null ? List.of() : cargo.shipmentIds()); + } + + private String referenceId(OrderEntity order, CargoSummary cargo) { + return joinIdentifiers(order == null ? null : order.getReferenceId(), cargo == null ? List.of() : cargo.referenceIds()); + } + + private String joinIdentifiers(String orderValue, List cargoValues) { + LinkedHashSet values = new LinkedHashSet<>(); + if (StringUtils.hasText(orderValue)) values.add(orderValue.trim()); + cargoValues.stream().filter(StringUtils::hasText).map(String::trim).forEach(values::add); + return values.isEmpty() ? null : String.join(", ", values); + } + private Integer totalQuantity(CargoSummary cargo) { return cargo == null ? null : cargo.totalQuantity(); } @@ -301,11 +317,16 @@ public class UnifiedOrderQueryService { return cargo == null ? null : cargo.volumeCbm(); } - private record CargoSummary(List skuNos, Integer totalQuantity, Integer totalPackageCount, - BigDecimal grossWeightKg, BigDecimal netWeightKg, BigDecimal volumeCbm) { + private record CargoSummary(List skuNos, List shipmentIds, List referenceIds, + Integer totalQuantity, Integer totalPackageCount, BigDecimal grossWeightKg, + BigDecimal netWeightKg, BigDecimal volumeCbm) { private static CargoSummary from(List cargoes) { List skuNos = cargoes.stream().map(OrderCargoEntity::getSkuNo) .filter(Objects::nonNull).map(String::trim).filter(value -> !value.isEmpty()).distinct().toList(); + List shipmentIds = cargoes.stream().map(OrderCargoEntity::getShipmentId) + .filter(StringUtils::hasText).map(String::trim).distinct().toList(); + List referenceIds = cargoes.stream().map(OrderCargoEntity::getReferenceId) + .filter(StringUtils::hasText).map(String::trim).distinct().toList(); int totalQuantity = cargoes.stream().map(OrderCargoEntity::getTotalQuantity) .filter(Objects::nonNull).mapToInt(Integer::intValue).sum(); List packageCounts = cargoes.stream().map(OrderCargoEntity::getPackageCount) @@ -315,7 +336,8 @@ public class UnifiedOrderQueryService { BigDecimal grossWeightKg = sum(cargoes, OrderCargoEntity::getGrossWeightKg); BigDecimal netWeightKg = sum(cargoes, OrderCargoEntity::getNetWeightKg); BigDecimal volumeCbm = sum(cargoes, OrderCargoEntity::getVolumeCbm); - return new CargoSummary(skuNos, totalQuantity, totalPackageCount, grossWeightKg, netWeightKg, volumeCbm); + return new CargoSummary(skuNos, shipmentIds, referenceIds, totalQuantity, totalPackageCount, + grossWeightKg, netWeightKg, volumeCbm); } private static BigDecimal sum(List cargoes, diff --git a/order/src/test/java/com/baiyue/order/service/UnifiedOrderQueryServiceTest.java b/order/src/test/java/com/baiyue/order/service/UnifiedOrderQueryServiceTest.java index b0e6e56e6..7f371db00 100644 --- a/order/src/test/java/com/baiyue/order/service/UnifiedOrderQueryServiceTest.java +++ b/order/src/test/java/com/baiyue/order/service/UnifiedOrderQueryServiceTest.java @@ -101,6 +101,27 @@ class UnifiedOrderQueryServiceTest { verify(orders).findCargoByOrderIds(10001L, List.of(77L)); } + @Test + void listAggregatesShipmentAndReferenceIdsFromAllCargoLines() { + UnifiedOrderQuery query = query(null); + OrderEntity order = order(OrderStatus.APPROVED); + order.setShipmentId("SHIP-1"); + order.setReferenceId("REF-1"); + OrderCargoEntity first = new OrderCargoEntity(); + first.setOrderId(77L); first.setShipmentId("SHIP-1"); first.setReferenceId("REF-1"); + OrderCargoEntity second = new OrderCargoEntity(); + second.setOrderId(77L); second.setShipmentId("SHIP-2"); second.setReferenceId("REF-2"); + when(orders.unifiedPage(eq(10001L), eq(query), any())) + .thenReturn(new PageResult<>(List.of(order), 1, 20, 1, 1)); + when(firstleg.summaries(eq("internal-token"), any())).thenReturn(ApiResponse.success(List.of())); + when(orders.findCargoByOrderIds(10001L, List.of(77L))).thenReturn(List.of(first, second)); + + assertThat(service.page(10001L, query).records()).singleElement().satisfies(row -> { + assertThat(row.shipmentId()).isEqualTo("SHIP-1, SHIP-2"); + assertThat(row.referenceId()).isEqualTo("REF-1, REF-2"); + }); + } + @Test void inboundPackageCountRemainsTheFulfillmentFactAfterSplit() { UnifiedOrderQuery query = query(null); diff --git a/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseRequests.java b/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseRequests.java index c48a3f81d..2065c4115 100644 --- a/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseRequests.java +++ b/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseRequests.java @@ -29,16 +29,16 @@ public final class WarehouseRequests { @NotBlank @Size(max = 50) String warehouseCode, @Schema(description = "Warehouse name / 仓库名称", example = "Los Angeles Main Warehouse / 洛杉矶主仓", maxLength = 120, requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank @Size(max = 120) String warehouseName, - @Schema(description = "Warehouse type; defaults to DOMESTIC / 仓库类型,默认 DOMESTIC", example = "FBA", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS"}, defaultValue = "DOMESTIC") - @Pattern(regexp = "DOMESTIC|FBA|OVERSEAS") String warehouseType, + @Schema(description = "Warehouse type; defaults to DOMESTIC / 仓库类型,默认 DOMESTIC", example = "FBA", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS", "OVERSEAS_PARTNER"}, defaultValue = "DOMESTIC") + @Pattern(regexp = "DOMESTIC|FBA|OVERSEAS|OVERSEAS_PARTNER") String warehouseType, @Schema(description = "ISO 3166-1 alpha-2 country code; DOMESTIC defaults to CN / ISO 两位国家代码,DOMESTIC 默认 CN", example = "US", pattern = "[A-Z]{2}") @Pattern(regexp = "[A-Z]{2}") String countryCode, @Schema(description = "UTC offset; DOMESTIC defaults to UTC+8 / UTC 偏移量,DOMESTIC 默认 UTC+8", example = "UTC-8") String timezone, @Schema(description = "Contact name / 联系人姓名", example = "Alex Chen", maxLength = 64) @Size(max = 64) String contactName, - @Schema(description = "Contact phone / 联系电话", example = "+1-213-555-0100", maxLength = 32) - @Size(max = 32) String contactPhone, + @Schema(description = "Contact phone; digits only / 联系电话,仅允许数字", example = "12135550100", maxLength = 32) + @Pattern(regexp = "\\d*") @Size(max = 32) String contactPhone, @Schema(description = "Contact email / 联系邮箱", example = "warehouse@example.com") @Email String contactEmail, @Schema(description = "Warehouse status; defaults to ACTIVE / 仓库状态,默认 ACTIVE", example = "ACTIVE", allowableValues = {"ACTIVE", "INACTIVE"}, defaultValue = "ACTIVE") @@ -104,8 +104,8 @@ public final class WarehouseRequests { String countryCode, @Schema(description = "Enabled capability code filter / 已启用能力编码筛选", example = "STORAGE") String capabilityCode, - @Schema(description = "Warehouse type filter / 仓库类型筛选", example = "DOMESTIC", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS"}) - @Pattern(regexp = "DOMESTIC|FBA|OVERSEAS") String warehouseType, + @Schema(description = "Warehouse type filter / 仓库类型筛选", example = "DOMESTIC", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS", "OVERSEAS_PARTNER"}) + @Pattern(regexp = "DOMESTIC|FBA|OVERSEAS|OVERSEAS_PARTNER") String warehouseType, @Schema(description = "Status filter / 状态筛选", example = "ACTIVE", allowableValues = {"ACTIVE", "INACTIVE"}) @Pattern(regexp = "ACTIVE|INACTIVE") String status ) { diff --git a/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseSummaryResponse.java b/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseSummaryResponse.java index f990c2322..9c300aed9 100644 --- a/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseSummaryResponse.java +++ b/warehouse/src/main/java/com/baiyue/warehouse/dto/WarehouseSummaryResponse.java @@ -13,7 +13,7 @@ public record WarehouseSummaryResponse( @Schema(description = "Platform public scope ID, fixed to 0 / 平台公共作用域,固定为 0") Long tenantId, @Schema(description = "Warehouse code / 仓库编码") String warehouseCode, @Schema(description = "Warehouse name / 仓库名称") String warehouseName, - @Schema(description = "Warehouse type / 仓库类型", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS"}) String warehouseType, + @Schema(description = "Warehouse type / 仓库类型", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS", "OVERSEAS_PARTNER"}) String warehouseType, @Schema(description = "ISO two-letter country code / 国家二字码") String countryCode, @Schema(description = "UTC offset / UTC 偏移量") String timezone, @Schema(description = "Contact name / 联系人") String contactName, diff --git a/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseImportService.java b/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseImportService.java index 841ae7216..e7ab7b1b6 100644 --- a/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseImportService.java +++ b/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseImportService.java @@ -202,7 +202,7 @@ public class WarehouseImportService { } else { optionalLength(row, errors, "仓库名称", row.name(), 120); } - if (!row.type().isBlank() && !row.type().matches("(?i)DOMESTIC|FBA|OVERSEAS")) { + if (!row.type().isBlank() && !row.type().matches("(?i)DOMESTIC|FBA|OVERSEAS|OVERSEAS_PARTNER")) { errors.add(new WarehouseImportError(row.row(), "仓库类型", "只能填写 DOMESTIC、FBA 或 OVERSEAS")); } if (!row.countryCode().isBlank() && !row.countryCode().matches("(?i)[A-Z]{2}")) { diff --git a/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseSaveNormalizer.java b/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseSaveNormalizer.java index fe901037a..fa4cf84ec 100644 --- a/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseSaveNormalizer.java +++ b/warehouse/src/main/java/com/baiyue/warehouse/service/WarehouseSaveNormalizer.java @@ -15,6 +15,8 @@ final class WarehouseSaveNormalizer { static final long PUBLIC_TENANT_ID = 0L; static final String TYPE_DOMESTIC = "DOMESTIC"; static final String TYPE_FBA = "FBA"; + static final String TYPE_OVERSEAS = "OVERSEAS"; + static final String TYPE_OVERSEAS_PARTNER = "OVERSEAS_PARTNER"; static final String STATUS_ACTIVE = "ACTIVE"; static final String DEFAULT_ADDRESS_TYPE = "WAREHOUSE"; static final String DEFAULT_DOMESTIC_COUNTRY = "CN"; @@ -68,8 +70,8 @@ final class WarehouseSaveNormalizer { static NormalizedSaveRequest normalize(WarehouseRequests.SaveRequest request, long tenantId) { String warehouseType = upperOrDefault(request.warehouseType(), TYPE_DOMESTIC); - if (!TYPE_DOMESTIC.equals(warehouseType) && !TYPE_FBA.equals(warehouseType) && !"OVERSEAS".equals(warehouseType)) { - throw new BizException(ResultCode.VALIDATION_FAILED, "Warehouse type must be DOMESTIC, FBA or OVERSEAS / 仓库类型只能为 DOMESTIC、FBA 或 OVERSEAS"); + if (!Set.of(TYPE_DOMESTIC, TYPE_FBA, TYPE_OVERSEAS, TYPE_OVERSEAS_PARTNER).contains(warehouseType)) { + throw new BizException(ResultCode.VALIDATION_FAILED, "Warehouse type must be DOMESTIC, FBA, OVERSEAS or OVERSEAS_PARTNER / 仓库类型只能为 DOMESTIC、FBA、OVERSEAS 或 OVERSEAS_PARTNER"); } String status = upperOrDefault(request.status(), STATUS_ACTIVE); if (!STATUS_ACTIVE.equals(status) && !"INACTIVE".equals(status)) { diff --git a/warehouse/src/main/resources/db/warehouse.sql b/warehouse/src/main/resources/db/warehouse.sql index 044e55cb3..9ad8e6691 100644 --- a/warehouse/src/main/resources/db/warehouse.sql +++ b/warehouse/src/main/resources/db/warehouse.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS wh_warehouse ( tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共仓库固定使用 tenant_id=0', warehouse_code VARCHAR(50) NOT NULL COMMENT '平台内唯一仓库编码', warehouse_name VARCHAR(120) NOT NULL COMMENT '仓库名称', - warehouse_type VARCHAR(32) NOT NULL DEFAULT 'DOMESTIC' COMMENT '公共仓库类型:DOMESTIC、FBA、OVERSEAS', + warehouse_type VARCHAR(32) NOT NULL DEFAULT 'DOMESTIC' COMMENT '公共仓库类型:DOMESTIC、FBA、OVERSEAS、OVERSEAS_PARTNER', country_code CHAR(2) NOT NULL COMMENT '所在国家二字码', timezone VARCHAR(64) NOT NULL COMMENT '仓库所在地UTC偏移量', contact_name VARCHAR(64) DEFAULT NULL COMMENT '仓库联系人',