diff --git a/backend/migrations/2026_04_17_100000_refactor_skus_mapping_constraints.php b/backend/migrations/2026_04_17_100000_refactor_skus_mapping_constraints.php new file mode 100644 index 0000000..b9928e6 --- /dev/null +++ b/backend/migrations/2026_04_17_100000_refactor_skus_mapping_constraints.php @@ -0,0 +1,85 @@ +after() + Schema::table('skus_mapping', function (Blueprint $table) { + $table->boolean('bundled')->default(false)->comment('是否为组合商品映射'); + }); + + // 2. 回填 origin_sku_id:通过 (origin_sku, company_id) 关联 skus_origin + // 预部署阶段:若回填后仍存在 NULL(孤儿记录),后续 SET NOT NULL 会失败, + // 由开发者手动清理;此处不做静默兜底 + Db::statement(' + UPDATE skus_mapping sm + SET origin_sku_id = so.id + FROM skus_origin so + WHERE sm.origin_sku_id IS NULL + AND so.sku = sm.origin_sku + AND so.company_id = sm.company_id + '); + + // 3. 收紧 origin_sku_id 为 NOT NULL + Db::statement('ALTER TABLE skus_mapping ALTER COLUMN origin_sku_id SET NOT NULL'); + + // 4. 回填 platform_outer_sku:NULL 或空串回落到 origin_sku(保持最小可行映射值) + Db::statement(" + UPDATE skus_mapping + SET platform_outer_sku = origin_sku + WHERE platform_outer_sku IS NULL OR platform_outer_sku = '' + "); + + // 5. 收紧 platform_outer_sku 为 NOT NULL + Db::statement('ALTER TABLE skus_mapping ALTER COLUMN platform_outer_sku SET NOT NULL'); + + // 6. 替换唯一索引:uk_platform_product → uk_origin_platform_sku + // 旧索引形态:CREATE UNIQUE INDEX uk_platform_product ... WHERE platform_product_id IS NOT NULL + // 因此必须用 DROP INDEX(兼容 constraint 形态的历史数据用 IF EXISTS 两边兜底) + Db::statement('ALTER TABLE skus_mapping DROP CONSTRAINT IF EXISTS uk_platform_product'); + Db::statement('DROP INDEX IF EXISTS uk_platform_product'); + + Db::statement(' + CREATE UNIQUE INDEX uk_origin_platform_sku + ON skus_mapping (origin_sku_id, platform_id, platform_outer_sku) + '); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // 反向顺序:删新索引 → 恢复旧索引 → 放宽 outer → 放宽 origin_sku_id → 删除 bundled + Db::statement('DROP INDEX IF EXISTS uk_origin_platform_sku'); + + // 恢复旧索引(保留 2026_04_14_120000 中的 WHERE 子句形态) + // DROP IF EXISTS 兜底:若 up 半途失败使旧索引未被 drop,此 down 仍能幂等恢复 + Db::statement('ALTER TABLE skus_mapping DROP CONSTRAINT IF EXISTS uk_platform_product'); + Db::statement('DROP INDEX IF EXISTS uk_platform_product'); + Db::statement(' + CREATE UNIQUE INDEX uk_platform_product + ON skus_mapping (platform_id, platform_product_id) + WHERE platform_product_id IS NOT NULL + '); + + Db::statement('ALTER TABLE skus_mapping ALTER COLUMN platform_outer_sku DROP NOT NULL'); + Db::statement('ALTER TABLE skus_mapping ALTER COLUMN origin_sku_id DROP NOT NULL'); + + Schema::table('skus_mapping', function (Blueprint $table) { + $table->dropColumn('bundled'); + }); + } +};