优化
This commit is contained in:
-8
@@ -64,14 +64,6 @@ public class CustomerVisitController {
|
||||
return ApiResponse.success(service.upload(requiredScope(user, customerId), user.userId(), visitId, file));
|
||||
}
|
||||
|
||||
@PostMapping("/delivery-orders/{deliveryNo}/complete")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.DELIVERY_STATUS + "')")
|
||||
public ApiResponse<CustomerVisitResponses.Completed> completeDelivery(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam(required = false) String customerId,
|
||||
@PathVariable String deliveryNo, @Valid @RequestBody CustomerVisitRequests.DeliveryStatus request) {
|
||||
return ApiResponse.success(service.completeDelivery(requiredScope(user, customerId), operator(user), deliveryNo, request));
|
||||
}
|
||||
|
||||
@PutMapping("/delivery-orders/{deliveryNo}/status")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.DELIVERY_STATUS + "')")
|
||||
public ApiResponse<CustomerVisitResponses.Completed> updateDeliveryStatus(
|
||||
|
||||
+4
-34
@@ -76,39 +76,6 @@ public class CustomerVisitService {
|
||||
return detail(visit);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CustomerVisitResponses.Completed completeDelivery(String customerId, String operatorId, String deliveryNo,
|
||||
CustomerVisitRequests.DeliveryStatus request) {
|
||||
if (!"DELIVERED".equals(trim(request.status()).toUpperCase(Locale.ROOT))) {
|
||||
throw badRequest("派送完成接口只接受 DELIVERED 状态");
|
||||
}
|
||||
DeliveryOrder delivery = deliveryOrders.lockByNo(customerId, deliveryNo);
|
||||
if (delivery == null) throw notFound("派送单不存在");
|
||||
boolean replayed = "DELIVERED".equals(delivery.getStatus());
|
||||
if (!replayed) {
|
||||
requireVersion(delivery, request.expectedVersion());
|
||||
requireDeliveryTransition(delivery.getStatus(), "DELIVERED");
|
||||
}
|
||||
LocalDateTime now = replayed && delivery.getSignedAt() != null ? delivery.getSignedAt() : LocalDateTime.now();
|
||||
if (!replayed) {
|
||||
delivery.setStatus("DELIVERED");
|
||||
delivery.setSignedAt(now);
|
||||
delivery.setUpdatedAt(now);
|
||||
delivery.setVersion(version(delivery) + 1);
|
||||
deliveryOrders.updateById(delivery);
|
||||
for (LastlegOrder order : lastMileOrders.selectList(new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(LastlegOrder::getCustomerId, customerId)
|
||||
.eq(LastlegOrder::getDeliveryOrderNo, deliveryNo))) {
|
||||
order.setSubOrderStatus("DELIVERY:DELIVERED");
|
||||
order.setUpdatedAt(now);
|
||||
lastMileOrders.updateById(order);
|
||||
}
|
||||
recordOperation(delivery, operatorId, "COMPLETE_DELIVERY", "派送完成并生成客户回访记录", now);
|
||||
}
|
||||
CustomerVisit visit = ensureVisit(delivery, now);
|
||||
return new CustomerVisitResponses.Completed(visit.getId(), deliveryNo, delivery.getStatus(), visit.getStatus(), replayed);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CustomerVisitResponses.Completed updateDeliveryStatus(String customerId, String operatorId,
|
||||
String deliveryNo,
|
||||
@@ -117,7 +84,6 @@ public class CustomerVisitService {
|
||||
if (!DeliveryStatusPolicy.isEditableTarget(target)) {
|
||||
throw badRequest("不支持的派送状态");
|
||||
}
|
||||
if ("DELIVERED".equals(target)) return completeDelivery(customerId, operatorId, deliveryNo, request);
|
||||
DeliveryOrder delivery = deliveryOrders.lockByNo(customerId, deliveryNo);
|
||||
if (delivery == null) throw notFound("派送单不存在");
|
||||
if (target.equals(delivery.getStatus())) {
|
||||
@@ -192,6 +158,10 @@ public class CustomerVisitService {
|
||||
uploaded.contentType(), uploaded.sizeBytes());
|
||||
}
|
||||
|
||||
public void ensureForCompletedDelivery(DeliveryOrder delivery, LocalDateTime completedAt) {
|
||||
ensureVisit(delivery, completedAt);
|
||||
}
|
||||
|
||||
private CustomerVisit ensureVisit(DeliveryOrder delivery, LocalDateTime completedAt) {
|
||||
CustomerVisit existing = visits.findByDelivery(delivery.getCustomerId(), delivery.getId());
|
||||
if (existing != null) return existing;
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public class DeliveryTransferController {
|
||||
|
||||
@PostMapping("/dispatches/{dispatchNo}/transfer-delivery")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.DISPATCH_TRANSFER_DELIVERY + "')")
|
||||
@Operation(summary = "Create one pending outbound order and one pending delivery order")
|
||||
@Operation(summary = "Create one pending outbound order and one delivery order per destination FBA warehouse")
|
||||
public ApiResponse<DeliveryResponses.TransferResult> transfer(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String dispatchNo,
|
||||
@Valid @RequestBody DeliveryRequests.Transfer request) {
|
||||
|
||||
+10
-4
@@ -7,9 +7,13 @@ import java.util.List;
|
||||
public final class DeliveryResponses {
|
||||
private DeliveryResponses() {}
|
||||
|
||||
public record TransferDelivery(String deliveryNo, String deliveryStatus,
|
||||
String destinationWarehouseId, String destinationWarehouseCode,
|
||||
String destinationWarehouseName, int palletCount, int packageCount) {}
|
||||
|
||||
public record TransferResult(String dispatchNo, String outboundNo, String outboundStatus,
|
||||
String deliveryNo, String deliveryStatus, int palletCount,
|
||||
int packageCount, boolean replayed) {}
|
||||
int palletCount, int packageCount,
|
||||
List<TransferDelivery> deliveries, boolean replayed) {}
|
||||
|
||||
public record OutboundRow(Long id, String outboundNo, String dispatchNo, String overseasWarehouseId, String lxCodes,
|
||||
String status, int palletCount, int packageCount, LocalDateTime createdAt, String outboundType) {}
|
||||
@@ -25,7 +29,9 @@ public final class DeliveryResponses {
|
||||
LocalDateTime plannedDeliveryAt, LocalDateTime dispatchedAt,
|
||||
LocalDateTime expectedArrivalAt, LocalDateTime signedAt,
|
||||
String remark, LocalDateTime createdAt, long version, List<String> allowedActions,
|
||||
String bolNo, DeliveryDocumentDtos.Summary documentSummary) {}
|
||||
String bolNo, DeliveryDocumentDtos.Summary documentSummary,
|
||||
String destinationWarehouseId, String destinationWarehouseCode,
|
||||
String destinationWarehouseName) {}
|
||||
|
||||
public record DeliverySku(String inboundCode, String customerName, String destinationWarehouseCode,
|
||||
String receivingAddress, String shipmentNo, String fbaTrackingNo,
|
||||
@@ -45,7 +51,7 @@ public final class DeliveryResponses {
|
||||
public record OutboundPalletRow(Long id, String palletNo, String cartonMarks, String inboundCode,
|
||||
String destinationWarehouseCode, String destinationWarehouseName,
|
||||
String skuCode, int packageCount, int pickedPackageCount, String status) {}
|
||||
public record OutboundDetail(OutboundRow outbound, DeliveryRow delivery, List<OutboundPalletRow> pallets) {}
|
||||
public record OutboundDetail(OutboundRow outbound, List<DeliveryRow> deliveries, List<OutboundPalletRow> pallets) {}
|
||||
public record OneClickCandidate(Long inventoryPositionId, String orderNo, String customerId, String customerName, String overseasWarehouseId,
|
||||
String overseasWarehouseName, String palletNo, String inboundCode,
|
||||
String skuCode, String productName, long availablePackages) {}
|
||||
|
||||
@@ -23,6 +23,10 @@ public class DeliveryOrder {
|
||||
private String overseasWarehouseId;
|
||||
private String overseasWarehouseName;
|
||||
private String customerName;
|
||||
private String destinationWarehouseId;
|
||||
private String destinationWarehouseCode;
|
||||
private String destinationWarehouseName;
|
||||
private String deliveryGroupKey;
|
||||
private String status;
|
||||
private String waybillNo;
|
||||
private String bolNo;
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@ import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DeliveryOrderMapper extends BaseMapper<DeliveryOrder> {
|
||||
@@ -14,6 +15,6 @@ public interface DeliveryOrderMapper extends BaseMapper<DeliveryOrder> {
|
||||
DeliveryOrder lockByNo(@Param("customerId") String customerId, @Param("deliveryNo") String deliveryNo);
|
||||
@Select("SELECT * FROM ll_delivery_order WHERE customer_id=#{customerId} AND idempotency_key=#{key} LIMIT 1")
|
||||
DeliveryOrder findByIdempotency(@Param("customerId") String customerId, @Param("key") String key);
|
||||
@Select("SELECT * FROM ll_delivery_order WHERE customer_id=#{customerId} AND outbound_order_id=#{outboundOrderId} LIMIT 1 FOR UPDATE")
|
||||
DeliveryOrder lockByOutbound(@Param("customerId") String customerId, @Param("outboundOrderId") Long outboundOrderId);
|
||||
@Select("SELECT * FROM ll_delivery_order WHERE customer_id=#{customerId} AND outbound_order_id=#{outboundOrderId} ORDER BY id FOR UPDATE")
|
||||
List<DeliveryOrder> lockByOutbound(@Param("customerId") String customerId, @Param("outboundOrderId") Long outboundOrderId);
|
||||
}
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import java.util.Set;
|
||||
|
||||
public final class DeliveryStatusPolicy {
|
||||
private static final Set<String> EDITABLE_TARGETS = Set.of(
|
||||
"WAIT_DELIVERY", "DELIVERING", "DELIVERED", "REJECTED", "CANCELLED");
|
||||
"WAIT_DELIVERY", "DELIVERING", "REJECTED", "CANCELLED");
|
||||
private static final Map<String, Set<String>> TRANSITIONS = Map.of(
|
||||
"WAIT_DELIVERY", Set.of("DELIVERING", "DELIVERED", "REJECTED", "CANCELLED"),
|
||||
"DELIVERING", Set.of("DELIVERED", "REJECTED"),
|
||||
@@ -29,8 +29,8 @@ public final class DeliveryStatusPolicy {
|
||||
public static List<String> allowedActions(String status) {
|
||||
return switch (status == null ? "" : status) {
|
||||
case "WAIT_DELIVERY" -> List.of(
|
||||
"START_DELIVERY", "COMPLETE_DELIVERY", "REJECT_DELIVERY", "CANCEL_DELIVERY");
|
||||
case "DELIVERING" -> List.of("COMPLETE_DELIVERY", "REJECT_DELIVERY", "MARK_RETURN");
|
||||
"START_DELIVERY", "REJECT_DELIVERY", "CANCEL_DELIVERY");
|
||||
case "DELIVERING" -> List.of("REJECT_DELIVERY", "MARK_RETURN");
|
||||
case "DELIVERED", "REJECTED" -> List.of("MARK_RETURN");
|
||||
default -> List.of();
|
||||
};
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package com.baiyue.lastleg.delivery.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.EventIdentifiers;
|
||||
import com.baiyue.common.event.EventPayloadCanonicalizer;
|
||||
import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.OverseasDeliveryCompletedV1;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||
import com.baiyue.lastleg.entity.LastlegEventOutbox;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.mapper.LastlegEventOutboxMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/** 在派送完成事务内按来源入仓单冻结海外到国内的完成事件。 */
|
||||
@Service
|
||||
public class DeliveryCompletedOutboxService {
|
||||
@Resource private LastlegEventOutboxMapper outbox;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
public void publish(DeliveryOrder delivery, List<LastlegOrder> orders, Instant completedAt) {
|
||||
Objects.requireNonNull(delivery, "delivery");
|
||||
Objects.requireNonNull(completedAt, "completedAt");
|
||||
Map<String, LastlegOrder> bySourceInbound = new LinkedHashMap<>();
|
||||
for (LastlegOrder order : orders == null ? List.<LastlegOrder>of() : orders) {
|
||||
if (!StringUtils.hasText(order.getSourceInboundId())) continue;
|
||||
requireText(order.getSourceOrderId(), "sourceOrderId");
|
||||
requireText(order.getInboundCode(), "inboundCode");
|
||||
requireText(order.getCustomerId(), "customerId");
|
||||
LastlegOrder existing = bySourceInbound.putIfAbsent(order.getSourceInboundId(), order);
|
||||
if (existing != null && (!Objects.equals(existing.getSourceOrderId(), order.getSourceOrderId())
|
||||
|| !Objects.equals(existing.getInboundCode(), order.getInboundCode()))) {
|
||||
throw new IllegalStateException("同一来源入仓单对应的派送完成快照冲突");
|
||||
}
|
||||
}
|
||||
for (LastlegOrder order : bySourceInbound.values()) publishOne(delivery, order, completedAt);
|
||||
}
|
||||
|
||||
private void publishOne(DeliveryOrder delivery, LastlegOrder order, Instant completedAt) {
|
||||
long aggregateVersion = Math.max(1L, delivery.getVersion() == null ? 1L : delivery.getVersion());
|
||||
String eventKey = EventTypes.OVERSEAS_DELIVERY_COMPLETED + ':' + delivery.getDeliveryNo()
|
||||
+ ':' + order.getSourceInboundId();
|
||||
var payload = new OverseasDeliveryCompletedV1(String.valueOf(delivery.getId()), delivery.getDeliveryNo(),
|
||||
String.valueOf(order.getId()), order.getOrderNo(), order.getSourceInboundId(), order.getInboundCode(),
|
||||
order.getSourceOrderId(), order.getSourceOrderNo(), order.getRootOrderId(),
|
||||
order.getFbaWarehouseCode(), completedAt);
|
||||
JsonNode node = objectMapper.valueToTree(payload);
|
||||
EventEnvelope envelope = new EventEnvelope(EventIdentifiers.newUuid(),
|
||||
EventTypes.OVERSEAS_DELIVERY_COMPLETED, 1, completedAt, RegionCode.OVERSEAS, RegionCode.CN,
|
||||
order.getCustomerId(), "INBOUND_ORDER", order.getSourceInboundId(), aggregateVersion, 1L,
|
||||
EventIdentifiers.newUuid(), eventKey,
|
||||
EventIdentifiers.isSupported(order.getSourceEventId()) ? order.getSourceEventId() : null,
|
||||
node, EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(node))).requireValid();
|
||||
LastlegEventOutbox row = new LastlegEventOutbox();
|
||||
row.setId(IdWorker.getId()); row.setCustomerId(order.getCustomerId());
|
||||
row.setAggregateType(envelope.aggregateType()); row.setAggregateId(envelope.aggregateId());
|
||||
row.setEventType(envelope.eventType()); row.setEventVersion(envelope.eventVersion());
|
||||
row.setEventKey(eventKey); row.setPayloadJson(json(envelope)); row.setStatus("PENDING");
|
||||
row.setRetryCount(0); row.setCreatedAt(LocalDateTime.ofInstant(completedAt, ZoneOffset.UTC));
|
||||
try {
|
||||
outbox.insert(row);
|
||||
} catch (DuplicateKeyException duplicate) {
|
||||
LastlegEventOutbox existing = outbox.findByEventKey(eventKey);
|
||||
if (existing == null || !Objects.equals(existing.getAggregateId(), order.getSourceInboundId())
|
||||
|| !Objects.equals(existing.getCustomerId(), order.getCustomerId())) throw duplicate;
|
||||
}
|
||||
}
|
||||
|
||||
private static void requireText(String value, String field) {
|
||||
if (!StringUtils.hasText(value)) throw new IllegalStateException(field + " is required");
|
||||
}
|
||||
|
||||
private String json(Object value) {
|
||||
try { return objectMapper.writeValueAsString(value); }
|
||||
catch (JsonProcessingException exception) {
|
||||
throw new IllegalStateException("Cannot serialize delivery-completed event", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -6,6 +6,10 @@ import com.baiyue.lastleg.delivery.entity.*;
|
||||
import com.baiyue.lastleg.delivery.gateway.DeliveryDocumentFileGateway;
|
||||
import com.baiyue.lastleg.delivery.mapper.*;
|
||||
import com.baiyue.lastleg.delivery.model.*;
|
||||
import com.baiyue.lastleg.customervisit.service.CustomerVisitService;
|
||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -17,6 +21,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@@ -26,6 +31,9 @@ public class DeliveryDocumentService {
|
||||
@Resource private DeliveryOperationMapper operations;
|
||||
@Resource private DeliveryDocumentFileGateway files;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Resource private LastlegOrderMapper lastlegOrders;
|
||||
@Resource private DeliveryCompletedOutboxService deliveryCompletedOutbox;
|
||||
@Resource private CustomerVisitService customerVisits;
|
||||
|
||||
public Detail detail(String customerId, String deliveryNo) { return detail(require(customerId, deliveryNo), false); }
|
||||
|
||||
@@ -69,7 +77,21 @@ public class DeliveryDocumentService {
|
||||
order.setDocumentsJson(write(categories));
|
||||
order.setVersion(version(order) + 1);
|
||||
order.setUpdatedAt(now);
|
||||
boolean receiptSaved = !bindings.getOrDefault(DeliveryDocumentCategory.RECEIPT, List.of()).isEmpty();
|
||||
boolean autoCompleted = receiptSaved && !"DELIVERED".equals(order.getStatus());
|
||||
if (autoCompleted) {
|
||||
if (!DeliveryStatusPolicy.canTransition(order.getStatus(), "DELIVERED")) {
|
||||
throw conflict("当前派送状态不允许通过签收文件完成");
|
||||
}
|
||||
order.setStatus("DELIVERED");
|
||||
order.setSignedAt(now);
|
||||
}
|
||||
orders.updateById(order);
|
||||
if (autoCompleted) {
|
||||
List<LastlegOrder> completedOrders = completeRelatedOrders(order, now);
|
||||
deliveryCompletedOutbox.publish(order, completedOrders, now.toInstant(ZoneOffset.UTC));
|
||||
customerVisits.ensureForCompletedDelivery(order, now);
|
||||
}
|
||||
Detail result = detail(order, false);
|
||||
DeliveryDocumentRevision revision = new DeliveryDocumentRevision();
|
||||
revision.setCustomerId(customerId); revision.setDeliveryOrderId(order.getId()); revision.setVersion(order.getVersion());
|
||||
@@ -79,6 +101,7 @@ public class DeliveryDocumentService {
|
||||
operation.setCustomerId(customerId); operation.setDeliveryOrderId(order.getId()); operation.setOperatorId(operatorId);
|
||||
operation.setOperationNode("SAVE_DOCUMENTS"); operation.setResult("保存派送资料:" + result.uploadedCategoryCount() + "/3类");
|
||||
operation.setCreatedAt(now); operations.insert(operation);
|
||||
if (autoCompleted) recordAutomaticCompletion(order, operatorId, now);
|
||||
return result;
|
||||
}
|
||||
public List<History> history(String customerId, String deliveryNo) {
|
||||
@@ -110,6 +133,25 @@ public class DeliveryDocumentService {
|
||||
(int) categories.stream().filter(c -> !c.files().isEmpty()).count(), 3, categories, List.of("MANAGE_DOCUMENTS"), replayed);
|
||||
}
|
||||
private static long version(DeliveryOrder order) { return Objects.requireNonNullElse(order.getVersion(), 0L); }
|
||||
private List<LastlegOrder> completeRelatedOrders(DeliveryOrder delivery, LocalDateTime now) {
|
||||
List<LastlegOrder> completedOrders = lastlegOrders.selectList(new LambdaQueryWrapper<LastlegOrder>()
|
||||
.eq(LastlegOrder::getCustomerId, delivery.getCustomerId())
|
||||
.eq(LastlegOrder::getDeliveryOrderNo, delivery.getDeliveryNo()));
|
||||
for (LastlegOrder order : completedOrders) {
|
||||
order.setSubOrderStatus("DELIVERY:DELIVERED");
|
||||
order.setUpdatedAt(now);
|
||||
lastlegOrders.updateById(order);
|
||||
}
|
||||
return completedOrders;
|
||||
}
|
||||
private void recordAutomaticCompletion(DeliveryOrder order, String operatorId, LocalDateTime now) {
|
||||
DeliveryOperation operation = new DeliveryOperation();
|
||||
operation.setCustomerId(order.getCustomerId()); operation.setDeliveryOrderId(order.getId());
|
||||
operation.setOperatorId(operatorId); operation.setOperatorName(operatorId);
|
||||
operation.setOperationNode("RECEIPT_AUTO_COMPLETE");
|
||||
operation.setResult("签收文件保存成功,派送单自动完成");
|
||||
operation.setCreatedAt(now); operations.insert(operation);
|
||||
}
|
||||
private String hash(Object value) {
|
||||
try { return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(write(value).getBytes(StandardCharsets.UTF_8))); }
|
||||
catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e); }
|
||||
|
||||
+73
-44
@@ -134,10 +134,10 @@ public class DeliveryTransferService {
|
||||
OutboundOrder replay = outboundOrders.findByIdempotency(customerId, request.idempotencyKey().trim());
|
||||
if (replay != null) {
|
||||
if (!dispatch.getId().equals(replay.getDispatchOrderId())) throw conflict("Idempotency key belongs to another dispatch");
|
||||
return result(replay, requireDelivery(customerId, replay.getId()), true);
|
||||
return result(replay, requireDeliveries(customerId, replay.getId()), true);
|
||||
}
|
||||
OutboundOrder existing = outboundOrders.lockByDispatch(customerId, dispatch.getId());
|
||||
if (existing != null) return result(existing, requireDelivery(customerId, existing.getId()), true);
|
||||
if (existing != null) return result(existing, requireDeliveries(customerId, existing.getId()), true);
|
||||
if (Set.of("CANCELLED", "DEPARTED", "PARTIAL_DEPARTED", "COMPLETED").contains(dispatch.getStatus())) {
|
||||
throw conflict("Dispatch cannot be transferred to delivery in its current status");
|
||||
}
|
||||
@@ -167,11 +167,11 @@ public class DeliveryTransferService {
|
||||
// the document created by the winning transaction rather than receive a false failure.
|
||||
OutboundOrder concurrent = outboundOrders.findByIdempotency(customerId, request.idempotencyKey().trim());
|
||||
if (concurrent != null && dispatch.getId().equals(concurrent.getDispatchOrderId())) {
|
||||
return result(concurrent, requireDelivery(customerId, concurrent.getId()), true);
|
||||
return result(concurrent, requireDeliveries(customerId, concurrent.getId()), true);
|
||||
}
|
||||
concurrent = outboundOrders.lockByDispatch(customerId, dispatch.getId());
|
||||
if (concurrent != null) {
|
||||
return result(concurrent, requireDelivery(customerId, concurrent.getId()), true);
|
||||
return result(concurrent, requireDeliveries(customerId, concurrent.getId()), true);
|
||||
}
|
||||
throw conflict("Dispatch was concurrently transferred to delivery");
|
||||
}
|
||||
@@ -195,43 +195,61 @@ public class DeliveryTransferService {
|
||||
outboundPallets.insert(item);
|
||||
}
|
||||
|
||||
DeliveryOrder delivery = new DeliveryOrder();
|
||||
delivery.setId(IdWorker.getId()); delivery.setCustomerId(customerId);
|
||||
delivery.setDeliveryNo(orderNoGenerator.next("DELIVERY", dispatch.getOverseasWarehouseId(), delivery.getId()));
|
||||
delivery.setOutboundOrderId(outbound.getId()); delivery.setOutboundNo(outbound.getOutboundNo());
|
||||
delivery.setDispatchOrderId(dispatch.getId()); delivery.setDispatchNo(dispatch.getDispatchNo());
|
||||
delivery.setOverseasWarehouseId(dispatch.getOverseasWarehouseId()); delivery.setOverseasWarehouseName(dispatch.getOverseasWarehouseName());
|
||||
Map<String, List<DispatchItem>> destinationGroups = items.stream().collect(java.util.stream.Collectors.groupingBy(
|
||||
this::deliveryGroupKey, LinkedHashMap::new, java.util.stream.Collectors.toList()));
|
||||
List<DeliveryOrder> deliveries = new ArrayList<>();
|
||||
int groupIndex = 0;
|
||||
for (Map.Entry<String, List<DispatchItem>> entry : destinationGroups.entrySet()) {
|
||||
List<DispatchItem> groupItems = entry.getValue();
|
||||
DispatchItem destination = groupItems.get(0);
|
||||
DeliveryOrder delivery = new DeliveryOrder();
|
||||
delivery.setId(IdWorker.getId()); delivery.setCustomerId(customerId);
|
||||
delivery.setDeliveryNo(orderNoGenerator.next("DELIVERY", dispatch.getOverseasWarehouseId(), delivery.getId()));
|
||||
delivery.setOutboundOrderId(outbound.getId()); delivery.setOutboundNo(outbound.getOutboundNo());
|
||||
delivery.setDispatchOrderId(dispatch.getId()); delivery.setDispatchNo(dispatch.getDispatchNo());
|
||||
delivery.setOverseasWarehouseId(dispatch.getOverseasWarehouseId()); delivery.setOverseasWarehouseName(dispatch.getOverseasWarehouseName());
|
||||
delivery.setDestinationWarehouseId(trim(destination.getDestinationWarehouseId()));
|
||||
delivery.setDestinationWarehouseCode(firstText(
|
||||
firstText(destination.getDestinationWarehouseCode(), destination.getFbaWarehouseCode()),
|
||||
destination.getDestinationWarehouseId()));
|
||||
delivery.setDestinationWarehouseName(trim(destination.getDestinationWarehouseName()));
|
||||
delivery.setDeliveryGroupKey(entry.getKey());
|
||||
LastlegOrder firstOrder = firstTailOrder(customerId, groupItems);
|
||||
delivery.setCustomerName(firstOrder == null ? null : firstOrder.getCustomerName()); delivery.setStatus("WAIT_DELIVERY"); delivery.setVersion(0L);
|
||||
delivery.setWaybillNo(trim(request.waybillNo())); delivery.setDeliveryMethod(request.deliveryMethod().trim());
|
||||
delivery.setCarrierName(firstText(request.carrierName(), dispatch.getCarrierName())); delivery.setPickupMethod(trim(request.pickupMethod()));
|
||||
delivery.setLoadingType(trim(request.loadingType())); delivery.setVehicleType(trim(request.vehicleType())); delivery.setVehicleSize(trim(request.vehicleSize()));
|
||||
delivery.setVehiclePlate(firstText(request.vehiclePlate(), dispatch.getVehiclePlate())); delivery.setDriverName(firstText(request.driverName(), dispatch.getDriverName()));
|
||||
delivery.setDriverPhone(firstText(request.driverPhone(), dispatch.getDriverPhone())); delivery.setAppointmentAccount(trim(request.appointmentAccount()));
|
||||
delivery.setSellerName(trim(request.sellerName())); delivery.setPlannedDeliveryAt(request.plannedDeliveryAt());
|
||||
delivery.setPackageCount(groupItems.stream().map(DispatchItem::getPackageCount).filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
|
||||
delivery.setPalletCount(groupItems.size());
|
||||
delivery.setTotalVolumeCbm(groupItems.stream().map(DispatchItem::getVolumeCbm).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||
delivery.setTotalWeightKg(totalWeight(customerId, groupItems)); delivery.setExpectedArrivalAt(dispatch.getEstimatedArrivalAt()); delivery.setRemark(trim(request.remark()));
|
||||
delivery.setIdempotencyKey(childKey(request.idempotencyKey(), "DEST-" + groupIndex++)); delivery.setCreatedBy(operatorId);
|
||||
delivery.setCreatedAt(now); delivery.setUpdatedAt(now); deliveryOrders.insert(delivery);
|
||||
recordOperation(customerId, delivery.getId(), operatorId, "CREATE_DELIVERY", "按目的FBA仓生成派送单", now);
|
||||
bindTailOrders(customerId, groupItems, outbound.getOutboundNo(), delivery.getDeliveryNo(), request.remark(), now);
|
||||
deliveries.add(delivery);
|
||||
}
|
||||
DeliveryOrder feeDelivery = deliveries.get(0);
|
||||
LastlegOrder firstOrder = firstTailOrder(customerId, items);
|
||||
delivery.setCustomerName(firstOrder == null ? null : firstOrder.getCustomerName()); delivery.setStatus("WAIT_DELIVERY"); delivery.setVersion(0L);
|
||||
delivery.setWaybillNo(trim(request.waybillNo())); delivery.setDeliveryMethod(request.deliveryMethod().trim());
|
||||
delivery.setCarrierName(firstText(request.carrierName(), dispatch.getCarrierName())); delivery.setPickupMethod(trim(request.pickupMethod()));
|
||||
delivery.setLoadingType(trim(request.loadingType())); delivery.setVehicleType(trim(request.vehicleType())); delivery.setVehicleSize(trim(request.vehicleSize()));
|
||||
delivery.setVehiclePlate(firstText(request.vehiclePlate(), dispatch.getVehiclePlate())); delivery.setDriverName(firstText(request.driverName(), dispatch.getDriverName()));
|
||||
delivery.setDriverPhone(firstText(request.driverPhone(), dispatch.getDriverPhone())); delivery.setAppointmentAccount(trim(request.appointmentAccount()));
|
||||
delivery.setSellerName(trim(request.sellerName())); delivery.setPlannedDeliveryAt(request.plannedDeliveryAt());
|
||||
delivery.setPackageCount(outbound.getPackageCount()); delivery.setPalletCount(outbound.getPalletCount());
|
||||
delivery.setTotalVolumeCbm(items.stream().map(DispatchItem::getVolumeCbm).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||
delivery.setTotalWeightKg(totalWeight(customerId, items)); delivery.setExpectedArrivalAt(dispatch.getEstimatedArrivalAt()); delivery.setRemark(trim(request.remark()));
|
||||
delivery.setIdempotencyKey(request.idempotencyKey().trim()); delivery.setCreatedBy(operatorId);
|
||||
delivery.setCreatedAt(now); delivery.setUpdatedAt(now); deliveryOrders.insert(delivery);
|
||||
List<DeliveryRequests.FeeItem> feeInputs = request.fees() == null ? List.of() : request.fees();
|
||||
for (DeliveryRequests.FeeItem input : feeInputs) {
|
||||
feeSources.publishApproved(operatorId, childKey(request.idempotencyKey(), input.feeType()),
|
||||
new OverseasFeeSourceDtos.SaveRequest(customerId, delivery.getCustomerName(),
|
||||
"OVERSEAS_DELIVERY_FEE", "DELIVERY_ORDER", delivery.getId(), delivery.getDeliveryNo(),
|
||||
new OverseasFeeSourceDtos.SaveRequest(customerId, feeDelivery.getCustomerName(),
|
||||
"OVERSEAS_DELIVERY_FEE", "DELIVERY_ORDER", feeDelivery.getId(), feeDelivery.getDeliveryNo(),
|
||||
firstOrder == null ? null : firstOrder.getId(), null,
|
||||
firstOrder == null ? null : firstOrder.getInboundCode(), null,
|
||||
firstOrder == null ? null : parseLong(firstOrder.getSourceContainerBookingId()),
|
||||
firstOrder == null ? null : firstOrder.getContainerNo(), parseLong(delivery.getOverseasWarehouseId()),
|
||||
firstOrder == null ? null : firstOrder.getContainerNo(), parseLong(feeDelivery.getOverseasWarehouseId()),
|
||||
input.feeType().trim(), input.feeName().trim(), input.direction(), input.supplierId(),
|
||||
input.supplierName(), input.supplierCategory(), input.settlementMode(), input.paidAt(),
|
||||
input.paymentMethod(), input.paymentReferenceNo(), input.paymentVoucherFileIds(), LocalDate.now(),
|
||||
input.currency(), input.amount(), input.chargeableQuantity(), input.quantityUnit(), input.unitPrice(),
|
||||
input.periodStart(), input.periodEnd()));
|
||||
}
|
||||
recordOperation(customerId, delivery.getId(), operatorId, "CREATE_DELIVERY", "CREATED", now);
|
||||
bindTailOrders(customerId, items, outbound.getOutboundNo(), delivery.getDeliveryNo(), request.remark(), now);
|
||||
return result(outbound, delivery, false);
|
||||
return result(outbound, deliveries, false);
|
||||
}
|
||||
|
||||
private void validateTransferFees(DeliveryRequests.Transfer request) {
|
||||
@@ -622,17 +640,19 @@ public class DeliveryTransferService {
|
||||
|| !Set.of("WAIT_PICK", "WAIT_OUTBOUND").contains(i.getStatus()))) {
|
||||
throw conflict("Outbound order cannot be cancelled after execution has started");
|
||||
}
|
||||
DeliveryOrder existingDelivery = deliveryOrders.lockByOutbound(customerId, outbound.getId());
|
||||
if (existingDelivery != null && !"WAIT_DELIVERY".equals(existingDelivery.getStatus()))
|
||||
List<DeliveryOrder> existingDeliveries = deliveryOrders.lockByOutbound(customerId, outbound.getId());
|
||||
if (existingDeliveries.stream().anyMatch(delivery -> !"WAIT_DELIVERY".equals(delivery.getStatus())))
|
||||
throw conflict("Outbound order cannot be cancelled while delivery is in progress");
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DeliveryOrder delivery = existingDelivery;
|
||||
if (delivery != null) { delivery.setStatus("CANCELLED"); delivery.setVersion(value(delivery.getVersion()) + 1); delivery.setUpdatedAt(now); deliveryOrders.updateById(delivery); }
|
||||
for (DeliveryOrder delivery : existingDeliveries) {
|
||||
delivery.setStatus("CANCELLED"); delivery.setVersion(value(delivery.getVersion()) + 1);
|
||||
delivery.setUpdatedAt(now); deliveryOrders.updateById(delivery);
|
||||
}
|
||||
OutboundPdaTask task = pdaTasks.lockByOutbound(customerId, outbound.getId());
|
||||
if (task != null) { task.setStatus("CANCELLED"); task.setUpdatedAt(now); pdaTasks.updateById(task); }
|
||||
outbound.setStatus("CANCELLED"); outbound.setUpdatedAt(now); outboundOrders.updateById(outbound);
|
||||
for (OutboundPalletItem item : outboundItems) { item.setStatus("CANCELLED"); outboundPallets.updateById(item); }
|
||||
if (delivery != null) {
|
||||
if (!existingDeliveries.isEmpty()) {
|
||||
for (LastlegOrder order : tailOrders.selectList(new LambdaQueryWrapper<LastlegOrder>().eq(LastlegOrder::getCustomerId, customerId).eq(LastlegOrder::getOutboundOrderNo, outboundNo))) {
|
||||
String from = order.getCurrentStatus(); order.setCurrentStatus("RECEIVED"); order.setSubOrderStatus(null); order.setOutboundOrderNo(null); order.setDeliveryOrderNo(null); order.setUpdatedAt(now); tailOrders.updateById(order);
|
||||
LastlegOrderStatusHistory history = new LastlegOrderStatusHistory(); history.setCustomerId(customerId); history.setOrderId(order.getId()); history.setFromStatus(from); history.setToStatus("RECEIVED"); history.setEventId("CANCEL_OUTBOUND:" + outboundNo); history.setRemark(request.reason().trim()); history.setCreatedAt(now); histories.insert(history);
|
||||
@@ -772,13 +792,13 @@ public class DeliveryTransferService {
|
||||
OutboundOrder outbound = outboundOrders.findGlobalByNo(trim(outboundNo));
|
||||
if (outbound == null) throw notFound("Outbound order not found");
|
||||
String customerId = outbound.getCustomerId();
|
||||
DeliveryOrder delivery = deliveryOrders.lockByOutbound(customerId, outbound.getId());
|
||||
if (delivery == null && "DISPATCH_DELIVERY".equals(outbound.getOutboundType())) {
|
||||
List<DeliveryOrder> deliveries = deliveryOrders.lockByOutbound(customerId, outbound.getId());
|
||||
if (deliveries.isEmpty() && "DISPATCH_DELIVERY".equals(outbound.getOutboundType())) {
|
||||
throw new IllegalStateException("Delivery document is missing for dispatch outbound order");
|
||||
}
|
||||
List<DeliveryResponses.OutboundPalletRow> pallets = outboundPallets.findByOutbound(customerId, outbound.getId()).stream()
|
||||
.map(this::outboundPalletRow).toList();
|
||||
return new DeliveryResponses.OutboundDetail(outboundRow(outbound), delivery == null ? null : deliveryRow(delivery), pallets);
|
||||
return new DeliveryResponses.OutboundDetail(outboundRow(outbound), deliveries.stream().map(this::deliveryRow).toList(), pallets);
|
||||
}
|
||||
|
||||
public PageResult<DeliveryResponses.DeliveryRow> deliveryPage(String customerId, DeliveryRequests.Query query) {
|
||||
@@ -801,7 +821,8 @@ public class DeliveryTransferService {
|
||||
public DeliveryResponses.DeliveryDetail deliveryDetail(String customerId, String deliveryNo) {
|
||||
DeliveryOrder delivery = deliveryOrders.findByNo(customerId, deliveryNo);
|
||||
if (delivery == null) throw notFound("Delivery order not found");
|
||||
List<DispatchItem> items = dispatchItems.findByDispatchOrder(customerId, delivery.getDispatchOrderId());
|
||||
List<DispatchItem> items = dispatchItems.findByDispatchOrder(customerId, delivery.getDispatchOrderId()).stream()
|
||||
.filter(item -> delivery.getDeliveryGroupKey().equals(deliveryGroupKey(item))).toList();
|
||||
List<Long> orderIds = items.stream().map(DispatchItem::getOrderId).filter(Objects::nonNull).distinct().toList();
|
||||
Map<Long, LastlegOrder> ordersById = orderIds.isEmpty() ? Map.of() : tailOrders.selectBatchIds(orderIds).stream()
|
||||
.filter(order -> customerId.equals(order.getCustomerId())).collect(java.util.stream.Collectors.toMap(LastlegOrder::getId, order -> order));
|
||||
@@ -843,14 +864,16 @@ public class DeliveryTransferService {
|
||||
}
|
||||
}
|
||||
|
||||
private DeliveryOrder requireDelivery(String customerId, Long outboundId) {
|
||||
DeliveryOrder value = deliveryOrders.lockByOutbound(customerId, outboundId);
|
||||
if (value == null) throw new IllegalStateException("Delivery document is missing for outbound order");
|
||||
return value;
|
||||
private List<DeliveryOrder> requireDeliveries(String customerId, Long outboundId) {
|
||||
List<DeliveryOrder> values = deliveryOrders.lockByOutbound(customerId, outboundId);
|
||||
if (values.isEmpty()) throw new IllegalStateException("Delivery documents are missing for outbound order");
|
||||
return values;
|
||||
}
|
||||
private DeliveryResponses.TransferResult result(OutboundOrder o, DeliveryOrder d, boolean replayed) {
|
||||
private DeliveryResponses.TransferResult result(OutboundOrder o, List<DeliveryOrder> deliveries, boolean replayed) {
|
||||
return new DeliveryResponses.TransferResult(o.getDispatchNo(), o.getOutboundNo(), o.getStatus(),
|
||||
d.getDeliveryNo(), d.getStatus(), o.getPalletCount(), o.getPackageCount(), replayed);
|
||||
o.getPalletCount(), o.getPackageCount(), deliveries.stream().map(d -> new DeliveryResponses.TransferDelivery(
|
||||
d.getDeliveryNo(), d.getStatus(), d.getDestinationWarehouseId(), d.getDestinationWarehouseCode(),
|
||||
d.getDestinationWarehouseName(), value(d.getPalletCount()), value(d.getPackageCount()))).toList(), replayed);
|
||||
}
|
||||
private DeliveryResponses.OutboundRow outboundRow(OutboundOrder o) { return new DeliveryResponses.OutboundRow(o.getId(), o.getOutboundNo(), o.getDispatchNo(), o.getOverseasWarehouseId(), o.getLxCodes(), o.getStatus(), o.getPalletCount(), o.getPackageCount(), o.getCreatedAt(), o.getOutboundType()); }
|
||||
private DeliveryResponses.OutboundPalletRow outboundPalletRow(OutboundPalletItem item) {
|
||||
@@ -864,12 +887,18 @@ public class DeliveryTransferService {
|
||||
candidate.getInboundCode(), candidate.getSkuCode(), candidate.getProductName(),
|
||||
value(candidate.getAvailablePackages()));
|
||||
}
|
||||
private DeliveryResponses.DeliveryRow deliveryRow(DeliveryOrder d) { return new DeliveryResponses.DeliveryRow(d.getId(), d.getCustomerId(), d.getCustomerName(), d.getDeliveryNo(), d.getOutboundNo(), d.getDispatchNo(), d.getOverseasWarehouseId(), d.getOverseasWarehouseName(), d.getStatus(), d.getWaybillNo(), d.getDeliveryMethod(), d.getCarrierName(), d.getPickupMethod(), d.getLoadingType(), d.getVehicleType(), d.getVehicleSize(), d.getVehiclePlate(), d.getDriverName(), d.getDriverPhone(), d.getAppointmentAccount(), d.getSellerName(), value(d.getPalletCount()), value(d.getPackageCount()), d.getTotalWeightKg(), d.getTotalVolumeCbm(), d.getPlannedDeliveryAt(), d.getDispatchedAt(), d.getExpectedArrivalAt(), d.getSignedAt(), d.getRemark(), d.getCreatedAt(), value(d.getVersion()), DeliveryStatusPolicy.allowedActions(d.getStatus()), d.getBolNo(), com.baiyue.lastleg.delivery.model.DeliveryDocumentSnapshot.summary(d.getDocumentsJson())); }
|
||||
private DeliveryResponses.DeliveryRow deliveryRow(DeliveryOrder d) { return new DeliveryResponses.DeliveryRow(d.getId(), d.getCustomerId(), d.getCustomerName(), d.getDeliveryNo(), d.getOutboundNo(), d.getDispatchNo(), d.getOverseasWarehouseId(), d.getOverseasWarehouseName(), d.getStatus(), d.getWaybillNo(), d.getDeliveryMethod(), d.getCarrierName(), d.getPickupMethod(), d.getLoadingType(), d.getVehicleType(), d.getVehicleSize(), d.getVehiclePlate(), d.getDriverName(), d.getDriverPhone(), d.getAppointmentAccount(), d.getSellerName(), value(d.getPalletCount()), value(d.getPackageCount()), d.getTotalWeightKg(), d.getTotalVolumeCbm(), d.getPlannedDeliveryAt(), d.getDispatchedAt(), d.getExpectedArrivalAt(), d.getSignedAt(), d.getRemark(), d.getCreatedAt(), value(d.getVersion()), DeliveryStatusPolicy.allowedActions(d.getStatus()), d.getBolNo(), com.baiyue.lastleg.delivery.model.DeliveryDocumentSnapshot.summary(d.getDocumentsJson()), d.getDestinationWarehouseId(), d.getDestinationWarehouseCode(), d.getDestinationWarehouseName()); }
|
||||
private DeliveryResponses.DeliverySku sku(LastlegOrderDetail d, LastlegOrder order) { return new DeliveryResponses.DeliverySku(order == null ? null : order.getInboundCode(), order == null ? null : order.getCustomerName(), d.getDestinationWarehouseCode(), null, d.getShipmentNo(), d.getFbaTrackingNo(), d.getSkuCode(), firstText(d.getProductNameZh(), d.getProductNameEn()), value(d.getPackageCount()), d.getGrossWeightKg(), d.getVolumeCbm()); }
|
||||
private DeliveryResponses.DeliveryFeeRow feeRow(OverseasFeeSource f) { return new DeliveryResponses.DeliveryFeeRow(f.getId(), f.getDirection(), f.getFeeItemCode(), f.getOriginalCurrency(), f.getOriginalAmount(), f.getReviewReason()); }
|
||||
private DeliveryResponses.DeliveryOperationRow operationRow(DeliveryOperation o) { return new DeliveryResponses.DeliveryOperationRow(o.getId(), o.getCreatedAt(), o.getOperationNode(), firstText(o.getOperatorName(), o.getOperatorId()), o.getResult()); }
|
||||
private void recordOperation(String customerId, Long deliveryId, String operatorId, String node, String result, LocalDateTime now) { DeliveryOperation operation = new DeliveryOperation(); operation.setCustomerId(customerId); operation.setDeliveryOrderId(deliveryId); operation.setOperationNode(node); operation.setOperatorId(operatorId); operation.setOperatorName(operatorId); operation.setResult(result); operation.setCreatedAt(now); deliveryOperations.insert(operation); }
|
||||
private LastlegOrder firstTailOrder(String customerId, List<DispatchItem> items) { return items.stream().map(DispatchItem::getOrderId).filter(Objects::nonNull).map(tailOrders::selectById).filter(Objects::nonNull).filter(order -> customerId.equals(order.getCustomerId())).findFirst().orElse(null); }
|
||||
private String deliveryGroupKey(DispatchItem item) {
|
||||
String code = firstText(item.getDestinationWarehouseCode(), item.getFbaWarehouseCode());
|
||||
if (StringUtils.hasText(code)) return "CODE:" + code.toUpperCase(Locale.ROOT);
|
||||
if (StringUtils.hasText(item.getDestinationWarehouseId())) return "ID:" + item.getDestinationWarehouseId().trim();
|
||||
throw conflict("Dispatch pallet is missing its destination FBA warehouse");
|
||||
}
|
||||
private String resolveLxCodes(String customerId, List<DispatchItem> items) {
|
||||
List<String> inboundCodes = items.stream().map(DispatchItem::getInboundCode)
|
||||
.filter(StringUtils::hasText).map(String::trim).distinct().toList();
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ public class UnloadCompletedOutboxRepository {
|
||||
private static final List<String> OVERSEAS_TO_CN_TYPES = List.of(
|
||||
EventTypes.OVERSEAS_CONTAINER_UNLOADED,
|
||||
EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED,
|
||||
EventTypes.OVERSEAS_DELIVERY_COMPLETED,
|
||||
EventTypes.WALLET_COMMAND_REQUESTED);
|
||||
@Resource private LastlegEventOutboxMapper mapper;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user