diff --git a/backend/router/v1/chat/chat.py b/backend/router/v1/chat/chat.py index d0cad93..baf8ea9 100644 --- a/backend/router/v1/chat/chat.py +++ b/backend/router/v1/chat/chat.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Path from fastapi.security import HTTPAuthorizationCredentials from common.models.gmodel import UserInfo @@ -17,7 +17,7 @@ router = APIRouter(prefix="/v1/negotiation", tags=["Chat"], responses={404: {"de description="채팅 화면 진입용. 상품/견적 정보 + 현재 세션 상태 + 마감 시각(타이머)을 반환한다. 소유(공급사) 검증.", ) async def chat_init( - session_id: str, + session_id: str = Path(description="대상 협상 세션 uuid"), user_info: UserInfo = Depends(IsValidAccessToken), credentials: HTTPAuthorizationCredentials = Depends(security), service: ChatService = Depends(), @@ -32,7 +32,7 @@ async def chat_init( description="세션의 대화 말풍선 목록(seq 오름차순). 비어 있고 협상중이면 오프닝 메시지를 생성해 포함한다.", ) async def chat_messages( - session_id: str, + session_id: str = Path(description="대상 협상 세션 uuid"), user_info: UserInfo = Depends(IsValidAccessToken), credentials: HTTPAuthorizationCredentials = Depends(security), service: ChatService = Depends(), @@ -47,8 +47,8 @@ async def chat_messages( description="유저 입력을 보내고 agent 가 만든 봇 응답 1건을 반환한다(append-only). 종료 시 세션 입찰을 확정한다.", ) async def chat_send( - session_id: str, - req: Req_ChatSend, + session_id: str = Path(description="대상 협상 세션 uuid"), + req: Req_ChatSend = ..., user_info: UserInfo = Depends(IsValidAccessToken), credentials: HTTPAuthorizationCredentials = Depends(security), service: ChatService = Depends(), diff --git a/backend/router/v1/chat/protocol.py b/backend/router/v1/chat/protocol.py index d944fa2..71ba77c 100644 --- a/backend/router/v1/chat/protocol.py +++ b/backend/router/v1/chat/protocol.py @@ -17,42 +17,42 @@ from common.models.gmodel import Res_WebPacketProtocol, WebPacketProtocol # 협상 결과 요약 카드(summaryRSP/summaryCM)에 표시할 데이터. 종료 스텝에서만 채워 내려간다. # 필드명은 프론트 ChatSummary 타입과 1:1 (item_isVAT 등 camelCase 유지). class ChatSummary(WebPacketProtocol): - md_name: str = "" - md_email: str = "" - md_phone_number: str = "" - item_code: str = "" - item_name: str = "" - item_spec: str = "" - item_moq: str = "" - item_model: str = "" - item_maker: str = "" - item_isVAT: bool = False - item_lead_time: str = "" - item_display_date: str = "" - item_delivery_type: str = "" - final_price: int = 0 - nego_start_date: str = "" - nego_end_date: str = "" - supplier_name: str = "" - supplier_manager_name: str = "" - supplier_manager_email: str = "" - supplier_manager_phone: str = "" - delivery_type: Optional[str] = None + md_name: str = Field("", description="견적 담당 MD(고객사 담당자) 이름") + md_email: str = Field("", description="MD 이메일") + md_phone_number: str = Field("", description="MD 연락처") + item_code: str = Field("", description="상품 코드") + item_name: str = Field("", description="상품명") + item_spec: str = Field("", description="상품 규격") + item_moq: str = Field("", description="최소 주문 수량") + item_model: str = Field("", description="모델명") + item_maker: str = Field("", description="제조사") + item_isVAT: bool = Field(False, description="VAT 포함 여부") + item_lead_time: str = Field("", description="배송 리드타임(일)") + item_display_date: str = Field("", description="상품 게시일(견적 시작 시각, ISO 8601)") + item_delivery_type: str = Field("", description="상품 기본 배송유형 라벨(선택값 없을 때 폴백용)") + final_price: int = Field(0, description="최종 제시가(원)") + nego_start_date: str = Field("", description="협상 시작 시각(견적 시작, ISO 8601)") + nego_end_date: str = Field("", description="협상 종료 시각(견적 마감, ISO 8601)") + supplier_name: str = Field("", description="공급사명") + supplier_manager_name: str = Field("", description="공급사 담당자 이름") + supplier_manager_email: str = Field("", description="공급사 담당자 이메일") + supplier_manager_phone: str = Field("", description="공급사 담당자 연락처") + delivery_type: Optional[str] = Field(None, description="재견적 '배송형태선택'에서 공급사가 고른 배송형태 라벨(재협상은 단계 없어 null)") # 말풍선 한 건. sender 는 ChatSender 정수 코드(1=BOT, 2=USER)로 내려가고 라벨 매핑은 프론트가 한다. class ChatMessage(WebPacketProtocol): - chat_id: str = "" - session_id: str = "" - seq: int = 0 + chat_id: str = Field("", description="채팅 메시지 식별자(uuid)") + session_id: str = Field("", description="소속 협상 세션 uuid") + seq: int = Field(0, description="세션 내 메시지 순번") sender: int = Field(0, description="발신자 코드 (ChatSender: 1=BOT, 2=USER)") - script: str = "" + script: str = Field("", description="말풍선 본문 텍스트") user_input_type: Optional[str] = Field(None, description="유저 입력 종류: text|percent|price") - step: str = "" + step: str = Field("", description="agent 내부 단계 코드") display_step: str = Field("", description="agent client_step (표시용 단계)") next_input_mode: Optional[str] = Field(None, description="다음 입력 모드: confirm|yes_no|percent|price|delivery_type") next_input_type: Optional[list[str]] = Field(None, description="다음 입력 선택지(버튼 라벨)") - chat_end: bool = False + chat_end: bool = Field(False, description="이 메시지로 협상이 종료되는지 여부") indicator_value: Optional[float] = Field(None, description="협상 지표(1~99). 가격협상 턴에 표시") bot_chat_type: Optional[str] = Field(None, description="폼 종류: summaryRSP|summaryCM|rejectRSP|rejectCM|indicator") summary: Optional[ChatSummary] = Field(None, description="summaryRSP/summaryCM 일 때만 채워짐") @@ -60,28 +60,28 @@ class ChatMessage(WebPacketProtocol): # 채팅 진입 — 상품/견적 메타 + 현재 세션 상태 + 마감 시각(타이머용) class Res_ChatInit(Res_WebPacketProtocol): - session_id: str = "" + session_id: str = Field("", description="협상 세션 uuid") session_status: int = Field(0, description="세션 상태 코드 (SessionStatus: 1=생성 2=진행중 3=완료 4=미참여 5=거부)") - quotation_id: str = "" + quotation_id: str = Field("", description="소속 견적 uuid") quotation_end_time: str = Field("", description="견적 마감 시각 (ISO 8601, 타이머용)") - quotation_memo: str = "" - item_id: str = "" - item_name: str = "" - item_code: str = "" - item_image: str = "" - item_price: int = 0 - item_model_name: str = "" - item_maker_name: str = "" - item_spec: str = "" - item_lead_time: str = "" - item_min_order_quantity: str = "" - item_vat_yn: Optional[bool] = None - item_delivery_fee_yn: Optional[bool] = None + quotation_memo: str = Field("", description="견적 메모") + item_id: str = Field("", description="상품 uuid") + item_name: str = Field("", description="상품명") + item_code: str = Field("", description="상품 코드") + item_image: str = Field("", description="상품 이미지 URL") + item_price: int = Field(0, description="상품 가격(원)") + item_model_name: str = Field("", description="모델명") + item_maker_name: str = Field("", description="제조사") + item_spec: str = Field("", description="상품 규격") + item_lead_time: str = Field("", description="배송 리드타임(일)") + item_min_order_quantity: str = Field("", description="최소 주문 수량") + item_vat_yn: Optional[bool] = Field(None, description="VAT 포함 여부(미설정 시 null)") + item_delivery_fee_yn: Optional[bool] = Field(None, description="배송비 포함 여부(미설정 시 null)") # 대화 히스토리(재진입 복원) class Res_ChatMessages(Res_WebPacketProtocol): - items: list[ChatMessage] = [] + items: list[ChatMessage] = Field(default_factory=list, description="대화 말풍선 목록 (seq 오름차순)") # 한 턴 전송. user_input 은 버튼 텍스트 또는 가격/퍼센트 문자열. @@ -92,5 +92,5 @@ class Req_ChatSend(WebPacketProtocol): # append-only: 새 봇 메시지 1건 + 갱신된 세션 상태만 반환(전체 refetch 회피) class Res_ChatSend(Res_WebPacketProtocol): - message: Optional[ChatMessage] = None - session_status: int = 0 + message: Optional[ChatMessage] = Field(None, description="새로 생성된 봇 메시지 1건(append-only)") + session_status: int = Field(0, description="갱신된 세션 상태 코드 (SessionStatus)")