release
This commit is contained in:
parent
ceb05de7f3
commit
3d3f1e5383
@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## ⚙️ 환경 설정
|
## ⚙️ 환경 설정
|
||||||
|
|
||||||
### 필수 환경변수
|
### 필수 환경변수
|
||||||
|
|||||||
@ -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
|
||||||
|
)
|
||||||
@ -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="실행 시간(초)")
|
||||||
|
|
||||||
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user