This commit is contained in:
2026-09-16 16:17:38 +08:00
parent 4571e3ac16
commit b79cd9ec42
2 changed files with 43 additions and 4 deletions
@@ -79,14 +79,20 @@ public class ContainerService {
public ContainerBookingEntity linkOceanBooking(long id, long operatorId, String operatorName, OceanBookingLinkRequest request) {
ContainerBookingEntity container = required(id); requireVersion(container, request.expectedVersion()); requireFirstLinkMutable(container);
if (container.getOceanBookingId() != null) throw conflict("模拟柜已关联真实订舱柜");
String existingContainerNo = trim(container.getContainerNo());
String requestedContainerNo = trim(request.containerNo());
String containerNo = requestedContainerNo == null
? existingContainerNo
: Iso6346ContainerNumber.require(requestedContainerNo);
if (existingContainerNo != null && !existingContainerNo.equals(containerNo)) {
throw conflict("输入的真实集装箱号与当前柜次已录入柜号不一致,请先在预装运输信息中纠正");
}
OceanBookingEntity booking = oceanBookings.find(request.oceanBookingId());
if (booking == null || !"RELEASED".equals(booking.getStatus())) throw conflict("只能关联已发布订舱");
OceanBookingContainerRequirementEntity requirement = oceanBookings.requirement(request.oceanBookingId(), request.requirementId());
if (requirement == null) throw new BizException(ResultCode.NOT_FOUND, "订舱箱型需求不存在");
long allocated = oceanBookings.activeLinkedCount(request.oceanBookingId(), request.requirementId());
if (allocated >= requirement.getQuantity()) throw conflict("该订舱箱型需求已达到数量上限");
String requestedContainerNo = trim(request.containerNo());
String containerNo = requestedContainerNo == null ? null : Iso6346ContainerNumber.require(requestedContainerNo);
if (containerNo != null && repository.containerNoExists(containerNo, id)) throw conflict("集装箱号已存在");
if (!java.util.Objects.equals(container.getWarehouseId(), booking.getWarehouseId())
|| !java.util.Objects.equals(container.getChannelGroupCode(), booking.getChannelCode())
@@ -134,7 +140,6 @@ public class ContainerService {
private void requireFirstLinkMutable(ContainerBookingEntity container) {
SmartLoadingStatus value = status(container);
if ((value != SmartLoadingStatus.WAIT_LOADING && value != SmartLoadingStatus.EDITING)
|| trim(container.getContainerNo()) != null
|| repository.hasPickTask(container.getId()) || repository.hasEvidence(container.getId())
|| repository.hasDocuments(container.getId())) throw conflict("已进入PDA、证据、单证或不可逆装柜阶段,不能关联订舱");
}
@@ -252,6 +252,39 @@ class ContainerServiceTest {
verify(oceanBookings).insert(any(OceanBookingContainerAllocationEntity.class));
}
@Test
@DisplayName("预装已录入真实柜号时关联订舱保留原柜号")
void linksReleasedBookingWhilePreservingExistingRealContainerNumber() {
ContainerBookingEntity container = stubLinkContext();
container.setContainerNo("MSCU6639870");
when(repository.find(11L)).thenReturn(container, container);
when(repository.linkReleasedBooking(eq(11L), eq(21L), eq(31L), eq("MSCU6639870"),
any(OceanBookingEntity.class), eq(2), eq(9L), eq("admin"))).thenReturn(1);
ContainerBookingEntity result = service.linkOceanBooking(11L, 9L, "admin",
new OceanBookingLinkRequest(21L, 31L, null, 2));
assertThat(result.getContainerNo()).isEqualTo("MSCU6639870");
verify(repository).linkReleasedBooking(eq(11L), eq(21L), eq(31L), eq("MSCU6639870"),
any(OceanBookingEntity.class), eq(2), eq(9L), eq("admin"));
verify(oceanBookings).insert(any(OceanBookingContainerAllocationEntity.class));
}
@Test
@DisplayName("关联订舱时拒绝用不同柜号覆盖预装已录入柜号")
void rejectsDifferentRealContainerNumberWhenLinkingReleasedBooking() {
ContainerBookingEntity container = stubLinkContext();
container.setContainerNo("MSCU6639870");
assertThatThrownBy(() -> service.linkOceanBooking(11L, 9L, "admin",
new OceanBookingLinkRequest(21L, 31L, "TSTU1503306", 2)))
.isInstanceOf(BizException.class).hasMessageContaining("与当前柜次已录入柜号不一致");
verify(repository, never()).linkReleasedBooking(anyLong(), anyLong(), anyLong(), anyString(),
any(), anyInt(), anyLong(), anyString());
verifyNoInteractions(oceanBookings);
}
@Test
@DisplayName("提柜后按柜次版本原子补录真实柜号")
void assignsRealContainerNumberAfterPickup() {
@@ -436,7 +469,7 @@ class ContainerServiceTest {
any(), anyInt(), anyLong(), anyString());
}
private void stubLinkContext() {
private ContainerBookingEntity stubLinkContext() {
ContainerBookingEntity container = booking("WAIT_LOADING", 2);
container.setContainerNo(null);
container.setWarehouseId(101L); container.setChannelGroupCode("MATSON_CLX"); container.setChannelCode(null);
@@ -450,6 +483,7 @@ class ContainerServiceTest {
when(oceanBookings.find(21L)).thenReturn(oceanBooking);
when(oceanBookings.requirement(21L, 31L)).thenReturn(requirement);
when(oceanBookings.activeLinkedCount(21L, 31L)).thenReturn(0L);
return container;
}
@Test