This commit is contained in:
2026-09-18 09:59:04 +08:00
106 changed files with 5113 additions and 471 deletions
@@ -76,9 +76,9 @@ baiyue:
url: ${DOMESTIC_WAREHOUSE_URL:}
cache-ttl: ${DOMESTIC_WAREHOUSE_CACHE_TTL:30m}
file:
url: ${FILE_SERVICE_URL:http://127.0.0.1:9205}
url: ${FILE_SERVICE_URL:http://127.0.0.1:9206}
domestic-file:
url: ${FILE_SERVICE_URL:http://127.0.0.1:9205}
url: ${FILE_SERVICE_URL:http://127.0.0.1:9206}
event-bridge:
url: ${CN_EVENT_BRIDGE_URL:${FIRSTLEG_EVENT_BRIDGE_URL:}}
firstleg:
@@ -75,6 +75,7 @@ CREATE TABLE IF NOT EXISTS ll_order (
version INT NOT NULL DEFAULT 0 COMMENT '尾程订单命令乐观锁版本',
pallet_count INT NOT NULL DEFAULT 0 COMMENT '托数',
package_count INT NOT NULL DEFAULT 0 COMMENT '箱数',
received_package_count INT NULL COMMENT 'PDA实际入库箱数',
picked_package_count INT NOT NULL DEFAULT 0 COMMENT '累计已拣箱数',
shipped_package_count INT NOT NULL DEFAULT 0 COMMENT '累计已出库箱数',
volume_cbm DECIMAL(18,4) COMMENT '体积(立方米)',
@@ -267,14 +268,19 @@ CREATE TABLE IF NOT EXISTS ll_delivery_order (
delivery_no VARCHAR(64) NOT NULL COMMENT '派送单号',
outbound_order_id BIGINT NOT NULL COMMENT '关联出库单ID',
outbound_no VARCHAR(64) NOT NULL COMMENT '关联出库单号',
dispatch_order_id BIGINT NOT NULL COMMENT '关联派车单ID',
dispatch_no VARCHAR(64) NOT NULL COMMENT '关联派车单号',
dispatch_order_id BIGINT NULL COMMENT '关联派车单ID,仓储转运直接出库时为空',
dispatch_no VARCHAR(64) NULL COMMENT '关联派车单号,仓储转运直接出库时为空',
overseas_warehouse_id VARCHAR(128) NULL COMMENT '海外仓ID',
overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照',
customer_name VARCHAR(255) NULL COMMENT '客户名称快照',
destination_warehouse_id VARCHAR(128) NULL COMMENT '本派送单目的FBA仓ID快照',
destination_warehouse_code VARCHAR(128) NOT NULL COMMENT '本派送单目的FBA仓编码快照',
destination_warehouse_code VARCHAR(128) NULL COMMENT '本派送单目的FBA仓编码快照,私人仓为空',
destination_warehouse_name VARCHAR(255) NULL COMMENT '本派送单目的FBA仓名称快照',
destination_type VARCHAR(16) NULL COMMENT '目的地类型:FBA或PRIVATE',
delivery_address VARCHAR(1000) NULL COMMENT '私人仓派送地址',
postal_code VARCHAR(32) NULL COMMENT '私人仓邮编',
contact_name VARCHAR(128) NULL COMMENT '私人仓联系人',
contact_phone VARCHAR(64) NULL COMMENT '私人仓联系电话',
delivery_group_key VARCHAR(255) NOT NULL COMMENT '同一出库单内按目的FBA仓拆单的稳定分组键',
status VARCHAR(32) NOT NULL DEFAULT 'WAIT_DELIVERY' COMMENT '派送状态:WAIT_DELIVERY待派送及后续正式状态',
waybill_no VARCHAR(128) NULL COMMENT '承运运单号',
@@ -318,6 +324,12 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
PREPARE delivery_detail_stmt FROM @ddl; EXECUTE delivery_detail_stmt; DEALLOCATE PREPARE delivery_detail_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='destination_warehouse_code'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN destination_warehouse_id VARCHAR(128) NULL COMMENT ''本派送单目的FBA仓ID快照'' AFTER customer_name, ADD COLUMN destination_warehouse_code VARCHAR(128) NULL COMMENT ''本派送单目的FBA仓编码快照'' AFTER destination_warehouse_id, ADD COLUMN destination_warehouse_name VARCHAR(255) NULL COMMENT ''本派送单目的FBA仓名称快照'' AFTER destination_warehouse_code, ADD COLUMN delivery_group_key VARCHAR(255) NULL COMMENT ''同一出库单内按目的FBA仓拆单的稳定分组键'' AFTER destination_warehouse_name'));
PREPARE delivery_destination_stmt FROM @ddl; EXECUTE delivery_destination_stmt; DEALLOCATE PREPARE delivery_destination_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN destination_type VARCHAR(16) NULL COMMENT ''目的地类型:FBA或PRIVATE'' AFTER destination_warehouse_name, ADD COLUMN delivery_address VARCHAR(1000) NULL COMMENT ''私人仓派送地址'' AFTER destination_type, ADD COLUMN contact_name VARCHAR(128) NULL COMMENT ''私人仓联系人'' AFTER delivery_address, ADD COLUMN contact_phone VARCHAR(64) NULL COMMENT ''私人仓联系电话'' AFTER contact_name'));
PREPARE delivery_private_destination_stmt FROM @ddl; EXECUTE delivery_private_destination_stmt; DEALLOCATE PREPARE delivery_private_destination_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='postal_code'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编'' AFTER delivery_address'));
PREPARE delivery_postal_stmt FROM @ddl; EXECUTE delivery_postal_stmt; DEALLOCATE PREPARE delivery_postal_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='dispatch_order_id' AND is_nullable='YES'), 'SELECT 1', 'ALTER TABLE ll_delivery_order MODIFY COLUMN dispatch_order_id BIGINT NULL COMMENT ''关联派车单ID,仓储转运直接出库时为空'', MODIFY COLUMN dispatch_no VARCHAR(64) NULL COMMENT ''关联派车单号,仓储转运直接出库时为空'', MODIFY COLUMN destination_warehouse_code VARCHAR(128) NULL COMMENT ''本派送单目的FBA仓编码快照,私人仓为空'''));
PREPARE delivery_transfer_nullable_stmt FROM @ddl; EXECUTE delivery_transfer_nullable_stmt; DEALLOCATE PREPARE delivery_transfer_nullable_stmt;
SET @old_delivery_outbound_index = (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND index_name='uk_ll_delivery_outbound');
SET @new_delivery_destination_index = (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND index_name='uk_ll_delivery_outbound_destination');
SET @ddl = IF(@new_delivery_destination_index > 0, 'SELECT 1', IF(@old_delivery_outbound_index > 0, 'ALTER TABLE ll_delivery_order DROP INDEX uk_ll_delivery_outbound, ADD UNIQUE KEY uk_ll_delivery_outbound_destination (customer_id,outbound_order_id,delivery_group_key)', 'ALTER TABLE ll_delivery_order ADD UNIQUE KEY uk_ll_delivery_outbound_destination (customer_id,outbound_order_id,delivery_group_key)'));
@@ -599,7 +611,7 @@ CREATE TABLE IF NOT EXISTS ll_inbound_plan (
assigned_flag TINYINT NOT NULL DEFAULT 0 COMMENT '人工计划是否已分配:0否1是',
merge_required TINYINT NOT NULL DEFAULT 0 COMMENT '人工计划是否要求合托:0否1是',
urgent_flag TINYINT NOT NULL DEFAULT 0 COMMENT '人工计划是否加急:0否1是',
creation_mode VARCHAR(16) NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工',
creation_mode VARCHAR(32) NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运',
plan_remark VARCHAR(1000) NULL COMMENT '入仓计划备注',
status VARCHAR(32) NOT NULL DEFAULT 'PLANNED' COMMENT '入库计划状态',
line_count INT NOT NULL DEFAULT 0 COMMENT '入仓码明细数量',
@@ -648,6 +660,10 @@ CREATE TABLE IF NOT EXISTS ll_inbound_plan_line (
destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID快照',
destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照',
destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
delivery_address VARCHAR(1000) NULL COMMENT '私人仓目的地址快照',
postal_code VARCHAR(32) NULL COMMENT '私人仓邮编快照',
contact_name VARCHAR(128) NULL COMMENT '私人仓联系人快照',
contact_phone VARCHAR(64) NULL COMMENT '私人仓联系电话快照',
volume_cbm DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '入仓码体积(立方米)',
planned_pallet_count INT NOT NULL DEFAULT 0 COMMENT '该明细分配的托盘数量',
PRIMARY KEY (id),
@@ -682,6 +698,11 @@ CREATE TABLE IF NOT EXISTS ll_inbound_plan_pallet (
destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID快照',
destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照',
destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
destination_type VARCHAR(32) NULL COMMENT '目的类型:FBA或PRIVATE',
delivery_address VARCHAR(1000) NULL COMMENT '私人仓目的地址快照',
postal_code VARCHAR(32) NULL COMMENT '私人仓邮编快照',
contact_name VARCHAR(128) NULL COMMENT '私人仓联系人快照',
contact_phone VARCHAR(64) NULL COMMENT '私人仓联系电话快照',
capacity_cbm DECIMAL(18,4) NOT NULL DEFAULT 2.0000 COMMENT '托盘容量,固定为2立方米',
allocated_volume_cbm DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '已分配体积(立方米)',
sequence_no INT NOT NULL COMMENT '计划内托盘序号',
@@ -721,8 +742,15 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
PREPARE inbound_plan_manual_stmt FROM @ddl; EXECUTE inbound_plan_manual_stmt; DEALLOCATE PREPARE inbound_plan_manual_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan' AND column_name='urgent_flag'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan ADD COLUMN urgent_flag TINYINT NOT NULL DEFAULT 0 COMMENT ''人工计划是否加急:0否1是'' AFTER merge_required'));
PREPARE inbound_plan_manual_stmt FROM @ddl; EXECUTE inbound_plan_manual_stmt; DEALLOCATE PREPARE inbound_plan_manual_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan' AND column_name='creation_mode'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan ADD COLUMN creation_mode VARCHAR(16) NOT NULL DEFAULT ''AUTO'' COMMENT ''计划生成方式:AUTO柜级自动、MANUAL单票人工'' AFTER urgent_flag'));
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan' AND column_name='creation_mode'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan ADD COLUMN creation_mode VARCHAR(32) NOT NULL DEFAULT ''AUTO'' COMMENT ''计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运'' AFTER urgent_flag'));
PREPARE inbound_plan_manual_stmt FROM @ddl; EXECUTE inbound_plan_manual_stmt; DEALLOCATE PREPARE inbound_plan_manual_stmt;
ALTER TABLE ll_inbound_plan MODIFY COLUMN creation_mode VARCHAR(32) NOT NULL DEFAULT 'AUTO' COMMENT '计划生成方式:AUTO柜级自动、MANUAL单票人工、WAREHOUSE_TRANSFER仓储转运';
UPDATE ll_inbound_plan_pallet_allocation a
JOIN ll_inbound_plan p ON p.id=a.plan_id
SET a.picked_package_count=COALESCE(a.counted_package_count,a.planned_package_count,0)
WHERE p.creation_mode='WAREHOUSE_TRANSFER' AND p.status='COMPLETED'
AND COALESCE(a.picked_package_count,0)=0
AND COALESCE(a.counted_package_count,a.planned_package_count,0)>0;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan' AND column_name='plan_remark'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan ADD COLUMN plan_remark VARCHAR(1000) NULL COMMENT ''入仓计划备注'' AFTER creation_mode'));
PREPARE inbound_plan_manual_stmt FROM @ddl; EXECUTE inbound_plan_manual_stmt; DEALLOCATE PREPARE inbound_plan_manual_stmt;
ALTER TABLE ll_inbound_plan
@@ -1329,6 +1357,11 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_item (
destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照',
destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
fba_warehouse_code VARCHAR(128) NULL COMMENT 'FBA仓库编码',
destination_type VARCHAR(32) NULL COMMENT '目的类型:FBA或PRIVATE',
delivery_address VARCHAR(1000) NULL COMMENT '私人仓目的地址快照',
postal_code VARCHAR(32) NULL COMMENT '私人仓邮编快照',
contact_name VARCHAR(128) NULL COMMENT '私人仓联系人快照',
contact_phone VARCHAR(64) NULL COMMENT '私人仓联系电话快照',
package_count INT NOT NULL DEFAULT 0 COMMENT '箱数',
volume_cbm DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '体积(立方米)',
source_stage VARCHAR(32) NOT NULL COMMENT '数据来源阶段',
@@ -1346,6 +1379,7 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_item (
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='carton_marks'), 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN carton_marks VARCHAR(4000) NULL COMMENT ''换标箱唛映射快照'' AFTER source_stage')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='source_business_type'), 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN source_business_type VARCHAR(32) NULL COMMENT ''来源业务类型'' AFTER carton_marks')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='source_business_no'), 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN source_business_no VARCHAR(128) NULL COMMENT ''来源业务单号'' AFTER source_business_type')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN destination_type VARCHAR(32) NULL COMMENT ''目的类型:FBA或PRIVATE'' AFTER fba_warehouse_code, ADD COLUMN delivery_address VARCHAR(1000) NULL COMMENT ''私人仓目的地址快照'' AFTER destination_type, ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'' AFTER delivery_address, ADD COLUMN contact_name VARCHAR(128) NULL COMMENT ''私人仓联系人快照'' AFTER postal_code, ADD COLUMN contact_phone VARCHAR(64) NULL COMMENT ''私人仓联系电话快照'' AFTER contact_name')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock (
id BIGINT NOT NULL COMMENT '主键ID',
@@ -1521,7 +1555,11 @@ CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_order (
transfer_no VARCHAR(64) NOT NULL COMMENT '转仓单号', inbound_order_id BIGINT NOT NULL COMMENT '入仓单ID', inbound_code VARCHAR(128) NOT NULL COMMENT '入仓单号快照',
lastleg_order_id BIGINT NOT NULL COMMENT '尾程订单ID', lastleg_order_no VARCHAR(32) NOT NULL COMMENT '尾程订单号',
operating_warehouse_id VARCHAR(128) NOT NULL COMMENT '入库中转仓ID', fba_warehouse_code VARCHAR(128) NULL COMMENT '可选FBA目的仓编码',
delivery_address VARCHAR(1000) NOT NULL COMMENT '提货地址或中转仓收货地址', package_count INT NOT NULL COMMENT '箱数', delivery_method VARCHAR(64) NOT NULL COMMENT '入仓方式',
destination_type VARCHAR(16) NULL COMMENT '目的类型:FBA或PRIVATE', destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID快照',
destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照', destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
private_destination_address VARCHAR(1000) NULL COMMENT '私人仓完整地址', private_destination_postal_code VARCHAR(32) NULL COMMENT '私人仓邮编', private_destination_contact_name VARCHAR(128) NULL COMMENT '私人仓联系人',
private_destination_contact_phone VARCHAR(64) NULL COMMENT '私人仓联系电话',
delivery_address VARCHAR(1000) NOT NULL COMMENT '提货地址或中转仓收货地址', pickup_contact_name VARCHAR(128) NULL COMMENT '提货联系人', pickup_contact_phone VARCHAR(64) NULL COMMENT '提货联系电话', package_count INT NOT NULL COMMENT '箱数', delivery_method VARCHAR(64) NOT NULL COMMENT '入仓方式',
estimated_arrival_at DATETIME(3) NOT NULL COMMENT '预计入仓时间', fba_shipment_no VARCHAR(128) NULL COMMENT '可选FBA货件编号',
tracking_no VARCHAR(255) NULL COMMENT '物流跟踪号', fba_box_nos VARCHAR(2000) NULL COMMENT 'FBA箱号集合', remark VARCHAR(1000) NULL COMMENT '备注',
need_relabel TINYINT NOT NULL DEFAULT 0 COMMENT '是否需要换标', relabel_application_id BIGINT NULL COMMENT '换标申请单ID', relabel_no VARCHAR(64) NULL COMMENT '换标申请单号',
@@ -1559,6 +1597,10 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
PREPARE transfer_outbound_at_stmt FROM @ddl; EXECUTE transfer_outbound_at_stmt; DEALLOCATE PREPARE transfer_outbound_at_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='estimated_ship_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN estimated_ship_at DATETIME(3) NULL COMMENT ''预计发货时间'' AFTER estimated_arrival_at'));
PREPARE transfer_estimated_ship_stmt FROM @ddl; EXECUTE transfer_estimated_ship_stmt; DEALLOCATE PREPARE transfer_estimated_ship_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='pickup_contact_name'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN pickup_contact_name VARCHAR(128) NULL COMMENT ''提货联系人'' AFTER delivery_address'));
PREPARE transfer_pickup_contact_name_stmt FROM @ddl; EXECUTE transfer_pickup_contact_name_stmt; DEALLOCATE PREPARE transfer_pickup_contact_name_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='pickup_contact_phone'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN pickup_contact_phone VARCHAR(64) NULL COMMENT ''提货联系电话'' AFTER pickup_contact_name'));
PREPARE transfer_pickup_contact_phone_stmt FROM @ddl; EXECUTE transfer_pickup_contact_phone_stmt; DEALLOCATE PREPARE transfer_pickup_contact_phone_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='label_deadline'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN label_deadline DATETIME(3) NULL COMMENT ''换标截止时间'', ADD COLUMN relabel_contact_name VARCHAR(128) NULL COMMENT ''换标联系人'', ADD COLUMN relabel_contact_phone VARCHAR(64) NULL COMMENT ''换标联系电话'', ADD COLUMN relabel_charge_amount DECIMAL(20,2) NULL COMMENT ''换标费用CNY'', ADD COLUMN relabel_delivery_method VARCHAR(32) NULL COMMENT ''换标后派送方式'''));
PREPARE transfer_relabel_config_stmt FROM @ddl; EXECUTE transfer_relabel_config_stmt; DEALLOCATE PREPARE transfer_relabel_config_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='relabel_completed_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN relabel_completed_at DATETIME(3) NULL COMMENT ''关联贴标完成时间'' AFTER relabel_no'));
@@ -1567,6 +1609,20 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab
PREPARE transfer_fulfillment_stmt FROM @ddl; EXECUTE transfer_fulfillment_stmt; DEALLOCATE PREPARE transfer_fulfillment_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='review_decision'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN review_decision VARCHAR(32) NULL COMMENT ''审核决定'' AFTER status, ADD COLUMN review_remark VARCHAR(1000) NULL COMMENT ''审核备注或驳回原因'' AFTER review_decision, ADD COLUMN reviewed_by VARCHAR(128) NULL COMMENT ''审核人ID'' AFTER review_remark, ADD COLUMN reviewed_at DATETIME(3) NULL COMMENT ''审核时间'' AFTER reviewed_by'));
PREPARE transfer_review_fact_stmt FROM @ddl; EXECUTE transfer_review_fact_stmt; DEALLOCATE PREPARE transfer_review_fact_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN destination_type VARCHAR(16) NULL COMMENT ''目的类型:FBA或PRIVATE'' AFTER fba_warehouse_code, ADD COLUMN destination_warehouse_id VARCHAR(128) NULL COMMENT ''目的仓ID快照'' AFTER destination_type, ADD COLUMN destination_warehouse_code VARCHAR(128) NULL COMMENT ''目的仓编码快照'' AFTER destination_warehouse_id, ADD COLUMN destination_warehouse_name VARCHAR(255) NULL COMMENT ''目的仓名称快照'' AFTER destination_warehouse_code, ADD COLUMN private_destination_address VARCHAR(1000) NULL COMMENT ''私人仓完整地址'' AFTER destination_warehouse_name, ADD COLUMN private_destination_postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编'' AFTER private_destination_address, ADD COLUMN private_destination_contact_name VARCHAR(128) NULL COMMENT ''私人仓联系人'' AFTER private_destination_postal_code, ADD COLUMN private_destination_contact_phone VARCHAR(64) NULL COMMENT ''私人仓联系电话'' AFTER private_destination_contact_name, ADD COLUMN destination_binding_idempotency_key VARCHAR(255) NULL COMMENT ''目的地绑定幂等键'', ADD COLUMN destination_binding_request_hash CHAR(64) NULL COMMENT ''目的地绑定请求哈希'''));
PREPARE transfer_destination_stmt FROM @ddl; EXECUTE transfer_destination_stmt; DEALLOCATE PREPARE transfer_destination_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan_line' AND column_name='delivery_address'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan_line ADD COLUMN delivery_address VARCHAR(1000) NULL COMMENT ''私人仓目的地址快照'', ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'', ADD COLUMN contact_name VARCHAR(128) NULL COMMENT ''私人仓联系人快照'', ADD COLUMN contact_phone VARCHAR(64) NULL COMMENT ''私人仓联系电话快照'''));
PREPARE transfer_line_destination_stmt FROM @ddl; EXECUTE transfer_line_destination_stmt; DEALLOCATE PREPARE transfer_line_destination_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan_pallet' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan_pallet ADD COLUMN destination_type VARCHAR(32) NULL COMMENT ''目的类型:FBA或PRIVATE'', ADD COLUMN delivery_address VARCHAR(1000) NULL COMMENT ''私人仓目的地址快照'', ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'', ADD COLUMN contact_name VARCHAR(128) NULL COMMENT ''私人仓联系人快照'', ADD COLUMN contact_phone VARCHAR(64) NULL COMMENT ''私人仓联系电话快照'''));
PREPARE transfer_pallet_destination_stmt FROM @ddl; EXECUTE transfer_pallet_destination_stmt; DEALLOCATE PREPARE transfer_pallet_destination_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_order' AND column_name='private_destination_postal_code'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_order ADD COLUMN private_destination_postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编'' AFTER private_destination_address'));
PREPARE transfer_postal_stmt FROM @ddl; EXECUTE transfer_postal_stmt; DEALLOCATE PREPARE transfer_postal_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan_line' AND column_name='postal_code'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan_line ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'' AFTER delivery_address'));
PREPARE transfer_line_postal_stmt FROM @ddl; EXECUTE transfer_line_postal_stmt; DEALLOCATE PREPARE transfer_line_postal_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan_pallet' AND column_name='postal_code'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan_pallet ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'' AFTER delivery_address'));
PREPARE transfer_pallet_postal_stmt FROM @ddl; EXECUTE transfer_pallet_postal_stmt; DEALLOCATE PREPARE transfer_pallet_postal_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='postal_code'), 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN postal_code VARCHAR(32) NULL COMMENT ''私人仓邮编快照'' AFTER delivery_address'));
PREPARE dispatch_item_postal_stmt FROM @ddl; EXECUTE dispatch_item_postal_stmt; DEALLOCATE PREPARE dispatch_item_postal_stmt;
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_source_order (
id BIGINT NOT NULL COMMENT '主键ID', transfer_order_id BIGINT NOT NULL COMMENT '转仓单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
@@ -1603,18 +1659,22 @@ PREPARE transfer_customer_carton_stmt FROM @ddl; EXECUTE transfer_customer_carto
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_pallet (
id BIGINT NOT NULL COMMENT '打托事实ID', transfer_order_id BIGINT NOT NULL COMMENT '转运单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', sequence_no INT NOT NULL COMMENT '托序号',
warehouse_pallet_id BIGINT NOT NULL COMMENT '海外仓真实托盘ID', pallet_no VARCHAR(128) NOT NULL COMMENT '真实托盘码',
warehouse_location_id BIGINT NOT NULL COMMENT '真实库位ID', warehouse_location_code VARCHAR(128) NULL COMMENT '真实库位码',
warehouse_resource_lock_id BIGINT NOT NULL COMMENT '仓储资源锁ID', carton_count INT NOT NULL COMMENT '本托实收箱数',
warehouse_pallet_id BIGINT NULL COMMENT '生成后的海外仓真实托盘ID', pallet_no VARCHAR(128) NULL COMMENT '生成后的真实托盘码',
warehouse_location_id BIGINT NULL COMMENT '生成后的真实库位ID', warehouse_location_code VARCHAR(128) NULL COMMENT '真实库位码',
warehouse_resource_lock_id BIGINT NULL COMMENT '生成后的仓储资源锁ID', carton_count INT NOT NULL COMMENT '本托实收箱数',
length_cm DECIMAL(12,2) NOT NULL COMMENT '实测长cm', width_cm DECIMAL(12,2) NOT NULL COMMENT '实测宽cm',
height_cm DECIMAL(12,2) NOT NULL COMMENT '实测高cm', gross_weight_kg DECIMAL(12,3) NOT NULL COMMENT '实测毛重kg',
volume_cbm DECIMAL(18,6) NOT NULL COMMENT '实测体积CBM', status VARCHAR(32) NOT NULL COMMENT '打托状态',
completed_at DATETIME(3) NOT NULL COMMENT 'PDA打托完成时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
volume_cbm DECIMAL(18,6) NOT NULL COMMENT '实测体积CBM', status VARCHAR(32) NOT NULL COMMENT '逐托状态DRAFT/GENERATED/BOUND/COMPLETED',
draft_idempotency_key VARCHAR(255) NULL COMMENT '草稿保存幂等键', draft_request_hash CHAR(64) NULL COMMENT '草稿请求摘要',
generate_idempotency_key VARCHAR(255) NULL COMMENT '新托盘生成幂等键', bind_idempotency_key VARCHAR(255) NULL COMMENT '扫码绑定幂等键',
bound_at DATETIME(3) NULL COMMENT '扫码确认绑定时间', completed_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_transfer_pallet_seq(transfer_order_id,sequence_no),
UNIQUE KEY uk_ll_transfer_pallet_physical(customer_id,warehouse_pallet_id),
KEY idx_ll_transfer_pallet_order(customer_id,transfer_order_id,status)
) ENGINE=InnoDB COMMENT='仓储转运PDA实测打托事实';
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_pallet' AND column_name='draft_idempotency_key'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_pallet MODIFY COLUMN warehouse_pallet_id BIGINT NULL COMMENT ''生成后的海外仓真实托盘ID'', MODIFY COLUMN pallet_no VARCHAR(128) NULL COMMENT ''生成后的真实托盘码'', MODIFY COLUMN warehouse_location_id BIGINT NULL COMMENT ''生成后的真实库位ID'', MODIFY COLUMN warehouse_resource_lock_id BIGINT NULL COMMENT ''生成后的仓储资源锁ID'', MODIFY COLUMN completed_at DATETIME(3) NULL COMMENT ''整单打托完成时间'', ADD COLUMN draft_idempotency_key VARCHAR(255) NULL COMMENT ''草稿保存幂等键'' AFTER status, ADD COLUMN draft_request_hash CHAR(64) NULL COMMENT ''草稿请求摘要'' AFTER draft_idempotency_key, ADD COLUMN generate_idempotency_key VARCHAR(255) NULL COMMENT ''新托盘生成幂等键'' AFTER draft_request_hash, ADD COLUMN bind_idempotency_key VARCHAR(255) NULL COMMENT ''扫码绑定幂等键'' AFTER generate_idempotency_key, ADD COLUMN bound_at DATETIME(3) NULL COMMENT ''扫码确认绑定时间'' AFTER bind_idempotency_key'));
PREPARE transfer_pallet_lifecycle_stmt FROM @ddl; EXECUTE transfer_pallet_lifecycle_stmt; DEALLOCATE PREPARE transfer_pallet_lifecycle_stmt;
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_pallet_item (
id BIGINT NOT NULL COMMENT '主键ID', transfer_order_id BIGINT NOT NULL COMMENT '转运单ID',
@@ -1625,6 +1685,18 @@ CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_pallet_item (
KEY idx_ll_transfer_pallet_item_order(customer_id,transfer_order_id)
) ENGINE=InnoDB COMMENT='仓储转运每托SKU箱数回填';
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_measurement_photo (
id BIGINT NOT NULL COMMENT '测量照片引用ID', transfer_order_id BIGINT NOT NULL COMMENT '仓储转运单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', file_id BIGINT NOT NULL COMMENT '文件服务文件ID',
file_name VARCHAR(255) NULL COMMENT '文件名快照', content_type VARCHAR(128) NULL COMMENT '文件媒体类型快照',
size_bytes BIGINT NOT NULL DEFAULT 0 COMMENT '文件字节数快照', uploaded_by VARCHAR(128) NULL COMMENT 'PDA上传操作人',
uploaded_at DATETIME(3) NOT NULL COMMENT '上传时间', submitted_at DATETIME(3) NULL COMMENT '随测量打托提交时间', PRIMARY KEY(id),
UNIQUE KEY uk_ll_transfer_measurement_photo_file(customer_id,transfer_order_id,file_id),
KEY idx_ll_transfer_measurement_photo_order(customer_id,transfer_order_id,uploaded_at)
) ENGINE=InnoDB COMMENT='仓储转运PDA测量现场照片引用';
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_warehouse_transfer_measurement_photo' AND column_name='submitted_at'), 'SELECT 1', 'ALTER TABLE ll_warehouse_transfer_measurement_photo ADD COLUMN submitted_at DATETIME(3) NULL COMMENT ''随测量打托提交时间'' AFTER uploaded_at'));
PREPARE transfer_measurement_photo_submitted_stmt FROM @ddl; EXECUTE transfer_measurement_photo_submitted_stmt; DEALLOCATE PREPARE transfer_measurement_photo_submitted_stmt;
CREATE TABLE IF NOT EXISTS ll_warehouse_transfer_relabel_binding (
id BIGINT NOT NULL COMMENT '主键ID', transfer_order_id BIGINT NOT NULL COMMENT '转运单ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', operation_type VARCHAR(32) NOT NULL COMMENT '换标类型',
@@ -959,6 +959,75 @@ paths:
security: [{ bearerAuth: [] }]
parameters: [{ name: deliveryNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 当前客户派送单详情 }, '403': { description: 缺少客户派送查看权限 }, '404': { description: 当前客户下派送单不存在 } }
/api/merchant/v1/last-leg/orders:
get:
summary: 客户分页查询自己的海外尾程订单
description: 权限 merchant:overseas-warehouse:last-mile-order:list;列表权限包含详情只读,客户范围只取认证 Token。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 当前客户海外尾程订单分页 }, '403': { description: 缺少海外尾程订单查看权限 } }
/api/merchant/v1/last-leg/orders/{id}:
get:
summary: 客户查询自己的海外尾程订单详情
description: 权限 merchant:overseas-warehouse:last-mile-order:list;不泄露其他客户订单。
security: [{ bearerAuth: [] }]
parameters: [{ name: id, in: path, required: true, schema: { type: integer, format: int64 } }]
responses: { '200': { description: 当前客户海外尾程订单详情 }, '403': { description: 缺少海外尾程订单查看权限 }, '404': { description: 当前客户下订单不存在 } }
/api/merchant/v1/last-leg/warehouse-transfers:
get:
summary: 客户分页查询自己的仓储转运单
description: 权限 merchant:overseas-warehouse:transfer:list;列表权限包含详情只读。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 当前客户仓储转运单分页 }, '403': { description: 缺少仓储转运查看权限 } }
post:
summary: 客户创建仓储转运单
description: 权限 merchant:overseas-warehouse:transfer:create;客户范围只取认证 Token。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 创建后的仓储转运单 }, '403': { description: 缺少仓储转运创建权限 }, '409': { description: 幂等、库存或状态冲突 } }
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}:
get:
summary: 客户查询自己的仓储转运单详情
description: 权限 merchant:overseas-warehouse:transfer:list;不泄露其他客户订单。
security: [{ bearerAuth: [] }]
parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 当前客户仓储转运单详情 }, '403': { description: 缺少仓储转运查看权限 }, '404': { description: 当前客户下转运单不存在 } }
/api/merchant/v1/last-leg/warehouse-transfers/{transferNo}/confirm-measurement:
post:
summary: 客户确认仓储转运实测数据
description: 权限 merchant:overseas-warehouse:transfer:confirm;仅当前客户满足状态门禁的转运单可确认。
security: [{ bearerAuth: [] }]
parameters: [{ name: transferNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 确认后的仓储转运单 }, '403': { description: 缺少实测确认权限 }, '409': { description: 状态或版本冲突 } }
/api/merchant/v1/last-leg/cargo-discards:
get:
summary: 客户分页查询自己的货物废弃申请
description: 权限 merchant:overseas-warehouse:discard:list;列表权限包含详情和库存候选只读。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 当前客户货物废弃申请分页 }, '403': { description: 缺少货物废弃查看权限 } }
post:
summary: 客户创建货物废弃申请
description: 权限 merchant:overseas-warehouse:discard:create;客户范围只取认证 Token。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 创建后的货物废弃申请 }, '403': { description: 缺少货物废弃创建权限 }, '409': { description: 库存或状态冲突 } }
/api/merchant/v1/last-leg/cargo-discards/inventory-candidates:
get:
summary: 查询当前客户可废弃库存候选
description: 权限 merchant:overseas-warehouse:discard:list;候选按认证客户隔离。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 可废弃库存候选 }, '403': { description: 缺少货物废弃查看权限 } }
/api/merchant/v1/last-leg/cargo-discards/{discardNo}:
get:
summary: 客户查询自己的货物废弃申请详情
description: 权限 merchant:overseas-warehouse:discard:list;不泄露其他客户申请。
security: [{ bearerAuth: [] }]
parameters: [{ name: discardNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 当前客户货物废弃申请详情 }, '403': { description: 缺少货物废弃查看权限 }, '404': { description: 当前客户下申请不存在 } }
/api/merchant/v1/last-leg/cargo-discards/{discardNo}/cancel:
post:
summary: 客户取消自己的货物废弃申请
description: 权限 merchant:overseas-warehouse:discard:cancel;仅满足状态门禁的申请可取消。
security: [{ bearerAuth: [] }]
parameters: [{ name: discardNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 取消后的货物废弃申请 }, '403': { description: 缺少货物废弃取消权限 }, '409': { description: 状态冲突 } }
/api/merchant/v1/last-leg/overseas-procurements:
post:
summary: 客户创建海外采购订单
@@ -1310,7 +1379,7 @@ paths:
/api/merchant/v1/last-leg/relabel-applications/inventory-candidates:
get:
summary: 查询客户可换标库存候选
description: 权限 merchant:overseas-warehouse:relabel:list;仅返回当前客户在指定海外中转仓仍有可用箱数的历史入仓单和库位摘要。候选按客户 Token 隔离,不接收 customerId;inboundCode 可选精确筛选。
description: 权限 merchant:overseas-warehouse:relabel:list;返回当前客户在指定海外中转仓仍有可用箱数的历史入仓库存,以及仓库合箱后生成的可用退货箱库存。候选按客户 Token 隔离,不接收 customerId;inboundCode 可按入仓码、退货单号或仓库唛头模糊筛选。
security: [{ bearerAuth: [] }]
parameters:
- { name: operationWarehouseId, in: query, required: true, schema: { type: string, maxLength: 128 } }
@@ -1374,6 +1443,17 @@ paths:
'200': { description: 更新后的换标详情, content: { application/json: { schema: { $ref: '#/components/schemas/RelabelMerchantDetailResponse' } } } }
'400': { description: 文件类型或大小不合法 }
'409': { description: 当前状态不允许上传 }
/api/merchant/v1/last-leg/relabel-applications/{relabelNo}/files:
post:
summary: 上传客户换标总文件或明细文件
description: 权限 merchant:overseas-warehouse:relabel:files;文件只能绑定到当前客户的换标申请。
security: [{ bearerAuth: [] }]
parameters:
- { name: relabelNo, in: path, required: true, schema: { type: string, maxLength: 64 } }
- { name: scope, in: query, required: true, schema: { type: string, enum: [CARTON_TOTAL, SKU_TOTAL, ITEM] } }
- { name: itemId, in: query, required: false, schema: { type: integer, format: int64 } }
requestBody: { required: true, content: { multipart/form-data: { schema: { type: object, required: [file], properties: { file: { type: string, format: binary } } } } } }
responses: { '200': { description: 更新后的换标详情 }, '403': { description: 缺少换标文件维护权限 }, '409': { description: 状态或归属冲突 } }
/api/merchant/v1/last-leg/relabel-applications/{relabelNo}/shipping-label/{fileId}/download:
get:
summary: 下载换标快递面单
@@ -1432,6 +1512,20 @@ paths:
security: [{ bearerAuth: [] }]
parameters: [{ name: returnNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 当前客户退货订单详情 }, '404': { description: 当前客户下退货订单不存在 } }
/api/merchant/v1/last-leg/return-orders/{returnNo}/relabel-candidates:
get:
summary: 查询退货单可转换标候选
description: 权限 merchant:overseas-warehouse:return-order:relabel;只返回当前客户退货单的可转换标明细。
security: [{ bearerAuth: [] }]
parameters: [{ name: returnNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 可转换标候选 }, '403': { description: 缺少退货转换标权限 }, '404': { description: 当前客户下退货单不存在 } }
/api/merchant/v1/last-leg/return-orders/{returnNo}/relabel:
post:
summary: 将退货库存转换为换标申请
description: 权限 merchant:overseas-warehouse:return-order:relabel;只能处理当前客户且满足状态门禁的退货库存。
security: [{ bearerAuth: [] }]
parameters: [{ name: returnNo, in: path, required: true, schema: { type: string } }]
responses: { '200': { description: 创建后的换标申请 }, '403': { description: 缺少退货转换标权限 }, '409': { description: 状态、库存或幂等冲突 } }
/api/pda/v1/last-leg/return-receipts/start:
post:
summary: PDA 以退件单号开始签收
@@ -2531,6 +2625,19 @@ components:
availablePackages: { type: integer, format: int64 }
version: { type: integer, format: int64 }
positions: { type: array, items: { $ref: '#/components/schemas/RelabelInventoryPosition' } }
sourceType: { type: string, enum: [INBOUND, RETURN_CARTON] }
returnCartonId: { type: string, nullable: true, description: 退货合箱库存主键;sourceType=RETURN_CARTON 时提供 }
cartonMark: { type: string, nullable: true, description: 仓库合箱后打印并贴箱的仓库唛头 }
skuItems:
type: array
description: 退货箱内 SKU 与件数
items:
type: object
properties:
skuCode: { type: string }
newSkuCode: { type: string, nullable: true }
quantity: { type: integer, minimum: 1 }
remark: { type: string, nullable: true }
RelabelInventoryPosition:
type: object
properties:
@@ -2559,10 +2666,10 @@ components:
remark: { type: string, nullable: true, maxLength: 1000 }
RelabelCreate:
type: object
required: [customerOrderNo, labelDeadline, contactName, contactPhone, idempotencyKey, items]
required: [customerOrderNo, operationWarehouseId, labelDeadline, contactName, contactPhone, idempotencyKey, items]
properties:
customerOrderNo: { type: string, maxLength: 128 }
overseasWarehouseId: { type: string, nullable: true, maxLength: 128, description: 统一目的海外仓;明细未填写目的仓时使用 }
operationWarehouseId: { type: string, maxLength: 128, description: 当前库存所在海外操作仓 }
labelDeadline: { type: string, format: date-time }
contactName: { type: string, maxLength: 128 }
contactPhone: { type: string, maxLength: 64 }
@@ -2576,6 +2683,10 @@ components:
type: object
required: [productNameZh, totalPackageCount, notifiedPackageCount, shipmentNo, newSkuCode, cartonMark, fbaCartonMark]
properties:
sourceInventoryBalanceId: { type: string, nullable: true, description: 普通入仓库存来源;与 returnCartonId 二选一 }
sourceInventoryPositionId: { type: string, nullable: true, description: 普通入仓库存仓位;与 returnCartonId 二选一 }
sourceInboundOrderId: { type: string, nullable: true, description: 普通入仓单;与 returnCartonId 二选一 }
returnCartonId: { type: string, nullable: true, description: 仓库合箱产生的退货箱库存;与普通入仓库存字段二选一 }
skuCode: { type: string, nullable: true, maxLength: 128, description: 商品 SKU(选填) }
productImageFileId: { type: string, nullable: true, maxLength: 128 }
productImageFileName: { type: string, nullable: true, maxLength: 255 }
@@ -163,12 +163,32 @@ paths:
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绑定仓 } }
responses: { '200': { description: 新托盘及可打印标签,已认领处理中无需客户确认即可创建 }, '409': { description: 状态、认领人或Token绑定仓不符 } }
/api/pda/v1/last-leg/pallet-operations/assemble:
post:
summary: 将多个来源托盘的指定库存行组到目标托盘
@@ -182,7 +202,7 @@ paths:
/api/pda/v1/last-leg/pallet-operations/relabel-disassemble:
post:
summary: 按换标箱唛拆托并绑定目标托盘
description: PDA先扫描原托盘,再扫描换标单指定箱唛;服务端按换标单库存快照解析仓位,在同一事务中扣减原托盘并绑定目标托盘。目标托盘须先由仓储服务创建后扫码确认。
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:
@@ -218,6 +238,27 @@ paths:
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: 登记一件有原因的跳过事实
@@ -260,6 +301,107 @@ paths:
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 }
@@ -274,6 +416,107 @@ components:
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' }
@@ -732,7 +975,19 @@ components:
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]