Files
CosScene/server/app/api/v1/endpoints/favorites.py
T
2026-05-09 16:40:29 +08:00

113 lines
3.6 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_current_active_user, get_db
from app.models.favorite import Favorite
from app.models.spot import Spot
from app.models.user import User
from app.schemas.common import PageResponse
from app.schemas.spot import SpotBrief
router = APIRouter()
def _spot_to_brief(spot: Spot) -> SpotBrief:
cover = next((img for img in spot.images if img.is_cover), None)
if cover is None and spot.images:
cover = spot.images[0]
return SpotBrief(
id=spot.id,
title=spot.title,
city=spot.city,
longitude=spot.longitude,
latitude=spot.latitude,
cover_image_url=cover.image_url if cover else None,
audit_status=spot.audit_status,
created_at=spot.created_at,
)
@router.post("/{spot_id}", status_code=status.HTTP_201_CREATED)
async def add_favorite(
spot_id: int,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Spot).where(Spot.id == spot_id))
if not result.scalar_one_or_none():
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Spot not found")
existing = await db.execute(
select(Favorite).where(
Favorite.user_id == current_user.id, Favorite.spot_id == spot_id
)
)
if existing.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Already favorited"
)
fav = Favorite(user_id=current_user.id, spot_id=spot_id)
db.add(fav)
await db.execute(
select(Spot).where(Spot.id == spot_id)
)
spot_obj = (await db.execute(select(Spot).where(Spot.id == spot_id))).scalar_one()
spot_obj.favorite_count = (spot_obj.favorite_count or 0) + 1
await db.commit()
return {"code": 0, "message": "success"}
@router.delete("/{spot_id}", status_code=status.HTTP_204_NO_CONTENT)
async def remove_favorite(
spot_id: int,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(Favorite).where(
Favorite.user_id == current_user.id, Favorite.spot_id == spot_id
)
)
fav = result.scalar_one_or_none()
if not fav:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Favorite not found"
)
spot_obj = (await db.execute(select(Spot).where(Spot.id == spot_id))).scalar_one_or_none()
if spot_obj:
spot_obj.favorite_count = max((spot_obj.favorite_count or 0) - 1, 0)
await db.delete(fav)
await db.commit()
@router.get("/", response_model=PageResponse[SpotBrief])
async def list_favorites(
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=100),
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db),
):
count_query = select(func.count(Favorite.id)).where(
Favorite.user_id == current_user.id
)
total_result = await db.execute(count_query)
total = total_result.scalar() or 0
offset = (page - 1) * page_size
query = (
select(Favorite)
.where(Favorite.user_id == current_user.id)
.order_by(Favorite.created_at.desc())
.offset(offset)
.limit(page_size)
)
result = await db.execute(query)
favorites = result.scalars().all()
return PageResponse(
total=total,
items=[_spot_to_brief(f.spot) for f in favorites],
)