diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java index 520b82bba..66605e865 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/ContainerBookingMapper.java @@ -53,29 +53,8 @@ public interface ContainerBookingMapper extends BaseMapper page(String workbench, Query query) { Page result = mapper.selectBookingPage( new Page<>(query.page(), query.size()), query, workbench); - result.getRecords().forEach(this::resolveLoadingTime); + result.getRecords().forEach(this::resolveReadModel); return PageResult.from(result); } - public ContainerBookingEntity find(long id) { return resolveLoadingTime(mapper.selectBooking(id)); } + public ContainerBookingEntity find(long id) { return resolveReadModel(mapper.selectBooking(id)); } public ContainerBookingEntity findForUpdate(long id) { return mapper.selectBookingForUpdate(id); } public ContainerBookingEntity findActiveForUpdate(long id) { return mapper.selectActiveBookingForUpdate(id); } public boolean departed(long id) { return mapper.hasActiveOutboundDeparture(id) > 0; } @@ -62,6 +65,21 @@ public class ContainerRepository { estimatedSailingAt, operatorId, operatorName); } + private ContainerBookingEntity resolveReadModel(ContainerBookingEntity value) { + resolveCurrentPortSchedule(value); + return resolveLoadingTime(value); + } + + private void resolveCurrentPortSchedule(ContainerBookingEntity value) { + if (value == null || value.getCustomerId() == null || value.getOceanBookingId() == null) return; + PortScheduleEntity schedule = schedules.latestForDispatch(value.getCustomerId(), value.getOceanBookingId(), value.getId()); + if (schedule == null) return; + value.setVesselName(schedule.getVesselName()); + value.setVoyageNo(schedule.getVoyageNo()); + value.setEstimatedSailingAt(schedule.getEtdAt()); + value.setEstimatedPortArrivalAt(schedule.getEtaAt()); + } + private ContainerBookingEntity resolveLoadingTime(ContainerBookingEntity value) { if (value != null && value.getLoadingAt() == null && "COMPLETED".equals(value.getPlanStatus())) { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerBookingMapperContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerBookingMapperContractTest.java index a9acdcd48..ba1b0e742 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerBookingMapperContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerBookingMapperContractTest.java @@ -57,26 +57,6 @@ class ContainerBookingMapperContractTest { .contains("customer_item.account_id = #{query.customerId}"); } - @Test - @DisplayName("平台货柜读模型投影最新港务船期但保留订舱事实") - void projectsLatestPortScheduleForPlatformReads() throws Exception { - String detail = String.join("\n", ContainerBookingMapper.class - .getDeclaredMethod("selectBooking", long.class).getAnnotation(Select.class).value()); - String page = String.join("\n", ContainerBookingMapper.class - .getDeclaredMethod("selectBookingPage", com.baomidou.mybatisplus.extension.plugins.pagination.Page.class, - com.baiyue.firstleg.dto.ContainerDtos.Query.class, String.class) - .getAnnotation(Select.class).value()); - for (String sql : java.util.List.of(detail, page)) { - assertThat(sql) - .contains("fl_port_schedule", "ROW_NUMBER() OVER", "PARTITION BY candidate.customer_id", - "COALESCE(schedule.vessel_name,b.vessel_name)", - "COALESCE(schedule.voyage_no,b.voyage_no)", - "COALESCE(schedule.etd_at,b.estimated_sailing_at)", - "COALESCE(schedule.eta_at,b.estimated_port_arrival_at)") - .doesNotContain("actual_sailing_at=schedule", "actual_port_arrival_at=schedule"); - } - } - @Test @DisplayName("关联订舱 SQL 的预计到港参数与 Mapper 参数名一致") void linkReleasedBookingUsesDeclaredEstimatedArrivalParameter() { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerRepositoryLoadingTimeTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerRepositoryLoadingTimeTest.java index 1090a9a42..2cd56dd02 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerRepositoryLoadingTimeTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/ContainerRepositoryLoadingTimeTest.java @@ -2,6 +2,8 @@ package com.baiyue.firstleg.repository; import com.baiyue.firstleg.entity.ContainerBookingEntity; import com.baiyue.firstleg.mapper.ContainerBookingMapper; +import com.baiyue.firstleg.portschedule.entity.PortScheduleEntity; +import com.baiyue.firstleg.portschedule.mapper.PortScheduleMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -16,6 +18,7 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ContainerRepositoryLoadingTimeTest { @Mock private ContainerBookingMapper mapper; + @Mock private PortScheduleMapper schedules; @InjectMocks private ContainerRepository repository; @Test @@ -44,4 +47,30 @@ class ContainerRepositoryLoadingTimeTest { assertThat(result.getLoadingAt()).isEqualTo(plannedAt); } + + @Test + void projectsLatestPortScheduleWithoutChangingContainerIdentity() { + ContainerBookingEntity booking = new ContainerBookingEntity(); + booking.setId(8L); + booking.setCustomerId(9L); + booking.setOceanBookingId(10L); + booking.setContainerNo("TSTU0000001"); + booking.setVesselName("OLD-VESSEL"); + booking.setVoyageNo("OLD-V01"); + PortScheduleEntity schedule = new PortScheduleEntity(); + schedule.setVesselName("NEW-VESSEL"); + schedule.setVoyageNo("NEW-V01"); + schedule.setEtdAt(LocalDateTime.of(2026, 10, 7, 8, 0)); + schedule.setEtaAt(LocalDateTime.of(2026, 10, 27, 8, 0)); + when(mapper.selectBooking(8L)).thenReturn(booking); + when(schedules.latestForDispatch(9L, 10L, 8L)).thenReturn(schedule); + + ContainerBookingEntity result = repository.find(8L); + + assertThat(result.getContainerNo()).isEqualTo("TSTU0000001"); + assertThat(result.getVesselName()).isEqualTo("NEW-VESSEL"); + assertThat(result.getVoyageNo()).isEqualTo("NEW-V01"); + assertThat(result.getEstimatedSailingAt()).isEqualTo(schedule.getEtdAt()); + assertThat(result.getEstimatedPortArrivalAt()).isEqualTo(schedule.getEtaAt()); + } }