This commit is contained in:
ondal 2025-06-17 12:30:32 +09:00
parent ceb05de7f3
commit 3d3f1e5383
4 changed files with 221 additions and 5 deletions

View File

@ -94,7 +94,8 @@ vector/
| Method | Endpoint | 설명 | 요청 모델 | 응답 모델 | | Method | Endpoint | 설명 | 요청 모델 | 응답 모델 |
|--------|----------|------|-----------|-----------| |--------|----------|------|-----------|-----------|
| `POST` | `/find-reviews` | 리뷰 수집 및 Vector DB 저장 | VectorBuildRequest | FindReviewsResponse | | `POST` | `/find-reviews` | 리뷰 수집 및 Vector DB 저장 | VectorBuildRequest | FindReviewsResponse |
| `POST` | `/action-recommendation` | AI 기반 비즈니스 추천 | ActionRecommendationRequest | ActionRecommendationSimpleResponse | | `POST` | `/action-recommendation` | AI 기반 비즈니스 추천 | ActionRecommendationRequest |
| `GET` | `/store/{store_id}` | store_id로 매장 정보 조회 | Path Parameter | StoreInfoResponse ActionRecommendationSimpleResponse |
## 🚀 빠른 시작 ## 🚀 빠른 시작
@ -779,6 +780,80 @@ curl -X POST "http://vector-api.20.249.191.180.nip.io/action-recommendation" \
} }
``` ```
### 3. 매장 정보 조회 API
#### 요청 예시
```bash
curl -X GET "http://vector-api.20.249.191.180.nip.io/store/501745730"
```
#### 응답 예시 (성공)
```json
{
"success": true,
"message": "매장 정보 조회 성공: 맛있는 치킨집",
"store_id": "501745730",
"store_info": {
"id": "501745730",
"place_name": "맛있는 치킨집",
"category_name": "음식점 > 치킨",
"address_name": "서울특별시 강남구 역삼동 123-45",
"phone": "02-123-4567",
"place_url": "http://place.map.kakao.com/501745730",
"x": "127.0276543",
"y": "37.4979234"
},
"reviews": [
{
"reviewer_name": "김○○",
"rating": 5,
"date": "2024-06-10",
"content": "치킨이 정말 맛있어요!",
"badges": ["맛집", "친절", "빠른배달"],
"likes": 12
}
],
"review_summary": {
"total_reviews": 23,
"average_rating": 4.2,
"rating_distribution": {
"5": 12,
"4": 8,
"3": 2,
"2": 1,
"1": 0
},
"total_likes": 145,
"common_keywords": ["맛집", "친절", "빠른배달", "바삭함"]
},
"metadata": {
"store_id": "501745730",
"store_name": "맛있는 치킨집",
"food_category": "치킨",
"region": "서울특별시 강남구 역삼동",
"last_updated": "2024-06-17T10:30:00"
},
"last_updated": "2024-06-17T10:30:00",
"total_reviews": 23,
"execution_time": 0.15
}
```
#### 응답 예시 (실패)
```json
{
"success": false,
"message": "매장 정보를 찾을 수 없습니다: store_id=999999",
"store_id": "999999",
"store_info": null,
"reviews": null,
"review_summary": null,
"metadata": null,
"last_updated": null,
"total_reviews": 0,
"execution_time": 0.05
}
```
## ⚙️ 환경 설정 ## ⚙️ 환경 설정
### 필수 환경변수 ### 필수 환경변수

View File

@ -30,7 +30,7 @@ from contextlib import asynccontextmanager
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
import asyncio import asyncio
from fastapi import FastAPI, HTTPException, Depends from fastapi import FastAPI, HTTPException, Depends, Path
from fastapi.responses import HTMLResponse, JSONResponse from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
@ -673,4 +673,63 @@ async def health_check():
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host=settings.HOST, port=settings.PORT) uvicorn.run(app, host=settings.HOST, port=settings.PORT)
@app.get(
"/store/{store_id}",
response_model=StoreInfoResponse,
summary="매장 정보 조회",
description="store_id를 이용하여 Vector DB에서 매장 기본정보와 리뷰 정보를 조회합니다"
)
async def get_store_info(
store_id: str = Path(..., description="조회할 매장 ID", example="501745730"),
vector_service: VectorService = Depends(get_vector_service)
):
"""🏪 store_id로 매장 정보 조회 API"""
start_time = datetime.now()
try:
logger.info(f"🔍 매장 정보 조회 요청: store_id={store_id}")
# Vector DB에서 매장 정보 조회
store_data = vector_service.get_store_by_id(store_id)
execution_time = (datetime.now() - start_time).total_seconds()
if not store_data:
return StoreInfoResponse(
success=False,
message=f"매장 정보를 찾을 수 없습니다: store_id={store_id}",
store_id=store_id,
execution_time=execution_time
)
# 성공 응답
store_info = store_data.get('store_info', {})
reviews = store_data.get('reviews', [])
review_summary = store_data.get('review_summary', {})
metadata = store_data.get('metadata', {})
return StoreInfoResponse(
success=True,
message=f"매장 정보 조회 성공: {store_info.get('place_name', store_id)}",
store_id=store_id,
store_info=store_info,
reviews=reviews,
review_summary=review_summary,
metadata=metadata,
last_updated=store_data.get('combined_at'),
total_reviews=len(reviews),
execution_time=execution_time
)
except Exception as e:
execution_time = (datetime.now() - start_time).total_seconds()
logger.error(f"❌ 매장 정보 조회 실패: store_id={store_id}, error={e}")
return StoreInfoResponse(
success=False,
message=f"매장 정보 조회 중 오류 발생: {str(e)}",
store_id=store_id,
execution_time=execution_time
)

