diff --git a/app/api/routers/auth.py b/app/api/routers/auth.py index 5709a88..22c9d47 100644 --- a/app/api/routers/auth.py +++ b/app/api/routers/auth.py @@ -39,6 +39,7 @@ from ..schemas.auth import ( ) from ...infrastructure.repositories import Database from ...services import sms, wechat +from ...domain.account_types import account_type, permission_role, label as account_type_label router = APIRouter(prefix="/auth", tags=["auth"]) @@ -144,17 +145,19 @@ async def _issue_token( 否则回退到 ``users.role``(单角色/中性令牌)。 """ role = (identity or user).get("role", "opc_member") - sub_role = (identity or user).get("sub_role") + # 账号类型(四种)统一解析;子角色已停用(置 None) + atype = account_type(role) + sub_role = None org_id = (identity or user).get("org_id") region_id = (identity or user).get("region_id") identity_id = identity.get("id") if identity else None - perms = await db.roles.permissions_for(role, sub_role) + perms = await db.roles.permissions_for(permission_role(atype), None) scope_ids = await db.regions.visible_region_ids(region_id) scope_level = await db.regions.level(region_id) token_user = dict(user) - token_user.update(role=role, sub_role=sub_role, org_id=org_id, region_id=region_id) + token_user.update(account_type=atype, role=role, sub_role=None, org_id=org_id, region_id=region_id) await db.users.mark_login(user["id"]) # 记录最近登录时间(每次签发令牌=一次活动) return await db.tokens.create( token_user, @@ -173,7 +176,8 @@ async def _profile_for( ) -> dict: """按身份(或回退用户单角色)构建对外资料字典。""" role = (identity or user).get("role", "opc_member") - sub_role = (identity or user).get("sub_role") + atype = account_type(role) # 四种账号类型(子角色已停用) + sub_role = None org_id = (identity or user).get("org_id") region_id = (identity or user).get("region_id") profile = {f: user.get(f, "") for f in _PROFILE_FIELDS} @@ -195,12 +199,13 @@ async def _profile_for( profile["affiliation"] = user.get("affiliation", "independent") profile["park_id"] = user.get("park_id", "") profile["park_name"] = user.get("park_name", "") - profile["account_type"] = user.get("account_type", "opc_default") - profile["role"] = role - profile["sub_role"] = sub_role + profile["account_type"] = atype # 四种账号类型(operator/carrier/opc/service) + profile["account_type_label"] = account_type_label(atype) + profile["role"] = role # 端口角色(兼容守卫) + profile["sub_role"] = None # 子角色已停用 profile["org_id"] = org_id profile["region_id"] = region_id - profile["permissions"] = await db.roles.permissions_for(role, sub_role) + profile["permissions"] = await db.roles.permissions_for(permission_role(atype), None) profile["scope_region_ids"] = await db.regions.visible_region_ids(region_id) if identity: profile["identity_id"] = identity["id"] diff --git a/app/domain/account_types.py b/app/domain/account_types.py new file mode 100644 index 0000000..6d9c9d8 --- /dev/null +++ b/app/domain/account_types.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +"""账号类型(唯一来源)—— 权限/身份逻辑集中于此,不分散。 + +账号类型只四种: + operator 运营方 —— 平台运营 + carrier 载体方 —— 创业载体/园区运营方 + opc OPC —— 超级个体 + service 服务方 —— 其余全部(服务商/甲方企业/政务/投资/开发…… 统一归服务方, + 为后期扩展 服务商/甲方企业 等预留) + +子角色(sub_role)已停用:权限一律按账号类型解析,不再读子角色。 +端口角色(role, 如 opc_member/provider/enterprise...) 仅作内部路由守卫兼容, +对外暴露/鉴权的「账号类型」统一用本模块。 +""" +from __future__ import annotations + +ACCOUNT_TYPES = ("operator", "carrier", "opc", "service") + + +def account_type(role: str | None) -> str: + """把任意(端口)角色归一为四种账号类型之一。非运营/非OPC → service。""" + r = (role or "").strip().lower() + if r in ("operator", "operator_internal", "op_admin", "admin"): + return "operator" + if r in ("carrier", "park", "park_staff", "carrier_staff"): + return "carrier" + if r in ("opc_member", "opc", "opc_certified", "opc_independent"): + return "opc" + # provider / enterprise / government / investor / ... 全部归 服务方 + return "service" + + +def permission_role(atype: str) -> str: + """账号类型 → 用于查权限表( roles/ )的角色码。service 用 provider 基座。""" + return { + "operator": "operator", + "carrier": "carrier", + "opc": "opc_member", + "service": "provider", + }.get(atype, "provider") + + +def label(atype: str) -> str: + """账号类型中文展示名。""" + return { + "operator": "运营方", + "carrier": "载体方", + "opc": "OPC", + "service": "服务方", + }.get(atype, "服务方")