From 93f06f8ff89576f389fc690433a129dc1db125f9 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 4 Sep 2026 14:03:04 +0800 Subject: [PATCH] fix: fall back to order declared volume --- .../MeasurementLocationOccupationService.java | 28 +++++++++++++++++-- .../main/resources/openapi/firstleg-v1.yaml | 3 +- ...surementLocationOccupationServiceTest.java | 28 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementLocationOccupationService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementLocationOccupationService.java index 1d7ce7f44..752ad35c4 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementLocationOccupationService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementLocationOccupationService.java @@ -5,6 +5,7 @@ import jakarta.annotation.Resource; import com.baiyue.common.core.api.ApiResponse; import com.baiyue.common.core.api.ResultCode; import com.baiyue.common.core.exception.BizException; +import com.baiyue.firstleg.client.OrderCargoClient; import com.baiyue.firstleg.client.WarehousePalletLocationClient; import com.baiyue.firstleg.config.InboundProperties; import com.baiyue.firstleg.entity.InboundOrderEntity; @@ -32,6 +33,8 @@ public class MeasurementLocationOccupationService { @Resource private InboundMeasurementRepository measurements; @Resource + private OrderCargoClient orderCargo; + @Resource private InboundProperties properties; @Value("${baiyue.security.internal-service-token}") private String internalToken; @@ -167,15 +170,34 @@ public class MeasurementLocationOccupationService { } private BigDecimal requireVolume(long tenantId, long inboundId, InboundOrderEntity order) { + if (order.getActualVolumeCbm() != null && order.getActualVolumeCbm().signum() > 0) { + return order.getActualVolumeCbm(); + } var measurement = measurements.findLatestMeasurement(tenantId, inboundId); if (measurement != null && measurement.getOriginalVolumeCbm() != null && measurement.getOriginalVolumeCbm().signum() > 0) { return measurement.getOriginalVolumeCbm(); } - if (order.getActualVolumeCbm() == null || order.getActualVolumeCbm().signum() <= 0) { - throw new BizException(ResultCode.CONFLICT, "入库单缺少有效客户申报体积"); + if (order.getOrderId() == null || order.getOrderId() <= 0) { + throw new BizException(ResultCode.CONFLICT, "入库单缺少有效实测或客户申报体积"); } - return order.getActualVolumeCbm(); + try { + ApiResponse response = orderCargo.measurementDeclaration( + internalToken, tenantId, order.getOrderId()); + if (response == null || !response.successful() || response.data() == null) { + String message = response == null || !org.springframework.util.StringUtils.hasText(response.message()) + ? "客户订单申报体积查询失败" : response.message(); + throw new BizException(response == null ? ResultCode.SERVICE_UNAVAILABLE + : ResultCode.of(response.code()), message); + } + BigDecimal declaredVolume = response.data().originalVolumeCbm(); + if (declaredVolume != null && declaredVolume.signum() > 0) { + return declaredVolume; + } + } catch (FeignException exception) { + throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "订单服务暂不可用,无法读取客户申报体积"); + } + throw new BizException(ResultCode.CONFLICT, "入库单缺少有效实测或客户申报体积"); } private static int requirePackageCount(InboundOrderEntity order) { diff --git a/firstleg/src/main/resources/openapi/firstleg-v1.yaml b/firstleg/src/main/resources/openapi/firstleg-v1.yaml index 887e63226..e239e48b7 100644 --- a/firstleg/src/main/resources/openapi/firstleg-v1.yaml +++ b/firstleg/src/main/resources/openapi/firstleg-v1.yaml @@ -996,7 +996,8 @@ paths: 分别写入 CORRECT 和 CONFIRM 修订记录。输入表示含托盘毛重;服务端统一换算为 KG, 使用创建时固化的托盘皮重计算 netWeightKg,且毛重必须大于皮重。 入库单 actualGrossWeightKg 为实测毛重;actualNetWeightKg 为扣除托盘皮重后的货物净重,两者不得混用。 - 首张图片确认成功时按实测体积容量与已确认图片数建立并占用整票库位块;后续图片绑定下一个空槽。 + 首张图片确认成功时优先按墨家/人工测量形成的实测体积建立并占用整票库位块;尚无实测体积时依次使用 + 测量批次冻结的申报体积、Order 最新客户申报体积,三者均无有效值才拒绝。后续图片绑定下一个空槽。 所需槽数增长时原子扩容原块,既有 blockId、slotNo、reservationId 和 locationId 保持不变, 普通连续位不足的新增槽使用同仓共享 A0。确认响应立即返回图片对应的权威块槽绑定。 parameters: diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementLocationOccupationServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementLocationOccupationServiceTest.java index b51217813..98b45698b 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementLocationOccupationServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementLocationOccupationServiceTest.java @@ -2,6 +2,7 @@ package com.baiyue.firstleg.service; import com.baiyue.common.core.api.ApiResponse; import com.baiyue.common.core.exception.BizException; +import com.baiyue.firstleg.client.OrderCargoClient; import com.baiyue.firstleg.client.WarehousePalletLocationClient; import com.baiyue.firstleg.config.InboundProperties; import com.baiyue.firstleg.entity.InboundOrderEntity; @@ -45,6 +46,7 @@ class MeasurementLocationOccupationServiceTest { @MockBean private InboundPalletAllocationRepository allocations; @MockBean private InboundWorkflowRepository workflow; @MockBean private InboundMeasurementRepository measurements; + @MockBean private OrderCargoClient orderCargo; @Resource private InboundProperties properties; @Resource private MeasurementLocationOccupationService service; @@ -160,6 +162,32 @@ class MeasurementLocationOccupationServiceTest { 20, new BigDecimal("2.5000"), block("OCCUPIED", 3)); } + @Test + void fallsBackToLatestOrderDeclarationWhenNoMeasurementBatchExists() { + InboundOrderEntity inbound = inbound(); + inbound.setOrderId(77L); + inbound.setActualVolumeCbm(null); + when(workflow.findOwned(BUSINESS_TENANT, INBOUND)).thenReturn(inbound); + when(measurements.findLatestMeasurement(BUSINESS_TENANT, INBOUND)).thenReturn(null); + when(orderCargo.measurementDeclaration("internal-token", BUSINESS_TENANT, inbound.getOrderId())) + .thenReturn(ApiResponse.success(new OrderCargoClient.MeasurementDeclarationSnapshot( + "REF-88", new BigDecimal("100"), new BigDecimal("6.5000"), "货物"))); + when(measurements.listWeights(BUSINESS_TENANT, INBOUND)) + .thenReturn(confirmedWeights().subList(0, 1)); + when(warehouse.reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"), + org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("RESERVED", 6))); + when(warehouse.confirmBlock(org.mockito.ArgumentMatchers.eq("internal-token"), + org.mockito.ArgumentMatchers.any())).thenReturn(ApiResponse.success(block("OCCUPIED", 6))); + + service.ensureForConfirmedWeight(BUSINESS_TENANT, INBOUND, OPERATOR, MEASUREMENT, 1); + + verify(orderCargo).measurementDeclaration("internal-token", BUSINESS_TENANT, inbound.getOrderId()); + verify(warehouse).reserveBlock(org.mockito.ArgumentMatchers.eq("internal-token"), + org.mockito.ArgumentMatchers.argThat(request -> request.requiredSlotCount() == 6)); + verify(allocations).replaceBlock(BUSINESS_TENANT, INBOUND, WAREHOUSE, + 20, new BigDecimal("6.5000"), block("OCCUPIED", 6)); + } + @Test void rejectsConfirmedWeightWithoutConfirmationTimeInsteadOfDroppingIt() { InboundWeightResultEntity weight = confirmedWeights().get(0);