import hashlib import logging from fastapi import APIRouter, Depends, Query from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models.brand import Brand from app.schemas.health_score import ( HealthScoreDimension, HealthScoreRecommendation, HealthScoreResponse, ) from app.services.cache import get_cache_service from app.services.diagnosis.data_collector import DataCollectorService from app.services.diagnosis.geo_diagnosis import GEODiagnosisService from app.utils.health import get_health_level, get_health_level_label logger = logging.getLogger(__name__) router = APIRouter() _FREE_TIER_DIMENSIONS = {"内容可提取性", "E-E-A-T信号", "引用就绪度"} _CACHE_TTL = 86400 def _build_default_response(brand_name: str) -> HealthScoreResponse: return HealthScoreResponse( brand_name=brand_name, overall_score=0.0, health_level="danger", health_level_label=get_health_level_label("danger"), dimensions=[ HealthScoreDimension( name=d, score=0.0, max_score=0.0, percentage=0.0, status="fail", ) for d in sorted(_FREE_TIER_DIMENSIONS) ], recommendations=[], is_full_report=False, cached=False, ) @router.get("/health-score", response_model=HealthScoreResponse) async def get_public_health_score( brand: str = Query(..., min_length=1), competitors: str = Query(default=""), db: AsyncSession = Depends(get_db), ): cache = get_cache_service() cache_key = f"health_score:{hashlib.md5(brand.lower().encode()).hexdigest()}" cached_data = await cache.get_json(cache_key) if cached_data is not None: cached_data["cached"] = True return cached_data brand_name = brand.strip() competitor_list = [c.strip() for c in competitors.split(",") if c.strip()][:3] brand_aliases: list[str] = [] website: str | None = None industry: str | None = None stmt = select(Brand).where(Brand.name == brand_name).limit(1) result = await db.execute(stmt) brand_record = result.scalar_one_or_none() if brand_record: brand_aliases = brand_record.aliases or [] website = brand_record.website industry = brand_record.industry try: collector = DataCollectorService(db) collection = await collector.collect( brand_name=brand_name, brand_aliases=brand_aliases, website=website, industry=industry, ) geo_service = GEODiagnosisService() diagnosis_result = geo_service.diagnose(collection.diagnosis_input) except Exception as e: logger.error(f"健康分数据采集失败: brand={brand_name}, error={e}", exc_info=True) default_resp = _build_default_response(brand_name) await cache.set_json(cache_key, default_resp.model_dump(mode="json"), expire=_CACHE_TTL) return default_resp all_dimensions = diagnosis_result.to_dict().get("dimensions", []) free_dimensions = [d for d in all_dimensions if d.get("name") in _FREE_TIER_DIMENSIONS] overall_score = round(sum(d.get("score", 0.0) for d in free_dimensions), 2) health_level = get_health_level(overall_score) dimension_responses = [ HealthScoreDimension( name=d.get("name", ""), score=round(d.get("score", 0.0), 2), max_score=d.get("max_score", 0.0), percentage=round(d.get("percentage", 0.0), 2), status=d.get("status", "fail"), ) for d in free_dimensions ] all_recommendations = diagnosis_result.to_dict().get("recommendations", []) free_recommendations = [ r for r in all_recommendations if r.get("priority") == "P0" and r.get("dimension") in _FREE_TIER_DIMENSIONS ] recommendation_responses = [ HealthScoreRecommendation( priority=r.get("priority", "P0"), dimension=r.get("dimension", ""), title=r.get("title", ""), description=r.get("description", ""), ) for r in free_recommendations ] response = HealthScoreResponse( brand_name=brand_name, overall_score=overall_score, health_level=health_level, health_level_label=get_health_level_label(health_level), dimensions=dimension_responses, recommendations=recommendation_responses, is_full_report=False, cached=False, ) await cache.set_json(cache_key, response.model_dump(mode="json"), expire=_CACHE_TTL) return response