diff --git a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java
index 52467a049..dba2585a6 100644
--- a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java
+++ b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java
@@ -182,7 +182,8 @@ public class LoadingDeliveryService {
}
loadingModelBridge.requireCompleted(tenantId, target.sourceInboundId());
completionGate.requireReleasedBooking(tenantId, target.sourceInboundId());
- requireCompletedPicking(tenantId, target.sourceInboundId());
+ // The cabinet PDA task is the source of truth while settling. The inbound-level
+ // picking review is completed after this transition and must not gate it here.
requireCompletionEvidence(tenantId, evidenceInboundId);
if (repository.completeLoadedChild(tenantId, target.sourceInboundId(), operatorId,
request.expectedVersion()) != 1) throw concurrent();
diff --git a/scripts/admin-test-env-modules/phase3-container-loading.mjs b/scripts/admin-test-env-modules/phase3-container-loading.mjs
index 5a09b0a54..e45f3519c 100644
--- a/scripts/admin-test-env-modules/phase3-container-loading.mjs
+++ b/scripts/admin-test-env-modules/phase3-container-loading.mjs
@@ -160,6 +160,7 @@ export async function run(context) {
channelCode, warehouseId,
billOfLadingNo: `BL-${suffix}`.slice(0, 128),
shippingCompanyId: id(bookingAgent.id), shippingCompanyName: bookingAgent.name,
+ carrierCode: 'MATSON',
bookingAgentId: id(bookingAgent.id), bookingAgentName: bookingAgent.name,
vesselName: `验收船-${suffix}`.slice(0, 128), voyageNo: `V-${suffix}`.slice(0, 64),
departurePortCode: 'CNSZX', departurePortName: '深圳',
diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs
index 060f643d0..988f70afe 100644
--- a/scripts/admin-test-env-modules/phase3-loading.mjs
+++ b/scripts/admin-test-env-modules/phase3-loading.mjs
@@ -176,6 +176,7 @@ export async function run(context) {
channelCode: initial?.summary?.channelCode,
billOfLadingNo: `P3BL-${suffix}`.slice(0, 128),
shippingCompanyId: id(shippingCarrier.id), shippingCompanyName: shippingCarrier.name,
+ carrierCode: 'MATSON',
bookingAgentId: id(supplier.id), bookingAgentName: supplier.name,
vesselName: `P3VESSEL-${suffix}`.slice(0, 128), voyageNo: `P3V-${suffix}`.slice(0, 64),
departurePortCode: 'CNSZX', departurePortName: '深圳', destinationPortCode: 'USLAX',
diff --git a/settings-central.xml b/settings-central.xml
new file mode 100644
index 000000000..d89b6314e
--- /dev/null
+++ b/settings-central.xml
@@ -0,0 +1,12 @@
+
+
+ D:/firstlegRepo
+
+
+ central-direct
+ Maven Central
+ https://repo.maven.apache.org/maven2
+ external:*
+
+
+
diff --git a/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryController.java b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryController.java
new file mode 100644
index 000000000..b3f271267
--- /dev/null
+++ b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryController.java
@@ -0,0 +1,82 @@
+package com.baiyue.warehouse.controller;
+
+import com.baiyue.common.core.api.ApiResponse;
+import com.baiyue.common.core.api.PageResult;
+import com.baiyue.common.core.api.ResultCode;
+import com.baiyue.common.core.exception.BizException;
+import com.baiyue.common.security.AuthenticatedUser;
+import com.baiyue.warehouse.security.OverseasWarehousePermissions;
+import com.baiyue.warehouse.dto.PalletRequests;
+import com.baiyue.warehouse.entity.WarehousePalletEntity;
+import com.baiyue.warehouse.service.WarehouseResourceService;
+import com.baiyue.warehouse.service.DomesticWarehouseDirectory;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/** Exposes the domestic authoritative directory as read-only overseas resource options. */
+@RestController
+@RequestMapping("/api/platform/v1/warehouse-admin")
+@Tag(name = "Platform overseas warehouse directory")
+public class PlatformWarehouseDirectoryController {
+ private final DomesticWarehouseDirectory directory;
+ private final WarehouseResourceService resources;
+
+ public PlatformWarehouseDirectoryController(DomesticWarehouseDirectory directory,
+ WarehouseResourceService resources) {
+ this.directory = directory;
+ this.resources = resources;
+ }
+
+ @GetMapping("/pallets")
+ @PreAuthorize("hasAuthority('" + OverseasWarehousePermissions.PALLET_LIST + "')")
+ @Operation(summary = "Page public pallets across warehouses",
+ description = "Required authority: " + OverseasWarehousePermissions.PALLET_LIST)
+ public ApiResponse> pallets(
+ @AuthenticationPrincipal AuthenticatedUser user,
+ @Valid PalletRequests.QueryRequest query) {
+ requirePlatform(user);
+ return ApiResponse.success(resources.publicPallets(WarehouseResourceService.PUBLIC_RESOURCE_SCOPE, query));
+ }
+
+ @GetMapping
+ @PreAuthorize("hasAuthority('" + OverseasWarehousePermissions.WAREHOUSE_LIST + "')")
+ @Operation(summary = "Page shared overseas receiving warehouses",
+ description = "Required authority: " + OverseasWarehousePermissions.WAREHOUSE_LIST)
+ public ApiResponse> warehouses(
+ @AuthenticationPrincipal AuthenticatedUser user,
+ @RequestParam(defaultValue = "1") long page,
+ @RequestParam(defaultValue = "200") long size) {
+ requirePlatform(user);
+ if (page < 1 || size < 1 || size > 200) {
+ throw new BizException(ResultCode.BAD_REQUEST, "分页参数无效");
+ }
+ List all = directory.options().stream()
+ .map(value -> new WarehouseOption(value.warehouseId(), 0L, value.warehouseCode(),
+ value.warehouseName(), "OVERSEAS", value.status()))
+ .toList();
+ long from = Math.min((page - 1) * size, all.size());
+ long to = Math.min(from + size, all.size());
+ long pages = all.isEmpty() ? 0 : (all.size() + size - 1) / size;
+ return ApiResponse.success(new PageResult<>(all.subList((int) from, (int) to),
+ page, size, all.size(), pages));
+ }
+
+ private static void requirePlatform(AuthenticatedUser user) {
+ if (user == null || !"PLATFORM".equals(user.appCode())
+ || user.tenantId() == null || user.tenantId() != 0L) {
+ throw new BizException(ResultCode.FORBIDDEN, "仅允许平台端访问");
+ }
+ }
+
+ public record WarehouseOption(long id, long customerId, String warehouseCode,
+ String warehouseName, String warehouseType, String status) { }
+}
diff --git a/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/repository/WarehouseResourceRepository.java b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/repository/WarehouseResourceRepository.java
index 32b854ea8..9ebed969c 100644
--- a/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/repository/WarehouseResourceRepository.java
+++ b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/repository/WarehouseResourceRepository.java
@@ -146,6 +146,18 @@ public class WarehouseResourceRepository {
return PageResult.from(pallets.selectPage(new Page<>(query.page(), query.size()), wrapper));
}
+ public PageResult pagePublicPallets(long customerId,
+ PalletRequests.QueryRequest query) {
+ var wrapper = new LambdaQueryWrapper()
+ .eq(WarehousePalletEntity::getCustomerId, customerId)
+ .eq(StringUtils.hasText(query.palletType()), WarehousePalletEntity::getPalletType, query.palletType())
+ .eq(StringUtils.hasText(query.status()), WarehousePalletEntity::getStatus, query.status())
+ .eq(query.locationId() != null, WarehousePalletEntity::getLocationId, query.locationId())
+ .like(StringUtils.hasText(query.keyword()), WarehousePalletEntity::getPalletCode, query.keyword())
+ .orderByDesc(WarehousePalletEntity::getCreatedAt);
+ return PageResult.from(pallets.selectPage(new Page<>(query.page(), query.size()), wrapper));
+ }
+
public void deletePallet(long id) {
pallets.deleteById(id);
}
diff --git a/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/WarehouseResourceService.java b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/WarehouseResourceService.java
index d44e16759..f4c47176f 100644
--- a/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/WarehouseResourceService.java
+++ b/warehouse-overseas-service/src/main/java/com/baiyue/warehouse/service/WarehouseResourceService.java
@@ -218,6 +218,11 @@ public class WarehouseResourceService {
return repository.pagePallets(customerId, warehouseId, query);
}
+ public PageResult publicPallets(long customerId,
+ PalletRequests.QueryRequest query) {
+ return repository.pagePublicPallets(customerId, query);
+ }
+
@Transactional
public void deletePallet(long customerId, long warehouseId, long id) {
WarehousePalletEntity pallet = requirePallet(customerId, warehouseId, id);
diff --git a/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryControllerTest.java b/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryControllerTest.java
new file mode 100644
index 000000000..ae3f2b09e
--- /dev/null
+++ b/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/PlatformWarehouseDirectoryControllerTest.java
@@ -0,0 +1,33 @@
+package com.baiyue.warehouse.controller;
+
+import com.baiyue.common.security.AuthenticatedUser;
+import com.baiyue.warehouse.dto.SharedWarehouseReference;
+import com.baiyue.warehouse.service.DomesticWarehouseDirectory;
+import com.baiyue.warehouse.service.WarehouseResourceService;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class PlatformWarehouseDirectoryControllerTest {
+ @Test
+ void pagesPublicSharedWarehouseOptions() {
+ DomesticWarehouseDirectory directory = mock(DomesticWarehouseDirectory.class);
+ when(directory.options()).thenReturn(List.of(
+ new SharedWarehouseReference(21L, "US-LAX", "洛杉矶仓", "ACTIVE")));
+ var controller = new PlatformWarehouseDirectoryController(directory, mock(WarehouseResourceService.class));
+ var user = new AuthenticatedUser(1L, 0L, 0L, 2L,
+ "tester", "PLATFORM", "PLATFORM", Set.of());
+
+ var page = controller.warehouses(user, 1, 200).data();
+
+ assertEquals(1, page.total());
+ assertEquals(21L, page.records().get(0).id());
+ assertEquals(0L, page.records().get(0).customerId());
+ assertEquals("OVERSEAS", page.records().get(0).warehouseType());
+ }
+}
diff --git a/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/WarehouseResourcePermissionContractTest.java b/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/WarehouseResourcePermissionContractTest.java
index 65588f077..c147dad87 100644
--- a/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/WarehouseResourcePermissionContractTest.java
+++ b/warehouse-overseas-service/src/test/java/com/baiyue/warehouse/controller/WarehouseResourcePermissionContractTest.java
@@ -32,6 +32,13 @@ class WarehouseResourcePermissionContractTest {
assertPermission(methods.get("deletePallet"), PREFIX + "pallet:delete");
}
+ @Test
+ void warehouseDirectoryUsesCanonicalListAuthority() throws Exception {
+ assertPermission(PlatformWarehouseDirectoryController.class.getDeclaredMethod(
+ "warehouses", com.baiyue.common.security.AuthenticatedUser.class, long.class, long.class),
+ PREFIX + "warehouse:list");
+ }
+
private static void assertPermission(Method method, String authority) {
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
Operation operation = method.getAnnotation(Operation.class);