Implement WalletService with atomic credit/debit, row locking, and wallet/transactions API (task 909e3e9a59af)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"""Wallet & transactions API endpoints.
|
||||
|
||||
docs/planning/economy.md US-1 (task 909e3e9a59af).
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Annotated, Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.dependencies import CurrentUser
|
||||
from app.models.wallet import TransactionType
|
||||
from app.services.wallet import wallet_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
DbDep = Annotated[AsyncSession, Depends(get_db)]
|
||||
|
||||
|
||||
class WalletResponse(BaseModel):
|
||||
id: int
|
||||
user_id: int
|
||||
balance: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class TransactionResponse(BaseModel):
|
||||
id: int
|
||||
wallet_id: int
|
||||
type: TransactionType
|
||||
amount: int
|
||||
balance_after: int
|
||||
reference_type: str | None = None
|
||||
reference_id: str | None = None
|
||||
idempotency_key: str | None = None
|
||||
metadata_json: dict[str, Any] | None = None
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class TransactionListResponse(BaseModel):
|
||||
transactions: list[TransactionResponse]
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
|
||||
|
||||
@router.get("", response_model=WalletResponse)
|
||||
@router.get("/", response_model=WalletResponse)
|
||||
async def get_wallet(current_user: CurrentUser, db: DbDep):
|
||||
"""Retrieve the current user's wallet balance and metadata."""
|
||||
wallet = await wallet_service.get_or_create_wallet(db, current_user.id)
|
||||
return wallet
|
||||
|
||||
|
||||
@router.get("/transactions", response_model=TransactionListResponse)
|
||||
async def get_wallet_transactions(
|
||||
current_user: CurrentUser,
|
||||
db: DbDep,
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""List the current user's ledger transactions, ordered newest-first."""
|
||||
wallet = await wallet_service.get_or_create_wallet(db, current_user.id)
|
||||
txs, total = await wallet_service.get_transactions(db, wallet.id, limit=limit, offset=offset)
|
||||
return TransactionListResponse(
|
||||
transactions=txs,
|
||||
total=total,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
Reference in New Issue
Block a user