From 0b34f99be584ee0ecb5e6ea92cd6acf3e43d3d31 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 2 Oct 2026 11:16:15 +0800 Subject: [PATCH] fix(firstleg): validate public port schedule queries with zero scope --- .../portschedule/dto/PortScheduleDtos.java | 2 +- .../service/PortScheduleServiceTest.java | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/PortScheduleDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/PortScheduleDtos.java index f21ac4c86..e1fc557a7 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/PortScheduleDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/portschedule/dto/PortScheduleDtos.java @@ -16,7 +16,7 @@ import java.util.List; public final class PortScheduleDtos { private PortScheduleDtos() {} - public record Query(@Positive Long customerId, @Positive Long oceanBookingId, + public record Query(@Min(0) Long customerId, @Positive Long oceanBookingId, @Positive Long containerBookingId, Boolean expired, @Size(max = 128) String keyword, @Min(1) long page, @Min(1) @Max(200) long size) { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/portschedule/service/PortScheduleServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/portschedule/service/PortScheduleServiceTest.java index 7bfaae9aa..d0a794755 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/portschedule/service/PortScheduleServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/portschedule/service/PortScheduleServiceTest.java @@ -15,6 +15,7 @@ import com.baiyue.firstleg.portschedule.repository.PortScheduleRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import jakarta.validation.Validation; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -34,6 +35,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class PortScheduleServiceTest { + @Test + void platformPublicQueryAllowsZeroButRejectsNegativeCustomerScope() { + try (var factory = Validation.buildDefaultValidatorFactory()) { + var validator = factory.getValidator(); + assertThat(validator.validate(new com.baiyue.firstleg.portschedule.dto.PortScheduleDtos.Query( + 0L, null, null, null, null, 1, 5))).isEmpty(); + assertThat(validator.validate(new com.baiyue.firstleg.portschedule.dto.PortScheduleDtos.Query( + -1L, null, null, null, null, 1, 5))).anyMatch(row -> row.getPropertyPath().toString().equals("customerId")); + } + } @Mock private PortScheduleRepository repository; @InjectMocks private PortScheduleService service; @@ -79,6 +90,35 @@ class PortScheduleServiceTest { verify(repository, never()).insert(any(PortScheduleEntity.class)); } + @Test + void platformBookingRequiresZeroScopeAndRejectsArbitraryCustomerWithoutInsert() { + OceanBookingEntity platformBooking = new OceanBookingEntity(); + platformBooking.setId(10L); + when(repository.oceanBooking(10L)).thenReturn(platformBooking); + assertThatThrownBy(() -> service.create(7L, "admin", "wrong-platform-scope", + new CreateRequest(0, data(null, future(1), future(2)), null))) + .isInstanceOf(BizException.class).hasMessageContaining("customerId与订舱客户不一致"); + verify(repository, never()).insert(any(PortScheduleEntity.class)); + } + + @Test + void createsZeroScopeScheduleForPlatformBooking() { + OceanBookingEntity platformBooking = new OceanBookingEntity(); + platformBooking.setId(10L); + when(repository.oceanBooking(10L)).thenReturn(platformBooking); + when(repository.containerBooking(20L)).thenReturn(new ContainerBookingEntity()); + when(repository.allocation(10L, 20L)).thenReturn(new OceanBookingContainerAllocationEntity()); + ScheduleData d = data(20L, future(1), future(2)); + ScheduleData platformData = new ScheduleData(0L, d.oceanBookingId(), d.containerBookingId(), + d.sourceType(), d.sourceName(), d.sourceRef(), d.vesselName(), d.voyageNo(), + d.departurePortCode(), d.departurePortName(), d.destinationPortCode(), d.destinationPortName(), + d.etdAt(), d.etaAt(), d.bookingCutoffAt(), d.customsCutoffAt(), d.portCutoffAt(), + d.observedAt(), d.expiresAt(), d.remark()); + Detail result = service.create(7L, "admin", "platform-scope-create", + new CreateRequest(0, platformData, "平台公共船期")); + assertThat(result.portSchedule().schedule().getCustomerId()).isZero(); + } + @Test void rejectsStaleUpdateWithoutMutation() { when(repository.find(1L)).thenReturn(schedule(3));