# Conflicts:
#	firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java
This commit is contained in:
2026-08-25 14:53:27 +08:00
2 changed files with 47 additions and 8 deletions
@@ -462,10 +462,12 @@ 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任务关联入库单不存在");
completionStage(taskId, "完成前业务门禁", () ->
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(), loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(),
evidenceOwner.inboundId()); evidenceOwner.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拣货的箱数");
} }
@@ -473,18 +475,33 @@ 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) {
completionStage(taskId, "入库单状态完成", () ->
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(),
evidenceOwner.inboundId(), operatorId, operatorName, evidenceOwner.inboundId(), operatorId, operatorName,
"PDA-" + operationId + '-' + owner.inboundId(), remark); "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 = () -> {
@@ -591,6 +591,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();