openapi: 3.0.3 info: title: Lastleg Overseas PDA Warehouse Operations API version: 1.1.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/resolve/{businessNo}: get: summary: 解析出库作业扫码业务号 description: 接受一件代发任务号、出库单号或排车单号;按Token绑定仓返回 OUTBOUND 的 taskNo 或 DISPATCH 的 dispatchNo,PDA据此进入正确作业页。 parameters: [{ name: businessNo, in: path, required: true, schema: { type: string } }] responses: '200': { description: 正确作业类型及稳定业务编号 } '404': { 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}/pallets/{palletNo}/pick: post: summary: 扫描一件代发托盘并原子拣取整托剩余箱件 description: 仅适用于一件代发出库;按托盘汇总本任务内全部 SKU 和未拣箱唛,任一库存校验失败则整托回滚。 parameters: - { name: taskNo, in: path, required: true, schema: { type: string } } - { name: palletNo, in: path, required: true, schema: { type: string } } requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/IdempotentAction' } } } } responses: { '200': { description: 整托拣货后的任务 }, '404': { 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: 盘点任务分页, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeSummaryPageResponse' } } } } post: summary: 按库位或托盘创建盘点任务 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeCreate' } } } } responses: '200': { description: 创建后的完整盘点任务, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeDetailResponse' } } } } /api/pda/v1/last-leg/stocktakes/{taskNo}: get: summary: 查询盘点任务详情 parameters: [{ name: taskNo, in: path, required: true, schema: { type: string } }] responses: '200': { description: 盘点任务及冻结库存位置, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeDetailResponse' } } } } /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: 已计数明细, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeItemResponse' } } } } '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: 已提交盘点任务, content: { application/json: { schema: { $ref: '#/components/schemas/StocktakeDetailResponse' } } } } '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: 移库结果, content: { application/json: { schema: { $ref: '#/components/schemas/InventoryMoveResponse' } } } } '404': { description: 来源托盘不存在(统一响应实际 HTTP 200,body.code=A0404), content: { application/json: { schema: { $ref: '#/components/schemas/ApiResponse' } } } } '409': { description: 跨仓、资源或库存版本冲突 } /api/platform/v1/last-leg/inventory-moves: post: summary: 平台发起海外仓整托移库 description: 平台按海外仓业务标识执行普通库位或 A0 共享溢出位移库;目标虚拟收货位、跨仓和版本冲突均拒绝。 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PlatformInventoryMove' } } } } responses: '200': { description: 移库结果, content: { application/json: { schema: { $ref: '#/components/schemas/InventoryMoveResponse' } } } } '409': { description: 物理资源或本地版本冲突 } /api/pda/v1/last-leg/inventory/resolve: post: summary: 扫码查询Token绑定仓库存 description: 支持箱唛、SKU、入仓码、托盘码或库位码;不接收customerId或warehouseId。 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ScanCode' } } } } responses: '200': { description: 匹配的库存位置列表, content: { application/json: { schema: { $ref: '#/components/schemas/InventoryScanResponse' } } } } /api/pda/v1/last-leg/locations/resolve: post: summary: 扫码查询Token绑定仓库位 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ScanCode' } } } } responses: '200': { description: 库位及占用摘要, content: { application/json: { schema: { $ref: '#/components/schemas/LocationResponse' } } } } /api/pda/v1/last-leg/pallets/resolve: post: summary: 扫码查询Token绑定仓托盘 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ScanCode' } } } } responses: '200': { description: 托盘及其库存内容, content: { application/json: { schema: { $ref: '#/components/schemas/PalletResponse' } } } } '404': { description: 托盘不存在(统一响应实际 HTTP 200,body.code=A0404), content: { application/json: { schema: { $ref: '#/components/schemas/ApiResponse' } } } } /api/pda/v1/last-leg/operations: get: summary: 查询当前操作人的仓内操作历史 parameters: - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } } - { name: size, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 20 } } - { name: operationType, in: query, required: false, schema: { type: string } } responses: '200': { description: 当前Token绑定仓及操作人的历史分页, content: { application/json: { schema: { $ref: '#/components/schemas/OperationPageResponse' } } } } /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绑定仓托盘内容 }, '404': { description: 托盘不存在(统一响应实际 HTTP 200,body.code=A0404), content: { application/json: { schema: { $ref: '#/components/schemas/ApiResponse' } } } } } /api/pda/v1/last-leg/pallet-operations/relabel-sources: post: summary: 查询换标原入仓单在各托盘上的库存数量 description: 范围取Token冻结仓和换标单客户;返回库存组成而非历史箱唛定位。movablePackages不包括其他单据冻结、已分配或已拣货箱数。cartonMarks为同来源库存池候选,不代表这些箱历史上位于该托盘。 requestBody: required: true content: application/json: schema: { type: object, required: [relabelNo], properties: { relabelNo: { type: string, maxLength: 64 } } } responses: '200': description: 原入仓单托盘组成 content: application/json: schema: type: object properties: code: { type: string } data: { type: array, items: { $ref: '#/components/schemas/RelabelSource' } } '409': { description: 状态、认领人或仓库不符 } /api/pda/v1/last-leg/pallet-operations/relabel-pallet-label: post: summary: 为换标拆托幂等生成并返回可打印的新托盘标签 description: 返回空托盘编码、初始版本和打印HTML;随后可将该托盘作为relabel-disassemble的target。库存移入后仍继承原库存余额对应的客户和入库单。 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/NewRelabelPallet' } } } } responses: { '200': { description: 新托盘及可打印标签,已认领处理中无需客户确认即可创建 }, '409': { 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/relabel-disassemble: post: summary: 按换标箱唛拆托并绑定目标托盘 description: PDA选择原入仓单库存行并扫描实际来源托盘,输入拆出箱数,核对本批cartonMarks后扫描目标托盘。传actualSource时以其数量和版本为准,同入仓余额及批次内原子调整本单冻结记账位置,再移动冻结库存和换标明细;不新增入库、不改变库存归属、不占用其他单据冻结量。每次一个来源库存行,可分批处理多个托盘。已有出库单时不允许此数量重分配。未传actualSource的箱唛模式仍要求已有来源仓位一致。 requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelPalletDisassemble' } } } } 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: 待认领、处理中或待复核换标任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelCandidatesResponse' } } } } /api/pda/v1/last-leg/relabel-tasks/{relabelNo}: get: summary: 查询换标现场任务详情 parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }] responses: '200': { description: 申请明细、逐件事实、照片与历史, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } } /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: 已认领任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } }, '409': { description: 仓库、状态或幂等冲突 } } /api/pda/v1/last-leg/relabel-tasks/{relabelNo}/units: post: summary: 扫描我方箱唛并登记整箱正常换标事实;可只传oldLabel由服务端定位箱唛任务 parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }] requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelUnit' } } } } responses: { '200': { description: 更新后的任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } }, '409': { description: 旧标重复、新标不符或版本冲突 } } /api/pda/v1/last-leg/relabel-tasks/{relabelNo}/skus/scan: post: summary: 仅扫描旧SKU完成当前箱的一条SKU换标明细 description: itemId与skuIndex精确指定一条明细,服务端验证旧SKU并读取已批准的新SKU及件数;同旧SKU多箱由PDA选择目标,不批量隐式完成。逐条写SKU_SCAN历史并按轮次防重,全部SKU处理后完成SKU阶段,混合单仍须完成箱唛阶段。响应history包含旧新SKU、件数及索引,供提示与隐藏已处理明细。 parameters: [{ name: relabelNo, in: path, required: true, schema: { type: string } }] requestBody: required: true content: application/json: schema: type: object required: [itemId, skuIndex, oldSku, expectedVersion, idempotencyKey] properties: itemId: { type: string, pattern: '^[0-9]+$' } skuIndex: { type: integer, minimum: 0 } oldSku: { type: string, maxLength: 255 } expectedVersion: { type: integer, minimum: 0 } idempotencyKey: { type: string, maxLength: 255 } responses: '200': { description: 更新后的任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } } '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: 更新后的任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } } } /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: 更新后的任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } } } /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文件元数据, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelPhotoResponse' } } } } } /api/pda/v1/last-leg/relabel-tasks/{relabelNo}/photos/files/{fileId}/download: get: summary: 读取当前操作人处理中任务的已上传照片,用于缩略图和大图预览 description: 校验 Token 绑定仓、任务认领人和文件的客户及任务归属;移除和重拍只替换待提交文件引用,不删除历史文件。 parameters: - { name: relabelNo, in: path, required: true, schema: { type: string } } - { name: fileId, in: path, required: true, schema: { type: integer, format: int64 } } responses: '200': { description: 作业照片内容, content: { 'image/*': { schema: { type: string, format: binary } } } } '404': { description: 照片不属于当前任务或仓库 } '409': { description: 当前操作人未认领任务或任务不在处理中 } /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任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } }, '409': { description: 箱数未核平、照片或版本冲突 } } /api/pda/v1/last-leg/relabel-tasks/{relabelNo}/review: post: summary: 已停用;客户确认完成复核,退回重贴由平台执行 deprecated: true 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任务, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelDetailResponse' } } } }, '409': { description: 状态或版本冲突 } } /api/pda/v1/last-leg/warehouse-transfers/candidates: get: summary: 查询Token绑定仓待测量的仓储转运单 description: 仅返回当前PDA冻结海外仓内已审核且等待测量打托的转运单;不接收customerId或warehouseId。 responses: '200': description: 待测量转运单列表 content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferSummaryListResponse' } } } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}: get: summary: 查询当前仓仓储转运测量详情 parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }] responses: '200': description: 转运申报、已生成的新托盘及测量照片;items仅为服务端自动分摊结果,PDA不提交每托SKU。 content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferDetailResponse' } } } '404': { description: 当前Token绑定仓无此转运单 } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/measurement-photos/files: get: summary: 查询当前转运单已上传的测量现场照片 parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }] responses: '200': { description: 照片元数据列表 } post: summary: 上传仓储转运测量现场照片 parameters: [{ name: transferNo, 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: 已绑定当前转运单的照片元数据 } '404': { description: 当前Token绑定仓无此转运单 } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/measurement-photos/files/{fileId}/download: get: summary: 鉴权下载仓储转运测量现场照片 parameters: - { name: transferNo, in: path, required: true, schema: { type: string } } - { name: fileId, in: path, required: true, schema: { type: integer, format: int64 } } responses: '200': { description: 照片内容, content: { 'image/*': { schema: { type: string, format: binary } } } } '404': { description: 照片不属于当前转运单或绑定仓 } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/palletize: post: summary: 完成仓储转运整单测量打托 description: 只提交当前转运单照片和整单幂等键;所有托盘必须已逐托生成并扫码绑定,箱数必须核平。此接口不接收测量字段、托盘码或每托SKU,也绝不再次生成托盘。 parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }] requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferPalletize' } } } } responses: '200': description: WAIT_CUSTOMER_CONFIRMATION详情 content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferDetailResponse' } } } '409': { description: 状态、照片归属、箱数核平或幂等冲突 } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/pallets/drafts: post: summary: 新建或更新一个转运测量托盘草稿 description: palletId为空时创建DRAFT;传入既有DRAFT的palletId时更新。GENERATED及以后测量字段锁定,必须拒绝修改。 parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }] requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferPalletDraft' } } } } responses: { '200': { description: 包含持久化草稿的最新整单详情 }, '409': { description: 草稿已生成、状态或幂等冲突 } } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/pallets/{palletId}: delete: summary: 删除未生成的转运托盘草稿 description: 只允许删除DRAFT;GENERATED、BOUND和COMPLETED不得删除。 parameters: - { name: transferNo, in: path, required: true, schema: { type: string } } - { name: palletId, in: path, required: true, schema: { type: integer, format: int64 } } responses: { '200': { description: 删除后的整单详情 }, '409': { description: 本托已经生成真实托盘 } } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/pallets/{palletId}/generate: post: summary: 为测量草稿生成一个全新实物托盘 description: 只允许DRAFT调用一次;服务端始终向Warehouse Overseas传空palletCode,不能复用已有托盘。有FBA则写入目的仓,无FBA则标签显示“目的仓待定”。成功后实测字段锁定且可立即下载PDF标签。 parameters: - { name: transferNo, in: path, required: true, schema: { type: string } } - { name: palletId, in: path, required: true, schema: { type: integer, format: int64 } } requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferPalletAction' } } } } responses: { '200': { description: 包含GENERATED新托盘号和标签路径的最新详情 }, '409': { description: 已生成或资源生成冲突 } } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/pallets/{palletId}/bind: post: summary: 扫描并确认本托新生成的托盘码 description: 扫描码必须精确等于本草稿服务端生成码,同时验证Warehouse Overseas物理托盘ID和Token冻结仓一致;其他已有托盘不得冒充。 parameters: - { name: transferNo, in: path, required: true, schema: { type: string } } - { name: palletId, in: path, required: true, schema: { type: integer, format: int64 } } requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/WarehouseTransferPalletBind' } } } } responses: { '200': { description: 包含BOUND事实和boundAt的最新详情 }, '409': { description: 扫描码、归属、仓库或状态不匹配 } } /api/pda/v1/last-leg/warehouse-transfers/{transferNo}/pallets/{warehousePalletId}/label: get: summary: 下载仓储转运实物托盘PDF标签 description: 校验Token冻结仓、转运单和托盘归属后,代理Warehouse Overseas使用统一托盘标签打印服务生成真实PDF。已知FBA显示目的仓编码,未知FBA显示“目的仓待定”。 parameters: - { name: transferNo, in: path, required: true, schema: { type: string } } - { name: warehousePalletId, in: path, required: true, schema: { type: integer, format: int64 } } - { name: X-Request-Id, in: header, required: false, schema: { type: string } } responses: '200': { description: 托盘标签PDF, content: { application/pdf: { schema: { type: string, format: binary } } } } '404': { description: 转运单或托盘不属于当前Token绑定仓 } components: securitySchemes: bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT } schemas: ApiResponse: type: object required: [code, data] properties: code: { type: string, example: '200' } message: { type: string, nullable: true } data: { nullable: true } requestId: { type: string, nullable: true } traceId: { type: string, nullable: true } timestamp: { type: string, format: date-time, nullable: true } WarehouseTransferSummaryListResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: data: { type: array, items: { $ref: '#/components/schemas/WarehouseTransferSummary' } } WarehouseTransferDetailResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/WarehouseTransferDetail' } } WarehouseTransferSummary: type: object required: [id, transferNo, operatingWarehouseId, packageCount, deliveryMethod, needRelabel, status] properties: id: { type: integer, format: int64 } transferNo: { type: string } inboundCode: { type: string, nullable: true } operatingWarehouseId: { type: string } fbaWarehouseCode: { type: string, nullable: true, description: 为空表示目的仓待定 } packageCount: { type: integer, minimum: 1 } deliveryMethod: { type: string } needRelabel: { type: boolean } relabelNo: { type: string, nullable: true } status: { type: string } estimatedArrivalAt: { type: string, format: date-time, nullable: true } createdAt: { type: string, format: date-time, nullable: true } WarehouseTransferDetail: type: object required: [id, transferNo, operatingWarehouseId, packageCount, status, pallets, measurementPhotos] properties: id: { type: integer, format: int64 } transferNo: { type: string } operatingWarehouseId: { type: string } fbaWarehouseCode: { type: string, nullable: true, description: 为空表示目的仓待定 } pickupContactName: { type: string, nullable: true } pickupContactPhone: { type: string, nullable: true } packageCount: { type: integer, minimum: 1 } status: { type: string } pallets: { type: array, items: { $ref: '#/components/schemas/WarehouseTransferPallet' } } measurementPhotos: { type: array, items: { $ref: '#/components/schemas/WarehouseTransferMeasurementPhoto' } } WarehouseTransferPalletize: type: object required: [idempotencyKey, photoFileIds] properties: idempotencyKey: { type: string, minLength: 1, maxLength: 255 } photoFileIds: type: array minItems: 1 maxItems: 20 uniqueItems: true items: { type: integer, format: int64, minimum: 1 } WarehouseTransferPalletDraft: type: object required: [idempotencyKey, cartonCount, lengthCm, widthCm, heightCm, volumeCbm, grossWeightKg] properties: palletId: { type: integer, format: int64, nullable: true, description: 更新现有DRAFT时传入;创建时不传 } idempotencyKey: { type: string, minLength: 1, maxLength: 255 } cartonCount: { type: integer, minimum: 1 } lengthCm: { type: number, format: double, minimum: 0.01 } widthCm: { type: number, format: double, minimum: 0.01 } heightCm: { type: number, format: double, minimum: 0.01 } volumeCbm: { type: number, format: double, minimum: 0.000001 } grossWeightKg: { type: number, format: double, minimum: 0.01 } WarehouseTransferPalletAction: type: object required: [idempotencyKey] properties: { idempotencyKey: { type: string, minLength: 1, maxLength: 255 } } WarehouseTransferPalletBind: type: object required: [palletCode, idempotencyKey] properties: palletCode: { type: string, minLength: 1, maxLength: 128 } idempotencyKey: { type: string, minLength: 1, maxLength: 255 } WarehouseTransferPallet: type: object required: [id, sequenceNo, cartonCount, lengthCm, widthCm, heightCm, volumeCbm, grossWeightKg, status] properties: id: { type: integer, format: int64 } sequenceNo: { type: integer, minimum: 1 } warehousePalletId: { type: integer, format: int64, nullable: true } palletNo: { type: string, nullable: true, description: GENERATED后由Warehouse Overseas为本次测量新生成 } cartonCount: { type: integer, minimum: 1 } lengthCm: { type: number, format: double } widthCm: { type: number, format: double } heightCm: { type: number, format: double } volumeCbm: { type: number, format: double } grossWeightKg: { type: number, format: double } status: { type: string, enum: [DRAFT, GENERATED, BOUND, COMPLETED] } fbaWarehouseCode: { type: string, nullable: true, description: 为空时标签显示目的仓待定 } labelDownloadPath: { type: string, nullable: true, description: GENERATED后返回当前PDA鉴权下的真实PDF下载路径 } boundAt: { type: string, format: date-time, nullable: true } WarehouseTransferMeasurementPhoto: type: object required: [fileId, sizeBytes, uploadedAt] properties: fileId: { type: integer, format: int64 } fileName: { type: string, nullable: true } contentType: { type: string, nullable: true } sizeBytes: { type: integer, format: int64, minimum: 0 } uploadedAt: { type: string, format: date-time } StocktakeSummaryPageResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/StocktakeSummaryPage' } } StocktakeDetailResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/StocktakeDetail' } } StocktakeItemResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/StocktakeItem' } } InventoryMoveResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/InventoryMoveResult' } } InventoryScanResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/InventoryScan' } } LocationResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/WarehouseLocation' } } PalletResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/WarehousePallet' } } OperationPageResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/OperationPage' } } RelabelCandidatesResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: data: { type: array, items: { $ref: '#/components/schemas/RelabelCandidate' } } RelabelDetailResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/RelabelDetail' } } RelabelPhotoResponse: allOf: - { $ref: '#/components/schemas/ApiResponse' } - type: object properties: { data: { $ref: '#/components/schemas/RelabelPhoto' } } StocktakeSummaryPage: type: object required: [records, page, size, total, pages] properties: records: { type: array, items: { $ref: '#/components/schemas/StocktakeSummary' } } page: { type: integer, format: int64 } size: { type: integer, format: int64 } total: { type: integer, format: int64 } pages: { type: integer, format: int64 } StocktakeSummary: type: object required: [taskNo, scopeType, scopeCode, status, positionCount, countedCount, differenceCount, version, createdAt] properties: taskNo: { type: string } scopeType: { type: string, enum: [LOCATION, PALLET] } scopeCode: { type: string } status: { type: string, enum: [IN_PROGRESS, SUBMITTED] } positionCount: { type: integer } countedCount: { type: integer } differenceCount: { type: integer } version: { type: integer, format: int64 } createdAt: { type: string, format: date-time } submittedAt: { type: string, format: date-time, nullable: true } StocktakeItem: type: object required: [itemId, customerId, balanceId, positionId, skuCode, inboundCode, palletId, palletCode, locationId, locationCode, bookPackages, status, positionVersion, version] properties: itemId: { type: string, pattern: '^[0-9]+$' } customerId: { type: string } balanceId: { type: string, pattern: '^[0-9]+$' } positionId: { type: string, pattern: '^[0-9]+$' } skuCode: { type: string } productName: { type: string, nullable: true } inboundCode: { type: string } palletId: { type: string, pattern: '^[0-9]+$' } palletCode: { type: string } locationId: { type: string, pattern: '^[0-9]+$' } locationCode: { type: string } bookPackages: { type: integer, format: int64 } actualPackages: { type: integer, format: int64, nullable: true } differencePackages: { type: integer, format: int64, nullable: true } status: { type: string } positionVersion: { type: integer, format: int64 } version: { type: integer, format: int64 } StocktakeDetail: type: object required: [task, items] properties: task: { $ref: '#/components/schemas/StocktakeSummary' } items: { type: array, items: { $ref: '#/components/schemas/StocktakeItem' } } InventoryMoveResult: type: object required: [moveNo, palletId, palletCode, sourceLocationId, sourceLocationCode, targetLocationId, targetLocationCode, customerCount, positionCount, packageCount, palletVersion, status, completedAt, replayed] properties: moveNo: { type: string } palletId: { type: string, pattern: '^[0-9]+$' } palletCode: { type: string } sourceLocationId: { type: string, pattern: '^[0-9]+$' } sourceLocationCode: { type: string } targetLocationId: { type: string, pattern: '^[0-9]+$' } targetLocationCode: { type: string } customerCount: { type: integer } positionCount: { type: integer } packageCount: { type: integer, format: int64 } palletVersion: { type: integer } status: { type: string } completedAt: { type: string, format: date-time } replayed: { type: boolean } InventoryScan: type: object required: [queryCode, items] properties: queryCode: { type: string } items: { type: array, items: { $ref: '#/components/schemas/InventoryItem' } } InventoryItem: type: object required: [customerId, balanceId, positionId, skuCode, inboundCode, palletId, palletCode, availablePackages, holdPackages, receivingPackages, status, version] properties: customerId: { type: string } customerName: { type: string, nullable: true } balanceId: { type: string, pattern: '^[0-9]+$' } positionId: { type: string, pattern: '^[0-9]+$' } skuCode: { type: string } productName: { type: string, nullable: true } inboundCode: { type: string } cartonMark: { type: string, nullable: true } palletId: { type: string, pattern: '^[0-9]+$' } palletCode: { type: string } locationId: { type: string, pattern: '^[0-9]+$', nullable: true } locationCode: { type: string, nullable: true } availablePackages: { type: integer, format: int64 } holdPackages: { type: integer, format: int64 } receivingPackages: { type: integer, format: int64 } status: { type: string } version: { type: integer, format: int64 } WarehouseLocation: type: object required: [locationId, locationCode, locationType, status, occupiedPackages, inventoryPositionCount] properties: locationId: { type: string, pattern: '^[0-9]+$' } locationCode: { type: string } locationType: { type: string } maxLoadKg: { type: number, nullable: true } status: { type: string } occupiedPackages: { type: integer, format: int64 } inventoryPositionCount: { type: integer } WarehousePallet: type: object required: [palletId, palletCode, status, version, occupiedPackages, inventoryPositionCount, inventory] properties: palletId: { type: string, pattern: '^[0-9]+$' } palletCode: { type: string } status: { type: string } version: { type: integer } locationId: { type: string, pattern: '^[0-9]+$', nullable: true } locationCode: { type: string, nullable: true } occupiedPackages: { type: integer, format: int64 } inventoryPositionCount: { type: integer } inventory: { type: array, items: { $ref: '#/components/schemas/InventoryItem' } } OperationPage: type: object required: [records, page, size, total, pages] properties: records: { type: array, items: { $ref: '#/components/schemas/WarehouseOperation' } } page: { type: integer, format: int64 } size: { type: integer, format: int64 } total: { type: integer, format: int64 } pages: { type: integer, format: int64 } WarehouseOperation: type: object required: [operationId, operationType, businessKey, status, occurredAt] properties: operationId: { type: string, pattern: '^[0-9]+$' } operationType: { type: string } businessKey: { type: string } status: { type: string } summary: { type: string, nullable: true } customerId: { type: string, nullable: true } occurredAt: { type: string, format: date-time } RelabelCandidate: type: object required: [relabelNo, applicationStatus, taskStatus, operationType, itemCount, expectedPackages, relabelledPackages, skippedPackages, exceptionPackages, version, claimedByCurrentOperator] properties: relabelNo: { type: string } customerOrderNo: { type: string, nullable: true } labelDeadline: { type: string, format: date-time, nullable: true } applicationStatus: { type: string } taskStatus: { type: string } operationType: { type: string, enum: [SKU_LABEL, CARTON_LABEL, CARTON_AND_SKU] } itemCount: { type: integer } expectedPackages: { type: integer } relabelledPackages: { type: integer } skippedPackages: { type: integer } exceptionPackages: { type: integer } version: { type: integer } claimedByCurrentOperator: { type: boolean } RelabelDetail: type: object required: [relabelNo, applicationStatus, operationType, taskStatus, expectedPackages, relabelledPackages, skippedPackages, exceptionPackages, version, completionPhotoFileIds, items, unitFacts, history] properties: relabelNo: { type: string } customerOrderNo: { type: string, nullable: true } labelDeadline: { type: string, format: date-time, nullable: true } contactName: { type: string, nullable: true } contactPhone: { type: string, nullable: true } applicationStatus: { type: string } operationType: { type: string, enum: [SKU_LABEL, CARTON_LABEL, CARTON_AND_SKU] } taskStatus: { type: string } expectedPackages: { type: integer } relabelledPackages: { type: integer } skippedPackages: { type: integer } exceptionPackages: { type: integer } version: { type: integer } operatorName: { type: string, nullable: true } reviewerName: { type: string, nullable: true } completionPhotoFileIds: { type: array, items: { type: string, pattern: '^[0-9]+$' } } completionRemark: { type: string, nullable: true } reviewDecision: { type: string, nullable: true } reviewRemark: { type: string, nullable: true } claimedAt: { type: string, format: date-time, nullable: true } submittedAt: { type: string, format: date-time, nullable: true } reviewedAt: { type: string, format: date-time, nullable: true } items: { type: array, items: { $ref: '#/components/schemas/RelabelItem' } } unitFacts: { type: array, items: { $ref: '#/components/schemas/RelabelUnitFact' } } history: { type: array, items: { $ref: '#/components/schemas/RelabelHistory' } } RelabelItem: type: object properties: itemId: { type: string, pattern: '^[0-9]+$' } skuCode: { type: string, nullable: true } productNameZh: { type: string, nullable: true } shipmentNo: { type: string, nullable: true } trackingCode: { type: string, nullable: true } newSkuCode: { type: string, nullable: true } labelRequirement: { type: string, nullable: true } relabelFileId: { type: string, nullable: true } relabelFileName: { type: string, nullable: true } relabelFileUrl: { type: string, nullable: true } cartonMark: { type: string, nullable: true, description: 我方箱唛 } fbaCartonMark: { type: string, nullable: true, description: 新FBA箱唛 } operationType: { type: string, enum: [CARTON_LABEL, SKU_LABEL, CARTON_AND_SKU] } skuItems: { type: array, items: { $ref: '#/components/schemas/RelabelSkuItem' } } expectedPackages: { type: integer } relabelledPackages: { type: integer } skippedPackages: { type: integer } exceptionPackages: { type: integer } cartonProcessedPackages: { type: integer, description: 本轮箱唛作业已完成箱数,混合单独立计数 } skuProcessedPackages: { type: integer, description: 本轮SKU作业已完成箱数,两项均完成才增加整箱已换标数 } RelabelSkuItem: type: object required: [skuCode, newSkuCode, quantity] properties: skuCode: { type: string } newSkuCode: { type: string } quantity: { type: integer, description: 换标件数 } remark: { type: string, nullable: true } RelabelUnitFact: type: object properties: factId: { type: string, pattern: '^[0-9]+$' } itemId: { type: string, pattern: '^[0-9]+$' } oldLabel: { type: string, nullable: true } newLabel: { type: string, nullable: true } result: { type: string } exceptionCode: { type: string, nullable: true } reason: { type: string, nullable: true } operatorName: { type: string, nullable: true } occurredAt: { type: string, format: date-time } RelabelHistory: type: object properties: action: { type: string } fromStatus: { type: string, nullable: true } toStatus: { type: string, nullable: true } operatorName: { type: string, nullable: true } detail: { type: string, nullable: true } createdAt: { type: string, format: date-time } RelabelPhoto: type: object required: [fileId, fileName, contentType, sizeBytes] properties: fileId: { type: string, pattern: '^[0-9]+$' } fileName: { type: string } contentType: { type: string } sizeBytes: { type: integer, format: int64 } ScanCode: type: object required: [code] properties: { code: { type: string, minLength: 1, maxLength: 128 } } NewRelabelPallet: type: object required: [relabelNo, idempotencyKey] properties: relabelNo: { type: string, minLength: 1, maxLength: 64 } idempotencyKey: { 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 } cartonMarks: type: array maxItems: 500 description: 本次实际扫描的箱唛。一件代发指定箱唛任务分批拣货时必须提交本次子集,数量与 packages 一致;换标任务仍须提交全部指定箱唛。 items: { 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, description: Warehouse 权威实物托盘码 } 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, description: Warehouse 权威实物托盘码 } 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 } PlatformInventoryMove: type: object required: [overseasWarehouseId, sourcePalletCode, targetLocationCode, expectedPalletVersion, idempotencyKey, reason] properties: overseasWarehouseId: { type: string, pattern: '^[0-9]+$' } sourcePalletCode: { type: string, maxLength: 128 } targetLocationCode: { type: string, maxLength: 128 } expectedPalletVersion: { type: integer, format: int64, 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 } RelabelPalletDisassemble: type: object required: [idempotencyKey, relabelNo, source, target, cartonMarks] properties: idempotencyKey: { type: string, maxLength: 128 } relabelNo: { type: string, maxLength: 64 } source: { $ref: '#/components/schemas/PalletExpectation' } target: { $ref: '#/components/schemas/PalletExpectation' } cartonMarks: { type: array, minItems: 1, maxItems: 500, uniqueItems: true, items: { type: string, maxLength: 255 } } actualSource: { $ref: '#/components/schemas/PalletMoveLine' } reason: { type: string, maxLength: 500 } RelabelSource: type: object properties: positionId: { type: string, pattern: '^[0-9]+$' } positionVersion: { type: integer, format: int64 } palletCode: { type: string } locationCode: { type: string, nullable: true } inboundCode: { type: string } stockPackages: { type: integer, format: int64 } movablePackages: { type: integer, format: int64 } cartonMarks: { type: array, items: { type: string } } 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, description: 当前箱的仓库唛头,用于逐箱防重复 } newLabel: { type: string, maxLength: 255, description: 新FBA箱唛,SKU-only不得提交SKU到此字段 } skuVerifications: type: array description: SKU及组合换标必须提交该箱全部旧新SKU和件数核对结果 items: type: object required: [skuCode, newSkuCode, quantity] properties: skuCode: { type: string } newSkuCode: { type: string } quantity: { type: integer, minimum: 1 } 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 }