feat(firstleg): enforce indivisible coload loading

This commit is contained in:
2026-08-29 10:59:38 +08:00
parent ac6d3289a6
commit 3339ec91f9
11 changed files with 638 additions and 15 deletions
@@ -25,6 +25,9 @@ import com.baiyue.firstleg.loading.mapper.LoadingInboundMapper;
import com.baiyue.firstleg.loading.service.LoadingDeliveryService;
import com.baiyue.firstleg.loading.bridge.dto.LoadingModelBridgeDtos.Link;
import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService;
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.GroupSnapshot;
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper;
import com.baiyue.firstleg.mapper.LoadingPickItemMapper;
import com.baiyue.firstleg.repository.LoadingPickRepository;
@@ -43,6 +46,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
@@ -78,6 +82,8 @@ class LoadingPickServiceTest {
@MockBean
private LoadingDeliveryService loadingDeliveryService;
@MockBean
private ColoadGroupAssignmentPort coloadGroups;
@MockBean
private TruckAppointmentService truckAppointmentService;
@MockBean
private InboundStatusHistoryMapper inboundHistories;
@@ -800,6 +806,40 @@ class LoadingPickServiceTest {
verify(completionCommands, never()).insert(any());
}
@Test
void shortPickedColoadMemberCannotCreateCompletionCommandOrRemainderChild() {
LoadingPickTaskEntity task = task();
LoadingPickItemEntity shortPicked = item();
shortPicked.setInboundId(50L);
shortPicked.setPlannedPackageCount(5);
shortPicked.setPickedPackageCount(4);
shortPicked.setItemStatus("PARTIAL");
LoadingPickItemEntity complete = item();
complete.setId(31L);
complete.setInboundId(60L);
complete.setPlannedPackageCount(5);
complete.setPickedPackageCount(5);
complete.setItemStatus("PICKED");
GroupSnapshot group = coloadGroup("LOCKED", 10L,
new Member(101L, 301L, 50L, 5), new Member(202L, 302L, 60L, 5));
when(repository.taskForUpdate(20L)).thenReturn(task);
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
when(repository.inboundOwners(20L)).thenReturn(List.of(
new LoadingPickItemMapper.InboundOwner(101L, 50L),
new LoadingPickItemMapper.InboundOwner(202L, 60L)));
when(repository.items(20L)).thenReturn(List.of(shortPicked, complete));
when(coloadGroups.findByInbound(101L, 50L)).thenReturn(group);
when(coloadGroups.findByInbound(202L, 60L)).thenReturn(group);
assertThatThrownBy(() -> service.complete(20L,
new CompleteLoadingRequest(0, "complete-short-coload", "短拣异常"), 9L, "operator"))
.isInstanceOf(BizException.class).hasMessageContaining("全组箱数守恒");
verify(completionCommands, never()).insert(any());
verify(repository, never()).acceptCompletion(anyLong(), anyInt());
verify(settlementService, never()).settleOwner(anyLong(), anyLong(), anyLong(), anyString());
}
@Test
void workerProcessesFifteenInboundOwnersInStableOrderAndFinalizesOnce() {
LoadingPickTaskEntity completing = task();
@@ -841,6 +881,10 @@ class LoadingPickServiceTest {
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
when(repository.completeAccepted(20L, 9L, "operator")).thenReturn(1);
when(completionCommands.complete(903L, "worker-multi")).thenReturn(true);
GroupSnapshot group = coloadGroup("LOCKED", 10L,
new Member(101L, 301L, 50L, 1), new Member(101L, 302L, 60L, 1),
new Member(202L, 303L, 70L, 1), new Member(202L, 304L, 80L, 1));
when(coloadGroups.findByInbound(anyLong(), anyLong())).thenReturn(group);
service.processCompletion(completionCommand(903L, "async-multi"), "worker-multi");
@@ -855,6 +899,8 @@ class LoadingPickServiceTest {
"COMPLETE_LOADING".equals(operation.getActionType())
&& "async-multi".equals(operation.getOperationId())));
verify(loadingListService, times(1)).publishCompletedEvent(10L);
verify(coloadGroups).consumeWholeGroup(eq(700L), eq(10L),
org.mockito.ArgumentMatchers.startsWith("PDA-CONSUME-"), eq(9L), eq("operator"));
verify(completionCommands).complete(903L, "worker-multi");
}
@@ -1200,4 +1246,9 @@ class LoadingPickServiceTest {
return assignment;
}
private static GroupSnapshot coloadGroup(String status, Long lockedPlanId, Member... members) {
return new GroupSnapshot(700L, "CLG-700", status, 101L, "FBA", "FBA", 201L,
"FBA1", lockedPlanId, 1, List.of(members), LocalDateTime.now(), LocalDateTime.now());
}
}