This commit is contained in:
2026-09-11 11:15:01 +08:00
parent a4b7247c57
commit 7fd2e70024
5 changed files with 36 additions and 21 deletions
@@ -134,8 +134,7 @@ public class LoadingModelBridgeService {
var preload = repository.preload(tenantId, inboundId);
if (preload == null) throw new BizException(ResultCode.NOT_FOUND, "入库单预装计划不存在");
boolean running = "LOADING".equals(plan.status());
if (!Set.of("PENDING", "CONFIRMED").contains(preload.status())
&& !(running && "CONFIRMING".equals(preload.status()))) {
if (!Set.of("PENDING", "CONFIRMING", "CONFIRMED").contains(preload.status())) {
throw conflict("新增入库单预装状态不允许进入当前柜次");
}
PreloadLoadingPlanLinkEntity link = new PreloadLoadingPlanLinkEntity();
@@ -82,8 +82,8 @@ public class SplitPreloadService {
}
PreloadPlanEntity plan = repository.findPlan(tenantId, inboundId);
if (plan != null) {
if (!PreloadStatus.PENDING.equals(plan.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划已进入确认流程,不能修改智能装柜分配");
if (!reusableForLoadingAllocation(plan.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划当前状态不能修改智能装柜分配");
}
return;
}
@@ -96,17 +96,13 @@ public class SplitPreloadService {
repository.createPlan(plan);
} catch (DuplicateKeyException exception) {
PreloadPlanEntity concurrent = repository.findPlan(tenantId, inboundId);
if (concurrent == null || !PreloadStatus.PENDING.equals(concurrent.getStatus())) {
if (concurrent == null || !reusableForLoadingAllocation(concurrent.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划已被并发修改,请刷新后重试");
}
}
}
/**
* A running PDA task may restore or add an unpicked allocation after the plan-level preload
* confirmation. A running PDA task owns the mutation lock, so CONFIRMING and CONFIRMED
* lifecycles may be reused while the restored loading item is synchronised atomically.
*/
/** Reuses the confirmed preload lifecycle while a running PDA task owns the mutation lock. */
@Transactional
public void ensureForRunningLoadingAllocation(long tenantId, long inboundId, long operatorId,
String operatorName) {
@@ -126,10 +122,8 @@ public class SplitPreloadService {
throw new BizException(ResultCode.CONFLICT, "装柜中的订单缺少既有预装计划,不能新增分配");
}
if (plan != null) {
if (!PreloadStatus.PENDING.equals(plan.getStatus())
&& !(running && Set.of(PreloadStatus.CONFIRMING, PreloadStatus.CONFIRMED)
.contains(plan.getStatus()))) {
throw new BizException(ResultCode.CONFLICT, "预装计划已进入确认流程,不能修改智能装柜分配");
if (!reusableForLoadingAllocation(plan.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划当前状态不能修改智能装柜分配");
}
return;
}
@@ -142,12 +136,17 @@ public class SplitPreloadService {
repository.createPlan(plan);
} catch (DuplicateKeyException exception) {
PreloadPlanEntity concurrent = repository.findPlan(tenantId, inboundId);
if (concurrent == null || !PreloadStatus.PENDING.equals(concurrent.getStatus())) {
if (concurrent == null || !reusableForLoadingAllocation(concurrent.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "预装计划已被并发修改,请刷新后重试");
}
}
}
private static boolean reusableForLoadingAllocation(String status) {
return Set.of(PreloadStatus.PENDING, PreloadStatus.CONFIRMING, PreloadStatus.CONFIRMED)
.contains(status);
}
public PreloadPlanResponse detail(long tenantId, long inboundId) {
requireInbound(tenantId, inboundId);
return response(requirePlan(tenantId, inboundId));
@@ -278,6 +278,20 @@ class LoadingModelBridgeServiceTest {
verify(repository).confirmIncrementalPreload(30L, 10L, 20L, 4, 7L, "admin");
}
@Test
void bindsConfirmingPreloadToEditingPlanWithoutFinalizingIt() {
when(repository.byPair(10, 20, 40)).thenReturn(null);
when(repository.loadingPlan(40)).thenReturn(new LoadingPlanSnapshot(40L, 50L, "C1", "EDITING", 3));
when(repository.allocatedPackages(10, 20, 40)).thenReturn(2);
when(repository.preload(10, 20)).thenReturn(new PreloadSnapshot(30L, 10L, 20L, "CONFIRMING", 4));
service.bindIncrementalAllocation(40L, 10L, 20L, 7L, "admin", "assign-confirming-editing");
verify(repository).insert(any());
verify(repository, never()).confirmIncrementalPreload(anyLong(), anyLong(), anyLong(),
anyInt(), anyLong(), anyString());
}
private static Link link(String preloadStatus, int preloadVersion, String loadingStatus,
int loadingVersion, int packages) {
return new Link(1L, 10L, 20L, 30L, preloadStatus, preloadVersion, 40L, 50L, "C1",
@@ -84,14 +84,16 @@ class SplitPreloadServiceTest {
}
@Test
void loadingAllocationDoesNotOverwriteConfirmedPreload() {
void loadingAllocationMayReuseConfirmingAndConfirmedPreloads() {
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);
PreloadPlanEntity confirming = new PreloadPlanEntity();
confirming.setStatus("CONFIRMING");
PreloadPlanEntity confirmed = new PreloadPlanEntity();
confirmed.setStatus("CONFIRMED");
when(repository.findPlan(10, 20)).thenReturn(confirming, confirmed);
assertThatThrownBy(() -> service.ensurePendingForLoadingAllocation(10, 20, 7, "admin"))
.hasMessageContaining("已进入确认流程");
service.ensurePendingForLoadingAllocation(10, 20, 7, "admin");
service.ensurePendingForLoadingAllocation(10, 20, 7, "admin");
verify(repository, never()).createPlan(any());
}