feat: gate overseas receiving and harden phase4 workflows

This commit is contained in:
2026-08-25 10:31:04 +08:00
parent cb78bc6b41
commit 52b9fc382f
93 changed files with 2779 additions and 201 deletions
@@ -18,6 +18,23 @@ CREATE TABLE IF NOT EXISTS ll_loading_event_inbox (
UNIQUE KEY uk_ll_loading_inbox_tenant_key (customer_id,event_type,event_id)
) ENGINE=InnoDB COMMENT='头程装柜完成事件接收幂等箱';
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户标识',
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
location_code VARCHAR(128) NULL COMMENT '节点地点编码', location_name VARCHAR(255) NULL COMMENT '节点地点名称',
provider_code VARCHAR(64) NULL COMMENT '船节点供应商编码', provider_event_id VARCHAR(128) NULL COMMENT '供应商事件ID',
provider_node_code VARCHAR(128) NULL COMMENT '供应商原始节点编码', source_reference VARCHAR(255) NULL COMMENT '供应商来源引用',
raw_payload_hash VARCHAR(64) NULL COMMENT '供应商原始负载SHA-256', aggregate_version BIGINT NOT NULL DEFAULT 0 COMMENT '来源查询聚合版本',
handover_version BIGINT NOT NULL DEFAULT 1 COMMENT '跨区交接版本', last_event_id VARCHAR(128) NULL COMMENT '最近采纳的跨区事件ID',
payload_hash VARCHAR(71) NULL COMMENT '最近采纳事件规范化负载摘要', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY(container_booking_id), KEY idx_ll_shipping_gate_candidate(customer_id,container_no,status),
KEY idx_ll_shipping_gate_version(container_booking_id,aggregate_version,node_sequence)
) ENGINE=InnoDB COMMENT='海外PDA柜号船节点收货资格快照';
CREATE TABLE IF NOT EXISTS ll_order (
id BIGINT NOT NULL COMMENT '雪花 ID',
order_no VARCHAR(32) NOT NULL COMMENT '尾程单号',
@@ -715,6 +732,18 @@ CREATE TABLE IF NOT EXISTS ll_receiving_container_operation (
UNIQUE KEY uk_ll_receiving_operation(operator_key,idempotency_key), KEY idx_ll_receiving_operation_session(session_id,created_at)
) ENGINE=InnoDB COMMENT='海外PDA柜号收货幂等审计记录';
CREATE TABLE IF NOT EXISTS ll_pda_operation_history (
id BIGINT NOT NULL COMMENT 'PDA作业历史ID', overseas_warehouse_id BIGINT NOT NULL COMMENT 'Token冻结的海外实际仓ID',
customer_id VARCHAR(128) NULL COMMENT '作业涉及的客户标识', operator_key VARCHAR(128) NOT NULL COMMENT 'PDA操作账号标识',
operation_type VARCHAR(64) NOT NULL COMMENT '作业类型', business_key VARCHAR(255) NOT NULL COMMENT '作业业务键',
status VARCHAR(32) NOT NULL COMMENT '作业结果状态', idempotency_key VARCHAR(255) NULL COMMENT '写操作幂等键',
request_hash VARCHAR(128) NULL COMMENT '写请求内容哈希', summary VARCHAR(1000) NULL COMMENT '作业结果摘要',
occurred_at DATETIME(3) NOT NULL COMMENT '作业发生时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY(id), UNIQUE KEY uk_ll_pda_history_idempotency(operator_key,idempotency_key),
KEY idx_ll_pda_history_operator(overseas_warehouse_id,operator_key,occurred_at),
KEY idx_ll_pda_history_business(overseas_warehouse_id,operation_type,business_key)
) ENGINE=InnoDB COMMENT='海外PDA真实作业历史';
CREATE TABLE IF NOT EXISTS ll_inbound_fba_appointment (
id BIGINT NOT NULL COMMENT '主键ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
@@ -1253,6 +1282,7 @@ CREATE TABLE IF NOT EXISTS ll_cargo_discard_application (
status VARCHAR(32) NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态', remark VARCHAR(1000) NULL COMMENT '备注',
idempotency_key VARCHAR(255) NOT NULL COMMENT '业务幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求内容哈希',
created_by VARCHAR(128) NULL COMMENT '创建人ID', reviewed_by VARCHAR(128) NULL COMMENT '审核人ID', review_remark VARCHAR(1000) NULL COMMENT '审核备注',
review_idempotency_key VARCHAR(255) NULL COMMENT '审核幂等键', review_request_hash CHAR(64) NULL COMMENT '审核请求内容哈希',
reviewed_at DATETIME(3) NULL COMMENT '审核时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY(id), UNIQUE KEY uk_ll_cargo_discard_no(customer_id,discard_no),
@@ -1303,6 +1333,7 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order (
estimated_arrival_at DATETIME(3) NULL COMMENT '预计到达时间', status VARCHAR(32) NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '业务状态',
remark VARCHAR(1000) NULL COMMENT '备注', idempotency_key VARCHAR(255) NOT NULL COMMENT '业务幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求内容哈希',
created_by VARCHAR(128) NULL COMMENT '创建人ID', reviewed_by VARCHAR(128) NULL COMMENT '审核人ID', review_remark VARCHAR(1000) NULL COMMENT '审核备注',
review_idempotency_key VARCHAR(255) NULL COMMENT '审核幂等键', review_request_hash CHAR(64) NULL COMMENT '审核请求内容哈希',
reviewed_at DATETIME(3) NULL COMMENT '审核时间', version BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
@@ -1316,7 +1347,7 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_status_history (
id BIGINT NOT NULL COMMENT '主键ID', order_id BIGINT NOT NULL COMMENT '订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
operation_type VARCHAR(32) NOT NULL COMMENT '操作类型:CREATE、REVIEW或UPDATE',
from_status VARCHAR(32) NULL COMMENT '变更前状态', to_status VARCHAR(32) NOT NULL COMMENT '变更后状态', operator_id VARCHAR(128) NULL COMMENT '操作人ID',
change_detail JSON NULL COMMENT '字段变更前后值JSON', remark VARCHAR(1000) NULL COMMENT '备注',
idempotency_key VARCHAR(255) NULL COMMENT '操作幂等键', change_detail JSON NULL COMMENT '字段变更前后值JSON', remark VARCHAR(1000) NULL COMMENT '备注',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY(id), KEY idx_ll_overseas_procurement_history(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='海外采购操作流水';
@@ -1335,7 +1366,6 @@ CREATE TABLE IF NOT EXISTS ll_us_truck_order (
package_count INT NULL COMMENT '可选箱数,零或空值均有效',
received_pallet_count INT NOT NULL DEFAULT 0 COMMENT 'PDA实收托盘数',
allocated_pallet_count INT NOT NULL DEFAULT 0 COMMENT '已分配到合车行程的托盘数',
shipped_pallet_count INT NOT NULL DEFAULT 0 COMMENT '已出库托盘数',
total_weight_kg DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT '总重量(千克)',
total_volume_cbm DECIMAL(18,6) NOT NULL DEFAULT 0 COMMENT '总体积(立方米)',
budget_amount DECIMAL(18,2) NULL COMMENT '预算金额',
@@ -1345,8 +1375,6 @@ CREATE TABLE IF NOT EXISTS ll_us_truck_order (
requested_date DATE NOT NULL COMMENT '要求服务日期',
status VARCHAR(32) NOT NULL COMMENT '美国卡车订单状态',
remark VARCHAR(1000) NULL COMMENT '备注',
receipt_idempotency_key VARCHAR(255) NULL COMMENT '实物收货幂等键',
receipt_request_hash CHAR(64) NULL COMMENT '实物收货请求哈希',
idempotency_key VARCHAR(255) NOT NULL COMMENT '业务幂等键',
request_hash CHAR(64) NOT NULL COMMENT '请求内容哈希',
created_by VARCHAR(128) NULL COMMENT '创建人ID',
@@ -1356,7 +1384,6 @@ CREATE TABLE IF NOT EXISTS ll_us_truck_order (
PRIMARY KEY(id),
UNIQUE KEY uk_ll_us_truck_order_no(customer_id,order_no),
UNIQUE KEY uk_ll_us_truck_order_key(customer_id,idempotency_key),
UNIQUE KEY uk_ll_us_truck_receipt_key(customer_id,receipt_idempotency_key),
KEY idx_ll_us_truck_order_list(customer_id,status,created_at),
KEY idx_ll_us_truck_order_date(customer_id,requested_date,created_at)
) ENGINE=InnoDB COMMENT='美国卡车运输订单';
@@ -1380,13 +1407,25 @@ CREATE TABLE IF NOT EXISTS ll_us_truck_order_pallet (
KEY idx_ll_us_truck_order_pallet_list(customer_id,order_id,status)
) ENGINE=InnoDB COMMENT='美国卡车订单托盘实收记录';
CREATE TABLE IF NOT EXISTS ll_us_truck_order_operation (
id BIGINT NOT NULL COMMENT '操作记录ID', order_id BIGINT NOT NULL COMMENT '美国卡车订单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', operation_type VARCHAR(64) NOT NULL COMMENT '具名操作类型',
idempotency_key VARCHAR(255) NOT NULL COMMENT '操作幂等键', request_hash CHAR(64) NOT NULL COMMENT '操作请求内容哈希',
result_version BIGINT NOT NULL COMMENT '操作完成后的订单版本', created_by VARCHAR(128) NULL COMMENT '操作人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY(id),
UNIQUE KEY uk_ll_us_truck_operation_key(customer_id,order_id,idempotency_key),
KEY idx_ll_us_truck_operation_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='美国卡车订单幂等操作账';
CREATE TABLE IF NOT EXISTS ll_us_truck_order_status_history (
id BIGINT NOT NULL COMMENT '主键ID',
order_id BIGINT NOT NULL COMMENT '订单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
operation_type VARCHAR(64) NOT NULL COMMENT '具名操作类型',
from_status VARCHAR(32) NULL COMMENT '变更前状态',
to_status VARCHAR(32) NOT NULL COMMENT '变更后状态',
operator_id VARCHAR(128) NULL COMMENT '操作人ID',
idempotency_key VARCHAR(255) NULL COMMENT '操作幂等键',
remark VARCHAR(1000) NULL COMMENT '备注',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY(id),
@@ -1421,7 +1460,17 @@ CREATE TABLE IF NOT EXISTS ll_local_looseboard_history (
id BIGINT NOT NULL COMMENT '主键ID', order_id BIGINT NOT NULL COMMENT '本土散板订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
operation_type VARCHAR(32) NOT NULL COMMENT '操作类型:CREATE、UPDATE或STATUS', from_status VARCHAR(32) NULL COMMENT '变更前状态',
to_status VARCHAR(32) NOT NULL COMMENT '变更后状态', operator_id VARCHAR(128) NULL COMMENT '操作人ID',
change_detail JSON NULL COMMENT '字段变更前后值JSON', remark VARCHAR(1000) NULL COMMENT '操作备注',
idempotency_key VARCHAR(255) NULL COMMENT '操作幂等键', change_detail JSON NULL COMMENT '字段变更前后值JSON', remark VARCHAR(1000) NULL COMMENT '操作备注',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY(id),
KEY idx_ll_local_looseboard_history(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='本土散板操作流水';
CREATE TABLE IF NOT EXISTS ll_local_looseboard_operation (
id BIGINT NOT NULL COMMENT '操作记录ID', order_id BIGINT NOT NULL COMMENT '本土散板订单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', operation_type VARCHAR(32) NOT NULL COMMENT '操作类型',
idempotency_key VARCHAR(255) NOT NULL COMMENT '操作幂等键', request_hash CHAR(64) NOT NULL COMMENT '操作请求内容哈希',
result_version BIGINT NOT NULL COMMENT '操作完成后的订单版本', created_by VARCHAR(128) NULL COMMENT '操作人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY(id),
UNIQUE KEY uk_ll_local_looseboard_operation_key(customer_id,order_id,idempotency_key),
KEY idx_ll_local_looseboard_operation_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='本土散板订单幂等操作账';
@@ -6,7 +6,7 @@ paths:
/api/pda/v1/last-leg/receiving-containers/candidates:
get:
summary: 查询当前客户待签收柜号
description: 权限 lastleg:pda:operate;仅 PLATFORM WAREHOUSE_PICKER 可用,操作账号从 Token 获取,客户由所选柜业务事实唯一解析,不接收 customerId。
description: 权限 lastleg:pda:operate;仅 PLATFORM WAREHOUSE_PICKER 可用;仅返回已单调消费 PICKUP_READY(80) 或更高实际船节点、状态为 RECEIVING_AVAILABLE 的柜,不接收 customerId。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 柜号候选及箱数进度, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingCandidateListApiResponse' } } } }, '403': { description: 缺少 PDA 操作权限 } }
/api/pda/v1/last-leg/receiving-containers/{containerNo}:
@@ -19,7 +19,7 @@ paths:
/api/pda/v1/last-leg/receiving-containers/{containerNo}/prepare:
post:
summary: 选择当前待签收柜号
description: 权限 lastleg:pda:operate;建立 WAIT_SIGN 会话并以平台操作账号、柜号全局双唯一键防止切柜或多人同时处理。
description: 权限 lastleg:pda:operate;仅 RECEIVING_AVAILABLE 柜可建立 WAIT_SIGN 会话,并以平台操作账号、柜号全局双唯一键防止切柜或多人同时处理。
security: [{ bearerAuth: [] }]
parameters: [{ name: containerNo, in: path, required: true, schema: { type: string } }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingAction' } } } }
@@ -86,6 +86,37 @@ paths:
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingAction' } } } }
responses: { '200': { description: COMPLETED 柜号会话, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingSessionApiResponse' } } } }, '409': { description: 数量未核平或状态冲突 } }
/api/pda/v1/last-leg/inventory/resolve:
post:
summary: PDA按SKU、箱唛或入仓码扫码查询库存
description: 仅 WAREHOUSE_PICKER + lastleg:pda:operate;海外实际仓只取Token overseasWarehouseId,不接收客户或仓库参数。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PdaWarehouseScan' } } } }
responses: { '200': { description: 同仓跨客户库存与物理仓位事实, content: { application/json: { schema: { $ref: '#/components/schemas/PdaInventoryScanApiResponse' } } } }, '404': { description: A0404:当前绑定仓无匹配库存 } }
/api/pda/v1/last-leg/locations/resolve:
post:
summary: PDA查询库位容量、状态与库存占用
description: 库位从Warehouse Overseas公共资源解析,并强制匹配Token绑定仓。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PdaWarehouseScan' } } } }
responses: { '200': { description: 库位物理事实及Lastleg库存占用汇总, content: { application/json: { schema: { $ref: '#/components/schemas/PdaLocationApiResponse' } } } }, '404': { description: A0404:库位不存在或跨仓 } }
/api/pda/v1/last-leg/pallets/resolve:
post:
summary: PDA查询托盘、库位与绑定库存
description: 托盘从Warehouse Overseas公共资源解析;跨Token绑定仓返回A0409。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/PdaWarehouseScan' } } } }
responses: { '200': { description: 托盘物理事实及Lastleg绑定库存, content: { application/json: { schema: { $ref: '#/components/schemas/PdaPalletApiResponse' } } } }, '409': { description: A0409:托盘属于其他仓 } }
/api/pda/v1/last-leg/operations:
get:
summary: 查询当前PDA账号真实作业历史
description: 返回Token绑定仓内当前操作账号的柜号作业及盘点、移库、托盘、换标历史;不含出库或派送。
security: [{ bearerAuth: [] }]
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, schema: { type: string } }
responses: { '200': { description: 真实作业历史分页, content: { application/json: { schema: { $ref: '#/components/schemas/PdaOperationPageApiResponse' } } } } }
/api/platform/v1/last-leg/orders:
get:
summary: 分页查询尾程订单
@@ -269,14 +300,14 @@ paths:
/api/platform/v1/last-leg/overseas-procurements/{orderNo}/review:
post:
summary: 审核海外采购订单
description: 权限 platform:overseas-business:purchase:review。
description: 权限 platform:overseas-business:purchase:review;同幂等键同请求重放成功,异请求、旧 version 或终态反向审核返回 A0409。
security: [{ bearerAuth: [] }]
parameters:
- { name: orderNo, in: path, required: true, schema: { type: string } }
- { name: customerId, in: query, required: true, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody:
required: true
content: { application/json: { schema: { type: object, required: [decision], properties: { decision: { type: string, enum: [APPROVE, REJECT] }, remark: { type: string, nullable: true, maxLength: 1000 } } } } }
content: { application/json: { schema: { $ref: '#/components/schemas/ProcurementReview' } } }
responses: { '200': { description: 统一 ApiResponse 包装的采购订单详情 } }
/api/merchant/v1/last-leg/overseas-procurements:
post:
@@ -325,14 +356,24 @@ paths:
responses: { '200': { description: 统一 ApiResponse 包装的卡车订单详情 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/status:
patch:
summary: 推进本土卡车订单状态
description: 权限 platform:overseas-business:truck:status;普通编辑接口不能修改状态。
summary: 取消未发车的本土卡车订单
description: 权限 platform:overseas-business:truck:status;该通用入口只接受 CANCELLED,正向状态必须使用具名命令,不能绕过实收、超托与分配事实。
security: [{ bearerAuth: [] }]
parameters:
- { name: orderNo, in: path, required: true, schema: { type: string } }
- { name: customerId, in: query, required: true, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody: { required: true, content: { application/json: { schema: { type: object, required: [version, status], properties: { version: { type: integer, format: int64, minimum: 0 }, status: { $ref: '#/components/schemas/TruckStatus' }, remark: { type: string, nullable: true, maxLength: 1000 } } } } } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckCancel' } } } }
responses: { '200': { description: 统一 ApiResponse 包装的卡车订单详情 }, '409': { description: 非法状态迁移 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/review:
parameters: &truckOrderTarget
- { name: orderNo, in: path, required: true, schema: { type: string } }
- { name: customerId, in: query, required: true, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
post:
summary: 审核本土卡车订单
description: 权限 platform:overseas-business:truck:status;PENDING_REVIEW 审核通过进入 WAITING_INBOUND 并生成预期托盘;拒绝进入 REJECTED。幂等键异请求、旧 version 或非法跳转返回 A0409。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckReview' } } } }
responses: { '200': { description: 卡车订单详情 }, '409': { description: 版本、状态或幂等冲突 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/receive-pallets:
post:
summary: 记录本土卡车订单实收托盘
@@ -341,7 +382,56 @@ paths:
parameters:
- { name: orderNo, in: path, required: true, schema: { type: string } }
- { name: customerId, in: query, required: true, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckReceivePallets' } } } }
responses: { '200': { description: 统一 ApiResponse 包装的卡车订单详情 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/review-pallet-overage:
parameters: *truckOrderTarget
post:
summary: 审核实收超出申报数的托盘
description: 权限 platform:overseas-business:truck:status;APPROVE 将超收托盘纳入本单并进入 WAITING_CONSOLIDATION;REJECT 标记超收托盘拒收并按原申报数继续。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckReviewPalletOverage' } } } }
responses: { '200': { description: 卡车订单详情 }, '409': { description: 版本、状态或幂等冲突 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/allocate-pallets:
parameters: *truckOrderTarget
post:
summary: 分配已实收托盘到本土卡车行程
description: 权限 platform:overseas-business:truck:status;仅接收本单 RECEIVED 托盘 ID;部分分配进入 PARTIALLY_ALLOCATED,全部分配进入 ALLOCATED。不创建海外仓出库单或扣减库存。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckAllocatePallets' } } } }
responses: { '200': { description: 卡车订单详情 }, '409': { description: 托盘归属、状态、版本或幂等冲突 } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/prepare-dispatch:
parameters: *truckOrderTarget
post:
summary: 全部托盘分配后准备派车
description: 权限 platform:overseas-business:truck:status;旧 version、幂等键异请求或非 ALLOCATED 状态返回 A0409。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckCommand' } } } }
responses: { '200': { description: 状态 PENDING_DISPATCH } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/dispatch:
parameters: *truckOrderTarget
post:
summary: 确认本土卡车已派车
description: 权限 platform:overseas-business:truck:status;本土卡车单自身履约,不创建海外仓排车单、出库单或派送单。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckCommand' } } } }
responses: { '200': { description: 状态 DISPATCHED } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/start-transit:
parameters: *truckOrderTarget
post:
summary: 确认本土卡车开始运输
description: 权限 platform:overseas-business:truck:status;仅 DISPATCHED 可进入 IN_TRANSIT。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckCommand' } } } }
responses: { '200': { description: 状态 IN_TRANSIT } }
/api/platform/v1/last-leg/us-truck-orders/{orderNo}/complete:
parameters: *truckOrderTarget
post:
summary: 完成本土卡车订单
description: 权限 platform:overseas-business:truck:status;仅 IN_TRANSIT 可进入不可逆 COMPLETED。
security: [{ bearerAuth: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/TruckCommand' } } } }
responses: { '200': { description: 状态 COMPLETED } }
/api/platform/v1/last-leg/local-looseboard-orders:
get:
summary: 分页查询本土散板订单
@@ -373,12 +463,12 @@ paths:
/api/platform/v1/last-leg/local-looseboard-orders/{orderNo}/status:
patch:
summary: 推进或取消本土散板订单
description: 权限 platform:overseas-business:looseboard:status;只允许 PENDING_ACCEPTANCE→DISPATCHED→IN_TRANSIT→COMPLETED,未完成节点按规则取消。
description: 权限 platform:overseas-business:looseboard:status;只允许 PENDING_ACCEPTANCE→DISPATCHED→IN_TRANSIT→COMPLETED,另建订单可进入 CANCELLED;幂等键异请求、旧 version、非法跳转或终态反向返回 A0409。
security: [{ bearerAuth: [] }]
parameters:
- { name: orderNo, in: path, required: true, schema: { type: string } }
- { name: customerId, in: query, required: true, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody: { required: true, content: { application/json: { schema: { type: object, required: [version, status], properties: { version: { type: integer, format: int64, minimum: 0 }, status: { $ref: '#/components/schemas/LooseboardStatus' }, remark: { type: string, nullable: true, maxLength: 1000 } } } } } }
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/LooseboardStatusChange' } } } }
responses: { '200': { description: 统一 ApiResponse 包装的散板订单详情 }, '409': { description: 非法状态迁移 } }
/api/platform/v1/last-leg/relabel-applications:
get:
@@ -423,6 +513,101 @@ paths:
responses: { '200': { description: 统一 ApiResponse 包装的换标单详情 }, '409': { description: 非法状态迁移或关联尾程单缺失 } }
components:
schemas:
PdaWarehouseScan:
type: object
required: [code]
properties: { code: { type: string, minLength: 1, maxLength: 128 } }
PdaInventoryItem:
type: object
required: [customerId, balanceId, positionId, inboundCode, palletId, palletCode, availablePackages, holdPackages, receivingPackages, status, version]
properties:
customerId: { type: string }
customerName: { type: string, nullable: true }
balanceId: { type: integer, format: int64 }
positionId: { type: integer, format: int64 }
skuCode: { type: string, nullable: true }
productName: { type: string, nullable: true }
inboundCode: { type: string }
cartonMark: { type: string, nullable: true }
palletId: { type: integer, format: int64 }
palletCode: { type: string }
locationId: { type: integer, format: int64, nullable: true }
locationCode: { type: string, nullable: true }
availablePackages: { type: integer, format: int64, minimum: 0 }
holdPackages: { type: integer, format: int64, minimum: 0 }
receivingPackages: { type: integer, format: int64, minimum: 0 }
status: { type: string }
version: { type: integer, format: int64, minimum: 0 }
PdaInventoryScan:
type: object
required: [queryCode, items]
properties:
queryCode: { type: string }
items: { type: array, items: { $ref: '#/components/schemas/PdaInventoryItem' } }
PdaLocation:
type: object
required: [locationId, locationCode, locationType, status, occupiedPackages, inventoryPositionCount]
properties:
locationId: { type: integer, format: int64 }
locationCode: { type: string }
locationType: { type: string }
maxLoadKg: { type: number, nullable: true }
status: { type: string }
occupiedPackages: { type: integer, format: int64, minimum: 0 }
inventoryPositionCount: { type: integer, minimum: 0 }
PdaPallet:
type: object
required: [palletId, palletCode, status, version, locationId, locationCode, occupiedPackages, inventoryPositionCount, inventory]
properties:
palletId: { type: integer, format: int64 }
palletCode: { type: string }
status: { type: string }
version: { type: integer, minimum: 0 }
locationId: { type: integer, format: int64 }
locationCode: { type: string }
occupiedPackages: { type: integer, format: int64, minimum: 0 }
inventoryPositionCount: { type: integer, minimum: 0 }
inventory: { type: array, items: { $ref: '#/components/schemas/PdaInventoryItem' } }
PdaOperation:
type: object
required: [operationId, operationType, businessKey, status, occurredAt]
properties:
operationId: { type: integer, format: int64 }
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 }
PdaInventoryScanApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
- type: object
properties: { data: { $ref: '#/components/schemas/PdaInventoryScan' } }
PdaLocationApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
- type: object
properties: { data: { $ref: '#/components/schemas/PdaLocation' } }
PdaPalletApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
- type: object
properties: { data: { $ref: '#/components/schemas/PdaPallet' } }
PdaOperationPageApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
- type: object
properties:
data:
type: object
required: [records, page, size, total, pages]
properties:
records: { type: array, items: { $ref: '#/components/schemas/PdaOperation' } }
page: { type: integer }
size: { type: integer }
total: { type: integer, format: int64 }
pages: { type: integer, format: int64 }
ReceivingAction:
type: object
required: [idempotencyKey]
@@ -765,6 +950,14 @@ components:
status: { $ref: '#/components/schemas/RelabelStatus' }
remark: { type: string, nullable: true, maxLength: 1000 }
ProcurementStatus: { type: string, enum: [PENDING_REVIEW, APPROVED, REJECTED] }
ProcurementReview:
type: object
required: [version, decision, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
decision: { type: string, enum: [APPROVE, REJECT] }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
ProcurementCreate:
type: object
required: [purchaseCategory, productName, purchaseQuantity, contactName, contactPhone, idempotencyKey]
@@ -812,6 +1005,49 @@ components:
code: { type: string, enum: ['200'] }
data: { type: array, items: { type: object, properties: { warehouseId: { type: string }, warehouseCode: { type: string }, warehouseName: { type: string }, countryCode: { type: string } } } }
TruckStatus: { type: string, enum: [PENDING_REVIEW, WAITING_INBOUND, RECEIVING, WAITING_CONSOLIDATION, PALLET_OVERAGE_REVIEW, PARTIALLY_ALLOCATED, ALLOCATED, PENDING_DISPATCH, DISPATCHED, IN_TRANSIT, COMPLETED, REJECTED, CANCELLED] }
TruckReview:
type: object
required: [version, decision, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
decision: { type: string, enum: [APPROVE, REJECT] }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
TruckCancel:
type: object
required: [version, status, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
status: { type: string, enum: [CANCELLED] }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
TruckReceivePallets:
type: object
required: [version, actualPalletCount, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
actualPalletCount: { type: integer, minimum: 1 }
packageCount: { type: integer, nullable: true, minimum: 0 }
palletPackageCounts: { type: array, maxItems: 500, items: { type: integer, minimum: 0 } }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
TruckReviewPalletOverage:
allOf: [{ $ref: '#/components/schemas/TruckReview' }]
TruckAllocatePallets:
type: object
required: [version, palletIds, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
palletIds: { type: array, minItems: 1, maxItems: 500, uniqueItems: true, items: { type: integer, format: int64, minimum: 1 } }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
TruckCommand:
type: object
required: [version, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
TruckCreate:
type: object
required: [origin, destination, palletCount, totalWeightKg, totalVolumeCbm, contactName, contactInfo, requestedDate, idempotencyKey]
@@ -855,6 +1091,14 @@ components:
requestedDate: { type: string, format: date }
remark: { type: string, nullable: true, maxLength: 1000 }
LooseboardStatus: { type: string, enum: [PENDING_ACCEPTANCE, DISPATCHED, IN_TRANSIT, COMPLETED, CANCELLED] }
LooseboardStatusChange:
type: object
required: [version, status, idempotencyKey]
properties:
version: { type: integer, format: int64, minimum: 0 }
status: { $ref: '#/components/schemas/LooseboardStatus' }
idempotencyKey: { type: string, minLength: 1, maxLength: 255 }
remark: { type: string, nullable: true, maxLength: 1000 }
LooseboardCreate:
type: object
required: [transportMode, palletRequired, transportType, origin, destination, packageCount, totalWeightKg, totalVolumeCbm, idempotencyKey]