343 lines
10 KiB
Python
343 lines
10 KiB
Python
|
|
"""
|
|||
|
|
角色管理 API
|
|||
|
|
"""
|
|||
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
|
from sqlalchemy import select, func, delete
|
|||
|
|
from typing import Optional
|
|||
|
|
from pydantic import BaseModel
|
|||
|
|
|
|||
|
|
from app.core.database import get_db
|
|||
|
|
from app.core.deps import get_current_user
|
|||
|
|
from app.models.user import User
|
|||
|
|
from app.models.role import Role, UserRole
|
|||
|
|
from app.schemas.response import success_response
|
|||
|
|
|
|||
|
|
router = APIRouter()
|
|||
|
|
|
|||
|
|
|
|||
|
|
# === Pydantic Schemas ===
|
|||
|
|
|
|||
|
|
class RoleCreateRequest(BaseModel):
|
|||
|
|
"""创建角色请求"""
|
|||
|
|
role_name: str
|
|||
|
|
role_code: str
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
status: int = 1
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RoleUpdateRequest(BaseModel):
|
|||
|
|
"""更新角色请求"""
|
|||
|
|
role_name: Optional[str] = None
|
|||
|
|
role_code: Optional[str] = None
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
status: Optional[int] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
# === API Endpoints ===
|
|||
|
|
|
|||
|
|
@router.get("/", response_model=dict)
|
|||
|
|
async def get_roles_list(
|
|||
|
|
page: int = Query(1, ge=1),
|
|||
|
|
page_size: int = Query(10, ge=1, le=100),
|
|||
|
|
keyword: Optional[str] = Query(None, description="搜索关键词(角色名称、编码)"),
|
|||
|
|
status: Optional[int] = Query(None, description="状态筛选:0-禁用 1-启用"),
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""获取角色列表(分页)"""
|
|||
|
|
|
|||
|
|
# 构建查询条件
|
|||
|
|
conditions = []
|
|||
|
|
if keyword:
|
|||
|
|
conditions.append(
|
|||
|
|
(Role.role_name.like(f"%{keyword}%")) | (Role.role_code.like(f"%{keyword}%"))
|
|||
|
|
)
|
|||
|
|
if status is not None:
|
|||
|
|
conditions.append(Role.status == status)
|
|||
|
|
|
|||
|
|
# 查询总数
|
|||
|
|
count_query = select(func.count(Role.id))
|
|||
|
|
if conditions:
|
|||
|
|
count_query = count_query.where(*conditions)
|
|||
|
|
total_result = await db.execute(count_query)
|
|||
|
|
total = total_result.scalar()
|
|||
|
|
|
|||
|
|
# 查询角色列表
|
|||
|
|
query = select(Role).order_by(Role.created_at.desc())
|
|||
|
|
if conditions:
|
|||
|
|
query = query.where(*conditions)
|
|||
|
|
query = query.offset((page - 1) * page_size).limit(page_size)
|
|||
|
|
|
|||
|
|
result = await db.execute(query)
|
|||
|
|
roles = result.scalars().all()
|
|||
|
|
|
|||
|
|
# 获取每个角色的用户数量
|
|||
|
|
roles_data = []
|
|||
|
|
for role in roles:
|
|||
|
|
# 查询该角色的用户数量
|
|||
|
|
user_count_result = await db.execute(
|
|||
|
|
select(func.count(UserRole.user_id)).where(UserRole.role_id == role.id)
|
|||
|
|
)
|
|||
|
|
user_count = user_count_result.scalar()
|
|||
|
|
|
|||
|
|
roles_data.append({
|
|||
|
|
"id": role.id,
|
|||
|
|
"role_name": role.role_name,
|
|||
|
|
"role_code": role.role_code,
|
|||
|
|
"description": role.description,
|
|||
|
|
"status": role.status,
|
|||
|
|
"is_system": role.is_system,
|
|||
|
|
"user_count": user_count,
|
|||
|
|
"created_at": role.created_at.isoformat() if role.created_at else None,
|
|||
|
|
"updated_at": role.updated_at.isoformat() if role.updated_at else None,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": roles_data,
|
|||
|
|
"total": total,
|
|||
|
|
"page": page,
|
|||
|
|
"page_size": page_size
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/", response_model=dict)
|
|||
|
|
async def create_role(
|
|||
|
|
role_data: RoleCreateRequest,
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""创建新角色"""
|
|||
|
|
|
|||
|
|
# 检查角色编码是否已存在
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(Role).where(Role.role_code == role_data.role_code)
|
|||
|
|
)
|
|||
|
|
existing_role = result.scalar_one_or_none()
|
|||
|
|
if existing_role:
|
|||
|
|
raise HTTPException(status_code=400, detail="角色编码已存在")
|
|||
|
|
|
|||
|
|
# 检查角色名称是否已存在
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(Role).where(Role.role_name == role_data.role_name)
|
|||
|
|
)
|
|||
|
|
existing_name = result.scalar_one_or_none()
|
|||
|
|
if existing_name:
|
|||
|
|
raise HTTPException(status_code=400, detail="角色名称已存在")
|
|||
|
|
|
|||
|
|
# 创建角色
|
|||
|
|
new_role = Role(
|
|||
|
|
role_name=role_data.role_name,
|
|||
|
|
role_code=role_data.role_code,
|
|||
|
|
description=role_data.description,
|
|||
|
|
status=role_data.status,
|
|||
|
|
is_system=0 # 新创建的角色都不是系统角色
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
db.add(new_role)
|
|||
|
|
await db.commit()
|
|||
|
|
await db.refresh(new_role)
|
|||
|
|
|
|||
|
|
return success_response(
|
|||
|
|
data={
|
|||
|
|
"id": new_role.id,
|
|||
|
|
"role_name": new_role.role_name,
|
|||
|
|
"role_code": new_role.role_code
|
|||
|
|
},
|
|||
|
|
message="角色创建成功"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.get("/{role_id}", response_model=dict)
|
|||
|
|
async def get_role(
|
|||
|
|
role_id: int,
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""获取角色详情"""
|
|||
|
|
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(Role).where(Role.id == role_id)
|
|||
|
|
)
|
|||
|
|
role = result.scalar_one_or_none()
|
|||
|
|
if not role:
|
|||
|
|
raise HTTPException(status_code=404, detail="角色不存在")
|
|||
|
|
|
|||
|
|
# 获取该角色的用户数量
|
|||
|
|
user_count_result = await db.execute(
|
|||
|
|
select(func.count(UserRole.user_id)).where(UserRole.role_id == role.id)
|
|||
|
|
)
|
|||
|
|
user_count = user_count_result.scalar()
|
|||
|
|
|
|||
|
|
return success_response(data={
|
|||
|
|
"id": role.id,
|
|||
|
|
"role_name": role.role_name,
|
|||
|
|
"role_code": role.role_code,
|
|||
|
|
"description": role.description,
|
|||
|
|
"status": role.status,
|
|||
|
|
"is_system": role.is_system,
|
|||
|
|
"user_count": user_count,
|
|||
|
|
"created_at": role.created_at.isoformat() if role.created_at else None,
|
|||
|
|
"updated_at": role.updated_at.isoformat() if role.updated_at else None,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.put("/{role_id}", response_model=dict)
|
|||
|
|
async def update_role(
|
|||
|
|
role_id: int,
|
|||
|
|
role_data: RoleUpdateRequest,
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""更新角色信息"""
|
|||
|
|
|
|||
|
|
# 查询角色
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(Role).where(Role.id == role_id)
|
|||
|
|
)
|
|||
|
|
role = result.scalar_one_or_none()
|
|||
|
|
if not role:
|
|||
|
|
raise HTTPException(status_code=404, detail="角色不存在")
|
|||
|
|
|
|||
|
|
# 系统角色不允许修改
|
|||
|
|
if role.is_system == 1:
|
|||
|
|
raise HTTPException(status_code=400, detail="系统角色不允许修改")
|
|||
|
|
|
|||
|
|
# 更新字段
|
|||
|
|
if role_data.role_name is not None:
|
|||
|
|
# 检查角色名称是否与其他角色冲突
|
|||
|
|
if role_data.role_name != role.role_name:
|
|||
|
|
name_result = await db.execute(
|
|||
|
|
select(Role).where(Role.role_name == role_data.role_name, Role.id != role_id)
|
|||
|
|
)
|
|||
|
|
if name_result.scalar_one_or_none():
|
|||
|
|
raise HTTPException(status_code=400, detail="角色名称已被其他角色使用")
|
|||
|
|
role.role_name = role_data.role_name
|
|||
|
|
|
|||
|
|
if role_data.role_code is not None:
|
|||
|
|
# 检查角色编码是否与其他角色冲突
|
|||
|
|
if role_data.role_code != role.role_code:
|
|||
|
|
code_result = await db.execute(
|
|||
|
|
select(Role).where(Role.role_code == role_data.role_code, Role.id != role_id)
|
|||
|
|
)
|
|||
|
|
if code_result.scalar_one_or_none():
|
|||
|
|
raise HTTPException(status_code=400, detail="角色编码已被其他角色使用")
|
|||
|
|
role.role_code = role_data.role_code
|
|||
|
|
|
|||
|
|
if role_data.description is not None:
|
|||
|
|
role.description = role_data.description
|
|||
|
|
|
|||
|
|
if role_data.status is not None:
|
|||
|
|
role.status = role_data.status
|
|||
|
|
|
|||
|
|
await db.commit()
|
|||
|
|
await db.refresh(role)
|
|||
|
|
|
|||
|
|
return success_response(data={"id": role.id}, message="角色信息更新成功")
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.delete("/{role_id}", response_model=dict)
|
|||
|
|
async def delete_role(
|
|||
|
|
role_id: int,
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""删除角色"""
|
|||
|
|
|
|||
|
|
# 查询角色
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(Role).where(Role.id == role_id)
|
|||
|
|
)
|
|||
|
|
role = result.scalar_one_or_none()
|
|||
|
|
if not role:
|
|||
|
|
raise HTTPException(status_code=404, detail="角色不存在")
|
|||
|
|
|
|||
|
|
# 系统角色不允许删除
|
|||
|
|
if role.is_system == 1:
|
|||
|
|
raise HTTPException(status_code=400, detail="系统角色不允许删除")
|
|||
|
|
|
|||
|
|
# 检查是否有用户使用该角色
|
|||
|
|
user_count_result = await db.execute(
|
|||
|
|
select(func.count(UserRole.user_id)).where(UserRole.role_id == role_id)
|
|||
|
|
)
|
|||
|
|
user_count = user_count_result.scalar()
|
|||
|
|
if user_count > 0:
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=400,
|
|||
|
|
detail=f"该角色下有 {user_count} 个用户,无法删除。请先移除这些用户的角色。"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 删除角色的菜单权限关联
|
|||
|
|
from app.models.menu import RoleMenu
|
|||
|
|
await db.execute(
|
|||
|
|
delete(RoleMenu).where(RoleMenu.role_id == role_id)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 删除角色
|
|||
|
|
await db.delete(role)
|
|||
|
|
await db.commit()
|
|||
|
|
|
|||
|
|
return success_response(message="角色删除成功")
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.get("/{role_id}/users", response_model=dict)
|
|||
|
|
async def get_role_users(
|
|||
|
|
role_id: int,
|
|||
|
|
page: int = Query(1, ge=1),
|
|||
|
|
page_size: int = Query(10, ge=1, le=100),
|
|||
|
|
current_user: User = Depends(get_current_user),
|
|||
|
|
db: AsyncSession = Depends(get_db)
|
|||
|
|
):
|
|||
|
|
"""获取角色下的用户列表"""
|
|||
|
|
|
|||
|
|
# 检查角色是否存在
|
|||
|
|
role_result = await db.execute(
|
|||
|
|
select(Role).where(Role.id == role_id)
|
|||
|
|
)
|
|||
|
|
role = role_result.scalar_one_or_none()
|
|||
|
|
if not role:
|
|||
|
|
raise HTTPException(status_code=404, detail="角色不存在")
|
|||
|
|
|
|||
|
|
# 查询总数
|
|||
|
|
count_result = await db.execute(
|
|||
|
|
select(func.count(UserRole.user_id)).where(UserRole.role_id == role_id)
|
|||
|
|
)
|
|||
|
|
total = count_result.scalar()
|
|||
|
|
|
|||
|
|
# 查询用户列表
|
|||
|
|
result = await db.execute(
|
|||
|
|
select(User)
|
|||
|
|
.join(UserRole, UserRole.user_id == User.id)
|
|||
|
|
.where(UserRole.role_id == role_id)
|
|||
|
|
.order_by(User.created_at.desc())
|
|||
|
|
.offset((page - 1) * page_size)
|
|||
|
|
.limit(page_size)
|
|||
|
|
)
|
|||
|
|
users = result.scalars().all()
|
|||
|
|
|
|||
|
|
users_data = [{
|
|||
|
|
"id": user.id,
|
|||
|
|
"username": user.username,
|
|||
|
|
"nickname": user.nickname,
|
|||
|
|
"email": user.email,
|
|||
|
|
"phone": user.phone,
|
|||
|
|
"status": user.status,
|
|||
|
|
"created_at": user.created_at.isoformat() if user.created_at else None,
|
|||
|
|
} for user in users]
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": users_data,
|
|||
|
|
"total": total,
|
|||
|
|
"page": page,
|
|||
|
|
"page_size": page_size,
|
|||
|
|
"role_info": {
|
|||
|
|
"id": role.id,
|
|||
|
|
"role_name": role.role_name,
|
|||
|
|
"role_code": role.role_code
|
|||
|
|
}
|
|||
|
|
}
|