113 lines
3.6 KiB
Python
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],
|
|
)
|