fix(firstleg): recover multi-customer PDA completion
This commit is contained in:
+28
-21
@@ -110,17 +110,20 @@ public class LoadingDeliveryService {
|
||||
@Transactional
|
||||
public LoadingActionResponse completeLoading(long tenantId, long inboundId, long operatorId,
|
||||
String operatorName, String key, CompleteLoadingRequest request) {
|
||||
return completeLoading(tenantId, inboundId, inboundId, operatorId, operatorName, key, request);
|
||||
return completeLoading(tenantId, inboundId, tenantId, inboundId,
|
||||
operatorId, operatorName, key, request);
|
||||
}
|
||||
|
||||
private LoadingActionResponse completeLoading(long tenantId, long inboundId, long evidenceInboundId,
|
||||
private LoadingActionResponse completeLoading(long tenantId, long inboundId,
|
||||
long evidenceTenantId, long evidenceInboundId,
|
||||
long operatorId, String operatorName, String key,
|
||||
CompleteLoadingRequest request) {
|
||||
return completeLoading(tenantId, inboundId, evidenceInboundId, operatorId, operatorName,
|
||||
key, request, false);
|
||||
return completeLoading(tenantId, inboundId, evidenceTenantId, evidenceInboundId,
|
||||
operatorId, operatorName, key, request, false);
|
||||
}
|
||||
|
||||
private LoadingActionResponse completeLoading(long tenantId, long inboundId, long evidenceInboundId,
|
||||
private LoadingActionResponse completeLoading(long tenantId, long inboundId,
|
||||
long evidenceTenantId, long evidenceInboundId,
|
||||
long operatorId, String operatorName, String key,
|
||||
CompleteLoadingRequest request,
|
||||
boolean acceptedPdaCompletion) {
|
||||
@@ -131,8 +134,8 @@ public class LoadingDeliveryService {
|
||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||
if (target != null) {
|
||||
return completeFulfillmentSlice(tenantId, evidenceInboundId, operatorId, operatorName,
|
||||
key, request, target, acceptedPdaCompletion);
|
||||
return completeFulfillmentSlice(tenantId, inboundId, evidenceTenantId, evidenceInboundId,
|
||||
operatorId, operatorName, key, request, target, acceptedPdaCompletion);
|
||||
}
|
||||
if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) {
|
||||
requireLoadingPlanGate(tenantId, inboundId, acceptedPdaCompletion);
|
||||
@@ -143,7 +146,7 @@ public class LoadingDeliveryService {
|
||||
requireLoadingPlanGate(tenantId, inboundId, acceptedPdaCompletion);
|
||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||
requirePickingGate(tenantId, inboundId, acceptedPdaCompletion);
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
requireCompletionEvidence(evidenceTenantId, evidenceInboundId);
|
||||
if (repository.completeLoading(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) throw concurrent();
|
||||
addHistory(tenantId, inboundId, "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName);
|
||||
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
||||
@@ -159,12 +162,14 @@ public class LoadingDeliveryService {
|
||||
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||
long operatorId, String operatorName,
|
||||
String key, String remark) {
|
||||
return completeAfterPdaSettlement(tenantId, inboundId, inboundId, operatorId, operatorName, key, remark);
|
||||
return completeAfterPdaSettlement(tenantId, inboundId, tenantId, inboundId,
|
||||
operatorId, operatorName, key, remark);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||
long evidenceInboundId, long operatorId,
|
||||
long evidenceTenantId, long evidenceInboundId,
|
||||
long operatorId,
|
||||
String operatorName, String key, String remark) {
|
||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||
long completionInboundId = target == null ? inboundId : target.completionInboundId();
|
||||
@@ -172,8 +177,8 @@ public class LoadingDeliveryService {
|
||||
if (current == null || current.version() == null) {
|
||||
throw new BizException(ResultCode.CONFLICT, "PDA结算后的实装履约分片不存在,请刷新后重试");
|
||||
}
|
||||
return completeLoading(tenantId, inboundId, evidenceInboundId, operatorId, operatorName, key,
|
||||
new CompleteLoadingRequest(current.version(), remark), true);
|
||||
return completeLoading(tenantId, inboundId, evidenceTenantId, evidenceInboundId,
|
||||
operatorId, operatorName, key, new CompleteLoadingRequest(current.version(), remark), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,27 +186,29 @@ public class LoadingDeliveryService {
|
||||
* every external/business hard gate must pass before the PDA task is mutated.
|
||||
*/
|
||||
public void requirePdaCompletionReady(long tenantId, long inboundId) {
|
||||
requirePdaCompletionReady(tenantId, inboundId, inboundId);
|
||||
requirePdaCompletionReady(tenantId, inboundId, tenantId, inboundId);
|
||||
}
|
||||
|
||||
public void requirePdaCompletionReady(long tenantId, long inboundId, long evidenceInboundId) {
|
||||
public void requirePdaCompletionReady(long tenantId, long inboundId,
|
||||
long evidenceTenantId, long evidenceInboundId) {
|
||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||
if (!"LOADING".equals(inbound.processStatus())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "PDA确认装柜完成前订单必须处于LOADING");
|
||||
}
|
||||
loadingModelBridge.requireStarted(tenantId, inboundId);
|
||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
requireCompletionEvidence(evidenceTenantId, evidenceInboundId);
|
||||
}
|
||||
|
||||
private LoadingActionResponse completeFulfillmentSlice(long tenantId, long evidenceInboundId,
|
||||
private LoadingActionResponse completeFulfillmentSlice(long tenantId, long inboundId,
|
||||
long evidenceTenantId, long evidenceInboundId,
|
||||
long operatorId, String operatorName, String key,
|
||||
CompleteLoadingRequest request,
|
||||
CompletionTarget target,
|
||||
boolean acceptedPdaCompletion) {
|
||||
if ("LOADED".equals(target.loadedChildStatus())) {
|
||||
return complete(tenantId, evidenceInboundId, operatorId, COMPLETE_LOADING, key,
|
||||
new LoadingActionResponse("LOADED", evidenceResponse(tenantId, evidenceInboundId)));
|
||||
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
||||
new LoadingActionResponse("LOADED", evidenceResponse(tenantId, inboundId)));
|
||||
}
|
||||
if (!"LOADING".equals(target.loadedChildStatus())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "实装履约分片不在装柜中");
|
||||
@@ -209,14 +216,14 @@ public class LoadingDeliveryService {
|
||||
requireLoadingPlanGate(tenantId, target.sourceInboundId(), acceptedPdaCompletion);
|
||||
completionGate.requireReleasedBooking(tenantId, target.sourceInboundId());
|
||||
requirePickingGate(tenantId, target.sourceInboundId(), acceptedPdaCompletion);
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
requireCompletionEvidence(evidenceTenantId, evidenceInboundId);
|
||||
if (repository.completeLoadedChild(tenantId, target.sourceInboundId(), operatorId,
|
||||
request.expectedVersion()) != 1) throw concurrent();
|
||||
repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId);
|
||||
addHistory(tenantId, target.completionInboundId(), "LOADING", "LOADED", COMPLETE_LOADING,
|
||||
request.remark(), operatorId, operatorName);
|
||||
return complete(tenantId, evidenceInboundId, operatorId, COMPLETE_LOADING, key,
|
||||
new LoadingActionResponse("LOADED", evidenceResponse(tenantId, evidenceInboundId)));
|
||||
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
||||
new LoadingActionResponse("LOADED", evidenceResponse(tenantId, inboundId)));
|
||||
}
|
||||
|
||||
public EvidenceListResponse evidence(long tenantId, long inboundId) {
|
||||
|
||||
@@ -512,17 +512,32 @@ public class LoadingPickService {
|
||||
.thenComparing(LoadingPickItemMapper.InboundOwner::inboundId))
|
||||
.toList();
|
||||
if (owners.isEmpty()) throw new CompletionProcessingException("OWNER_DISCOVERY", conflict("PDA任务未关联有效入库单"));
|
||||
long evidenceInboundId = owners.get(0).inboundId();
|
||||
LoadingPickItemMapper.InboundOwner evidenceOwner = owners.get(0);
|
||||
|
||||
// Phase 1 is read-only: every owner must pass its business gates against the same cabinet evidence owner
|
||||
// before any warehouse resource or inbound status can be changed. This prevents a later customer gate from
|
||||
// leaving earlier customers partially completed.
|
||||
for (int index = 0; index < owners.size(); index++) {
|
||||
requireCompletionLease(command.getId(), worker);
|
||||
LoadingPickItemMapper.InboundOwner owner = owners.get(index);
|
||||
String checkpoint = completionCommands.checkpoint(command.getId(), owner.tenantId(), owner.inboundId());
|
||||
if (!checkpointAtLeast(checkpoint, "READY")) {
|
||||
if (!checkpointAtLeast(checkpoint, "SETTLED")) {
|
||||
runCompletionStage("OWNER_READY", () -> loadingDeliveryService.requirePdaCompletionReady(
|
||||
owner.tenantId(), owner.inboundId(), evidenceInboundId));
|
||||
requireCompletionLease(command.getId(), worker);
|
||||
completionCommands.checkpoint(command.getId(), owner.tenantId(), owner.inboundId(), index + 1, "READY");
|
||||
owner.tenantId(), owner.inboundId(), evidenceOwner.tenantId(), evidenceOwner.inboundId()));
|
||||
if (!checkpointAtLeast(checkpoint, "READY")) {
|
||||
requireCompletionLease(command.getId(), worker);
|
||||
completionCommands.checkpoint(command.getId(), owner.tenantId(), owner.inboundId(),
|
||||
index + 1, "READY");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2 is recoverable by per-owner checkpoints and stable idempotency keys. It starts only after the
|
||||
// complete cabinet passed phase 1, while prior deployments' completed checkpoints remain safely resumable.
|
||||
for (int index = 0; index < owners.size(); index++) {
|
||||
requireCompletionLease(command.getId(), worker);
|
||||
LoadingPickItemMapper.InboundOwner owner = owners.get(index);
|
||||
String checkpoint = completionCommands.checkpoint(command.getId(), owner.tenantId(), owner.inboundId());
|
||||
if (!checkpointAtLeast(checkpoint, "SETTLED")) {
|
||||
runCompletionStage("WAREHOUSE_SETTLEMENT", () -> settlementService.settleOwner(
|
||||
taskId, owner.inboundId(), command.getOperatorId(), command.getOperatorName()));
|
||||
@@ -531,9 +546,9 @@ public class LoadingPickService {
|
||||
}
|
||||
if (!checkpointAtLeast(checkpoint, "INBOUND_COMPLETED")) {
|
||||
runCompletionStage("INBOUND_COMPLETE", () -> loadingDeliveryService.completeAfterPdaSettlement(
|
||||
owner.tenantId(), owner.inboundId(), evidenceInboundId, command.getOperatorId(),
|
||||
command.getOperatorName(), "PDA-" + command.getOperationId() + '-' + owner.inboundId(),
|
||||
command.getRemark()));
|
||||
owner.tenantId(), owner.inboundId(), evidenceOwner.tenantId(), evidenceOwner.inboundId(),
|
||||
command.getOperatorId(), command.getOperatorName(),
|
||||
"PDA-" + command.getOperationId() + '-' + owner.inboundId(), command.getRemark()));
|
||||
requireCompletionLease(command.getId(), worker);
|
||||
completionCommands.checkpoint(command.getId(), owner.tenantId(), owner.inboundId(), index + 1,
|
||||
"INBOUND_COMPLETED");
|
||||
|
||||
+63
-1
@@ -253,10 +253,72 @@ class LoadingDeliveryServiceTest {
|
||||
long.class, long.class, long.class, String.class, String.class, String.class)
|
||||
.getAnnotation(Transactional.class)).isNotNull();
|
||||
assertThat(LoadingDeliveryService.class.getMethod("completeAfterPdaSettlement",
|
||||
long.class, long.class, long.class, long.class, String.class, String.class, String.class)
|
||||
long.class, long.class, long.class, long.class, long.class,
|
||||
String.class, String.class, String.class)
|
||||
.getAnnotation(Transactional.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaPreflightUsesCabinetEvidenceOwnerWithoutCrossCustomerLookup() {
|
||||
when(repository.findInbound(20, 200)).thenReturn(new Snapshot(200L, 20L, "LOADING", 3));
|
||||
when(repository.listEvidence(10, 100)).thenReturn(List.of(
|
||||
evidence("PHOTO_STEP_1", 1L, null), evidence("PHOTO_STEP_2", 2L, null),
|
||||
evidence("PHOTO_STEP_3", 3L, null), evidence("PHOTO_STEP_4", 4L, null),
|
||||
evidence("SEAL", 5L, "LOCK-001")));
|
||||
when(files.validate(eq("token"), eq(10L), eq(100L), anyLong())).thenAnswer(invocation -> {
|
||||
long fileId = invocation.getArgument(3);
|
||||
String type = fileId == 5L ? "SEAL" : "PHOTO_STEP_" + fileId;
|
||||
return ApiResponse.success(new LoadingEvidenceFileClient.FileMetadata(
|
||||
fileId, type.toLowerCase() + ".png", "LOADING_" + type, "image/png"));
|
||||
});
|
||||
|
||||
service.requirePdaCompletionReady(20, 200, 10, 100);
|
||||
|
||||
verify(loadingModelBridge).requireStarted(20, 200);
|
||||
verify(completionGate).requireReleasedBooking(20, 200);
|
||||
verify(repository).listEvidence(10, 100);
|
||||
verify(repository, never()).listEvidence(20, 100);
|
||||
verify(files, times(5)).validate(eq("token"), eq(10L), eq(100L), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaFinalizeKeepsOperationOnBusinessOwnerWhileUsingCabinetEvidenceOwner() {
|
||||
when(repository.claimOperation(eq(20L), eq(200L), eq(7L), eq("COMPLETE_LOADING"),
|
||||
eq("pda-multi-customer"), anyString())).thenReturn(true);
|
||||
when(repository.findInboundForUpdate(20, 200)).thenReturn(new Snapshot(200L, 20L, "LOADING", 7));
|
||||
when(repository.findInbound(20, 200)).thenReturn(new Snapshot(200L, 20L, "LOADING", 7));
|
||||
LoadingPickItemMapper.InboundProgress progress = mock(LoadingPickItemMapper.InboundProgress.class);
|
||||
when(progress.getExpectedCount()).thenReturn(1);
|
||||
when(progress.getPickedCount()).thenReturn(1);
|
||||
when(progress.getTaskCount()).thenReturn(1);
|
||||
when(progress.getCompletedTaskCount()).thenReturn(0);
|
||||
when(progress.getUnresolvedCount()).thenReturn(0);
|
||||
when(loadingPicks.inboundProgress(20, 200)).thenReturn(progress);
|
||||
when(repository.listEvidence(10, 100)).thenReturn(List.of(
|
||||
evidence("PHOTO_STEP_1", 1L, null), evidence("PHOTO_STEP_2", 2L, null),
|
||||
evidence("PHOTO_STEP_3", 3L, null), evidence("PHOTO_STEP_4", 4L, null),
|
||||
evidence("SEAL", 5L, "LOCK-001")));
|
||||
when(files.validate(eq("token"), eq(10L), eq(100L), anyLong())).thenAnswer(invocation -> {
|
||||
long fileId = invocation.getArgument(3);
|
||||
String type = fileId == 5L ? "SEAL" : "PHOTO_STEP_" + fileId;
|
||||
return ApiResponse.success(new LoadingEvidenceFileClient.FileMetadata(
|
||||
fileId, type.toLowerCase() + ".png", "LOADING_" + type, "image/png"));
|
||||
});
|
||||
when(repository.completeLoading(20, 200, 7, 7)).thenReturn(1);
|
||||
|
||||
LoadingActionResponse result = service.completeAfterPdaSettlement(
|
||||
20, 200, 10, 100, 7, "admin", "pda-multi-customer", "完成");
|
||||
|
||||
assertThat(result.mainStatus()).isEqualTo("LOADED");
|
||||
verify(repository).completeLoading(20, 200, 7, 7);
|
||||
verify(repository).completeOperation(eq(20L), eq(200L), eq(7L),
|
||||
eq("COMPLETE_LOADING"), eq("pda-multi-customer"), anyString());
|
||||
verify(repository, never()).completeOperation(eq(20L), eq(100L), anyLong(),
|
||||
anyString(), anyString(), anyString());
|
||||
verify(repository).listEvidence(10, 100);
|
||||
verify(repository, never()).listEvidence(20, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaFinalizeLocksAndUsesLoadedSliceVersionAfterPartialSettlement() {
|
||||
readyForPdaFinalize();
|
||||
|
||||
@@ -567,17 +567,17 @@ class LoadingPickServiceTest {
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(settlementService).settle(20L, 9L, "operator");
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 60L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L, 101L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 60L, 101L, 50L);
|
||||
verify(truckAppointmentService).completeLoadingFromPda(99L, 20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 101L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-50", "checked");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 60L, 50L, 9L, "operator",
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 60L, 101L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-60", "checked");
|
||||
verify(loadingListService).publishCompletedEvent(10L);
|
||||
InOrder finalization = inOrder(loadingListService, loadingDeliveryService);
|
||||
finalization.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 101L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-50", "checked");
|
||||
finalization.verify(loadingListService).publishCompletedEvent(10L);
|
||||
}
|
||||
@@ -608,7 +608,7 @@ class LoadingPickServiceTest {
|
||||
verify(settlementService).settle(20L, 9L, "operator");
|
||||
verify(repository).approve(20L, 9L, "operator", "checked");
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 101L, 50L, 9L, "operator",
|
||||
"PDA-complete-not-blocked-50", "checked");
|
||||
verify(loadingListService).publishCompletedEvent(10L);
|
||||
}
|
||||
@@ -627,7 +627,7 @@ class LoadingPickServiceTest {
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||
doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
|
||||
"入库单并发修改"))
|
||||
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 101L, 50L, 9L, "operator",
|
||||
"PDA-complete-finalize-fails-50", "checked");
|
||||
|
||||
assertThatThrownBy(() -> service.complete(20L,
|
||||
@@ -687,7 +687,7 @@ class LoadingPickServiceTest {
|
||||
verify(loadingListService, never()).finishFromPda(anyLong(), anyLong(), anyLong(), anyString());
|
||||
verify(loadingListService, never()).publishCompletedEvent(anyLong());
|
||||
verify(loadingDeliveryService, never()).completeAfterPdaSettlement(anyLong(), anyLong(), anyLong(),
|
||||
anyLong(), anyString(), anyString(), anyString());
|
||||
anyLong(), anyLong(), anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -827,6 +827,65 @@ class LoadingPickServiceTest {
|
||||
verify(completionCommands).complete(900L, "worker-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void workerUsesOneStableCabinetEvidenceOwnerAcrossTwoCustomers() {
|
||||
LoadingPickTaskEntity completing = task();
|
||||
completing.setTaskStatus("COMPLETING");
|
||||
when(repository.task(20L)).thenReturn(completing);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(202L, 80L),
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 60L),
|
||||
new LoadingPickItemMapper.InboundOwner(202L, 70L),
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L)));
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
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);
|
||||
|
||||
service.processCompletion(completionCommand(903L, "async-multi"), "worker-multi");
|
||||
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L, 101L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 60L, 101L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(202L, 70L, 101L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(202L, 80L, 101L, 50L);
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(
|
||||
202L, 70L, 101L, 50L, 9L, "operator", "PDA-async-multi-70", "checked");
|
||||
verify(settlementService, times(4)).settleOwner(eq(20L), anyLong(), eq(9L), eq("operator"));
|
||||
verify(repository, times(1)).insert(argThat((LoadingPickOperationEntity operation) ->
|
||||
"COMPLETE_LOADING".equals(operation.getActionType())
|
||||
&& "async-multi".equals(operation.getOperationId())));
|
||||
verify(loadingListService, times(1)).publishCompletedEvent(10L);
|
||||
verify(completionCommands).complete(903L, "worker-multi");
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondCustomerOwnerReadyFailureLeavesEveryOwnerUnsettledAndUncompleted() {
|
||||
LoadingPickTaskEntity completing = task();
|
||||
completing.setTaskStatus("COMPLETING");
|
||||
when(repository.task(20L)).thenReturn(completing);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L),
|
||||
new LoadingPickItemMapper.InboundOwner(202L, 70L)));
|
||||
doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT, "第二客户门禁未就绪"))
|
||||
.when(loadingDeliveryService).requirePdaCompletionReady(202L, 70L, 101L, 50L);
|
||||
|
||||
assertThatThrownBy(() -> service.processCompletion(
|
||||
completionCommand(904L, "async-owner-ready-fails"), "worker-failure"))
|
||||
.isInstanceOf(LoadingPickService.CompletionProcessingException.class)
|
||||
.satisfies(error -> assertThat(((LoadingPickService.CompletionProcessingException) error).stage())
|
||||
.isEqualTo("OWNER_READY"));
|
||||
|
||||
verify(settlementService, never()).settleOwner(anyLong(), anyLong(), anyLong(), anyString());
|
||||
verify(loadingDeliveryService, never()).completeAfterPdaSettlement(anyLong(), anyLong(), anyLong(),
|
||||
anyLong(), anyLong(), anyString(), anyString(), anyString());
|
||||
verify(completionCommands).checkpoint(904L, 101L, 50L, 1, "READY");
|
||||
verify(completionCommands, never()).checkpoint(eq(904L), anyLong(), anyLong(), anyInt(),
|
||||
eq("SETTLED"));
|
||||
verify(repository, never()).insert(any(LoadingPickOperationEntity.class));
|
||||
verify(loadingListService, never()).finishFromPda(anyLong(), anyLong(), anyLong(), anyString());
|
||||
verify(loadingListService, never()).publishCompletedEvent(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void workerRestartResumesFromOwnerCheckpointsWithoutRepeatingSettlements() {
|
||||
LoadingPickTaskEntity completing = task();
|
||||
@@ -834,9 +893,9 @@ class LoadingPickServiceTest {
|
||||
when(repository.task(20L)).thenReturn(completing);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 50L),
|
||||
new LoadingPickItemMapper.InboundOwner(101L, 60L)));
|
||||
new LoadingPickItemMapper.InboundOwner(202L, 60L)));
|
||||
when(completionCommands.checkpoint(901L, 101L, 50L)).thenReturn("INBOUND_COMPLETED");
|
||||
when(completionCommands.checkpoint(901L, 101L, 60L)).thenReturn("SETTLED");
|
||||
when(completionCommands.checkpoint(901L, 202L, 60L)).thenReturn("SETTLED");
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
||||
when(repository.completeAccepted(20L, 9L, "operator")).thenReturn(1);
|
||||
@@ -846,8 +905,8 @@ class LoadingPickServiceTest {
|
||||
|
||||
verify(settlementService, never()).settleOwner(anyLong(), anyLong(), anyLong(), anyString());
|
||||
verify(loadingDeliveryService, never()).completeAfterPdaSettlement(eq(101L), eq(50L), anyLong(),
|
||||
anyLong(), anyString(), anyString(), anyString());
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 60L, 50L, 9L, "operator",
|
||||
anyLong(), anyLong(), anyString(), anyString(), anyString());
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(202L, 60L, 101L, 50L, 9L, "operator",
|
||||
"PDA-async-resume-60", "checked");
|
||||
verify(loadingListService).publishCompletedEvent(10L);
|
||||
verify(repository).insert(org.mockito.ArgumentMatchers.<LoadingPickOperationEntity>argThat(
|
||||
|
||||
Reference in New Issue
Block a user