diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepository.java index 500295196..618375789 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepository.java @@ -86,7 +86,11 @@ public class InboundPalletAllocationRepository { if (allocated <= picked) { continue; } - if (allocation.getId() == null || mapper.markTransferred(allocation.getId(), tenantId, inboundId) != 1) { + if (allocation.getId() == null) { + throw new IllegalStateException("库位资源转移状态已被并发修改"); + } + int changed = mapper.markTransferred(allocation.getId(), tenantId, inboundId); + if (changed != 1 && !isAllocationIdState(allocation.getId(), tenantId, inboundId, "TRANSFERRED")) { throw new IllegalStateException("库位资源转移状态已被并发修改"); } } @@ -94,12 +98,32 @@ public class InboundPalletAllocationRepository { public void markReleased(long tenantId, long inboundId, List reservationIds) { for (Long reservationId : reservationIds) { - if (reservationId == null || mapper.markReleased(tenantId, inboundId, reservationId) != 1) { + if (reservationId == null) { + throw new IllegalStateException("库位释放状态已被并发修改"); + } + int changed = mapper.markReleased(tenantId, inboundId, reservationId); + if (changed != 1 && !isReservationState(reservationId, tenantId, inboundId, "RELEASED")) { throw new IllegalStateException("库位释放状态已被并发修改"); } } } + private boolean isAllocationIdState(long id, long tenantId, long inboundId, String status) { + InboundPalletAllocationEntity current = mapper.selectOne(new LambdaQueryWrapper() + .eq(InboundPalletAllocationEntity::getTenantId, tenantId) + .eq(InboundPalletAllocationEntity::getInboundId, inboundId) + .eq(InboundPalletAllocationEntity::getId, id)); + return current != null && status.equals(current.getAllocationStatus()); + } + + private boolean isReservationState(long reservationId, long tenantId, long inboundId, String status) { + InboundPalletAllocationEntity current = mapper.selectOne(new LambdaQueryWrapper() + .eq(InboundPalletAllocationEntity::getTenantId, tenantId) + .eq(InboundPalletAllocationEntity::getInboundId, inboundId) + .eq(InboundPalletAllocationEntity::getReservationId, reservationId)); + return current != null && status.equals(current.getAllocationStatus()); + } + /** * 依据已经锁定且已扣减本次拣货数量的父单分配,预先固化子单快照。 * 仓库 transfer 是本地事务最后一个外部动作,成功后不再执行本地写入,避免跨服务重试时目标子单变化。 diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepositoryTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepositoryTest.java new file mode 100644 index 000000000..9b27cc89e --- /dev/null +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/InboundPalletAllocationRepositoryTest.java @@ -0,0 +1,55 @@ +package com.baiyue.firstleg.repository; + +import com.baiyue.firstleg.entity.InboundPalletAllocationEntity; +import com.baiyue.firstleg.mapper.InboundPalletAllocationMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class InboundPalletAllocationRepositoryTest { + @Mock + private InboundPalletAllocationMapper mapper; + @InjectMocks + private InboundPalletAllocationRepository repository; + + @Test + void acceptsReleasedSnapshotWhenRetryUpdateAffectsNoRows() { + InboundPalletAllocationEntity current = allocation(1L, 91L, "RELEASED", 10, 10); + when(mapper.markReleased(10001L, 31L, 91L)).thenReturn(0); + when(mapper.selectOne(any())).thenReturn(current); + + assertThatCode(() -> repository.markReleased(10001L, 31L, List.of(91L))) + .doesNotThrowAnyException(); + } + + @Test + void acceptsTransferredSnapshotWhenRetryUpdateAffectsNoRows() { + InboundPalletAllocationEntity source = allocation(1L, 91L, "CONFIRMED", 10, 2); + InboundPalletAllocationEntity current = allocation(1L, 91L, "TRANSFERRED", 10, 2); + when(mapper.markTransferred(1L, 10001L, 31L)).thenReturn(0); + when(mapper.selectOne(any())).thenReturn(current); + + assertThatCode(() -> repository.markTransferred(10001L, 31L, List.of(source), List.of())) + .doesNotThrowAnyException(); + } + + private static InboundPalletAllocationEntity allocation(long id, long reservationId, String status, + int allocated, int picked) { + InboundPalletAllocationEntity entity = new InboundPalletAllocationEntity(); + entity.setId(id); + entity.setReservationId(reservationId); + entity.setAllocationStatus(status); + entity.setAllocatedPackageCount(allocated); + entity.setPickedPackageCount(picked); + return entity; + } +}