import uuid from datetime import datetime from typing import Optional from pydantic import ConfigDict from common.enums import FactStatus, SourceType from common.models.gmodel import Res_WebPacketProtocol, WebPacketProtocol class FaqProtocol(WebPacketProtocol): pass class Req_CreateFaq(FaqProtocol): """사장님이 직접 쓴 FAQ. ★ generated_by 를 요청으로 받지 않는다 — 받으면 LLM 생성물을 사람이 쓴 것처럼 올려 승인 절차를 통째로 건너뛸 수 있다. 출처는 서버가 OWNER 로 고정한다.""" question: str = "" answer: str = "" sort_order: Optional[int] = None # 미지정이면 기존 FAQ 뒤에 붙인다 class Req_TransitionFaq(FaqProtocol): """검증 상태 전이. 허용 전이는 fact 와 같은 표(FACT_STATUS_TRANSITIONS)가 유일한 소스다. CORRECTED 로 갈 때는 고친 question / answer 중 하나 이상이 필요하다(다른 전이에선 무시).""" status: FactStatus = FactStatus.VERIFIED question: Optional[str] = None answer: Optional[str] = None class FaqData(WebPacketProtocol): model_config = ConfigDict(from_attributes=True) faq_id: uuid.UUID place_id: uuid.UUID question: str answer: str # 근거 목록. 컬럼명은 ids 지만 copy 잡이 담는 값은 fact 의 **key** 다 — # 사람이 승인 화면에서 "무슨 사실로 쓴 문장인지" 읽을 수 있어야 하기 때문이다. source_fact_ids: Optional[list[str]] = None generated_by: SourceType status: FactStatus sort_order: int = 0 created_at: Optional[datetime] = None updated_at: Optional[datetime] = None class Res_FaqList(Res_WebPacketProtocol): faqs: list[FaqData] = [] # ★ 사이트에 나갈 수 있는 건수(VERIFIED·CORRECTED). FAQPage JSON-LD 는 이 건수만큼만 나간다. publishable: int = 0 # 승인 대기 건수 — 관리 화면의 '검토할 것' 배지. # fact 와 달리 UNVERIFIED 도 포함한다: FAQ 에는 '재수집 후보' 개념이 없고, # LLM 이 만들어 둔 UNVERIFIED 가 곧 사장님 승인 대기 큐다. pending_review: int = 0 class Res_Faq(Res_WebPacketProtocol): faq: Optional[FaqData] = None