""" 视频分析API路由 (T-024) GET /api/v1/videos/{item_id}/analysis - 单个视频分析 POST /api/v1/videos/search - 搜索视频列表(支持 star_id / nickname) """ from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.services.video_analysis import ( get_video_analysis_data, get_video_base_info, search_videos_by_star_id, search_videos_by_unique_id, search_videos_by_nickname, get_video_list_with_a3, ) from app.services.yuntu_api import YuntuAPIError router = APIRouter(prefix="/videos", tags=["视频分析"]) class SearchRequest(BaseModel): """搜索请求""" type: str # "star_id" | "unique_id" | "nickname" value: str class VideoListItem(BaseModel): """视频列表项""" item_id: str title: str star_nickname: str star_unique_id: str create_date: Optional[str] hot_type: str total_play_cnt: int total_new_a3_cnt: int total_cost: float @router.get("/{item_id}/analysis") async def get_video_analysis( item_id: str, db: AsyncSession = Depends(get_db), ): """ 获取单个视频分析数据。 返回6大类指标: - 基础信息 (8字段) - 触达指标 (7字段) - A3指标 (3字段) - 搜索指标 (5字段) - 费用指标 (3字段) - 成本指标 (6字段,计算得出) Args: item_id: 视频ID Returns: 视频分析数据 Raises: 404: 视频不存在 500: API调用失败 """ try: result = await get_video_analysis_data(db, item_id) return { "success": True, "data": result, } except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except YuntuAPIError as e: # API失败但有降级数据时不抛错 raise HTTPException(status_code=500, detail=f"API Error: {e.message}") except Exception as e: raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}") @router.post("/search") async def search_videos( request: SearchRequest, db: AsyncSession = Depends(get_db), ): """ 搜索视频列表。 支持三种搜索方式(均返回列表,点击详情查看完整数据): - star_id: 星图ID精准匹配 - unique_id: 达人unique_id精准匹配 - nickname: 达人昵称模糊匹配 Args: request: 搜索请求,包含 type 和 value Returns: 视频列表(含A3数据和成本指标) """ try: if request.type == "star_id": # 星图ID查询,返回视频列表 videos = await search_videos_by_star_id(db, request.value) if not videos: return { "success": True, "type": "list", "data": [], "total": 0, } # 获取 A3 数据 result = await get_video_list_with_a3(db, videos) return { "success": True, "type": "list", "data": result, "total": len(result), } elif request.type == "unique_id": # 达人unique_id查询,返回视频列表 videos = await search_videos_by_unique_id(db, request.value) if not videos: return { "success": True, "type": "list", "data": [], "total": 0, } # 获取 A3 数据 result = await get_video_list_with_a3(db, videos) return { "success": True, "type": "list", "data": result, "total": len(result), } elif request.type == "nickname": # 昵称模糊查询,返回视频列表 videos = await search_videos_by_nickname(db, request.value) if not videos: return { "success": True, "type": "list", "data": [], "total": 0, } # 获取 A3 数据 result = await get_video_list_with_a3(db, videos) return { "success": True, "type": "list", "data": result, "total": len(result), } else: raise HTTPException(status_code=400, detail=f"Invalid search type: {request.type}") except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) except YuntuAPIError as e: raise HTTPException(status_code=500, detail=f"API Error: {e.message}") except Exception as e: raise HTTPException(status_code=500, detail=f"Internal error: {str(e)}")