From 98766e2b1adbfac7a099304818d94ba085d83c00 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 25 Aug 2026 13:59:03 +0800 Subject: [PATCH] fix(firstleg): continue same-plan loading allocations --- .../mapper/SmartLoadingCandidateMapper.java | 13 ++++++++++--- .../repository/SmartLoadingRepository.java | 4 ++-- .../firstleg/service/SmartLoadingService.java | 6 ++++-- .../SmartLoadingCandidateMapperContractTest.java | 8 +++++++- .../firstleg/service/SmartLoadingServiceTest.java | 15 ++++++++------- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/SmartLoadingCandidateMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/SmartLoadingCandidateMapper.java index 955715c40..9d1a34e60 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/SmartLoadingCandidateMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/SmartLoadingCandidateMapper.java @@ -79,12 +79,19 @@ import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate; import org.apache.iba WHERE inbound_detail_id IS NOT NULL AND package_count>0 GROUP BY tenant_id,measurement_id,inbound_detail_id ) i ON i.tenant_id=o.tenant_id AND i.measurement_id=m.id - WHERE o.warehouse_id=#{warehouseId} AND o.id=#{inboundId} AND o.process_status='WAIT_SPLIT' + WHERE o.warehouse_id=#{warehouseId} AND o.id=#{inboundId} + AND (o.process_status='WAIT_SPLIT' OR (o.process_status='LOADING' AND EXISTS ( + SELECT 1 FROM fl_loading_plan_item same_plan_item + WHERE same_plan_item.loading_plan_id=#{planId} + AND same_plan_item.account_id=o.tenant_id + AND same_plan_item.inbound_id=o.id + AND same_plan_item.deleted=0 + ))) AND o.actual_gross_weight_kg IS NOT NULL AND o.actual_net_weight_kg IS NOT NULL AND (o.inbound_role='MASTER' OR (o.split_purpose='REMAINDER' AND o.split_status='NOT_SPLIT')) AND i.inbound_detail_id=#{detailId} ORDER BY m.id DESC LIMIT 1 FOR UPDATE """) - Candidate candidate(@Param("warehouseId") long warehouseId,@Param("inboundId") long inboundId, - @Param("detailId") long detailId); + Candidate candidate(@Param("planId") long planId,@Param("warehouseId") long warehouseId, + @Param("inboundId") long inboundId,@Param("detailId") long detailId); } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/SmartLoadingRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/SmartLoadingRepository.java index ab28e444c..73c5bf050 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/SmartLoadingRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/SmartLoadingRepository.java @@ -31,8 +31,8 @@ public class SmartLoadingRepository { @Resource private InboundOrderMapper inbounds; public List candidates(long warehouseId) { return candidates.candidates(warehouseId); } - public Candidate candidate(long warehouseId, long inboundId, long detailId) { - return candidates.candidate(warehouseId, inboundId, detailId); + public Candidate candidate(long planId, long warehouseId, long inboundId, long detailId) { + return candidates.candidate(planId, warehouseId, inboundId, detailId); } public InboundOrderEntity inbound(long accountId, long inboundId) { return inbounds.selectOne(new LambdaQueryWrapper() 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 a3ac10ebb..c03fafb0b 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java @@ -132,7 +132,8 @@ public class SmartLoadingService { if (replay != null) return replay; LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName); ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId()); - Candidate candidate = repository.candidate(booking.getWarehouseId(), request.inboundId(), request.inboundDetailId()); + Candidate candidate = repository.candidate(planId, booking.getWarehouseId(), request.inboundId(), + request.inboundDetailId()); if (candidate == null) throw conflict("入库货物不存在或尚未完成入库"); requireSameChannel(booking, candidate); requireSameCustomerScope(booking, candidate); @@ -307,7 +308,8 @@ public class SmartLoadingService { throw conflict("计划箱数不能低于已拣货箱数"); } ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId()); - Candidate candidate = repository.candidate(booking.getWarehouseId(), item.getInboundId(), item.getInboundDetailId()); + Candidate candidate = repository.candidate(planId, booking.getWarehouseId(), item.getInboundId(), + item.getInboundDetailId()); if (candidate == null || request.packageCount() > candidate.availablePackageCount() + item.getPackageCount()) throw validation("调整箱数超过可用箱数"); requireSameChannel(booking, candidate); requireSameCustomerScope(booking, candidate); diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/SmartLoadingCandidateMapperContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/SmartLoadingCandidateMapperContractTest.java index f651828fe..f6477f5de 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/repository/SmartLoadingCandidateMapperContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/SmartLoadingCandidateMapperContractTest.java @@ -44,7 +44,7 @@ class SmartLoadingCandidateMapperContractTest { @Test void detailUsesSameBatchRule() throws Exception { Method detail = SmartLoadingCandidateMapper.class.getDeclaredMethod( - "candidate", long.class, long.class, long.class); + "candidate", long.class, long.class, long.class, long.class); String sql = String.join(" ", detail.getAnnotation(Select.class).value()); assertThat(sql) @@ -53,6 +53,12 @@ class SmartLoadingCandidateMapperContractTest { .contains("SELECT MAX(m2.id)") .contains("o.warehouse_id=#{warehouseId}") .contains("o.process_status='WAIT_SPLIT'") + .contains("o.process_status='LOADING'") + .contains("same_plan_item.loading_plan_id=#{planId}") + .contains("same_plan_item.account_id=o.tenant_id") + .contains("same_plan_item.inbound_id=o.id") + .contains("same_plan_item.deleted=0") + .doesNotContain("o.process_status IN ('WAIT_SPLIT','LOADING')") .contains("unitGrossWeightKg") .contains("unitNetWeightKg") .doesNotContain("i.unit_weight_kg") 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 9b5cba633..8f39dfa00 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java @@ -242,7 +242,7 @@ class SmartLoadingServiceTest { LoadingPlanEntity plan = plan("EDITING", 0); when(repository.plan(1)).thenReturn(plan); when(repository.booking(99L)).thenReturn(booking()); - when(repository.candidate(100, 10, 20)).thenReturn(candidate(5)); + when(repository.candidate(1, 100, 10, 20)).thenReturn(candidate(5)); when(repository.activeItem(1L, 9L, 20L)).thenReturn(item(7, 9, 20, 4)); BizException error = catchThrowableOfType(() -> service.assign(1, @@ -259,7 +259,7 @@ class SmartLoadingServiceTest { linked.setCustomerId(88L); when(repository.plan(1L)).thenReturn(plan); when(repository.booking(99L)).thenReturn(linked); - when(repository.candidate(100L, 10L, 20L)).thenReturn(candidate(5)); + when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(candidate(5)); BizException error = catchThrowableOfType(() -> service.assign(1L, new AssignRequest(10L, 20L, 1, 0, "assign-customer-mismatch"), 7L, "u"), BizException.class); @@ -357,7 +357,7 @@ class SmartLoadingServiceTest { when(repository.booking(99)).thenReturn(booking()); when(repository.touch(1, 5, 7, "u")).thenReturn(1); when(repository.items(1)).thenReturn(List.of()); - when(repository.candidate(100, 10, 20)).thenReturn(candidate(10)); + when(repository.candidate(1, 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())); @@ -368,6 +368,7 @@ class SmartLoadingServiceTest { Plan result = service.assign(1, new AssignRequest(10L, 20L, 1, 5, "assign-loading"), 7, "u"); assertThat(result.status()).isEqualTo("LOADING"); + verify(repository).candidate(1L, 100L, 10L, 20L); var order = inOrder(loadingPickService, splitPreloadService, repository, loadingModelBridge); order.verify(loadingPickService).lockMutable(1L); order.verify(splitPreloadService).ensureForRunningLoadingAllocation(9L, 10L, 7L, "u"); @@ -393,7 +394,7 @@ class SmartLoadingServiceTest { Plan result = service.assign(1, request, 7, "u"); assertThat(result.status()).isEqualTo("LOADING"); - verify(repository, never()).candidate(anyLong(), anyLong(), anyLong()); + verify(repository, never()).candidate(anyLong(), anyLong(), anyLong(), anyLong()); verify(cargoClient, never()).detail(anyString(), anyLong(), anyLong(), anyLong()); } @@ -592,7 +593,7 @@ class SmartLoadingServiceTest { when(repository.plan(1L)).thenReturn(plan, plan("LOADING", 3)); when(repository.item(1L, 8L)).thenReturn(line); when(repository.booking(99L)).thenReturn(booking()); - when(repository.candidate(100L, 10L, 20L)).thenReturn(candidate(10)); + when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(candidate(10)); when(repository.items(1L)).thenReturn(List.of(line)); when(repository.touch(1L, 2, 7L, "u")).thenReturn(1); when(repository.update(line)).thenReturn(1); @@ -617,7 +618,7 @@ class SmartLoadingServiceTest { new AdjustRequest(1, 2, 0, "adjust-picked"), 7L, "u")) .hasMessageContaining("不能低于已拣货箱数"); - verify(repository, never()).candidate(anyLong(), anyLong(), anyLong()); + verify(repository, never()).candidate(anyLong(), anyLong(), anyLong(), anyLong()); verify(repository, never()).touch(anyLong(), anyInt(), anyLong(), anyString()); } @@ -878,7 +879,7 @@ class SmartLoadingServiceTest { when(repository.plan(1)).thenReturn(plan, plan("EDITING", 1)); when(repository.booking(99)).thenReturn(booking()); when(repository.items(1)).thenReturn(List.of()); - when(repository.candidate(100, 10, 20)).thenReturn(candidate(10)); + when(repository.candidate(1, 100, 10, 20)).thenReturn(candidate(10)); when(repository.activeItem(1L, 9L, 20L)).thenReturn(null); when(repository.touch(1, 0, 7, "u")).thenReturn(1); }