-- ============================================================================= -- Merged price upgrade script -- Sources (in order): -- - unify_price_type_version.sql -- - prc_price_set_is_del_alter.sql -- - prc_price_set_product_effect_alter.sql -- - prc_price_excel_file.sql -- - prc_price_excel_psv_alter.sql -- - prc_order_bill_fail_snapshot_alter.sql -- - order_bill_query_menu.sql -- - order_bill_rebill_menu.sql -- Safe to re-run where original scripts used information_schema guards. -- Run once on target DB. Backup first if production. -- Generated: 2026-10-18 -- ============================================================================= SET NAMES utf8mb4; SET @db := DATABASE(); -- --------------------------------------------------------------------------- -- 1) unify PRODUCT / REDLINE / SALES into version + product/detail -- source: unify_price_type_version.sql -- --------------------------------------------------------------------------- -- Unify PRODUCT / REDLINE / SALES into prc_price_set_version + product/detail -- Run once on existing DB. Safe-ish: checks columns via information_schema patterns where needed. -- price_type: PRODUCT | REDLINE | SALES -- ========== 1) prc_price_set_version: price_type + soft FK ========== SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_version ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT产品价/REDLINE红线价/SALES销售价'' AFTER price_set_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'price_type' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; UPDATE prc_price_set_version SET price_type = 'SALES' WHERE price_type IS NULL OR price_type = ''; -- drop FK fk_psv_set if exists SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_version DROP FOREIGN KEY fk_psv_set', 'SELECT 1') FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND CONSTRAINT_NAME = 'fk_psv_set' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- price_set_id nullable ALTER TABLE prc_price_set_version MODIFY COLUMN price_set_id bigint(20) DEFAULT NULL COMMENT '价格套餐id(弱关联;PRODUCT/REDLINE为空)'; -- generated key for uniqueness (NULL price_set_id -> 0) SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_version ADD COLUMN price_set_id_key bigint(20) GENERATED ALWAYS AS (IFNULL(price_set_id,0)) STORED COMMENT ''唯一键辅助:空price_set_id视为0'' AFTER price_set_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'price_set_id_key' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- replace unique keys SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_version DROP INDEX uk_psv_set_no', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_set_no' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_version DROP INDEX uk_psv_set_seq', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_set_seq' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_version ADD UNIQUE KEY uk_psv_type_set_no (price_type, price_set_id_key, version_no)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_type_set_no' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_version ADD UNIQUE KEY uk_psv_type_set_seq (price_type, price_set_id_key, version_seq)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_type_set_seq' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_version ADD KEY idx_psv_type_eff (price_type, status, effective_time)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'idx_psv_type_eff' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ========== 2) prc_price_set_product: price_type, drop price_set_id ========== SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT/REDLINE/SALES'' AFTER psp_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'price_type' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; UPDATE prc_price_set_product psp INNER JOIN prc_price_set_version v ON v.psv_id = psp.psv_id SET psp.price_type = IFNULL(NULLIF(v.price_type,''), 'SALES') WHERE psp.price_type IS NULL OR psp.price_type = '' OR psp.price_type = 'SALES'; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP FOREIGN KEY fk_psp_set', 'SELECT 1') FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND CONSTRAINT_NAME = 'fk_psp_set' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP COLUMN price_set_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'price_set_id' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD KEY idx_psp_type_psv (price_type, psv_id)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'idx_psp_type_psv' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ========== 3) prc_price_set_detail: price_type, catalog cols, drop price_set_id ========== SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT/REDLINE/SALES'' AFTER psd_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'price_type' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; UPDATE prc_price_set_detail d INNER JOIN prc_price_set_version v ON v.psv_id = d.psv_id SET d.price_type = IFNULL(NULLIF(v.price_type,''), 'SALES') WHERE d.psv_id IS NOT NULL; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD COLUMN size_limit varchar(1000) DEFAULT NULL COMMENT ''尺寸限制'' AFTER fv_registration_fee', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'size_limit' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD COLUMN last_mile varchar(500) DEFAULT NULL COMMENT ''尾程派送说明'' AFTER size_limit', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'last_mile' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD COLUMN lead_time varchar(100) DEFAULT NULL COMMENT ''参考时效'' AFTER last_mile', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'lead_time' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_detail DROP FOREIGN KEY fk_psd_set', 'SELECT 1') FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND CONSTRAINT_NAME = 'fk_psd_set' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_detail DROP COLUMN price_set_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'price_set_id' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD KEY idx_psd_type_psv (price_type, psv_id)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_type_psv' LIMIT 1 ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- ========== 4) Migrate PRODUCT catalog -> version snapshot ========== -- Create one version per distinct effective_date group from active product rows (status in 0,1), -- collapsed into global versions ordered by effective_date. DROP TEMPORARY TABLE IF EXISTS tmp_product_version_map; CREATE TEMPORARY TABLE tmp_product_version_map ( old_product_id bigint(20) NOT NULL PRIMARY KEY, new_psv_id bigint(20) NOT NULL, new_psp_id bigint(20) DEFAULT NULL ); DROP TEMPORARY TABLE IF EXISTS tmp_product_eff; CREATE TEMPORARY TABLE tmp_product_eff AS SELECT DISTINCT effective_date FROM product WHERE IFNULL(is_del,'0') = '0' ORDER BY IFNULL(effective_date, '1970-01-01'); -- Build PRODUCT versions from distinct effective dates (empty catalog skipped) INSERT INTO prc_price_set_version ( price_set_id, price_type, version_no, version_seq, effective_time, expire_time, status, price_build_mode, created_by, create_time ) SELECT NULL, 'PRODUCT', CONCAT('V', (@seq := @seq + 1)), @seq, e.effective_date, NULL, CASE WHEN e.effective_date IS NOT NULL AND e.effective_date > NOW() THEN '0' ELSE '1' END, 'IMPORT', 'migrate', NOW() FROM tmp_product_eff e CROSS JOIN (SELECT @seq := IFNULL((SELECT MAX(version_seq) FROM prc_price_set_version WHERE price_type='PRODUCT' AND IFNULL(price_set_id,0)=0), 0)) s WHERE NOT EXISTS ( SELECT 1 FROM prc_price_set_version v WHERE v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0) = 0 ) ORDER BY IFNULL(e.effective_date, '1970-01-01'); -- If versions already exist (re-run), skip product migrate body when PRODUCT versions already present with products SET @product_migrated := ( SELECT COUNT(*) FROM prc_price_set_product WHERE price_type = 'PRODUCT' ); -- Chain expire_time for PRODUCT versions UPDATE prc_price_set_version v LEFT JOIN prc_price_set_version n ON n.price_type = 'PRODUCT' AND IFNULL(n.price_set_id,0)=0 AND n.version_seq = v.version_seq + 1 SET v.expire_time = n.effective_time, v.status = CASE WHEN n.psv_id IS NOT NULL AND n.effective_time IS NOT NULL AND n.effective_time <= NOW() THEN '2' WHEN v.effective_time IS NOT NULL AND v.effective_time > NOW() THEN '0' WHEN v.effective_time IS NULL OR v.effective_time <= NOW() THEN '1' ELSE v.status END WHERE v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0)=0; -- Map each product row to version by effective_date match (only if not yet migrated) INSERT INTO prc_price_set_product ( price_type, psv_id, product_code, product_name, service_level, cargo_attr, dest_region, update_note, tax_mode, source_product_id, source_sheet, create_time ) SELECT 'PRODUCT', v.psv_id, p.product_code, p.product_name, p.service_level, p.cargo_attr, p.dest_region, p.update_note, IFNULL(p.tax_mode, 'TAX_INCLUDED'), p.product_id, p.source_sheet, IFNULL(p.create_time, NOW()) FROM product p INNER JOIN prc_price_set_version v ON v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0)=0 AND ( (p.effective_date IS NULL AND v.effective_time IS NULL) OR p.effective_date = v.effective_time ) WHERE IFNULL(p.is_del,'0') = '0' AND @product_migrated = 0 AND NOT EXISTS ( SELECT 1 FROM prc_price_set_product x WHERE x.psv_id = v.psv_id AND x.product_code = p.product_code ); INSERT INTO tmp_product_version_map (old_product_id, new_psv_id, new_psp_id) SELECT p.product_id, psp.psv_id, psp.psp_id FROM product p INNER JOIN prc_price_set_product psp ON psp.price_type = 'PRODUCT' AND psp.source_product_id = p.product_id; INSERT INTO prc_price_set_detail ( price_type, psv_id, psp_id, product_code, product_name, country_code, country_name, zone_id, zone_code, zone_name, weight_label, weight_min, weight_max, product_country_id, fv_value, fv_registration_fee, size_limit, last_mile, lead_time, round_mode, effective_date, expire_date, create_time ) SELECT 'PRODUCT', m.new_psv_id, m.new_psp_id, c.product_code, p.product_name, c.country_code, c.country_name, c.zone_id, c.zone_code, c.zone_name, CASE WHEN c.weight_min IS NULL AND c.weight_max IS NULL THEN NULL ELSE CONCAT( IFNULL(TRIM(TRAILING '0' FROM TRIM(TRAILING '.' FROM CAST(c.weight_min AS CHAR))), ''), ' NOW() THEN '0' ELSE '1' END, 'IMPORT', 'migrate', NOW() FROM tmp_redline_eff e CROSS JOIN (SELECT @rseq := IFNULL((SELECT MAX(version_seq) FROM prc_price_set_version WHERE price_type='REDLINE' AND IFNULL(price_set_id,0)=0), 0)) s WHERE NOT EXISTS ( SELECT 1 FROM prc_price_set_version v WHERE v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0) = 0 ) ORDER BY IFNULL(e.effective_date, '1970-01-01'); SET @redline_migrated := ( SELECT COUNT(*) FROM prc_price_set_product WHERE price_type = 'REDLINE' ); UPDATE prc_price_set_version v LEFT JOIN prc_price_set_version n ON n.price_type = 'REDLINE' AND IFNULL(n.price_set_id,0)=0 AND n.version_seq = v.version_seq + 1 SET v.expire_time = n.effective_time, v.status = CASE WHEN n.psv_id IS NOT NULL AND n.effective_time IS NOT NULL AND n.effective_time <= NOW() THEN '2' WHEN v.effective_time IS NOT NULL AND v.effective_time > NOW() THEN '0' WHEN v.effective_time IS NULL OR v.effective_time <= NOW() THEN '1' ELSE v.status END WHERE v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0)=0; INSERT INTO prc_price_set_product ( price_type, psv_id, product_code, product_name, service_level, cargo_attr, dest_region, update_note, tax_mode, source_product_id, source_sheet, create_time ) SELECT 'REDLINE', v.psv_id, p.product_code, p.product_name, p.service_level, p.cargo_attr, p.dest_region, p.update_note, IFNULL(p.tax_mode, 'TAX_INCLUDED'), p.product_id, p.source_sheet, IFNULL(p.create_time, NOW()) FROM redline_product p INNER JOIN prc_price_set_version v ON v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0)=0 AND ( (p.effective_date IS NULL AND v.effective_time IS NULL) OR p.effective_date = v.effective_time ) WHERE IFNULL(p.is_del,'0') = '0' AND @redline_migrated = 0 AND NOT EXISTS ( SELECT 1 FROM prc_price_set_product x WHERE x.psv_id = v.psv_id AND x.product_code = p.product_code ); INSERT INTO tmp_redline_version_map (old_product_id, new_psv_id, new_psp_id) SELECT p.product_id, psp.psv_id, psp.psp_id FROM redline_product p INNER JOIN prc_price_set_product psp ON psp.price_type = 'REDLINE' AND psp.source_product_id = p.product_id; INSERT INTO prc_price_set_detail ( price_type, psv_id, psp_id, product_code, product_name, country_code, country_name, zone_id, zone_code, zone_name, weight_label, weight_min, weight_max, product_country_id, fv_value, fv_registration_fee, size_limit, last_mile, lead_time, round_mode, effective_date, expire_date, create_time ) SELECT 'REDLINE', m.new_psv_id, m.new_psp_id, c.product_code, p.product_name, c.country_code, c.country_name, c.zone_id, c.zone_code, c.zone_name, CASE WHEN c.weight_min IS NULL AND c.weight_max IS NULL THEN NULL ELSE CONCAT( IFNULL(TRIM(TRAILING '0' FROM TRIM(TRAILING '.' FROM CAST(c.weight_min AS CHAR))), ''), ' 0, 'ALTER TABLE prc_price_set_product DROP INDEX uk_psp_psv_code', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'uk_psp_psv_code' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD COLUMN active_product_code varchar(20) GENERATED ALWAYS AS (CASE WHEN IFNULL(is_del,''0'') = ''0'' THEN product_code ELSE NULL END) STORED COMMENT ''未删除时等于product_code,用于唯一约束''', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'active_product_code' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD UNIQUE KEY uk_psp_psv_active_code (psv_id, active_product_code)', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'uk_psp_psv_active_code' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- --------------------------------------------------------------------------- -- 3) product/detail effective_time expire_time status sync from version -- source: prc_price_set_product_effect_alter.sql -- --------------------------------------------------------------------------- -- product effective/expire time and status; detail status; detail dates keep time SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD COLUMN effective_time datetime DEFAULT NULL COMMENT ''生效时间'' AFTER tax_mode', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'effective_time' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD COLUMN expire_time datetime DEFAULT NULL COMMENT ''失效时间(空=长期)'' AFTER effective_time', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'expire_time' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_time', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'status' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- If status was added before expire_time, move status after expire_time when both exist SET @has_status := ( SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'status' ); SET @has_expire := ( SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'expire_time' ); SET @sql := ( SELECT IF(@has_status > 0 AND @has_expire > 0, 'ALTER TABLE prc_price_set_product MODIFY COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_time', 'SELECT 1') ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; ALTER TABLE prc_price_set_detail MODIFY COLUMN effective_date datetime DEFAULT NULL COMMENT '生效时间', MODIFY COLUMN expire_date datetime DEFAULT NULL COMMENT '失效时间(空=长期)'; SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_date', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'status' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; UPDATE prc_price_set_product p INNER JOIN prc_price_set_version v ON v.psv_id = p.psv_id SET p.effective_time = v.effective_time, p.expire_time = v.expire_time, p.status = v.status WHERE p.price_type IN ('PRODUCT', 'REDLINE') AND v.price_set_id IS NULL; UPDATE prc_price_set_detail d INNER JOIN prc_price_set_version v ON v.psv_id = d.psv_id SET d.effective_date = v.effective_time, d.expire_date = v.expire_time, d.status = v.status WHERE d.price_type IN ('PRODUCT', 'REDLINE') AND v.price_set_id IS NULL; -- --------------------------------------------------------------------------- -- 4) create prc_price_excel_file -- source: prc_price_excel_file.sql -- --------------------------------------------------------------------------- -- ------------------------------------------------------------ -- 导入价表 Excel 附件记录 -- 关联:prc_price_excel_file.psv_id -> prc_price_set_version.psv_id -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS prc_price_excel_file ( pef_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', excel_type varchar(20) NOT NULL COMMENT '价表类型 PRODUCT/REDLINE/SALES', psv_id bigint(20) DEFAULT NULL COMMENT '价格版本id prc_price_set_version.psv_id', effective_time datetime NOT NULL COMMENT '生效时间', file_name varchar(255) NOT NULL COMMENT '文件原始名称', file_path varchar(500) NOT NULL COMMENT '文件存储路径', file_size bigint(20) DEFAULT 0 COMMENT '文件大小(字节)', content_type varchar(100) DEFAULT '' COMMENT 'MIME', file_extension varchar(20) DEFAULT '' COMMENT '扩展名', oss_id varchar(64) DEFAULT NULL, sheet_names varchar(1000) DEFAULT NULL, remark varchar(500) DEFAULT NULL, created_by varchar(64) DEFAULT '', create_time datetime DEFAULT NULL, updated_by varchar(64) DEFAULT '', update_time datetime DEFAULT NULL, PRIMARY KEY (pef_id), KEY idx_pef_type_eff (excel_type, effective_time), KEY idx_pef_psv (psv_id), KEY idx_pef_create_time (create_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='导入价表Excel附件'; -- --------------------------------------------------------------------------- -- 5) excel_file.psv_id backfill + drop product.pef_id -- source: prc_price_excel_psv_alter.sql -- --------------------------------------------------------------------------- -- Move Excel attachment link: product.pef_id -> excel_file.psv_id SET @sql := ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_excel_file ADD COLUMN psv_id bigint(20) DEFAULT NULL COMMENT ''价格版本id prc_price_set_version.psv_id'' AFTER excel_type, ADD KEY idx_pef_psv (psv_id)', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_excel_file' AND COLUMN_NAME = 'psv_id' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @has_psp_pef := ( SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'pef_id' ); SET @sql := IF(@has_psp_pef > 0, 'UPDATE prc_price_excel_file f INNER JOIN (SELECT pef_id, MIN(psv_id) AS psv_id FROM prc_price_set_product WHERE pef_id IS NOT NULL GROUP BY pef_id) x ON x.pef_id = f.pef_id SET f.psv_id = IFNULL(f.psv_id, x.psv_id)', 'SELECT 1'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP INDEX idx_psp_pef_id', 'SELECT 1') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'idx_psp_pef_id' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql := ( SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP COLUMN pef_id', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'pef_id' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- --------------------------------------------------------------------------- -- 6) order bill fail snapshot + income refund_status -- source: prc_order_bill_fail_snapshot_alter.sql -- --------------------------------------------------------------------------- -- idempotent: order bill fail snapshot + income refund_status SET @sql = ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_order_bill ADD COLUMN error_message varchar(1000) NULL COMMENT \'失败原因\' AFTER billing_fail_status', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'error_message' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_order_bill ADD COLUMN order_status int NULL COMMENT \'上游订单状态\' AFTER order_time', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'order_status' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_order_bill ADD COLUMN bl_pickup tinyint NULL COMMENT \'是否收货:0否/1是\' AFTER order_status', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'bl_pickup' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_order_bill ADD COLUMN pickup_date datetime NULL COMMENT \'收货时间\' AFTER bl_pickup', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'pickup_date' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = ( SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_income_bill ADD COLUMN refund_status varchar(20) NOT NULL DEFAULT \'NONE\' COMMENT \'是否退货:NONE未退货/REFUNDED已退货\' AFTER income_total', 'SELECT 1') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_income_bill' AND COLUMN_NAME = 'refund_status' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; ALTER TABLE prc_order_bill MODIFY COLUMN product_code varchar(20) NULL COMMENT '产品代码', MODIFY COLUMN weight decimal(10,3) NULL COMMENT '计费重量KG', MODIFY COLUMN billing_fail_status tinyint NOT NULL DEFAULT 0 COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败。仅0/1/2/3写入应收主表,仅0写入应收费用'; -- --------------------------------------------------------------------------- -- 7) order bill query menu (2340/2341) -- source: order_bill_query_menu.sql -- --------------------------------------------------------------------------- -- Move order source back under system monitor, add read-only order bill query under billing center SET @monitorParent := ( SELECT menu_id FROM sys_menu WHERE menu_type = 'M' AND (path = 'monitor' OR menu_name = '系统监控') ORDER BY menu_id LIMIT 1 ); UPDATE sys_menu SET parent_id = @monitorParent, menu_name = '订单数据源', order_num = 9, path = 'orderSource', component = 'monitor/orderSource/index', route_name = 'OrderSource', visible = '0', status = '0', update_by = 'admin', update_time = sysdate() WHERE @monitorParent IS NOT NULL AND menu_type = 'C' AND (menu_id = 2325 OR perms = 'monitor:orderSource:list'); SET @billParent := ( SELECT parent_id FROM sys_menu WHERE menu_type = 'C' AND component = 'price/billing/bill' ORDER BY menu_id LIMIT 1 ); SET @orderBillMenuExists := ( SELECT COUNT(*) FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill:list' ); UPDATE sys_menu SET order_num = order_num + 1 WHERE @billParent IS NOT NULL AND @orderBillMenuExists = 0 AND parent_id = @billParent AND menu_type = 'C'; INSERT INTO sys_menu ( menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark ) SELECT 2340, '订单列表查询', @billParent, 1, 'orderBill', 'price/orderBill/index', '', 'OrderBillQuery', 1, 0, 'C', '0', '0', 'price:orderBill:list', 'list', 'admin', sysdate(), '查询 prc_order_bill 全部订单,只读' FROM DUAL WHERE @billParent IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill:list'); INSERT INTO sys_menu ( menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time ) SELECT 2341, '查询', 2340, 1, '', NULL, 1, 0, 'F', '0', '0', 'price:orderBill:list', '#', 'admin', sysdate() FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2341); INSERT INTO sys_role_menu (role_id, menu_id) SELECT 1, m.menu_id FROM (SELECT 2340 AS menu_id UNION SELECT 2341) m WHERE NOT EXISTS ( SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id ); INSERT INTO sys_role_menu (role_id, menu_id) SELECT DISTINCT rm.role_id, 2340 FROM sys_role_menu rm JOIN sys_menu src ON src.menu_id = rm.menu_id WHERE src.menu_type = 'C' AND src.component = 'price/billing/bill' AND NOT EXISTS ( SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2340 ); INSERT INTO sys_role_menu (role_id, menu_id) SELECT DISTINCT rm.role_id, 2341 FROM sys_role_menu rm WHERE rm.menu_id = 2340 AND NOT EXISTS ( SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2341 ); -- --------------------------------------------------------------------------- -- 8) order bill rebill menu (2342, depends on 2340) -- source: order_bill_rebill_menu.sql -- --------------------------------------------------------------------------- -- Order list query: rebill button, same upstream sync as order data source INSERT INTO sys_menu ( menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark ) SELECT 2342, '重新计费', 2340, 2, '', NULL, 1, 0, 'F', '0', '0', 'price:orderBill:rebill', '#', 'admin', sysdate(), '订单列表按单号或选中行重走上游计费' FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2342 OR perms = 'price:orderBill:rebill'); INSERT INTO sys_role_menu (role_id, menu_id) SELECT 1, 2342 FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu WHERE role_id = 1 AND menu_id = 2342); INSERT INTO sys_role_menu (role_id, menu_id) SELECT DISTINCT rm.role_id, 2342 FROM sys_role_menu rm WHERE rm.menu_id = 2340 AND NOT EXISTS ( SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2342 );