0032_user_managed_org.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. """用户级管理范围(契约1.30):sys_user 加 managed_org_id 列。
  2. Revision ID: 0032_user_managed_org
  3. Revises: 0031_user_sort_no
  4. Create Date: 2026-09-12
  5. 变更要点:
  6. 1. sys_user 新增 managed_org_id(BIGINT NULL DEFAULT 0)三态:
  7. NULL=全局、0=跟随该用户所在机构(SELF 哨兵)、机构ID=固定锚点;
  8. 2. 角色锚点 "SELF"(0)的回落目标由「用户所在机构」改为「用户的管理范围」:
  9. 实际生效范围仍以角色锚点为准,仅角色为 SELF 时取用户设置(契约1.30);
  10. 3. 存量回填哨兵 0(跟随所在机构),与升级前「角色 SELF → user.organization_id」
  11. 行为完全一致,无感切换;
  12. 4. 幂等与双引擎互冲安全:加列先探测;回填仅在本次真正执行加列时进行,
  13. 防止 Alembic/Flyway 先后执行时把已显式设为 NULL(全局)的行覆盖回 0。
  14. """
  15. from __future__ import annotations
  16. from collections.abc import Sequence
  17. import sqlalchemy as sa
  18. from alembic import op
  19. revision: str = "0032_user_managed_org"
  20. down_revision: str | None = "0031_user_sort_no"
  21. branch_labels: str | Sequence[str] | None = None
  22. depends_on: str | Sequence[str] | None = None
  23. def _existing_user_columns() -> set[str]:
  24. # 达梦返回的列名为大写,统一小写比较(同 0031)。
  25. return {
  26. column["name"].lower()
  27. for column in sa.inspect(op.get_bind()).get_columns("sys_user")
  28. }
  29. def upgrade() -> None:
  30. if "managed_org_id" not in _existing_user_columns():
  31. op.add_column(
  32. "sys_user",
  33. sa.Column(
  34. "managed_org_id",
  35. sa.BigInteger(),
  36. nullable=True,
  37. server_default=sa.text("0"),
  38. ),
  39. )
  40. # 存量回填哨兵 0:仅随本次加列执行(见模块注释第 4 点)
  41. op.get_bind().execute(
  42. sa.text("UPDATE sys_user SET managed_org_id = 0 "
  43. "WHERE managed_org_id IS NULL")
  44. )
  45. def downgrade() -> None:
  46. if "managed_org_id" in _existing_user_columns():
  47. op.drop_column("sys_user", "managed_org_id")