refactor: 角色组织账号模型重构 - 阶段二(保守)

1. 创建数据迁移脚本 migrate_members_to_org.py
   - ParkMember.admin 同步到 organization_members (type=carrier)
   - CompanyMember.is_admin 同步到 organization_members (type=enterprise)
   - 自动创建缺失的 organization 记录,幂等
2. 修改 _compute_capabilities,优先查 organization_members
   - carrier 能力:org_members.role=admin + org.type=carrier
   - enterprise 能力:org_members.role=admin + org.type=enterprise
   - 旧表 park_members/company_members 保留兼容
3. 不删除旧表,不修改表结构,风险可控
This commit is contained in:
Pine
2026-09-03 00:39:41 +08:00
parent 6bb988cc3e
commit 97f3c1956c
2 changed files with 176 additions and 14 deletions
+46 -14
View File
@@ -41,10 +41,12 @@ async def _compute_capabilities(db: Database, user: dict) -> list[str]:
"""叠加制能力集合(身份不互斥):
- opc_member:所有账号的基础能力;
- operator:运营端角色;
- carrier:园区端角色 或 已绑定园区管理员(park_members.admin / operator_user_id);
- enterprise:已绑定企业管理员(company_members.is_admin)。
- carrier:园区端角色 或 已绑定园区管理员(organization_members.role=admin + org.type=carrier,或旧表 park_members.admin / park_tenants.operator_user_id);
- enterprise:已绑定企业管理员(organization_members.role=admin + org.type=enterprise,或旧表 company_members.is_admin)。
优先查统一的 organization_members 表,回退查旧表(park_members/company_members),保持兼容。
"""
from ..infrastructure.models import CompanyMember, ParkMember, ParkTenant
from ..infrastructure.models import CompanyMember, Organization, OrganizationMember, ParkMember, ParkTenant
from sqlalchemy import select
role = user.get("role", "")
@@ -54,19 +56,49 @@ async def _compute_capabilities(db: Database, user: dict) -> list[str]:
if role in ("carrier", "park", "park_staff", "carrier_staff"):
caps.append("carrier")
else:
bound = await db.session.scalar(select(ParkMember).where(
ParkMember.user_id == user.get("id", ""), ParkMember.park_id != "",
ParkMember.member_type == "admin", ParkMember.status == "active").limit(1))
if not bound:
bound = await db.session.scalar(select(ParkTenant).where(
ParkTenant.operator_user_id == user.get("id", "")).limit(1))
if bound:
# 优先查统一表:organization_members.role=admin + org.type=carrier
admin_orgs = (await db.session.scalars(
select(Organization.id).join(
OrganizationMember, OrganizationMember.org_id == Organization.id
).where(
OrganizationMember.user_id == user.get("id", ""),
OrganizationMember.role == "admin",
OrganizationMember.status == "active",
Organization.type == "carrier",
)
)).all()
if admin_orgs:
caps.append("carrier")
ent = await db.session.scalar(select(CompanyMember).where(
CompanyMember.user_id == user.get("id", ""), CompanyMember.is_admin.is_(True),
CompanyMember.status == "active").limit(1))
if ent:
else:
# 回退查旧表
bound = await db.session.scalar(select(ParkMember).where(
ParkMember.user_id == user.get("id", ""), ParkMember.park_id != "",
ParkMember.member_type == "admin", ParkMember.status == "active").limit(1))
if not bound:
bound = await db.session.scalar(select(ParkTenant).where(
ParkTenant.operator_user_id == user.get("id", "")).limit(1))
if bound:
caps.append("carrier")
# enterprise 能力:优先查统一表,回退查旧表
ent_orgs = (await db.session.scalars(
select(Organization.id).join(
OrganizationMember, OrganizationMember.org_id == Organization.id
).where(
OrganizationMember.user_id == user.get("id", ""),
OrganizationMember.role == "admin",
OrganizationMember.status == "active",
Organization.type == "enterprise",
)
)).all()
if ent_orgs:
caps.append("enterprise")
else:
ent = await db.session.scalar(select(CompanyMember).where(
CompanyMember.user_id == user.get("id", ""), CompanyMember.is_admin.is_(True),
CompanyMember.status == "active").limit(1))
if ent:
caps.append("enterprise")
return caps