openapi: 3.0.3 info: title: 简化海外采购批次流程 API version: 1.0.0 description: 创建采购单后为待审核;审核通过进入收货中。实收可分批登记,客户确认后按采购库存分批出库。库存仅在本批次签收完成时扣减,创建出库单先占用可用库存。 security: - bearerAuth: [] paths: /api/merchant/v1/last-leg/overseas-procurements/{orderNo}/reject: post: summary: 客户驳回采购结果并填写原因 description: 仅当前认证客户,权限 merchant:cross-border:procurement:create。仅 WAIT_CUSTOMER_CONFIRM,成功回到 RECEIVING;保存原因及当前已上传照片快照,平台须重新上传各批收货照片才能再次提交确认。 parameters: - $ref: '#/components/parameters/OrderNo' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/RejectionCommand' } responses: '200': { $ref: '#/components/responses/WorkflowResponse' } /api/platform/v1/last-leg/overseas-procurements/{orderNo}/receipt-photos: post: summary: 驳回后替换指定批次的收货照片 description: 权限 platform:overseas-business:purchase:receive-overseas。仅客户驳回后的 RECEIVING;文件须为本单新上传图片,不接受驳回前的文件;仅替换照片,不改变实收数量及时间。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ReceiptPhotosCommand' } responses: '200': { $ref: '#/components/responses/WorkflowResponse' } /api/platform/v1/last-leg/overseas-procurements/{orderNo}/workflow: get: summary: 读取采购实收、库存和出库批次 description: 权限 platform:overseas-business:purchase:list;仅平台主体,必须指定目标客户 customerId。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/platform/v1/last-leg/overseas-procurements/{orderNo}/receipts: post: summary: 分批登记实收数量和照片 description: 权限 platform:overseas-business:purchase:receive-overseas;仅平台主体,必须指定目标客户 customerId。仅 RECEIVING;本次数量不得超过未收采购数量。文件须已上传到本采购单,须为图片,分类为 RECEIPT 或 VOUCHER。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReceiptCommand' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/platform/v1/last-leg/overseas-procurements/{orderNo}/request-confirmation: post: summary: 采购完成并请求客户确认 description: 权限 platform:overseas-business:purchase:receive-overseas;仅平台主体,必须指定目标客户 customerId。仅 RECEIVING 且已有实收;客户驳回后必须替换各批旧照片。提交后不再追加收货。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Command' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/platform/v1/last-leg/overseas-procurements/{orderNo}/outbounds: post: summary: 从采购库存创建出库单 description: 权限 platform:overseas-business:purchase:ship-customer;仅平台主体,必须指定目标客户 customerId。仅 CONFIRMED、IN_TRANSIT 或 DELIVERING;数量不得超过 availableQuantity,物流跟踪号必填。费用明细缺失或尚未审核仅作提示,不阻止创建出库单。创建后占用数量增加,不实际扣库存。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OutboundCommand' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/platform/v1/last-leg/overseas-procurements/{orderNo}/outbounds/{batchId}/dispatch: post: summary: 本批出库开始派送 description: 权限 platform:overseas-business:purchase:ship-customer;仅平台主体,必须指定目标客户 customerId。仅本单 IN_TRANSIT 批次可转为 DELIVERING。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' - $ref: '#/components/parameters/BatchId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Command' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/platform/v1/last-leg/overseas-procurements/{orderNo}/outbounds/{batchId}: put: summary: 修改尚未开始派送的出库单 description: 仅 IN_TRANSIT 批次可修改数量和物流跟踪号;开始派送后拒绝修改。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' - $ref: '#/components/parameters/BatchId' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/OutboundCommand' } responses: '200': { $ref: '#/components/responses/WorkflowResponse' } /api/platform/v1/last-leg/overseas-procurements/{orderNo}/outbounds/{batchId}/cancel: post: summary: 取消尚未开始派送的出库单 description: 仅 IN_TRANSIT 批次可取消;取消后释放采购库存占用,开始派送后拒绝取消。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' - $ref: '#/components/parameters/BatchId' requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/Command' } responses: '200': { $ref: '#/components/responses/WorkflowResponse' } /api/platform/v1/last-leg/overseas-procurements/{orderNo}/outbounds/{batchId}/complete: post: summary: 上传凭证并完成本批出库 description: 权限 platform:overseas-business:purchase:ship-customer;仅平台主体,必须指定目标客户 customerId。仅本单 DELIVERING 批次;凭证须属于本单且分类为 RECEIPT 或 VOUCHER,支持文件或图片。批次完成与采购库存扣减同事务,重复请求不重复扣减。 parameters: - $ref: '#/components/parameters/OrderNo' - $ref: '#/components/parameters/CustomerId' - $ref: '#/components/parameters/BatchId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CompletionCommand' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/merchant/v1/last-leg/overseas-procurements/{orderNo}/workflow: get: summary: 客户读取采购实收与出库批次 description: 权限 merchant:cross-border:procurement:list;客户范围只取认证身份,不接收 customerId。仅返回已关联收货或出库批次的文件元信息,不暴露内部文件。 parameters: - $ref: '#/components/parameters/OrderNo' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/merchant/v1/last-leg/overseas-procurements/{orderNo}/confirm: post: summary: 客户确认采购结果 description: 权限 merchant:cross-border:procurement:create;客户范围只取认证身份,不接收 customerId。仅 WAIT_CUSTOMER_CONFIRM 可确认,成功进入 CONFIRMED。 parameters: - $ref: '#/components/parameters/OrderNo' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Command' responses: '200': $ref: '#/components/responses/WorkflowResponse' '401': description: 未认证 '403': description: 身份或操作权限不允许 /api/merchant/v1/last-leg/overseas-procurements/{orderNo}/files/{fileId}/content: get: summary: 客户下载收货照片或签收凭证 description: 权限 merchant:cross-border:procurement:list;客户范围只取认证身份。fileId 必须已关联本单收货或出库批次,内部文件和其他客户文件均不可下载。成功为原始字节流,响应 Cache-Control 为 no-store。 parameters: - $ref: '#/components/parameters/OrderNo' - name: fileId in: path required: true schema: $ref: '#/components/schemas/Id' responses: '200': description: 原始凭证内容;Content-Type 采用文件真实类型,Content-Disposition 为 attachment 并携带文件名 headers: Cache-Control: schema: {type: string, example: no-store} Content-Disposition: schema: {type: string} content: application/octet-stream: schema: {type: string, format: binary} image/*: schema: {type: string, format: binary} '401': description: 未认证 '403': description: 身份或只读权限不允许 components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: OrderNo: name: orderNo in: path required: true schema: {type: string} CustomerId: name: customerId in: query required: true schema: {type: string, pattern: '^[1-9][0-9]*$'} BatchId: name: batchId in: path required: true schema: $ref: '#/components/schemas/Id' responses: WorkflowResponse: description: HTTP 200 业务响应;必须核对 code,版本冲突和非法状态返回非成功业务码 content: application/json: schema: $ref: '#/components/schemas/Response' schemas: Id: type: string pattern: '^[1-9][0-9]*$' description: Long 主键在 JSON 中按字符串传输,避免前端精度损失 Status: type: string enum: [PENDING_REVIEW, RECEIVING, WAIT_CUSTOMER_CONFIRM, CONFIRMED, IN_TRANSIT, DELIVERING, COMPLETED] description: 依次为待审核、收货中、待客户确认、已确认、运输中、派送中、签收完成 Command: type: object required: [version, idempotencyKey] description: version 为当前采购单版本;同单原幂等键与相同参数重试不重复执行,跨动作或改参数复用键会被拒绝 properties: version: {type: integer, format: int64, minimum: 0} idempotencyKey: {type: string, minLength: 1, maxLength: 128} FileIds: type: array minItems: 1 maxItems: 20 uniqueItems: true items: $ref: '#/components/schemas/Id' RejectionCommand: allOf: - $ref: '#/components/schemas/Command' - type: object required: [reason] properties: reason: {type: string, minLength: 1, maxLength: 1000} ReceiptPhotosCommand: allOf: - $ref: '#/components/schemas/Command' - type: object required: [receiptId, fileIds] properties: receiptId: { $ref: '#/components/schemas/Id' } fileIds: { $ref: '#/components/schemas/FileIds' } ReceiptCommand: allOf: - $ref: '#/components/schemas/Command' - type: object required: [quantity, fileIds] properties: quantity: {type: integer, format: int64, minimum: 1} fileIds: $ref: '#/components/schemas/FileIds' OutboundCommand: allOf: - $ref: '#/components/schemas/Command' - type: object required: [quantity, trackingNo] properties: quantity: {type: integer, format: int64, minimum: 1} trackingNo: {type: string, minLength: 1, maxLength: 255} CompletionCommand: allOf: - $ref: '#/components/schemas/Command' - type: object required: [fileIds] properties: fileIds: $ref: '#/components/schemas/FileIds' Receipt: type: object required: [id, quantity, fileIds, receivedAt] properties: id: $ref: '#/components/schemas/Id' quantity: {type: integer, format: int64, minimum: 1} fileIds: type: array items: $ref: '#/components/schemas/Id' receivedAt: {type: string, format: date-time} Outbound: type: object required: [id, outboundNo, quantity, trackingNo, status, fileIds, createdAt, completedAt] properties: id: $ref: '#/components/schemas/Id' outboundNo: {type: string} quantity: {type: integer, format: int64, minimum: 1} trackingNo: {type: string} status: {type: string, enum: [IN_TRANSIT, DELIVERING, COMPLETED]} fileIds: type: array items: $ref: '#/components/schemas/Id' createdAt: {type: string, format: date-time} completedAt: {type: string, format: date-time, nullable: true} FileInfo: type: object required: [fileId, fileName, contentType] properties: fileId: $ref: '#/components/schemas/Id' fileName: {type: string} contentType: {type: string} Workflow: type: object required: [orderNo, version, status, receivedQuantity, stockQuantity, reservedQuantity, availableQuantity, receipts, outbounds, files] description: stockQuantity = receivedQuantity - 已完成出库数量;reservedQuantity 为未完成出库占用;availableQuantity = stockQuantity - reservedQuantity。主状态优先显示运输中批次,其次派送中;全部批次完成但仍有库存时回到已确认,无剩余库存才签收完成。 properties: rejectionReason: {type: string, nullable: true, description: 客户驳回原因} photosNeedUpdate: {type: boolean, description: 存在尚未替换的被驳回照片} orderNo: {type: string} version: {type: integer, format: int64, minimum: 0} status: $ref: '#/components/schemas/Status' receivedQuantity: {type: integer, format: int64, minimum: 0} stockQuantity: {type: integer, format: int64, minimum: 0} reservedQuantity: {type: integer, format: int64, minimum: 0} availableQuantity: {type: integer, format: int64, minimum: 0} receipts: type: array items: $ref: '#/components/schemas/Receipt' outbounds: type: array items: $ref: '#/components/schemas/Outbound' files: type: array items: $ref: '#/components/schemas/FileInfo' Response: type: object required: [code, message, data] properties: code: {type: string, example: '200'} message: {type: string} data: $ref: '#/components/schemas/Workflow'