View File

@ -108,4 +108,26 @@ class VectorDBStatusResponse(BaseModel):
"""Vector DB 상태 조회 응답""" """Vector DB 상태 조회 응답"""
success: bool = Field(description="조회 성공 여부") success: bool = Field(description="조회 성공 여부")
status: VectorDBStatus = Field(description="DB 상태 정보") status: VectorDBStatus = Field(description="DB 상태 정보")
message: str = Field(description="응답 메시지") message: str = Field(description="응답 메시지")
class StoreInfoRequest(BaseModel):
"""매장 정보 조회 요청 모델"""
store_id: str = Field(
...,
description="조회할 매장 ID",
example="501745730"
)
class StoreInfoResponse(BaseModel):
"""매장 정보 조회 응답 모델"""
success: bool = Field(description="조회 성공 여부")
message: str = Field(description="응답 메시지")
store_id: str = Field(description="매장 ID")
store_info: Optional[Dict[str, Any]] = Field(None, description="매장 기본 정보")
reviews: Optional[List[Dict[str, Any]]] = Field(None, description="리뷰 목록")
review_summary: Optional[Dict[str, Any]] = Field(None, description="리뷰 요약 정보")
metadata: Optional[Dict[str, Any]] = Field(None, description="Vector DB 메타데이터")
last_updated: Optional[str] = Field(None, description="마지막 업데이트 시간")
total_reviews: int = Field(default=0, description="총 리뷰 수")
execution_time: Optional[float] = Field(None, description="실행 시간(초)")

View File

@ -1178,4 +1178,64 @@ class VectorService:
return { return {
'added_count': added_count, 'added_count': added_count,
'add_details': add_details 'add_details': add_details
} }
def get_store_by_id(self, store_id: str) -> Optional[Dict[str, Any]]:
"""
store_id로 특정 매장의 정보를 조회합니다.
Args:
store_id: 조회할 매장 ID
Returns:
매장 정보 딕셔너리 또는 None
"""
try:
if not self.is_ready():
logger.warning("⚠️ VectorService가 준비되지 않음")
return None
logger.info(f"🔍 매장 정보 조회 시작: store_id={store_id}")
# Vector DB에서 해당 store_id로 검색
results = self.collection.get(
where={"store_id": store_id},
include=['documents', 'metadatas', 'ids']
)
if not results or not results.get('metadatas') or not results['metadatas']:
logger.warning(f"❌ 매장 정보를 찾을 수 없음: store_id={store_id}")
return None
# 첫 번째 결과 사용 (store_id는 유니크해야 함)
metadata = results['metadatas'][0]
document = results['documents'][0] if results.get('documents') else None
document_id = results['ids'][0] if results.get('ids') else None
logger.info(f"✅ 매장 정보 조회 성공: {metadata.get('store_name', 'Unknown')}")
# 문서에서 JSON 파싱 (combine_store_and_reviews 형태로 저장되어 있음)
store_data = None
if document:
try:
import json
store_data = json.loads(document)
except json.JSONDecodeError as e:
logger.error(f"❌ JSON 파싱 실패: {e}")
return None
# 응답 데이터 구성
result = {
"metadata": metadata,
"document_id": document_id,
"store_info": store_data.get('store_info') if store_data else None,
"reviews": store_data.get('reviews', []) if store_data else [],
"review_summary": store_data.get('review_summary', {}) if store_data else {},
"combined_at": store_data.get('combined_at') if store_data else None
}
return result
except Exception as e:
logger.error(f"❌ 매장 정보 조회 실패: store_id={store_id}, error={e}")
return None