This commit is contained in:
hiondal 2025-06-16 00:42:59 +00:00
parent 33996f96eb
commit a04b6c97aa
2 changed files with 99 additions and 33 deletions

View File

@ -37,13 +37,15 @@ from pydantic import BaseModel, Field
# 프로젝트 모듈 import
from app.config.settings import settings
from app.models.restaurant_models import RestaurantSearchRequest, ErrorResponse
from app.models.restaurant_models import RestaurantInfo
from app.models.vector_models import (
VectorBuildRequest, VectorBuildResponse,
ActionRecommendationRequest, ActionRecommendationResponse,
ActionRecommendationSimpleResponse,
VectorDBStatusResponse, VectorDBStatus
VectorDBStatusResponse, VectorDBStatus,
FindReviewsResponse, StoredDataInfo
)
from app.services.restaurant_service import RestaurantService
from app.services.review_service import ReviewService
from app.services.vector_service import VectorService
@ -371,18 +373,24 @@ async def root():
@app.post("/find-reviews", response_model=FindReviewsResponse)
async def find_reviews(
request: FindReviewsRequest,
request: VectorBuildRequest, # VectorBuildRequest 재사용
vector_service: VectorService = Depends(get_vector_service),
restaurant_service: RestaurantService = Depends(get_restaurant_service),
review_service: ReviewService = Depends(get_review_service)
):
"""
지역과 가게명으로 리뷰를 찾아 Vector DB에 저장합니다.
지역과 가게명으로 리뷰를 찾아 Vector DB에 저장하고,
실제 저장된 JSON 구조를 응답으로 리턴합니다.
🔥 본인 가게 리뷰는 반드시 포함됩니다.
Note: force_rebuild 파라미터는 API에서는 무시됩니다.
"""
start_time = datetime.now()
logger.info(f"🔍 리뷰 검색 요청: {request.region} - {request.store_name}")
# force_rebuild 파라미터는 find-reviews에서는 무시
# (VectorBuildRequest를 재사용하지만 이 기능은 사용하지 않음)
try:
# 1단계: 본인 가게 검색
logger.info("1단계: 본인 가게 검색 중...")
@ -392,9 +400,9 @@ async def find_reviews(
if not target_restaurant:
logger.error(f"❌ 본인 가게를 찾을 수 없음: {request.store_name}")
raise HTTPException(
status_code=404,
detail=f"'{request.store_name}' 가게를 찾을 수 없습니다. 가게명과 지역을 정확히 입력해주세요."
return FindReviewsResponse(
success=False,
message=f"'{request.store_name}' 가게를 찾을 수 없습니다. 가게명과 지역을 정확히 입력해주세요."
)
logger.info(f"✅ 본인 가게 찾기 성공: {target_restaurant.place_name}")
@ -427,6 +435,7 @@ async def find_reviews(
review_results.append((target_restaurant.id, target_store_info, target_reviews))
# 동종 업체 리뷰 수집 (본인 가게 제외)
similar_store_names = []
for store in similar_stores[:10]: # 최대 10개 동종 업체
if store.id != target_restaurant.id: # 본인 가게 제외
store_info, reviews = await review_service.collect_store_reviews(
@ -434,8 +443,9 @@ async def find_reviews(
)
if store_info and reviews:
review_results.append((store.id, store_info, reviews))
similar_store_names.append(store.place_name)
# 6단계: Vector DB 저장
# 6단계: Vector DB 저장 및 저장 데이터 수집
logger.info("6단계: Vector DB 저장 중...")
try:
target_store_info_dict = {
@ -449,6 +459,26 @@ async def find_reviews(
'y': target_restaurant.y
}
# Vector DB에 저장하기 전에 저장될 데이터 구조 생성
stored_data = {}
# combine_store_and_reviews 함수를 사용하여 실제 저장될 JSON 구조 생성
from app.utils.data_utils import combine_store_and_reviews
import json
for store_id, store_info, reviews in review_results:
# Vector DB에 저장되는 실제 JSON 구조 생성
json_data = combine_store_and_reviews(store_info, reviews)
parsed_data = json.loads(json_data)
store_key = store_info.get('name', store_id)
stored_data[store_key] = StoredDataInfo(
store_info=parsed_data['store_info'],
reviews=parsed_data['reviews'],
review_summary=parsed_data['review_summary'],
combined_at=parsed_data['combined_at']
)
# Vector DB에 저장
vector_result = await vector_service.build_vector_store(
target_store_info_dict, review_results, food_category, request.region
@ -461,40 +491,56 @@ async def find_reviews(
except Exception as e:
logger.error(f"❌ Vector DB 구축 실패: {e}")
raise HTTPException(
status_code=500,
detail=f"Vector DB 구축 중 오류가 발생했습니다: {str(e)}"
return FindReviewsResponse(
success=False,
message=f"Vector DB 구축 중 오류가 발생했습니다: {str(e)}",
target_store={
'id': target_restaurant.id,
'place_name': target_restaurant.place_name,
'category_name': target_restaurant.category_name,
'address_name': target_restaurant.address_name,
'phone': target_restaurant.phone,
'place_url': target_restaurant.place_url,
'x': target_restaurant.x,
'y': target_restaurant.y
},
food_category=food_category
)
# 성공 응답
# 성공 응답 - Vector DB 저장 데이터 포함
total_reviews = sum(len(reviews) for _, _, reviews in review_results)
execution_time = (datetime.now() - start_time).total_seconds()
return FindReviewsResponse(
success=True,
message=f"✅ 본인 가게 리뷰 포함 보장 완료! (총 {len(review_results)}개 가게, {total_reviews}개 리뷰)",
target_store=RestaurantInfo(
id=target_restaurant.id,
place_name=target_restaurant.place_name,
category_name=target_restaurant.category_name,
address_name=target_restaurant.address_name,
phone=target_restaurant.phone,
place_url=target_restaurant.place_url,
x=target_restaurant.x,
y=target_restaurant.y
),
total_stores=len(review_results),
total_reviews=total_reviews,
message=f"✅ 본인 가게 '{target_restaurant.place_name}' 리뷰 분석 완료! "
f"{total_reviews}개 리뷰, {len(review_results)}개 업체 분석됨",
target_store={
'id': target_restaurant.id,
'place_name': target_restaurant.place_name,
'category_name': target_restaurant.category_name,
'address_name': target_restaurant.address_name,
'phone': target_restaurant.phone,
'place_url': target_restaurant.place_url,
'x': target_restaurant.x,
'y': target_restaurant.y
},
food_category=food_category,
region=request.region
total_reviews=total_reviews,
total_stores=len(review_results),
execution_time=execution_time,
stored_data=stored_data,
sample_similar_stores=similar_store_names[:5] # 상위 5개만 샘플로 제공
)
except HTTPException:
raise
except Exception as e:
logger.error(f"❌ 전체 프로세스 실패: {e}")
raise HTTPException(
status_code=500,
detail=f"서비스 처리 중 예상치 못한 오류가 발생했습니다: {str(e)}"
execution_time = (datetime.now() - start_time).total_seconds()
logger.error(f"❌ find_reviews 처리 실패: {str(e)}")
return FindReviewsResponse(
success=False,
message=f"처리 중 오류가 발생했습니다: {str(e)}",
execution_time=execution_time
)
@app.post(

View File

@ -20,6 +20,26 @@ class VectorBuildRequest(BaseModel):
example=False
)
class StoredDataInfo(BaseModel):
"""Vector DB에 저장된 데이터 정보"""
store_info: Dict[str, Any] = Field(description="가게 정보")
reviews: List[Dict[str, Any]] = Field(description="리뷰 목록")
review_summary: Dict[str, Any] = Field(description="리뷰 요약 정보")
combined_at: str = Field(description="데이터 결합 시간")
class FindReviewsResponse(BaseModel):
"""리뷰 검색 응답 모델 - Vector DB 저장 데이터 포함"""
success: bool = Field(description="검색 성공 여부")
message: str = Field(description="응답 메시지")
target_store: Optional[Dict[str, Any]] = Field(None, description="본인 가게 정보")
food_category: Optional[str] = Field(None, description="추출된 음식 카테고리")
total_reviews: int = Field(default=0, description="총 수집된 리뷰 수")
total_stores: int = Field(default=0, description="분석된 동종 업체 수")
execution_time: Optional[float] = Field(None, description="실행 시간(초)")
# Vector DB 저장 데이터
stored_data: Optional[Dict[str, StoredDataInfo]] = Field(None, description="Vector DB에 저장된 데이터")
sample_similar_stores: List[str] = Field(default=[], description="동종 업체 샘플 목록")
class VectorBuildResponse(BaseModel):
"""Vector DB 구축 응답 모델"""
success: bool = Field(description="구축 성공 여부")