Implement WalletService with atomic credit/debit, row locking, and wallet/transactions API (task 909e3e9a59af)

This commit is contained in:
2026-09-15 19:54:03 +00:00
parent 80cbcd334b
commit d2bf0e9118
4 changed files with 418 additions and 1 deletions
+78
View File
@@ -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,
)