2026-08-08 22:36:41 +08:00
openapi : 3.0.3
info :
2026-08-26 12:05:42 +08:00
title : Phase 3 Container-level Domestic Outbound Dispatch API
version : 2.0.0
2026-08-08 22:36:41 +08:00
paths :
/api/platform/v1/first-leg/outbound-dispatches :
get :
2026-08-26 12:05:42 +08:00
summary : Query the outbound aggregate for one container booking
description : Returns one container-level aggregate containing every real inbound binding. No per-inbound partial outbound aggregate is exposed.
x-permission : platform:firstleg:outbound-dispatch:list
2026-08-08 22:36:41 +08:00
parameters :
2026-08-26 12:05:42 +08:00
- {$ref : '#/components/parameters/ContainerBookingIdQuery' }
2026-08-08 22:36:41 +08:00
responses :
2026-08-17 19:51:54 +08:00
'200' :
2026-08-26 12:05:42 +08:00
description : Business envelope containing a single matching aggregate in a list
2026-08-17 19:51:54 +08:00
content :
application/json :
schema :
type : object
properties :
data : {type : array, items : {$ref : '#/components/schemas/Aggregate' }}
2026-08-26 12:05:42 +08:00
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}:
2026-08-08 22:36:41 +08:00
get :
2026-08-26 12:05:42 +08:00
summary : Query one container-level aggregate and unified timeline
x-permission : platform:firstleg:outbound-dispatch:view
2026-08-08 22:36:41 +08:00
parameters :
2026-08-26 12:05:42 +08:00
- {$ref : '#/components/parameters/ContainerBookingIdPath' }
2026-08-08 22:36:41 +08:00
responses :
2026-08-17 19:51:54 +08:00
'200' :
2026-08-26 12:05:42 +08:00
description : Business envelope containing all inbound bindings and one visible dispatch version
2026-08-17 19:51:54 +08:00
content :
application/json :
schema :
type : object
properties :
2026-08-26 12:05:42 +08:00
data : {$ref : '#/components/schemas/Detail' }
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}/gate:
2026-08-08 22:36:41 +08:00
get :
2026-08-26 12:05:42 +08:00
summary : Evaluate every inbound binding in the fail-closed container outbound gate
2026-08-26 13:58:33 +08:00
description : Loading evidence is evaluated once for the physical container/loading plan and the same result applies to every inbound binding. The customs gate is resolved once from the current container CUSTOMS_DECLARATION whose completedAt must be non-null; upload alone remains blocked and fl_customs_case is not an alternative gate fact. A missing standalone sailing schedule is reference-only and never affects gate.passed.
2026-08-26 12:05:42 +08:00
x-permission : platform:firstleg:outbound-dispatch:check
2026-08-08 22:36:41 +08:00
parameters :
2026-08-26 12:05:42 +08:00
- {$ref : '#/components/parameters/ContainerBookingIdPath' }
2026-08-08 22:36:41 +08:00
responses :
2026-08-26 12:05:42 +08:00
'200' : {description : Business envelope containing the container gate and per-inbound gate results}
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}/confirm:
2026-08-08 22:36:41 +08:00
post :
2026-08-26 12:05:42 +08:00
summary : Atomically confirm domestic departure for the entire container
description : Locks every real inbound binding, requires every gate to pass, advances all technical rows in one transaction, records one visible history, and creates the CarrierScheduleQuery/DEPARTED_WAREHOUSE fact once. No partial success is possible.
x-permission : platform:firstleg:outbound-dispatch:confirm
2026-08-08 22:36:41 +08:00
parameters :
2026-08-26 12:05:42 +08:00
- {$ref : '#/components/parameters/ContainerBookingIdPath' }
2026-08-08 22:36:41 +08:00
- {$ref : '#/components/parameters/IdempotencyKey' }
requestBody :
required : true
content :
application/json : {schema : {$ref : '#/components/schemas/ActionRequest' }}
responses :
2026-08-26 12:05:42 +08:00
'200' : {description : Same key and same payload replays the single container-level result; stale version or changed payload returns A0409}
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}/reverse:
2026-08-08 22:36:41 +08:00
post :
2026-08-26 12:05:42 +08:00
summary : Atomically reverse the entire container before any actual shipping node exists
description : Once an actual DEPARTED_WAREHOUSE node exists, reversal is rejected with business code A0409 so an outbound fact can never coexist with READY_FOR_DISPATCH.
x-permission : platform:firstleg:outbound-dispatch:reverse
2026-08-08 22:36:41 +08:00
parameters :
2026-08-26 12:05:42 +08:00
- {$ref : '#/components/parameters/ContainerBookingIdPath' }
2026-08-08 22:36:41 +08:00
- {$ref : '#/components/parameters/IdempotencyKey' }
requestBody :
required : true
content :
application/json : {schema : {$ref : '#/components/schemas/ActionRequest' }}
responses :
2026-08-26 12:05:42 +08:00
'200' : {description : Business envelope containing the reverted container aggregate}
2026-08-08 22:36:41 +08:00
components :
parameters :
2026-08-26 12:05:42 +08:00
ContainerBookingIdQuery :
name : containerBookingId
in : query
required : true
schema : {type : integer, format : int64, minimum : 1 }
ContainerBookingIdPath :
name : containerBookingId
in : path
required : true
schema : {type : integer, format : int64, minimum : 1 }
IdempotencyKey :
name : Idempotency-Key
in : header
required : true
schema : {type : string, maxLength : 128 }
2026-08-08 22:36:41 +08:00
schemas :
2026-08-17 19:51:54 +08:00
Aggregate :
type : object
2026-08-26 12:05:42 +08:00
required : [ containerBookingId, cabinetNo, loadingPlanId, loadingPlanStatus, dispatchStatus, version, inbounds, gate]
properties :
containerBookingId : {type : integer, format : int64}
cabinetNo : {type : string, description : Stable cabinet number}
containerNo : {type : string, nullable : true, description : Real container number}
loadingPlanId : {type : integer, format : int64}
loadingPlanStatus : {type : string}
oceanBookingId : {type : integer, format : int64, nullable : true }
2026-08-26 13:07:30 +08:00
oceanBookingStatus : {type : string, nullable : true, description : Booking reference retained independently from the non-blocking standalone sailing schedule}
2026-08-26 12:36:14 +08:00
truckAppointmentId : {type : integer, format : int64, nullable : true, description : Reference-only truck appointment; absence never blocks manual outbound confirmation}
truckAppointmentStatus : {type : string, nullable : true, description : Reference-only status; PENDING or any non-terminal state never affects gate.passed}
2026-08-26 12:05:42 +08:00
dispatchStatus : {type : string, description : One visible container-level status}
version : {type : integer, minimum : 0, description : One optimistic-lock version shared by all technical inbound rows}
inbounds :
type : array
minItems : 1
items : {$ref : '#/components/schemas/InboundBinding' }
gate : {$ref : '#/components/schemas/ContainerGateResult' }
InboundBinding :
type : object
required : [ orderId, orderNo, inboundId, inboundCode, tenantId, inboundStatus]
2026-08-17 19:51:54 +08:00
properties :
orderId : {type : integer, format : int64}
orderNo : {type : string}
inboundId : {type : integer, format : int64}
inboundCode : {type : string}
tenantId : {type : integer, format : int64}
customerId : {type : integer, format : int64, nullable : true }
customerName : {type : string, nullable : true }
inboundStatus : {type : string}
pickTaskId : {type : integer, format : int64, nullable : true }
pickTaskStatus : {type : string, nullable : true }
2026-08-26 12:05:42 +08:00
ContainerGateResult :
type : object
required : [ passed, inbounds, checkedAt]
properties :
passed : {type : boolean}
inbounds :
type : array
items :
type : object
required : [ inboundId, inboundCode, gate]
properties :
inboundId : {type : integer, format : int64}
inboundCode : {type : string}
gate : {type : object}
checkedAt : {type : string, format : date-time}
Detail :
type : object
required : [ aggregate, timeline]
properties :
aggregate : {$ref : '#/components/schemas/Aggregate' }
timeline :
type : array
description : Unified immutable facts; a container confirm or reverse contributes one visible history item.
items : {type : object}
2026-08-08 22:36:41 +08:00
ActionRequest :
type : object
2026-08-26 12:05:42 +08:00
required : [ expectedVersion]
2026-08-08 22:36:41 +08:00
properties :
expectedVersion : {type : integer, minimum : 0 }
remark : {type : string, maxLength : 500 }