From 015fc090912fcca694a77efede5f58558786decb Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 24 Aug 2026 20:47:13 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85PDA=E8=A3=85=E6=9F=9C?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=98=B6=E6=AE=B5=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../firstleg/service/LoadingPickService.java | 29 +++++++++++++++---- .../service/LoadingPickServiceTest.java | 22 ++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java index 6fda57d36..1e5f3cd45 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java @@ -459,9 +459,11 @@ public class LoadingPickService { for (LoadingPickItemMapper.InboundOwner owner : owners) { LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId()); 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())) { throw conflict("柜次仍有未完成PDA拣货的箱数"); } @@ -469,17 +471,32 @@ public class LoadingPickService { throw conflict("PDA任务已经复核"); } 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); for (LoadingPickItemMapper.InboundOwner owner : owners) { - loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), operatorId, - operatorName, "PDA-" + operationId + '-' + owner.inboundId(), remark); + completionStage(taskId, "入库单状态完成", () -> + 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); 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, long operatorId, String operatorName) { Runnable completion = () -> { diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/LoadingPickServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/LoadingPickServiceTest.java index dbaefc6b3..2c47d3736 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/LoadingPickServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/LoadingPickServiceTest.java @@ -585,6 +585,28 @@ class LoadingPickServiceTest { 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 void atomicallySubmitsCompleteBatchAndReplaysSameResult() { LoadingPickTaskEntity task = task();