From c5b6abd37055651347fc878afb91eaefdbe05556 Mon Sep 17 00:00:00 2001 From: Hooby Date: Thu, 4 Jun 2026 09:30:18 +0900 Subject: [PATCH] =?UTF-8?q?feat(roadmap):=20=EC=99=84=EB=A3=8C=20=EC=BD=9C?= =?UTF-8?q?=EB=B0=B1=EC=9D=84=20result=20=ED=8F=AC=ED=95=A8=20/roadmap/com?= =?UTF-8?q?plete=EB=A1=9C=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로드맵은 PDF가 없어 공용 notify_be(/api/v1/ai/jobs/complete)로는 status가 항상 ERROR가 되고 평가 result도 BE에 전달되지 않았다. BE가 결과를 받아 FE로 흘릴 수 있게 로드맵 전용 콜백을 추가한다: - webhook.notify_be_roadmap(ai_job_id, status, result, error_message) → POST {BE}/api/v1/ai/roadmap/complete (X-INTERNAL-API-KEY 동일) - run_assess_task finally: 성공 시 status=DONE+result, 실패 시 ERROR+error_message로 notify_be_roadmap 호출(공용 notify_be 대신). 공용 notify_be(포폴/자소서 PDF 경로)는 그대로 유지. BE는 completeRoadmapJob에서 result를 result_json으로 저장 후 SSE/폴링으로 FE에 전달. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/services/roadmap_assess.py | 14 +++++++++++--- app/services/webhook.py | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/services/roadmap_assess.py b/app/services/roadmap_assess.py index 2a37dc6..c819014 100644 --- a/app/services/roadmap_assess.py +++ b/app/services/roadmap_assess.py @@ -11,7 +11,7 @@ from app.services.roadmap import rule_filter, llm_analyzer, market_tier, market_loader from app.services.roadmap_merger import run_merged from app.services._rag_utils import _fetch_code_analysis as _fetch_project -from app.services.webhook import notify_be +from app.services.webhook import notify_be_roadmap from app.jobs.store import JobStatus, update_job logger = logging.getLogger(__name__) @@ -197,7 +197,15 @@ def _run() -> list[dict[str, Any]]: logger.error("[로드맵] 평가 태스크 실패: %s", e) update_job(job_id, JobStatus.ERROR, message=error_message) finally: + # 로드맵은 PDF가 없으므로 공용 /jobs/complete가 아니라 result를 싣는 + # /roadmap/complete로 콜백한다(BE가 result_json 저장 후 FE로 전달). + status = "ERROR" if error_message else "DONE" try: - await notify_be(ai_job_id=ai_job_id, output_pdf_url=None, error_message=error_message) + await notify_be_roadmap( + ai_job_id=ai_job_id, + status=status, + result=results, + error_message=error_message, + ) except Exception as webhook_err: - logger.warning("[Webhook] 콜백 실패 (결과는 저장됨): %s", webhook_err) + logger.warning("[Webhook] 로드맵 콜백 실패 (결과는 저장됨): %s", webhook_err) diff --git a/app/services/webhook.py b/app/services/webhook.py index 9da22f7..bd2aaa1 100644 --- a/app/services/webhook.py +++ b/app/services/webhook.py @@ -29,3 +29,27 @@ async def notify_be( resp = await client.post(be_url, json=payload, headers=headers) resp.raise_for_status() logger.info("[Webhook] 전송 완료: %s", ai_job_id) + + +async def notify_be_roadmap( + ai_job_id: str, + status: str, + result: list | dict | None = None, + error_message: str | None = None, +) -> None: + """로드맵 전용 완료 콜백. PDF가 없는 로드맵은 평가 결과(result)를 BE에 직접 실어 보낸다. + (포폴용 notify_be는 result를 싣지 않으므로 별도 엔드포인트로 분리.)""" + settings = get_settings() + payload = { + "ai_job_id": ai_job_id, + "status": status, + "result": result, + "error_message": error_message, + } + be_url = f"{settings.be_base_url}/api/v1/ai/roadmap/complete" + headers = {"X-INTERNAL-API-KEY": settings.passfolio_internal_api_key} + logger.info("[Webhook] POST %s | status=%s | job=%s", be_url, status, ai_job_id) + async with httpx.AsyncClient(timeout=10.0) as client: + resp = await client.post(be_url, json=payload, headers=headers) + resp.raise_for_status() + logger.info("[Webhook] 로드맵 전송 완료: %s", ai_job_id)