diff --git a/app/common/db.py b/app/common/db.py index a6cfda7..468af75 100644 --- a/app/common/db.py +++ b/app/common/db.py @@ -59,6 +59,17 @@ async def fetchone(sql: str, args: tuple = ()) -> dict | None: finally: conn.close() + +async def fetchall(sql: str, args: tuple = ()) -> list[dict]: + pool = await get_pool() + async with pool.acquire() as conn: + try: + async with conn.cursor(aiomysql.DictCursor) as cur: + await cur.execute(sql, args) + return await cur.fetchall() + finally: + conn.close() + async def insert_instagram_row(hospital_id: str, url: str) -> int: return await execute("INSERT INTO instagram_data (hospital_id, url) VALUES (%s, %s)", (hospital_id, url)) @@ -247,3 +258,14 @@ async def save_hospital_raw_data(hospital_id: str, data: dict, analysis_run_id: ), ) await _insert_hospital_history(hospital_id, analysis_run_id) + + +async def get_market_analysis(analysis_run_id: str) -> dict: + rows = await fetchall( + "SELECT analysis_type, data FROM market_analysis WHERE analysis_run_id = %s AND status = 'done'", + (analysis_run_id,), + ) + return { + row["analysis_type"]: json.loads(row["data"]) if isinstance(row["data"], str) else row["data"] + for row in rows + } diff --git a/app/integrations/llm/schemas/plan.py b/app/integrations/llm/schemas/plan.py index 80a15b8..a12a957 100644 --- a/app/integrations/llm/schemas/plan.py +++ b/app/integrations/llm/schemas/plan.py @@ -11,6 +11,10 @@ class PlanInput(BaseModel): services: str | None = None doctors: str | None = None report: str | None = None + market_competitors: str | None = None + market_keywords: str | None = None + market_trend: str | None = None + market_target_audience: str | None = None # --- BrandGuide --- diff --git a/app/integrations/llm/schemas/report.py b/app/integrations/llm/schemas/report.py index 96092a0..2b56110 100644 --- a/app/integrations/llm/schemas/report.py +++ b/app/integrations/llm/schemas/report.py @@ -317,6 +317,10 @@ class ReportInput(BaseModel): naver_blog: str | None = None youtube: str | None = None gangnam_unni: str | None = None + market_competitors: str | None = None + market_keywords: str | None = None + market_trend: str | None = None + market_target_audience: str | None = None # --- MarketingReport --- diff --git a/app/integrations/llm/temp-prompt/plan_prompt.txt b/app/integrations/llm/temp-prompt/plan_prompt.txt index fe822e8..af7c52e 100644 --- a/app/integrations/llm/temp-prompt/plan_prompt.txt +++ b/app/integrations/llm/temp-prompt/plan_prompt.txt @@ -15,6 +15,20 @@ - 시술: {services} - 의료진: {doctors} +## 시장 분석 데이터 + +### 경쟁 병원 +{market_competitors} + +### 검색 키워드 트렌드 +{market_keywords} + +### 시장 트렌드 +{market_trend} + +### 잠재 고객 분석 +{market_target_audience} + ## 분석 리포트 {report} diff --git a/app/integrations/llm/temp-prompt/report_prompt.txt b/app/integrations/llm/temp-prompt/report_prompt.txt index f87383a..34a9086 100644 --- a/app/integrations/llm/temp-prompt/report_prompt.txt +++ b/app/integrations/llm/temp-prompt/report_prompt.txt @@ -12,6 +12,20 @@ - 시술: {services} - 의료진: {doctors} +## 시장 분석 데이터 + +### 경쟁 병원 +{market_competitors} + +### 검색 키워드 트렌드 +{market_keywords} + +### 시장 트렌드 +{market_trend} + +### 잠재 고객 분석 +{market_target_audience} + ## 채널 데이터 ### 인스타그램 diff --git a/app/services/analysis.py b/app/services/analysis.py index d27d2ae..c040eff 100644 --- a/app/services/analysis.py +++ b/app/services/analysis.py @@ -1,6 +1,6 @@ import json import logging -from common.db import fetchone, execute, get_analysis_raw_data, save_analysis_report +from common.db import fetchone, execute, get_analysis_raw_data, save_analysis_report, get_market_analysis from integrations.llm.llm_service import LLMService from integrations.llm.prompt import report_prompt, plan_prompt from integrations.llm.schemas.report import ReportOutput @@ -22,17 +22,25 @@ async def generate_report(analysis_run_id: str) -> ReportOutput: raw_data = clinic_row["raw_data"] if clinic_row else None clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {}) raw = await get_analysis_raw_data(analysis_run_id) + market = await get_market_analysis(analysis_run_id) + + def _json(v) -> str | None: + return json.dumps(v, ensure_ascii=False) if v else None input_data = { - "clinic_name": clinic.get("clinicName"), - "clinic_name_en": clinic.get("clinicNameEn"), - "address": clinic.get("address"), - "phone": clinic.get("phone"), - "slogan": clinic.get("slogan"), - "services": json.dumps(clinic.get("services", []), ensure_ascii=False), - "doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False), + "clinic_name": clinic.get("clinicName"), + "clinic_name_en": clinic.get("clinicNameEn"), + "address": clinic.get("address"), + "phone": clinic.get("phone"), + "slogan": clinic.get("slogan"), + "services": json.dumps(clinic.get("services", []), ensure_ascii=False), + "doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False), + "market_competitors": _json(market.get("competitors")), + "market_keywords": _json(market.get("keywords")), + "market_trend": _json(market.get("trend")), + "market_target_audience": _json(market.get("target_audience")), **{ - channel: json.dumps(data, ensure_ascii=False) if data else None + channel: _json(data) for channel, data in raw.items() }, } @@ -53,16 +61,24 @@ async def generate_plan(analysis_run_id: str) -> PlanOutput: clinic = json.loads(raw_data) if isinstance(raw_data, str) else (raw_data or {}) report_data = run["report_data"] report = json.loads(report_data) if isinstance(report_data, str) else report_data + market = await get_market_analysis(analysis_run_id) + + def _json(v) -> str | None: + return json.dumps(v, ensure_ascii=False) if v else None input_data = { - "clinic_name": clinic.get("clinicName"), - "clinic_name_en": clinic.get("clinicNameEn"), - "address": clinic.get("address"), - "phone": clinic.get("phone"), - "slogan": clinic.get("slogan"), - "services": json.dumps(clinic.get("services", []), ensure_ascii=False), - "doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False), - "report": json.dumps(report, ensure_ascii=False) if report else None, + "clinic_name": clinic.get("clinicName"), + "clinic_name_en": clinic.get("clinicNameEn"), + "address": clinic.get("address"), + "phone": clinic.get("phone"), + "slogan": clinic.get("slogan"), + "services": json.dumps(clinic.get("services", []), ensure_ascii=False), + "doctors": json.dumps(clinic.get("doctors", []), ensure_ascii=False), + "report": _json(report), + "market_competitors": _json(market.get("competitors")), + "market_keywords": _json(market.get("keywords")), + "market_trend": _json(market.get("trend")), + "market_target_audience": _json(market.get("target_audience")), } return await LLMService(provider="perplexity").generate(plan_prompt, input_data)