253 lines
15 KiB
YAML
253 lines
15 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. 预约创建及派车不要求订舱附件。 Container identities and real container numbers remain in the container/loading modules and are not part of this API. PDA loading completion alone does not complete the appointment. All required containers must be allocated and confirmed departed from the domestic warehouse. Completion atomically creates approved ON_ACCOUNT container payable fees allocated by loaded volume and publishes their immutable snapshots through the existing Finance Outbox. Create, edit, submit and reschedule also recheck departure facts, covering late appointments. Zero amounts create no fee. Cancelled and exception appointments are excluded. RMB is the only appointment currency and maps to CNY in Finance.
|
|
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 an active or completed 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 an active or completed 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 number, excluding bookings that already have an active or completed 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 and LOAD persist their actual event timestamps. Manual DEPART and COMPLETE actions are not supported; confirmation of all required containers departing the domestic warehouse completes the appointment automatically.
|
|
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, 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]
|
|
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}
|
|
|
|
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 number. 订舱附件不作为前置条件。}
|
|
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 with truckingScope DOMESTIC (国内拖车).}
|
|
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: 应付拖车费,最多两位小数;全部订舱需求货柜确认国内离仓后按实装体积分摊生成应付来源;零金额不生成费用。}
|
|
currency: {type: string, enum: [RMB], default: RMB, description: 人民币拖车费;完成时以CNY进入应付费用来源。}
|
|
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]{12}$', example: TA202608180001, description: 派车单号;按北京时间创建日逐日编号}
|
|
oceanBookingId: {type: integer, format: int64, nullable: true}
|
|
oceanBookingNo: {type: string, nullable: true, pattern: '^OB[0-9]{12}$', example: OB202608180001}
|
|
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]}
|
|
attachmentCount: {type: integer, format: int64, minimum: 0, description: Number of attachment references owned by this appointment}
|