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);