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
|
||
}
|
||
}
|