feat: expose dispatch BOL lines

This commit is contained in:
2026-09-10 21:22:06 +08:00
parent 800ebb875f
commit 6cf144e53d
5 changed files with 82 additions and 5 deletions
@@ -12,11 +12,16 @@ public record DispatchResponse(Long id, String dispatchNo, String vehiclePlate,
int itemCount, int palletCount, int packageCount, int pickedPackageCount,
int shippedPackageCount, int remainingPackageCount, String partialCompletionReason,
LocalDateTime createdAt,
List<Item> items) {
List<Item> items, List<BolLine> bolLines) {
public record Item(Long id, Long planPalletId, Long warehousePalletId, String palletNo,
String overseasWarehouseId, String overseasWarehouseName,
String orderNo, String orderRefs, String inboundCode, String destinationWarehouseCode,
String destinationWarehouseName, String fbaWarehouseCode, int packageCount,
BigDecimal volumeCbm, String sourceStage) {
}
public record BolLine(String containerNo, String palletNo, String fbaWarehouseCode,
String destinationWarehouseName, String warehouseNo, String fbaShipmentId,
String poReferenceNo, int packageCount) {
}
}
@@ -0,0 +1,35 @@
package com.baiyue.lastleg.dispatch.mapper;
import com.baiyue.lastleg.dispatch.dto.DispatchResponse;
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 DispatchBolMapper {
@Select("SELECT " +
"COALESCE(MAX(NULLIF(o.container_no,'')),MAX(NULLIF(ip.container_no,''))) AS containerNo," +
"MAX(NULLIF(di.pallet_no,'')) AS palletNo," +
"COALESCE(MAX(NULLIF(l.destination_warehouse_code,'')),MAX(NULLIF(di.fba_warehouse_code,''))," +
"MAX(NULLIF(di.destination_warehouse_code,''))) AS fbaWarehouseCode," +
"COALESCE(MAX(NULLIF(l.destination_warehouse_name,'')),MAX(NULLIF(di.destination_warehouse_name,''))) AS destinationWarehouseName," +
"COALESCE(MAX(NULLIF(l.inbound_code,'')),MAX(NULLIF(di.inbound_code,''))) AS warehouseNo," +
"GROUP_CONCAT(DISTINCT NULLIF(od.shipment_no,'') ORDER BY od.shipment_no SEPARATOR ', ') AS fbaShipmentId," +
"GROUP_CONCAT(DISTINCT NULLIF(od.fba_tracking_no,'') ORDER BY od.fba_tracking_no SEPARATOR ', ') AS poReferenceNo," +
"CASE WHEN MAX(l.id) IS NULL THEN COALESCE(MAX(di.package_count),0) " +
"WHEN COALESCE(MAX(a.picked_package_count),0)>0 THEN MAX(a.picked_package_count) " +
"ELSE COALESCE(MAX(a.planned_package_count),0) END AS packageCount " +
"FROM ll_dispatch_item di " +
"LEFT JOIN ll_inbound_plan_pallet p ON p.id=di.plan_pallet_id " +
"LEFT JOIN ll_inbound_plan ip ON ip.id=p.plan_id AND ip.customer_id=di.customer_id " +
"LEFT JOIN ll_inbound_plan_pallet_allocation a ON a.pallet_id=p.id " +
"LEFT JOIN ll_inbound_plan_line l ON l.id=a.line_id AND l.customer_id=di.customer_id " +
"LEFT JOIN ll_order o ON o.id=l.order_id AND o.customer_id=di.customer_id " +
"LEFT JOIN ll_order_detail od ON od.order_id=l.order_id AND od.customer_id=di.customer_id " +
"WHERE di.customer_id=#{customerId} AND di.dispatch_order_id=#{dispatchOrderId} " +
"GROUP BY di.id,l.id ORDER BY di.id,l.id")
List<DispatchResponse.BolLine> findByDispatchOrder(@Param("customerId") String customerId,
@Param("dispatchOrderId") Long dispatchOrderId);
}
@@ -12,6 +12,7 @@ import com.baiyue.lastleg.dispatch.entity.DispatchPalletLock;
import com.baiyue.lastleg.dispatch.entity.DispatchDraft;
import com.baiyue.lastleg.dispatch.entity.DispatchDraftContainer;
import com.baiyue.lastleg.dispatch.mapper.DispatchCandidateMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchBolMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchItemMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchOrderMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchPalletLockMapper;
@@ -49,6 +50,7 @@ public class DispatchService {
@Resource private DispatchOrderMapper orders;
@Resource private DispatchItemMapper items;
@Resource private DispatchBolMapper bolLines;
@Resource private DispatchPalletLockMapper locks;
@Resource private DispatchDraftMapper drafts;
@Resource private DispatchDraftContainerMapper draftContainers;
@@ -429,7 +431,8 @@ public class DispatchService {
public DispatchResponse detail(String customerId, String dispatchNo) {
DispatchOrder dispatch = find(customerId, dispatchNo);
return response(dispatch, items.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()));
return response(dispatch, items.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()),
bolLines.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()));
}
public DispatchResponse platformDetail(String dispatchNo) {
@@ -437,7 +440,8 @@ public class DispatchService {
if (dispatch == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Dispatch order not found");
}
return response(dispatch, items.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()));
return response(dispatch, items.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()),
bolLines.findByDispatchOrder(dispatch.getCustomerId(), dispatch.getId()));
}
@Transactional
@@ -499,6 +503,11 @@ public class DispatchService {
}
private DispatchResponse response(DispatchOrder order, List<DispatchItem> itemList) {
return response(order, itemList, List.of());
}
private DispatchResponse response(DispatchOrder order, List<DispatchItem> itemList,
List<DispatchResponse.BolLine> bolLineList) {
String orderNos = itemList.stream().flatMap(item -> splitOrderRefs(item.getOrderRefs(), item.getOrderNo()).stream())
.distinct().collect(Collectors.joining(","));
return new DispatchResponse(order.getId(), order.getDispatchNo(), order.getVehiclePlate(), order.getDriverName(),
@@ -515,7 +524,8 @@ public class DispatchService {
item.getWarehousePalletId(), item.getPalletNo(), item.getOverseasWarehouseId(), item.getOverseasWarehouseName(),
item.getOrderNo(), item.getOrderRefs(), item.getInboundCode(),
item.getDestinationWarehouseCode(), item.getDestinationWarehouseName(), item.getFbaWarehouseCode(),
Objects.requireNonNullElse(item.getPackageCount(), 0), item.getVolumeCbm(), item.getSourceStage())).toList());
Objects.requireNonNullElse(item.getPackageCount(), 0), item.getVolumeCbm(), item.getSourceStage())).toList(),
bolLineList == null ? List.of() : bolLineList);
}
private static String route(List<DispatchPalletCandidate> selected) {
@@ -2251,6 +2251,10 @@ components:
partialCompletionReason: { type: string, nullable: true }
createdAt: { type: string, format: date-time }
items: { type: array, items: { $ref: '#/components/schemas/DispatchItem' } }
bolLines:
type: array
description: 仅详情接口返回的BOL打印聚合行;分页接口返回空数组,避免列表查询放大
items: { $ref: '#/components/schemas/DispatchBolLine' }
DispatchItem:
type: object
properties:
@@ -2269,6 +2273,17 @@ components:
packageCount: { type: integer }
volumeCbm: { type: number }
sourceStage: { type: string, enum: [PLAN, RECEIVED] }
DispatchBolLine:
type: object
properties:
containerNo: { type: string, nullable: true }
palletNo: { type: string, nullable: true }
fbaWarehouseCode: { type: string, nullable: true }
destinationWarehouseName: { type: string, nullable: true }
warehouseNo: { type: string, nullable: true, description: 来源入仓码/仓库单号 }
fbaShipmentId: { type: string, nullable: true, description: 来源冻结货件编号;多值以逗号分隔 }
poReferenceNo: { type: string, nullable: true, description: 来源冻结FBA跟踪/参考号;多值以逗号分隔 }
packageCount: { type: integer, minimum: 0 }
RelabelStatus: { type: string, enum: [PENDING_PROCESSING, PROCESSING, CUSTOMER_CONFIRM_PENDING, READY_FOR_OUTBOUND, COMPLETED, REJECTED, CANCELLED] }
RelabelMerchantPageResponse:
allOf:
@@ -10,6 +10,7 @@ import com.baiyue.lastleg.dispatch.entity.DispatchItem;
import com.baiyue.lastleg.dispatch.entity.DispatchOrder;
import com.baiyue.lastleg.dispatch.entity.DispatchPalletLock;
import com.baiyue.lastleg.dispatch.mapper.DispatchCandidateMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchBolMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchDraftContainerMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchDraftMapper;
import com.baiyue.lastleg.dispatch.mapper.DispatchItemMapper;
@@ -49,6 +50,7 @@ import static org.mockito.Mockito.when;
class DispatchServiceTest {
@Mock DispatchOrderMapper orders;
@Mock DispatchItemMapper items;
@Mock DispatchBolMapper bolLines;
@Mock DispatchPalletLockMapper locks;
@Mock DispatchCandidateMapper candidates;
@Mock DispatchDraftMapper drafts;
@@ -62,6 +64,7 @@ class DispatchServiceTest {
service = new DispatchService();
ReflectionTestUtils.setField(service, "orders", orders);
ReflectionTestUtils.setField(service, "items", items);
ReflectionTestUtils.setField(service, "bolLines", bolLines);
ReflectionTestUtils.setField(service, "locks", locks);
ReflectionTestUtils.setField(service, "candidates", candidates);
ReflectionTestUtils.setField(service, "drafts", drafts);
@@ -70,6 +73,7 @@ class DispatchServiceTest {
ReflectionTestUtils.setField(service, "tailOrders", tailOrders);
ReflectionTestUtils.setField(service, "orderNoGenerator", new com.baiyue.lastleg.service.LastlegOrderNoGenerator());
lenient().when(items.findByDispatchOrder(any(), any())).thenReturn(List.of());
lenient().when(bolLines.findByDispatchOrder(any(), any())).thenReturn(List.of());
lenient().when(tailOrders.selectList(any())).thenReturn(List.of());
}
@@ -322,6 +326,7 @@ class DispatchServiceTest {
service.detail("200", "PC1");
verify(items).findByDispatchOrder("200", 1L);
verify(bolLines).findByDispatchOrder("200", 1L);
}
@Test
@@ -339,6 +344,7 @@ class DispatchServiceTest {
assertEquals(2, result.total());
verify(items).findByDispatchOrder("100", 1L);
verify(items).findByDispatchOrder("200", 2L);
verify(bolLines, never()).findByDispatchOrder(any(), any());
}
@Test
@@ -347,9 +353,15 @@ class DispatchServiceTest {
dispatch.setId(1L); dispatch.setCustomerId("200"); dispatch.setDispatchNo("PC1");
when(orders.findGlobalByDispatchNo("PC1")).thenReturn(dispatch);
service.platformDetail("PC1");
var expected = new DispatchResponse.BolLine("D2144", "D2144-112-1", "LBE1", "Amazon LBE1",
"NPHK2607030206", "FBA19HFHQCHB", "7UH3F1DW", 138);
when(bolLines.findByDispatchOrder("200", 1L)).thenReturn(List.of(expected));
var result = service.platformDetail("PC1");
verify(items).findByDispatchOrder("200", 1L);
verify(bolLines).findByDispatchOrder("200", 1L);
assertEquals(List.of(expected), result.bolLines());
}
@Test