feat(orders): expose customer-owned loaded transport facts

This commit is contained in:
2026-10-05 01:06:11 +08:00
parent 5ad3fec449
commit b000b056d1
15 changed files with 286 additions and 9 deletions
@@ -48,7 +48,11 @@ public interface FirstlegUnifiedOrderClient {
record Summary(Inbound inbound, String mainStatus, String nextAction, Boolean customerFeeConfirmationRequired,
Preload preload, Loading loading, Delivery delivery) {}
record Detail(Summary summary, List<Timeline> timeline) {}
record Detail(Summary summary, List<Timeline> timeline, List<TransportFragment> transportFragments) {}
record TransportFragment(String inboundCode, String cabinetNo, String containerNo, List<TransportNode> nodes) {}
record TransportNode(String standardNode, Integer nodeSequence, String providerCode,
LocalDateTime estimatedAt, LocalDateTime actualAt,
String locationCode, String locationName) {}
record Inbound(Long id, Long tenantId, Long orderId, String orderNo, String inboundCode,
String processStatus, String orderStatusSnapshot, String measureReviewStatus,
Integer version,
@@ -48,7 +48,13 @@ public final class UnifiedOrderResponse {
List<String> memberInboundCodes) {}
public record Detail(Row summary, OrderDetailResponse order,
List<TimelineEvent> timeline) {}
List<TimelineEvent> timeline,
List<TransportFragment> transportFragments) {}
public record TransportFragment(String inboundCode, String cabinetNo, String containerNo,
List<TransportNode> nodes) {}
public record TransportNode(String standardNode, Integer nodeSequence, String providerCode,
LocalDateTime estimatedAt, LocalDateTime actualAt,
String locationCode, String locationName) {}
public record MeasurementSummary(String reviewStatus, BigDecimal actualGrossWeightKg,
BigDecimal actualVolumeCbm, BigDecimal chargeableWeightKg,
@@ -153,7 +153,13 @@ public class UnifiedOrderQueryService {
}
timeline.sort(Comparator.comparing(UnifiedOrderResponse.TimelineEvent::occurredAt,
Comparator.nullsLast(Comparator.naturalOrder())));
return new UnifiedOrderResponse.Detail(summary, orderDetail, timeline);
var fragments = inbound == null ? List.<UnifiedOrderResponse.TransportFragment>of()
: inbound.transportFragments().stream().map(fragment -> new UnifiedOrderResponse.TransportFragment(
fragment.inboundCode(), fragment.cabinetNo(), fragment.containerNo(),
fragment.nodes().stream().map(node -> new UnifiedOrderResponse.TransportNode(
node.standardNode(), node.nodeSequence(), node.providerCode(), node.estimatedAt(),
node.actualAt(), node.locationCode(), node.locationName())).toList())).toList();
return new UnifiedOrderResponse.Detail(summary, orderDetail, timeline, fragments);
}
private PageResult<UnifiedOrderResponse.Row> pageFromFirstleg(Long tenantId, UnifiedOrderQuery query,
@@ -910,6 +910,25 @@ components:
scheme: bearer
bearerFormat: JWT
schemas:
CustomerTransportFragment:
type: object
required: [nodes]
properties:
inboundCode: {type: string, nullable: true}
cabinetNo: {type: string, nullable: true}
containerNo: {type: string, nullable: true}
nodes: {type: array, items: {$ref: '#/components/schemas/CustomerTransportNode'}}
CustomerTransportNode:
type: object
properties:
standardNode: {type: string, description: 标准船节点;进港为AT_ORIGIN_PORT,不表示开航}
nodeSequence: {type: integer}
providerCode: {type: string, nullable: true}
estimatedAt: {type: string, format: date-time, nullable: true}
actualAt: {type: string, format: date-time, nullable: true, description: 仅实际时间表示实际发生,不能用预计时间替代}
locationCode: {type: string, nullable: true}
locationName: {type: string, nullable: true}
CargoLabelBindingListEnvelope:
type: object
required: [code, data]
@@ -1110,7 +1129,12 @@ components:
data: {$ref: '#/components/schemas/UnifiedOrderDetail'}
UnifiedOrderDetail:
type: object
required: [transportFragments]
properties:
transportFragments:
type: array
description: 仅当前客户当前订单已完成实装片段的柜级运输事实,按入仓码和柜次分别返回,不包含跳拣来源或其他客户
items: {$ref: '#/components/schemas/CustomerTransportFragment'}
summary: {$ref: '#/components/schemas/UnifiedOrderRow'}
order:
type: object