kol-insight/backend/app/api/v1/video_analysis.py
zfc 7cd29c5980 feat(frontend): 重构视频分析页面,支持多种搜索方式
主要更新:
- 前端改用 Ant Design 组件(Table、Modal、Select 等)
- 支持三种搜索方式:星图ID、达人unique_id、达人昵称模糊匹配
- 列表页实时调用云图 API 获取 A3 数据和成本指标
- 详情弹窗显示完整 6 大类指标,支持文字复制
- 品牌 API URL 格式修复为查询参数形式
- 优化云图 API 参数格式和会话池管理

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:01:55 +08:00

176 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
视频分析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)}")