1+ from typing import Annotated , List , Optional
2+
3+ from sqlalchemy .ext .asyncio import AsyncSession
4+ from sqlalchemy import select , update , func
5+ from sqlalchemy .orm import selectinload , joinedload , contains_eager
6+ from datetime import datetime
7+ from fastapi import Depends
8+
9+ from carrot .app .auction .models import Auction , Bid , AuctionStatus
10+ from carrot .app .product .models import Product
11+ from carrot .db .connection import get_db_session
12+
13+ from carrot .app .auction .exceptions import (
14+ AuctionAlreadyExistsError ,
15+ AuctionNotFoundError ,
16+ NotAllowedActionError
17+ )
18+
19+ class AuctionRepository :
20+ def __init__ (self , session : AsyncSession ) -> None :
21+ self .session = session
22+
23+
24+ async def create_auction (self , product : Product , auction : Auction ) -> Auction :
25+ self .session .add (product )
26+ await self .session .flush () # Ensure product ID is generated
27+
28+ auction .product_id = product .id
29+ self .session .add (auction )
30+
31+ await self .session .commit ()
32+ await self .session .refresh (auction , attribute_names = ["product" ])
33+ return auction
34+
35+ async def get_auction_by_id (self , auction_id : str ) -> Optional [Auction ]:
36+ # 1. 쿼리 작성 (bids 로딩 제거)
37+ stmt = (
38+ select (Auction )
39+ .where (Auction .id == auction_id )
40+ .options (joinedload (Auction .product )) # 상품 정보는 보통 같이 필요하니까요!
41+ )
42+
43+ result = await self .session .execute (stmt )
44+
45+ # 2. 결과 추출
46+ # 이제 1:N 조인이 없으므로 .unique()가 없어도 에러는 안 나지만,
47+ # 관습적으로 넣어두거나 scalar_one_or_none()으로 안전하게 받습니다.
48+ auction = result .scalar_one_or_none ()
49+
50+ if not auction :
51+ raise AuctionNotFoundError ()
52+
53+ return auction
54+
55+ async def get_active_auctions (
56+ self ,
57+ category_id : Optional [str ] = None ,
58+ region_id : Optional [str ] = None
59+ )-> List [Auction ]:
60+ stmt = (
61+ select (Auction )
62+ .where (Auction .status == AuctionStatus .ACTIVE )
63+ .options (selectinload (Auction .product ))
64+ )
65+
66+ if category_id :
67+ stmt = stmt .join (Auction .product ).where (Product .category_id == category_id )
68+
69+ if region_id :
70+ stmt = stmt .join (Auction .product ).where (Product .region_id == region_id )
71+
72+ stmt = stmt .order_by (Auction .end_at .asc ())
73+
74+ result = await self .session .execute (stmt )
75+ return result .scalars ().all ()
76+
77+ async def delete_auction (self , auction : Auction ) -> None :
78+ await self .session .delete (auction )
79+ await self .session .commit ()
80+
81+ async def update_auction (self , auction : Auction ) -> Auction :
82+ merged = await self .session .merge (auction )
83+ await self .session .commit ()
84+ return merged
85+
86+ async def update_auction_without_commit (self , auction : Auction ) -> Auction :
87+ merged = await self .session .merge (auction )
88+ return merged
89+
90+ async def add_bid_without_commit (self , bid : Bid ) -> Bid :
91+ self .session .add (bid )
92+ return bid
93+
94+ async def place_bid (self , bid : Bid ) -> Bid :
95+ self .session .add (bid )
96+ await self .session .commit ()
97+ await self .session .refresh (bid )
98+ return bid
99+
100+ async def update_bid (self , bid : Bid ) -> Bid :
101+ merged = await self .session .merge (bid )
102+ await self .session .commit ()
103+ await self .session .refresh (merged )
104+ return merged
105+
106+ async def gets_user_bids (self , user_id : str ) -> List [Bid ]:
107+ stmt = (
108+ select (Bid )
109+ .where (Bid .bidder_id == user_id )
110+ .options (joinedload (Bid .auction ).joinedload (Auction .product ))
111+ )
112+ result = await self .session .execute (stmt )
113+ return result .scalars ().all ()
114+
115+
0 commit comments