fix(lastleg): enforce delivery and return workflows

This commit is contained in:
2026-09-08 18:49:52 +08:00
parent d42629d289
commit d1090671ce
23 changed files with 546 additions and 61 deletions
@@ -643,6 +643,49 @@ paths:
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
responses: { '200': { description: 已创建异常订单 } }
/api/platform/v1/last-leg/exception-orders/{exceptionNo}:
get:
summary: 查询异常订单详情与冻结退回明细
description: 权限 platform:overseas-warehouse:exception-order:list;返回 version 和服务端 allowedActions。
security: [{ bearerAuth: [] }]
parameters: [{ name: exceptionNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 异常订单详情 }, '404': { description: 异常订单不存在 } }
/api/platform/v1/last-leg/exception-orders/{exceptionNo}/start-processing:
post:
summary: 开始处理异常订单
description: 权限 platform:overseas-warehouse:exception-order:process;仅 PENDING→PROCESSING,同版本与同幂等请求可重放。
security: [{ bearerAuth: [] }]
parameters: [{ name: exceptionNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ExceptionStartProcessing' } } } }
responses: { '200': { description: 异常处理命令结果 }, '409': { description: 版本、状态或幂等冲突 } }
/api/platform/v1/last-leg/exception-orders/{exceptionNo}/evidence:
post:
summary: 上传异常处理证据
description: 权限 platform:overseas-warehouse:exception-order:process;文件按当前客户和异常订单绑定为 OVERSEAS_EXCEPTION_EVIDENCE。
security: [{ bearerAuth: [] }]
parameters: [{ name: exceptionNo, 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: 异常处理证据元数据 }, '400': { description: 文件或归属无效 } }
/api/platform/v1/last-leg/exception-orders/{exceptionNo}/complete:
post:
summary: 完成异常处理并原子恢复退回库存
description: 权限 platform:overseas-warehouse:exception-order:process;仅 PROCESSING→COMPLETED。退回海外仓必须提交证据和实收目标,库存 RETURN_STOCK_IN 成功后才完成异常单和派送退回。
security: [{ bearerAuth: [] }]
parameters: [{ name: exceptionNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ExceptionComplete' } } } }
responses: { '200': { description: 异常处理与库存恢复结果 }, '409': { description: 版本、状态、归属、数量或幂等冲突 } }
/api/platform/v1/last-leg/exception-orders/{exceptionNo}/cancel:
post:
summary: 取消未处理异常订单
description: 权限 platform:overseas-warehouse:exception-order:process;仅 PENDING→CANCELLED。
security: [{ bearerAuth: [] }]
parameters: [{ name: exceptionNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ExceptionCancel' } } } }
responses: { '200': { description: 异常取消结果 }, '409': { description: 版本、状态或幂等冲突 } }
/api/platform/v1/last-leg/exception-orders/resolve:
get:
summary: 解析异常订单关联关系
@@ -652,17 +695,17 @@ paths:
/api/platform/v1/last-leg/delivery-orders/{deliveryNo}/return-inbound-options:
get:
summary: 查询派送退回候选入仓单
description: 权限 platform:overseas-warehouse:exception-order:list;属于异常订单列表只读辅助接口。
description: 权限 platform:overseas-warehouse:exception-order:list;返回按原出库拣货事实解析的 returnSources,页面不得自由填写 SKU 或猜测库存位置。
security: [{ bearerAuth: [] }]
parameters: [{ name: deliveryNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 退回候选入仓单 } }
/api/platform/v1/last-leg/delivery-orders/{deliveryNo}/mark-return:
post:
summary: 将派送单标记为退回入仓
description: 权限 platform:overseas-warehouse:delivery:return。
description: 权限 platform:overseas-warehouse:delivery:return;items 必须来自 return-inbound-options,服务端冻结源出库明细与批准退回上限。
security: [{ bearerAuth: [] }]
parameters: [{ name: deliveryNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { type: object } } } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeliveryMarkReturn' } } } }
responses: { '200': { description: 已创建退回异常单 } }
/api/platform/v1/last-leg/delivery-orders/{deliveryNo}/complete:
post:
@@ -670,7 +713,8 @@ paths:
description: 权限 platform:overseas-warehouse:delivery:status。仅 WAIT_DELIVERY/DELIVERING 可完成;DELIVERED 同状态重放成功,其他终态或倒退返回 409。
security: [{ bearerAuth: [] }]
parameters: [{ name: deliveryNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 派送状态与回访记录 } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/DeliveryStatusChange' } } } }
responses: { '200': { description: 派送状态与回访记录 }, '409': { description: 版本或状态冲突 } }
/api/platform/v1/last-leg/delivery-orders/{deliveryNo}/status:
put:
summary: 更新派送状态
@@ -1709,14 +1753,74 @@ components:
fees: { type: array, minItems: 1, maxItems: 20, items: { $ref: '#/components/schemas/DeliveryFeeInput' } }
DeliveryStatusChange:
type: object
required: [status]
required: [status, expectedVersion]
properties:
status: { type: string, enum: [WAIT_DELIVERY, DELIVERING, DELIVERED, REJECTED, CANCELLED] }
expectedVersion: { type: integer, format: int64, minimum: 0 }
remark: { type: string, nullable: true, maxLength: 500 }
DeliveryAllowedAction:
type: string
enum: [START_DELIVERY, COMPLETE_DELIVERY, REJECT_DELIVERY, CANCEL_DELIVERY, MARK_RETURN]
description: START/COMPLETE/REJECT/CANCEL 使用通用 status 接口;MARK_RETURN 使用 mark-return 接口。
DeliveryMarkReturn:
type: object
required: [inboundOrderId, reason, returnDirection, items, idempotencyKey]
properties:
inboundOrderId: { type: string, pattern: '^[1-9][0-9]*$' }
reason: { type: string, minLength: 1, maxLength: 1000 }
returnDirection: { type: string, enum: [OVERSEAS_WAREHOUSE, CUSTOMER, OTHER] }
returnLogisticsNo: { type: string, nullable: true, maxLength: 128 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
items:
type: array
minItems: 1
maxItems: 200
items:
type: object
required: [outboundPalletItemId, inventoryPositionId, packageCount]
properties:
outboundPalletItemId: { type: string, pattern: '^[1-9][0-9]*$' }
inventoryPositionId: { type: string, pattern: '^[1-9][0-9]*$' }
packageCount: { type: integer, format: int64, minimum: 1 }
ExceptionStartProcessing:
type: object
required: [expectedVersion, idempotencyKey]
properties:
expectedVersion: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
handlingAction: { type: string, nullable: true, maxLength: 1000 }
ExceptionCancel:
type: object
required: [expectedVersion, idempotencyKey, reason]
properties:
expectedVersion: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
reason: { type: string, minLength: 1, maxLength: 1000 }
ExceptionComplete:
type: object
required: [expectedVersion, idempotencyKey, handlingResult, evidenceFileIds]
properties:
expectedVersion: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
handlingResult: { type: string, minLength: 1, maxLength: 2000 }
evidenceFileIds: { type: array, minItems: 1, maxItems: 20, items: { type: string } }
receipts:
type: array
maxItems: 200
items:
type: object
required: [outboundPalletItemId, inventoryPositionId, receivedPackages, targetPalletId, targetPalletNo, targetLocationId, targetLocationCode]
properties:
outboundPalletItemId: { type: string, pattern: '^[1-9][0-9]*$' }
inventoryPositionId: { type: string, pattern: '^[1-9][0-9]*$' }
receivedPackages: { type: integer, format: int64, minimum: 1 }
targetPalletId: { type: string, pattern: '^[1-9][0-9]*$' }
targetPalletNo: { type: string, minLength: 1, maxLength: 128 }
targetLocationId: { type: string, pattern: '^[1-9][0-9]*$' }
targetLocationCode: { type: string, minLength: 1, maxLength: 128 }
ExceptionAllowedAction:
type: string
enum: [START_PROCESSING, COMPLETE_EXCEPTION, CANCEL_EXCEPTION]
DeliveryFeeInput:
type: object
required: [feeType, currency, amount]