394 lines
23 KiB
YAML
394 lines
23 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Phase 3 Container PDA Picking API
|
|
version: 1.0.0
|
|
paths:
|
|
/api/platform/v1/first-leg/pda-loading/warehouses:
|
|
get:
|
|
summary: List active domestic warehouses available to the PDA operator
|
|
responses:
|
|
'200':
|
|
description: Warehouse master-data options used to scope PDA pending-task lists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: {$ref: '#/components/schemas/WarehouseOption'}
|
|
/api/platform/v1/first-leg/pda-loading/tasks:
|
|
get:
|
|
summary: Find the active container picking task in one platform warehouse
|
|
parameters:
|
|
- {name: cabinetNo, in: query, required: false, schema: {type: string}}
|
|
- {name: loadingPlanId, in: query, required: false, schema: {type: integer, format: int64}}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
responses:
|
|
'200':
|
|
description: Task with assignment, review, stable carton codes and item-level versions
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/Task'}}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/pending:
|
|
get:
|
|
summary: List active tasks available to the authenticated PDA operator
|
|
parameters:
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
- {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}}
|
|
responses:
|
|
'200':
|
|
description: Unassigned tasks and tasks assigned to the current operator, including AWAITING_REVIEW tasks with zero unresolved items so PDA can enter evidence and final completion; no customer selector required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items: {$ref: '#/components/schemas/PendingTask'}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}:
|
|
get:
|
|
summary: Read one PDA task by stable task identity
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
responses:
|
|
'200':
|
|
description: Task with current transport display and ACTIVE container lifecycle fields
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/Task'}}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/claim:
|
|
post:
|
|
summary: Atomically claim an unassigned task for the authenticated operator
|
|
description: Claiming the canonical PDA task idempotently starts its plan and advances every associated inbound from WAIT_SPLIT to LOADING with optimistic locking; workflowStatus may later become AWAITING_REVIEW while the task remains LOADING until manual completion.
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
|
requestBody:
|
|
required: true
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/ClaimRequest'}}}
|
|
responses:
|
|
'200': {description: Claimed task or A0409 when another operator owns it}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/inventory-snapshot:
|
|
get:
|
|
summary: Read the current Warehouse inventory snapshot before atomic submission
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
responses:
|
|
'200':
|
|
description: Current occupied resources grouped by inbound order with a stable snapshot token
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/InventorySnapshot'}}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/assign:
|
|
post:
|
|
summary: Assign or reassign a task with optimistic locking
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
|
requestBody:
|
|
required: true
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/AssignRequest'}}}
|
|
responses:
|
|
'200': {description: Assigned task or A0409 on assignment version conflict}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/pick:
|
|
post:
|
|
summary: Increment a picked item owned by the authenticated assignee
|
|
description: The first successful pick also repairs the same WAIT_SPLIT to LOADING transition when the plan was already started, without using containerNo as an association key.
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
|
responses: {'200': {description: Updated task}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/partial:
|
|
post:
|
|
summary: Record the final partial quantity for one assigned item
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
|
responses: {'200': {description: Task awaiting review when every item is resolved}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/skip:
|
|
post:
|
|
summary: Resolve an untouched assigned item as skipped
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
|
responses: {'200': {description: Task awaiting review when every item is resolved}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/batch:
|
|
post:
|
|
summary: Atomically submit the complete online PDA draft once
|
|
description: A batch containing PICK or PARTIAL idempotently advances associated inbound process status to LOADING before evidence operations become available.
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
|
requestBody:
|
|
required: true
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/BatchRequest'}}}
|
|
responses:
|
|
'200': {description: 'Completed task, identical replay result, or A0409 when any snapshot changed'}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/review:
|
|
post:
|
|
summary: 人工确认装柜完成的兼容入口
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
|
requestBody:
|
|
required: true
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/ReviewRequest'}}}
|
|
responses:
|
|
'200': {description: 操作员明确确认后完成装柜}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/complete:
|
|
post:
|
|
summary: PDA拣货全部处理后人工确认装柜完成
|
|
description: 短事务校验任务版本、全部明细和operationId请求摘要,持久化可靠完成命令并将任务置为COMPLETING后立即返回PROCESSING事实。后台worker按稳定入库单顺序以checkpoint恢复Order、Warehouse、入库状态、计划和事件;请求事务内不调用任何远程服务。同operationId重放立即返回相同PROCESSING或最终结果;不同operationId在处理期间返回A0409。
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
|
requestBody:
|
|
required: true
|
|
content: {application/json: {schema: {$ref: '#/components/schemas/CompleteLoadingRequest'}}}
|
|
responses:
|
|
'200': {description: 已可靠受理;taskStatus=COMPLETING且workflowStatus=PROCESSING,或幂等返回最终COMPLETED结果;冲突返回A0409}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/operations:
|
|
get:
|
|
summary: Read immutable task-level claim, assignment and review audit records
|
|
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
|
responses:
|
|
'200': {description: Ordered operation history}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/evidence:
|
|
get:
|
|
summary: 查询当前领取人的柜次装柜证据
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
responses: {'200': {description: 仅platform:firstleg:pda:work且任务必须由当前用户领取}}
|
|
post:
|
|
summary: 通过Firstleg代理上传并登记当前柜次装柜证据
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
- {name: expectedVersion, in: query, required: true, schema: {type: integer, minimum: 0}}
|
|
- {name: evidenceType, in: query, required: true, schema: {type: string, enum: [PHOTO_STEP_1, PHOTO_STEP_2, PHOTO_STEP_3, PHOTO_STEP_4, SEAL]}}
|
|
- {name: sealNo, in: query, required: false, schema: {type: string, maxLength: 64}}
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required: [file]
|
|
properties: {file: {type: string, format: binary}}
|
|
responses: {'200': {description: 柜级证据列表}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/evidence/{evidenceId}/content:
|
|
get:
|
|
summary: 下载当前柜次有效装柜证据
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: evidenceId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
responses: {'200': {description: 图片字节}}
|
|
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/evidence/{evidenceId}/void:
|
|
post:
|
|
summary: 作废当前柜次错误证据
|
|
parameters:
|
|
- {$ref: '#/components/parameters/TaskId'}
|
|
- {name: evidenceId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [warehouseId, expectedVersion, reason]
|
|
properties:
|
|
warehouseId: {type: integer, format: int64, minimum: 1}
|
|
expectedVersion: {type: integer, minimum: 0}
|
|
reason: {type: string, minLength: 1, maxLength: 500}
|
|
responses: {'200': {description: 作废后柜级证据列表}}
|
|
components:
|
|
parameters:
|
|
TaskId: {name: taskId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
ItemId: {name: itemId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
|
schemas:
|
|
WarehouseOption:
|
|
type: object
|
|
required: [id, warehouseCode, warehouseName]
|
|
properties:
|
|
id: {type: integer, format: int64}
|
|
warehouseCode: {type: string}
|
|
warehouseName: {type: string}
|
|
Task:
|
|
type: object
|
|
required: [id, loadingPlanId, containerBookingId, inboundId, inboundVersion, loadingVersion, cabinetNo, sourceType, recordStatus, taskStatus, workflowStatus, version, items]
|
|
properties:
|
|
id: {type: integer, format: int64}
|
|
loadingPlanId: {type: integer, format: int64}
|
|
containerBookingId: {type: integer, format: int64}
|
|
inboundId: {type: integer, format: int64, description: Stable inbound identity selected from the task item; evidence identity is taskId+inboundId+loadingPlanId+containerBookingId}
|
|
inboundVersion: {type: integer, minimum: 0, description: Current fl_inbound_order.version for inboundId; never inferred from PDA task version}
|
|
loadingVersion: {type: integer, minimum: 0, description: Compatibility alias of inboundVersion from the same fl_inbound_order row; /loading/complete uses this inbound loading-stage version}
|
|
cabinetNo: {type: string}
|
|
currentContainerNo: {type: string, nullable: true, description: Mutable transport display field; changing it never resynchronizes the task or item versions and it is never task identity}
|
|
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING], description: Source of the surviving stable container identity}
|
|
recordStatus: {type: string, enum: [ACTIVE], description: Task detail is served only from the ACTIVE container booking currently owned by the loading plan}
|
|
mergedIntoContainerBookingId: {type: integer, format: int64, nullable: true, description: Always null for the surviving ACTIVE operational container; populated only on hidden MERGED source history}
|
|
taskStatus: {type: string, enum: [LOADING, COMPLETING, COMPLETION_FAILED, COMPLETED]}
|
|
workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW, PROCESSING, COMPLETION_FAILED, COMPLETED]}
|
|
completionCommand: {$ref: '#/components/schemas/CompletionCommand', nullable: true}
|
|
version: {type: integer, minimum: 0}
|
|
assignment: {type: object, nullable: true}
|
|
review: {type: object, nullable: true}
|
|
items:
|
|
type: array
|
|
items: {$ref: '#/components/schemas/PickItem'}
|
|
PickItem:
|
|
type: object
|
|
required: [id, loadingPlanItemId, tenantId, inboundId, inboundDetailId, unitType, palletId, palletNo, indivisible, boxCodes, inventoryResources, plannedPackageCount, pickedPackageCount, itemStatus, version]
|
|
properties:
|
|
id: {type: integer, format: int64}
|
|
loadingPlanItemId: {type: integer, format: int64}
|
|
tenantId: {type: integer, format: int64, description: Hidden isolation key for subsequent PDA evidence calls}
|
|
inboundId: {type: integer, format: int64}
|
|
inboundDetailId: {type: integer, format: int64}
|
|
unitType: {type: string, enum: [PACKAGE, SEALED_PALLET], description: Authoritative loading plan unit; SEALED_PALLET must use the whole-pallet pick command}
|
|
palletId: {type: integer, format: int64, nullable: true, description: Dropship sealed pallet identity; not the weighing pallet in inventoryResources}
|
|
palletNo: {type: string, nullable: true, description: System-issued DP pallet code from the loading plan item}
|
|
indivisible: {type: boolean, description: Whole-pallet indivisibility from the loading plan item}
|
|
orderNo: {type: string, nullable: true}
|
|
inboundCode: {type: string, nullable: true}
|
|
cargoName: {type: string, nullable: true}
|
|
boxCodes:
|
|
type: array
|
|
description: Stable carton label codes; one successful scan represents one carton
|
|
items: {type: string}
|
|
inventoryResources:
|
|
type: array
|
|
description: Current authoritative whole-block slot snapshots for this inbound; never projected from legacy occupancy rows
|
|
items: {$ref: '#/components/schemas/InventoryResource'}
|
|
plannedPackageCount: {type: integer, minimum: 1}
|
|
pickedPackageCount: {type: integer, minimum: 0}
|
|
itemStatus: {type: string, enum: [PENDING, PICKING, PICKED, PARTIAL, SKIPPED]}
|
|
version: {type: integer, minimum: 0}
|
|
PendingTask:
|
|
type: object
|
|
required: [taskId, loadingPlanId, containerBookingId, cabinetNo, sourceType, recordStatus, warehouseId, taskStatus, workflowStatus, version, assignmentVersion, itemCount, pendingItemCount]
|
|
properties:
|
|
taskId: {type: integer, format: int64}
|
|
loadingPlanId: {type: integer, format: int64}
|
|
containerBookingId: {type: integer, format: int64}
|
|
cabinetNo: {type: string}
|
|
currentContainerNo: {type: string, nullable: true}
|
|
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING]}
|
|
recordStatus: {type: string, enum: [ACTIVE], description: Enforced by the server-side pending-task query; PDA clients must not infer or relax this filter}
|
|
mergedIntoContainerBookingId: {type: integer, format: int64, nullable: true, description: Always null for an ACTIVE pending task}
|
|
warehouseId: {type: integer, format: int64}
|
|
warehouseName: {type: string, nullable: true}
|
|
taskStatus: {type: string, enum: [LOADING, COMPLETING, COMPLETION_FAILED]}
|
|
workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW, PROCESSING, COMPLETION_FAILED], description: Server projection aligned with task detail; completion acceptance is returned immediately as PROCESSING}
|
|
version: {type: integer, minimum: 0}
|
|
assignmentVersion: {type: integer, minimum: 0}
|
|
itemCount: {type: integer, minimum: 1}
|
|
pendingItemCount: {type: integer, minimum: 0}
|
|
ClaimRequest:
|
|
type: object
|
|
required: [expectedAssignmentVersion, operationId]
|
|
properties:
|
|
expectedAssignmentVersion: {type: integer, minimum: 0}
|
|
operationId: {type: string, maxLength: 128}
|
|
AssignRequest:
|
|
type: object
|
|
required: [assigneeId, assigneeName, expectedAssignmentVersion, operationId]
|
|
properties:
|
|
assigneeId: {type: integer, format: int64, minimum: 1}
|
|
assigneeName: {type: string, maxLength: 64}
|
|
expectedAssignmentVersion: {type: integer, minimum: 0}
|
|
operationId: {type: string, maxLength: 128}
|
|
BatchRequest:
|
|
type: object
|
|
required: [warehouseId, loadingPlanId, containerBookingId, expectedVersion, inventorySnapshotToken, items]
|
|
properties:
|
|
warehouseId: {type: integer, format: int64, minimum: 1}
|
|
loadingPlanId: {type: integer, format: int64, minimum: 1}
|
|
containerBookingId: {type: integer, format: int64, minimum: 1}
|
|
expectedVersion: {type: integer, minimum: 0}
|
|
inventorySnapshotToken: {type: string, minLength: 64, maxLength: 64}
|
|
items:
|
|
type: array
|
|
minItems: 1
|
|
maxItems: 500
|
|
items: {$ref: '#/components/schemas/BatchItemRequest'}
|
|
BatchItemRequest:
|
|
type: object
|
|
description: PICK_PALLET submits one representative pickItem for each scanned DP pallet. The server expands that physical pallet to every SKU line and verifies them atomically. No carton quantities or skip reason are accepted. Scanning only changes the device draft.
|
|
required: [itemId, action, version]
|
|
properties:
|
|
itemId: {type: integer, format: int64, minimum: 1}
|
|
action: {type: string, enum: [PICK, PARTIAL, SKIP, PICK_PALLET]}
|
|
pickedPackageCount: {type: integer, minimum: 1, description: Incremental count required by PICK}
|
|
finalPickedPackageCount: {type: integer, minimum: 1, description: Final actual count required by PARTIAL}
|
|
version: {type: integer, minimum: 0}
|
|
reason: {type: string, maxLength: 500, description: Required by SKIP}
|
|
palletNo: {type: string, nullable: true, pattern: '^DP-[0-9]{6}-[0-9]{4}$', description: Required only by PICK_PALLET; ordinary carton actions must omit it}
|
|
ReviewRequest:
|
|
type: object
|
|
required: [expectedTaskVersion, operationId]
|
|
properties:
|
|
expectedTaskVersion: {type: integer, minimum: 0}
|
|
operationId: {type: string, maxLength: 128}
|
|
remark: {type: string, maxLength: 500}
|
|
CompletionCommand:
|
|
type: object
|
|
required: [id, operationId, status, expectedTaskVersion, attemptCount]
|
|
properties:
|
|
id: {type: integer, format: int64}
|
|
operationId: {type: string, maxLength: 128, description: Retry must reuse this exact identity}
|
|
status: {type: string, enum: [PROCESSING, RETRYABLE, FAILED, COMPLETED]}
|
|
expectedTaskVersion: {type: integer, minimum: 0, description: Retry must preserve this request-hashed version}
|
|
remark: {type: string, maxLength: 500, nullable: true, description: Retry must preserve this request-hashed remark}
|
|
attemptCount: {type: integer, minimum: 0}
|
|
nextRetryAt: {type: string, format: date-time, nullable: true}
|
|
failureStage: {type: string, nullable: true}
|
|
failureCode: {type: string, nullable: true}
|
|
requestId: {type: string, nullable: true}
|
|
traceId: {type: string, nullable: true}
|
|
completedAt: {type: string, format: date-time, nullable: true}
|
|
CompleteLoadingRequest:
|
|
type: object
|
|
required: [expectedTaskVersion, operationId]
|
|
properties:
|
|
expectedTaskVersion: {type: integer, minimum: 0}
|
|
operationId: {type: string, maxLength: 128}
|
|
remark: {type: string, maxLength: 500}
|
|
InventorySnapshot:
|
|
type: object
|
|
required: [taskId, loadingPlanId, containerBookingId, cabinetNo, taskVersion, warehouseId, capturedAt, snapshotToken, inbounds, units]
|
|
properties:
|
|
taskId: {type: integer, format: int64}
|
|
loadingPlanId: {type: integer, format: int64}
|
|
containerBookingId: {type: integer, format: int64}
|
|
cabinetNo: {type: string}
|
|
taskVersion: {type: integer, minimum: 0}
|
|
currentContainerNo: {type: string, nullable: true}
|
|
warehouseId: {type: integer, format: int64}
|
|
capturedAt: {type: string, format: date-time}
|
|
snapshotToken: {type: string, minLength: 64, maxLength: 64}
|
|
inbounds:
|
|
type: array
|
|
items: {$ref: '#/components/schemas/InboundInventory'}
|
|
units:
|
|
type: array
|
|
description: Authoritative plan units ordered by pickItemId and included in snapshotToken; one inbound may contain multiple sealed pallets
|
|
items: {$ref: '#/components/schemas/InventoryUnit'}
|
|
InventoryUnit:
|
|
type: object
|
|
required: [pickItemId, loadingPlanItemId, inboundId, unitType, palletId, palletNo, indivisible]
|
|
properties:
|
|
pickItemId: {type: integer, format: int64}
|
|
loadingPlanItemId: {type: integer, format: int64}
|
|
inboundId: {type: integer, format: int64}
|
|
unitType: {type: string, enum: [PACKAGE, SEALED_PALLET]}
|
|
palletId: {type: integer, format: int64, nullable: true}
|
|
palletNo: {type: string, nullable: true}
|
|
indivisible: {type: boolean}
|
|
InboundInventory:
|
|
type: object
|
|
required: [inboundId, availablePackageCount, resources]
|
|
properties:
|
|
inboundId: {type: integer, format: int64}
|
|
availablePackageCount: {type: integer, minimum: 0}
|
|
resources:
|
|
type: array
|
|
items: {$ref: '#/components/schemas/InventoryResource'}
|
|
InventoryResource:
|
|
type: object
|
|
required: [blockId, slotNo, reservationId, locationId, sharedOverflow, slotStatus, availablePackageCount]
|
|
properties:
|
|
blockId: {type: integer, format: int64}
|
|
slotNo: {type: integer, minimum: 1}
|
|
reservationId: {type: integer, format: int64}
|
|
weightResultId: {type: integer, format: int64, nullable: true}
|
|
palletSequence: {type: integer, minimum: 1, nullable: true}
|
|
palletId: {type: integer, format: int64, nullable: true}
|
|
locationId: {type: integer, format: int64}
|
|
locationCode: {type: string, nullable: true}
|
|
sharedOverflow: {type: boolean}
|
|
slotStatus: {type: string, enum: [RESERVED_EMPTY, BOUND]}
|
|
availablePackageCount: {type: integer, minimum: 0}
|