feat(lastleg): redispatch returned cargo and reconcile source signatures

This commit is contained in:
2026-10-03 16:06:54 +08:00
parent e740047e03
commit 8700fc3423
39 changed files with 1605 additions and 139 deletions
@@ -41,6 +41,7 @@ public class InventoryCommandService {
@Resource private InventoryLedgerMapper ledgers;
@Resource private InventoryOperationMapper operations;
@Resource private InventoryCommandMapper commands;
@Resource private com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper returnReceipts;
@Resource private WarehouseResourceGateway warehouseGateway;
@Resource private ObjectMapper objectMapper;
@@ -335,6 +336,17 @@ public class InventoryCommandService {
InventoryOperation existing = operations.findIdempotency(customerId, normalizedKey);
if (existing != null) return replayReturned(existing, requestHash);
Map<Long, com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnOwner> returnOwners = new LinkedHashMap<>();
for (var source : sources.values()) {
var owner = returnReceipts.owner(customerId, exceptionNo, outboundNo, source.outboundPalletItemId(), source.inventoryPositionId());
if (owner == null || owner.orderId() == null || !Objects.equals(owner.inboundOrderId(), request.inboundOrderId()))
throw conflict("Returned stock receipt is missing the original order attribution");
returnOwners.put(source.outboundPalletItemId(), owner);
}
for (Long orderId : returnOwners.values().stream().map(com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnOwner::orderId).distinct().sorted().toList()) {
if (returnReceipts.lockOrder(customerId, orderId) == null) throw conflict("Returned stock source order is missing or held");
}
InventoryOperation operation = new InventoryOperation();
operation.setId(IdWorker.getId());
operation.setCustomerId(customerId);
@@ -430,6 +442,16 @@ public class InventoryCommandService {
normalizedKey + ":" + receipt.outboundPalletItemId() + ":" + target.getId(),
"Exception return received with evidence " + request.evidenceFileIds().size());
ledgerIds.add(ledger.getId());
var owner = returnOwners.get(source.outboundPalletItemId());
var stockReceipt = new com.baiyue.lastleg.inventory.entity.ReturnStockReceipt();
stockReceipt.setId(IdWorker.getId()); stockReceipt.setCustomerId(customerId);
stockReceipt.setReceiptNo("RSR" + stockReceipt.getId()); stockReceipt.setInventoryLedgerId(ledger.getId());
stockReceipt.setExceptionOrderId(owner.exceptionOrderId()); stockReceipt.setOrderId(owner.orderId());
stockReceipt.setInboundOrderId(owner.inboundOrderId()); stockReceipt.setSourceOutboundNo(outboundNo);
stockReceipt.setSourceOutboundPalletItemId(source.outboundPalletItemId()); stockReceipt.setSourceInventoryPositionId(source.inventoryPositionId());
stockReceipt.setInventoryBalanceId(balance.getId()); stockReceipt.setInventoryPositionId(target.getId());
stockReceipt.setReceivedPackages(receipt.receivedPackages()); stockReceipt.setReceivedAt(ledger.getOccurredAt());
stockReceipt.setCreatedAt(ledger.getOccurredAt()); returnReceipts.insert(stockReceipt);
restoredPackages += receipt.receivedPackages();
}
long beforePackages = balanceBefore.values().stream().mapToLong(Long::longValue).sum();
@@ -0,0 +1,27 @@
package com.baiyue.lastleg.inventory.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/** An immutable receipt from a real return ledger; it is not another customer order or initial inbound receipt. */
@Data
@TableName("ll_return_stock_receipt")
public class ReturnStockReceipt {
@TableId private Long id;
private String customerId;
private String receiptNo;
private Long inventoryLedgerId;
private Long exceptionOrderId;
private Long orderId;
private Long inboundOrderId;
private String sourceOutboundNo;
private Long sourceOutboundPalletItemId;
private Long sourceInventoryPositionId;
private Long inventoryBalanceId;
private Long inventoryPositionId;
private Long receivedPackages;
private LocalDateTime receivedAt;
private LocalDateTime createdAt;
}
@@ -0,0 +1,34 @@
package com.baiyue.lastleg.inventory.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.lastleg.inventory.entity.ReturnStockReceipt;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ReturnStockReceiptMapper extends BaseMapper<ReturnStockReceipt> {
@Select("SELECT id FROM ll_order WHERE customer_id=#{customerId} AND id=#{id} FOR UPDATE")
Long lockOrder(@Param("customerId") String customerId, @Param("id") Long id);
record ReturnOwner(Long exceptionOrderId, Long orderId, Long inboundOrderId) { }
@Select("SELECT e.id AS exception_order_id,e.last_mile_order_id AS order_id,e.inbound_order_id " +
"FROM ll_exception_order e JOIN ll_order o ON o.customer_id=e.customer_id AND o.id=e.last_mile_order_id " +
"JOIN ll_exception_return_item i ON i.customer_id=e.customer_id AND i.exception_order_id=e.id " +
"WHERE e.customer_id=#{customerId} AND e.exception_no=#{exceptionNo} AND e.outbound_no=#{outboundNo} " +
"AND e.exception_type='RETURN' AND e.return_direction='OVERSEAS_WAREHOUSE' AND e.status='PROCESSING' " +
"AND i.outbound_pallet_item_id=#{itemId} AND i.inventory_position_id=#{positionId}")
ReturnOwner owner(@Param("customerId") String customerId, @Param("exceptionNo") String exceptionNo,
@Param("outboundNo") String outboundNo, @Param("itemId") Long itemId, @Param("positionId") Long positionId);
@Select("SELECT r.* FROM ll_return_stock_receipt r JOIN ll_inventory_position p ON p.id=#{positionId} " +
"AND p.customer_id=r.customer_id AND p.balance_id=r.inventory_balance_id " +
"WHERE r.customer_id=#{customerId} AND r.received_packages > (SELECT COALESCE(SUM(a.packages),0) " +
"FROM ll_dispatch_pda_pick_order a WHERE a.customer_id=r.customer_id AND a.return_receipt_id=r.id) " +
"ORDER BY r.received_at,r.id")
List<ReturnStockReceipt> available(@Param("customerId") String customerId, @Param("positionId") Long positionId);
@Select("SELECT * FROM ll_return_stock_receipt WHERE customer_id=#{customerId} AND id=#{id} FOR UPDATE")
ReturnStockReceipt lock(@Param("customerId") String customerId, @Param("id") Long id);
@Select("SELECT COALESCE(SUM(packages),0) FROM ll_dispatch_pda_pick_order WHERE customer_id=#{customerId} AND return_receipt_id=#{id}")
long picked(@Param("customerId") String customerId, @Param("id") Long id);
}