-- ============================================================================ -- 老「厂家/出货单/授权码」结构 → 新「品牌商/生产批次/授权码(batchno)」结构 -- -- 只动 starpivot 库(广州测试环境)。Supabase 源库只读,不受影响。 -- 全程一个事务;幂等,可重复执行(靠 production_batch.remark 里的老批次号做锚点)。 -- -- factory → brand (id 沿用,providerid=0 待后台分配) -- factory_deliverynote → production_batch(并上授权码里实际存在的批次) -- license_b* → 加 brandid / batchno 并回填(保留 factoryid、probatch 作对照) -- product → factoryid 改名 brandid -- factory_public_code → factoryid 改名 brandid -- 老主表 license 不动(新代码只读分表) -- ============================================================================ \set ON_ERROR_STOP on BEGIN; -- ── 批次短码生成:与 comm.GeneratePublicCode 同字符集/长度(6 位,去掉易混字符)── CREATE OR REPLACE FUNCTION _mig_gen_batchno() RETURNS varchar(16) AS $fn$ DECLARE charset text := 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; code text; i int; BEGIN LOOP code := ''; FOR i IN 1..6 LOOP code := code || substr(charset, 1 + floor(random() * 32)::int, 1); END LOOP; IF NOT EXISTS (SELECT 1 FROM production_batch WHERE batchno = code) THEN RETURN code; END IF; END LOOP; END $fn$ LANGUAGE plpgsql; -- ── 1. 方案商:先建三条备用,品牌商暂不关联(providerid=0,后台手工分配)── INSERT INTO solution_provider (name, description, contactmails, createtime) SELECT v.name, v.descr, '', extract(epoch from now())::bigint FROM (VALUES ('jieli', '杰理方案'), ('lanxun', '蓝讯方案'), ('henxuan', '恒玄方案')) AS v(name, descr) WHERE NOT EXISTS (SELECT 1 FROM solution_provider s WHERE s.name = v.name); -- ── 2. 厂家 → 品牌商(id 保持不变,这样所有 factoryid 引用无需换值)── -- factory.probatch 是老批次计数器,新结构无对应字段,丢弃。 -- ⚠️ factory.factoryflag 不要丢:它就是方案商 ID(十六进制,如 ABEF/ABF0~ABF4/AB21), -- providerid 由它解析而来 —— 见 2026-07-31-solution-provider-vendor.sql,本脚本先置 0。 INSERT INTO brand (id, brand, providerid, description, contactmails, productlists) SELECT f.id, f.factory, 0, COALESCE(f.description, ''), COALESCE(f.contactmails, ''), COALESCE(f.productlists, '') FROM factory f WHERE NOT EXISTS (SELECT 1 FROM brand b WHERE b.id = f.id); SELECT setval('brand_id_seq', GREATEST((SELECT COALESCE(MAX(id), 0) FROM brand), 1)); -- ── 3. 汇总历史批次 = 授权码里实际存在的 ∪ 出货单里登记的 ── CREATE TEMP TABLE _mig_batch ( brandid bigint, productid bigint, probatch bigint, serialstart bigint, serialend bigint, startmac text, endmac text, devicenum bigint, ts bigint, src text, batchno varchar(16) ) ON COMMIT DROP; -- 3a. 来源 A:逐张 license_b* 分表按 (productid, probatch) 聚合 —— 授权码的真实归属。 -- MAC 区间按 number 排序取首尾(不能用 MIN/MAX:那是字典序,与序号顺序未必一致)。 DO $$ DECLARE r record; BEGIN FOR r IN SELECT table_name t FROM information_schema.tables WHERE table_schema = 'public' AND table_name ~ '^license_b' ORDER BY table_name LOOP EXECUTE format($q$ INSERT INTO _mig_batch (brandid, productid, probatch, serialstart, serialend, startmac, endmac, devicenum, ts, src) SELECT COALESCE(MAX(factoryid), 0), productid, COALESCE(probatch, 0), MIN(number), MAX(number), COALESCE((array_agg(devicemac ORDER BY number) FILTER (WHERE COALESCE(devicemac,'') <> ''))[1], ''), COALESCE((array_agg(devicemac ORDER BY number DESC) FILTER (WHERE COALESCE(devicemac,'') <> ''))[1], ''), COUNT(*), COALESCE(MIN(NULLIF(createtime, 0)), 0), 'authcode' FROM %I GROUP BY productid, COALESCE(probatch, 0) $q$, r.t); END LOOP; END $$; -- 3b. 来源 B:出货单里有、但授权码里没有的批次(productid=0 是脏数据,跳过) INSERT INTO _mig_batch (brandid, productid, probatch, serialstart, serialend, startmac, endmac, devicenum, ts, src) SELECT MIN(d.factoryid), d.productid, d.probatch, 0, 0, COALESCE(MIN(NULLIF(d.startmac, '')), ''), COALESCE(MAX(NULLIF(d.endmac, '')), ''), SUM(d.devicetnum), MIN(d.ts), 'deliverynote' FROM factory_deliverynote d WHERE d.productid <> 0 AND NOT EXISTS (SELECT 1 FROM _mig_batch m WHERE m.productid = d.productid AND m.probatch = d.probatch) GROUP BY d.productid, d.probatch; -- 3c. 授权码聚合出的批次,用出货单补齐缺失的 MAC 区间与生产时间 UPDATE _mig_batch m SET startmac = CASE WHEN m.startmac = '' THEN COALESCE(d.startmac, '') ELSE m.startmac END, endmac = CASE WHEN m.endmac = '' THEN COALESCE(d.endmac, '') ELSE m.endmac END, ts = CASE WHEN m.ts = 0 THEN COALESCE(d.ts, 0) ELSE m.ts END FROM (SELECT productid, probatch, MIN(NULLIF(startmac, '')) startmac, MAX(NULLIF(endmac, '')) endmac, MIN(ts) ts FROM factory_deliverynote WHERE productid <> 0 GROUP BY productid, probatch) d WHERE m.productid = d.productid AND m.probatch = d.probatch; -- ── 4. 给每个批次定短码 ── -- 重复执行时:已迁过的批次沿用原短码(按 remark 锚点找回),只有新批次才生成。 UPDATE _mig_batch m SET batchno = p.batchno FROM production_batch p WHERE p.productid = m.productid AND p.remark = format('迁移自老数据(%s):probatch=%s', m.src, m.probatch); UPDATE _mig_batch SET batchno = _mig_gen_batchno() WHERE batchno IS NULL; -- 批内撞码兜底(32^6 概率极低,但唯一索引会直接报错,这里先消掉) DO $$ BEGIN WHILE EXISTS (SELECT 1 FROM _mig_batch GROUP BY batchno HAVING count(*) > 1) LOOP UPDATE _mig_batch SET batchno = _mig_gen_batchno() WHERE ctid IN (SELECT max(ctid) FROM _mig_batch GROUP BY batchno HAVING count(*) > 1); END LOOP; END $$; -- ── 5. 写入 production_batch;remark 留老批次号,便于对账与重跑 ── INSERT INTO production_batch (ts, brandid, productid, serialstart, serialend, startmac, endmac, devicenum, batchno, remark) SELECT m.ts, m.brandid, m.productid, m.serialstart, m.serialend, m.startmac, m.endmac, m.devicenum, m.batchno, format('迁移自老数据(%s):probatch=%s', m.src, m.probatch) FROM _mig_batch m WHERE NOT EXISTS (SELECT 1 FROM production_batch p WHERE p.batchno = m.batchno); -- ── 6. 各 license 分表:加 brandid / batchno 并回填 ── -- factoryid、probatch 原样保留,作为对账依据、也留回滚余地。 DO $$ DECLARE r record; BEGIN FOR r IN SELECT table_name t FROM information_schema.tables WHERE table_schema = 'public' AND table_name ~ '^license_b' ORDER BY table_name LOOP EXECUTE format('ALTER TABLE %I ADD COLUMN IF NOT EXISTS brandid bigint', r.t); EXECUTE format('ALTER TABLE %I ADD COLUMN IF NOT EXISTS batchno varchar(16)', r.t); -- brandid 直接取老的 factoryid(品牌商 id 沿用了厂家 id,值不变) EXECUTE format('UPDATE %I SET brandid = factoryid WHERE brandid IS DISTINCT FROM factoryid', r.t); -- batchno 按 (productid, probatch) 从临时表关联(不依赖 remark 文本) EXECUTE format($q$ UPDATE %I l SET batchno = m.batchno FROM _mig_batch m WHERE m.productid = l.productid AND m.probatch = COALESCE(l.probatch, 0) AND l.batchno IS DISTINCT FROM m.batchno $q$, r.t); EXECUTE format('CREATE INDEX IF NOT EXISTS %I ON %I (brandid)', 'idx_' || r.t || '_brandid', r.t); EXECUTE format('CREATE INDEX IF NOT EXISTS %I ON %I (batchno)', 'idx_' || r.t || '_batchno', r.t); END LOOP; END $$; -- ── 7. product / factory_public_code:factoryid 改名 brandid(值不变)── DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='factoryid') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='brandid') THEN ALTER TABLE product RENAME COLUMN factoryid TO brandid; CREATE INDEX IF NOT EXISTS idx_product_brandid ON product (brandid); END IF; IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='factory_public_code' AND column_name='factoryid') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='factory_public_code' AND column_name='brandid') THEN ALTER TABLE factory_public_code RENAME COLUMN factoryid TO brandid; CREATE INDEX IF NOT EXISTS idx_factory_public_code_brandid ON factory_public_code (brandid); END IF; END $$; DROP FUNCTION IF EXISTS _mig_gen_batchno(); COMMIT;