Files
baiyue-firstleg/firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml
T

368 lines
25 KiB
YAML

openapi: 3.0.3
info:
title: Ocean Booking API
version: 2.2.0
description: Booking-owned files use File businessType OCEAN_BOOKING_DOCUMENT and businessId=oceanBookingId. 放舱不要求上传订舱确认文件或提单文件。 Released transport and declared cargo snapshots and file links are read-only. Manual actual cargo may be supplemented with version, idempotency and audit reason.
paths:
/api/platform/v1/ocean-bookings:
get:
summary: List DRAFT and RELEASED bookings
description: containerCount is the sum of quantity across all container requirements for the booking. It is zero when no requirement exists and never represents linked or ACTIVE containers.
responses:
'200':
description: Booking page; each row uses the frozen requirement-total containerCount semantic
post:
summary: Create an editable DRAFT
description: Creates only the booking aggregate and container requirements; no containerNo, container row, or allocation is created.
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreateRequest'}}}}
responses: {'200': {description: DRAFT detail}}
/api/platform/v1/ocean-bookings/{oceanBookingId}:
get: {summary: Get booking detail, parameters: [{$ref: '#/components/parameters/OceanBookingId'}], responses: {'200': {description: Detail}}}
put:
summary: Replace a DRAFT
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/UpdateRequest'}}}}
responses: {'200': {description: Updated DRAFT}}
delete:
summary: Delete a DRAFT; RELEASED is immutable
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}}}
responses: {'200': {description: Deleted}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/estimated-arrival:
put:
summary: 放舱后维护预计到港
description: 仅允许RELEASED订舱按当前版本与幂等键维护estimatedArrivalAt,不得清空或早于预计开船时间;其他运输、预报、关系方、柜型与附件仍冻结,保存前后快照及操作人。不修改实际到港或计划到仓。
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/EstimatedArrivalRequest'}}}}
responses: {'200': {description: 已保存的RELEASED订舱详情,包含当前版本和预计到港}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/actual-cargo:
put:
summary: Supplement manual actual cargo on a RELEASED booking
description: Only actual package count, gross weight and volume may change. Transport and declared cargo remain frozen. Requires expectedVersion, Idempotency-Key and a reason; saves before/after audit snapshots atomically. Null means not recorded, never zero or a fallback to declared cargo.
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ActualCargoRequest'}}}}
responses: {'200': {description: Updated RELEASED detail}, '409': {description: State or version conflict}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/release:
post:
summary: Validate and permanently release a DRAFT
description: 放舱校验已保存的订舱业务资料,不以订舱确认文件或提单文件作为前置条件。 The customer is optional; a supplied customer must remain ACTIVE. Null denotes no single customer snapshot, never public access. It never requires or creates a real container number.
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ReleaseRequest'}}}}
responses: {'200': {description: RELEASED detail}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/documents:
get:
summary: List current and invalidated booking-owned file references
x-permission: platform:firstleg:ocean-booking:list
parameters: [{$ref: '#/components/parameters/OceanBookingId'}]
responses:
'200': {description: Booking document history, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentListResponse'}}}}
post:
summary: Reference an existing File object in a DRAFT booking
description: File scope must be platform tenantId=0 + OCEAN_BOOKING_DOCUMENT + oceanBookingId, independent of the optional customer snapshot. The booking version is incremented atomically.
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentRequest'}}}}
responses:
'200': {description: Created reference and latest booking version, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentMutationResponse'}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/documents/upload:
post:
summary: Upload and associate a file with a DRAFT booking
description: Uploads through File businessType OCEAN_BOOKING_DOCUMENT and increments the booking version atomically.
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [expectedVersion, documentType, file]
properties:
expectedVersion: {type: integer, minimum: 0}
documentType: {$ref: '#/components/schemas/DocumentType'}
file: {type: string, format: binary}
responses:
'200': {description: Created reference and latest booking version, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentMutationResponse'}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/documents/{documentId}:
delete:
summary: Logically invalidate a booking file reference while DRAFT
description: The File object and immutable reference history are retained; RELEASED bookings are read-only.
x-permission: platform:firstleg:ocean-booking:edit
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/DocumentId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentActionRequest'}}}}
responses:
'200': {description: Invalidation result and latest booking version, content: {application/json: {schema: {$ref: '#/components/schemas/DocumentInvalidationResponse'}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/documents/{documentId}/download:
get:
summary: Download an active booking-owned file
x-permission: platform:firstleg:ocean-booking:list
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/DocumentId'}]
responses:
'200': {description: Original file bytes, content: {application/octet-stream: {schema: {type: string, format: binary}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/prints/shipping-instruction:
get:
summary: Render the current saved booking as an export shipping instruction PDF
description: Firstleg maps the authoritative saved aggregate (DRAFT or RELEASED, with missing fields displayed as —) to SEA_FREIGHT_SHIPPING_INSTRUCTION_A4_V1 and synchronously proxies the generated PDF. Cargo uses saved declared values; container requirements describe requested containers only. Count in words is generated from the declared count and packageUnit.
x-permission: platform:firstleg:ocean-booking:list
parameters: [{$ref: '#/components/parameters/OceanBookingId'}]
responses:
'200': {description: PDF or a business-error JSON envelope, content: {application/pdf: {schema: {type: string, format: binary}}, application/json: {schema: {type: object}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/prints/bill-of-lading-confirmation:
get:
summary: Render the current saved booking as a bill-of-lading confirmation PDF
description: Firstleg maps the authoritative saved aggregate (DRAFT or RELEASED, with missing fields displayed as —) to BILL_OF_LADING_CONFIRMATION_A4_V1 and synchronously proxies the generated PDF. Cargo uses saved manually entered actual values only; missing actual data displays — and never falls back to declared values. This is a confirmation copy; the externally issued bill of lading remains an uploaded attachment.
x-permission: platform:firstleg:ocean-booking:list
parameters: [{$ref: '#/components/parameters/OceanBookingId'}]
responses:
'200': {description: PDF or a business-error JSON envelope, content: {application/pdf: {schema: {type: string, format: binary}}, application/json: {schema: {type: object}}}}
/api/platform/v1/ocean-bookings/link-candidates:
get:
summary: Page released booking requirement candidates for one container
description: >-
Requires platform:firstleg:ocean-booking:list. Returns only linkable requirement candidates:
RELEASED, remainingQuantity greater than zero, exact warehouseId and containerType, and a concrete
booking channel belonging to channelGroupCode.
Capacity and max weight are derived display snapshots and never eligibility fields. Allocated quantity
counts only ACTIVE containers still pointing to the same booking requirement and is calculated by one
bounded set query; booking detail calls are not used. An empty result is a normal successful page.
Final target-container mutability is revalidated by the link command.
x-permission: platform:firstleg:ocean-booking:list
parameters:
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
- {name: channelGroupCode, in: query, required: true, schema: {type: string, maxLength: 64}}
- {name: containerType, in: query, required: true, schema: {type: string, enum: [40GP, 40HQ, 45HQ]}}
- {name: page, in: query, required: false, schema: {type: integer, format: int64, minimum: 1, default: 1}}
- {name: size, in: query, required: false, schema: {type: integer, format: int64, minimum: 1, maximum: 200, default: 20}}
responses:
'200':
description: Business envelope containing a bounded candidate page
content:
application/json:
schema: {$ref: '#/components/schemas/LinkCandidateResponse'}
/api/platform/v1/ocean-bookings/channels:
get:
summary: List ACTIVE channels for an origin warehouse
description: Requires platform:firstleg:ocean-booking:list. Channel names are resolved from the current ACTIVE price book and frozen by the server on save.
parameters:
- {name: originWarehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
responses:
'200':
description: Channel options
content:
application/json:
schema: {type: array, items: {$ref: '#/components/schemas/ChannelOption'}}
/api/platform/v1/ocean-bookings/booking-agents:
get:
summary: List ACTIVE BOOKING agents associated with one carrier code
description: Requires platform:firstleg:ocean-booking:list. Filtering is enforced by Warehouse master data; clients must not infer associations.
x-permission: platform:firstleg:ocean-booking:list
parameters:
- {name: carrierCode, in: query, required: true, schema: {type: string, enum: [MATSON, SML, ZIM, COSCO, EMC, HEDE, OTHER]}}
responses:
'200':
description: Server-filtered booking agent options
content:
application/json:
schema: {type: array, items: {$ref: '#/components/schemas/BookingAgentOption'}}
components:
parameters:
OceanBookingId: {name: oceanBookingId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
DocumentId: {name: documentId, 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:
CreateRequest: {type: object, required: [expectedVersion, data], properties: {expectedVersion: {type: integer, enum: [0]}, data: {$ref: '#/components/schemas/DraftData'}}}
UpdateRequest: {type: object, required: [expectedVersion, data], properties: {expectedVersion: {type: integer, minimum: 0}, data: {$ref: '#/components/schemas/DraftData'}}}
DraftData:
type: object
description: Fields may be incomplete in DRAFT. RELEASE validates the complete persisted aggregate.
properties:
customerId: {type: integer, format: int64, minimum: 1, nullable: true, description: 可选启用客户ID;多客户订舱留空,空值不是公开访问授权}
customerName: {type: string, nullable: true, description: 可选名称提示;有客户ID时由服务端校验并保存客户主数据名称,清空客户时同时置null}
channelCode: {type: string, nullable: true, description: 固定计划物流渠道组编码}
warehouseId: {type: integer, format: int64, nullable: true}
billOfLadingNo: {type: string, nullable: true, description: 全局唯一提单号;草稿未取得时可为空}
shippingCompanyId: {type: integer, format: int64, nullable: true}
shippingCompanyName: {type: string, nullable: true}
carrierCode:
type: string
enum: [MATSON, SML, ZIM, COSCO, EMC, HEDE, OTHER]
nullable: true
description: Frozen standard carrier code. Required before RELEASE; OTHER means manual carrier-node tracking.
bookingAgentId:
type: integer
format: int64
nullable: true
description: Warehouse supplier ID; release validates ACTIVE category BOOKING; booking suppliers do not use domestic/international scope.
bookingAgentName:
type: string
nullable: true
description: Read-only consistency hint; the persisted snapshot always comes from Warehouse.
vesselName: {type: string, nullable: true}
voyageNo: {type: string, nullable: true}
departurePortCode: {type: string, nullable: true}
departurePortName: {type: string, nullable: true}
destinationPortCode: {type: string, nullable: true}
destinationPortName: {type: string, nullable: true}
placeOfDelivery: {type: string, nullable: true}
estimatedSailingAt: {type: string, format: date-time, nullable: true}
estimatedArrivalAt: {type: string, format: date-time, nullable: true, description: 草稿可留空,放舱前必填;不得早于预计开船时间,关联柜次后作为到港日历及默认到仓日期来源}
remark: {type: string, nullable: true}
freightTerm: {type: string, maxLength: 64, nullable: true, description: 运费条款;新建页面默认FREIGHT PREPAID}
serviceTerm: {type: string, maxLength: 64, nullable: true, description: 运输条款;新建页面默认CY-CY}
packageUnit: {type: string, enum: [CARTONS, PCS, PALLETS], nullable: true}
cargoDescription: {type: string, maxLength: 1000, nullable: true}
marksAndNos: {type: string, maxLength: 1000, nullable: true}
declaredPackageCount: {type: integer, minimum: 1, maximum: 999999999, nullable: true}
declaredGrossWeightKg: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
declaredVolumeCbm: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
actualPackageCount: {type: integer, minimum: 1, maximum: 999999999, nullable: true}
actualGrossWeightKg: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
actualVolumeCbm: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
parties: {type: array, nullable: true, items: {$ref: '#/components/schemas/PartyRequest'}}
containerRequirements: {type: array, nullable: true, items: {$ref: '#/components/schemas/ContainerRequirementRequest'}}
PartyRequest: {type: object, required: [partyType, partyName], properties: {partyType: {type: string, enum: [SHIPPER, CONSIGNEE, NOTIFY_PARTY]}, partyName: {type: string}, address: {type: string, nullable: true}, contactName: {type: string, nullable: true}, contactPhone: {type: string, nullable: true}, contactEmail: {type: string, format: email, maxLength: 255, nullable: true}, taxNo: {type: string, maxLength: 128, nullable: true}}}
ContainerRequirementRequest: {type: object, required: [containerType, containerKind, quantity, capacityCbm, maxWeightKg], properties: {containerType: {type: string, enum: [40GP, 40HQ, 45HQ], default: 40HQ}, containerKind: {type: string}, quantity: {type: integer, minimum: 1}, capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, description: 统一实装上限;40GP=58、40HQ=68、45HQ=86}, maxWeightKg: {type: number, enum: [19500], description: 统一货重上限}}}
EstimatedArrivalRequest:
type: object
additionalProperties: false
required: [expectedVersion, estimatedArrivalAt]
properties:
expectedVersion: {type: integer, minimum: 0}
estimatedArrivalAt: {type: string, format: date-time}
ActualCargoRequest:
type: object
additionalProperties: false
required: [expectedVersion, reason]
properties:
expectedVersion: {type: integer, minimum: 0}
reason: {type: string, minLength: 1, maxLength: 1000}
actualPackageCount: {type: integer, minimum: 1, maximum: 999999999, nullable: true}
actualGrossWeightKg: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
actualVolumeCbm: {type: number, minimum: 0, exclusiveMinimum: true, maximum: 999999999999.999, multipleOf: 0.001, nullable: true}
ReleaseRequest: {type: object, required: [expectedVersion], properties: {expectedVersion: {type: integer, minimum: 0}, reason: {type: string, nullable: true}}}
ActionRequest: {type: object, required: [expectedVersion], properties: {expectedVersion: {type: integer, minimum: 0}, reason: {type: string, nullable: true}}}
DocumentType: {type: string, enum: [BOOKING_CONFIRMATION, BILL_OF_LADING]}
DocumentRequest:
type: object
required: [expectedVersion, fileId, documentType]
properties:
expectedVersion: {type: integer, minimum: 0}
fileId: {type: integer, format: int64, minimum: 1}
documentType: {$ref: '#/components/schemas/DocumentType'}
DocumentActionRequest: {type: object, required: [expectedVersion], properties: {expectedVersion: {type: integer, minimum: 0}}}
OceanBookingDocument:
type: object
required: [id, oceanBookingId, fileId, documentType, fileName, valid, uploadedBy, uploadedAt]
properties:
id: {type: integer, format: int64}
oceanBookingId: {type: integer, format: int64}
fileId: {type: integer, format: int64}
documentType: {$ref: '#/components/schemas/DocumentType'}
fileName: {type: string}
fileHash: {type: string, nullable: true}
sizeBytes: {type: integer, format: int64, nullable: true}
contentType: {type: string, nullable: true}
valid: {type: boolean}
uploadedBy: {type: integer, format: int64}
uploadedByName: {type: string, nullable: true}
uploadedAt: {type: string, format: date-time}
invalidatedBy: {type: integer, format: int64, nullable: true}
invalidatedByName: {type: string, nullable: true}
invalidatedAt: {type: string, format: date-time, nullable: true}
DocumentMutation:
type: object
required: [document, bookingVersion]
properties:
document: {$ref: '#/components/schemas/OceanBookingDocument'}
bookingVersion: {type: integer, minimum: 1}
DocumentInvalidation:
type: object
required: [documentId, bookingVersion]
properties:
documentId: {type: integer, format: int64}
bookingVersion: {type: integer, minimum: 1}
DocumentListResponse:
type: object
properties: {code: {type: string, example: '200'}, data: {type: array, items: {$ref: '#/components/schemas/OceanBookingDocument'}}}
DocumentMutationResponse:
type: object
properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/DocumentMutation'}}
DocumentInvalidationResponse:
type: object
properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/DocumentInvalidation'}}
ChannelOption:
type: object
required: [priceBookVersionId, channelCode, channelName, priceModel, pricingTaxModes]
properties:
priceBookVersionId: {type: integer, format: int64}
channelCode: {type: string}
channelName: {type: string}
priceModel: {type: string}
pricingTaxModes: {type: array, items: {type: string}}
BookingAgentOption:
type: object
required: [supplierId, name, carrierCodes]
properties:
supplierId: {type: integer, format: int64}
name: {type: string}
carrierCodes: {type: array, items: {type: string, enum: [MATSON, SML, ZIM, COSCO, EMC, HEDE, OTHER]}}
LinkCandidate:
type: object
required: [oceanBookingId, bookingNo, billOfLadingNo, requirementId, containerType, capacityCbm, maxWeightKg, requiredQuantity, allocatedQuantity, remainingQuantity, customerName, warehouseName, channelName]
properties:
oceanBookingId: {type: string, pattern: '^[0-9]+$'}
bookingNo: {type: string}
billOfLadingNo: {type: string, description: 已发布订舱的全局唯一提单号}
requirementId: {type: string, pattern: '^[0-9]+$'}
containerType: {type: string}
capacityCbm: {type: number}
maxWeightKg: {type: number}
requiredQuantity: {type: integer, minimum: 1}
allocatedQuantity: {type: integer, format: int64, minimum: 0}
remainingQuantity: {type: integer, format: int64, minimum: 1}
customerName: {type: string, nullable: true}
warehouseName: {type: string}
channelName: {type: string}
LinkCandidatePage:
type: object
required: [records, page, size, total, pages]
properties:
records: {type: array, items: {$ref: '#/components/schemas/LinkCandidate'}}
page: {type: integer, format: int64, minimum: 1}
size: {type: integer, format: int64, minimum: 1}
total: {type: integer, format: int64, minimum: 0}
pages: {type: integer, format: int64, minimum: 0}
LinkCandidateResponse:
type: object
required: [code, data]
properties:
code: {type: string, example: '200'}
message: {type: string, nullable: true}
data: {$ref: '#/components/schemas/LinkCandidatePage'}
OceanBookingListRow:
type: object
properties:
containerCount:
type: integer
format: int64
minimum: 0
description: Sum of all container requirement quantities; independent of allocation, linking or unlinking.
Detail:
type: object
required: [oceanBooking, parties, containerRequirements, containers, documents, histories]
properties:
oceanBooking:
allOf:
- {$ref: '#/components/schemas/DraftData'}
type: object
properties:
bookingNo: {type: string, pattern: '^OB[0-9]{12}$', example: OB202608180001, description: 平台订舱号;按北京时间创建日逐日编号}
parties: {type: array}
containerRequirements: {type: array}
containers: {type: array, description: Existing containers allocated after port pickup linking}
documents: {type: array, items: {$ref: '#/components/schemas/OceanBookingDocument'}, description: Current and invalidated booking-owned file references}
histories: {type: array}