暂存提交
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@Component
|
||||
public class LastlegOrderNoGenerator {
|
||||
public String next() {
|
||||
return "LMO" + IdWorker.getIdStr();
|
||||
}
|
||||
|
||||
/** The physical container number is the tail-leg business order grouping boundary. */
|
||||
public String forContainerNo(String containerNo) {
|
||||
if (containerNo == null || containerNo.isBlank()) {
|
||||
throw new IllegalArgumentException("containerNo is required");
|
||||
}
|
||||
String normalized = containerNo.trim();
|
||||
if (normalized.matches("[0-9]{1,29}")) {
|
||||
return "LMO" + normalized;
|
||||
}
|
||||
try {
|
||||
byte[] digest = MessageDigest.getInstance("SHA-256")
|
||||
.digest(normalized.getBytes(StandardCharsets.UTF_8));
|
||||
String digits = new BigInteger(1, digest).toString();
|
||||
return "LMO" + digits.substring(0, Math.min(29, digits.length()));
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException("SHA-256 is unavailable", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderDetailResponse;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderRequests;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderSummary;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipSkuInventory;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipCartonMapper;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderDetail;
|
||||
import com.baiyue.lastleg.entity.LastlegOrderStatusHistory;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderDetailMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderStatusHistoryMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class LastlegOrderService {
|
||||
public static final String PENDING_ASSIGNMENT = "PENDING_ASSIGNMENT";
|
||||
public static final String DESTINATION_ASSIGNED = "DESTINATION_ASSIGNED";
|
||||
public static final String INBOUND_PLANNED = "INBOUND_PLANNED";
|
||||
|
||||
@Resource private LastlegOrderMapper orderMapper;
|
||||
@Resource private LastlegOrderDetailMapper detailMapper;
|
||||
@Resource private LastlegOrderStatusHistoryMapper historyMapper;
|
||||
@Resource private DropshipOrderProfileMapper dropshipProfiles;
|
||||
@Resource private DropshipSkuInventoryMapper dropshipSkuInventory;
|
||||
@Resource private DropshipCartonMapper dropshipCartons;
|
||||
|
||||
public PageResult<LastlegOrderSummary> page(String customerId, LastlegOrderRequests.Query query) {
|
||||
Page<LastlegOrder> page = orderMapper.selectPage(
|
||||
new Page<>(query.page(), query.size()), filter(customerId, query).orderByDesc(LastlegOrder::getCreatedAt));
|
||||
return new PageResult<>(page.getRecords().stream().map(this::summary).toList(),
|
||||
page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
|
||||
}
|
||||
|
||||
public LastlegOrderDetailResponse detail(String customerId, long orderId) {
|
||||
LastlegOrder order = findOwned(customerId, orderId);
|
||||
List<LastlegOrderDetail> cargo = detailMapper.selectList(new LambdaQueryWrapper<LastlegOrderDetail>()
|
||||
.eq(LastlegOrderDetail::getOrderId, orderId).orderByAsc(LastlegOrderDetail::getId));
|
||||
List<LastlegOrderStatusHistory> history = historyMapper.selectList(
|
||||
new LambdaQueryWrapper<LastlegOrderStatusHistory>()
|
||||
.eq(LastlegOrderStatusHistory::getOrderId, orderId)
|
||||
.orderByAsc(LastlegOrderStatusHistory::getCreatedAt));
|
||||
List<LastlegOrderDetailResponse.DropshipPallet> pallets = dropshipPallets(customerId, order);
|
||||
return new LastlegOrderDetailResponse(order, cargo, history, pallets);
|
||||
}
|
||||
|
||||
private List<LastlegOrderDetailResponse.DropshipPallet> dropshipPallets(String customerId, LastlegOrder order) {
|
||||
if (!"ONE_CLICK_DROPSHIP".equals(order.getOrderType())) return List.of();
|
||||
var profiles = dropshipProfiles.listOrder(customerId, order.getId());
|
||||
if (profiles.isEmpty()) return List.of();
|
||||
Map<Long, List<DropshipSkuInventory>> skuByProfile = dropshipSkuInventory
|
||||
.listOrder(customerId, order.getId()).stream().collect(Collectors.groupingBy(DropshipSkuInventory::getProfileId));
|
||||
Map<Long, List<DropshipCarton>> cartonsByProfile = dropshipCartons
|
||||
.listOrder(customerId, order.getId()).stream().collect(Collectors.groupingBy(DropshipCarton::getProfileId));
|
||||
return profiles.stream().map(profile -> new LastlegOrderDetailResponse.DropshipPallet(
|
||||
profile.getId(), profile.getSourcePalletNo(), profile.getWarehousePalletId(), profile.getPalletNo(),
|
||||
profile.getInventoryPositionId(), profile.getStatus(),
|
||||
skuByProfile.getOrDefault(profile.getId(), List.of()),
|
||||
cartonsByProfile.getOrDefault(profile.getId(), List.of()))).toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public int transferDestination(String customerId, LastlegOrderRequests.TransferDestination request) {
|
||||
List<Long> ids = request.orderIds().stream().filter(Objects::nonNull).distinct().toList();
|
||||
if (ids.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "At least one order is required");
|
||||
}
|
||||
List<LastlegOrder> orders = orderMapper.selectList(new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(LastlegOrder::getCustomerId, customerId).in(LastlegOrder::getId, ids).last("FOR UPDATE"));
|
||||
if (orders.size() != ids.size()) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "One or more tail-leg orders were not found");
|
||||
}
|
||||
for (LastlegOrder order : orders) {
|
||||
ensureTransferable(order);
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (LastlegOrder order : orders) {
|
||||
String fromStatus = order.getCurrentStatus();
|
||||
order.setTransferWarehouseId(request.warehouseId());
|
||||
order.setTransferWarehouseCode(request.warehouseCode());
|
||||
order.setTransferWarehouseName(request.warehouseName());
|
||||
order.setCurrentStatus(DESTINATION_ASSIGNED);
|
||||
order.setUpdatedAt(now);
|
||||
orderMapper.updateById(order);
|
||||
|
||||
LastlegOrderDetail detailUpdate = new LastlegOrderDetail();
|
||||
detailUpdate.setDestinationWarehouseId(request.warehouseId());
|
||||
detailUpdate.setDestinationWarehouseCode(request.warehouseCode());
|
||||
detailUpdate.setDestinationWarehouseName(request.warehouseName());
|
||||
detailMapper.update(detailUpdate, new LambdaQueryWrapper<LastlegOrderDetail>()
|
||||
.eq(LastlegOrderDetail::getOrderId, order.getId()));
|
||||
|
||||
LastlegOrderStatusHistory history = new LastlegOrderStatusHistory();
|
||||
history.setOrderId(order.getId());
|
||||
history.setFromStatus(fromStatus);
|
||||
history.setToStatus(DESTINATION_ASSIGNED);
|
||||
history.setRemark(StringUtils.hasText(request.remark())
|
||||
? request.remark() : "Destination warehouse assigned: " + request.warehouseCode());
|
||||
history.setCreatedAt(now);
|
||||
historyMapper.insert(history);
|
||||
}
|
||||
return orders.size();
|
||||
}
|
||||
|
||||
public byte[] exportCsv(String customerId, LastlegOrderRequests.Query query) {
|
||||
List<LastlegOrder> orders = orderMapper.selectList(filter(customerId, query)
|
||||
.orderByDesc(LastlegOrder::getCreatedAt).last("LIMIT 10000"));
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add("ID,尾程单号,订单类型,入仓码,头程单号,货主,海外仓,转仓,转仓编码,FBA仓库,集装箱号,货柜状态,是否异常,是否暂扣,托数,箱数,已拣箱数,已出库箱数,剩余箱数,体积(m3),数量,体积重量,预计到港时间,实际到港时间,预计到仓,入库单号,出库单号,派送单号,换标单号,换标状态,合托标记,主流程状态,子单状态,备注(仅自己),备注(客户),备注(仓库)");
|
||||
for (LastlegOrder order : orders) {
|
||||
lines.add(String.join(",", List.of(
|
||||
csv(order.getId()), csv(order.getOrderNo()), csv(order.getOrderType()), csv(order.getInboundCode()),
|
||||
csv(order.getSourceOrderNo()), csv(order.getCustomerName()), csv(order.getOverseasWarehouseName()),
|
||||
csv(order.getTransferWarehouseName()), csv(order.getTransferWarehouseCode()), csv(order.getFbaWarehouseCode()),
|
||||
csv(order.getContainerNo()), csv(order.getContainerStatus()), csv(order.getAbnormal()), csv(order.getDetained()),
|
||||
csv(order.getPalletCount()), csv(order.getPackageCount()), csv(order.getPickedPackageCount()),
|
||||
csv(order.getShippedPackageCount()), csv(Math.max(0, Objects.requireNonNullElse(order.getPackageCount(), 0)
|
||||
- Objects.requireNonNullElse(order.getShippedPackageCount(), 0))),
|
||||
csv(order.getVolumeCbm()), csv(order.getProductQuantity()),
|
||||
csv(order.getVolumeWeightKg()), csv(order.getEstimatedArrivalAt()), csv(order.getActualArrivalAt()),
|
||||
csv(order.getEstimatedWarehouseAt()), csv(order.getInboundOrderNo()), csv(order.getOutboundOrderNo()),
|
||||
csv(order.getDeliveryOrderNo()), csv(order.getRelabelOrderNo()), csv(order.getRelabelStatus()),
|
||||
csv(order.getPalletMergeFlag()), csv(order.getCurrentStatus()), csv(order.getSubOrderStatus()), csv(order.getPrivateRemark()),
|
||||
csv(order.getCustomerRemark()), csv(order.getWarehouseRemark()))));
|
||||
}
|
||||
return ("\uFEFF" + String.join("\r\n", lines)).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<LastlegOrder> filter(String customerId, LastlegOrderRequests.Query query) {
|
||||
if (query.createdFrom() != null && query.createdTo() != null && query.createdFrom().isAfter(query.createdTo())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "createdFrom must not be after createdTo");
|
||||
}
|
||||
return new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(LastlegOrder::getCustomerId, customerId)
|
||||
.like(StringUtils.hasText(query.orderNo()), LastlegOrder::getOrderNo, query.orderNo())
|
||||
.like(StringUtils.hasText(query.inboundCode()), LastlegOrder::getInboundCode, query.inboundCode())
|
||||
.like(StringUtils.hasText(query.sourceOrderNo()), LastlegOrder::getSourceOrderNo, query.sourceOrderNo())
|
||||
.like(StringUtils.hasText(query.containerNo()), LastlegOrder::getContainerNo, query.containerNo())
|
||||
.eq(StringUtils.hasText(query.status()), LastlegOrder::getCurrentStatus, query.status())
|
||||
.eq(StringUtils.hasText(query.transferWarehouseCode()), LastlegOrder::getTransferWarehouseCode,
|
||||
query.transferWarehouseCode())
|
||||
.eq(query.abnormal() != null, LastlegOrder::getAbnormal, query.abnormal())
|
||||
.eq(query.detained() != null, LastlegOrder::getDetained, query.detained())
|
||||
.ge(query.createdFrom() != null, LastlegOrder::getCreatedAt, query.createdFrom())
|
||||
.le(query.createdTo() != null, LastlegOrder::getCreatedAt, query.createdTo());
|
||||
}
|
||||
|
||||
private LastlegOrder findOwned(String customerId, long orderId) {
|
||||
LastlegOrder order = orderMapper.selectOne(new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(LastlegOrder::getCustomerId, customerId).eq(LastlegOrder::getId, orderId));
|
||||
if (order == null) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Tail-leg order not found");
|
||||
return order;
|
||||
}
|
||||
|
||||
private void ensureTransferable(LastlegOrder order) {
|
||||
if (StringUtils.hasText(order.getInboundOrderNo()) || INBOUND_PLANNED.equals(order.getCurrentStatus())) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT,
|
||||
"Destination warehouse cannot be changed after inbound plan creation: " + order.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private LastlegOrderSummary summary(LastlegOrder o) {
|
||||
return new LastlegOrderSummary(o.getId(), o.getOrderNo(), o.getOrderType(), o.getSourceInboundId(),
|
||||
o.getInboundCode(), o.getSourceOrderNo(), o.getCustomerName(), o.getOverseasWarehouseId(),
|
||||
o.getOverseasWarehouseName(), o.getTransferWarehouseId(), o.getTransferWarehouseCode(),
|
||||
o.getTransferWarehouseName(), o.getFbaWarehouseCode(), o.getContainerNo(), o.getContainerStatus(),
|
||||
o.getAbnormal(), o.getDetained(), o.getPalletCount(), o.getPackageCount(), o.getPickedPackageCount(),
|
||||
o.getShippedPackageCount(), Math.max(0, Objects.requireNonNullElse(o.getPackageCount(), 0)
|
||||
- Objects.requireNonNullElse(o.getShippedPackageCount(), 0)), o.getVolumeCbm(),
|
||||
o.getProductQuantity(), o.getVolumeWeightKg(), o.getEstimatedArrivalAt(), o.getActualArrivalAt(),
|
||||
o.getEstimatedWarehouseAt(), o.getInboundOrderNo(), o.getOutboundOrderNo(), o.getDeliveryOrderNo(),
|
||||
o.getRelabelOrderNo(), o.getRelabelStatus(), o.getPalletMergeFlag(), o.getCurrentStatus(), o.getSubOrderStatus(),
|
||||
o.getPrivateRemark(), o.getCustomerRemark(), o.getWarehouseRemark(), o.getCreatedAt());
|
||||
}
|
||||
|
||||
private static String csv(Object value) {
|
||||
if (value == null) return "";
|
||||
String text = String.valueOf(value);
|
||||
if (!text.isEmpty() && "=+-@".indexOf(text.charAt(0)) >= 0) text = "'" + text;
|
||||
return '"' + text.replace("\"", "\"\"") + '"';
|
||||
}
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
package com.baiyue.lastleg.service;
|
||||
|
||||
import com.baiyue.common.event.*;
|
||||
import com.baiyue.lastleg.entity.*;
|
||||
import com.baiyue.lastleg.mapper.*;
|
||||
import com.baiyue.lastleg.dropship.dto.DropshipRequests;
|
||||
import com.baiyue.lastleg.dropship.service.DropshipOrderService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
@Service
|
||||
public class LoadingCompletedConsumer {
|
||||
@Resource private LastlegEventInboxMapper inboxMapper;
|
||||
@Resource private LastlegOrderMapper orderMapper;
|
||||
@Resource private LastlegOrderDetailMapper detailMapper;
|
||||
@Resource private LastlegOrderStatusHistoryMapper historyMapper;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Resource private LastlegOrderNoGenerator orderNoGenerator;
|
||||
@Resource private DropshipOrderService dropshipOrders;
|
||||
|
||||
@Transactional
|
||||
public int consume(EventEnvelope envelope) {
|
||||
envelope.requireValid();
|
||||
if(!EventTypes.FIRST_LEG_LOADING_PLAN_COMPLETED.equals(envelope.eventType())||envelope.eventVersion()!=1)
|
||||
throw new IllegalArgumentException("不支持的尾程交接事件");
|
||||
if (envelope.sourceRegion() != RegionCode.CN || envelope.targetRegion() != RegionCode.OVERSEAS)
|
||||
throw new IllegalArgumentException("尾程交接事件区域无效");
|
||||
if (!"LOADING_PLAN".equals(envelope.aggregateType()))
|
||||
throw new IllegalArgumentException("尾程交接事件聚合类型无效");
|
||||
FirstLegLoadingPlanCompletedV1 payload = parsePayload(envelope);
|
||||
if(payload.containerNo()==null||payload.containerNo().isBlank())
|
||||
throw new IllegalArgumentException("事件没有实际集装箱号 containerNo");
|
||||
if(payload.orders()==null||payload.orders().isEmpty()) throw new IllegalArgumentException("事件没有入仓单");
|
||||
LastlegEventInbox existing=inboxMapper.selectById(envelope.eventId());
|
||||
if(existing!=null) {
|
||||
if(!existing.getPayloadHash().equalsIgnoreCase(envelope.payloadHash())) throw new IllegalArgumentException("事件摘要冲突");
|
||||
return countOrders(envelope,payload.containerNo());
|
||||
}
|
||||
|
||||
LastlegEventInbox inbox=new LastlegEventInbox();
|
||||
inbox.setEventId(envelope.eventId()); inbox.setCustomerId(envelope.tenantId()); inbox.setEventType(envelope.eventType());
|
||||
inbox.setEventVersion(envelope.eventVersion()); inbox.setPayloadHash(envelope.payloadHash());
|
||||
inbox.setPayloadJson(envelope.payload().toString()); inbox.setStatus("RECEIVED"); inbox.setReceivedAt(LocalDateTime.now());
|
||||
try { inboxMapper.insert(inbox); } catch(DuplicateKeyException exception) {
|
||||
return countOrders(envelope,payload.containerNo());
|
||||
}
|
||||
|
||||
String orderNo = orderNoGenerator.forContainerNo(payload.containerNo());
|
||||
JsonNode rawOrders = envelope.payload().path("orders");
|
||||
for (int index = 0; index < payload.orders().size(); index++) {
|
||||
var source = payload.orders().get(index);
|
||||
JsonNode rawSource = rawOrders.isArray() && index < rawOrders.size() ? rawOrders.get(index) : objectMapper.createObjectNode();
|
||||
createOrder(envelope, payload, source, orderNo, rawSource);
|
||||
}
|
||||
inbox.setStatus("PROCESSED"); inbox.setProcessedAt(LocalDateTime.now()); inboxMapper.updateById(inbox);
|
||||
return payload.orders().size();
|
||||
}
|
||||
|
||||
private FirstLegLoadingPlanCompletedV1 parsePayload(EventEnvelope envelope) {
|
||||
try {
|
||||
ObjectMapper compatibleMapper = objectMapper.copy()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
return compatibleMapper.treeToValue(envelope.payload(), FirstLegLoadingPlanCompletedV1.class);
|
||||
}
|
||||
catch(Exception exception) { throw new IllegalArgumentException("尾程事件负载无效",exception); }
|
||||
}
|
||||
|
||||
private int countOrders(EventEnvelope envelope,String containerNo) {
|
||||
return Math.toIntExact(orderMapper.countContainer(envelope.tenantId(),containerNo));
|
||||
}
|
||||
|
||||
private void createOrder(EventEnvelope event,FirstLegLoadingPlanCompletedV1 payload,
|
||||
FirstLegLoadingPlanCompletedV1.Order source,String orderNo, JsonNode rawSource) {
|
||||
String orderHash=EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(rawSource));
|
||||
LastlegOrder existing=orderMapper.findSource(event.tenantId(),event.sourceRegion().name(),source.sourceInboundId());
|
||||
if(existing!=null) {
|
||||
if(!Objects.equals(existing.getSourcePayloadHash(),orderHash)) throw new IllegalStateException("来源入仓单快照冲突: "+source.sourceInboundId());
|
||||
return;
|
||||
}
|
||||
LastlegOrder order=new LastlegOrder();
|
||||
String orderType = source.orderType();
|
||||
String customerId = source.customerId() == null || source.customerId().isBlank()
|
||||
? event.tenantId() : source.customerId();
|
||||
if (!event.tenantId().equals(customerId)) {
|
||||
throw new IllegalArgumentException("Order customerId does not match the trusted event customer");
|
||||
}
|
||||
List<DropshipRequests.Pallet> dropshipPallets = "ONE_CLICK_DROPSHIP".equals(orderType)
|
||||
? pallets(source, rawSource) : List.of();
|
||||
if ("ONE_CLICK_DROPSHIP".equals(orderType) && dropshipPallets.isEmpty()) {
|
||||
throw new IllegalArgumentException("One-click dropship event has no pallet snapshot");
|
||||
}
|
||||
order.setId(IdWorker.getId()); order.setOrderNo(orderNo); order.setCustomerId(customerId); order.setOrderType(orderType);
|
||||
order.setInboundCode(source.inboundCode()); order.setSourceRegion(event.sourceRegion().name());
|
||||
order.setSourceInboundId(source.sourceInboundId()); order.setRootInboundId(source.rootInboundId());
|
||||
order.setSourceOrderId(source.sourceOrderId()); order.setSourceOrderNo(source.sourceOrderNo()); order.setRootOrderId(source.rootOrderId());
|
||||
order.setOverseasWarehouseId(payload.warehouseId()); order.setOverseasWarehouseName(payload.warehouseName());
|
||||
order.setFbaWarehouseCode(source.destinationWarehouseCode()); order.setContainerNo(payload.containerNo());
|
||||
order.setContainerStatus("待转柜"); order.setAbnormal(false); order.setDetained(false);
|
||||
order.setPackageCount(source.packageCount()); order.setPalletCount(dropshipPallets.size());
|
||||
order.setProductQuantity(source.productQuantity()); order.setVolumeCbm(source.volumeCbm());
|
||||
order.setVolumeWeightKg(source.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()); order.setSourceLoadingPlanVersion(event.aggregateVersion());
|
||||
order.setSourceHandoverVersion(event.handoverVersion()); order.setSourcePayloadHash(orderHash);
|
||||
order.setCreatedAt(LocalDateTime.now()); order.setUpdatedAt(LocalDateTime.now());
|
||||
try {
|
||||
orderMapper.insert(order);
|
||||
} catch (DuplicateKeyException exception) {
|
||||
LastlegOrder raced = orderMapper.findSource(event.tenantId(), event.sourceRegion().name(),
|
||||
source.sourceInboundId());
|
||||
if (raced != null && Objects.equals(raced.getSourcePayloadHash(), orderHash)) return;
|
||||
throw exception;
|
||||
}
|
||||
|
||||
for(var item:source.items()) {
|
||||
LastlegOrderDetail detail=new LastlegOrderDetail();
|
||||
detail.setOrderId(order.getId()); detail.setSourceInboundDetailId(item.sourceInboundDetailId()); detail.setSkuCode(item.skuCode());
|
||||
detail.setProductNameZh(item.productNameZh()); detail.setProductNameEn(item.productNameEn()); detail.setPackageCount(item.packageCount());
|
||||
detail.setProductQuantity(item.productQuantity()); detail.setPalletCount(item.palletCount()); detail.setGrossWeightKg(item.grossWeightKg());
|
||||
detail.setNetWeightKg(item.netWeightKg()); detail.setDimensionalWeightKg(item.dimensionalWeightKg()); detail.setVolumeCbm(item.volumeCbm());
|
||||
detail.setDestinationWarehouseId(item.destinationWarehouseId()); detail.setDestinationWarehouseCode(item.destinationWarehouseCode());
|
||||
detail.setDestinationWarehouseName(item.destinationWarehouseName()); detail.setFbaTrackingNo(item.fbaTrackingNo());
|
||||
detail.setShipmentNo(item.shipmentNo()); detail.setShippingMark(item.shippingMark()); detailMapper.insert(detail);
|
||||
}
|
||||
LastlegOrderStatusHistory history=new LastlegOrderStatusHistory();
|
||||
history.setOrderId(order.getId()); history.setToStatus("PENDING_ASSIGNMENT"); history.setEventId(event.eventId());
|
||||
history.setRemark("头程装柜完成自动生成"); history.setCreatedAt(LocalDateTime.now()); historyMapper.insert(history);
|
||||
if ("ONE_CLICK_DROPSHIP".equals(orderType)) {
|
||||
long warehouseId;
|
||||
try { warehouseId = Long.parseLong(payload.warehouseId()); }
|
||||
catch (Exception exception) { throw new IllegalArgumentException("One-click dropship warehouseId is invalid", exception); }
|
||||
dropshipOrders.receive(new DropshipRequests.Receive(customerId, order.getId(), warehouseId,
|
||||
event.eventId() + ":DROPSHIP", dropshipPallets));
|
||||
}
|
||||
}
|
||||
|
||||
private List<DropshipRequests.Pallet> pallets(FirstLegLoadingPlanCompletedV1.Order source, JsonNode rawSource) {
|
||||
if (source.pallets() != null && !source.pallets().isEmpty()) {
|
||||
return source.pallets().stream().map(pallet -> new DropshipRequests.Pallet(pallet.palletNo(),
|
||||
pallet.skuPackages().stream().map(sku -> new DropshipRequests.SkuPackages(
|
||||
sku.skuCode(), sku.productName(), sku.packageCount(), sku.productQuantity(),
|
||||
sku.cartonMarks())).toList())).toList();
|
||||
}
|
||||
return legacyPallets(rawSource);
|
||||
}
|
||||
|
||||
private List<DropshipRequests.Pallet> legacyPallets(JsonNode source) {
|
||||
List<DropshipRequests.Pallet> result = new ArrayList<>();
|
||||
JsonNode nodes = source.path("pallets");
|
||||
if (nodes.isArray()) {
|
||||
for (JsonNode pallet : nodes) {
|
||||
String palletNo = pallet.path("palletNo").asText(pallet.path("sourcePalletNo").asText(null));
|
||||
List<DropshipRequests.SkuPackages> skuRows = skuPackages(pallet);
|
||||
if (palletNo != null && !palletNo.isBlank() && !skuRows.isEmpty()) {
|
||||
result.add(new DropshipRequests.Pallet(palletNo, skuRows));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
String palletNo = source.path("palletNo").asText(source.path("sourcePalletNo").asText(null));
|
||||
List<DropshipRequests.SkuPackages> skuRows = skuPackages(source);
|
||||
if (palletNo != null && !palletNo.isBlank() && !skuRows.isEmpty()) {
|
||||
result.add(new DropshipRequests.Pallet(palletNo, skuRows));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<DropshipRequests.SkuPackages> skuPackages(JsonNode source) {
|
||||
List<DropshipRequests.SkuPackages> result = new ArrayList<>();
|
||||
JsonNode nodes = source.path("skuPackages");
|
||||
if (!nodes.isArray()) nodes = source.path("items");
|
||||
if (!nodes.isArray()) return result;
|
||||
for (JsonNode item : nodes) {
|
||||
String sku = item.path("skuCode").asText(null);
|
||||
long packageCount = item.path("packageCount").asLong(0);
|
||||
long productQuantity = item.path("productQuantity").asLong(0);
|
||||
JsonNode markNodes = item.path("cartonMarks");
|
||||
if (!markNodes.isArray()) markNodes = item.path("boxMarks");
|
||||
if (!markNodes.isArray()) markNodes = item.path("cartonNos");
|
||||
List<String> marks = new ArrayList<>();
|
||||
if (markNodes.isArray()) {
|
||||
for (JsonNode mark : markNodes) if (!mark.asText().isBlank()) marks.add(mark.asText());
|
||||
}
|
||||
if (sku != null && !sku.isBlank() && packageCount > 0 && productQuantity > 0 && !marks.isEmpty()) {
|
||||
String productName = item.path("productName").asText(item.path("productNameZh").asText(null));
|
||||
result.add(new DropshipRequests.SkuPackages(sku, productName, packageCount, productQuantity, marks));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Business orchestration and transaction boundaries for last-leg delivery.
|
||||
* / 尾程配送业务编排与事务边界。
|
||||
*/
|
||||
package com.baiyue.lastleg.service;
|
||||
Reference in New Issue
Block a user