暂存提交
This commit is contained in:
@@ -19,7 +19,7 @@ public interface LoadingCompletedEventMapper {
|
||||
COALESCE(o.root_inbound_id, o.id) AS root_inbound_id,
|
||||
o.order_id AS source_order_id, o.order_no AS source_order_no,
|
||||
COALESCE(root_order.order_id, o.order_id) AS root_order_id,
|
||||
o.inbound_code, o.destination_warehouse_id, o.destination_code AS destination_warehouse_code,
|
||||
o.inbound_code, o.business_type, o.destination_warehouse_id, o.destination_code AS destination_warehouse_code,
|
||||
o.destination_name AS destination_warehouse_name,
|
||||
i.inbound_detail_id AS source_inbound_detail_id, i.sku_code,
|
||||
i.product_name_zh, i.product_name_en, i.package_count, i.product_quantity,
|
||||
@@ -36,6 +36,27 @@ public interface LoadingCompletedEventMapper {
|
||||
""")
|
||||
List<Row> rows(@Param("planId") long planId);
|
||||
|
||||
/** 读取一键代发托盘及其箱唛快照;普通订单不会使用该结果。 */
|
||||
@Select("""
|
||||
SELECT i.account_id, i.inbound_id AS source_inbound_id,
|
||||
a.pallet_code, a.allocation_no, a.picked_package_count AS allocated_package_count,
|
||||
i.inbound_detail_id AS source_inbound_detail_id, i.sku_code,
|
||||
i.product_name_zh, i.package_count, i.product_quantity,
|
||||
label.box_code
|
||||
FROM fl_loading_plan_item i
|
||||
JOIN fl_inbound_pallet_allocation a
|
||||
ON a.tenant_id = i.account_id AND a.inbound_id = i.inbound_id
|
||||
AND a.allocation_status IN ('CONFIRMED','TRANSFERRED','RELEASED')
|
||||
AND a.picked_package_count > 0
|
||||
LEFT JOIN fl_inbound_carton_label label
|
||||
ON label.tenant_id = i.account_id AND label.inbound_id = i.inbound_id
|
||||
AND label.inbound_detail_id = i.inbound_detail_id AND label.batch_status = 'ACTIVE'
|
||||
WHERE i.loading_plan_id = #{planId} AND i.deleted = 0
|
||||
ORDER BY i.account_id, i.inbound_id, a.allocation_no,
|
||||
i.inbound_detail_id, label.carton_index, label.id
|
||||
""")
|
||||
List<PalletRow> palletRows(@Param("planId") long planId);
|
||||
|
||||
@Getter @Setter
|
||||
class Row {
|
||||
private Long loadingPlanId; private String planNo; private Long containerBookingId;
|
||||
@@ -49,5 +70,14 @@ public interface LoadingCompletedEventMapper {
|
||||
private Integer productQuantity; private Integer palletCount; private BigDecimal grossWeightKg;
|
||||
private BigDecimal netWeightKg; private BigDecimal dimensionalWeightKg; private BigDecimal volumeCbm;
|
||||
private String fbaTrackingNo; private String shipmentNo; private String shippingMark;
|
||||
private String businessType;
|
||||
}
|
||||
|
||||
@Getter @Setter
|
||||
class PalletRow {
|
||||
private Long accountId; private Long sourceInboundId; private String palletCode;
|
||||
private Integer allocationNo; private Integer allocatedPackageCount;
|
||||
private Long sourceInboundDetailId; private String skuCode; private String productNameZh;
|
||||
private Integer packageCount; private Integer productQuantity; private String boxCode;
|
||||
}
|
||||
}
|
||||
|
||||
+78
-6
@@ -14,6 +14,7 @@ import com.baiyue.firstleg.entity.InboundOutboxEntity;
|
||||
import com.baiyue.firstleg.mapper.InboundOutboxMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingCompletedEventMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingCompletedEventMapper.Row;
|
||||
import com.baiyue.firstleg.mapper.LoadingCompletedEventMapper.PalletRow;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -29,6 +30,7 @@ import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/** 在装柜完成事务内按客户写跨区业务 Outbox。 */
|
||||
@Service
|
||||
@@ -41,17 +43,20 @@ public class LoadingCompletedOutboxService {
|
||||
public void publish(long planId) {
|
||||
List<Row> rows = snapshotMapper.rows(planId);
|
||||
if (rows.isEmpty()) throw new BizException(ResultCode.CONFLICT, "装柜计划没有最终实装入仓单,不能生成尾程交接事件");
|
||||
List<PalletRow> palletRows = snapshotMapper.palletRows(planId);
|
||||
rows.stream().collect(java.util.stream.Collectors.groupingBy(Row::getAccountId,
|
||||
java.util.TreeMap::new, java.util.stream.Collectors.toList()))
|
||||
.forEach((accountId, accountRows) -> insert(envelope(accountId, accountRows)));
|
||||
.forEach((accountId, accountRows) -> insert(envelope(accountId, accountRows,
|
||||
palletRows.stream().filter(item -> accountId.equals(item.getAccountId())).toList())));
|
||||
}
|
||||
|
||||
private EventEnvelope envelope(long accountId, List<Row> rows) {
|
||||
private EventEnvelope envelope(long accountId, List<Row> rows, List<PalletRow> palletRows) {
|
||||
Row first = rows.get(0);
|
||||
Map<Long, List<Row>> byInbound = rows.stream().collect(java.util.stream.Collectors.groupingBy(
|
||||
Row::getSourceInboundId, java.util.TreeMap::new, java.util.stream.Collectors.toList()));
|
||||
List<FirstLegLoadingPlanCompletedV1.Order> orders = byInbound.values().stream()
|
||||
.map(this::order).toList();
|
||||
.map(value -> order(value, palletRows.stream()
|
||||
.filter(item -> Objects.equals(item.getSourceInboundId(), value.get(0).getSourceInboundId())).toList())).toList();
|
||||
var payload = new FirstLegLoadingPlanCompletedV1(String.valueOf(first.getLoadingPlanId()),
|
||||
first.getPlanNo(), string(first.getContainerBookingId()), first.getContainerNo(), first.getCabinetNo(),
|
||||
string(first.getWarehouseId()), first.getWarehouseName(), orders);
|
||||
@@ -65,7 +70,7 @@ public class LoadingCompletedOutboxService {
|
||||
traceId, eventKey, null, payloadNode, hash).requireValid();
|
||||
}
|
||||
|
||||
private FirstLegLoadingPlanCompletedV1.Order order(List<Row> values) {
|
||||
private FirstLegLoadingPlanCompletedV1.Order order(List<Row> values, List<PalletRow> palletRows) {
|
||||
List<Row> sorted = values.stream().sorted(Comparator.comparing(Row::getSourceInboundDetailId)).toList();
|
||||
Row first = sorted.get(0);
|
||||
List<FirstLegLoadingPlanCompletedV1.Item> items = sorted.stream().map(row ->
|
||||
@@ -75,14 +80,81 @@ public class LoadingCompletedOutboxService {
|
||||
row.getVolumeCbm(), string(row.getDestinationWarehouseId()), row.getDestinationWarehouseCode(),
|
||||
row.getDestinationWarehouseName(), row.getFbaTrackingNo(), row.getShipmentNo(), row.getShippingMark()))
|
||||
.toList();
|
||||
String orderType = dropship(first.getBusinessType()) ? "ONE_CLICK_DROPSHIP" : "FIRST_LEG";
|
||||
List<FirstLegLoadingPlanCompletedV1.PalletSnapshot> pallets = dropship(first.getBusinessType())
|
||||
? palletSnapshots(sorted, palletRows) : List.of();
|
||||
if ("ONE_CLICK_DROPSHIP".equals(orderType) && pallets.isEmpty()) {
|
||||
throw new BizException(ResultCode.CONFLICT, "一键代发入仓单缺少托盘冻结快照");
|
||||
}
|
||||
return new FirstLegLoadingPlanCompletedV1.Order(string(first.getSourceInboundId()),
|
||||
string(first.getRootInboundId()), string(first.getSourceOrderId()), first.getSourceOrderNo(),
|
||||
string(first.getRootOrderId()), first.getInboundCode(), string(first.getDestinationWarehouseId()),
|
||||
first.getDestinationWarehouseCode(), first.getDestinationWarehouseName(), sumInt(sorted, Row::getPackageCount),
|
||||
sumNullableInt(sorted, Row::getProductQuantity), sumNullableInt(sorted, Row::getPalletCount),
|
||||
sum(sorted, Row::getGrossWeightKg), sum(sorted, Row::getNetWeightKg), sum(sorted, Row::getVolumeCbm), items);
|
||||
sumNullableInt(sorted, Row::getProductQuantity), "ONE_CLICK_DROPSHIP".equals(orderType)
|
||||
? pallets.size() : 0, sum(sorted, Row::getGrossWeightKg), sum(sorted, Row::getNetWeightKg),
|
||||
sum(sorted, Row::getVolumeCbm), items, string(first.getAccountId()), orderType, pallets);
|
||||
}
|
||||
|
||||
private List<FirstLegLoadingPlanCompletedV1.PalletSnapshot> palletSnapshots(List<Row> values, List<PalletRow> source) {
|
||||
if (source.isEmpty()) return List.of();
|
||||
Map<String, List<PalletRow>> byPallet = source.stream().collect(java.util.stream.Collectors.groupingBy(
|
||||
p -> (p.getAllocationNo() == null ? "" : p.getAllocationNo()) + "|" + p.getPalletCode(),
|
||||
LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
||||
int loadedPackages = byPallet.values().stream().map(list -> list.get(0)).map(PalletRow::getAllocatedPackageCount)
|
||||
.filter(Objects::nonNull).mapToInt(Integer::intValue).sum();
|
||||
if (loadedPackages != sumInt(values, Row::getPackageCount)) {
|
||||
throw new BizException(ResultCode.CONFLICT, "一键代发实装箱数与托盘已拣箱数不一致");
|
||||
}
|
||||
Map<Long, DetailSnapshot> details = new LinkedHashMap<>();
|
||||
values.stream().sorted(Comparator.comparing(Row::getSourceInboundDetailId)).forEach(row ->
|
||||
details.putIfAbsent(row.getSourceInboundDetailId(), new DetailSnapshot(row.getSourceInboundDetailId(),
|
||||
row.getSkuCode(), row.getProductNameZh(), row.getPackageCount(), row.getProductQuantity(), new ArrayList<>())));
|
||||
source.stream().filter(item -> item.getBoxCode() != null).forEach(item -> {
|
||||
DetailSnapshot detail = details.get(item.getSourceInboundDetailId());
|
||||
if (detail != null && !detail.marks().contains(item.getBoxCode())) detail.marks().add(item.getBoxCode());
|
||||
});
|
||||
Map<Long, Integer> remainingPackages = details.values().stream().collect(java.util.stream.Collectors.toMap(
|
||||
DetailSnapshot::detailId, DetailSnapshot::packageCount, (a,b)->a, LinkedHashMap::new));
|
||||
Map<Long, Integer> remainingMarks = details.values().stream().collect(java.util.stream.Collectors.toMap(
|
||||
DetailSnapshot::detailId, ignored -> 0, (a,b)->a, LinkedHashMap::new));
|
||||
List<FirstLegLoadingPlanCompletedV1.PalletSnapshot> result = new ArrayList<>();
|
||||
for (List<PalletRow> palletRows : byPallet.values()) {
|
||||
PalletRow first = palletRows.get(0);
|
||||
int capacity = first.getAllocatedPackageCount() == null ? 0 : first.getAllocatedPackageCount();
|
||||
List<FirstLegLoadingPlanCompletedV1.SkuPackageSnapshot> sku = new ArrayList<>();
|
||||
for (DetailSnapshot detail : details.values()) {
|
||||
int remaining = remainingPackages.getOrDefault(detail.detailId(), 0);
|
||||
if (capacity <= 0 || remaining <= 0) continue;
|
||||
int count = Math.min(capacity, remaining);
|
||||
int totalQuantity = detail.productQuantity() == null ? count
|
||||
: (int) Math.round((double) detail.productQuantity() * count / Math.max(1, detail.packageCount()));
|
||||
int markStart = remainingMarks.getOrDefault(detail.detailId(), 0);
|
||||
int markEnd = Math.min(detail.marks().size(), markStart + count);
|
||||
List<String> marks = new ArrayList<>(detail.marks().subList(markStart, markEnd));
|
||||
if (marks.size() != count) throw new BizException(ResultCode.CONFLICT, "一键代发箱唛数量与托盘箱数不一致");
|
||||
sku.add(new FirstLegLoadingPlanCompletedV1.SkuPackageSnapshot(detail.skuCode(),
|
||||
detail.productName(), count, totalQuantity, marks));
|
||||
remainingPackages.put(detail.detailId(), remaining - count);
|
||||
remainingMarks.put(detail.detailId(), markEnd);
|
||||
capacity -= count;
|
||||
}
|
||||
if (!sku.isEmpty()) result.add(new FirstLegLoadingPlanCompletedV1.PalletSnapshot(
|
||||
first.getPalletCode() == null ? "PALLET-" + first.getAllocationNo() : first.getPalletCode(), sku));
|
||||
}
|
||||
if (remainingPackages.values().stream().mapToInt(Integer::intValue).sum() != 0) {
|
||||
throw new BizException(ResultCode.CONFLICT, "一键代发托盘未覆盖全部实装货物明细");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean dropship(String value) {
|
||||
return value != null && ("ONE_CLICK_DROPSHIP".equalsIgnoreCase(value)
|
||||
|| value.toUpperCase(java.util.Locale.ROOT).contains("DROPSHIP"));
|
||||
}
|
||||
|
||||
private record DetailSnapshot(Long detailId, String skuCode, String productName,
|
||||
Integer packageCount, Integer productQuantity, List<String> marks) {}
|
||||
|
||||
private void insert(EventEnvelope envelope) {
|
||||
InboundOutboxEntity value = new InboundOutboxEntity();
|
||||
value.setId(IdWorker.getId()); value.setTenantId(Long.valueOf(envelope.tenantId()));
|
||||
|
||||
Reference in New Issue
Block a user