From b92f7fe991963feae4adea4ebd6244632ca522e5 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 23 Oct 2026 04:47:33 +0800 Subject: [PATCH] fix(lastleg): group same-code FBA cargo with optional internal warehouse reference --- .../delivery/model/DeliveryCargoGroups.java | 14 ++++++++++-- .../delivery/DeliveryCargoGroupsTest.java | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/delivery/model/DeliveryCargoGroups.java b/lastleg-service/src/main/java/com/baiyue/lastleg/delivery/model/DeliveryCargoGroups.java index db94a64a1..26bf1f3a2 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/delivery/model/DeliveryCargoGroups.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/delivery/model/DeliveryCargoGroups.java @@ -59,16 +59,26 @@ public class DeliveryCargoGroups { var rows=entry.getValue().stream().sorted(Comparator.comparing(CargoLine::dispatchItemId) .thenComparing(CargoLine::attributionId)).toList(); var first=rows.get(0); - if (rows.stream().anyMatch(row->!Objects.equals(row.destination(),first.destination()))) + // FBA以真实仓码分组;外接来货可缺内部仓ID,只沿同组已知ID核对,不能覆盖冲突值。 + var destination=rows.stream().map(CargoLine::destination) + .filter(value->!blank(value.warehouseId())).findFirst().orElse(first.destination()); + if (rows.stream().anyMatch(row->!Objects.equals(withKnownFbaWarehouseId(row.destination(),destination.warehouseId()),destination))) throw conflict("同组货物的目的地信息不一致,请核对后安排"); long packages=rows.stream().mapToLong(CargoLine::packages).reduce(0,Math::addExact); long pallets=rows.stream().map(CargoLine::warehousePalletId).filter(Objects::nonNull).distinct().count(); - result.add(new Group(entry.getKey(),first.customerId(),first.customerName(),first.destination(), + result.add(new Group(entry.getKey(),first.customerId(),first.customerName(),destination, hash(rows),rows,packages,pallets)); } return List.copyOf(result); } + /** 仅补同一分组FBA缺失的内部引用,其他目的地事实必须保持完全一致。 */ + private Destination withKnownFbaWarehouseId(Destination value,String warehouseId) { + if (!"FBA".equals(value.type()) || !blank(value.warehouseId()) || blank(warehouseId)) return value; + return new Destination(value.key(),value.type(),warehouseId,value.warehouseCode(),value.warehouseName(), + value.address(),value.postalCode(),value.contactName(),value.contactPhone()); + } + /** 客户是组身份的一部分,同目的仓的其他客户永不合单。 */ public String groupKey(String customerId,Destination destination) { return hash(List.of(customerId,destination.key())); diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/delivery/DeliveryCargoGroupsTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/delivery/DeliveryCargoGroupsTest.java index 2fc32939b..4cd3ef355 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/delivery/DeliveryCargoGroupsTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/delivery/DeliveryCargoGroupsTest.java @@ -1,6 +1,8 @@ package com.baiyue.lastleg.delivery; import com.baiyue.lastleg.delivery.model.DeliveryCargoGroups; +import com.baiyue.lastleg.dispatch.port.DispatchContracts.CargoLine; +import com.baiyue.lastleg.dispatch.port.DispatchContracts.Destination; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.util.List; @@ -27,4 +29,24 @@ class DeliveryCargoGroupsTest { assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(row,row),false))); assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(row,cargo(2,"101","LAX9",51,3)),false))); } + @Test void sameFbaCodeKeepsKnownWarehouseReferenceWhenIncomingCargoHasNoInternalId() { + var incoming=cargo(1,"101","ONT8",51,3); + var known=destination(cargo(2,"101","ONT8",52,4),new Destination("FBA:ONT8","FBA","900","ONT8","ONT8",null,null,null,null)); + var group=policy.groups(context(List.of(known,incoming),false)).get(0); + assertEquals("900",group.destination().warehouseId());assertEquals(7,group.packages()); + assertNull(group.cargo().get(0).destination().warehouseId(),"真实来源快照不被覆盖"); + assertEquals(group,policy.groups(context(List.of(incoming,known),false)).get(0)); + var conflicting=destination(incoming,new Destination("FBA:ONT8","FBA","901","ONT8","ONT8",null,null,null,null)); + assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,conflicting),false))); + var wrongCode=destination(incoming,new Destination("FBA:ONT8","FBA",null,"LAX9","ONT8",null,null,null,null)); + assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,wrongCode),false))); + var changedAddress=destination(incoming,new Destination("FBA:ONT8","FBA",null,"ONT8","ONT8","other",null,null,null)); + assertThrows(org.springframework.web.server.ResponseStatusException.class,()->policy.groups(context(List.of(known,changedAddress),false))); + } + private CargoLine destination(CargoLine row,Destination destination) { + return new CargoLine(row.dispatchItemId(),row.customerId(),row.customerName(),row.orderId(),row.orderNo(),row.sourceKey(),row.sourceStage(),row.sourceRegion(), + row.sourceInboundId(),row.inboundCode(),row.planPalletId(),row.warehousePalletId(),row.palletNo(),row.pickId(),row.attributionId(),row.inventoryPositionId(), + row.receiptId(),row.returnReceiptId(),row.returnCartonId(),row.cartonMark(),row.relabelNo(),destination,row.packages(),row.productQuantity(), + row.grossWeightKg(),row.volumeCbm(),row.batchAllocations(),row.sourceOrigin()); + } }