11# app/routers/chat_rest.py
2- from fastapi import APIRouter , Depends , HTTPException , Query , Path
2+ from fastapi import APIRouter , Depends , HTTPException , Query , Path , BackgroundTasks # 🔹 BackgroundTasks 추가
33from pydantic import BaseModel
44from typing import Optional , List
55from sqlalchemy .orm import Session
66from datetime import datetime , timezone
7- import asyncio
87
98from app .core .db import get_db
109from app .core .auth import get_current_user
1110from app .models .user import User
1211from app .models .posting import Posting
1312from app .models .chat import ChatRoom , ChatMessage , ChatRead
1413from app .routers import chat_ws
14+ from app .routers .chat_list_ws import broadcast_chat_created # 🔹 추가
1515
1616router = APIRouter (prefix = "/api/chat" , tags = ["Chat REST" ])
1717
18+ # 🔹 채팅방 생성 요청/응답 모델 추가
19+ class CreateChatIn (BaseModel ):
20+ postingId : int
21+
22+ class CreateChatOut (BaseModel ):
23+ chatId : int
24+ postingId : int
25+ sellerId : int
26+ buyerId : int
27+ createdAt : str
28+
1829
1930class MessageItem (BaseModel ):
2031 messageId : int
@@ -46,6 +57,46 @@ class DealStatusOut(BaseModel):
4657 changedAt : str
4758
4859
60+ # 🔥 새 채팅방 생성 + 판매자에게만 chat_created 브로드캐스트
61+ @router .post ("" , response_model = CreateChatOut )
62+ def create_chat (
63+ body : CreateChatIn ,
64+ background_tasks : BackgroundTasks ,
65+ db : Session = Depends (get_db ),
66+ me : User = Depends (get_current_user ),
67+ ):
68+ # 1) 게시글 확인
69+ posting = db .query (Posting ).filter (Posting .id == body .postingId ).first ()
70+ if not posting :
71+ raise HTTPException (status_code = 404 , detail = "posting_not_found" )
72+
73+ # 2) 자기 자신에게 채팅 방지
74+ if posting .seller_id == me .user_id :
75+ raise HTTPException (status_code = 400 , detail = "cannot_chat_with_self" )
76+
77+ # 3) 채팅방 생성 (buyer = me, seller = 게시글 작성자)
78+ room = ChatRoom (
79+ posting_id = posting .id ,
80+ seller_id = posting .seller_id ,
81+ buyer_id = me .user_id ,
82+ # status는 모델 default 있으면 생략 가능
83+ )
84+ db .add (room )
85+ db .commit ()
86+ db .refresh (room )
87+
88+ # 4) 🔔 판매자에게만 chat_created 이벤트 전송 (백그라운드)
89+ background_tasks .add_task (broadcast_chat_created , room , db )
90+
91+ return CreateChatOut (
92+ chatId = room .id ,
93+ postingId = room .posting_id ,
94+ sellerId = room .seller_id ,
95+ buyerId = room .buyer_id ,
96+ createdAt = room .created_at .astimezone (timezone .utc ).isoformat (),
97+ )
98+
99+
49100@router .get ("/{chat_id}" , response_model = MessagesOut )
50101def list_messages (
51102 chat_id : int = Path (...),
@@ -70,9 +121,9 @@ def list_messages(
70121
71122 messages : List [MessageItem ] = []
72123 for m in rows :
73- if m .type == "SYSTEM" :
124+ if m .type . upper () == "SYSTEM" :
74125 is_mine = False
75- read = True # 시스템 메세지는 그냥 항상 읽은 걸로 취급
126+ read = True # 시스템 메세지는 항상 읽은 걸로 취급
76127 else :
77128 is_mine = (m .sender_id == me .user_id )
78129 read = db .query (ChatRead ).filter (ChatRead .message_id == m .id ).count () > 0
@@ -91,23 +142,22 @@ def list_messages(
91142 next_cursor = rows [- 1 ].id if rows else None
92143 return MessagesOut (messages = messages , hasNext = has_next , nextCursor = next_cursor )
93144
145+
94146@router .patch ("/{chat_id}/deal" , response_model = DealStatusOut )
95147async def update_deal_status (
96148 chat_id : int = Path (..., description = "대상 채팅방 ID" ),
97149 body : UpdateDealStatusIn = ...,
98150 db : Session = Depends (get_db ),
99151 me : User = Depends (get_current_user ),
100152):
101- # 1) 채팅방 조회
153+ # 이하 내용은 네가 올린 그대로 (거래 상태 변경 + system 메시지 + broadcast_deal_update)
102154 room = db .query (ChatRoom ).filter (ChatRoom .id == chat_id ).first ()
103155 if not room :
104156 raise HTTPException (status_code = 404 , detail = "chat_not_found" )
105157
106- # 2) 권한 체크
107158 if me .user_id not in (room .buyer_id , room .seller_id ):
108159 raise HTTPException (status_code = 403 , detail = "forbidden" )
109160
110- # 3) 허용 status만
111161 allowed = {"ACTIVE" , "RESERVED" , "COMPLETED" }
112162 if body .status not in allowed :
113163 raise HTTPException (status_code = 400 , detail = "invalid_status" )
@@ -121,33 +171,27 @@ async def update_deal_status(
121171 if prev_status == new_status :
122172 raise HTTPException (status_code = 400 , detail = "same_status" )
123173
124- # 4) 게시글 조회
125174 posting = db .query (Posting ).filter (Posting .id == room .posting_id ).first ()
126175 if not posting :
127176 raise HTTPException (status_code = 404 , detail = "posting_not_found" )
128177
129- # 5) posting.status 연동
130178 if prev_status == "ACTIVE" and new_status == "RESERVED" :
131179 if posting .status == "SELLING" :
132180 posting .status = "RESERVED"
133-
134181 elif prev_status == "RESERVED" and new_status == "ACTIVE" :
135182 if posting .status == "RESERVED" :
136183 posting .status = "SELLING"
137-
138184 elif prev_status == "RESERVED" and new_status == "COMPLETED" :
139185 if posting .status == "RESERVED" :
140186 posting .status = "SOLD"
141187
142188 seller = db .query (User ).filter (User .user_id == room .seller_id ).first ()
143189 buyer = db .query (User ).filter (User .user_id == room .buyer_id ).first ()
144-
145190 if seller is not None :
146191 seller .sell_count = (seller .sell_count or 0 ) + 1
147192 if buyer is not None :
148193 buyer .buy_count = (buyer .buy_count or 0 ) + 1
149194
150- # 6) 채팅방 상태 변경
151195 room .status = new_status
152196 changed_at = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
153197
@@ -166,24 +210,21 @@ async def update_deal_status(
166210 else :
167211 msg_text = f"{ nick } 님이 거래 상태를 변경했습니다"
168212
169- # ✅ 시스템 메시지를 ChatMessage 로 저장
170213 system_msg = ChatMessage (
171214 room_id = room .id ,
172- sender_id = me .user_id , # 시스템이라서 보낸 사람 없음
215+ sender_id = me .user_id ,
173216 type = "system" ,
174217 content = msg_text ,
175218 )
176219 db .add (system_msg )
177220 db .commit ()
178221 db .refresh (system_msg )
179222
180- # ✅ 기존처럼 브로드캐스트 (필요하면 messageId도 같이 넘겨도 됨)
181223 await chat_ws .broadcast_deal_update (
182224 chat_id = room .id ,
183225 deal_status = new_status ,
184226 post_status = posting .status ,
185227 system_message = msg_text ,
186- # 필요하면 system_message_id=system_msg.id 이런 식으로 확장
187228 )
188229
189230 return DealStatusOut (
@@ -195,4 +236,4 @@ async def update_deal_status(
195236 postStatus = posting .status ,
196237 changedBy = me .user_id ,
197238 changedAt = changed_at ,
198- )
239+ )
0 commit comments