修复随机货道出货没有扣减随机出货货道的库存

This commit is contained in:
zjf committed 2026-03-20 17:44:37 +08:00
1 parent 701be5a0c0
commit ffef2845e9
2 files changed
+50 -24

No files matched your search

@@ -312,7 +312,8 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler {
/**
* 构建设备出货请求体
*
* 逻辑:一个商品(SPU)下可能存在多个货道(SKU)。每条订单行在该商品下「有库存且通道类型匹配」的货道中随机选一个出货,同一货道多次被选中则合并数量。
* 逻辑:优先使用订单创建时记录的 stockDeductionAllocation(哪个货道扣的库存就从哪个货道出货),
* 保证库存扣减与出货货道一致。无 allocation 的旧订单则从有库存货道中随机选(兼容历史数据)。
*
* message 协议格式(示例):T,C1&N1,C2&N2
* - T:通道类型(cabinetType)
@@ -327,24 +328,48 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler {
String cabinetType) {
Map<String, Integer> channelToCount = new LinkedHashMap<>();
String lastChannel = null;
boolean hasAllocation = orderItems.stream().anyMatch(item -> CollUtil.isNotEmpty(item.getStockDeductionAllocation()));
for (TradeOrderItemDO orderItem : orderItems) {
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(Collections.singletonList(orderItem.getSpuId()));
// 有库存、货道非空、通道类型一致
List<ProductSkuRespDTO> inStock = skus.stream()
.filter(sku -> sku != null
&& StrUtil.isNotBlank(sku.getCabinetChannel())
&& Objects.equals(sku.getCabinetType(), cabinetType)
&& sku.getStock() != null
&& sku.getStock() >= orderItem.getCount())
.collect(Collectors.toList());
if (inStock.isEmpty()) {
log.error("订单:{} 商品 spuId={} 无可用货道(有库存且通道类型匹配)", order.getNo(), orderItem.getSpuId());
return null;
if (hasAllocation) {
// 使用创建订单时记录的库存扣减分配:哪个货道扣的库存就从哪个货道出货
for (TradeOrderItemDO orderItem : orderItems) {
List<TradeOrderItemDO.StockDeductionAllocation> allocation = orderItem.getStockDeductionAllocation();
if (CollUtil.isEmpty(allocation)) {
log.error("订单:{} 订单项 spuId={} 无 stockDeductionAllocation,无法确定出货货道", order.getNo(), orderItem.getSpuId());
return null;
}
List<Long> skuIds = allocation.stream().map(TradeOrderItemDO.StockDeductionAllocation::getSkuId).collect(Collectors.toList());
List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(skuIds);
Map<Long, ProductSkuRespDTO> skuMap = skus.stream().collect(Collectors.toMap(ProductSkuRespDTO::getId, s -> s, (a, b) -> a));
for (TradeOrderItemDO.StockDeductionAllocation alloc : allocation) {
ProductSkuRespDTO sku = skuMap.get(alloc.getSkuId());
if (sku == null || StrUtil.isBlank(sku.getCabinetChannel())) {
log.error("订单:{} skuId={} 无货道编号(cabinetChannel)", order.getNo(), alloc.getSkuId());
return null;
}
channelToCount.merge(sku.getCabinetChannel(), alloc.getCount(), Integer::sum);
lastChannel = sku.getCabinetChannel();
}
}
} else {
// 兼容旧订单:从有库存货道中随机选
for (TradeOrderItemDO orderItem : orderItems) {
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(Collections.singletonList(orderItem.getSpuId()));
List<ProductSkuRespDTO> inStock = skus.stream()
.filter(sku -> sku != null
&& StrUtil.isNotBlank(sku.getCabinetChannel())
&& Objects.equals(sku.getCabinetType(), cabinetType)
&& sku.getStock() != null
&& sku.getStock() >= orderItem.getCount())
.collect(Collectors.toList());
if (inStock.isEmpty()) {
log.error("订单:{} 商品 spuId={} 无可用货道(有库存且通道类型匹配)", order.getNo(), orderItem.getSpuId());
return null;
}
ProductSkuRespDTO chosen = inStock.get(ThreadLocalRandom.current().nextInt(inStock.size()));
channelToCount.merge(chosen.getCabinetChannel(), orderItem.getCount(), Integer::sum);
lastChannel = chosen.getCabinetChannel();
}
ProductSkuRespDTO chosen = inStock.get(ThreadLocalRandom.current().nextInt(inStock.size()));
channelToCount.merge(chosen.getCabinetChannel(), orderItem.getCount(), Integer::sum);
lastChannel = chosen.getCabinetChannel();
}
String messageStr = cabinetType + "," + channelToCount.entrySet().stream()
@@ -12,9 +12,10 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import static java.util.Collections.singletonList;
@@ -62,7 +63,8 @@ public class TradeProductSkuOrderHandler implements TradeOrderHandler {
}
/**
* 售货机订单:按 SPU 分配库存并扣减。从每个 SPU 下有库存的货道(SKU)中贪婪分配。
* 售货机订单:按 SPU 分配库存并扣减。从每个 SPU 下有库存的货道(SKU)中随机分配,
* 出货时从对应货道出货,保证扣减与出货货道一致。
*/
private void deductStockBySpuAllocation(List<TradeOrderItemDO> orderItems) {
List<ProductSkuUpdateStockReqDTO.Item> deductItems = new ArrayList<>();
@@ -76,15 +78,14 @@ public class TradeProductSkuOrderHandler implements TradeOrderHandler {
if (CollUtil.isEmpty(skus)) {
continue;
}
// 优先从库存多的货道扣,若有订单行的 skuId 且有库存则优先
List<ProductSkuRespDTO> sorted = skus.stream()
// 有库存的货道随机打乱,随机选货道扣减(出货时也用这批货道)
List<ProductSkuRespDTO> inStock = skus.stream()
.filter(s -> s.getStock() != null && s.getStock() > 0)
.sorted(Comparator.<ProductSkuRespDTO>comparingInt(s -> s.getId().equals(item.getSkuId()) ? 0 : 1)
.thenComparing(Comparator.<ProductSkuRespDTO>comparingInt(s -> s.getStock() != null ? s.getStock() : 0).reversed()))
.collect(Collectors.toList());
Collections.shuffle(inStock, ThreadLocalRandom.current());
int remain = item.getCount();
List<TradeOrderItemDO.StockDeductionAllocation> allocation = new ArrayList<>();
for (ProductSkuRespDTO sku : sorted) {
for (ProductSkuRespDTO sku : inStock) {
if (remain <= 0) {
break;
}