import math import uuid from fastapi import APIRouter, HTTPException, Query, status from sqlalchemy import func, select from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload from app.deps import CurrentUser, DbSession, OptionalUser from app.models import Cloud, Profile, User from app.schemas import CloudOut, PageOut, ProfileOut, ProfileUpdateIn, PublicProfileOut from app.serializers import cloud_out, profile_out, public_profile_out router = APIRouter(prefix="/profiles", tags=["用户资料"]) @router.get("/{user_id}", response_model=PublicProfileOut) async def get_profile(user_id: uuid.UUID, db: DbSession) -> PublicProfileOut: profile = await db.get(Profile, user_id) if not profile: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在") return public_profile_out(profile) @router.patch("/me", response_model=ProfileOut) async def update_my_profile( payload: ProfileUpdateIn, user: CurrentUser, db: DbSession, ) -> ProfileOut: user.profile.username = payload.username try: await db.commit() except IntegrityError as exc: await db.rollback() raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="这个昵称已经被使用") from exc return profile_out(user.profile) @router.get("/{user_id}/clouds", response_model=PageOut[CloudOut]) async def get_profile_clouds( user_id: uuid.UUID, db: DbSession, viewer: OptionalUser, page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=100), ) -> PageOut[CloudOut]: if not await db.get(Profile, user_id): raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在") can_manage = bool(viewer and (viewer.id == user_id or viewer.profile.role == "admin")) filters = [Cloud.user_id == user_id] if not can_manage: filters.extend([Cloud.status == "approved", Cloud.is_hidden.is_(False)]) total = await db.scalar(select(func.count()).select_from(Cloud).where(*filters)) or 0 result = await db.execute( select(Cloud) .options(joinedload(Cloud.user).joinedload(User.profile), joinedload(Cloud.cloud_type)) .where(*filters) .order_by(Cloud.captured_at.desc().nulls_last(), Cloud.created_at.desc(), Cloud.id.desc()) .offset((page - 1) * page_size) .limit(page_size) ) items = [cloud_out(item, include_private=can_manage) for item in result.scalars().unique().all()] return PageOut( items=items, page=page, page_size=page_size, total=total, total_pages=max(1, math.ceil(total / page_size)), )