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