1659 lines
101 KiB
SQL
1659 lines
101 KiB
SQL
SET NAMES utf8mb4;
|
||
|
||
CREATE DATABASE IF NOT EXISTS baiyue_firstleg
|
||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||
USE baiyue_firstleg;
|
||
|
||
-- 兼容已执行二期初始化的存量库:PDA 拣货需要累计每个托盘分配已拣箱数。
|
||
DROP PROCEDURE IF EXISTS phase3_add_allocation_picked_count;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_add_allocation_picked_count()
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_pallet_allocation'
|
||
) AND NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_pallet_allocation'
|
||
AND column_name = 'picked_package_count'
|
||
) THEN
|
||
ALTER TABLE fl_inbound_pallet_allocation
|
||
ADD COLUMN picked_package_count INT UNSIGNED NOT NULL DEFAULT 0
|
||
COMMENT '已拣货箱数' AFTER allocated_package_count;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_add_allocation_picked_count();
|
||
DROP PROCEDURE phase3_add_allocation_picked_count;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_document_sequence (
|
||
sequence_key VARCHAR(32) NOT NULL COMMENT '单据序列键;日序列如CB:20260818,柜次长期序列固定为CABINET',
|
||
current_value INT UNSIGNED NOT NULL COMMENT '该类型当日已分配的最大序号',
|
||
PRIMARY KEY (sequence_key),
|
||
CONSTRAINT chk_fl_document_sequence_value CHECK (current_value > 0)
|
||
) ENGINE=InnoDB COMMENT='头程履约业务单号日序列;生成YYMMDD加四位当日序号';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID(雪花ID)',
|
||
container_no VARCHAR(64) NULL COMMENT '国内柜次真实集装箱号权威快照;ISO 6346标准格式;同一订舱内唯一,不同提单允许复用;订舱关联可选写入、提柜后可原子补录、预装纠错命令可修改',
|
||
cabinet_no VARCHAR(64) NOT NULL COMMENT '平台唯一内部柜号',
|
||
source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION' COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜',
|
||
record_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '记录状态:ACTIVE可操作、MERGED已合并历史',
|
||
merged_into_container_booking_id BIGINT UNSIGNED NULL COMMENT '真实柜被模拟柜吸收后的目标稳定柜次ID',
|
||
channel_group_code VARCHAR(64) NULL COMMENT '模拟柜计划渠道分组;关联真实订舱后继续保留',
|
||
channel_code VARCHAR(64) NULL COMMENT '订舱渠道编码快照;存量回填前禁止混装',
|
||
channel_name VARCHAR(128) NULL COMMENT '订舱渠道名称快照',
|
||
ocean_booking_id BIGINT UNSIGNED NULL COMMENT '来源订舱ID',
|
||
ocean_booking_requirement_id BIGINT UNSIGNED NULL COMMENT '来源订舱箱型需求ID',
|
||
customer_id BIGINT UNSIGNED NULL COMMENT '已关联真实订舱指定的客户范围',
|
||
customer_name VARCHAR(128) NULL COMMENT '已关联真实订舱客户名称快照',
|
||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '装柜仓库ID;平台公共资源',
|
||
warehouse_name VARCHAR(128) NOT NULL COMMENT '装柜仓库名称快照',
|
||
bill_of_lading_no VARCHAR(128) NULL COMMENT '提单号',
|
||
vessel_name VARCHAR(128) NULL COMMENT '船名快照',
|
||
voyage_no VARCHAR(64) NULL COMMENT '航次;确认前允许修订',
|
||
container_type VARCHAR(32) NOT NULL COMMENT '平台统一柜型:40GP、40HQ、45HQ;默认40HQ',
|
||
shipping_company_id BIGINT UNSIGNED NULL COMMENT '船司ID快照',
|
||
shipping_company_name VARCHAR(128) NULL COMMENT '船司名称快照',
|
||
departure_port_code VARCHAR(32) NULL COMMENT '起运港编码快照',
|
||
departure_port_name VARCHAR(128) NULL COMMENT '起运港名称快照',
|
||
destination_port_code VARCHAR(32) NULL COMMENT '目的港编码快照',
|
||
destination_port_name VARCHAR(128) NULL COMMENT '目的港名称快照',
|
||
destination_warehouse_code VARCHAR(64) NULL COMMENT '目的仓编码快照',
|
||
destination_warehouse_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
capacity_cbm DECIMAL(18,4) NULL COMMENT '货柜容量m3',
|
||
max_weight_kg DECIMAL(18,3) NULL COMMENT '货柜最大承重kg',
|
||
terminal_trucking_company_id BIGINT UNSIGNED NULL COMMENT '码头拖车Supplier逻辑ID快照;历史数据无法推导时为空',
|
||
terminal_trucking_company VARCHAR(128) NULL COMMENT '码头拖车公司名称快照',
|
||
loading_at DATETIME(3) NULL COMMENT '计划或实际装柜时间',
|
||
estimated_sailing_at DATETIME(3) NULL COMMENT '预计开船时间',
|
||
actual_sailing_at DATETIME(3) NULL COMMENT '实际开船时间',
|
||
estimated_port_arrival_at DATETIME(3) NULL COMMENT '预计到港时间',
|
||
actual_port_arrival_at DATETIME(3) NULL COMMENT '实际到港时间',
|
||
estimated_warehouse_arrival_at DATETIME(3) NULL COMMENT '预计到仓时间',
|
||
actual_warehouse_arrival_at DATETIME(3) NULL COMMENT '实际到仓时间',
|
||
unloading_completed_at DATETIME(3) NULL COMMENT '海外仓卸柜完成时间(UTC)',
|
||
unloading_photo_uploaded_at DATETIME(3) NULL COMMENT '海外仓卸柜照片上传成功时间(UTC)',
|
||
estimated_empty_reported_at DATETIME(3) NULL COMMENT '预计还空时间(卸柜完成时间加72小时,UTC)',
|
||
actual_empty_reported_at DATETIME(3) NULL COMMENT '实际报空时间(UTC)',
|
||
empty_report_source_version BIGINT UNSIGNED NULL COMMENT '海外货柜事件聚合版本,用于阻止乱序覆盖',
|
||
pickup_code VARCHAR(64) NULL COMMENT 'PU数字码;按字符串保存以保留前导零',
|
||
pickup_code_updated_by BIGINT UNSIGNED NULL COMMENT 'PU码最后填写人ID',
|
||
pickup_code_updated_by_name VARCHAR(64) NULL COMMENT 'PU码最后填写人名称快照',
|
||
pickup_code_updated_at DATETIME(3) NULL COMMENT 'PU码最后填写时间',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_by_name VARCHAR(64) NULL COMMENT '最后修改人名称快照',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '货柜资料最后更新时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_container_booking_cabinet (cabinet_no),
|
||
UNIQUE KEY uk_container_booking_ocean_container (ocean_booking_id,container_no),
|
||
KEY idx_container_booking_container_no (container_no,updated_at,id),
|
||
KEY idx_container_booking_merge (record_status,merged_into_container_booking_id),
|
||
KEY idx_container_booking_warehouse (warehouse_id, updated_at, id),
|
||
KEY idx_container_booking_voyage (voyage_no, updated_at),
|
||
KEY idx_container_booking_transit_summary (actual_sailing_at, actual_warehouse_arrival_at, ocean_booking_id, container_type),
|
||
CONSTRAINT chk_container_booking_source CHECK (source_type IN ('SIMULATION','OCEAN_BOOKING')),
|
||
CONSTRAINT chk_container_booking_record_status CHECK (record_status IN ('ACTIVE','MERGED')),
|
||
CONSTRAINT chk_container_booking_capacity CHECK (capacity_cbm IS NULL OR capacity_cbm > 0),
|
||
CONSTRAINT chk_container_booking_weight CHECK (max_weight_kg IS NULL OR max_weight_kg > 0),
|
||
CONSTRAINT chk_container_booking_pickup_code CHECK (pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$'),
|
||
CONSTRAINT chk_container_booking_iso6346 CHECK
|
||
(container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$')
|
||
) ENGINE=InnoDB COMMENT='平台货柜预订资料;预装状态唯一来源为装柜计划';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_container_event_inbox (
|
||
event_id VARCHAR(64) NOT NULL COMMENT '跨区事件全局唯一ID',
|
||
event_type VARCHAR(64) NOT NULL COMMENT '事件类型',
|
||
event_version INT UNSIGNED NOT NULL COMMENT '事件契约版本',
|
||
aggregate_id VARCHAR(64) NOT NULL COMMENT '跨区业务聚合ID',
|
||
aggregate_version BIGINT UNSIGNED NOT NULL COMMENT '跨区业务聚合版本',
|
||
payload_hash VARCHAR(80) NOT NULL COMMENT '规范化负载SHA-256摘要',
|
||
payload_json JSON NOT NULL COMMENT '原始事件负载',
|
||
status VARCHAR(16) NOT NULL COMMENT 'RECEIVED或PROCESSED',
|
||
received_at DATETIME(3) NOT NULL COMMENT '接收时间(UTC)',
|
||
processed_at DATETIME(3) NULL COMMENT '处理完成时间(UTC)',
|
||
PRIMARY KEY (event_id),
|
||
KEY idx_container_event_aggregate (aggregate_id, aggregate_version)
|
||
) ENGINE=InnoDB COMMENT='海外到国内业务事件消费Inbox';
|
||
|
||
DROP PROCEDURE IF EXISTS phase3_container_real_unit_columns;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_container_real_unit_columns()
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='source_type') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION'
|
||
COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜' AFTER cabinet_no;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='record_status') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN record_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE'
|
||
COMMENT '记录状态:ACTIVE可操作、MERGED已合并历史' AFTER source_type;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='merged_into_container_booking_id') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN merged_into_container_booking_id BIGINT UNSIGNED NULL
|
||
COMMENT '真实柜被模拟柜吸收后的目标稳定柜次ID' AFTER record_status;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='customer_id') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN customer_id BIGINT UNSIGNED NULL
|
||
COMMENT '已关联真实订舱指定的客户范围' AFTER ocean_booking_requirement_id;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='customer_name') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN customer_name VARCHAR(128) NULL
|
||
COMMENT '已关联真实订舱客户名称快照' AFTER customer_id;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE()
|
||
AND table_name='fl_ocean_booking') THEN
|
||
UPDATE fl_container_booking container_booking
|
||
JOIN fl_ocean_booking ocean_booking ON ocean_booking.id=container_booking.ocean_booking_id
|
||
SET container_booking.customer_id=COALESCE(container_booking.customer_id,ocean_booking.customer_id),
|
||
container_booking.customer_name=COALESCE(container_booking.customer_name,ocean_booking.customer_name)
|
||
WHERE container_booking.customer_id IS NULL OR container_booking.customer_name IS NULL;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='unloading_completed_at') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN unloading_completed_at DATETIME(3) NULL
|
||
COMMENT '海外仓卸柜完成时间(UTC)' AFTER actual_warehouse_arrival_at;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='unloading_photo_uploaded_at') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN unloading_photo_uploaded_at DATETIME(3) NULL
|
||
COMMENT '海外仓卸柜照片上传成功时间(UTC)' AFTER unloading_completed_at;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='estimated_empty_reported_at') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN estimated_empty_reported_at DATETIME(3) NULL
|
||
COMMENT '预计还空时间(卸柜完成时间加72小时,UTC)' AFTER unloading_photo_uploaded_at;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='actual_empty_reported_at') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN actual_empty_reported_at DATETIME(3) NULL
|
||
COMMENT '实际报空时间(UTC)' AFTER estimated_empty_reported_at;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='empty_report_source_version') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN empty_report_source_version BIGINT UNSIGNED NULL
|
||
COMMENT '海外货柜事件聚合版本,用于阻止乱序覆盖' AFTER actual_empty_reported_at;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='pickup_code') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN pickup_code VARCHAR(64) NULL
|
||
COMMENT 'PU数字码;按字符串保存以保留前导零' AFTER empty_report_source_version;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='pickup_code_updated_by') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN pickup_code_updated_by BIGINT UNSIGNED NULL
|
||
COMMENT 'PU码最后填写人ID' AFTER pickup_code;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='pickup_code_updated_by_name') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN pickup_code_updated_by_name VARCHAR(64) NULL
|
||
COMMENT 'PU码最后填写人名称快照' AFTER pickup_code_updated_by;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND column_name='pickup_code_updated_at') THEN
|
||
ALTER TABLE fl_container_booking ADD COLUMN pickup_code_updated_at DATETIME(3) NULL
|
||
COMMENT 'PU码最后填写时间' AFTER pickup_code_updated_by_name;
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND index_name='uk_container_booking_container_no') THEN
|
||
ALTER TABLE fl_container_booking DROP INDEX uk_container_booking_container_no;
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND index_name='uk_container_booking_ocean_container') THEN
|
||
ALTER TABLE fl_container_booking ADD UNIQUE KEY uk_container_booking_ocean_container (ocean_booking_id,container_no);
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND index_name='idx_container_booking_container_no') THEN
|
||
ALTER TABLE fl_container_booking ADD KEY idx_container_booking_container_no (container_no,updated_at,id);
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND index_name='idx_container_booking_merge') THEN
|
||
ALTER TABLE fl_container_booking ADD KEY idx_container_booking_merge (record_status,merged_into_container_booking_id);
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_source') THEN
|
||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_source
|
||
CHECK (source_type IN ('SIMULATION','OCEAN_BOOKING'));
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_record_status') THEN
|
||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_record_status
|
||
CHECK (record_status IN ('ACTIVE','MERGED'));
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_iso6346') THEN
|
||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_iso6346
|
||
CHECK (container_no IS NULL OR container_no REGEXP '^[A-Z]{3}[UJZ][0-9]{7}$');
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_pickup_code') THEN
|
||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_pickup_code
|
||
CHECK (pickup_code IS NULL OR pickup_code REGEXP '^[0-9]{1,64}$');
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_container_real_unit_columns();
|
||
DROP PROCEDURE phase3_container_real_unit_columns;
|
||
|
||
-- 触发器创建前先收敛旧库货柜供应商ID列;历史名称无法反推ID,保持NULL。
|
||
DROP PROCEDURE IF EXISTS phase3_container_supplier_snapshot_add_column;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE phase3_container_supplier_snapshot_add_column()
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||
WHERE table_schema=DATABASE() AND table_name='fl_container_booking'
|
||
AND column_name='terminal_trucking_company_id') THEN
|
||
ALTER TABLE fl_container_booking
|
||
ADD COLUMN terminal_trucking_company_id BIGINT UNSIGNED NULL COMMENT '码头拖车Supplier逻辑ID快照;历史数据无法推导时为空' AFTER max_weight_kg;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL phase3_container_supplier_snapshot_add_column();
|
||
DROP PROCEDURE phase3_container_supplier_snapshot_add_column;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID(雪花ID)',
|
||
plan_no VARCHAR(40) NOT NULL COMMENT '平台唯一装柜计划号',
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
|
||
plan_status VARCHAR(24) NOT NULL DEFAULT 'EDITING' COMMENT '装柜计划状态:EDITING编辑中、LOADING装柜中、COMPLETED装柜完成;建议须经人工确认',
|
||
preload_completed_by BIGINT UNSIGNED NULL COMMENT '预装完成操作人ID',
|
||
preload_completed_by_name VARCHAR(64) NULL COMMENT '预装完成操作人名称快照',
|
||
preload_completed_at DATETIME(3) NULL COMMENT '预装完成时间',
|
||
confirmed_by BIGINT UNSIGNED NULL COMMENT '确认操作人ID',
|
||
confirmed_by_name VARCHAR(64) NULL COMMENT '确认操作人名称快照',
|
||
confirmed_at DATETIME(3) NULL COMMENT '确认时间',
|
||
remark VARCHAR(500) NULL COMMENT '平台装柜计划备注',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_by_name VARCHAR(64) NULL COMMENT '最后修改人名称快照',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '装柜计划最后更新时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_plan_no (plan_no),
|
||
UNIQUE KEY uk_loading_plan_booking (container_booking_id),
|
||
KEY idx_loading_plan_status (plan_status, updated_at, id),
|
||
CONSTRAINT fk_loading_plan_booking FOREIGN KEY (container_booking_id)
|
||
REFERENCES fl_container_booking (id),
|
||
CONSTRAINT chk_loading_plan_status
|
||
CHECK (plan_status IN ('EDITING', 'LOADING', 'COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='平台智能装柜计划与唯一预装状态源';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划明细ID(雪花ID)',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID',
|
||
account_id BIGINT UNSIGNED NOT NULL COMMENT '客户账号ID;装柜业务唯一客户归属字段',
|
||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT 'Firstleg入库单ID',
|
||
inbound_detail_id BIGINT UNSIGNED NOT NULL COMMENT '订单货物或入库货物明细ID,不建立跨库外键',
|
||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID快照,不建立跨库外键',
|
||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓ID快照',
|
||
destination_warehouse_code VARCHAR(64) NULL COMMENT '目的仓编码快照',
|
||
destination_warehouse_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||
sku_code VARCHAR(128) NULL COMMENT 'SKU编码快照',
|
||
product_name_zh VARCHAR(255) NULL COMMENT '中文品名快照',
|
||
product_name_en VARCHAR(255) NULL COMMENT '英文品名快照',
|
||
product_image_file_id BIGINT UNSIGNED NULL COMMENT '产品图片File对象ID',
|
||
product_image_url VARCHAR(1000) NULL COMMENT '产品图片访问地址快照',
|
||
package_count INT UNSIGNED NOT NULL COMMENT '本次装柜箱数',
|
||
product_quantity INT UNSIGNED NULL COMMENT '产品总数量',
|
||
unit_type VARCHAR(24) NOT NULL DEFAULT 'PACKAGE' COMMENT '分配单元:PACKAGE普通箱、SEALED_PALLET一件代发封托',
|
||
pallet_id BIGINT UNSIGNED NULL COMMENT '一件代发封托ID;普通箱为空',
|
||
pallet_no VARCHAR(32) NULL COMMENT '一件代发系统托盘码快照',
|
||
indivisible TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否为不可拆整托单元',
|
||
units_per_carton INT UNSIGNED NULL COMMENT '每箱产品数',
|
||
pallet_count INT UNSIGNED NULL COMMENT '托盘数',
|
||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜毛重kg',
|
||
net_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜净重kg',
|
||
dimensional_weight_kg DECIMAL(18,3) NULL COMMENT '本次装柜材积重kg',
|
||
volume_cbm DECIMAL(18,4) NULL COMMENT '本次装柜体积m3',
|
||
fba_tracking_no VARCHAR(128) NULL COMMENT 'FBA追踪号快照',
|
||
shipment_no VARCHAR(128) NULL COMMENT '货件编号快照',
|
||
shipping_mark VARCHAR(500) NULL COMMENT '唛头快照',
|
||
load_sequence INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '装柜顺序',
|
||
position_snapshot JSON NULL COMMENT '智能装柜位置与摆放结果快照',
|
||
internal_remark VARCHAR(1000) NULL COMMENT '平台内部备注',
|
||
customer_remark VARCHAR(1000) NULL COMMENT '客户备注快照',
|
||
warehouse_remark VARCHAR(1000) NULL COMMENT '仓库备注快照',
|
||
customer_service_remark VARCHAR(1000) NULL COMMENT '客服备注快照',
|
||
frozen_at DATETIME(3) NULL COMMENT '装柜完成后的永久冻结时间',
|
||
frozen_by BIGINT UNSIGNED NULL COMMENT '冻结操作人ID',
|
||
removed_by BIGINT UNSIGNED NULL COMMENT '推送前逻辑删除操作人ID',
|
||
removed_by_name VARCHAR(64) NULL COMMENT '推送前逻辑删除操作人名称快照',
|
||
removed_at DATETIME(3) NULL COMMENT '推送前逻辑删除时间',
|
||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '推送前逻辑删除标记;冻结后不可修改',
|
||
active_inbound_detail_id BIGINT UNSIGNED GENERATED ALWAYS AS (CASE WHEN deleted = 0 THEN inbound_detail_id ELSE NULL END) STORED COMMENT '未删除明细查询键;删除后返回NULL',
|
||
active_assignment_key VARCHAR(96) GENERATED ALWAYS AS (CASE WHEN deleted = 0 THEN CONCAT(CASE WHEN pallet_id IS NULL THEN 'D:0' ELSE CONCAT('P:', pallet_id) END, ':', inbound_detail_id) ELSE NULL END) STORED COMMENT '普通明细或已封托托盘明细的有效唯一锁',
|
||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
|
||
updated_by BIGINT UNSIGNED NOT NULL COMMENT '最后修改人ID',
|
||
updated_by_name VARCHAR(64) NULL COMMENT '最后修改人名称快照',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '装柜明细最后更新时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_item_plan_assignment (loading_plan_id, active_assignment_key),
|
||
KEY idx_loading_item_active_detail (account_id, active_inbound_detail_id),
|
||
KEY idx_loading_item_plan (loading_plan_id, deleted, load_sequence, id),
|
||
KEY idx_loading_item_account (account_id, created_at, id),
|
||
KEY idx_loading_item_inbound (account_id, inbound_id, deleted),
|
||
KEY idx_loading_item_candidate (account_id, inbound_id, inbound_detail_id, deleted),
|
||
KEY idx_loading_item_pallet (pallet_id, deleted),
|
||
KEY idx_loading_item_order (account_id, order_id, deleted),
|
||
CONSTRAINT fk_loading_item_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT chk_loading_item_account CHECK (account_id > 0),
|
||
CONSTRAINT chk_loading_item_package CHECK (package_count > 0),
|
||
CONSTRAINT chk_loading_item_product_quantity CHECK (product_quantity IS NULL OR product_quantity > 0),
|
||
CONSTRAINT chk_loading_item_units CHECK (units_per_carton IS NULL OR units_per_carton > 0),
|
||
CONSTRAINT chk_loading_item_pallet CHECK (pallet_count IS NULL OR pallet_count >= 0),
|
||
CONSTRAINT chk_loading_item_weight CHECK (
|
||
(gross_weight_kg IS NULL OR gross_weight_kg >= 0)
|
||
AND (net_weight_kg IS NULL OR net_weight_kg >= 0)
|
||
AND (dimensional_weight_kg IS NULL OR dimensional_weight_kg >= 0)
|
||
),
|
||
CONSTRAINT chk_loading_item_volume CHECK (volume_cbm IS NULL OR volume_cbm >= 0),
|
||
CONSTRAINT chk_loading_item_deleted CHECK (deleted IN (0, 1))
|
||
) ENGINE=InnoDB COMMENT='智能装柜客户货物明细;客户归属仅在明细层';
|
||
|
||
SET @loading_item_customer_service_remark_sql = IF(
|
||
EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema='baiyue_firstleg' AND table_name='fl_loading_plan_item' AND column_name='customer_service_remark'),
|
||
'SELECT 1',
|
||
'ALTER TABLE baiyue_firstleg.fl_loading_plan_item ADD COLUMN customer_service_remark VARCHAR(1000) NULL COMMENT ''客服备注快照'' AFTER warehouse_remark'
|
||
);
|
||
PREPARE loading_item_customer_service_remark_stmt FROM @loading_item_customer_service_remark_sql;
|
||
EXECUTE loading_item_customer_service_remark_stmt;
|
||
DEALLOCATE PREPARE loading_item_customer_service_remark_stmt;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pallet_label (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '散货托盘标签ID(雪花ID)',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID',
|
||
loading_plan_item_id BIGINT UNSIGNED NOT NULL COMMENT '标签归属装柜明细ID',
|
||
account_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID快照',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入仓单ID快照',
|
||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||
container_no VARCHAR(64) NOT NULL COMMENT '真实集装箱号快照',
|
||
destination_warehouse_code VARCHAR(64) NOT NULL COMMENT 'FBA仓编码快照',
|
||
destination_warehouse_name VARCHAR(128) NULL COMMENT 'FBA仓名称快照',
|
||
pallet_index INT UNSIGNED NOT NULL COMMENT '当前柜当前FBA仓内从1开始的序号',
|
||
pallet_code VARCHAR(128) NOT NULL COMMENT '托盘码:平台柜次号-FBA仓内部编号-三位序号;不依赖可跨提单复用的真实柜号',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '生成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pallet_code (pallet_code),
|
||
UNIQUE KEY uk_loading_pallet_index (loading_plan_id,destination_warehouse_code,pallet_index),
|
||
KEY idx_loading_pallet_item (loading_plan_item_id,pallet_index),
|
||
KEY idx_loading_pallet_inbound (account_id,inbound_id),
|
||
CONSTRAINT fk_loading_pallet_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan(id),
|
||
CONSTRAINT fk_loading_pallet_item FOREIGN KEY (loading_plan_item_id) REFERENCES fl_loading_plan_item(id),
|
||
CONSTRAINT chk_loading_pallet_index CHECK (pallet_index BETWEEN 1 AND 999)
|
||
) ENGINE=InnoDB COMMENT='散货按柜号和FBA仓冻结的可打印托盘码';
|
||
|
||
-- 柜次级简单文件仅承载目的港清关和提货单;PU资料作为数字码保存在货柜主表。
|
||
CREATE TABLE IF NOT EXISTS fl_container_document (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜文件关联ID(雪花ID)',
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID',
|
||
document_type VARCHAR(32) NOT NULL COMMENT '文件类型:DESTINATION_CLEARANCE目的港清关、DELIVERY_ORDER提货单',
|
||
file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务文件ID,不建立跨库外键',
|
||
file_name VARCHAR(255) NOT NULL COMMENT '文件名称快照',
|
||
file_hash VARCHAR(64) NULL COMMENT 'File服务SHA-256快照',
|
||
file_url VARCHAR(1000) NULL COMMENT '文件访问地址快照',
|
||
uploaded_by BIGINT UNSIGNED NOT NULL COMMENT '上传操作人ID',
|
||
uploaded_by_name VARCHAR(64) NULL COMMENT '上传操作人名称快照',
|
||
completed_by BIGINT UNSIGNED NULL COMMENT '一键完成操作人ID',
|
||
completed_by_name VARCHAR(64) 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 '货柜文件关联最后更新时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_container_document_type (container_booking_id, document_type),
|
||
KEY idx_container_document_file (file_id),
|
||
CONSTRAINT fk_container_document_booking FOREIGN KEY (container_booking_id)
|
||
REFERENCES fl_container_booking (id),
|
||
CONSTRAINT chk_container_document_type
|
||
CHECK (document_type IN ('DESTINATION_CLEARANCE', 'DELIVERY_ORDER'))
|
||
) ENGINE=InnoDB COMMENT='平台货柜清关与DO文件关联';
|
||
|
||
SET @has_container_document_type_check=(SELECT COUNT(*) FROM information_schema.table_constraints
|
||
WHERE table_schema=DATABASE() AND table_name='fl_container_document'
|
||
AND constraint_name='chk_container_document_type' AND constraint_type='CHECK');
|
||
SET @ddl=IF(@has_container_document_type_check=0,'SELECT 1',
|
||
'ALTER TABLE fl_container_document DROP CHECK chk_container_document_type');
|
||
PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||
ALTER TABLE fl_container_document ADD CONSTRAINT chk_container_document_type
|
||
CHECK (document_type IN ('DESTINATION_CLEARANCE','DELIVERY_ORDER'));
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_container_exception_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜异常历史ID', container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '物理货柜ID',
|
||
action VARCHAR(16) NOT NULL COMMENT '操作类型:MARK标记异常、CANCEL取消异常', reason VARCHAR(500) NOT NULL COMMENT '异常原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID', operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '同一操作人幂等键', request_hash CHAR(64) NOT NULL COMMENT '请求摘要',
|
||
occurred_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作发生时间', PRIMARY KEY(id),
|
||
UNIQUE KEY uk_container_exception_operation(container_booking_id,operator_id,operation_id),
|
||
KEY idx_container_exception_history(container_booking_id,occurred_at,id),
|
||
CONSTRAINT fk_container_exception_booking FOREIGN KEY(container_booking_id) REFERENCES fl_container_booking(id),
|
||
CONSTRAINT chk_container_exception_action CHECK(action IN ('MARK','CANCEL'))
|
||
) ENGINE=InnoDB COMMENT='货柜异常标记及取消原因历史';
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_exception_history_before_update;
|
||
DROP TRIGGER IF EXISTS trg_container_exception_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_exception_history_before_update BEFORE UPDATE ON fl_container_exception_history FOR EACH ROW
|
||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='货柜异常历史不可修改'; END$$
|
||
CREATE TRIGGER trg_container_exception_history_before_delete BEFORE DELETE ON fl_container_exception_history FOR EACH ROW
|
||
BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='货柜异常历史不可删除'; END$$
|
||
DELIMITER ;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_plan_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '装柜操作历史ID(雪花ID)',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID',
|
||
loading_plan_item_id BIGINT UNSIGNED NULL COMMENT '装柜计划明细ID',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '操作幂等ID',
|
||
action_type VARCHAR(32) NOT NULL COMMENT '动作类型:CREATE_PLAN创建计划、ADD_ITEM新增明细、ADJUST_ITEM调整明细、REMOVE_ITEM移除明细、START_LOADING开始装柜、COMPLETE_LOADING完成装柜、PICK_ITEM拣货',
|
||
from_status VARCHAR(24) NULL COMMENT '变更前状态',
|
||
to_status VARCHAR(24) NULL COMMENT '变更后状态',
|
||
before_snapshot JSON NULL COMMENT '变更前快照',
|
||
after_snapshot JSON NULL COMMENT '变更后快照',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
operation_reason VARCHAR(500) NULL COMMENT '操作原因',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '操作时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_history_operation (operation_id),
|
||
KEY idx_loading_history_plan (loading_plan_id, created_at, id),
|
||
KEY idx_loading_history_item (loading_plan_item_id, created_at, id),
|
||
CONSTRAINT fk_loading_history_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT fk_loading_history_item FOREIGN KEY (loading_plan_item_id)
|
||
REFERENCES fl_loading_plan_item (id)
|
||
) ENGINE=InnoDB COMMENT='智能装柜追加式不可变操作历史';
|
||
|
||
-- fl_loading_pick_task 曾被另一套入库汇总模型复用。先按字段签名识别并迁走,
|
||
-- 再创建智能装柜任务表;未知或混合结构必须人工处理,禁止静默套用错误表型。
|
||
DROP PROCEDURE IF EXISTS migrate_legacy_loading_pick_task;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE migrate_legacy_loading_pick_task()
|
||
BEGIN
|
||
DECLARE source_exists INT DEFAULT 0;
|
||
DECLARE source_total_columns INT DEFAULT 0;
|
||
DECLARE source_smart_columns INT DEFAULT 0;
|
||
DECLARE source_current_columns INT DEFAULT 0;
|
||
DECLARE source_inbound_columns INT DEFAULT 0;
|
||
DECLARE target_exists INT DEFAULT 0;
|
||
DECLARE target_total_columns INT DEFAULT 0;
|
||
DECLARE target_inbound_columns INT DEFAULT 0;
|
||
|
||
SELECT COUNT(*) INTO source_exists
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||
IF source_exists = 1 THEN
|
||
SELECT COUNT(*) INTO source_total_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||
SELECT COUNT(*) INTO source_smart_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
|
||
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(16)' AND is_nullable = 'NO' AND column_default = 'LOADING')
|
||
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
);
|
||
SELECT COUNT(*) INTO source_inbound_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
|
||
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%')
|
||
);
|
||
|
||
SELECT COUNT(*) INTO source_current_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'loading_plan_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'container_booking_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'cabinet_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'NO')
|
||
OR (column_name = 'container_no' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'task_status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'LOADING')
|
||
OR (column_name = 'started_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'started_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'started_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'completed_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'completed_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'YES')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
);
|
||
|
||
IF source_total_columns = 13 AND source_current_columns = 13
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0
|
||
AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'uk_loading_pick_task_booking' AND non_unique = 0
|
||
AND seq_in_index = 1 AND column_name = 'container_booking_id') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'idx_loading_pick_container'
|
||
AND ((seq_in_index = 1 AND column_name = 'container_no')
|
||
OR (seq_in_index = 2 AND column_name = 'task_status')
|
||
OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan'
|
||
AND referenced_column_name = 'id') = 1 THEN
|
||
SET source_exists = 1;
|
||
ELSEIF source_total_columns = 11 AND source_smart_columns = 11 AND source_inbound_columns < 12
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'uk_loading_pick_task_plan' AND non_unique = 0
|
||
AND seq_in_index = 1 AND column_name = 'loading_plan_id') = 1
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'idx_loading_pick_container'
|
||
AND ((seq_in_index = 1 AND column_name = 'container_no')
|
||
OR (seq_in_index = 2 AND column_name = 'task_status')
|
||
OR (seq_in_index = 3 AND column_name = 'started_at'))) = 3
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name = 'loading_plan_id' AND referenced_table_name = 'fl_loading_plan'
|
||
AND referenced_column_name = 'id') = 1 THEN
|
||
SET source_exists = 1;
|
||
ELSEIF source_total_columns = 12 AND source_inbound_columns = 12 AND source_smart_columns < 11
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0
|
||
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
|
||
OR (seq_in_index = 2 AND column_name = 'inbound_id'))) = 2
|
||
AND (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND index_name = 'idx_loading_pick_status'
|
||
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
|
||
OR (seq_in_index = 2 AND column_name = 'status')
|
||
OR (seq_in_index = 3 AND column_name = 'updated_at'))) = 3
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||
AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order'
|
||
AND referenced_column_name = 'id') = 1 THEN
|
||
SELECT COUNT(*) INTO target_exists
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_loading_pick_task';
|
||
IF target_exists = 0 THEN
|
||
RENAME TABLE fl_loading_pick_task TO fl_inbound_loading_pick_task;
|
||
ELSE
|
||
SELECT COUNT(*) INTO target_total_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task';
|
||
SELECT COUNT(*) INTO target_inbound_columns
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_inbound_loading_pick_task' AND (
|
||
(column_name = 'id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'tenant_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'inbound_id' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'status' AND LOWER(column_type) = 'varchar(24)' AND is_nullable = 'NO' AND column_default = 'PENDING')
|
||
OR (column_name = 'version' AND LOWER(column_type) = 'int' AND is_nullable = 'NO' AND CAST(column_default AS CHAR) = '0')
|
||
OR (column_name = 'expected_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'picked_count' AND LOWER(column_type) = 'int unsigned' AND is_nullable = 'YES')
|
||
OR (column_name = 'remark' AND LOWER(column_type) = 'varchar(1000)' AND is_nullable = 'YES')
|
||
OR (column_name = 'updated_by' AND LOWER(column_type) = 'bigint unsigned' AND is_nullable = 'NO')
|
||
OR (column_name = 'updated_by_name' AND LOWER(column_type) = 'varchar(64)' AND is_nullable = 'YES')
|
||
OR (column_name = 'created_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%')
|
||
OR (column_name = 'updated_at' AND data_type = 'datetime' AND datetime_precision = 3 AND is_nullable = 'NO' AND LOWER(column_default) LIKE 'current_timestamp%' AND LOWER(extra) LIKE '%on update current_timestamp%')
|
||
);
|
||
IF target_total_columns <> 12 OR target_inbound_columns <> 12
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND index_name = 'uk_loading_pick_tenant_inbound' AND non_unique = 0
|
||
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
|
||
OR (seq_in_index = 2 AND column_name = 'inbound_id'))) <> 2
|
||
OR (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND index_name = 'idx_loading_pick_status'
|
||
AND ((seq_in_index = 1 AND column_name = 'tenant_id')
|
||
OR (seq_in_index = 2 AND column_name = 'status')
|
||
OR (seq_in_index = 3 AND column_name = 'updated_at'))) <> 3
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_loading_pick_task'
|
||
AND column_name = 'inbound_id' AND referenced_table_name = 'fl_inbound_order'
|
||
AND referenced_column_name = 'id') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是完整入库汇总结构';
|
||
END IF;
|
||
INSERT INTO fl_inbound_loading_pick_task
|
||
(id, tenant_id, inbound_id, status, version, expected_count, picked_count,
|
||
remark, updated_by, updated_by_name, created_at, updated_at)
|
||
SELECT source.id, source.tenant_id, source.inbound_id, source.status, source.version,
|
||
source.expected_count, source.picked_count, source.remark, source.updated_by,
|
||
source.updated_by_name, source.created_at, source.updated_at
|
||
FROM fl_loading_pick_task source
|
||
LEFT JOIN fl_inbound_loading_pick_task target
|
||
ON target.id = source.id
|
||
WHERE target.id IS NULL;
|
||
IF EXISTS (
|
||
SELECT 1
|
||
FROM fl_loading_pick_task source
|
||
LEFT JOIN fl_inbound_loading_pick_task target ON target.id = source.id
|
||
WHERE target.id IS NULL
|
||
OR NOT (target.tenant_id <=> source.tenant_id)
|
||
OR NOT (target.inbound_id <=> source.inbound_id)
|
||
OR NOT (target.status <=> source.status)
|
||
OR NOT (target.version <=> source.version)
|
||
OR NOT (target.expected_count <=> source.expected_count)
|
||
OR NOT (target.picked_count <=> source.picked_count)
|
||
OR NOT (target.remark <=> source.remark)
|
||
OR NOT (target.updated_by <=> source.updated_by)
|
||
OR NOT (target.updated_by_name <=> source.updated_by_name)
|
||
OR NOT (target.created_at <=> source.created_at)
|
||
OR NOT (target.updated_at <=> source.updated_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:新旧入库汇总任务存在主键冲突';
|
||
END IF;
|
||
DROP TABLE fl_loading_pick_task;
|
||
END IF;
|
||
ELSE
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 字段签名未知或混合';
|
||
END IF;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL migrate_legacy_loading_pick_task();
|
||
DROP PROCEDURE migrate_legacy_loading_pick_task;
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID',
|
||
container_booking_id BIGINT UNSIGNED NOT NULL COMMENT '稳定柜次ID',
|
||
cabinet_no VARCHAR(64) NOT NULL COMMENT '稳定内部柜次号快照',
|
||
container_no VARCHAR(64) NULL COMMENT '废弃兼容字段;不得作为任务身份或随运输号变更重写',
|
||
task_status VARCHAR(24) NOT NULL DEFAULT 'LOADING' COMMENT 'PDA任务状态:LOADING、COMPLETING、COMPLETION_FAILED、COMPLETED',
|
||
started_by BIGINT UNSIGNED NOT NULL COMMENT '开始装柜人',
|
||
started_by_name VARCHAR(64) NULL COMMENT '开始装柜人名称',
|
||
started_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '开始时间',
|
||
completed_by BIGINT UNSIGNED NULL COMMENT '完成装柜人',
|
||
completed_by_name VARCHAR(64) NULL COMMENT '完成装柜人名称',
|
||
completed_at DATETIME(3) NULL COMMENT '完成时间',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pick_task_plan (loading_plan_id),
|
||
UNIQUE KEY uk_loading_pick_task_booking (container_booking_id),
|
||
KEY idx_loading_pick_container (container_no, task_status, started_at),
|
||
CONSTRAINT fk_loading_pick_task_plan FOREIGN KEY (loading_plan_id) REFERENCES fl_loading_plan (id),
|
||
CONSTRAINT chk_loading_pick_task_status CHECK (task_status IN ('LOADING','COMPLETING','COMPLETION_FAILED','COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜拣货任务';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_completion_command (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT '可靠装柜完成命令ID',
|
||
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA任务ID',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '客户端全局幂等操作ID',
|
||
request_hash CHAR(64) NOT NULL COMMENT '规范化请求摘要',
|
||
command_status VARCHAR(16) NOT NULL COMMENT '命令状态:PROCESSING处理中、RETRYABLE可重试、FAILED失败、COMPLETED已完成',
|
||
expected_task_version INT UNSIGNED NOT NULL COMMENT '受理时客户端任务版本',
|
||
remark VARCHAR(500) NULL COMMENT '完成备注',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||
attempt_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'worker认领次数',
|
||
next_retry_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '下次处理时间',
|
||
lease_owner VARCHAR(128) NULL COMMENT 'worker租约持有者',
|
||
lease_until DATETIME(3) NULL COMMENT 'worker租约截止时间',
|
||
failure_stage VARCHAR(40) NULL COMMENT '脱敏失败阶段',
|
||
failure_code VARCHAR(32) NULL COMMENT '下游或业务错误码',
|
||
failure_request_id VARCHAR(64) NULL COMMENT '下游请求ID',
|
||
failure_trace_id VARCHAR(64) 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 '命令更新时间',
|
||
completed_at DATETIME(3) NULL COMMENT '命令完成时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_completion_operation (operation_id),
|
||
KEY idx_loading_completion_dispatch (command_status,next_retry_at,lease_until),
|
||
CONSTRAINT fk_loading_completion_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task(id),
|
||
CONSTRAINT chk_loading_completion_status CHECK (command_status IN ('PROCESSING','RETRYABLE','FAILED','COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜完成可靠异步命令';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_completion_checkpoint (
|
||
command_id BIGINT UNSIGNED NOT NULL COMMENT '可靠装柜完成命令ID',
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '入仓单客户ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入仓单ID',
|
||
owner_sequence INT UNSIGNED NOT NULL COMMENT '稳定处理顺序',
|
||
checkpoint_stage VARCHAR(24) NOT NULL COMMENT '处理阶段:READY待处理、SETTLED资源已结算、INBOUND_COMPLETED入仓已完成',
|
||
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 (command_id,tenant_id,inbound_id),
|
||
KEY idx_loading_completion_owner (command_id,owner_sequence),
|
||
CONSTRAINT fk_loading_completion_checkpoint_command FOREIGN KEY (command_id)
|
||
REFERENCES fl_loading_completion_command(id),
|
||
CONSTRAINT chk_loading_completion_checkpoint CHECK (checkpoint_stage IN ('READY','SETTLED','INBOUND_COMPLETED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜完成按入库单恢复检查点';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_item (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货明细ID',
|
||
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
loading_plan_item_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划明细ID',
|
||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '父入库单ID',
|
||
inbound_detail_id BIGINT UNSIGNED NOT NULL COMMENT '订单货物明细ID',
|
||
planned_package_count INT UNSIGNED NOT NULL COMMENT '计划拣货箱数;上层拆分按业务规则向上取整',
|
||
picked_package_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已拣货箱数',
|
||
item_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '拣货明细状态:PENDING待拣、PICKING拣货中、PICKED已拣完、PARTIAL部分拣货、SKIPPED跳拣',
|
||
release_mode VARCHAR(10) NULL COMMENT 'AUTO按比例释放,MANUAL按人工选择释放',
|
||
release_location_ids JSON NULL COMMENT '人工选择释放库位ID;空数组表示明确释放0个',
|
||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'PDA拣货明细创建时间',
|
||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'PDA拣货明细最后更新时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pick_item_plan_item (loading_plan_item_id),
|
||
KEY idx_loading_pick_item_task (pick_task_id, item_status, id),
|
||
CONSTRAINT fk_loading_pick_item_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT fk_loading_pick_item_plan_item FOREIGN KEY (loading_plan_item_id) REFERENCES fl_loading_plan_item (id),
|
||
CONSTRAINT chk_loading_pick_item_planned CHECK (planned_package_count > 0),
|
||
CONSTRAINT chk_loading_pick_item_count CHECK (picked_package_count <= planned_package_count),
|
||
CONSTRAINT chk_loading_pick_item_status CHECK (item_status IN ('PENDING', 'PICKING', 'PICKED', 'PARTIAL', 'SKIPPED'))
|
||
) ENGINE=InnoDB COMMENT='PDA装柜拣货明细';
|
||
|
||
CREATE TABLE IF NOT EXISTS fl_loading_pick_history (
|
||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货操作历史ID',
|
||
pick_task_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||
pick_item_id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货明细ID',
|
||
operation_id VARCHAR(128) NOT NULL COMMENT '客户端幂等操作号',
|
||
request_hash CHAR(64) NOT NULL COMMENT '完整批量请求内容SHA-256摘要',
|
||
action_type VARCHAR(24) NOT NULL DEFAULT 'PICK' COMMENT '拣货动作:PICK正常拣货、PARTIAL部分拣货、SKIP跳拣',
|
||
picked_package_count INT UNSIGNED NOT NULL COMMENT '本次拣货箱数',
|
||
operation_reason VARCHAR(500) NULL COMMENT '部分拣货或跳拣原因',
|
||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '执行PDA拣货操作的作业员ID',
|
||
operator_name VARCHAR(64) NULL COMMENT '作业员姓名快照',
|
||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '不可变拣货操作历史创建时间',
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_loading_pick_history_operation (operation_id),
|
||
KEY idx_loading_pick_history_item (pick_item_id, created_at),
|
||
CONSTRAINT fk_loading_pick_history_task FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id),
|
||
CONSTRAINT fk_loading_pick_history_item FOREIGN KEY (pick_item_id) REFERENCES fl_loading_pick_item (id)
|
||
) ENGINE=InnoDB COMMENT='PDA拣货幂等和操作审计历史';
|
||
|
||
DROP PROCEDURE IF EXISTS repair_phase3_loading_pick_task_foreign_keys;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE repair_phase3_loading_pick_task_foreign_keys()
|
||
BEGIN
|
||
DECLARE item_fk_count INT DEFAULT 0;
|
||
DECLARE item_target_count INT DEFAULT 0;
|
||
DECLARE history_fk_count INT DEFAULT 0;
|
||
DECLARE history_target_count INT DEFAULT 0;
|
||
DECLARE item_drop_clauses TEXT;
|
||
DECLARE history_drop_clauses TEXT;
|
||
|
||
IF EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item item
|
||
LEFT JOIN fl_loading_pick_task task ON task.id = item.pick_task_id
|
||
WHERE task.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '柜次PDA明细存在无法迁移到智能装柜任务的引用';
|
||
END IF;
|
||
IF EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_history history
|
||
LEFT JOIN fl_loading_pick_task task ON task.id = history.pick_task_id
|
||
WHERE task.id IS NULL
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '柜次PDA历史存在无法迁移到智能装柜任务的引用';
|
||
END IF;
|
||
|
||
SELECT COUNT(*),
|
||
SUM(k.referenced_table_name = 'fl_loading_pick_task'
|
||
AND k.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = k.constraint_schema
|
||
AND peer.table_name = k.table_name
|
||
AND peer.constraint_name = k.constraint_name) = 1),
|
||
GROUP_CONCAT(CONCAT('DROP FOREIGN KEY `', REPLACE(k.constraint_name, '`', '``'), '`')
|
||
ORDER BY k.constraint_name SEPARATOR ', ')
|
||
INTO item_fk_count, item_target_count, item_drop_clauses
|
||
FROM information_schema.key_column_usage k
|
||
WHERE k.constraint_schema = 'baiyue_firstleg'
|
||
AND k.table_name = 'fl_loading_pick_item'
|
||
AND k.column_name = 'pick_task_id'
|
||
AND k.referenced_table_name IS NOT NULL;
|
||
IF item_fk_count <> 1 OR item_target_count <> 1 THEN
|
||
IF item_drop_clauses IS NOT NULL THEN
|
||
SET @phase3_fk_ddl = CONCAT('ALTER TABLE fl_loading_pick_item ', item_drop_clauses);
|
||
PREPARE phase3_fk_statement FROM @phase3_fk_ddl;
|
||
EXECUTE phase3_fk_statement;
|
||
DEALLOCATE PREPARE phase3_fk_statement;
|
||
END IF;
|
||
ALTER TABLE fl_loading_pick_item
|
||
ADD CONSTRAINT fk_loading_pick_item_task
|
||
FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id);
|
||
END IF;
|
||
|
||
SELECT COUNT(*),
|
||
SUM(k.referenced_table_name = 'fl_loading_pick_task'
|
||
AND k.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = k.constraint_schema
|
||
AND peer.table_name = k.table_name
|
||
AND peer.constraint_name = k.constraint_name) = 1),
|
||
GROUP_CONCAT(CONCAT('DROP FOREIGN KEY `', REPLACE(k.constraint_name, '`', '``'), '`')
|
||
ORDER BY k.constraint_name SEPARATOR ', ')
|
||
INTO history_fk_count, history_target_count, history_drop_clauses
|
||
FROM information_schema.key_column_usage k
|
||
WHERE k.constraint_schema = 'baiyue_firstleg'
|
||
AND k.table_name = 'fl_loading_pick_history'
|
||
AND k.column_name = 'pick_task_id'
|
||
AND k.referenced_table_name IS NOT NULL;
|
||
IF history_fk_count <> 1 OR history_target_count <> 1 THEN
|
||
IF history_drop_clauses IS NOT NULL THEN
|
||
SET @phase3_fk_ddl = CONCAT('ALTER TABLE fl_loading_pick_history ', history_drop_clauses);
|
||
PREPARE phase3_fk_statement FROM @phase3_fk_ddl;
|
||
EXECUTE phase3_fk_statement;
|
||
DEALLOCATE PREPARE phase3_fk_statement;
|
||
END IF;
|
||
ALTER TABLE fl_loading_pick_history
|
||
ADD CONSTRAINT fk_loading_pick_history_task
|
||
FOREIGN KEY (pick_task_id) REFERENCES fl_loading_pick_task (id);
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL repair_phase3_loading_pick_task_foreign_keys();
|
||
DROP PROCEDURE repair_phase3_loading_pick_task_foreign_keys;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_booking_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_booking_before_update
|
||
BEFORE UPDATE ON fl_container_booking
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status
|
||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||
NOT (OLD.id <=> NEW.id)
|
||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||
OR NOT (OLD.channel_code <=> NEW.channel_code)
|
||
OR NOT (OLD.channel_name <=> NEW.channel_name)
|
||
OR NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id)
|
||
OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id)
|
||
OR NOT (OLD.warehouse_id <=> NEW.warehouse_id)
|
||
OR NOT (OLD.warehouse_name <=> NEW.warehouse_name)
|
||
OR NOT (OLD.container_type <=> NEW.container_type)
|
||
OR NOT (OLD.shipping_company_id <=> NEW.shipping_company_id)
|
||
OR NOT (OLD.shipping_company_name <=> NEW.shipping_company_name)
|
||
OR NOT (OLD.departure_port_code <=> NEW.departure_port_code)
|
||
OR NOT (OLD.departure_port_name <=> NEW.departure_port_name)
|
||
OR NOT (OLD.destination_port_code <=> NEW.destination_port_code)
|
||
OR NOT (OLD.destination_port_name <=> NEW.destination_port_name)
|
||
OR NOT (OLD.destination_warehouse_code <=> NEW.destination_warehouse_code)
|
||
OR NOT (OLD.destination_warehouse_name <=> NEW.destination_warehouse_name)
|
||
OR NOT (OLD.capacity_cbm <=> NEW.capacity_cbm)
|
||
OR NOT (OLD.max_weight_kg <=> NEW.max_weight_kg)
|
||
OR NOT (OLD.terminal_trucking_company_id <=> NEW.terminal_trucking_company_id)
|
||
OR NOT (OLD.terminal_trucking_company <=> NEW.terminal_trucking_company)
|
||
OR NOT (OLD.loading_at <=> NEW.loading_at)
|
||
OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||
OR NOT (OLD.estimated_port_arrival_at <=> NEW.estimated_port_arrival_at)
|
||
OR NOT (OLD.actual_port_arrival_at <=> NEW.actual_port_arrival_at)
|
||
OR NOT (OLD.estimated_warehouse_arrival_at <=> NEW.estimated_warehouse_arrival_at)
|
||
OR NOT (OLD.actual_warehouse_arrival_at <=> NEW.actual_warehouse_arrival_at)
|
||
OR NOT (OLD.created_by <=> NEW.created_by)
|
||
OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '装柜作业开始后仅允许修改集装箱号与放舱运输数据';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_plan_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_plan_before_update
|
||
BEFORE UPDATE ON fl_loading_plan
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已完成装柜计划全部只读';
|
||
END IF;
|
||
IF NOT (
|
||
(OLD.plan_status = 'EDITING' AND NEW.plan_status IN ('EDITING', 'LOADING'))
|
||
OR (OLD.plan_status = 'LOADING'
|
||
AND NEW.plan_status IN ('LOADING', 'COMPLETED'))
|
||
OR (OLD.plan_status = 'LOADING' AND NEW.plan_status = 'EDITING'
|
||
AND NOT EXISTS (
|
||
SELECT 1
|
||
FROM fl_loading_pick_item pick_item
|
||
JOIN fl_loading_pick_task pick_task ON pick_task.id = pick_item.pick_task_id
|
||
WHERE pick_task.loading_plan_id = OLD.id
|
||
AND pick_item.picked_package_count > 0
|
||
))
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜状态只允许单向推进';
|
||
END IF;
|
||
IF OLD.plan_status = 'LOADING' AND (
|
||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.plan_no <=> NEW.plan_no)
|
||
OR NOT (OLD.container_booking_id <=> NEW.container_booking_id)
|
||
OR NOT (OLD.remark <=> NEW.remark)
|
||
OR NOT (OLD.created_by <=> NEW.created_by)
|
||
OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '装柜中计划仅允许乐观锁触碰或完成装柜';
|
||
END IF;
|
||
IF OLD.plan_status = NEW.plan_status
|
||
AND OLD.plan_status = 'LOADING' AND (
|
||
NOT (OLD.preload_completed_by <=> NEW.preload_completed_by)
|
||
OR NOT (OLD.preload_completed_by_name <=> NEW.preload_completed_by_name)
|
||
OR NOT (OLD.preload_completed_at <=> NEW.preload_completed_at)
|
||
OR NOT (OLD.confirmed_by <=> NEW.confirmed_by)
|
||
OR NOT (OLD.confirmed_by_name <=> NEW.confirmed_by_name)
|
||
OR NOT (OLD.confirmed_at <=> NEW.confirmed_at)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '同状态乐观锁触碰不得修改阶段审计字段';
|
||
END IF;
|
||
IF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN
|
||
IF EXISTS (SELECT 1 FROM fl_loading_plan_item item
|
||
WHERE item.loading_plan_id = OLD.id AND item.deleted = 0
|
||
AND NOT EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item pick_item
|
||
JOIN fl_loading_pick_task pick_task ON pick_task.id = pick_item.pick_task_id
|
||
WHERE pick_item.loading_plan_item_id = item.id
|
||
AND pick_task.loading_plan_id = OLD.id
|
||
AND (
|
||
(pick_item.item_status = 'PICKED'
|
||
AND pick_item.picked_package_count = item.package_count)
|
||
OR (pick_item.item_status = 'PARTIAL'
|
||
AND pick_item.picked_package_count > 0
|
||
AND pick_item.picked_package_count = item.package_count)
|
||
OR (pick_item.item_status = 'SKIPPED'
|
||
AND pick_item.picked_package_count = 0)
|
||
)
|
||
)) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仍有未完成拣货明细,不能完成装柜';
|
||
END IF;
|
||
SET NEW.confirmed_at = COALESCE(NEW.confirmed_at, CURRENT_TIMESTAMP(3));
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_plan_after_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_plan_after_update
|
||
AFTER UPDATE ON fl_loading_plan
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.plan_status = 'LOADING' AND NEW.plan_status = 'COMPLETED' THEN
|
||
UPDATE fl_loading_plan_item
|
||
SET frozen_at = COALESCE(frozen_at, NEW.confirmed_at, CURRENT_TIMESTAMP(3)),
|
||
frozen_by = COALESCE(frozen_by, NEW.updated_by),
|
||
updated_by = NEW.updated_by,
|
||
updated_by_name = NEW.updated_by_name,
|
||
version = version + 1
|
||
WHERE loading_plan_id = NEW.id AND deleted = 0;
|
||
UPDATE fl_loading_pick_task
|
||
SET task_status = 'COMPLETED', completed_by = NEW.updated_by,
|
||
completed_by_name = NEW.updated_by_name, completed_at = CURRENT_TIMESTAMP(3),
|
||
version = version + 1
|
||
WHERE loading_plan_id = NEW.id AND task_status = 'LOADING';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_insert;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_insert
|
||
BEFORE INSERT ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan WHERE id = NEW.loading_plan_id;
|
||
IF current_plan_status IS NULL OR current_plan_status NOT IN ('EDITING', 'LOADING') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '仅编辑中或装柜中的计划允许新增明细';
|
||
END IF;
|
||
IF NEW.deleted <> 0 OR NEW.frozen_at IS NOT NULL OR NEW.frozen_by IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '新增装柜明细必须为未删除且未冻结状态';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_update
|
||
BEFORE UPDATE ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status
|
||
FROM fl_loading_plan WHERE id = OLD.loading_plan_id;
|
||
IF current_plan_status = 'LOADING' AND EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item
|
||
WHERE loading_plan_item_id = OLD.id AND picked_package_count > 0) THEN
|
||
IF NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||
OR NOT (NEW.warehouse_remark <=> OLD.warehouse_remark)
|
||
OR NOT (NEW.customer_service_remark <=> OLD.customer_service_remark) THEN
|
||
IF NEW.package_count <> OLD.package_count OR NEW.deleted <> OLD.deleted
|
||
OR NOT (NEW.customer_name <=> OLD.customer_name)
|
||
OR NOT (NEW.inbound_id <=> OLD.inbound_id)
|
||
OR NOT (NEW.inbound_detail_id <=> OLD.inbound_detail_id)
|
||
OR NOT (NEW.order_id <=> OLD.order_id)
|
||
OR NOT (NEW.order_no <=> OLD.order_no)
|
||
OR NOT (NEW.inbound_code <=> OLD.inbound_code)
|
||
OR NOT (NEW.destination_warehouse_id <=> OLD.destination_warehouse_id)
|
||
OR NOT (NEW.destination_warehouse_code <=> OLD.destination_warehouse_code)
|
||
OR NOT (NEW.destination_warehouse_name <=> OLD.destination_warehouse_name)
|
||
OR NOT (NEW.sku_code <=> OLD.sku_code)
|
||
OR NOT (NEW.product_name_zh <=> OLD.product_name_zh)
|
||
OR NOT (NEW.product_name_en <=> OLD.product_name_en)
|
||
OR NOT (NEW.product_image_file_id <=> OLD.product_image_file_id)
|
||
OR NOT (NEW.product_image_url <=> OLD.product_image_url)
|
||
OR NOT (NEW.product_quantity <=> OLD.product_quantity)
|
||
OR NOT (NEW.units_per_carton <=> OLD.units_per_carton)
|
||
OR NOT (NEW.pallet_count <=> OLD.pallet_count)
|
||
OR NOT (NEW.gross_weight_kg <=> OLD.gross_weight_kg)
|
||
OR NOT (NEW.net_weight_kg <=> OLD.net_weight_kg)
|
||
OR NOT (NEW.dimensional_weight_kg <=> OLD.dimensional_weight_kg)
|
||
OR NOT (NEW.volume_cbm <=> OLD.volume_cbm)
|
||
OR NOT (NEW.fba_tracking_no <=> OLD.fba_tracking_no)
|
||
OR NOT (NEW.shipment_no <=> OLD.shipment_no)
|
||
OR NOT (NEW.shipping_mark <=> OLD.shipping_mark)
|
||
OR NOT (NEW.load_sequence <=> OLD.load_sequence)
|
||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||
OR NOT (NEW.removed_by_name <=> OLD.removed_by_name)
|
||
OR NOT (NEW.removed_at <=> OLD.removed_at) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有PDA拣货记录的装柜明细修改备注时不得变更其他业务字段';
|
||
END IF;
|
||
ELSEIF NOT EXISTS (
|
||
SELECT 1 FROM fl_loading_pick_item actual_pick
|
||
JOIN fl_loading_pick_task actual_task ON actual_task.id=actual_pick.pick_task_id
|
||
WHERE actual_pick.loading_plan_item_id = OLD.id
|
||
AND actual_task.loading_plan_id = OLD.loading_plan_id
|
||
AND actual_pick.item_status = 'PARTIAL'
|
||
AND OLD.unit_type = 'PACKAGE' AND OLD.pallet_id IS NULL
|
||
AND actual_pick.picked_package_count > 0
|
||
AND actual_pick.picked_package_count < actual_pick.planned_package_count
|
||
AND OLD.package_count = actual_pick.planned_package_count
|
||
AND NEW.deleted = 0
|
||
AND NEW.package_count = actual_pick.picked_package_count
|
||
) OR NEW.deleted <> OLD.deleted
|
||
OR NOT (NEW.customer_name <=> OLD.customer_name)
|
||
OR NOT (NEW.inbound_id <=> OLD.inbound_id)
|
||
OR NOT (NEW.inbound_detail_id <=> OLD.inbound_detail_id)
|
||
OR NOT (NEW.order_id <=> OLD.order_id)
|
||
OR NOT (NEW.order_no <=> OLD.order_no)
|
||
OR NOT (NEW.inbound_code <=> OLD.inbound_code)
|
||
OR NOT (NEW.destination_warehouse_id <=> OLD.destination_warehouse_id)
|
||
OR NOT (NEW.destination_warehouse_code <=> OLD.destination_warehouse_code)
|
||
OR NOT (NEW.destination_warehouse_name <=> OLD.destination_warehouse_name)
|
||
OR NOT (NEW.sku_code <=> OLD.sku_code)
|
||
OR NOT (NEW.product_name_zh <=> OLD.product_name_zh)
|
||
OR NOT (NEW.product_name_en <=> OLD.product_name_en)
|
||
OR NOT (NEW.product_image_file_id <=> OLD.product_image_file_id)
|
||
OR NOT (NEW.product_image_url <=> OLD.product_image_url)
|
||
OR NOT (NEW.product_quantity <=> CASE WHEN OLD.units_per_carton IS NOT NULL THEN OLD.units_per_carton * NEW.package_count ELSE ROUND(CAST(OLD.product_quantity AS DECIMAL(30,6)) * NEW.package_count / NULLIF(OLD.package_count,0),0) END)
|
||
OR NOT (NEW.units_per_carton <=> OLD.units_per_carton)
|
||
OR NOT (NEW.pallet_count <=> CEIL(CAST(COALESCE(OLD.pallet_count,0) AS DECIMAL(30,6)) * NEW.package_count / NULLIF(OLD.package_count,0)))
|
||
OR NOT (NEW.gross_weight_kg <=> ROUND(OLD.gross_weight_kg * NEW.package_count / NULLIF(OLD.package_count,0),3))
|
||
OR NOT (NEW.net_weight_kg <=> ROUND(OLD.net_weight_kg * NEW.package_count / NULLIF(OLD.package_count,0),3))
|
||
OR NOT (NEW.dimensional_weight_kg <=> ROUND(OLD.dimensional_weight_kg * NEW.package_count / NULLIF(OLD.package_count,0),3))
|
||
OR NOT (NEW.volume_cbm <=> ROUND(OLD.volume_cbm * NEW.package_count / NULLIF(OLD.package_count,0),4))
|
||
OR NOT (NEW.fba_tracking_no <=> OLD.fba_tracking_no)
|
||
OR NOT (NEW.shipment_no <=> OLD.shipment_no)
|
||
OR NOT (NEW.shipping_mark <=> OLD.shipping_mark)
|
||
OR NOT (NEW.load_sequence <=> OLD.load_sequence)
|
||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||
OR NOT (NEW.removed_by_name <=> OLD.removed_by_name)
|
||
OR NOT (NEW.removed_at <=> OLD.removed_at) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有PDA拣货记录的装柜明细仅允许按冻结分配比例收敛实际装载';
|
||
END IF;
|
||
END IF;
|
||
IF OLD.frozen_at IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后的明细永久冻结';
|
||
END IF;
|
||
IF OLD.deleted = 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已删除装柜明细不可恢复或修改';
|
||
END IF;
|
||
IF NEW.loading_plan_id <> OLD.loading_plan_id OR NEW.account_id <> OLD.account_id
|
||
OR NEW.inbound_detail_id <> OLD.inbound_detail_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜明细归属键不可修改,请删除后重新分配';
|
||
END IF;
|
||
IF NEW.deleted = 1 THEN
|
||
IF NEW.removed_by IS NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '删除装柜明细必须记录操作人';
|
||
END IF;
|
||
SET NEW.removed_at = COALESCE(NEW.removed_at, CURRENT_TIMESTAMP(3));
|
||
ELSEIF NEW.removed_by IS NOT NULL OR NEW.removed_by_name IS NOT NULL OR NEW.removed_at IS NOT NULL THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '删除审计字段只能在逻辑删除时写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_task_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_task_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_task
|
||
FOR EACH ROW
|
||
BEGIN
|
||
IF OLD.task_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已完成PDA拣货任务不可修改';
|
||
END IF;
|
||
IF NEW.loading_plan_id <> OLD.loading_plan_id
|
||
OR NOT (NEW.container_booking_id <=> OLD.container_booking_id)
|
||
OR NOT (NEW.cabinet_no <=> OLD.cabinet_no) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务稳定归属不可修改';
|
||
END IF;
|
||
IF NOT ((OLD.task_status = 'LOADING' AND NEW.task_status IN ('LOADING','COMPLETING'))
|
||
OR (OLD.task_status = 'COMPLETING' AND NEW.task_status IN ('COMPLETING','COMPLETION_FAILED','COMPLETED'))
|
||
OR (OLD.task_status = 'COMPLETION_FAILED' AND NEW.task_status = 'COMPLETING')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货任务状态流转非法';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_item_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_item_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_task_status VARCHAR(16);
|
||
SELECT MAX(task_status) INTO current_task_status
|
||
FROM fl_loading_pick_task WHERE id = OLD.pick_task_id;
|
||
IF current_task_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后PDA拣货明细不可修改';
|
||
END IF;
|
||
IF NEW.pick_task_id <> OLD.pick_task_id
|
||
OR NEW.loading_plan_item_id <> OLD.loading_plan_item_id
|
||
OR NEW.inbound_id <> OLD.inbound_id
|
||
OR NEW.inbound_detail_id <> OLD.inbound_detail_id THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货明细归属不可修改';
|
||
END IF;
|
||
IF NEW.item_status = 'PARTIAL' AND NOT (
|
||
OLD.item_status IN ('PENDING', 'PICKING')
|
||
AND OLD.picked_package_count < NEW.picked_package_count
|
||
AND NEW.picked_package_count < OLD.planned_package_count
|
||
AND NEW.planned_package_count = OLD.planned_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '部分拣货必须在计划箱数内增加实际拣出箱数';
|
||
END IF;
|
||
IF NEW.item_status = 'SKIPPED' AND NOT (
|
||
OLD.item_status = 'PENDING'
|
||
AND OLD.picked_package_count = 0
|
||
AND NEW.picked_package_count = 0
|
||
AND NEW.planned_package_count = OLD.planned_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '跳拣仅允许零拣货的待拣明细';
|
||
END IF;
|
||
IF OLD.item_status = 'PARTIAL' AND NEW.item_status = 'PICKED' AND NOT (
|
||
NEW.picked_package_count = OLD.picked_package_count
|
||
AND NEW.planned_package_count = OLD.picked_package_count
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '部分拣货收敛后计划箱数必须等于实际拣出箱数';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_item_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_item_before_delete
|
||
BEFORE DELETE ON fl_loading_pick_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_task_status VARCHAR(16);
|
||
SELECT MAX(task_status) INTO current_task_status
|
||
FROM fl_loading_pick_task WHERE id = OLD.pick_task_id;
|
||
IF current_task_status = 'COMPLETED' OR OLD.picked_package_count > 0 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已拣货或已完成的PDA明细不可删除';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_history_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_history_before_update
|
||
BEFORE UPDATE ON fl_loading_pick_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货历史不可修改';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_pick_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_pick_history_before_delete
|
||
BEFORE DELETE ON fl_loading_pick_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'PDA拣货历史不可删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_item_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_item_before_delete
|
||
BEFORE DELETE ON fl_loading_plan_item
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜明细只允许推送前逻辑删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_insert;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_insert
|
||
BEFORE INSERT ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = NEW.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED'
|
||
AND NEW.document_type NOT IN ('DESTINATION_CLEARANCE', 'DELIVERY_ORDER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可新增';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_update
|
||
BEFORE UPDATE ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = OLD.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED'
|
||
AND OLD.document_type NOT IN ('DESTINATION_CLEARANCE', 'DELIVERY_ORDER') THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可修改';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_container_document_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_container_document_before_delete
|
||
BEFORE DELETE ON fl_container_document
|
||
FOR EACH ROW
|
||
BEGIN
|
||
DECLARE current_plan_status VARCHAR(24);
|
||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan
|
||
WHERE container_booking_id = OLD.container_booking_id;
|
||
IF current_plan_status = 'COMPLETED' THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜文件不可删除';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_history_before_update;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_history_before_update
|
||
BEFORE UPDATE ON fl_loading_plan_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜操作历史不可修改';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
DROP TRIGGER IF EXISTS trg_loading_history_before_delete;
|
||
DELIMITER $$
|
||
CREATE TRIGGER trg_loading_history_before_delete
|
||
BEFORE DELETE ON fl_loading_plan_history
|
||
FOR EACH ROW
|
||
BEGIN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜操作历史不可删除';
|
||
END$$
|
||
DELIMITER ;
|
||
|
||
-- 三期智能装柜、预装清单和装柜清单平台权限;与一期平台根菜单幂等合并。
|
||
USE baiyue_iam;
|
||
DROP PROCEDURE IF EXISTS relocate_legacy_inbound_loading_permissions;
|
||
DELIMITER $$
|
||
CREATE PROCEDURE relocate_legacy_inbound_loading_permissions()
|
||
BEGIN
|
||
DECLARE legacy_count INT DEFAULT 0;
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE id IN (5102091, 5102092, 5102093, 5102094, 5102096, 5102097, 5102098, 5102099)
|
||
AND permission_code NOT IN (
|
||
'platform:firstleg:container:view', 'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:smart-loading:push',
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
)
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期权限迁移失败:5102091-5102099 存在未知权限占用';
|
||
END IF;
|
||
SELECT COUNT(*) INTO legacy_count
|
||
FROM iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||
AND permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
IF legacy_count > 0 THEN
|
||
IF EXISTS (
|
||
SELECT 1 FROM iam_permission
|
||
WHERE id BETWEEN 5102121 AND 5102129
|
||
AND permission_code <> CASE id
|
||
WHEN 5102121 THEN 'firstleg:inbound:split'
|
||
WHEN 5102122 THEN 'firstleg:inbound:preload'
|
||
WHEN 5102123 THEN 'firstleg:inbound:preload-view'
|
||
WHEN 5102124 THEN 'platform:firstleg:inbound:loading:edit'
|
||
WHEN 5102125 THEN 'platform:firstleg:inbound:loading:view'
|
||
WHEN 5102126 THEN 'platform:firstleg:inbound:loading:complete'
|
||
WHEN 5102127 THEN 'platform:firstleg:inbound:delivery:complete'
|
||
WHEN 5102128 THEN 'platform:firstleg:inbound:picking:edit'
|
||
WHEN 5102129 THEN 'platform:firstleg:inbound:picking:view'
|
||
END
|
||
) THEN
|
||
SIGNAL SQLSTATE '45000'
|
||
SET MESSAGE_TEXT = '三期权限迁移失败:5102121-5102129 已被其他权限占用';
|
||
END IF;
|
||
DROP TEMPORARY TABLE IF EXISTS tmp_phase3_inbound_role_grant;
|
||
CREATE TEMPORARY TABLE tmp_phase3_inbound_role_grant (
|
||
role_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
permission_code VARCHAR(128) NOT NULL,
|
||
PRIMARY KEY (role_id, tenant_id, permission_code)
|
||
) ENGINE=MEMORY;
|
||
INSERT IGNORE INTO tmp_phase3_inbound_role_grant (role_id, tenant_id, permission_code)
|
||
SELECT role_permission.role_id, role_permission.tenant_id, permission.permission_code
|
||
FROM iam_role_permission role_permission
|
||
JOIN iam_permission permission ON permission.id = role_permission.permission_id
|
||
WHERE permission.permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
DELETE role_permission FROM iam_role_permission role_permission
|
||
JOIN iam_permission permission ON permission.id = role_permission.permission_id
|
||
WHERE permission.permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
DELETE FROM iam_permission WHERE permission_code IN (
|
||
'firstleg:inbound:split', 'firstleg:inbound:preload', 'firstleg:inbound:preload-view',
|
||
'platform:firstleg:inbound:loading:edit', 'platform:firstleg:inbound:loading:view',
|
||
'platform:firstleg:inbound:loading:complete', 'platform:firstleg:inbound:delivery:complete',
|
||
'platform:firstleg:inbound:picking:edit', 'platform:firstleg:inbound:picking:view'
|
||
);
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
|
||
VALUES
|
||
(5102121, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:split', '智能分柜', NULL, NULL, 140, 'ACTIVE', 1, 0),
|
||
(5102122, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:preload', '编辑并确认预装清单', NULL, NULL, 150, 'ACTIVE', 1, 0),
|
||
(5102123, 0, 'PLATFORM', 'BUTTON', 5102030, 'firstleg:inbound:preload-view', '查看预装清单', NULL, NULL, 160, 'ACTIVE', 1, 0),
|
||
(5102124, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:edit', '维护装柜证据', NULL, NULL, 170, 'ACTIVE', 1, 0),
|
||
(5102125, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:view', '查看装柜证据', NULL, NULL, 180, 'ACTIVE', 1, 0),
|
||
(5102126, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:loading:complete', '完成装柜', NULL, NULL, 190, 'ACTIVE', 1, 0),
|
||
(5102127, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:delivery:complete', '完成派送', NULL, NULL, 200, 'ACTIVE', 1, 0),
|
||
(5102128, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:picking:edit', '执行拣货', NULL, NULL, 210, 'ACTIVE', 1, 0),
|
||
(5102129, 0, 'PLATFORM', 'BUTTON', 5102030, 'platform:firstleg:inbound:picking:view', '查看拣货任务', NULL, NULL, 220, 'ACTIVE', 1, 0);
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT grant_row.role_id, permission.id, grant_row.tenant_id
|
||
FROM tmp_phase3_inbound_role_grant grant_row
|
||
JOIN iam_permission permission ON permission.permission_code = grant_row.permission_code
|
||
AND permission.tenant_id = 0 AND permission.app_code = 'PLATFORM';
|
||
DROP TEMPORARY TABLE tmp_phase3_inbound_role_grant;
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL relocate_legacy_inbound_loading_permissions();
|
||
DROP PROCEDURE relocate_legacy_inbound_loading_permissions;
|
||
|
||
DELETE role_permission FROM iam_role_permission role_permission
|
||
JOIN iam_permission permission ON permission.id = role_permission.permission_id
|
||
WHERE permission.tenant_id = 0 AND permission.permission_code IN (
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:push');
|
||
DELETE FROM iam_permission WHERE tenant_id = 0 AND permission_code IN (
|
||
'platform:firstleg:smart-loading:create', 'platform:firstleg:smart-loading:push');
|
||
|
||
INSERT INTO iam_permission
|
||
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
|
||
VALUES
|
||
(5102090, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:container:list',
|
||
'货柜', '/first-leg/containers', 'GET', 40, 'ACTIVE', 1, 0),
|
||
(5102091, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:view',
|
||
'查看货柜', NULL, 'GET', 10, 'ACTIVE', 1, 0),
|
||
(5102092, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:create',
|
||
'建立货柜', NULL, 'POST', 20, 'ACTIVE', 1, 0),
|
||
(5102093, 0, 'PLATFORM', 'BUTTON', 5102090, 'platform:firstleg:container:edit',
|
||
'编辑货柜', NULL, 'PUT', 30, 'ACTIVE', 1, 0),
|
||
(5102094, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:smart-loading:list',
|
||
'智能装柜', '/first-leg/smart-loading', 'GET', 50, 'ACTIVE', 1, 0),
|
||
(5102097, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:assign',
|
||
'分配装柜明细', NULL, 'POST', 30, 'ACTIVE', 1, 0),
|
||
(5102098, 0, 'PLATFORM', 'BUTTON', 5102094, 'platform:firstleg:smart-loading:delete',
|
||
'删除装柜明细', NULL, 'DELETE', 40, 'ACTIVE', 1, 0),
|
||
(5102100, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:preload:list',
|
||
'预装清单', '/first-leg/preload-list', 'GET', 60, 'ACTIVE', 1, 0),
|
||
(5102102, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:view',
|
||
'查看预装详情', NULL, 'GET', 20, 'ACTIVE', 1, 0),
|
||
(5102103, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:edit',
|
||
'编辑预装运输字段', NULL, 'PUT', 30, 'ACTIVE', 1, 0),
|
||
(5102104, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:complete',
|
||
'完成预装', NULL, 'POST', 40, 'ACTIVE', 1, 0),
|
||
(5102105, 0, 'PLATFORM', 'BUTTON', 5102100, 'platform:firstleg:preload:confirm',
|
||
'确认预装', NULL, 'POST', 50, 'ACTIVE', 1, 0),
|
||
(5102106, 0, 'PLATFORM', 'MENU', 5102000, 'platform:firstleg:loading-list:list',
|
||
'装柜清单', '/first-leg/loading-list', 'GET', 70, 'ACTIVE', 1, 0),
|
||
(5102108, 0, 'PLATFORM', 'BUTTON', 5102106, 'platform:firstleg:loading-list:view',
|
||
'查看装柜详情', NULL, 'GET', 20, 'ACTIVE', 1, 0)
|
||
ON DUPLICATE KEY UPDATE
|
||
permission_type = VALUES(permission_type), parent_id = VALUES(parent_id),
|
||
permission_name = VALUES(permission_name), route_path = VALUES(route_path),
|
||
http_method = VALUES(http_method), sort_no = VALUES(sort_no), status = 'ACTIVE',
|
||
built_in = 1, deleted = 0;
|
||
|
||
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||
SELECT role.id, permission.id, role.tenant_id
|
||
FROM iam_role role
|
||
JOIN iam_permission permission
|
||
ON permission.tenant_id = 0
|
||
AND permission.permission_code IN (
|
||
'platform:firstleg:container:list',
|
||
'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:assign', 'platform:firstleg:smart-loading:delete',
|
||
'platform:firstleg:preload:list',
|
||
'platform:firstleg:preload:edit', 'platform:firstleg:preload:complete',
|
||
'platform:firstleg:preload:confirm', 'platform:firstleg:loading-list:list')
|
||
WHERE role.tenant_id = 0
|
||
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR');
|
||
|
||
USE baiyue_firstleg;
|
||
|
||
DROP PROCEDURE IF EXISTS assert_phase3_smart_loading_schema;
|
||
|
||
DELETE rp FROM baiyue_iam.iam_role_permission rp
|
||
JOIN baiyue_iam.iam_permission p ON p.tenant_id=rp.tenant_id AND p.id=rp.permission_id
|
||
WHERE p.permission_code IN ('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view');
|
||
UPDATE baiyue_iam.iam_permission SET status='INACTIVE',deleted=1
|
||
WHERE permission_code IN ('platform:firstleg:container:view','platform:firstleg:preload:view','platform:firstleg:loading-list:view');
|
||
DELIMITER $$
|
||
CREATE PROCEDURE assert_phase3_smart_loading_schema()
|
||
BEGIN
|
||
IF (SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name IN ('fl_container_booking', 'fl_loading_plan', 'fl_loading_plan_item',
|
||
'fl_container_document', 'fl_loading_plan_history',
|
||
'fl_loading_pick_task', 'fl_loading_pick_item',
|
||
'fl_loading_pick_history')) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:核心表未完整创建';
|
||
END IF;
|
||
IF EXISTS (SELECT 1 FROM fl_loading_plan
|
||
WHERE plan_status NOT IN ('EDITING', 'LOADING', 'COMPLETED')) THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:存在旧装柜状态';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.check_constraints
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND constraint_name = 'chk_loading_plan_status'
|
||
AND UPPER(check_clause) LIKE '%EDITING%'
|
||
AND UPPER(check_clause) LIKE '%LOADING%'
|
||
AND UPPER(check_clause) LIKE '%COMPLETED%') <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:装柜状态约束不是最终三状态';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_container_booking'
|
||
AND column_name IN ('cabinet_no', 'warehouse_id', 'bill_of_lading_no',
|
||
'vessel_name', 'voyage_no', 'container_type', 'capacity_cbm',
|
||
'max_weight_kg', 'terminal_trucking_company_id', 'terminal_trucking_company')) <> 10 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:货柜原型字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_plan_item'
|
||
AND column_name IN ('account_id', 'customer_name', 'active_inbound_detail_id', 'frozen_at',
|
||
'destination_warehouse_name', 'product_name_zh', 'product_name_en',
|
||
'product_image_file_id', 'pallet_count', 'net_weight_kg',
|
||
'dimensional_weight_kg', 'product_quantity', 'units_per_carton',
|
||
'unit_type', 'pallet_id', 'pallet_no', 'indivisible', 'active_assignment_key',
|
||
'fba_tracking_no', 'shipment_no', 'shipping_mark', 'version')) <> 22 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:明细归属、清单快照或冻结字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_plan_item'
|
||
AND index_name = 'uk_loading_item_plan_assignment' AND non_unique = 0) <> 2 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:计划内唯一活动明细锁缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_item'
|
||
AND column_name IN ('pick_task_id', 'loading_plan_item_id', 'inbound_id',
|
||
'inbound_detail_id', 'planned_package_count',
|
||
'picked_package_count', 'item_status', 'version')) <> 8 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:PDA拣货明细字段缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.statistics
|
||
WHERE table_schema = 'baiyue_firstleg' AND non_unique = 0
|
||
AND index_name IN ('uk_loading_pick_task_plan',
|
||
'uk_loading_pick_item_plan_item',
|
||
'uk_loading_pick_history_operation')) <> 3 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:PDA唯一索引缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_item'
|
||
AND column_name = 'pick_task_id'
|
||
AND referenced_table_name IS NOT NULL) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage outer_fk
|
||
WHERE outer_fk.constraint_schema = 'baiyue_firstleg'
|
||
AND outer_fk.table_name = 'fl_loading_pick_item'
|
||
AND outer_fk.column_name = 'pick_task_id'
|
||
AND outer_fk.referenced_table_name = 'fl_loading_pick_task'
|
||
AND outer_fk.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = outer_fk.constraint_schema
|
||
AND peer.table_name = outer_fk.table_name
|
||
AND peer.constraint_name = outer_fk.constraint_name) = 1) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:柜次PDA明细任务外键不唯一或指向错误';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.key_column_usage
|
||
WHERE constraint_schema = 'baiyue_firstleg'
|
||
AND table_name = 'fl_loading_pick_history'
|
||
AND column_name = 'pick_task_id'
|
||
AND referenced_table_name IS NOT NULL) <> 1
|
||
OR (SELECT COUNT(*) FROM information_schema.key_column_usage outer_fk
|
||
WHERE outer_fk.constraint_schema = 'baiyue_firstleg'
|
||
AND outer_fk.table_name = 'fl_loading_pick_history'
|
||
AND outer_fk.column_name = 'pick_task_id'
|
||
AND outer_fk.referenced_table_name = 'fl_loading_pick_task'
|
||
AND outer_fk.referenced_column_name = 'id'
|
||
AND (SELECT COUNT(*) FROM information_schema.key_column_usage peer
|
||
WHERE peer.constraint_schema = outer_fk.constraint_schema
|
||
AND peer.table_name = outer_fk.table_name
|
||
AND peer.constraint_name = outer_fk.constraint_name) = 1) <> 1 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:柜次PDA历史任务外键不唯一或指向错误';
|
||
END IF;
|
||
IF (SELECT COUNT(*) FROM information_schema.triggers
|
||
WHERE trigger_schema = 'baiyue_firstleg'
|
||
AND trigger_name IN ('trg_container_booking_before_update',
|
||
'trg_loading_plan_before_update', 'trg_loading_plan_after_update',
|
||
'trg_loading_item_before_insert', 'trg_loading_item_before_update',
|
||
'trg_loading_item_before_delete', 'trg_container_document_before_insert',
|
||
'trg_container_document_before_update', 'trg_container_document_before_delete',
|
||
'trg_loading_history_before_update', 'trg_loading_history_before_delete',
|
||
'trg_loading_pick_task_before_update',
|
||
'trg_loading_pick_item_before_update',
|
||
'trg_loading_pick_item_before_delete',
|
||
'trg_loading_pick_history_before_update',
|
||
'trg_loading_pick_history_before_delete')) <> 16 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:不可逆状态或冻结触发器缺失';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT permission_code) FROM baiyue_iam.iam_permission
|
||
WHERE tenant_id = 0 AND app_code = 'PLATFORM' AND status = 'ACTIVE' AND deleted = 0
|
||
AND permission_code IN (
|
||
'platform:firstleg:container:list',
|
||
'platform:firstleg:container:create', 'platform:firstleg:container:edit',
|
||
'platform:firstleg:smart-loading:list', 'platform:firstleg:smart-loading:assign',
|
||
'platform:firstleg:smart-loading:delete', 'platform:firstleg:preload:list',
|
||
'platform:firstleg:preload:edit',
|
||
'platform:firstleg:preload:complete', 'platform:firstleg:preload:confirm',
|
||
'platform:firstleg:loading-list:list')) <> 11 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:平台权限资源未完整创建';
|
||
END IF;
|
||
IF (SELECT COUNT(DISTINCT CONCAT(role.role_code, ':', permission.permission_code))
|
||
FROM baiyue_iam.iam_role role
|
||
JOIN baiyue_iam.iam_role_permission grant_row ON grant_row.role_id = role.id
|
||
JOIN baiyue_iam.iam_permission permission ON permission.id = grant_row.permission_id
|
||
WHERE role.tenant_id = 0 AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')
|
||
AND permission.tenant_id = 0
|
||
AND permission.permission_code IN (
|
||
'platform:firstleg:container:list',
|
||
'platform:firstleg:container:create',
|
||
'platform:firstleg:container:edit', 'platform:firstleg:smart-loading:list',
|
||
'platform:firstleg:smart-loading:assign', 'platform:firstleg:smart-loading:delete',
|
||
'platform:firstleg:preload:list',
|
||
'platform:firstleg:preload:edit', 'platform:firstleg:preload:complete',
|
||
'platform:firstleg:preload:confirm', 'platform:firstleg:loading-list:list')) <> 22 THEN
|
||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:平台角色授权未完整写入';
|
||
END IF;
|
||
END$$
|
||
DELIMITER ;
|
||
CALL assert_phase3_smart_loading_schema();
|
||
DROP PROCEDURE IF EXISTS assert_phase3_smart_loading_schema;
|