This commit is contained in:
2026-09-01 11:09:14 +08:00
191 changed files with 6486 additions and 403 deletions
@@ -53,6 +53,9 @@ baiyue:
relabel-photo-file:
connect-timeout: ${LASTLEG_RELABEL_PHOTO_FILE_CONNECT_TIMEOUT:3s}
read-timeout: ${LASTLEG_RELABEL_PHOTO_FILE_READ_TIMEOUT:30s}
customer-visit-file:
connect-timeout: ${LASTLEG_CUSTOMER_VISIT_FILE_CONNECT_TIMEOUT:3s}
read-timeout: ${LASTLEG_CUSTOMER_VISIT_FILE_READ_TIMEOUT:30s}
warehouse-resource:
connect-timeout: ${LASTLEG_WAREHOUSE_RESOURCE_CONNECT_TIMEOUT:3s}
read-timeout: ${LASTLEG_WAREHOUSE_RESOURCE_READ_TIMEOUT:5s}
+148 -17
View File
@@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS ll_order (
relabel_order_no VARCHAR(128) COMMENT '换标单号',
relabel_status VARCHAR(32) COMMENT '换标状态',
pallet_merge_flag VARCHAR(32) COMMENT '合托标记',
pallet_merge_batch_id VARCHAR(64) NULL COMMENT '平台预标记合托分组号',
current_status VARCHAR(32) NOT NULL DEFAULT 'PENDING_ASSIGNMENT' COMMENT '当前业务状态:PENDING_ASSIGNMENT待分配、PENDING_INBOUND待入库、INBOUND_COMPLETED入库完成、PENDING_DELIVERY待派送、DELIVERING派送中、DELIVERED派送完成',
sub_order_status VARCHAR(255) NULL COMMENT '子单据状态汇总',
private_remark VARCHAR(1000) COMMENT '备注(仅自己)',
@@ -104,6 +105,9 @@ CREATE TABLE IF NOT EXISTS ll_order (
KEY idx_ll_order_container_progress(source_container_booking_id,estimated_warehouse_at,created_at,id)
) ENGINE=InnoDB COMMENT='尾程订单主表';
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_order' AND column_name='pallet_merge_batch_id'), 'SELECT 1', 'ALTER TABLE ll_order ADD COLUMN pallet_merge_batch_id VARCHAR(64) NULL COMMENT ''平台预标记合托分组号'' AFTER pallet_merge_flag'));
PREPARE ll_order_merge_batch_ddl FROM @ddl; EXECUTE ll_order_merge_batch_ddl; DEALLOCATE PREPARE ll_order_merge_batch_ddl;
CREATE TABLE IF NOT EXISTS ll_order_detail (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单明细主键',
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
@@ -144,6 +148,56 @@ CREATE TABLE IF NOT EXISTS ll_loading_order_status_history (
KEY idx_ll_loading_history_customer_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='尾程订单状态变更历史';
CREATE TABLE IF NOT EXISTS ll_order_fee (
id BIGINT NOT NULL COMMENT '费用明细主键',
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
order_id BIGINT NOT NULL COMMENT '尾程订单ID',
fee_item VARCHAR(128) NOT NULL COMMENT '费用项目',
currency VARCHAR(3) NOT NULL COMMENT '币种代码',
amount DECIMAL(18,2) NOT NULL COMMENT '费用金额',
status VARCHAR(32) NOT NULL DEFAULT 'PENDING_REVIEW' COMMENT '费用状态:PENDING_REVIEW待审核、APPROVED审核通过、REJECTED已驳回',
supplier_description VARCHAR(500) NULL COMMENT '供应商或费用说明',
created_by VARCHAR(128) NOT 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),
KEY idx_ll_order_fee_customer_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='尾程订单费用明细';
CREATE TABLE IF NOT EXISTS ll_order_operation_log (
id BIGINT NOT NULL COMMENT '操作记录主键',
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
order_id BIGINT NOT NULL COMMENT '尾程订单ID',
operation_node VARCHAR(64) NOT NULL COMMENT '操作节点',
operator_name VARCHAR(128) NOT NULL COMMENT '操作人名称',
result VARCHAR(500) NOT NULL COMMENT '处理结果',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
PRIMARY KEY(id),
KEY idx_ll_order_operation_customer_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='尾程订单操作记录';
-- 派送单列表与详情快照、转派送费用及操作记录。
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_delivery_order' AND column_name='overseas_warehouse_name'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN overseas_warehouse_name VARCHAR(255) NULL COMMENT ''海外仓名称快照'' AFTER overseas_warehouse_id'));
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='customer_name'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN customer_name VARCHAR(255) NULL COMMENT ''客户名称快照'' AFTER overseas_warehouse_name'));
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='waybill_no'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN waybill_no VARCHAR(128) NULL COMMENT ''承运运单号'', ADD COLUMN delivery_method VARCHAR(32) NULL COMMENT ''派送方式'', ADD COLUMN carrier_name VARCHAR(255) NULL COMMENT ''派送承运商'', ADD COLUMN pickup_method VARCHAR(64) NULL COMMENT ''提货方式'', ADD COLUMN loading_type VARCHAR(64) NULL COMMENT ''装车类型'', ADD COLUMN vehicle_type VARCHAR(64) NULL COMMENT ''车辆类型'', ADD COLUMN vehicle_size VARCHAR(64) NULL COMMENT ''车辆大小'', ADD COLUMN vehicle_plate VARCHAR(64) NULL COMMENT ''车牌号'', ADD COLUMN driver_name VARCHAR(128) NULL COMMENT ''司机或车队名称'', ADD COLUMN driver_phone VARCHAR(64) NULL COMMENT ''车队联系电话'', ADD COLUMN appointment_account VARCHAR(128) NULL COMMENT ''预约账号'', ADD COLUMN seller_name VARCHAR(128) NULL COMMENT ''卖家姓名'' AFTER status'));
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='total_weight_kg'), 'SELECT 1', 'ALTER TABLE ll_delivery_order ADD COLUMN total_weight_kg DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''总重量(千克)'', ADD COLUMN total_volume_cbm DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''总体积(立方米)'', ADD COLUMN planned_delivery_at DATETIME(3) NULL COMMENT ''预计派送时间'', ADD COLUMN dispatched_at DATETIME(3) NULL COMMENT ''实际派送时间'', ADD COLUMN expected_arrival_at DATETIME(3) NULL COMMENT ''预计到达时间'', ADD COLUMN signed_at DATETIME(3) NULL COMMENT ''签收时间'', ADD COLUMN remark VARCHAR(1000) NULL COMMENT ''派送备注'' AFTER pallet_count'));
PREPARE delivery_detail_stmt FROM @ddl; EXECUTE delivery_detail_stmt; DEALLOCATE PREPARE delivery_detail_stmt;
CREATE TABLE IF NOT EXISTS ll_delivery_fee (
id BIGINT NOT NULL COMMENT '派送费用主键', customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键', delivery_order_id BIGINT NOT NULL COMMENT '派送单ID',
fee_direction VARCHAR(16) NOT NULL DEFAULT 'PAYABLE' COMMENT '应收应付方向', fee_type VARCHAR(64) NOT NULL COMMENT '费用类型', currency VARCHAR(8) NOT NULL COMMENT '币种',
amount DECIMAL(18,2) NOT NULL COMMENT '费用金额', remark VARCHAR(500) NULL COMMENT '费用备注', created_by VARCHAR(128) NULL COMMENT '创建人ID',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY(id), KEY idx_ll_delivery_fee_order(customer_id,delivery_order_id,id)
) ENGINE=InnoDB COMMENT='派送单费用明细';
CREATE TABLE IF NOT EXISTS ll_delivery_operation (
id BIGINT NOT NULL COMMENT '派送操作主键', customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键', delivery_order_id BIGINT NOT NULL COMMENT '派送单ID',
operation_node VARCHAR(64) NOT NULL COMMENT '操作节点', operator_id VARCHAR(128) NULL COMMENT '操作人ID', operator_name VARCHAR(128) NULL COMMENT '操作人名称',
result VARCHAR(500) NOT NULL COMMENT '处理结果', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
PRIMARY KEY(id), KEY idx_ll_delivery_operation_order(customer_id,delivery_order_id,created_at,id)
) ENGINE=InnoDB COMMENT='派送单操作记录';
CREATE TABLE IF NOT EXISTS ll_event_conflict (
id BIGINT NOT NULL COMMENT '冲突记录主键', event_id VARCHAR(128) NOT NULL COMMENT '冲突事件 ID',
customer_id BIGINT NOT NULL COMMENT '租户 ID', conflict_type VARCHAR(64) NOT NULL COMMENT '冲突类型',
@@ -205,6 +259,10 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE
PREPARE lastleg_order_container_progress_idx_stmt FROM @ddl; EXECUTE lastleg_order_container_progress_idx_stmt; DEALLOCATE PREPARE lastleg_order_container_progress_idx_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_order' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_order ADD COLUMN destination_type VARCHAR(32) NULL COMMENT ''目的类型:FBA按箱数接收、OVERSEAS逐箱扫码'' AFTER order_type'));
PREPARE lastleg_order_destination_type_stmt FROM @ddl; EXECUTE lastleg_order_destination_type_stmt; DEALLOCATE PREPARE lastleg_order_destination_type_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_order' AND column_name='source_warehouse_id'), 'SELECT 1', 'ALTER TABLE ll_order ADD COLUMN source_warehouse_id VARCHAR(128) NULL COMMENT ''来源国内装柜仓 ID 快照'' AFTER customer_name'));
PREPARE lastleg_order_source_warehouse_stmt FROM @ddl; EXECUTE lastleg_order_source_warehouse_stmt; DEALLOCATE PREPARE lastleg_order_source_warehouse_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_order' AND column_name='source_warehouse_name'), 'SELECT 1', 'ALTER TABLE ll_order ADD COLUMN source_warehouse_name VARCHAR(255) NULL COMMENT ''来源国内装柜仓名称快照'' AFTER source_warehouse_id'));
PREPARE lastleg_order_source_warehouse_stmt FROM @ddl; EXECUTE lastleg_order_source_warehouse_stmt; DEALLOCATE PREPARE lastleg_order_source_warehouse_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_inbound_order' AND column_name='destination_type'), 'SELECT 1', 'ALTER TABLE ll_inbound_order ADD COLUMN destination_type VARCHAR(32) NULL COMMENT ''目的类型:FBA按箱数接收、OVERSEAS逐箱扫码'' AFTER destination_warehouse_name'));
PREPARE lastleg_inbound_destination_type_stmt FROM @ddl; EXECUTE lastleg_inbound_destination_type_stmt; DEALLOCATE PREPARE lastleg_inbound_destination_type_stmt;
ALTER TABLE ll_order_detail COMMENT='尾程订单实装商品冻结明细';
@@ -389,6 +447,7 @@ CREATE TABLE IF NOT EXISTS ll_inbound_plan (
total_volume_cbm DECIMAL(18,4) 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 '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '入库计划乐观锁版本',
PRIMARY KEY (id),
UNIQUE KEY uk_ll_inbound_plan_no (plan_no),
UNIQUE KEY uk_ll_inbound_plan_source_order (customer_id, source_order_id),
@@ -485,7 +544,6 @@ ALTER TABLE ll_inbound_plan_line
MODIFY COLUMN plan_id BIGINT NOT NULL COMMENT '入库计划ID',
MODIFY COLUMN order_id BIGINT NOT NULL COMMENT '尾程订单ID',
MODIFY COLUMN inbound_code VARCHAR(128) NOT NULL COMMENT '头程入仓码快照',
MODIFY COLUMN destination_type VARCHAR(32) NOT NULL DEFAULT 'OVERSEAS' COMMENT '目的类型:FBA按箱数接收、OVERSEAS逐箱扫码',
MODIFY COLUMN destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID快照',
MODIFY COLUMN destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照',
MODIFY COLUMN destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
@@ -687,19 +745,17 @@ UPDATE ll_inbound_plan_pallet_allocation a JOIN ll_inbound_plan p ON p.id=a.plan
ALTER TABLE ll_inbound_plan_pallet_allocation MODIFY COLUMN customer_id VARCHAR(128) NOT NULL COMMENT '客户账号标识';
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name='ll_inbound_plan_pallet_allocation' AND index_name='idx_ll_inbound_allocation_customer_plan'), 'SELECT 1', 'ALTER TABLE ll_inbound_plan_pallet_allocation ADD KEY idx_ll_inbound_allocation_customer_plan(customer_id,plan_id)'));
PREPARE inbound_customer_scope_stmt FROM @ddl; EXECUTE inbound_customer_scope_stmt; DEALLOCATE PREPARE inbound_customer_scope_stmt;
DROP PROCEDURE IF EXISTS assert_inbound_plan_customer_scope_ready;
DELIMITER $$
CREATE PROCEDURE assert_inbound_plan_customer_scope_ready()
BEGIN
IF EXISTS (SELECT 1 FROM ll_inbound_plan_line l LEFT JOIN ll_inbound_plan p ON p.id=l.plan_id WHERE l.customer_id IS NULL OR l.customer_id='' OR p.id IS NULL OR l.customer_id<>p.customer_id)
OR EXISTS (SELECT 1 FROM ll_inbound_plan_pallet v LEFT JOIN ll_inbound_plan p ON p.id=v.plan_id WHERE v.customer_id IS NULL OR v.customer_id='' OR p.id IS NULL OR v.customer_id<>p.customer_id)
OR EXISTS (SELECT 1 FROM ll_inbound_plan_pallet_allocation a LEFT JOIN ll_inbound_plan p ON p.id=a.plan_id WHERE a.customer_id IS NULL OR a.customer_id='' OR p.id IS NULL OR a.customer_id<>p.customer_id) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Inbound plan child customer scope migration is incomplete';
END IF;
END$$
DELIMITER ;
CALL assert_inbound_plan_customer_scope_ready();
DROP PROCEDURE assert_inbound_plan_customer_scope_ready;
DROP TEMPORARY TABLE IF EXISTS inbound_plan_customer_scope_assertion;
CREATE TEMPORARY TABLE inbound_plan_customer_scope_assertion (
is_valid TINYINT NOT NULL,
CONSTRAINT chk_inbound_plan_customer_scope_ready CHECK (is_valid=1)
);
INSERT INTO inbound_plan_customer_scope_assertion(is_valid)
SELECT 0
WHERE EXISTS (SELECT 1 FROM ll_inbound_plan_line l LEFT JOIN ll_inbound_plan p ON p.id=l.plan_id WHERE l.customer_id IS NULL OR l.customer_id='' OR p.id IS NULL OR l.customer_id<>p.customer_id)
OR EXISTS (SELECT 1 FROM ll_inbound_plan_pallet v LEFT JOIN ll_inbound_plan p ON p.id=v.plan_id WHERE v.customer_id IS NULL OR v.customer_id='' OR p.id IS NULL OR v.customer_id<>p.customer_id)
OR EXISTS (SELECT 1 FROM ll_inbound_plan_pallet_allocation a LEFT JOIN ll_inbound_plan p ON p.id=a.plan_id WHERE a.customer_id IS NULL OR a.customer_id='' OR p.id IS NULL OR a.customer_id<>p.customer_id);
DROP TEMPORARY TABLE inbound_plan_customer_scope_assertion;
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='destination_type'), 'ALTER TABLE ll_inbound_plan_line MODIFY COLUMN destination_type VARCHAR(32) NOT NULL DEFAULT ''OVERSEAS'' COMMENT ''目的类型:FBA按箱数接收、OVERSEAS逐箱扫码''', 'ALTER TABLE ll_inbound_plan_line ADD COLUMN destination_type VARCHAR(32) NOT NULL DEFAULT ''OVERSEAS'' COMMENT ''目的类型:FBA按箱数接收、OVERSEAS逐箱扫码'' AFTER inbound_code'));
PREPARE receiving_container_line_stmt FROM @ddl; EXECUTE receiving_container_line_stmt; DEALLOCATE PREPARE receiving_container_line_stmt;
UPDATE ll_inbound_plan_line l JOIN ll_order o ON o.id=l.order_id
@@ -1115,11 +1171,13 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_pda_pick_order (
-- Transfer a dispatch to delivery: one outbound header, many pallet rows, one delivery header.
CREATE TABLE IF NOT EXISTS ll_outbound_order (
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) 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 '派车单号',
outbound_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', status VARCHAR(32) NOT NULL DEFAULT 'WAIT_OUTBOUND' COMMENT '业务状态',
package_count INT NOT NULL DEFAULT 0 COMMENT '箱数', pallet_count INT NOT NULL DEFAULT 0 COMMENT '托盘数',
idempotency_key VARCHAR(255) NOT NULL COMMENT '业务幂等键', created_by VARCHAR(128) NULL COMMENT '创建人ID',
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 '更新时间',
outbound_type VARCHAR(32) NOT NULL DEFAULT 'DISPATCH_DELIVERY' COMMENT '出库类型:派送转出库、一件代发',
recipient_info VARCHAR(1000) NULL COMMENT '一件代发收件信息',
PRIMARY KEY (id), UNIQUE KEY uk_ll_outbound_no (customer_id,outbound_no),
UNIQUE KEY uk_ll_outbound_idempotency (customer_id,idempotency_key), UNIQUE KEY uk_ll_outbound_dispatch (customer_id,dispatch_order_id),
KEY idx_ll_outbound_list (customer_id,status,created_at)
@@ -1129,12 +1187,39 @@ CREATE TABLE IF NOT EXISTS ll_outbound_pallet_item (
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', outbound_order_id BIGINT NOT NULL COMMENT '出库单ID',
warehouse_pallet_id BIGINT NULL COMMENT '仓库实物托盘ID', pallet_no VARCHAR(128) NULL COMMENT '托盘号',
destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID', destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照', destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照',
package_count INT NOT NULL DEFAULT 0 COMMENT '箱数', order_refs VARCHAR(2000) NULL COMMENT '关联订单信息', inbound_code VARCHAR(128) NULL COMMENT '入仓单号快照',
package_count INT NOT NULL DEFAULT 0 COMMENT '箱数', picked_package_count INT NOT NULL DEFAULT 0 COMMENT '已拣箱数', status VARCHAR(32) NOT NULL DEFAULT 'WAIT_PICK' COMMENT '拣货状态', sku_code VARCHAR(128) NULL COMMENT '一件代发SKU', order_refs VARCHAR(2000) NULL COMMENT '关联订单信息', inbound_code VARCHAR(128) NULL COMMENT '入仓单号快照',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY (id),
UNIQUE KEY uk_ll_outbound_pallet (customer_id,outbound_order_id,warehouse_pallet_id),
KEY idx_ll_outbound_pallet_list (customer_id,outbound_order_id), KEY idx_ll_outbound_pallet_destination (customer_id,destination_warehouse_code)
) ENGINE=InnoDB COMMENT='出库托盘明细';
CREATE TABLE IF NOT EXISTS ll_outbound_pda_task (
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', outbound_order_id BIGINT NOT NULL COMMENT '出库单ID',
task_no VARCHAR(64) NOT NULL COMMENT 'PDA任务号', status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '任务状态:待拣货、拣货中、已完成',
pushed_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) 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_outbound_pda_task_order(customer_id,outbound_order_id), UNIQUE KEY uk_ll_outbound_pda_task_no(customer_id,task_no)
) ENGINE=InnoDB COMMENT='一件代发出库PDA拣货任务';
CREATE TABLE IF NOT EXISTS ll_outbound_pda_pick (
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键', task_id BIGINT NOT NULL COMMENT 'PDA任务ID', outbound_pallet_item_id BIGINT NOT NULL COMMENT '出库托盘明细ID',
inventory_position_id BIGINT NOT NULL COMMENT '库存仓位ID', packages BIGINT NOT NULL COMMENT '拣货箱数', idempotency_key VARCHAR(255) NOT NULL COMMENT '拣货幂等键',
picked_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '拣货时间', PRIMARY KEY(id), UNIQUE KEY uk_ll_outbound_pda_pick_key(customer_id,idempotency_key)
) ENGINE=InnoDB COMMENT='一件代发出库PDA托盘拣货记录';
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='outbound_type'), 'SELECT 1', 'ALTER TABLE ll_outbound_order ADD COLUMN outbound_type VARCHAR(32) NOT NULL DEFAULT ''DISPATCH_DELIVERY'' COMMENT ''出库类型'' AFTER status'));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='sku_code'), 'SELECT 1', 'ALTER TABLE ll_outbound_pallet_item ADD COLUMN sku_code VARCHAR(128) NULL COMMENT ''一件代发SKU'''));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='recipient_info'), 'SELECT 1', 'ALTER TABLE ll_outbound_order ADD COLUMN recipient_info VARCHAR(1000) NULL COMMENT ''一件代发收件信息'' AFTER outbound_type'));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='picked_package_count'), 'SELECT 1', 'ALTER TABLE ll_outbound_pallet_item ADD COLUMN picked_package_count INT NOT NULL DEFAULT 0 COMMENT ''已拣箱数'' AFTER package_count'));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_pallet_item' AND column_name='status'), 'SELECT 1', 'ALTER TABLE ll_outbound_pallet_item ADD COLUMN status VARCHAR(32) NOT NULL DEFAULT ''WAIT_PICK'' COMMENT ''拣货状态'' AFTER picked_package_count'));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_outbound_order' AND column_name='dispatch_order_id' AND is_nullable='YES'), 'SELECT 1', 'ALTER TABLE ll_outbound_order MODIFY COLUMN dispatch_order_id BIGINT NULL COMMENT ''派车单ID,一件代发为空'', MODIFY COLUMN dispatch_no VARCHAR(64) NULL COMMENT ''派车单号,一件代发为空'''));
PREPARE outbound_upgrade_stmt FROM @ddl; EXECUTE outbound_upgrade_stmt; DEALLOCATE PREPARE outbound_upgrade_stmt;
CREATE TABLE IF NOT EXISTS ll_delivery_order (
id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
delivery_no VARCHAR(64) NOT NULL COMMENT '派送单号', outbound_order_id BIGINT NOT NULL COMMENT '出库单ID', outbound_no VARCHAR(64) NOT NULL COMMENT '出库单号',
@@ -1488,7 +1573,7 @@ CREATE TABLE IF NOT EXISTS ll_overseas_procurement_order (
purchase_quantity BIGINT NOT NULL COMMENT '采购数量', budget_unit_price DECIMAL(18,2) NULL COMMENT '预算单价',
currency CHAR(3) NOT NULL DEFAULT 'USD' COMMENT '币种', receiving_address VARCHAR(1000) NULL COMMENT '收货地址',
contact_name VARCHAR(128) NOT NULL COMMENT '联系人姓名', contact_phone VARCHAR(64) NOT NULL COMMENT '联系人电话',
purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称',
purchase_warehouse_id VARCHAR(128) NULL COMMENT '采购入仓仓库ID', purchase_warehouse_name VARCHAR(255) NULL COMMENT '采购入仓仓库名称', outbound_details JSON NULL COMMENT '出库明细',
inbound_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '采购单据累计入库数量',
domestic_carrier VARCHAR(128) NULL COMMENT '境内承运商', domestic_tracking_no VARCHAR(128) NULL COMMENT '境内物流单号',
overseas_carrier VARCHAR(128) NULL COMMENT '海外承运商', overseas_tracking_no VARCHAR(128) NULL COMMENT '海外物流单号',
@@ -1637,6 +1722,52 @@ CREATE TABLE IF NOT EXISTS ll_local_looseboard_operation (
KEY idx_ll_local_looseboard_operation_order(customer_id,order_id,created_at)
) ENGINE=InnoDB COMMENT='本土散板订单幂等操作账';
CREATE TABLE IF NOT EXISTS ll_exception_order (
id BIGINT NOT NULL COMMENT '异常订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键', exception_no VARCHAR(64) NOT NULL COMMENT '异常订单号',
exception_type VARCHAR(32) NOT NULL COMMENT '异常类型', status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '异常处理状态', reason VARCHAR(1000) NOT NULL COMMENT '异常原因',
last_mile_order_id BIGINT NULL COMMENT '关联尾程订单ID', last_mile_order_no VARCHAR(64) NULL COMMENT '关联尾程订单号', inbound_order_id BIGINT NULL COMMENT '关联入仓单ID', inbound_code VARCHAR(128) NULL COMMENT '关联入仓码',
delivery_order_id BIGINT NULL COMMENT '关联派送单ID', delivery_no VARCHAR(64) NULL COMMENT '关联派送单号', outbound_no VARCHAR(64) NULL COMMENT '关联出库单号',
customer_name VARCHAR(255) NULL COMMENT '客户名称快照', overseas_warehouse_id VARCHAR(128) NULL COMMENT '海外仓ID快照', overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照',
sku_code VARCHAR(128) NULL COMMENT '异常货物SKU', package_count INT NULL COMMENT '异常箱数', return_direction VARCHAR(32) NULL COMMENT '退回方向', return_logistics_no VARCHAR(128) NULL COMMENT '退回物流单号',
handling_action VARCHAR(500) NULL COMMENT '异常处理措施', handler_id VARCHAR(128) NULL COMMENT '处理人ID', handler_name VARCHAR(128) NULL COMMENT '处理人名称',
source_type VARCHAR(32) NOT NULL DEFAULT 'MANUAL' COMMENT '异常来源类型', created_by VARCHAR(128) NULL COMMENT '创建人ID',
exception_at DATETIME(3) NOT 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_exception_no(customer_id,exception_no),
UNIQUE KEY uk_ll_exception_delivery_return(customer_id,delivery_order_id,exception_type),
KEY idx_ll_exception_list(customer_id,status,exception_at), KEY idx_ll_exception_tail(customer_id,last_mile_order_no),
KEY idx_ll_exception_inbound(customer_id,inbound_code)
) ENGINE=InnoDB COMMENT='海外仓异常订单';
CREATE TABLE IF NOT EXISTS ll_customer_visit (
id BIGINT NOT NULL COMMENT '客户回访记录ID',
customer_id VARCHAR(128) NOT NULL COMMENT '客户数据隔离键',
delivery_order_id BIGINT NOT NULL COMMENT '关联派送单ID',
delivery_no VARCHAR(64) NOT NULL COMMENT '关联派送单号',
customer_name VARCHAR(255) NULL COMMENT '客户名称快照',
contact_name VARCHAR(128) NULL COMMENT '客户联系人姓名快照',
contact_phone VARCHAR(64) NULL COMMENT '客户联系人电话快照',
destination VARCHAR(500) NULL COMMENT '派送目的仓编码汇总快照',
delivery_completed_at DATETIME(3) NOT NULL COMMENT '派送完成时间',
status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '回访状态:PENDING待回访或COMPLETED已回访',
selected_last_mile_order_id BIGINT NULL COMMENT '本次回访选择的尾程订单ID',
selected_last_mile_order_no VARCHAR(64) NULL COMMENT '本次回访选择的尾程订单号',
selected_inbound_order_id BIGINT NULL COMMENT '本次回访选择的入仓单ID',
selected_inbound_code VARCHAR(128) NULL COMMENT '本次回访选择的入仓码',
visit_result VARCHAR(32) NULL COMMENT '最近一次回访结果',
customer_feedback VARCHAR(2000) NULL COMMENT '最近一次客户反馈',
remark VARCHAR(1000) NULL COMMENT '最近一次回访备注',
attachment_json JSON NULL COMMENT '当前回访资料文件元数据列表',
visited_by VARCHAR(128) NULL COMMENT '最近一次回访操作人ID',
visited_at DATETIME(3) NULL COMMENT '最近一次回访时间',
visit_count INT 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 '更新时间',
PRIMARY KEY(id),
UNIQUE KEY uk_ll_customer_visit_delivery(customer_id,delivery_order_id),
KEY idx_ll_customer_visit_list(customer_id,status,delivery_completed_at),
KEY idx_ll_customer_visit_delivery_no(customer_id,delivery_no)
) ENGINE=InnoDB COMMENT='派送单维度客户回访记录';
CREATE TABLE IF NOT EXISTS ll_security_event_inbox (
event_id VARCHAR(64) NOT NULL COMMENT '跨区安全事件ID',
event_type VARCHAR(96) NOT NULL COMMENT 'IAM安全事件类型',
@@ -167,6 +167,89 @@ paths:
- { name: createdFrom, in: query, schema: { type: string, format: date-time } }
- { name: createdTo, in: query, schema: { type: string, format: date-time } }
responses: { '200': { description: 统一 ApiResponse 包装的尾程订单分页;列表行 destinationType 为 FBA 或 OVERSEAS } }
/api/platform/v1/last-leg/orders/{id}:
get:
summary: 查询尾程订单详情
description: 权限 platform:overseas-warehouse:last-mile-order:list;返回订单、货物、状态历史、费用明细和操作记录。
security: [{ bearerAuth: [] }]
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 } }
- { name: customerId, in: query, required: false, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
responses:
'200': { description: 统一 ApiResponse 包装的尾程订单详情 }
'404': { description: 尾程订单不存在或不属于查询客户 }
/api/platform/v1/last-leg/orders/{id}/fees:
post:
summary: 新增尾程订单费用
description: 权限 platform:overseas-warehouse:last-mile-order:edit;在订单客户范围内新增费用并记录操作人和处理结果。
security: [{ bearerAuth: [] }]
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 } }
- { name: customerId, in: query, required: false, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody:
required: true
content: { application/json: { schema: { $ref: '#/components/schemas/LastMileFeeCreate' } } }
responses:
'200': { description: 新增后的费用明细, content: { application/json: { schema: { $ref: '#/components/schemas/LastMileFeeApiResponse' } } } }
'404': { description: 尾程订单不存在或不属于操作客户 }
/api/platform/v1/last-leg/orders/{id}/detained:
put:
summary: 设置或解除尾程订单暂扣
description: 权限 platform:overseas-warehouse:last-mile-order:edit;库存同步转入或释放暂扣,并通过跨区事件回写国内入仓单。
security: [{ bearerAuth: [] }]
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 } }
- { name: customerId, in: query, required: false, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [detained, remark]
properties:
detained: { type: boolean }
remark: { type: string, minLength: 1, maxLength: 500 }
responses: { '200': { description: 暂扣状态已更新 } }
/api/platform/v1/last-leg/orders/{id}/pallet-merge-required:
put:
summary: 设置尾程订单合托标记
description: 权限 platform:overseas-warehouse:last-mile-order:edit;标记后 PDA 扫描本单箱唛时返回合托提示;实际完成合托后不可取消。
security: [{ bearerAuth: [] }]
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 } }
- { name: customerId, in: query, required: false, schema: { type: string, pattern: '^[1-9][0-9]*$' } }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [palletMergeRequired]
properties: { palletMergeRequired: { type: boolean } }
responses: { '200': { description: 合托标记已更新 }, '409': { description: 已完成实际合托,不能修改标记 } }
/api/platform/v1/last-leg/orders/pallet-merge-required/batch:
put:
summary: 批量设置同组合托标记
description: 权限 platform:overseas-warehouse:last-mile-order:edit;所选订单按同一合托批次原子标记或取消。
security: [{ bearerAuth: [] }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [orderIds, palletMergeRequired]
properties:
orderIds: { type: array, minItems: 1, uniqueItems: true, items: { type: integer, format: int64 } }
palletMergeRequired: { type: boolean }
responses: { '200': { description: 批量合托标记已更新 } }
/api/platform/v1/last-leg/orders/export:
get:
summary: 导出筛选后的尾程订单
description: 权限 platform:overseas-warehouse:last-mile-order:export;筛选参数与列表一致,返回 UTF-8 CSV。
security: [{ bearerAuth: [] }]
responses: { '200': { description: CSV 文件, content: { text/csv: { schema: { type: string, format: binary } } } } }
/api/platform/v1/last-leg/orders/container-summaries:
get:
summary: 分页查询柜级入库计划进度
@@ -762,6 +845,37 @@ components:
planStatus: { type: string, nullable: true }
planValid: { type: boolean }
plannedArrivalAt: { type: string, format: date-time, nullable: true, description: ll_order.estimated_warehouse_at }
LastMileFeeCreate:
type: object
required: [feeItem, currency, amount, status]
properties:
feeItem: { type: string, minLength: 1, maxLength: 128 }
currency: { type: string, pattern: '^[A-Z]{3}$', example: USD }
amount: { type: number, format: decimal, minimum: 0.01 }
status: { type: string, enum: [PENDING_REVIEW, APPROVED, REJECTED] }
supplierDescription: { type: string, maxLength: 500 }
LastMileFee:
type: object
required: [id, customerId, orderId, feeItem, currency, amount, status, createdBy, createdAt, updatedAt]
properties:
id: { type: integer, format: int64 }
customerId: { type: string }
orderId: { type: integer, format: int64 }
feeItem: { type: string }
currency: { type: string }
amount: { type: number, format: decimal }
status: { type: string }
supplierDescription: { type: string, nullable: true }
createdBy: { type: string }
createdAt: { type: string, format: date-time }
updatedAt: { type: string, format: date-time }
LastMileFeeApiResponse:
type: object
required: [code, message, data]
properties:
code: { type: string }
message: { type: string }
data: { $ref: '#/components/schemas/LastMileFee' }
PdaWarehouseScan:
type: object
required: [code]
@@ -906,7 +1020,7 @@ components:
timestamp: { type: string, format: date-time }
ReceivingInbound:
type: object
required: [inboundCode, destinationType, plannedPalletCount, totalPackages, receivedPackages, pendingPackages]
required: [inboundCode, destinationType, plannedPalletCount, totalPackages, receivedPackages, pendingPackages, palletMergeRequired]
properties:
inboundCode: { type: string }
destinationType: { type: string, enum: [FBA, OVERSEAS] }
@@ -920,6 +1034,8 @@ components:
totalPackages: { type: integer }
receivedPackages: { type: integer }
pendingPackages: { type: integer }
palletMergeRequired: { type: boolean, description: 尾程订单是否要求PDA合托提醒 }
palletMergeHint: { type: string, nullable: true, description: 标记合托时供PDA直接展示的中文提示 }
ReceivingWarehouseProfile:
type: object
required: [warehouseId, warehouseCode, warehouseName]
@@ -996,7 +1112,7 @@ components:
compatible: { type: boolean }
ReceivingReceipt:
type: object
required: [result, containerNo, inboundCode, destinationType, palletCode, acceptedPackages, receivedPackages, pendingPackages, sessionStatus]
required: [result, containerNo, inboundCode, destinationType, palletCode, acceptedPackages, receivedPackages, pendingPackages, sessionStatus, palletMergeRequired]
properties:
result: { type: string }
containerNo: { type: string }
@@ -1007,6 +1123,8 @@ components:
receivedPackages: { type: integer }
pendingPackages: { type: integer }
sessionStatus: { type: string, enum: [SIGNED, RECEIVING] }
palletMergeRequired: { type: boolean }
palletMergeHint: { type: string, nullable: true }
ReceivingCandidateListApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
@@ -1256,6 +1374,19 @@ components:
overseasTrackingNo: { $ref: '#/components/schemas/NullableTextPatch' }
estimatedArrivalAt: { type: string, format: date-time, nullable: true, description: 缺失不修改;null 或空串清空 }
remark: { $ref: '#/components/schemas/NullableTextPatch' }
outboundDetails:
type: array
description: 出库明细,可一次提交多条;总出库量不得超过入库量
items:
type: object
required: [quantity]
properties:
id: { type: string }
quantity: { type: integer, format: int64, minimum: 0 }
carrier: { type: string, nullable: true, maxLength: 128 }
trackingNo: { type: string, nullable: true, maxLength: 128 }
shippedAt: { type: string, format: date-time, nullable: true }
remark: { type: string, nullable: true, maxLength: 500 }
CustomerOptionResponse:
type: object
properties: