Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/pre-processing-service/app/api/endpoints/blog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# app/api/endpoints/keywords.py
from fastapi import APIRouter
from app.decorators.logging import log_api_call
from ...errors.CustomException import *
from fastapi import APIRouter

from ...model.schemas import *
# 이 파일만의 독립적인 라우터를 생성합니다.
router = APIRouter()

@router.get("/")
async def root():
return {"message": "blog API"}

@router.post("/rag/create", response_model=ResponsetBlogCreate)
async def rag_create(request: RequestBlogCreate):
return {"message": "blog API"}

@router.post("/publish", response_model=RequestBlogPublish)
async def publish(request: ResponsetBlogPublish):
return {"message": "blog API"}
65 changes: 65 additions & 0 deletions apps/pre-processing-service/app/api/endpoints/keywords.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# app/api/endpoints/keywords.py
from ...service.keyword_service import keyword_search

from fastapi import APIRouter
from app.decorators.logging import log_api_call
from ...errors.CustomException import *
from fastapi import APIRouter
from ...errors.CustomException import *
from ...model.schemas import RequestNaverSearch, ResponseNaverSearch, RequestSadaguValidate, ResponsetSadaguValidate

# 이 파일만의 독립적인 라우터를 생성합니다.
router = APIRouter()

@router.get("/")
async def root():
return {"message": "Items API"}

@router.post("/search")
async def search(request: RequestNaverSearch):
"""
이 엔드포인트는 아래와 같은 JSON 요청을 받습니다.
RequestBase와 RequestNaverSearch의 모든 필드를 포함해야 합니다.
{
"job_id": "job-123",
"schedule_id": "schedule-456",
"schedule_his_id": 789,
"tag": "fastapi",
"category": "tech",
"start_date": "2025-09-01T12:00:00",
"end_date": "2025-09-02T15:00:00"
}
"""
job_id = request.job_id
schedule_id = request.schedule_id
category = request.category
keywords = "밥밥밥"
return ResponseNaverSearch(
job_id=job_id,
schedule_id=schedule_id,
category=category,
keyword=keywords,
total_keyword = {1: "바밥밥", 2: "밥밥밥", 3: "바밤바"}
)

@router.post("/search/test",response_model=ResponsetSadaguValidate)
async def search(request: RequestSadaguValidate):
"""
이 엔드포인트는 아래와 같은 JSON 요청을 받습니다.
RequestBase와 RequestNaverSearch의 모든 필드를 포함해야 합니다.
{
"job_id": "job-123",
"schedule_id": "schedule-456",
"schedule_his_id": 789,
"tag": "fastapi",
"category": "tech",
"start_date": "2025-09-01T12:00:00",
"end_date": "2025-09-02T15:00:00"
}
"""
response_data= keyword_search(request)
return response_data

@router.post("/ssadagu/validate",response_model=ResponseNaverSearch)
async def ssadagu_validate(request: RequestNaverSearch):
return ResponseNaverSearch()
12 changes: 0 additions & 12 deletions apps/pre-processing-service/app/api/endpoints/processing.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# app/api/endpoints/embedding.py
from fastapi import APIRouter
from app.decorators.logging import log_api_call
from ...errors.CustomException import *
from fastapi import APIRouter

from ...model.schemas import *;

# 이 파일만의 독립적인 라우터를 생성합니다.
router = APIRouter()

@router.get("/")
async def root():
return {"message": "Items API"}
@router.post("/crawl",response_model=ResponsetSadaguCrawl)
async def crawl(request: RequestSadaguCrawl):
return ResponsetSadaguCrawl()
9 changes: 6 additions & 3 deletions apps/pre-processing-service/app/api/router.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# app/api/router.py
from fastapi import APIRouter
from .endpoints import embedding, processing,test
from .endpoints import keywords, blog,test,product
from ..core.config import settings

api_router = APIRouter()

# embedding API URL
api_router.include_router(embedding.router, prefix="/emb", tags=["Embedding"])
api_router.include_router(keywords.router, prefix="/keywords", tags=["keyword"])

# processing API URL
api_router.include_router(processing.router, prefix="/prc", tags=["Processing"])
api_router.include_router(blog.router, prefix="/blog", tags=["blog"])

#상품 API URL
api_router.include_router(product.router, prefix="/product", tags=["product"])

#모듈 테스터를 위한 endpoint
api_router.include_router(test.router, prefix="/test", tags=["Test"])
Expand Down
75 changes: 50 additions & 25 deletions apps/pre-processing-service/app/errors/handlers.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,90 @@
# app/errors/handlers.py
from fastapi import Request, status
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.exceptions import RequestValidationError
from .messages import ERROR_MESSAGES, get_error_message
from ..errors.CustomException import CustomException

class ErrorBaseModel(BaseModel):
"""
모든 에러 응답의 기반이 되는 Pydantic 모델.
API의 에러 응답 형식을 통일하는 역할을 합니다.
"""
status_code: int
detail: str
code: str

