Files
wecom_it_smart_desk/backend/app/api/portal.py
T

250 lines
7.6 KiB
Python
Raw Normal View History

# =============================================================================
# 企微IT智能服务台 — Portal 统一入口 API
# =============================================================================
# 说明:统一入口(Portal)相关接口
# 包含:
# 1. 获取当前用户角色信息
# 2. 切换当前角色
# 3. 获取角色对应的入口 URL
# 所有接口需要有效的 Bearer Token
# =============================================================================
import json
import logging
from typing import Optional
from fastapi import APIRouter, Depends
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_current_user, UserInfo
from app.config import settings
from app.database import get_db
from app.models.role import Role
from app.models.user_role import UserRole
from app.schemas.role import (
PortalUserInfo,
RoleResponse,
SwitchRoleRequest,
SwitchRoleResponse,
)
from app.services.token_service import TokenService
from app.utils.response import AppException, success_response
logger = logging.getLogger(__name__)
# HTTP Bearer 认证方案
security = HTTPBearer()
# 创建路由器
router = APIRouter(prefix="/portal")
# --------------------------------------------------------------------------
# 获取当前用户角色信息
# --------------------------------------------------------------------------
@router.get("/roles")
async def get_user_roles(
current_user: UserInfo = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""获取当前用户的角色信息。
返回用户的基本信息和角色列表,用于路由选择页展示。
Args:
current_user: 当前用户(通过认证依赖注入)
db: 数据库会话
Returns:
Dict: 统一响应格式,包含用户信息和角色列表
"""
# 查询用户拥有的角色
stmt = (
select(Role, UserRole)
.join(UserRole, Role.id == UserRole.role_id)
.where(UserRole.employee_id == current_user.employee_id)
.where(
# 过滤已过期的角色
(UserRole.expires_at.is_(None)) | (UserRole.expires_at > func.now())
)
)
result = await db.execute(stmt)
role_rows = result.all()
# 构建角色列表
roles = []
for role, user_role in role_rows:
roles.append(
RoleResponse(
id=role.id,
name=role.name,
display_name=role.display_name,
description=role.description,
permissions=role.permissions or [],
is_default=role.is_default,
created_at=role.created_at,
updated_at=role.updated_at,
)
)
# 如果用户没有任何角色,添加默认的 user 角色
if not roles:
# 查询 user 角色
user_role_stmt = select(Role).where(Role.name == "user")
user_role_result = await db.execute(user_role_stmt)
user_role = user_role_result.scalars().first()
if user_role:
roles.append(
RoleResponse(
id=user_role.id,
name=user_role.name,
display_name=user_role.display_name,
description=user_role.description,
permissions=user_role.permissions or [],
is_default=user_role.is_default,
created_at=user_role.created_at,
updated_at=user_role.updated_at,
)
)
# 构建响应
user_info = PortalUserInfo(
employee_id=current_user.employee_id,
name=current_user.name,
department=current_user.department,
avatar=current_user.avatar,
roles=roles,
current_role=current_user.current_role,
)
return success_response(data=user_info.model_dump())
# --------------------------------------------------------------------------
# 切换当前角色
# --------------------------------------------------------------------------
@router.post("/switch-role")
async def switch_role(
body: SwitchRoleRequest,
current_user: UserInfo = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
credentials: HTTPAuthorizationCredentials = Depends(security),
):
"""切换当前角色。
更新 Redis Token 中的 current_role 字段,返回目标角色的入口 URL。
Args:
body: 切换角色请求
current_user: 当前用户(通过认证依赖注入)
db: 数据库会话
Returns:
Dict: 统一响应格式,包含切换后的角色和重定向 URL
"""
# 验证用户是否有目标角色
stmt = (
select(Role)
.join(UserRole, Role.id == UserRole.role_id)
.where(UserRole.employee_id == current_user.employee_id)
.where(Role.name == body.new_role)
)
result = await db.execute(stmt)
target_role = result.scalars().first()
if not target_role:
raise AppException(4003, f"没有 {body.new_role} 角色权限")
# 更新 Redis Token 中的 current_role
from app.dependencies import get_redis
redis_client = await get_redis()
token_service = TokenService(redis_client)
# 从请求头获取 token
token = credentials.credentials
switch_success = await token_service.switch_role(token, body.new_role)
if not switch_success:
raise AppException(4003, "角色切换失败")
# 获取目标角色的入口 URL
redirect_url = _get_role_url(body.new_role)
logger.info(f"用户 {current_user.employee_id} 切换角色到 {body.new_role}")
return success_response(
data=SwitchRoleResponse(
current_role=body.new_role,
redirect_url=redirect_url,
).model_dump()
)
# --------------------------------------------------------------------------
# 获取角色对应的入口 URL
# --------------------------------------------------------------------------
@router.get("/entry/{role_name}")
async def get_role_entry(
role_name: str,
current_user: UserInfo = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""获取角色对应的入口 URL。
Args:
role_name: 角色标识
current_user: 当前用户(通过认证依赖注入)
db: 数据库会话
Returns:
Dict: 统一响应格式,包含角色信息和入口 URL
"""
# 验证用户是否有目标角色
stmt = (
select(Role)
.join(UserRole, Role.id == UserRole.role_id)
.where(UserRole.employee_id == current_user.employee_id)
.where(Role.name == role_name)
)
result = await db.execute(stmt)
target_role = result.scalars().first()
if not target_role:
raise AppException(4003, f"没有 {role_name} 角色权限")
# 获取入口 URL
redirect_url = _get_role_url(role_name)
return success_response(
data={
"role": role_name,
"url": redirect_url,
"display_name": target_role.display_name,
}
)
# --------------------------------------------------------------------------
# 辅助函数:获取角色对应的 URL
# --------------------------------------------------------------------------
def _get_role_url(role_name: str) -> str:
"""获取角色对应的前端 URL。
Args:
role_name: 角色标识
Returns:
str: 前端 URL
"""
role_urls = {
"user": "/itdesk/",
"agent": "/itagent/",
"admin": "/itadmin/",
}
return role_urls.get(role_name, "/itdesk/")