fix(db): 禁止修改已发布迁移并分批兼容旧库
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
-- 113 迁移前置兼容修复。
|
||||
--
|
||||
-- 113 已经进入生产,文件必须保持字节不变。旧快照库中存在两类历史脏数据,
|
||||
-- 会让原版 113 在“加新约束/唯一索引”时失败:
|
||||
-- 1. sales_daily_facts 仍有旧约束不接受的渠道值;
|
||||
-- 2. cost_exceptions 同一范围的旧行会生成重复 scope_key。
|
||||
--
|
||||
-- 本文件不是迁移,不写 schema_migrations。它只做幂等兼容准备:
|
||||
-- - 113 已执行时立即跳过;
|
||||
-- - 清理旧库数据,让随后执行的原版 113 可以通过。
|
||||
--
|
||||
-- 由 deploy/ubuntu-deploy.sh 在数据库迁移前自动执行。
|
||||
|
||||
DO $preflight$
|
||||
DECLARE
|
||||
v_has_113 BOOLEAN := FALSE;
|
||||
v_sales_has_mode BOOLEAN := FALSE;
|
||||
v_scope_duplicates BIGINT := 0;
|
||||
v_scope_missing BIGINT := 0;
|
||||
BEGIN
|
||||
IF to_regclass('public.schema_migrations') IS NOT NULL THEN
|
||||
EXECUTE $query$
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM public.schema_migrations migration
|
||||
WHERE to_jsonb(migration)->>'filename' = '113_finance_channel_cutover.sql'
|
||||
OR to_jsonb(migration)->>'name' = '113_finance_channel_cutover'
|
||||
)
|
||||
$query$ INTO v_has_113;
|
||||
END IF;
|
||||
|
||||
IF v_has_113 THEN
|
||||
RAISE NOTICE '113 已执行,跳过前置兼容修复';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
IF to_regclass('public.sales_daily_facts') IS NOT NULL THEN
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_attribute
|
||||
WHERE attrelid = 'public.sales_daily_facts'::regclass
|
||||
AND attname = 'fulfillment_mode'
|
||||
AND NOT attisdropped
|
||||
) INTO v_sales_has_mode;
|
||||
|
||||
IF v_sales_has_mode THEN
|
||||
EXECUTE $sql$
|
||||
ALTER TABLE public.sales_daily_facts
|
||||
DROP CONSTRAINT IF EXISTS chk_sales_daily_facts_fulfillment_mode
|
||||
$sql$;
|
||||
|
||||
EXECUTE $sql$
|
||||
UPDATE public.sales_daily_facts
|
||||
SET fulfillment_mode = CASE
|
||||
WHEN fulfillment_mode = 'legacy_platform' THEN 'archived'
|
||||
ELSE 'unclassified'
|
||||
END
|
||||
WHERE fulfillment_mode NOT IN (
|
||||
'work_order', 'affiliate_dash', 'third_party_voucher',
|
||||
'manual', 'unclassified', 'archived'
|
||||
)
|
||||
$sql$;
|
||||
|
||||
-- 迁移前临时使用新旧并集的约束;原版 113 会立即替换成最终约束。
|
||||
EXECUTE $sql$
|
||||
ALTER TABLE public.sales_daily_facts
|
||||
ADD CONSTRAINT chk_sales_daily_facts_fulfillment_mode
|
||||
CHECK (
|
||||
fulfillment_mode IN (
|
||||
'work_order', 'affiliate_dash', 'third_party_voucher',
|
||||
'manual', 'unclassified', 'archived'
|
||||
)
|
||||
)
|
||||
$sql$;
|
||||
|
||||
RAISE NOTICE 'sales_daily_facts 历史渠道值已兼容归一';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF to_regclass('public.cost_exceptions') IS NOT NULL THEN
|
||||
EXECUTE $sql$
|
||||
ALTER TABLE public.cost_exceptions
|
||||
ADD COLUMN IF NOT EXISTS scope_key TEXT,
|
||||
ADD COLUMN IF NOT EXISTS item_count INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS first_seen_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ
|
||||
$sql$;
|
||||
|
||||
EXECUTE $sql$
|
||||
WITH duplicate_scope_keys AS (
|
||||
SELECT scope_key
|
||||
FROM public.cost_exceptions
|
||||
WHERE status <> 'resolved'
|
||||
AND NULLIF(BTRIM(scope_key), '') IS NOT NULL
|
||||
GROUP BY scope_key
|
||||
HAVING COUNT(*) > 1
|
||||
)
|
||||
UPDATE public.cost_exceptions ce
|
||||
SET scope_key =
|
||||
COALESCE(NULLIF(BTRIM(ce.reason), ''), 'legacy')
|
||||
|| '|'
|
||||
|| COALESCE(ce.order_item_id::text, ce.subject_id::text, ce.id::text)
|
||||
|| '|legacy:'
|
||||
|| ce.id,
|
||||
first_seen_at = COALESCE(ce.first_seen_at, ce.created_at),
|
||||
last_seen_at = COALESCE(ce.last_seen_at, ce.created_at)
|
||||
WHERE ce.scope_key IS NULL
|
||||
OR BTRIM(ce.scope_key) = ''
|
||||
OR ce.first_seen_at IS NULL
|
||||
OR ce.last_seen_at IS NULL
|
||||
OR (
|
||||
ce.status <> 'resolved'
|
||||
AND ce.scope_key IN (SELECT scope_key FROM duplicate_scope_keys)
|
||||
)
|
||||
$sql$;
|
||||
|
||||
EXECUTE $sql$
|
||||
SELECT COUNT(*)
|
||||
FROM (
|
||||
SELECT scope_key
|
||||
FROM public.cost_exceptions
|
||||
WHERE status <> 'resolved'
|
||||
AND NULLIF(BTRIM(scope_key), '') IS NOT NULL
|
||||
GROUP BY scope_key
|
||||
HAVING COUNT(*) > 1
|
||||
) duplicate_scope_keys
|
||||
$sql$ INTO v_scope_duplicates;
|
||||
|
||||
EXECUTE $sql$
|
||||
SELECT COUNT(*)
|
||||
FROM public.cost_exceptions
|
||||
WHERE scope_key IS NULL OR BTRIM(scope_key) = ''
|
||||
$sql$ INTO v_scope_missing;
|
||||
|
||||
IF v_scope_duplicates > 0 THEN
|
||||
RAISE EXCEPTION
|
||||
'cost_exceptions 仍有 % 组重复 scope_key,113 无法创建唯一索引',
|
||||
v_scope_duplicates;
|
||||
END IF;
|
||||
|
||||
IF v_scope_missing > 0 THEN
|
||||
RAISE EXCEPTION
|
||||
'cost_exceptions 仍有 % 行 scope_key 为空,113 的唯一索引前置数据不完整',
|
||||
v_scope_missing;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'cost_exceptions 历史 scope_key 已保证唯一';
|
||||
END IF;
|
||||
END
|
||||
$preflight$;
|
||||
Reference in New Issue
Block a user