补充PDA装柜完成阶段诊断

This commit is contained in:
2026-08-24 20:47:13 +08:00
parent 360115de35
commit 015fc09091
2 changed files with 45 additions and 6 deletions
@@ -459,9 +459,11 @@ public class LoadingPickService {
for (LoadingPickItemMapper.InboundOwner owner : owners) { for (LoadingPickItemMapper.InboundOwner owner : owners) {
LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId()); LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId());
if (snapshot == null) throw conflict("PDA任务关联入库单不存在"); if (snapshot == null) throw conflict("PDA任务关联入库单不存在");
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId()); completionStage(taskId, "完成前业务门禁", () ->
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId()));
} }
settlementService.settle(taskId, operatorId, operatorName); completionStage(taskId, "拆单与库位结算", () ->
settlementService.settle(taskId, operatorId, operatorName));
if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) { if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) {
throw conflict("柜次仍有未完成PDA拣货的箱数"); throw conflict("柜次仍有未完成PDA拣货的箱数");
} }
@@ -469,17 +471,32 @@ public class LoadingPickService {
throw conflict("PDA任务已经复核"); throw conflict("PDA任务已经复核");
} }
insertOperation(taskId, operationId, requestHash, "COMPLETE_LOADING", operatorId, operatorName); insertOperation(taskId, operationId, requestHash, "COMPLETE_LOADING", operatorId, operatorName);
loadingListService.finishFromPda(task.getLoadingPlanId(), taskId, operatorId, operatorName); completionStage(taskId, "装柜计划完成", () ->
loadingListService.finishFromPda(task.getLoadingPlanId(), taskId, operatorId, operatorName));
repository.syncInboundProjections(taskId, operatorId, operatorName); repository.syncInboundProjections(taskId, operatorId, operatorName);
for (LoadingPickItemMapper.InboundOwner owner : owners) { for (LoadingPickItemMapper.InboundOwner owner : owners) {
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), operatorId, completionStage(taskId, "入库单状态完成", () ->
operatorName, "PDA-" + operationId + '-' + owner.inboundId(), remark); loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), operatorId,
operatorName, "PDA-" + operationId + '-' + owner.inboundId(), remark));
} }
loadingListService.publishCompletedEvent(task.getLoadingPlanId()); completionStage(taskId, "完成事件发布", () ->
loadingListService.publishCompletedEvent(task.getLoadingPlanId()));
completeTruckAfterCommit(task.getContainerBookingId(), taskId, operatorId, operatorName); completeTruckAfterCommit(task.getContainerBookingId(), taskId, operatorId, operatorName);
return view(requiredTask(taskId), null); return view(requiredTask(taskId), null);
} }
private void completionStage(long taskId, String stage, Runnable action) {
try {
action.run();
} catch (BizException exception) {
throw exception;
} catch (RuntimeException exception) {
log.error("Unexpected PDA loading completion failure taskId={} stage={}", taskId, stage, exception);
throw new BizException(ResultCode.INTERNAL_ERROR,
"装柜完成在【" + stage + "】阶段处理异常,请联系管理员并提供请求编号");
}
}
private void completeTruckAfterCommit(long containerBookingId, long taskId, private void completeTruckAfterCommit(long containerBookingId, long taskId,
long operatorId, String operatorName) { long operatorId, String operatorName) {
Runnable completion = () -> { Runnable completion = () -> {
@@ -585,6 +585,28 @@ class LoadingPickServiceTest {
verify(loadingListService, never()).publishCompletedEvent(anyLong()); verify(loadingListService, never()).publishCompletedEvent(anyLong());
} }
@Test
void translatesUnexpectedSettlementFailureIntoActionableStageMessage() {
LoadingPickTaskEntity task = task();
when(repository.taskForUpdate(20L)).thenReturn(task);
when(repository.allTaskItemsResolved(20L)).thenReturn(true);
var owner = new LoadingPickItemMapper.InboundOwner(101L, 50L);
when(repository.inboundOwners(20L)).thenReturn(List.of(owner));
when(repository.inboundForUpdate(101L, 50L)).thenReturn(
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
doThrow(new IllegalStateException("unexpected database failure"))
.when(settlementService).settle(20L, 9L, "operator");
assertThatThrownBy(() -> service.complete(20L,
new CompleteLoadingRequest(0, "complete-stage-error", "checked"), 9L, "operator"))
.isInstanceOf(BizException.class)
.hasMessageContaining("拆单与库位结算")
.hasMessageContaining("请求编号");
verify(repository, never()).approve(anyLong(), anyLong(), anyString(), anyString());
verify(loadingListService, never()).finishFromPda(anyLong(), anyLong(), anyLong(), anyString());
}
@Test @Test
void atomicallySubmitsCompleteBatchAndReplaysSameResult() { void atomicallySubmitsCompleteBatchAndReplaysSameResult() {
LoadingPickTaskEntity task = task(); LoadingPickTaskEntity task = task();