"""Card-related API routes""" from typing import List, Dict from fastapi import APIRouter, HTTPException, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.models.card import Card, CardDraw, CardDrawResult from app.services.gacha import GachaService from app.services.card_master import card_master_service from app.repositories.user import UserRepository from app.repositories.card import CardRepository, UniqueCardRepository from app.db.base import get_session router = APIRouter(prefix="/cards", tags=["cards"]) @router.post("/draw", response_model=CardDrawResult) async def draw_card( draw_request: CardDraw, db: AsyncSession = Depends(get_session) ): """ カードを抽選する - **user_did**: ユーザーのatproto DID - **is_paid**: 課金ガチャかどうか """ try: gacha_service = GachaService(db) card, is_unique = await gacha_service.draw_card( user_did=draw_request.user_did, is_paid=draw_request.is_paid ) # 演出タイプを決定 animation_type = "normal" if is_unique: animation_type = "unique" elif card.status.value == "kira": animation_type = "kira" elif card.status.value in ["super_rare", "rare"]: animation_type = "rare" # 新規取得かチェック user_repo = UserRepository(db) card_repo = CardRepository(db) user = await user_repo.get_by_did(draw_request.user_did) count = await card_repo.count_user_cards(user.id, card.id) is_new = count == 1 # 今引いたカードが初めてなら1枚 result = CardDrawResult( card=card, is_new=is_new, animation_type=animation_type ) await db.commit() return result except Exception as e: await db.rollback() raise HTTPException(status_code=500, detail=str(e)) @router.get("/user/{user_did}", response_model=List[Card]) async def get_user_cards( user_did: str, skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_session) ): """ ユーザーの所有カード一覧を取得 - **user_did**: ユーザーのatproto DID """ user_repo = UserRepository(db) card_repo = CardRepository(db) user = await user_repo.get_by_did(user_did) if not user: raise HTTPException(status_code=404, detail="User not found") user_cards = await card_repo.get_user_cards(user.id, skip=skip, limit=limit) # Convert to API model cards = [] for uc in user_cards: card = Card( id=uc.card_id, cp=uc.cp, status=uc.status, skill=uc.skill, owner_did=user_did, obtained_at=uc.obtained_at, is_unique=uc.is_unique, unique_id=str(uc.unique_id) if uc.unique_id else None ) cards.append(card) return cards @router.get("/unique") async def get_unique_cards(db: AsyncSession = Depends(get_session)): """ 全てのuniqueカード一覧を取得(所有者情報付き) """ unique_repo = UniqueCardRepository(db) unique_cards = await unique_repo.get_all_unique_cards() return [ { "card_id": uc.card_id, "owner_did": uc.owner_did, "obtained_at": uc.obtained_at, "unique_id": str(uc.unique_id) } for uc in unique_cards ] @router.get("/stats") async def get_gacha_stats(db: AsyncSession = Depends(get_session)): """ ガチャ統計情報を取得 """ try: card_repo = CardRepository(db) # 総ガチャ実行数 total_draws = await card_repo.get_total_card_count() # レアリティ別カード数 cards_by_rarity = await card_repo.get_cards_by_rarity() # 成功率計算(簡易版) success_rates = {} if total_draws > 0: for rarity, count in cards_by_rarity.items(): success_rates[rarity] = count / total_draws # 最近の活動(最新10件) recent_cards = await card_repo.get_recent_cards(limit=10) recent_activity = [] for card_data in recent_cards: recent_activity.append({ "timestamp": card_data.get("obtained_at", "").isoformat() if card_data.get("obtained_at") else "", "user_did": card_data.get("owner_did", "unknown"), "card_name": f"Card #{card_data.get('card_id', 0)}", "rarity": card_data.get("status", "common") }) return { "total_draws": total_draws, "cards_by_rarity": cards_by_rarity, "success_rates": success_rates, "recent_activity": recent_activity } except Exception as e: raise HTTPException(status_code=500, detail=f"Statistics error: {str(e)}") @router.get("/master", response_model=List[Dict]) async def get_card_master_data(): """ 全カードマスターデータを取得(ai.jsonから) """ try: cards = card_master_service.get_all_cards() return cards except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get card master data: {str(e)}")