people-reading/backend/app/api/v1/endpoints/readings.py
2026-05-12 20:50:15 +08:00

111 lines
4.2 KiB
Python

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from pydantic import ValidationError
from sqlalchemy import desc, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import AsyncSessionLocal, get_db
from app.core.security import get_current_user
from app.models.reading import Reading
from app.models.uploaded_image import UploadedImage
from app.models.user import User
from app.schemas.quota import QuotaResponse
from app.schemas.reading import BaziInput, ReadingCreate, ReadingDetail, ReadingSummary
from app.services.image_service import ImageService
from app.services.quota_service import QuotaService
from app.services.reading_service import ReadingService
router = APIRouter()
async def generate_reading_task(reading_id: str) -> None:
async with AsyncSessionLocal() as session:
await ReadingService().generate(session, reading_id)
@router.get("/quota", response_model=QuotaResponse)
async def get_reading_quota(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
return await QuotaService().get_quota(db, user.id)
@router.post("", response_model=ReadingDetail, status_code=status.HTTP_201_CREATED)
async def create_reading(
payload: ReadingCreate,
background_tasks: BackgroundTasks,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
input_data = dict(payload.input_data)
image_id = payload.image_id
if payload.reading_type in {"palm", "face"}:
image_result = await db.execute(select(UploadedImage).where(UploadedImage.id == image_id, UploadedImage.user_id == user.id))
image = image_result.scalar_one_or_none()
if image is None:
raise HTTPException(status_code=404, detail="Image not found")
elif payload.reading_type == "bazi":
try:
input_data = BaziInput.model_validate(input_data).model_dump()
except ValidationError as exc:
raise HTTPException(status_code=422, detail=exc.errors()) from exc
await QuotaService().consume(db, user.id)
reading = Reading(
user_id=user.id,
reading_type=payload.reading_type,
image_id=image_id,
input_data=input_data,
status="pending",
)
db.add(reading)
await db.flush()
await db.refresh(reading)
await db.commit()
background_tasks.add_task(generate_reading_task, reading.id)
return reading
@router.get("", response_model=list[ReadingSummary])
async def list_readings(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(Reading).where(Reading.user_id == user.id).order_by(desc(Reading.created_at)).limit(80)
)
readings = result.scalars().all()
return [
ReadingSummary(
id=reading.id,
reading_type=reading.reading_type,
status=reading.status,
created_at=reading.created_at,
overall_summary=(reading.report_data or {}).get("overall_summary") if reading.report_data else None,
)
for reading in readings
]
@router.get("/{reading_id}", response_model=ReadingDetail)
async def get_reading(reading_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
return await _get_owned_reading(db, reading_id, user.id)
@router.delete("/{reading_id}")
async def delete_reading(reading_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
reading = await _get_owned_reading(db, reading_id, user.id)
image = None
if reading.image_id:
image_result = await db.execute(select(UploadedImage).where(UploadedImage.id == reading.image_id))
image = image_result.scalar_one_or_none()
await db.delete(reading)
await db.flush()
if image:
ImageService().delete(image.storage_key)
await db.delete(image)
return {"status": "deleted"}
async def _get_owned_reading(db: AsyncSession, reading_id: str, user_id: str) -> Reading:
result = await db.execute(select(Reading).where(Reading.id == reading_id, Reading.user_id == user_id))
reading = result.scalar_one_or_none()
if reading is None:
raise HTTPException(status_code=404, detail="Reading not found")
return reading