diff --git a/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml b/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml index 8b7097c85..a7ac3f556 100644 --- a/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml +++ b/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml @@ -36,7 +36,7 @@ paths: /api/platform/v1/ocean-bookings/link-candidates: get: summary: Page released booking requirement candidates for one container - description: Requires platform:firstleg:ocean-booking:list. Eligibility matches only warehouseId, channelCode and containerType, with remainingQuantity greater than zero. Capacity and max weight are derived display snapshots and never eligibility fields. Allocated quantity counts only ACTIVE containers still pointing to the same booking requirement and is calculated by one bounded set query; booking detail calls are not used. + description: Requires platform:firstleg:ocean-booking:list. Returns only linkable requirement candidates: RELEASED, remainingQuantity greater than zero, and exact warehouseId, channelCode and containerType. Capacity and max weight are derived display snapshots and never eligibility fields. Allocated quantity counts only ACTIVE containers still pointing to the same booking requirement and is calculated by one bounded set query; booking detail calls are not used. An empty result is a normal successful page. Final target-container mutability is revalidated by the link command. x-permission: platform:firstleg:ocean-booking:list parameters: - {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}} diff --git a/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingControllerContractTest.java index a7200cd27..7a4ba2a08 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingControllerContractTest.java @@ -71,7 +71,10 @@ class PlatformOceanBookingControllerContractTest { .contains("warehouseId", "channelCode", "containerType") .contains("LinkCandidateResponse", "requiredQuantity", "allocatedQuantity", "remainingQuantity") .contains("Capacity and max weight are derived display snapshots and never eligibility fields") - .contains("booking detail calls are not used"); + .contains("Returns only linkable requirement candidates") + .contains("An empty result is a normal successful page") + .contains("booking detail calls are not used") + .doesNotContain("mismatchReasons", "disabledReason"); } @Test diff --git a/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingServiceTest.java index 3af6f71b7..7cbc45b12 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingServiceTest.java @@ -210,6 +210,21 @@ class OceanBookingServiceTest { verify(repository, never()).containers(anyLong()); } + @Test + void linkCandidatesReturnsNormalEmptyPageWithOneSetQueryAndNoDiagnosticFields() { + when(repository.linkCandidates(any())).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0)); + + PageResult result = service.linkCandidates( + new LinkCandidateQuery(701L, "SEA-US", "45HC", null, null)); + + assertThat(result.records()).isEmpty(); + assertThat(result.total()).isZero(); + assertThat(result.pages()).isZero(); + verify(repository, times(1)).linkCandidates(any()); + assertThat(LinkCandidate.class.getRecordComponents()).extracting(java.lang.reflect.RecordComponent::getName) + .doesNotContain("mismatchReasons", "disabledReason", "warehouseId", "channelCode"); + } + @Test void releaseRejectsInvalidScheduleWithoutTransition() { OceanBookingEntity draft = completeBooking("DRAFT", 3);