feat(lastleg): retain receipt and redispatch history on exceptions

This commit is contained in:
2026-10-04 10:45:05 +08:00
parent 446fad33d0
commit dc993c9713
5 changed files with 194 additions and 4 deletions
@@ -27,7 +27,12 @@ public final class ExceptionOrderResponses {
public record ReturnSource(Long outboundPalletItemId, Long inventoryPositionId,
String palletNo, String skuCode, long maxReturnPackages) {}
public record Detail(Row order, List<ReturnSource> returnSources) {}
public record Detail(Row order, List<ReturnSource> returnSources, List<ReturnReceipt> returnReceipts) {}
public record ReturnReceipt(String receiptNo, long receivedPackages, LocalDateTime receivedAt,
List<Redispatch> redispatches) {}
public record Redispatch(String dispatchNo, String status, long selectedPackages, long pickedPackages,
List<RelatedDelivery> deliveries) {}
public record RelatedDelivery(String deliveryNo, String status) {}
public record CommandResult(Long id, String exceptionNo, String status, long version,
Long inventoryAdjustmentId, long restoredPackages,
LocalDateTime completedAt, boolean replayed) {}
@@ -83,6 +83,7 @@ public class ExceptionOrderService {
@Resource private InventoryCommandService inventoryCommands;
@Resource private ExceptionEvidenceFileGateway evidenceFiles;
@Resource private ObjectMapper objectMapper;
@Resource private com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper returnReceipts;
public PageResult<ExceptionOrderResponses.Row> page(String scopedCustomerId, ExceptionOrderRequests.Query query) {
String customerId = customerScope(scopedCustomerId, query.customerId());
@@ -114,7 +115,44 @@ public class ExceptionOrderService {
public ExceptionOrderResponses.Detail detail(String customerId, String exceptionNo) {
ExceptionOrder order = exceptionOrders.findByNo(customerId, exceptionNo);
if (order == null) throw notFound("异常订单不存在");
return new ExceptionOrderResponses.Detail(row(order), storedReturnSources(order));
return new ExceptionOrderResponses.Detail(row(order), storedReturnSources(order),
receiptHistory(returnReceipts.history(customerId, order.getId())));
}
private List<ExceptionOrderResponses.ReturnReceipt> receiptHistory(
List<com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnHistoryRow> rows) {
var grouped = new LinkedHashMap<Long, List<com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnHistoryRow>>();
for (var item : rows) grouped.computeIfAbsent(item.receiptId(), ignored -> new ArrayList<>()).add(item);
var receipts = new ArrayList<ExceptionOrderResponses.ReturnReceipt>();
for (var batch : grouped.values()) {
var first = batch.get(0);
if (first.receiptId() == null || !StringUtils.hasText(first.receiptNo())
|| first.receivedPackages() == null || first.receivedPackages() <= 0)
throw conflict("退回实收凭据缺少有效业务编号或数量");
var dispatches = new LinkedHashMap<String, List<com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnHistoryRow>>();
for (var item : batch) if (StringUtils.hasText(item.dispatchNo()))
dispatches.computeIfAbsent(item.dispatchNo(), ignored -> new ArrayList<>()).add(item);
var redispatches = new ArrayList<ExceptionOrderResponses.Redispatch>();
for (var entry : dispatches.entrySet()) {
var distinctItems = new LinkedHashMap<Long, com.baiyue.lastleg.inventory.mapper.ReturnStockReceiptMapper.ReturnHistoryRow>();
var deliveries = new LinkedHashMap<String, ExceptionOrderResponses.RelatedDelivery>();
for (var item : entry.getValue()) {
if (item.dispatchItemId() == null || item.selectedPackages() == null || item.selectedPackages() < 0
|| item.pickedPackages() == null || item.pickedPackages() < 0)
throw conflict("退回重派关联缺少有效排车数量");
distinctItems.putIfAbsent(item.dispatchItemId(), item);
if (StringUtils.hasText(item.deliveryNo())) deliveries.putIfAbsent(item.deliveryNo(),
new ExceptionOrderResponses.RelatedDelivery(item.deliveryNo(), item.deliveryStatus()));
}
long selected = distinctItems.values().stream().mapToLong(item -> item.selectedPackages()).sum();
long picked = distinctItems.values().stream().mapToLong(item -> item.pickedPackages()).sum();
redispatches.add(new ExceptionOrderResponses.Redispatch(entry.getKey(), entry.getValue().get(0).dispatchStatus(),
selected, picked, List.copyOf(deliveries.values())));
}
receipts.add(new ExceptionOrderResponses.ReturnReceipt(first.receiptNo(), first.receivedPackages(), first.receivedAt(),
List.copyOf(redispatches)));
}
return List.copyOf(receipts);
}
public ExceptionOrderResponses.UploadedEvidence uploadEvidence(String customerId, String operatorId,
@@ -4,9 +4,40 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.lastleg.inventory.entity.ReturnStockReceipt;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.time.LocalDateTime;
@Mapper
public interface ReturnStockReceiptMapper extends BaseMapper<ReturnStockReceipt> {
@Select("""
SELECT r.id AS receiptId,r.receipt_no AS receiptNo,r.received_packages AS receivedPackages,
r.received_at AS receivedAt,di.id AS dispatchItemId,d.dispatch_no AS dispatchNo,
d.status AS dispatchStatus,di.package_count AS selectedPackages,
COALESCE((SELECT SUM(a.packages) FROM ll_dispatch_pda_pick_order a
JOIN ll_dispatch_pda_pick p ON p.id=a.pick_id AND BINARY p.customer_id=BINARY a.customer_id
WHERE BINARY a.customer_id=BINARY r.customer_id AND a.return_receipt_id=r.id
AND p.dispatch_item_id=di.id),0) AS pickedPackages,
delivery.delivery_no AS deliveryNo,delivery.status AS deliveryStatus
FROM ll_return_stock_receipt r
LEFT JOIN ll_dispatch_item di ON di.return_receipt_id=r.id AND BINARY di.customer_id=BINARY r.customer_id
LEFT JOIN ll_dispatch_order d ON d.id=di.dispatch_order_id AND BINARY d.customer_id=BINARY r.customer_id
LEFT JOIN ll_delivery_order delivery ON delivery.dispatch_order_id=d.id
AND BINARY delivery.customer_id=BINARY r.customer_id
AND BINARY delivery.delivery_group_key=BINARY CASE
WHEN di.destination_type='PRIVATE' AND NULLIF(TRIM(di.delivery_address),'') IS NOT NULL
THEN CONCAT('PRIVATE:',TRIM(di.delivery_address),':',TRIM(COALESCE(di.postal_code,'')))
WHEN COALESCE(NULLIF(TRIM(di.destination_warehouse_code),''),NULLIF(TRIM(di.fba_warehouse_code),'')) IS NOT NULL
THEN CONCAT('CODE:',UPPER(COALESCE(NULLIF(TRIM(di.destination_warehouse_code),''),NULLIF(TRIM(di.fba_warehouse_code),''))))
WHEN NULLIF(TRIM(di.destination_warehouse_id),'') IS NOT NULL THEN CONCAT('ID:',TRIM(di.destination_warehouse_id))
ELSE NULL END
WHERE BINARY r.customer_id=BINARY #{customerId} AND r.exception_order_id=#{exceptionId}
ORDER BY r.received_at,r.id,d.id,di.id,delivery.id
""")
List<ReturnHistoryRow> history(@Param("customerId") String customerId, @Param("exceptionId") Long exceptionId);
record ReturnHistoryRow(Long receiptId, String receiptNo, Long receivedPackages, LocalDateTime receivedAt,
Long dispatchItemId, String dispatchNo, String dispatchStatus, Integer selectedPackages,
Long pickedPackages, String deliveryNo, String deliveryStatus) {}
@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) { }