lsy-tenant-level.sql 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. -- 乐收赢去企业化(租户账号级)2026-09-22
  2. -- 手工执行: psycopg2 连接 payment_platform_java 库
  3. --
  4. -- 背景: 乐收赢与原有企业逻辑完全不相干,商户映射应按**租户账号**寻址。
  5. -- 本脚本让 pay_lsy_merchant 支持「租户级商户」—— enterprise_id 为空的那一行。
  6. -- 不新增列、不新增表;订单表 pay_lsy_order.enterprise_id 建表时已可空,本脚本不碰它。
  7. --
  8. -- ⚠ 第一条**不可逆**: NOT NULL 加不回来,除非先把所有 enterprise_id IS NULL 的行清掉
  9. -- 或补上企业 ID。执行前请确认可以接受。
  10. --
  11. -- ⚠ **先执行本脚本,再部署代码**(顺序不能反)。配套的代码提交删掉了 save 里
  12. -- 「企业 ID 不能为空」的 400 校验,而列还是 NOT NULL 的话,留空保存会从 400
  13. -- 变成 500(NOT NULL 违约落进 GlobalExceptionHandler 兜底)。
  14. -- 本脚本执行前,现有行为一切照旧(老调用方都带 enterprise_id)。
  15. -- 1. 允许租户级商户
  16. ALTER TABLE pay_lsy_merchant ALTER COLUMN enterprise_id DROP NOT NULL;
  17. -- 2. 租户级商户的唯一性
  18. -- 现有唯一索引 uk_lsy_merchant_tenant_ent 建在 (tenant_id, enterprise_id) 上,
  19. -- 而 PostgreSQL 唯一索引里 **NULL 互不相等** ⇒ 缺了下面这条局部索引,
  20. -- 同一个租户可以插入任意多行租户级商户,数据库一声不吭,
  21. -- 之后 requireByTenant 的 LIMIT 1 会随机返回其中一行。
  22. --
  23. -- 存量数据核对(2026-09-22 只读实测,非推断):全表 1 行
  24. -- id=1, tenant_id=2, enterprise_id='2088780645490698', mch_no='Y52211090004'
  25. -- 该行 enterprise_id **非空**,落在局部索引的谓词之外 ⇒ 不冲突,无需清理。
  26. -- 将来若真出现同租户多行租户级商户(只可能来自索引建成之前的窗口),先清理再建索引:
  27. -- DELETE FROM pay_lsy_merchant a USING pay_lsy_merchant b
  28. -- WHERE a.id < b.id AND a.tenant_id = b.tenant_id
  29. -- AND a.enterprise_id IS NULL AND b.enterprise_id IS NULL;
  30. CREATE UNIQUE INDEX IF NOT EXISTS uk_lsy_merchant_tenant_level
  31. ON pay_lsy_merchant (tenant_id)
  32. WHERE enterprise_id IS NULL;
  33. -- 3. 只读核对(执行后可手工跑一遍确认)
  34. -- SELECT is_nullable FROM information_schema.columns
  35. -- WHERE table_name='pay_lsy_merchant' AND column_name='enterprise_id'; -- 期望 YES
  36. -- SELECT indexname FROM pg_indexes WHERE tablename='pay_lsy_merchant'; -- 期望含上述两条