from fastapi import APIRouter, Depends, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.credit.exceptions import ChargeRequestForbiddenError, ChargeRequestNotFoundError from app.credit.models import ChargeRequestStatus, CreditChargeRequest, CreditTransaction from app.credit.schemas.credit_schema import ( ChargeRequestCreate, ChargeRequestListResponse, ChargeRequestResponse, CreditTransactionListResponse, CreditTransactionResponse, ) from app.database.session import get_session from app.user.dependencies.auth import get_current_user from app.user.models import User router = APIRouter(prefix="/credits", tags=["Credits"]) @router.post( "/charge-requests", response_model=ChargeRequestResponse, status_code=201, summary="크레딧 충전 요청 제출", ) async def create_charge_request( body: ChargeRequestCreate, current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> ChargeRequestResponse: charge_request = CreditChargeRequest( user_uuid=current_user.user_uuid, requested_amount=body.requested_amount, message=body.message, ) session.add(charge_request) await session.commit() await session.refresh(charge_request) return ChargeRequestResponse.model_validate(charge_request) @router.get( "/charge-requests", response_model=ChargeRequestListResponse, summary="내 충전 요청 목록", ) async def list_charge_requests( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> ChargeRequestListResponse: offset = (page - 1) * page_size total_result = await session.execute( select(func.count()).where(CreditChargeRequest.user_uuid == current_user.user_uuid) ) total = total_result.scalar_one() items_result = await session.execute( select(CreditChargeRequest) .where(CreditChargeRequest.user_uuid == current_user.user_uuid) .order_by(CreditChargeRequest.created_at.desc()) .offset(offset) .limit(page_size) ) items = items_result.scalars().all() return ChargeRequestListResponse( items=[ChargeRequestResponse.model_validate(i) for i in items], total=total, page=page, page_size=page_size, ) @router.get( "/charge-requests/{request_id}", response_model=ChargeRequestResponse, summary="내 충전 요청 상세", ) async def get_charge_request( request_id: int, current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> ChargeRequestResponse: result = await session.execute( select(CreditChargeRequest).where( CreditChargeRequest.id == request_id, CreditChargeRequest.user_uuid == current_user.user_uuid, ) ) charge_request = result.scalar_one_or_none() if charge_request is None: raise ChargeRequestNotFoundError() return ChargeRequestResponse.model_validate(charge_request) @router.delete( "/charge-requests/{request_id}", status_code=204, summary="충전 요청 취소 (pending 상태만)", ) async def cancel_charge_request( request_id: int, current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> None: result = await session.execute( select(CreditChargeRequest).where(CreditChargeRequest.id == request_id) ) charge_request = result.scalar_one_or_none() if charge_request is None: raise ChargeRequestNotFoundError() if charge_request.user_uuid != current_user.user_uuid: raise ChargeRequestForbiddenError() from app.credit.exceptions import InvalidRequestStateError if charge_request.status != ChargeRequestStatus.PENDING: raise InvalidRequestStateError("대기 중인 요청만 취소할 수 있습니다.") charge_request.status = ChargeRequestStatus.CANCELLED await session.commit() @router.get( "/transactions", response_model=CreditTransactionListResponse, summary="내 크레딧 거래 이력", ) async def list_transactions( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), current_user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ) -> CreditTransactionListResponse: offset = (page - 1) * page_size total_result = await session.execute( select(func.count()).where(CreditTransaction.user_uuid == current_user.user_uuid) ) total = total_result.scalar_one() items_result = await session.execute( select(CreditTransaction) .where(CreditTransaction.user_uuid == current_user.user_uuid) .order_by(CreditTransaction.created_at.desc()) .offset(offset) .limit(page_size) ) items = items_result.scalars().all() return CreditTransactionListResponse( items=[CreditTransactionResponse.model_validate(i) for i in items], total=total, page=page, page_size=page_size, )