Files
baiyue-firstleg/lastleg-service/src/main/resources/openapi/overseas-pda-operations-v1.yaml
T

381 lines
22 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: Lastleg Overseas PDA Warehouse Operations API
version: 1.0.0
description: 海外仓PDA排车拣货发车、一件代发出库、盘点、同仓整托移库、托盘与换标作业;全部范围从PDA Token解析,派送不进入PDA。
security:
- bearerAuth: []
paths:
/api/pda/v1/last-leg/dispatches:
get:
summary: 查询Token绑定仓的排车PDA任务
description: 仅 OVERSEAS_WAREHOUSE_PDA_OPERATOR + lastleg:pda:operate;跨客户读取绑定仓任务,不接收customerId或warehouseId。
parameters: [{ name: status, in: query, required: false, schema: { type: string } }]
responses: { '200': { description: 排车任务列表 }, '403': { description: 角色、权限或仓库绑定缺失 } }
/api/pda/v1/last-leg/dispatches/{dispatchNo}:
get:
summary: 查询绑定仓排车任务详情
description: 返回 canDepart 与 departBlockReason;PDA 必须以服务端门禁决定是否开放发车,不得仅按本地拣货数量推断。
parameters: [{ name: dispatchNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 排车托盘、目的仓与库存位置详情 }, '404': { description: 当前绑定仓无此排车任务 } }
/api/pda/v1/last-leg/dispatches/{dispatchNo}/items/{itemId}/pick:
post:
summary: 扫码拣取排车托盘包裹
parameters:
- { name: dispatchNo, in: path, required: true, schema: { type: string } }
- { name: itemId, in: path, required: true, schema: { type: string, pattern: '^[0-9]+$' } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DispatchPick' } } } }
responses: { '200': { description: 本次及累计拣货结果;同键同请求重放既有结果 }, '409': { description: 跨仓、跨客户、库存、数量或幂等冲突 } }
/api/pda/v1/last-leg/dispatches/{dispatchNo}/complete:
post:
summary: 完成排车拣货
description: 每个要求托盘及全部包裹必须精确完成;不允许部分完成。
parameters: [{ name: dispatchNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/IdempotentAction' } } } }
responses: { '200': { description: READY_TO_DEPART结果 }, '409': { description: 未全部拣货或幂等冲突 } }
/api/pda/v1/last-leg/dispatches/{dispatchNo}/depart:
post:
summary: 排车发车并完成出库门禁
description: 仅完整拣货且存在待出库单时发车;同事务推进出库与排车状态、订单归属并释放托盘锁,不重复扣减扫码时已扣库存。
parameters: [{ name: dispatchNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DepartAction' } } } }
responses: { '200': { description: DEPARTED结果;同键同请求返回既有结果 }, '409': { description: 拣货、出库或幂等门禁未满足 } }
/api/pda/v1/last-leg/outbound-tasks:
get:
summary: 查询Token绑定仓的一件代发出库任务
description: 一件代发复用统一出库任务模型;不接收customerId或warehouseId,派送不进入PDA。
parameters: [{ name: status, in: query, required: false, schema: { type: string } }]
responses: { '200': { description: 出库任务列表 } }
/api/pda/v1/last-leg/outbound-tasks/{taskNo}:
get:
summary: 查询一件代发出库任务详情
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
responses:
'200':
description: 出库任务、出库项及每条可拣库存位置;inventoryPositionId仅作为pick提交键,不作为用户展示编号
content: { application/json: { schema: { $ref: '#/components/schemas/PdaOutboundTaskDetail' } } }
'404': { description: 当前绑定仓无此任务 }
/api/pda/v1/last-leg/outbound-tasks/{taskNo}/pick:
post:
summary: 扫码拣取一件代发箱件
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/OutboundPick' } } } }
responses: { '200': { description: 更新后的统一出库任务 }, '409': { description: 库存、数量或幂等冲突 } }
/api/pda/v1/last-leg/outbound-tasks/{taskNo}/complete:
post:
summary: 完成一件代发出库任务
description: 全部托盘及箱件精确拣完后完成;同一完成幂等键返回既有结果,其他键明确冲突。
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/IdempotentAction' } } } }
responses: { '200': { description: COMPLETED任务 }, '409': { description: 未全部拣货或幂等冲突 } }
/api/pda/v1/last-leg/stocktakes:
get:
summary: 查询Token绑定仓盘点任务
parameters:
- { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
- { name: size, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
- { name: status, in: query, schema: { type: string, enum: [IN_PROGRESS, SUBMITTED] } }
responses: { '200': { description: 盘点任务分页 } }
post:
summary: 按库位或托盘创建盘点任务
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeCreate' } } } }
responses: { '200': { description: 创建后的完整盘点任务 } }
/api/pda/v1/last-leg/stocktakes/{taskNo}:
get:
summary: 查询盘点任务详情
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 盘点任务及冻结库存位置 } }
/api/pda/v1/last-leg/stocktakes/{taskNo}/counts:
post:
summary: 提交一个库存位置的实盘箱数
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeCount' } } } }
responses: { '200': { description: 已计数明细 }, '409': { description: 版本或幂等冲突 } }
/api/pda/v1/last-leg/stocktakes/{taskNo}/submit:
post:
summary: 提交盘点并按差异调整库存
parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeSubmit' } } } }
responses: { '200': { description: 已提交盘点任务 }, '409': { description: 未全盘、库存版本变化或负库存风险 } }
/api/pda/v1/last-leg/inventory-moves:
post:
summary: 同仓整托移库
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/InventoryMove' } } } }
responses: { '200': { description: 移库结果 }, '409': { description: 跨仓、资源或库存版本冲突 } }
/api/pda/v1/last-leg/pallet-operations/pallets/resolve:
post:
summary: 扫码查询托盘及库存内容
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ScanCode' } } } }
responses: { '200': { description: Token绑定仓托盘内容 } }
/api/pda/v1/last-leg/pallet-operations/assemble:
post:
summary: 将多个来源托盘的指定库存行组到目标托盘
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PalletAssemble' } } } }
responses: { '200': { description: 组托及实物托盘状态结果 }, '409': { description: 目的仓、库存或资源版本冲突 } }
/api/pda/v1/last-leg/pallet-operations/disassemble:
post:
summary: 将来源托盘库存拆到一个或多个目标托盘
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PalletDisassemble' } } } }
responses: { '200': { description: 拆托结果 }, '409': { description: 库存或资源版本冲突 } }
/api/pda/v1/last-leg/pallet-operations/swap:
post:
summary: 将整托库存更换到空目标托盘
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PalletSwap' } } } }
responses: { '200': { description: 换托结果 }, '409': { description: 目标托盘非空或版本冲突 } }
/api/pda/v1/last-leg/pallet-operations/unbind:
post:
summary: 解除整托库存与实物托盘绑定
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PalletUnbind' } } } }
responses: { '200': { description: 解绑结果 }, '409': { description: 库存或资源版本冲突 } }
/api/pda/v1/last-leg/relabel-tasks/candidates:
get:
summary: 查询Token绑定仓可执行换标任务
responses: { '200': { description: 待认领、处理中或待复核换标任务 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}:
get:
summary: 查询换标现场任务详情
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 申请明细、逐件事实、照片与历史 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/claim:
post:
summary: 认领换标任务并推进申请至处理中
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/IdempotentAction' } } } }
responses: { '200': { description: 已认领任务 }, '409': { description: 仓库、状态或幂等冲突 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/units:
post:
summary: 登记一件正常换标事实
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelUnit' } } } }
responses: { '200': { description: 更新后的任务 }, '409': { description: 旧标重复、新标不符或版本冲突 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/units/skip:
post:
summary: 登记一件有原因的跳过事实
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelSkip' } } } }
responses: { '200': { description: 更新后的任务 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/exceptions:
post:
summary: 登记一件换标异常事实
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelException' } } } }
responses: { '200': { description: 更新后的任务 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/photos/files:
post:
summary: 上传换标完成现场照片
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { multipart/form-data: { schema: { type: object, required: [file], properties: { file: { type: string, format: binary } } } } } }
responses: { '200': { description: OVERSEAS_RELABEL_PHOTO文件元数据 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/complete:
post:
summary: 提交现场任务等待复核,不提前完成申请
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelComplete' } } } }
responses: { '200': { description: WAIT_REVIEW任务 }, '409': { description: 箱数未核平、照片或版本冲突 } }
/api/pda/v1/last-leg/relabel-tasks/{relabelNo}/review:
post:
summary: 现场复核;APPROVE才完成既有换标申请,REWORK退回处理
parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelReview' } } } }
responses: { '200': { description: COMPLETED或PROCESSING任务 }, '409': { description: 状态或版本冲突 } }
components:
securitySchemes:
bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT }
schemas:
ScanCode:
type: object
required: [code]
properties: { code: { type: string, minLength: 1, maxLength: 128 } }
IdempotentAction:
type: object
required: [idempotencyKey]
properties: { idempotencyKey: { type: string, minLength: 1, maxLength: 255 } }
DepartAction:
type: object
required: [idempotencyKey]
properties:
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, maxLength: 1000, nullable: true }
DispatchPick:
type: object
required: [positionId, packages, productQuantity, idempotencyKey]
properties:
positionId: { type: string, pattern: '^[0-9]+$' }
packages: { type: integer, format: int64, minimum: 1 }
productQuantity: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, maxLength: 1000, nullable: true }
OutboundPick:
type: object
required: [outboundPalletItemId, inventoryPositionId, packages, idempotencyKey]
properties:
outboundPalletItemId: { type: string, pattern: '^[0-9]+$' }
inventoryPositionId: { type: string, pattern: '^[0-9]+$' }
packages: { type: integer, format: int64, minimum: 1 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
PdaOutboundTaskDetail:
type: object
required: [task, items]
properties:
task:
type: object
required: [taskNo, status, outboundNo]
properties:
taskNo: { type: string }
status: { type: string }
outboundNo: { type: string }
pushedAt: { type: string, format: date-time, nullable: true }
completedAt: { type: string, format: date-time, nullable: true }
items:
type: array
items:
type: object
required: [outboundPalletItemId, palletNo, skuCode, inboundCode, packageCount, pickedPackageCount, status, positions]
properties:
outboundPalletItemId: { type: string, description: pick请求提交键,不作为用户展示编号 }
palletNo: { type: string }
skuCode: { type: string }
inboundCode: { type: string }
packageCount: { type: integer }
pickedPackageCount: { type: integer }
status: { type: string }
positions:
type: array
items:
type: object
required: [inventoryPositionId, palletNo, cartonMark, skuCode, inboundCode, availablePackages, pickedPackages]
properties:
inventoryPositionId: { type: string, description: pick请求提交键,不作为用户展示编号 }
palletNo: { type: string }
cartonMark: { type: string, nullable: true }
skuCode: { type: string }
inboundCode: { type: string }
availablePackages: { type: integer, format: int64, minimum: 0 }
pickedPackages: { type: integer, format: int64, minimum: 0 }
StocktakeCreate:
type: object
required: [scopeType, scopeCode, idempotencyKey]
properties:
scopeType: { type: string, enum: [LOCATION, PALLET] }
scopeCode: { type: string, maxLength: 128 }
idempotencyKey: { type: string, maxLength: 128 }
StocktakeCount:
type: object
required: [positionId, actualPackages, expectedVersion, idempotencyKey]
properties:
positionId: { type: string, pattern: '^[0-9]+$' }
actualPackages: { type: integer, format: int64, minimum: 0 }
expectedVersion: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, maxLength: 128 }
StocktakeSubmit:
type: object
required: [expectedVersion, reason, idempotencyKey]
properties:
expectedVersion: { type: integer, format: int64, minimum: 0 }
reason: { type: string, minLength: 1, maxLength: 500 }
idempotencyKey: { type: string, maxLength: 128 }
InventoryMove:
type: object
required: [sourcePalletCode, targetLocationCode, expectedPalletVersion, idempotencyKey, reason]
properties:
sourcePalletCode: { type: string, maxLength: 128 }
targetLocationCode: { type: string, maxLength: 128 }
expectedPalletVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 128 }
reason: { type: string, minLength: 1, maxLength: 500 }
PalletExpectation:
type: object
required: [palletCode, expectedVersion]
properties:
palletCode: { type: string, maxLength: 128 }
expectedVersion: { type: integer, minimum: 0 }
PalletMoveLine:
type: object
required: [positionId, packages, expectedPositionVersion]
properties:
positionId: { type: string, pattern: '^[0-9]+$' }
packages: { type: integer, format: int64, minimum: 1 }
expectedPositionVersion: { type: integer, format: int64, minimum: 0 }
PalletAssemble:
type: object
required: [idempotencyKey, target, sources, lines]
properties:
idempotencyKey: { type: string, maxLength: 128 }
target: { $ref: '#/components/schemas/PalletExpectation' }
sources: { type: array, minItems: 1, maxItems: 20, items: { $ref: '#/components/schemas/PalletExpectation' } }
lines: { type: array, minItems: 1, maxItems: 200, items: { $ref: '#/components/schemas/PalletMoveLine' } }
reason: { type: string, maxLength: 500 }
PalletTargetMove:
type: object
required: [target, lines]
properties:
target: { $ref: '#/components/schemas/PalletExpectation' }
lines: { type: array, minItems: 1, maxItems: 200, items: { $ref: '#/components/schemas/PalletMoveLine' } }
PalletDisassemble:
type: object
required: [idempotencyKey, source, targets]
properties:
idempotencyKey: { type: string, maxLength: 128 }
source: { $ref: '#/components/schemas/PalletExpectation' }
targets: { type: array, minItems: 1, maxItems: 20, items: { $ref: '#/components/schemas/PalletTargetMove' } }
reason: { type: string, maxLength: 500 }
PalletSwap:
type: object
required: [idempotencyKey, source, target]
properties:
idempotencyKey: { type: string, maxLength: 128 }
source: { $ref: '#/components/schemas/PalletExpectation' }
target: { $ref: '#/components/schemas/PalletExpectation' }
reason: { type: string, maxLength: 500 }
PalletUnbind:
type: object
required: [idempotencyKey, source]
properties:
idempotencyKey: { type: string, maxLength: 128 }
source: { $ref: '#/components/schemas/PalletExpectation' }
reason: { type: string, maxLength: 500 }
RelabelUnit:
type: object
required: [itemId, oldLabel, newLabel, expectedVersion, idempotencyKey]
properties:
itemId: { type: string, pattern: '^[0-9]+$' }
oldLabel: { type: string, maxLength: 255 }
newLabel: { type: string, maxLength: 255 }
expectedVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 255 }
RelabelSkip:
type: object
required: [itemId, oldLabel, reason, expectedVersion, idempotencyKey]
properties:
itemId: { type: string, pattern: '^[0-9]+$' }
oldLabel: { type: string, maxLength: 255 }
reason: { type: string, maxLength: 1000 }
expectedVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 255 }
RelabelException:
type: object
required: [itemId, exceptionCode, reason, expectedVersion, idempotencyKey]
properties:
itemId: { type: string, pattern: '^[0-9]+$' }
oldLabel: { type: string, maxLength: 255, nullable: true }
exceptionCode: { type: string, enum: [DAMAGED, MISSING_LABEL, LABEL_MISMATCH, UNREADABLE, OTHER] }
reason: { type: string, maxLength: 1000 }
expectedVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 255 }
RelabelComplete:
type: object
required: [photoFileIds, expectedVersion, idempotencyKey]
properties:
photoFileIds: { type: array, minItems: 1, maxItems: 20, items: { type: string, pattern: '^[0-9]+$' } }
expectedVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 255 }
remark: { type: string, maxLength: 1000, nullable: true }
RelabelReview:
type: object
required: [decision, expectedVersion, idempotencyKey]
properties:
decision: { type: string, enum: [APPROVE, REWORK] }
expectedVersion: { type: integer, minimum: 0 }
idempotencyKey: { type: string, maxLength: 255 }
remark: { type: string, maxLength: 1000, nullable: true }