From 360115de35c796706d8cf4d4207ccba90e9b6975 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 24 Aug 2026 18:18:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A1=AE=E8=AE=A4=E9=A2=84?= =?UTF-8?q?=E8=A3=85=E5=90=8E=E9=87=8D=E6=96=B0=E5=88=86=E9=85=8D=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../firstleg/service/SmartLoadingService.java | 10 ++++++-- .../split/service/SplitPreloadService.java | 19 ++++++++++++++- .../service/SmartLoadingServiceTest.java | 6 ++++- .../service/SplitPreloadServiceTest.java | 23 +++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java index 7fa07edcb..fefa398ca 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java @@ -136,8 +136,14 @@ public class SmartLoadingService { if (candidate == null) throw conflict("入库货物不存在或尚未完成入库"); requireSameChannel(booking, candidate); requireSameCustomerScope(booking, candidate); - splitPreloadService.ensurePendingForLoadingAllocation(candidate.accountId(), candidate.inboundId(), - operatorId, operatorName); + var task = loadingPickService.mutationSnapshot(planId); + if (task.taskId() != null && "LOADING".equals(task.taskStatus())) { + splitPreloadService.ensureForRunningLoadingAllocation(candidate.accountId(), candidate.inboundId(), + operatorId, operatorName); + } else { + splitPreloadService.ensurePendingForLoadingAllocation(candidate.accountId(), candidate.inboundId(), + operatorId, operatorName); + } OrderCargoClient.CargoBinding cargo = loadCargo(candidate); LoadingPlanItemEntity existing = repository.activeItem(planId, candidate.accountId(), request.inboundDetailId()); int total = request.packageCount() + (existing == null ? 0 : existing.getPackageCount()); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/split/service/SplitPreloadService.java b/firstleg/src/main/java/com/baiyue/firstleg/split/service/SplitPreloadService.java index 70146cf38..778d1ce6d 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/split/service/SplitPreloadService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/split/service/SplitPreloadService.java @@ -68,11 +68,28 @@ public class SplitPreloadService { @Transactional public void ensurePendingForLoadingAllocation(long tenantId, long inboundId, long operatorId, String operatorName) { + ensureLoadingAllocationLifecycle(tenantId, inboundId, operatorId, operatorName, false); + } + + /** + * A running PDA task may restore or add an unpicked allocation after the plan-level preload + * confirmation. CONFIRMING remains immutable; CONFIRMED is accepted because the caller owns + * the active PDA task lock and will synchronise the restored loading item immediately. + */ + @Transactional + public void ensureForRunningLoadingAllocation(long tenantId, long inboundId, long operatorId, + String operatorName) { + ensureLoadingAllocationLifecycle(tenantId, inboundId, operatorId, operatorName, true); + } + + private void ensureLoadingAllocationLifecycle(long tenantId, long inboundId, long operatorId, + String operatorName, boolean allowConfirmed) { InboundSnapshot inbound = requireInbound(tenantId, inboundId); requireWaitingSplit(inbound, "仅待分柜订单可以建立智能装柜预装计划"); PreloadPlanEntity plan = repository.findPlan(tenantId, inboundId); if (plan != null) { - if (!PreloadStatus.PENDING.equals(plan.getStatus())) { + if (!PreloadStatus.PENDING.equals(plan.getStatus()) + && !(allowConfirmed && PreloadStatus.CONFIRMED.equals(plan.getStatus()))) { throw new BizException(ResultCode.CONFLICT, "预装计划已进入确认流程,不能修改智能装柜分配"); } return; diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java index a86f7b75f..5869f9f9c 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java @@ -358,6 +358,8 @@ class SmartLoadingServiceTest { when(repository.items(1)).thenReturn(List.of()); when(repository.candidate(100, 10, 20)).thenReturn(candidate(10)); when(repository.activeItem(1L, 9L, 20L)).thenReturn(null); + when(loadingPickService.mutationSnapshot(1L)).thenReturn( + new LoadingPickService.MutationSnapshot(77L, "LOADING", 6, Map.of())); when(cargoClient.detail(anyString(), anyLong(), anyLong(), anyLong())).thenReturn(ApiResponse.success( new OrderCargoClient.CargoBinding(20L, 30L, 1, "货物", 1, BigDecimal.ONE, BigDecimal.ONE, null, null, null, null, null, null))); @@ -365,12 +367,14 @@ class SmartLoadingServiceTest { Plan result = service.assign(1, new AssignRequest(10L, 20L, 1, 5, "assign-loading"), 7, "u"); assertThat(result.status()).isEqualTo("LOADING"); - var order = inOrder(loadingPickService, repository, loadingModelBridge); + var order = inOrder(loadingPickService, splitPreloadService, repository, loadingModelBridge); order.verify(loadingPickService).lockMutable(1L); + order.verify(splitPreloadService).ensureForRunningLoadingAllocation(9L, 10L, 7L, "u"); order.verify(repository).touch(1, 5, 7, "u"); order.verify(loadingModelBridge).bindIncrementalAllocation(1L, 9L, 10L, 7L, "u", "assign-loading"); order.verify(loadingPickService).sync(eq(1L), any(LoadingPlanItemEntity.class)); + verify(splitPreloadService, never()).ensurePendingForLoadingAllocation(9L, 10L, 7L, "u"); } @Test diff --git a/firstleg/src/test/java/com/baiyue/firstleg/split/service/SplitPreloadServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/split/service/SplitPreloadServiceTest.java index 267aaaf36..ff1839740 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/split/service/SplitPreloadServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/split/service/SplitPreloadServiceTest.java @@ -80,4 +80,27 @@ class SplitPreloadServiceTest { verify(repository, never()).createPlan(any()); } + @Test + void runningPdaAllocationMayReuseConfirmedPreload() { + when(repository.findInbound(10, 20)).thenReturn(new InboundSnapshot(20L, 10L, "WAIT_SPLIT", 3)); + PreloadPlanEntity plan = new PreloadPlanEntity(); + plan.setStatus("CONFIRMED"); + when(repository.findPlan(10, 20)).thenReturn(plan); + + service.ensureForRunningLoadingAllocation(10, 20, 7, "admin"); + + verify(repository, never()).createPlan(any()); + } + + @Test + void runningPdaAllocationStillRejectsConfirmingPreload() { + when(repository.findInbound(10, 20)).thenReturn(new InboundSnapshot(20L, 10L, "WAIT_SPLIT", 3)); + PreloadPlanEntity plan = new PreloadPlanEntity(); + plan.setStatus("CONFIRMING"); + when(repository.findPlan(10, 20)).thenReturn(plan); + + assertThatThrownBy(() -> service.ensureForRunningLoadingAllocation(10, 20, 7, "admin")) + .hasMessageContaining("已进入确认流程"); + } + }