Files
baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml
T

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}