Files
baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-outbound-dispatch-v1.yaml
T

155 lines
7.0 KiB
YAML

openapi: 3.0.3
info:
title: Phase 3 Container-level Domestic Outbound Dispatch API
version: 2.0.0
paths:
/api/platform/v1/first-leg/outbound-dispatches:
get:
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
parameters:
- {$ref: '#/components/parameters/ContainerBookingIdQuery'}
responses:
'200':
description: Business envelope containing a single matching aggregate in a list
content:
application/json:
schema:
type: object
properties:
data: {type: array, items: {$ref: '#/components/schemas/Aggregate'}}
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}:
get:
summary: Query one container-level aggregate and unified timeline
x-permission: platform:firstleg:outbound-dispatch:view
parameters:
- {$ref: '#/components/parameters/ContainerBookingIdPath'}
responses:
'200':
description: Business envelope containing all inbound bindings and one visible dispatch version
content:
application/json:
schema:
type: object
properties:
data: {$ref: '#/components/schemas/Detail'}
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}/gate:
get:
summary: Evaluate every inbound binding in the fail-closed container outbound gate
x-permission: platform:firstleg:outbound-dispatch:check
parameters:
- {$ref: '#/components/parameters/ContainerBookingIdPath'}
responses:
'200': {description: Business envelope containing the container gate and per-inbound gate results}
/api/platform/v1/first-leg/outbound-dispatches/{containerBookingId}/confirm:
post:
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
parameters:
- {$ref: '#/components/parameters/ContainerBookingIdPath'}
- {$ref: '#/components/parameters/IdempotencyKey'}
requestBody:
required: true
content:
application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}
responses:
'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:
post:
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
parameters:
- {$ref: '#/components/parameters/ContainerBookingIdPath'}
- {$ref: '#/components/parameters/IdempotencyKey'}
requestBody:
required: true
content:
application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}
responses:
'200': {description: Business envelope containing the reverted container aggregate}
components:
parameters:
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}
schemas:
Aggregate:
type: object
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}
oceanBookingStatus: {type: string, nullable: true}
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}
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]
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}
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}
ActionRequest:
type: object
required: [expectedVersion]
properties:
expectedVersion: {type: integer, minimum: 0}
remark: {type: string, maxLength: 500}