fix(lastleg): scope inventory holds by cycle and align pick lock ordering

This commit is contained in:
2026-10-03 01:34:14 +08:00
parent 1db6e744c8
commit 7530a95fbd
9 changed files with 304 additions and 13 deletions
@@ -117,6 +117,8 @@ public class DispatchPdaWorkflowService {
}
long picked = picks.totalItem(customerId, task.getId(), itemId);
if (picked + request.packages() > item.getPackageCount()) throw conflict("Picked packages exceed dispatch pallet packages");
pickAttribution.preparePicking(customerId, request.positionId(),
"RELABEL_ORDER".equals(item.getSourceBusinessType()) ? item.getOrderId() : null);
String keyBase = "DISPATCH:" + order.getDispatchNo() + ":" + itemId + ":" + request.idempotencyKey();
InventoryCommandRequests.Move move = new InventoryCommandRequests.Move(request.positionId(),
request.packages(), order.getDispatchNo(), order.getDispatchNo(), null,
@@ -25,6 +25,25 @@ public class DispatchPickOrderAttributionService {
@Resource private LastlegOrderMapper orders;
@Resource private LastlegOrderStatusHistoryMapper history;
/** Acquire source order locks before any inventory lock, matching order hold/release. */
@org.springframework.transaction.annotation.Transactional(propagation = org.springframework.transaction.annotation.Propagation.MANDATORY)
public void preparePicking(String customerId, long positionId, Long fallbackOrderId) {
var sourceIds = new java.util.TreeSet<Long>();
attributions.availableReceipts(customerId, positionId).forEach(receipt -> sourceIds.add(receipt.getOrderId()));
if (fallbackOrderId != null && fallbackOrderId > 0) sourceIds.add(fallbackOrderId);
if (sourceIds.isEmpty()) throw conflict("PDA pick has no attributable source order");
for (Long orderId : sourceIds) {
LastlegOrder order = orders.lockById(customerId, orderId);
if (order == null || Boolean.TRUE.equals(order.getDetained())) {
throw conflict("PDA pick source order is missing or held");
}
}
if (attributions.availableReceipts(customerId, positionId).stream()
.anyMatch(receipt -> !sourceIds.contains(receipt.getOrderId()))) {
throw conflict("PDA pick source orders changed; reload the task");
}
}
/** Call after the inventory position has been locked/moved and the PDA pick row has been inserted. */
public List<OrderPackages> attribute(String customerId, DispatchPdaPick pick) {
return attribute(customerId, pick, null);