Merge branch 'test' into dev

This commit is contained in:
2026-09-14 17:04:24 +08:00
118 changed files with 2210 additions and 614 deletions
@@ -87,11 +87,11 @@ public class LastlegOrderService {
Page<LastlegOrder> page = orderMapper.selectPage(
new Page<>(query.page(), query.size()), filter(customerId, query, relabelNo).orderByDesc(LastlegOrder::getCreatedAt));
Map<Long, List<LastlegOrderMapper.PalletMergeRelationRow>> mergeRelations = palletMergeRelations(page.getRecords());
Map<Long, String> dispatchStatuses = dispatchStatuses(page.getRecords());
Map<String, String> containerNodes = containerNodes(page.getRecords());
Map<Long, String> dispatchStatuses = dispatchStatuses(page.getRecords());
return new PageResult<>(page.getRecords().stream().map(order -> summary(order, mergeRelations.get(order.getId()),
StringUtils.hasText(order.getSourceContainerBookingId()) ? containerNodes.get(order.getSourceContainerBookingId()) : null,
dispatchStatuses.getOrDefault(order.getId(), "UNSCHEDULED"))).toList(),
StringUtils.hasText(order.getSourceContainerBookingId())
? containerNodes.get(order.getSourceContainerBookingId()) : null, dispatchStatuses.getOrDefault(order.getId(), "UNSCHEDULED"))).toList(),
page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
}
@@ -581,7 +581,7 @@ public class LastlegOrderService {
return result;
}
private Map<Long, String> dispatchStatuses(List<LastlegOrder> orders) {
private Map<Long, String> dispatchStatuses(List<LastlegOrder> orders) {
if (orders.isEmpty()) return Map.of();
List<LastlegOrderMapper.DispatchStatusRow> rows = orderMapper.findDispatchStatuses(
orders.stream().map(LastlegOrder::getId).toList());
@@ -591,14 +591,13 @@ public class LastlegOrderService {
}
private LastlegOrderSummary summary(LastlegOrder o, List<LastlegOrderMapper.PalletMergeRelationRow> relations,
String containerNode, String dispatchStatus) {
String containerNode, String dispatchStatus) {
List<LastlegOrderMapper.PalletMergeRelationRow> related = relations == null ? List.of() : relations;
return new LastlegOrderSummary(o.getId(), displayOrderNo(o), o.getOrderType(),
LastlegOrderType.labelOf(o.getOrderType()), o.getDestinationType(), o.getSourceInboundId(),
displayInboundCode(o), o.getLxCode(), o.getSourceOrderNo(), o.getCustomerId(), o.getCustomerName(), o.getOverseasWarehouseId(),
o.getOverseasWarehouseCode(), o.getOverseasWarehouseName(), o.getTransferWarehouseId(), o.getTransferWarehouseCode(),
o.getTransferWarehouseName(), o.getFbaWarehouseCode(), o.getContainerNo(),
containerNode,
o.getTransferWarehouseName(), o.getFbaWarehouseCode(), o.getContainerNo(), containerNode,
o.getAbnormal(), o.getDetained(), o.getHoldChargeable(), o.getHoldReason(),
Boolean.TRUE.equals(o.getManualNodeOverride()), Boolean.TRUE.equals(o.getManualNodeOverride()) ? "MANUAL" : "CONTAINER",
o.getVersion() == null ? 0 : o.getVersion(), o.getPalletCount(), o.getPackageCount(), o.getVolumeCbm(),
@@ -234,6 +234,8 @@ public class LoadingCompletedConsumer {
String destinationType = destinationType(payload, source);
String customerId = owner.customerId();
String customerName = owner.customerName();
List<FirstLegLoadingPlanCompletedV1.Item> items = "ONE_CLICK_DROPSHIP".equals(orderType)
? canonicalItems(source.items()) : source.items();
List<DropshipRequests.Pallet> dropshipPallets = "ONE_CLICK_DROPSHIP".equals(orderType)
? pallets(source, rawSource) : List.of();
if ("ONE_CLICK_DROPSHIP".equals(orderType) && dropshipPallets.isEmpty()) {
@@ -259,7 +261,7 @@ public class LoadingCompletedConsumer {
? dropshipPallets.size() : Objects.requireNonNullElse(source.palletCount(), 0));
order.setProductQuantity(source.productQuantity()); order.setVolumeCbm(source.volumeCbm());
order.setEstimatedWarehouseAt(parsePlannedArrival(payload.estimatedWarehouseArrivalAt()));
order.setVolumeWeightKg(source.items().stream().map(FirstLegLoadingPlanCompletedV1.Item::dimensionalWeightKg)
order.setVolumeWeightKg(items.stream().map(FirstLegLoadingPlanCompletedV1.Item::dimensionalWeightKg)
.filter(Objects::nonNull).reduce(BigDecimal.ZERO,BigDecimal::add));
order.setCurrentStatus("PENDING_ASSIGNMENT"); order.setSourceEventId(event.eventId());
order.setSourceLoadingPlanId(payload.loadingPlanId());
@@ -293,7 +295,7 @@ public class LoadingCompletedConsumer {
throw new IllegalArgumentException("散货来源托盘码数量与托盘数不一致");
}
}
for(var item:source.items()) {
for(var item:items) {
LastlegOrderDetail detail=new LastlegOrderDetail();
detail.setCustomerId(customerId);
detail.setOrderId(order.getId()); detail.setSourceInboundDetailId(item.sourceInboundDetailId()); detail.setSkuCode(item.skuCode());
@@ -377,11 +379,8 @@ public class LoadingCompletedConsumer {
private Map<String, List<String>> dropshipReceivingLabels(FirstLegLoadingPlanCompletedV1.Order source, JsonNode rawSource) {
Map<String, FirstLegLoadingPlanCompletedV1.Item> itemsByDetail = new java.util.LinkedHashMap<>();
for (var item : source.items()) {
if (item.sourceInboundDetailId() == null || item.sourceInboundDetailId().isBlank()
|| itemsByDetail.putIfAbsent(item.sourceInboundDetailId(), item) != null) {
throw new IllegalArgumentException("一件代发来源明细标识缺失或重复");
}
for (var item : canonicalItems(source.items())) {
itemsByDetail.put(item.sourceInboundDetailId(), item);
}
Map<String, List<String>> marksByDetail = new java.util.LinkedHashMap<>();
for (JsonNode pallet : rawSource.path("pallets")) {
@@ -395,7 +394,7 @@ public class LoadingCompletedConsumer {
sku.path("cartonMarks").forEach(mark -> marks.add(mark.asText()));
}
}
for (var item : source.items()) {
for (var item : itemsByDetail.values()) {
if (marksByDetail.getOrDefault(item.sourceInboundDetailId(), List.of()).size() != item.packageCount()) {
throw new IllegalArgumentException("一件代发托盘箱唛与来源明细箱数不一致");
}
@@ -403,6 +402,51 @@ public class LoadingCompletedConsumer {
return marksByDetail;
}
/** 接受同一来源明细因多托装柜形成的多个分片,并在尾程落库前合并为唯一明细。 */
private List<FirstLegLoadingPlanCompletedV1.Item> canonicalItems(List<FirstLegLoadingPlanCompletedV1.Item> source) {
Map<String, List<FirstLegLoadingPlanCompletedV1.Item>> byDetail = new java.util.LinkedHashMap<>();
for (var item : source == null ? List.<FirstLegLoadingPlanCompletedV1.Item>of() : source) {
if (item.sourceInboundDetailId() == null || item.sourceInboundDetailId().isBlank()) {
throw new IllegalArgumentException("一件代发来源明细标识缺失");
}
byDetail.computeIfAbsent(item.sourceInboundDetailId(), ignored -> new ArrayList<>()).add(item);
}
List<FirstLegLoadingPlanCompletedV1.Item> result = new ArrayList<>();
for (List<FirstLegLoadingPlanCompletedV1.Item> fragments : byDetail.values()) {
var first = fragments.get(0);
if (fragments.stream().anyMatch(item -> !Objects.equals(first.skuCode(), item.skuCode())
|| !Objects.equals(first.destinationWarehouseId(), item.destinationWarehouseId())
|| !Objects.equals(first.destinationWarehouseCode(), item.destinationWarehouseCode()))) {
throw new IllegalArgumentException("同一来源明细的尾程事件分片快照不一致");
}
List<String> marks = fragments.stream().flatMap(item -> item.cartonMarks().stream()).toList();
result.add(new FirstLegLoadingPlanCompletedV1.Item(first.sourceInboundDetailId(), first.skuCode(),
first.productNameZh(), first.productNameEn(),
fragments.stream().mapToInt(FirstLegLoadingPlanCompletedV1.Item::packageCount).sum(),
sumNullableInt(fragments, FirstLegLoadingPlanCompletedV1.Item::productQuantity),
sumNullableInt(fragments, FirstLegLoadingPlanCompletedV1.Item::palletCount),
sumDecimal(fragments, FirstLegLoadingPlanCompletedV1.Item::grossWeightKg),
sumDecimal(fragments, FirstLegLoadingPlanCompletedV1.Item::netWeightKg),
sumDecimal(fragments, FirstLegLoadingPlanCompletedV1.Item::dimensionalWeightKg),
sumDecimal(fragments, FirstLegLoadingPlanCompletedV1.Item::volumeCbm),
first.destinationWarehouseId(), first.destinationWarehouseCode(), first.destinationWarehouseName(),
first.fbaTrackingNo(), first.shipmentNo(), first.shippingMark(), first.palletNo(), marks));
}
return List.copyOf(result);
}
private static Integer sumNullableInt(List<FirstLegLoadingPlanCompletedV1.Item> values,
java.util.function.Function<FirstLegLoadingPlanCompletedV1.Item, Integer> getter) {
List<Integer> present = values.stream().map(getter).filter(Objects::nonNull).toList();
return present.isEmpty() ? null : present.stream().mapToInt(Integer::intValue).sum();
}
private static BigDecimal sumDecimal(List<FirstLegLoadingPlanCompletedV1.Item> values,
java.util.function.Function<FirstLegLoadingPlanCompletedV1.Item, BigDecimal> getter) {
List<BigDecimal> present = values.stream().map(getter).filter(Objects::nonNull).toList();
return present.isEmpty() ? null : present.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
}
private void createCartonLabels(String customerId, Long orderId, String destinationType,
FirstLegLoadingPlanCompletedV1.Item item, List<String> sourceMarks) {
if (!"OVERSEAS".equals(destinationType)) return;