""" 角色管理 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 } }