Files
server-core/app/api/routers/rbac_org.py
T
Pine 416ade4842 feat: 统一权限聚合端点 + 事件名同步昵称 + IM/企业路由补强
- 新增 rbac_permissions 统一权限/组织归属聚合端点
- sync_event_name_to_nickname 脚本:事件名同步用户昵称
- im router/client、rbac_enterprise/opc/org/public 增强
- compute_catalog、nginx 配置、env.example 更新
2026-09-11 21:15:36 +08:00

123 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""组织/租户端点:企业 / 载体 / 服务商(按角色 + 组织作用域)。"""
from __future__ import annotations
import logging
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from ..dependencies import get_db, get_user_organizations, get_user_primary_org
from ..schemas.org import OrgMemberAdd
from ...rbac import require_roles
from ...infrastructure.repositories import Database
from ...park import tenants as tnt
log = logging.getLogger("rbac.org")
router = APIRouter(tags=["org"])
@router.get("/org/me", summary="当前组织信息")
async def org_me(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member", "carrier", "operator")),
):
"""返回用户绑定的所有组织(优先 organization_members,回退 users.org_id)。"""
orgs = await get_user_organizations(db, user["id"])
region = await db.regions.get(user["region_id"]) if user.get("region_id") else None
primary_org = orgs[0] if orgs else None
# 企业成员关系(company_members 多对多;前端据此渲染企业切换/企业权限)
company_memberships = []
for m in await db.company_members.list_by_user(user["id"]):
co = await tnt.get_company(m["company_id"])
company_memberships.append({
"company_id": m["company_id"],
"company_name": (co or {}).get("name", ""),
"is_admin": bool(m.get("is_admin")),
"member_type": m.get("member_type", ""),
"status": m.get("status", ""),
"joined_at": m.get("created_at", ""),
})
return {
"user_id": user["id"],
"role": user["role"],
"sub_role": user["sub_role"],
"org": primary_org,
"organizations": orgs,
"region": region,
"company_memberships": company_memberships,
}
@router.get("/carrier/enterprises", summary="载体下辖企业")
async def carrier_enterprises(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("carrier")),
):
"""返回绑定到当前载体(parent_id == 载体 org_id)的企业。"""
carrier_org = await get_user_primary_org(db, user, org_type="carrier")
if not carrier_org:
return []
return await db.orgs.list_by_parent(carrier_org["org_id"])
# ── 机构成员(机构主账号 + 机构内子账号,账号可挂多机构)────────────────────
@router.get("/org/{org_id}/members", summary="机构成员列表")
async def org_members(
org_id: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member", "carrier", "operator")),
):
# 叠加制权限:operator 全量;本 org 的载体机构管理员;本企业的企业成员(org_id 亦可能是 company_id
caps = user.get("capabilities") or []
if "operator" in caps:
pass
elif await db.org_members.is_admin(org_id, user["id"]):
pass
elif await db.company_members.get(user["id"], org_id):
pass
else:
raise HTTPException(status_code=403, detail="无权查看该组织成员")
# org_id 可能是 organization_members.org_id(载体机构),也可能被前端当作 company_id 传入:
# 优先返回企业成员(命中企业时),否则回退到机构成员。
company_members = await tnt.company_members(org_id)
if company_members:
return {"items": company_members}
return {"items": await db.org_members.list_for_org(org_id)}
@router.post("/org/{org_id}/members", summary="添加机构成员(子账号)")
async def org_add_member(
org_id: str,
req: OrgMemberAdd,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("carrier", "operator")),
):
if "operator" not in (user.get("capabilities") or []) and not await db.org_members.is_admin(org_id, user["id"]):
raise HTTPException(status_code=403, detail="Forbidden: not org admin")
target_id = req.user_id
if not target_id and req.username:
found = await db.users.get_by_username(req.username)
if found is None:
raise HTTPException(status_code=404, detail="账号不存在")
target_id = found["id"]
if not target_id:
raise HTTPException(status_code=400, detail="缺少 user_id 或 username")
return await db.org_members.add_member(org_id, target_id, req.role, req.is_admin)
@router.get("/me/orgs", summary="我加入的机构")
async def my_orgs(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member", "carrier", "operator")),
):
"""返回当前账号挂靠的所有机构及成员角色(支持一账号多机构)。"""
from ...services.org_service import OrgService
return await OrgService(db).my_orgs(user["id"])