import logging import uuid from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_current_user from app.database import get_db from app.models.user import User from app.models.brand import Brand from app.models.competitor_insight import CompetitorInsight from app.schemas.competitor_insight import ( CompetitorAnalysisRequest, CompetitorInsightResponse, CompetitorInsightList, CompetitorGapSummary, ) from app.services.competitor.competitor_analyzer_service import CompetitorAnalyzerService logger = logging.getLogger(__name__) router = APIRouter() async def _get_brand_if_owned( brand_id: uuid.UUID, current_user: User, db: AsyncSession, ) -> Brand: stmt = select(Brand).where(Brand.id == brand_id, Brand.user_id == current_user.id) result = await db.execute(stmt) brand = result.scalar_one_or_none() if not brand: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="品牌不存在", ) return brand @router.post("/analyze", response_model=CompetitorInsightList) async def analyze_competitor( request: CompetitorAnalysisRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): await _get_brand_if_owned(request.brand_id, current_user, db) service = CompetitorAnalyzerService() try: result = await service.analyze_competitor( brand_id=request.brand_id, analysis_types=request.analysis_types, period_days=request.period_days or 30, ) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e), ) stmt = ( select(CompetitorInsight) .where(CompetitorInsight.brand_id == request.brand_id) .order_by(CompetitorInsight.created_at.desc()) ) db_result = await db.execute(stmt) insights = list(db_result.scalars().all()) return {"items": insights, "total": len(insights)} @router.get("/brand/{brand_id}", response_model=CompetitorInsightList) async def get_brand_insights( brand_id: uuid.UUID, skip: int = Query(0, ge=0), limit: int = Query(20, ge=1, le=100), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): await _get_brand_if_owned(brand_id, current_user, db) count_stmt = select(func.count()).select_from(CompetitorInsight).where( CompetitorInsight.brand_id == brand_id, ) count_result = await db.execute(count_stmt) total = count_result.scalar_one() stmt = ( select(CompetitorInsight) .where(CompetitorInsight.brand_id == brand_id) .order_by(CompetitorInsight.created_at.desc()) .offset(skip) .limit(limit) ) result = await db.execute(stmt) insights = list(result.scalars().all()) return {"items": insights, "total": total} @router.get("/{insight_id}", response_model=CompetitorInsightResponse) async def get_insight_detail( insight_id: uuid.UUID, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): stmt = select(CompetitorInsight).where(CompetitorInsight.id == insight_id) result = await db.execute(stmt) insight = result.scalar_one_or_none() if not insight: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="洞察不存在", ) await _get_brand_if_owned(insight.brand_id, current_user, db) return insight @router.get("/brand/{brand_id}/gap-summary", response_model=list[CompetitorGapSummary]) async def get_gap_summary( brand_id: uuid.UUID, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): brand = await _get_brand_if_owned(brand_id, current_user, db) service = CompetitorAnalyzerService() gap_summaries = await service.calculate_gap_score(db, brand_id, brand.name) return gap_summaries