zfc 8fbcb72a3f feat(core): 完成 Phase 2 核心功能开发
- 实现查询API (query.py): 支持star_id/unique_id/nickname三种查询方式
- 实现计算模块 (calculator.py): CPM/自然搜索UV/搜索成本计算
- 实现品牌API集成 (brand_api.py): 批量并发调用,10并发限制
- 实现导出服务 (export_service.py): Excel/CSV导出
- 前端组件: QueryForm/ResultTable/ExportButton
- 主页面集成: 支持6种页面状态
- 测试: 44个测试全部通过,覆盖率88%

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

67 lines
2.3 KiB
Python

from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.schemas.query import QueryRequest, QueryResponse, VideoData
from app.services.query_service import query_videos
from app.services.calculator import calculate_metrics
from app.services.brand_api import get_brand_names
from app.api.v1.export import set_export_data
router = APIRouter()
@router.post("/query", response_model=QueryResponse)
async def query(
request: QueryRequest,
db: AsyncSession = Depends(get_db),
) -> QueryResponse:
"""
批量查询 KOL 视频数据.
支持三种查询方式:
- star_id: 按星图ID精准匹配
- unique_id: 按达人unique_id精准匹配
- nickname: 按达人昵称模糊匹配
"""
try:
# 1. 查询数据库
videos = await query_videos(db, request.type, request.values)
if not videos:
return QueryResponse(success=True, data=[], total=0)
# 2. 提取品牌ID并批量获取品牌名称
brand_ids = [v.brand_id for v in videos if v.brand_id]
brand_map = await get_brand_names(brand_ids) if brand_ids else {}
# 3. 转换为响应模型并计算指标
data = []
for video in videos:
video_data = VideoData.model_validate(video)
# 填充品牌名称
if video.brand_id:
video_data.brand_name = brand_map.get(video.brand_id, video.brand_id)
# 计算预估指标
metrics = calculate_metrics(
estimated_video_cost=video.estimated_video_cost,
natural_play_cnt=video.natural_play_cnt,
total_play_cnt=video.total_play_cnt,
after_view_search_uv=video.after_view_search_uv,
)
video_data.estimated_natural_cpm = metrics["estimated_natural_cpm"]
video_data.estimated_natural_search_uv = metrics["estimated_natural_search_uv"]
video_data.estimated_natural_search_cost = metrics["estimated_natural_search_cost"]
data.append(video_data)
# 缓存数据供导出使用
set_export_data([d.model_dump() for d in data])
return QueryResponse(success=True, data=data, total=len(data))
except Exception as e:
return QueryResponse(success=False, data=[], total=0, error=str(e))