# 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) {
LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId());
if (snapshot == null) throw conflict("PDA任务关联入库单不存在");
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(),
evidenceOwner.inboundId());
completionStage(taskId, "完成前业务门禁", () ->
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(),
evidenceOwner.inboundId()));
}
settlementService.settle(taskId, operatorId, operatorName);
completionStage(taskId, "拆单与库位结算", () ->
settlementService.settle(taskId, operatorId, operatorName));
if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) {
throw conflict("柜次仍有未完成PDA拣货的箱数");
}
@@ -473,18 +475,33 @@ 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(),
evidenceOwner.inboundId(), operatorId, operatorName,
"PDA-" + operationId + '-' + owner.inboundId(), remark);
completionStage(taskId, "入库单状态完成", () ->
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(),
evidenceOwner.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 = () -> {
@@ -591,6 +591,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();