253 lines
14 KiB
YAML
253 lines
14 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Truck Appointment API
|
|
version: 2.0.0
|
|
description: A truck appointment belongs to one RELEASED ocean booking, independently of when or how that booking's internal containers are linked. Each booking may have at most one non-terminal appointment. The bill-of-lading number and warehouse are frozen from the booking, which must own at least one active BOOKING_CONFIRMATION or BILL_OF_LADING file. Container identities and real container numbers remain in the container/loading modules and are not part of this API. PDA loading completion does not change a booking-level truck appointment.
|
|
paths:
|
|
/api/platform/v1/truck-appointments:
|
|
get:
|
|
summary: Query truck appointments
|
|
responses:
|
|
'200':
|
|
description: Paged booking-level truck appointments with stable booking snapshots and attachment count
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
type: object
|
|
properties:
|
|
records: {type: array, items: {$ref: '#/components/schemas/DetailStableReferences'}}
|
|
post:
|
|
summary: Create a truck appointment for a released booking
|
|
description: If the booking already has a non-terminal appointment, the server returns A0409 with the existing appointment number and instructs the operator to continue editing. The database booking-level unique key provides the same guarantee for concurrent creates.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: {$ref: '#/components/schemas/SaveRequest'}
|
|
responses:
|
|
'200': {description: 'Created appointment', content: {application/json: {schema: {$ref: '#/components/schemas/DetailEnvelope'}}}}
|
|
'409': {description: 'A0409 - this booking already has a non-terminal appointment; message includes its appointment number and asks the operator to continue editing'}
|
|
/api/platform/v1/truck-appointments/candidates:
|
|
get:
|
|
summary: Query authoritative create-time ocean-booking candidates
|
|
description: Requires platform:firstleg:truck-appointment:list. Returns one row per RELEASED booking with a bill of lading and an active booking-owned confirmation or bill file, excluding bookings that already have a non-terminal truck appointment. Container allocation and loading status do not affect eligibility. Edit pages display their current booking from appointment detail rather than injecting it into this create-only result.
|
|
parameters:
|
|
- {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}, description: Matches booking number or bill of lading}
|
|
- {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: Paged booking candidates, content: {application/json: {schema: {$ref: '#/components/schemas/CandidatePageEnvelope'}}}}
|
|
/api/platform/v1/truck-appointments/{id}:
|
|
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
|
get:
|
|
summary: Query a truck appointment
|
|
responses:
|
|
'200': {description: 'Appointment detail', content: {application/json: {schema: {$ref: '#/components/schemas/DetailStableReferences'}}}}
|
|
put:
|
|
summary: Update an editable truck appointment with optimistic locking
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: {$ref: '#/components/schemas/SaveRequest'}
|
|
responses:
|
|
'200': {description: Updated appointment}
|
|
/api/platform/v1/truck-appointments/{id}/{action}:
|
|
parameters:
|
|
- {$ref: '#/components/parameters/AppointmentId'}
|
|
- {$ref: '#/components/parameters/Action'}
|
|
- {$ref: '#/components/parameters/IdempotencyKey'}
|
|
- {$ref: '#/components/parameters/RequestHash'}
|
|
post:
|
|
summary: Apply an idempotent appointment lifecycle action
|
|
description: All actions transition the booking-level appointment as one aggregate. PICKUP does not accept or write a container number. ARRIVE, LOAD and DEPART persist their actual event timestamps atomically with the status transition.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: {$ref: '#/components/schemas/ActionRequest'}
|
|
responses:
|
|
'200': {description: Updated or replayed appointment}
|
|
/api/platform/v1/truck-appointments/{id}/reschedule:
|
|
parameters:
|
|
- {$ref: '#/components/parameters/AppointmentId'}
|
|
- {$ref: '#/components/parameters/IdempotencyKey'}
|
|
- {$ref: '#/components/parameters/RequestHash'}
|
|
- name: plannedLoadingAt
|
|
in: query
|
|
required: true
|
|
schema: {type: string, format: date-time}
|
|
post:
|
|
summary: Idempotently reschedule an appointment
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: {$ref: '#/components/schemas/ActionRequest'}
|
|
responses:
|
|
'200': {description: Rescheduled or replayed appointment}
|
|
/api/platform/v1/truck-appointments/{id}/history:
|
|
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
|
get:
|
|
summary: Query immutable appointment status history
|
|
responses:
|
|
'200': {description: 'History entries including action, from/to status, operator, reason and time'}
|
|
/api/platform/v1/truck-appointments/{id}/attachments:
|
|
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
|
get:
|
|
summary: Query non-booking operational attachment references owned by the appointment
|
|
description: 'Requires platform:firstleg:truck-appointment:list.'
|
|
responses:
|
|
'200': {description: 'Attachment references', content: {application/json: {schema: {type: array, items: {$ref: '#/components/schemas/Attachment'}}}}}
|
|
post:
|
|
summary: Reference a platform-owned file already scoped to this appointment
|
|
description: 'Requires platform:firstleg:truck-appointment:edit. The file must belong to platform scope + TRUCK_APPOINTMENT_ATTACHMENT + appointmentId.'
|
|
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/AttachmentRequest'}}}}
|
|
responses:
|
|
'200': {description: 'Created attachment reference', content: {application/json: {schema: {$ref: '#/components/schemas/Attachment'}}}}
|
|
/api/platform/v1/truck-appointments/{id}/attachments/upload:
|
|
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
|
post:
|
|
summary: Upload and reference a truck appointment attachment
|
|
description: 'Requires platform:firstleg:truck-appointment:edit.'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required: [attachmentType, file]
|
|
properties:
|
|
attachmentType: {type: string, enum: [DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER], description: Booking confirmations and bills of lading belong to the ocean-booking aggregate}
|
|
file: {type: string, format: binary}
|
|
responses:
|
|
'200': {description: Uploaded attachment reference}
|
|
/api/platform/v1/truck-appointments/{id}/attachments/{attachmentId}:
|
|
parameters:
|
|
- {$ref: '#/components/parameters/AppointmentId'}
|
|
- {$ref: '#/components/parameters/AttachmentId'}
|
|
delete:
|
|
summary: Delete an attachment reference owned by the appointment
|
|
description: 'Requires platform:firstleg:truck-appointment:edit. Deletes the appointment reference only; the File object is retained.'
|
|
responses:
|
|
'200': {description: Reference deleted}
|
|
components:
|
|
parameters:
|
|
AppointmentId: {name: id, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
|
AttachmentId: {name: attachmentId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
|
Action:
|
|
name: action
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
enum: [SUBMIT, DISPATCH, PICKUP, ARRIVE, LOAD, DEPART, COMPLETE, EXCEPTION, CANCEL]
|
|
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
|
RequestHash: {name: X-Request-Hash, in: header, required: false, schema: {type: string, pattern: '^[0-9a-fA-F]{64}$'}}
|
|
schemas:
|
|
CandidatePageEnvelope:
|
|
type: object
|
|
required: [code, message, data, timestamp]
|
|
properties:
|
|
code: {type: string, example: '200'}
|
|
message: {type: string}
|
|
data:
|
|
type: object
|
|
required: [records, page, size, total, pages]
|
|
properties:
|
|
records: {type: array, items: {$ref: '#/components/schemas/Candidate'}}
|
|
page: {type: integer, format: int64}
|
|
size: {type: integer, format: int64}
|
|
total: {type: integer, format: int64}
|
|
pages: {type: integer, format: int64}
|
|
requestId: {type: string, nullable: true}
|
|
traceId: {type: string, nullable: true}
|
|
timestamp: {type: string, format: date-time}
|
|
Candidate:
|
|
type: object
|
|
required: [oceanBookingId, oceanBookingNo, warehouseId, bookingDocumentReady]
|
|
properties:
|
|
oceanBookingId: {type: integer, format: int64}
|
|
oceanBookingNo: {type: string}
|
|
billOfLadingNo: {type: string, nullable: true}
|
|
warehouseId: {type: integer, format: int64}
|
|
warehouseName: {type: string, nullable: true}
|
|
channelCode: {type: string, nullable: true}
|
|
channelName: {type: string, nullable: true}
|
|
bookingDocumentReady: {type: boolean, enum: [true]}
|
|
DetailEnvelope:
|
|
type: object
|
|
required: [code, message, data, timestamp]
|
|
properties:
|
|
code: {type: string, example: '200'}
|
|
message: {type: string}
|
|
data:
|
|
allOf: [{$ref: '#/components/schemas/DetailStableReferences'}]
|
|
nullable: true
|
|
requestId: {type: string, nullable: true}
|
|
traceId: {type: string, nullable: true}
|
|
timestamp: {type: string, format: date-time}
|
|
AttachmentRequest:
|
|
type: object
|
|
required: [fileId, attachmentType]
|
|
properties:
|
|
fileId: {type: integer, format: int64, minimum: 1}
|
|
attachmentType: {type: string, enum: [DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER]}
|
|
fileName: {type: string, maxLength: 255, description: Ignored; authoritative metadata comes from File service}
|
|
fileHash: {type: string, maxLength: 128, description: Ignored; authoritative metadata comes from File service}
|
|
Attachment:
|
|
type: object
|
|
properties:
|
|
id: {type: integer, format: int64}
|
|
appointmentId: {type: integer, format: int64}
|
|
fileId: {type: integer, format: int64}
|
|
attachmentType: {type: string, enum: [DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER]}
|
|
fileName: {type: string}
|
|
fileHash: {type: string}
|
|
visibility: {type: string, enum: [PLATFORM]}
|
|
uploadedBy: {type: integer, format: int64}
|
|
uploadedAt: {type: string, format: date-time}
|
|
ActionRequest:
|
|
type: object
|
|
required: [expectedVersion]
|
|
properties:
|
|
expectedVersion: {type: integer, minimum: 0}
|
|
reason: {type: string, maxLength: 500}
|
|
SaveRequest:
|
|
type: object
|
|
required: [oceanBookingId, carrierId, plannedLoadingAt, expectedVersion]
|
|
properties:
|
|
outboundOrderId: {type: integer, format: int64, minimum: 1, nullable: true}
|
|
oceanBookingId: {type: integer, format: int64, minimum: 1, description: Required RELEASED booking; must have a bill of lading and own an active BOOKING_CONFIRMATION or BILL_OF_LADING file.}
|
|
warehouseId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional consistency hint. The persisted warehouse is frozen from the selected booking.}
|
|
carrierId: {type: integer, format: int64, minimum: 1, description: Required ACTIVE Warehouse supplier ID validated as TRUCKING.}
|
|
carrierName: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint only; the persisted snapshot comes from Warehouse supplier master data.}
|
|
billOfLadingNo: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint; the persisted snapshot is always frozen from the selected RELEASED booking.}
|
|
plannedLoadingAt: {type: string, format: date-time}
|
|
payableAmount: {type: number, format: double, minimum: 0, nullable: true, description: Amount payable to the trucking supplier.}
|
|
currency: {type: string, enum: [RMB, USD], default: RMB, description: Payable amount currency.}
|
|
expectedVersion: {type: integer, minimum: 0, description: Must be 0 on create; current version on edit.}
|
|
DetailStableReferences:
|
|
type: object
|
|
required: [appointmentNo, carrierId, carrierName, attachmentCount]
|
|
description: Stable booking and supplier references returned by appointment detail. Container identities are intentionally absent.
|
|
properties:
|
|
appointmentNo: {type: string, pattern: '^TA-[0-9]{6}-[0-9]{4}$', example: TA-260818-0001, description: 派车单号;按北京时间创建日逐日编号}
|
|
oceanBookingId: {type: integer, format: int64, nullable: true}
|
|
oceanBookingNo: {type: string, nullable: true, pattern: '^OB-[0-9]{6}-[0-9]{4}$', example: OB-260818-0001}
|
|
warehouseId: {type: integer, format: int64, nullable: true}
|
|
warehouseName: {type: string, nullable: true}
|
|
shippingCompanyId: {type: integer, format: int64, nullable: true}
|
|
shippingCompanyName: {type: string, nullable: true}
|
|
bookingAgentId: {type: integer, format: int64, nullable: true}
|
|
bookingAgentName: {type: string, nullable: true}
|
|
carrierId: {type: integer, format: int64}
|
|
carrierName: {type: string}
|
|
billOfLadingNo: {type: string, maxLength: 128, nullable: true, description: Frozen bill of lading snapshot used by dispatch readiness.}
|
|
payableAmount: {type: number, format: double, minimum: 0, nullable: true}
|
|
currency: {type: string, enum: [RMB, USD]}
|
|
attachmentCount: {type: integer, format: int64, minimum: 0, description: Number of attachment references owned by this appointment}
|