# CustomException 핸들러
async def custom_exception_handler(request: Request, exc: CustomException):
"""
CustomException을 상속받는 모든 예외를 처리합니다.
"""
# 변경점: ErrorBaseModel을 사용하여 응답 본문 생성
error_content = ErrorBaseModel(
status_code=exc.status_code,
detail=exc.detail,
code=exc.code
)
return JSONResponse(
status_code=exc.status_code,
content={
"error_code": exc.code,
"message": exc.detail,
},
content=error_content.model_dump(),
)


# FastAPI의 HTTPException 핸들러 (예: 404 Not Found)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
"""
FastAPI에서 기본적으로 발생하는 HTTP 관련 예외를 처리합니다.
"""
if exc.status_code == status.HTTP_404_NOT_FOUND:
# 404 에러의 경우, FastAPI의 기본 "Not Found" 메시지 대신 우리가 정의한 메시지를 사용합니다.
message = ERROR_MESSAGES.get(exc.status_code, "요청하신 리소스를 찾을 수 없습니다.")
else:
# 다른 HTTP 예외들은 FastAPI가 제공하는 detail 메시지를 우선적으로 사용합니다.
message = get_error_message(exc.status_code, exc.detail)
message = get_error_message(exc.status_code, exc.detail)

# 변경점: ErrorBaseModel을 사용하여 응답 본문 생성
error_content = ErrorBaseModel(
status_code=exc.status_code,
detail=message,
code=f"HTTP_{exc.status_code}"
)
return JSONResponse(
status_code=exc.status_code,
content={
"error_code": f"HTTP_{exc.status_code}",
"message": message
},
content=error_content.model_dump(),
)


# Pydantic Validation Error 핸들러 (422)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""
Pydantic 모델 유효성 검사 실패 시 발생하는 예외를 처리합니다.
"""
# 변경점: ErrorBaseModel을 기본 구조로 사용하고, 추가 정보를 더함
base_error = ErrorBaseModel(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=ERROR_MESSAGES[status.HTTP_422_UNPROCESSABLE_ENTITY],
code="VALIDATION_ERROR"
)

# 모델의 내용과 추가적인 'details' 필드를 결합
response_content = base_error.model_dump()
response_content["details"] = exc.errors()

return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"error_code": "VALIDATION_ERROR",
"message": ERROR_MESSAGES[status.HTTP_422_UNPROCESSABLE_ENTITY],
"details": exc.errors(),
},
content=response_content,
)


# 처리되지 않은 모든 예외 핸들러 (500)
async def unhandled_exception_handler(request: Request, exc: Exception):
# ...
"""
처리되지 않은 모든 예외를 처리합니다.
"""
# 변경점: ErrorBaseModel을 사용하여 응답 본문 생성
error_content = ErrorBaseModel(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES[status.HTTP_500_INTERNAL_SERVER_ERROR],
code="INTERNAL_SERVER_ERROR"
)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error_code": "INTERNAL_SERVER_ERROR",
"message": ERROR_MESSAGES[status.HTTP_500_INTERNAL_SERVER_ERROR],
},
content=error_content.model_dump(),
)
64 changes: 64 additions & 0 deletions apps/pre-processing-service/app/model/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel



#기본 요청
class RequestBase(BaseModel):
job_id: str
schedule_id: str
schedule_his_id: Optional[int] = None

#기본 응답
class ResponseBase(BaseModel):
job_id: str
schedule_id: str
status: str


#네이버 키워드 추출
class RequestNaverSearch(RequestBase):
tag: str
category: str
startDate :datetime
endDate :datetime

class ResponseNaverSearch(ResponseBase):
category: str
keyword: str
total_keyword: dict[int, str]


#키워드 사다구몰 검증
class RequestSadaguValidate(RequestBase):
tag: str
category: str

class ResponsetSadaguValidate(ResponseBase):
keyword: str


#사다구몰 상품 크롤링
class RequestSadaguCrawl(RequestBase):
tag: str
category: str

class ResponsetSadaguCrawl(ResponseBase):
pass

#블로그 생성
class RequestBlogCreate(RequestBase):
tag: str
category: str

class ResponsetBlogCreate(ResponseBase):
pass

#블로그 배포
class RequestBlogPublish(RequestBase):
tag: str
category: str

class ResponsetBlogPublish(ResponseBase):
pass
Empty file.
16 changes: 16 additions & 0 deletions apps/pre-processing-service/app/service/keyword_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Pydantic 모델을 가져오기 위해 schemas 파일 import
from ..model.schemas import RequestNaverSearch

def keyword_search(request: RequestNaverSearch) -> dict:
"""
네이버 검색 요청을 처리하는 비즈니스 로직입니다.
입력받은 데이터를 기반으로 응답 데이터를 생성하여 딕셔너리로 반환합니다.
"""

response_data = request.model_dump()

response_data["keyword"] = "밥밥밥"
total_keyword = {1: "바밥밥", 2: "밥밥밥", 3: "바밤바"}
response_data["total_keyword"] = total_keyword

return response_data
Loading