From 98c6515192dd7e6f8c43bb4781a58af4b11b4294 Mon Sep 17 00:00:00 2001 From: hbyang Date: Fri, 21 Aug 2026 10:57:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(marketing):=20=EC=A7=81=EC=A0=91=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EA=B2=BD=EB=A1=9C=EC=97=90=20official=5Fs?= =?UTF-8?q?ite=5Furl=20=EC=9E=85=EB=A0=A5=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST /marketing 요청에 official_site_url(선택, 기본 null)을 추가한다. http/https 스킴만 허용하고 2048자 초과는 422로 거부하며, 값이 있으면 marketing.official_site_url에 저장돼 기존 영상 조회 응답에 그대로 노출된다. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CVSeUT2pEmeHc6hG6gVXUS --- app/home/api/routers/v1/home.py | 2 ++ app/home/schemas/home_schema.py | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/home/api/routers/v1/home.py b/app/home/api/routers/v1/home.py index b1575f1..87dffec 100644 --- a/app/home/api/routers/v1/home.py +++ b/app/home/api/routers/v1/home.py @@ -494,6 +494,7 @@ async def _crawling_logic(url: str, session: AsyncSession): - **customer_name**: 업체명 / 브랜드명 (필수) - **address**: 도로명 또는 지번 주소 (필수) - **category**: 업종/카테고리 자유 입력 (선택, 예: 펜션, 카페). 비우면 업체명 기반 AI 분류 +- **official_site_url**: 업체 공식 홈페이지 링크 (선택, http/https만 허용, 최대 2048자). 영상 응답의 official_site_url로 노출 ## 반환 정보 - **processed_info**: 가공된 장소 정보 (customer_name, region, detail_region_info) @@ -537,6 +538,7 @@ async def manual_marketing( # Step 3: 분석 결과 DB 저장 (place_id=None — 네이버 장소와 연결되지 않음) marketing_intel = MarketingIntel( place_id=None, + official_site_url=request_body.official_site_url, intel_result=marketing_analysis.model_dump(), ) session.add(marketing_intel) diff --git a/app/home/schemas/home_schema.py b/app/home/schemas/home_schema.py index a6635e5..05189e5 100644 --- a/app/home/schemas/home_schema.py +++ b/app/home/schemas/home_schema.py @@ -1,6 +1,6 @@ from typing import Literal, Optional -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator from app.utils.prompts.schemas import MarketingPromptOutput class CrawlingRequest(BaseModel): @@ -261,6 +261,7 @@ class ManualMarketingRequest(BaseModel): "store_name": "스테이 머뭄", "address": "전북특별자치도 군산시 절골길 18", "category": "펜션", + "official_site_url": "https://www.staymeomoom.com", } } ) @@ -268,6 +269,23 @@ class ManualMarketingRequest(BaseModel): store_name: str = Field(..., description="업체명 / 브랜드명") address: str = Field(..., description="도로명 또는 지번 주소") category: str = Field(default="", description="업체 업종/카테고리 자유 입력 (예: 펜션, 카페). 크롤링 경로와 동일하게 AI가 8개 industry enum으로 자동 분류하는 데 사용. 비우면 업체명 기반 AI 분류") + official_site_url: Optional[str] = Field( + default=None, + max_length=2048, + description="업체 공식 홈페이지 링크 (선택, http/https만 허용). 영상 응답의 official_site_url로 노출됨", + ) + + @field_validator("official_site_url") + @classmethod + def _validate_official_site_url(cls, v: Optional[str]) -> Optional[str]: + if v is None: + return None + v = v.strip() + if not v: + return None + if not v.startswith(("http://", "https://")): + raise ValueError("official_site_url은 http:// 또는 https://로 시작해야 합니다.") + return v class ErrorResponse(BaseModel):