kol-insight/doc/DevelopmentPlan.md
zfc ac0f086821 feat(init): 完成 Phase 1 基础架构搭建
- 完成 T-001A: 前端项目初始化 (Next.js 14 + TypeScript + Tailwind CSS)
- 完成 T-001B: 后端项目初始化 (FastAPI + SQLAlchemy + asyncpg)
- 完成 T-002: 数据库配置 (KolVideo 模型 + 索引 + 测试)
- 完成 T-003: 基础 UI 框架 (Header/Footer 组件 + 品牌色系)
- 完成 T-004: 环境变量配置 (前后端环境变量)

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

1015 lines
53 KiB
Markdown
Raw Permalink 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.

# KOL Insight - 开发计划
## 文档信息
| 项目 | 内容 |
|------|------|
| 版本 | v1.0 |
| 创建日期 | 2025-01-28 |
| 来源文档 | FeatureSummary.md |
## 1. 项目概述
### 1.1 项目目标
| 目标 | 指标 | 衡量方式 |
|------|------|----------|
| 提升查询效率 | 单次可批量查询多个 KOL | 对比手动查询耗时 |
| 降低计算错误 | 自动计算预估指标准确率 100% | 人工抽检验证 |
| 提高数据可用性 | 支持数据导出 | 导出功能完整性 |
### 1.2 技术栈
<!-- MODIFIED: 改用前后端分离架构,后端使用 Python FastAPI -->
| 层级 | 技术选型 | 版本 | 说明 |
|------|----------|------|------|
| 前端框架 | Next.js (App Router) | 14.x | React 框架,纯前端渲染 |
| UI 框架 | React + Tailwind CSS | - | 组件化开发,响应式设计 |
| 后端框架 | Python FastAPI | 0.104+ | 高性能异步 Web 框架 |
| 数据库 | PostgreSQL | 14.x+ | 关系型数据库,存储 KOL 视频数据 |
| Python ORM | SQLAlchemy + asyncpg | 2.0+ | 异步 ORM类型安全的数据库访问 |
| API 文档 | FastAPI 自动生成 | - | Swagger UI + ReDoc |
| 前端部署 | Docker / Vercel | - | 容器化或 Serverless |
| 后端部署 | Docker + Uvicorn | - | ASGI 服务器 |
| 包管理(前端) | pnpm | 8.x | 高效的包管理器 |
| 包管理(后端) | Poetry / pip | - | Python 依赖管理 |
### 1.3 开发原则
- **简单优先**: 优先选择简单直接的实现方案
- **类型安全**: 前端使用 TypeScript后端使用 Pydantic 类型验证
- **组件化**: UI 组件化开发,便于复用和维护
- **API 设计**: RESTful 风格,清晰的接口契约
- **错误处理**: 完善的错误处理和用户提示
- **安全防护**: SQL 注入防护CORS 配置,环境变量管理敏感配置
<!-- NEW START -->
- **前后端分离**: 前端专注 UI 展示,后端专注业务逻辑和数据处理
- **异步优先**: 后端使用异步编程,提升并发性能
<!-- NEW END -->
## 2. 技术架构
### 2.1 系统架构图
<!-- MODIFIED: 更新为前后端分离架构,后端使用 FastAPI -->
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Web Browser │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 查询页面 │ │ 结果展示 │ │ 导出操作 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ HTTP/HTTPS (API 调用)
┌─────────────────────────────────────────────────────────────────────────┐
│ Next.js 前端应用层 │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ App Router (纯前端) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ React 组件 │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
│ │ │ │ QueryForm │ │ ResultTable │ │ ExportButton │ │ │ │
│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ HTTP API 调用 (跨域)
┌─────────────────────────────────────────────────────────────────────────┐
│ FastAPI 后端应用层 │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ API Router │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ RESTful API │ │ │
│ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │
│ │ │ │ POST /api/v1/ │ │ GET /api/v1/ │ │ │ │
│ │ │ │ query │ │ export │ │ │ │
│ │ │ └─────────────────┘ └─────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ 业务逻辑层 (Python) │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │ 查询服务 │ │ 计算服务 │ │ 导出服务 │ │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │ 品牌API │ │ 数据库层 │ │ │ │
│ │ │ │ 集成服务 │ │ (SQLAlchemy)│ │ │ │
│ │ │ └───────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ │
│ SQL (asyncpg) │ HTTP
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ 数据层 │ │ 外部服务 │
│ ┌───────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ PostgreSQL │ │ │ │ 品牌 API │ │
│ │ ┌─────────────────┐ │ │ │ │ /v1/yuntu/brands/ │ │
│ │ │ kol_videos │ │ │ │ └───────────────────────┘ │
│ │ │ (视频数据表) │ │ │ └─────────────────────────────┘
│ │ └─────────────────┘ │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
```
### 2.2 模块依赖图
<!-- MODIFIED: 更新为前后端分离架构,后端使用 FastAPI -->
```
┌────────────────────────────────────────────────────────────────┐
│ 前端模块 (Next.js) │
│ ┌──────────────┐ │
│ │ 页面组件 │ │
│ │ (Page) │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 查询表单 │ ──▶ │ 结果表格 │ ──▶ │ 导出按钮 │ │
│ │ 组件 │ │ 组件 │ │ 组件 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────┘
│ │ │
│ HTTP API │ 数据展示 │ HTTP API
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────┐
│ 后端模块 (FastAPI) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 查询 API │ ──▶ │ 计算服务 │ │ 导出 API │ │
│ │ POST /api/v1/│ │ (Python) │ │ GET /api/v1/ │ │
│ │ query │ │ │ │ export │ │
│ └──────┬───────┘ └──────────────┘ └──────┬───────┘ │
│ │ ▲ │ │
│ ▼ │ │ │
│ ┌──────────────┐ │ │ │
│ │ 品牌API服务 │────────────┘ │ │
│ │ (F-010) │ │ │
│ │ (httpx异步) │ │ │
│ └──────┬───────┘ │ │
│ │ │ │
│ └─────────────────┬───────────────────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 数据库服务 │ │
│ │ (SQLAlchemy) │ │
│ └──────┬───────┘ │
└────────────────────────────┼───────────────────────────────────┘
┌───────────────────┼───────────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ 品牌 API │
│ │ │ (外部服务) │
└──────────────┘ └──────────────┘
```
### 2.3 数据流图
<!-- MODIFIED: 更新数据流图体现批量调用品牌API的流程 (F-010) -->
```
用户操作 前端 后端 数据库/外部API
│ │ │ │
│ 1. 输入查询条件 │ │ │
│ ─────────────────────▶ │ │ │
│ │ │ │
│ │ 2. POST /api/query │ │
│ │ ─────────────────────▶ │ │
│ │ │ │
│ │ │ 3. SELECT FROM kol_videos
│ │ │ ─────────────────────────▶│
│ │ │ │
│ │ │ 4. 返回视频数据 │
│ │ │ ◀─────────────────────────│
│ │ │ │
│ │ │ 5. 提取唯一 brand_id │
│ │ │ (去重处理) │
│ │ │ │
│ │ │ 6. 批量 GET brands │
│ │ │ (并发10超时3s) │
│ │ │ ─────────────────────────▶│
│ │ │ │
│ │ │ 7. 返回品牌名称映射 │
│ │ │ ◀─────────────────────────│
│ │ │ │
│ │ │ 8. 填充品牌名称 │
│ │ │ (失败则降级显示ID) │
│ │ │ │
│ │ │ 9. 计算预估指标 │
│ │ │ (CPM/看后搜人数/成本) │
│ │ │ │
│ │ 10. 返回完整数据 │ │
│ │ ◀───────────────────── │ │
│ │ │ │
│ 11. 展示结果列表 │ │ │
│ ◀───────────────────── │ │ │
│ │ │ │
│ 12. 点击导出 │ │ │
│ ─────────────────────▶ │ │ │
│ │ 13. GET /api/export │ │
│ │ ─────────────────────▶ │ │
│ │ │ │
│ │ 14. 返回 Excel/CSV │ │
│ │ ◀───────────────────── │ │
│ │ │ │
│ 15. 下载文件 │ │ │
│ ◀───────────────────── │ │ │
```
## 3. 开发阶段
### 3.1 阶段时间线
```
Phase 1 Phase 2 Phase 3
│ │ │
基础架构搭建 核心功能开发 优化与测试
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 项目初始 │ ──────▶ │ 功能实现 │ ──────▶ │ 优化部署 │
│ 化配置 │ │ 与集成 │ │ 与测试 │
└─────────┘ └─────────┘ └─────────┘
交付物: 交付物: 交付物:
• 项目骨架 • 查询功能 • 性能优化
• 数据库连接 • 计算逻辑 • 错误处理
• 基础 UI • 导出功能 • 部署配置
```
### 3.2 Phase 1: 基础架构搭建
**目标**: 完成项目初始化和基础设施配置
<!-- MODIFIED: 更新为前后端分离架构的任务 -->
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|--------|------|------|------|--------|----------|
| T-001 | 前端项目初始化 | 创建 Next.js 项目,配置 TypeScript、ESLint、Prettier | - | P0 | - |
| T-002 | 后端项目初始化 | 创建 FastAPI 项目,配置 Poetry/pip、项目结构 | - | P0 | - |
| T-003 | 数据库配置 | 配置 SQLAlchemy + asyncpg定义数据模型连接 PostgreSQL | T-002 | P0 | F-001~003 |
| T-004 | 基础 UI 框架 | 安装 Tailwind CSS创建基础布局组件 | T-001 | P0 | F-007 |
| T-005 | 环境变量配置 | 配置前后端环境变量数据库连接字符串CORS 配置 | T-001, T-002 | P0 | - |
**阶段依赖图:**
<!-- MODIFIED: 更新为前后端分离架构的依赖关系 -->
```
T-001 (前端初始化) T-002 (后端初始化)
│ │
▼ ▼
T-004 (UI框架) T-003 (数据库)
│ │
└──────────┬───────────┘
T-005 (环境变量)
```
---
### 3.3 Phase 2: 核心功能开发
**目标**: 实现所有核心功能(查询、计算、展示、导出)
<!-- MODIFIED: 更新任务ID编号明确前后端分离的任务分配 -->
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|--------|------|------|------|--------|----------|
| T-006 | 查询 API 开发 (后端) | FastAPI 实现 POST /api/v1/query 接口,支持三种查询方式 | T-003 | P0 | F-001, F-002, F-003 |
| T-007 | 计算逻辑实现 (后端) | Python 实现 CPM、看后搜人数、成本计算 | T-006 | P0 | F-004, F-005, F-006 |
| T-008 | 品牌 API 批量集成 (后端) | 后端使用 httpx 批量异步调用品牌API支持并发控制和降级 | T-006 | P0 | F-010 |
| T-009 | 导出 API 开发 (后端) | FastAPI 实现 GET /api/v1/export 接口,生成 Excel/CSV | T-007, T-008 | P1 | F-009 |
| T-010 | 查询表单组件 (前端) | React 组件开发调用后端查询API | T-004 | P0 | F-001, F-002, F-003 |
| T-011 | 结果表格组件 (前端) | React 组件开发显示26个字段数据来自后端API | T-004, T-007, T-008 | P1 | F-007 |
| T-012 | 导出按钮组件 (前端) | React 组件开发调用后端导出API触发下载 | T-011, T-009 | P1 | F-009 |
**阶段依赖图:**
<!-- MODIFIED: 更新为前后端分离架构的依赖关系 -->
```
后端任务:
T-006 (查询API) ──────▶ T-007 (计算逻辑) ──────▶ T-009 (导出API)
│ │ │
└──▶ T-008 (品牌API) │ │
│ │
前端任务: │ │
T-010 (查询表单) ─────────────┼───────────────────────┤
▼ ▼
T-011 (结果表格) ──────▶ T-012 (导出按钮)
```
---
### 3.4 Phase 3: 优化与测试
**目标**: 性能优化、错误处理、部署配置
<!-- MODIFIED: 更新任务ID编号明确前后端分离的部署 -->
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|--------|------|------|------|--------|----------|
| T-013 | 错误处理 (前后端) | 完善前后端错误处理,添加用户友好提示 | T-012 | P1 | 全部 |
| T-014 | 性能优化 (后端) | 数据库索引优化,异步查询性能调优 | T-012 | P1 | F-001~003 |
| T-015 | 视频链接跳转 (前端) | 实现视频链接点击跳转功能 | T-011 | P2 | F-008 |
| T-016 | 部署配置 (前后端) | Docker 配置前后端分离部署CORS 配置 | T-013 | P1 | - |
| T-017 | 集成测试 | 端到端功能测试,前后端联调 | T-013 | P1 | 全部 |
**阶段依赖图:**
<!-- MODIFIED: 更新为前后端分离架构的依赖关系 -->
```
T-012 (导出按钮)
├──────────┬──────────┐
▼ ▼ ▼
T-013 T-014 T-015
(错误处理) (性能优化) (链接跳转)
├──────────┐
▼ ▼
T-016 T-017
(部署) (测试)
```
## 4. 技术方案
### 4.1 数据查询模块
**功能**: 支持三种查询方式星图ID/达人ID/昵称)批量查询 KOL 视频数据
**技术选型**:
<!-- MODIFIED: 更新为 FastAPI + SQLAlchemy 技术栈 -->
| 组件 | 技术 | 选型理由 |
|------|------|----------|
| ORM | SQLAlchemy 2.0+ | 异步 ORM类型安全成熟稳定 |
| 异步驱动 | asyncpg | PostgreSQL 异步驱动,高性能 |
| 查询优化 | 数据库索引 | 在 star_id, star_unique_id, star_nickname 字段建立索引 |
| 输入验证 | Pydantic | FastAPI 内置,类型安全的请求参数验证 |
**架构设计**:
```
┌─────────────────────────────────────────────────────────────┐
│ 查询模块 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 请求解析 │ ──▶ │ 参数验证 │ ──▶ │ 查询构建 │ │
│ │ (type/values)│ │ (Zod) │ │ (Prisma) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 数据库查询 │ │
│ │ PostgreSQL │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**接口设计**:
<!-- MODIFIED: 更新为 FastAPI RESTful API 接口 -->
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 批量查询 | POST | /api/v1/query | 支持 star_id/unique_id/nickname 三种类型 |
**请求/响应格式**:
<!-- MODIFIED: 更新为 Python Pydantic 模型定义 -->
```python
# 请求模型
from pydantic import BaseModel
from typing import List, Literal
class QueryRequest(BaseModel):
type: Literal['star_id', 'unique_id', 'nickname']
values: List[str] # 批量ID 或单个昵称
# 响应模型
class QueryResponse(BaseModel):
success: bool
data: List[VideoData]
total: int
error: str | None = None
```
**实现要点**:
<!-- MODIFIED: 更新为 FastAPI + SQLAlchemy 实现方式 -->
- 使用 SQLAlchemy 的 `select()` 配合 `where()` 条件
- 星图ID/达人ID 使用 `in_()` 查询批量匹配
- 昵称使用 `like()` 进行模糊匹配(使用 `%{value}%`
- 限制单次查询最大返回 1000 条
- SQL 注入防护由 SQLAlchemy 和 Pydantic 自动处理
- 使用异步查询 `session.execute(stmt)` 提升性能
---
### 4.2 数据计算模块
**功能**: 计算预估自然 CPM、看后搜人数、看后搜成本
**技术选型**:
<!-- MODIFIED: 更新为 Python 后端实现 -->
| 组件 | 技术 | 选型理由 |
|------|------|----------|
| 计算逻辑 | Python | 类型安全Type Hints易于维护 |
| 数值处理 | Python 原生 | 简单计算,无需额外库,性能优异 |
**架构设计**:
```
┌─────────────────────────────────────────────────────────────┐
│ 计算模块 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ │
│ │ 查询结果 │ │
│ └──────┬──────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ CPM 计算 │ │看后搜人数 │ │ 成本计算 │ │
│ │ F-004 │ │ F-005 │ │ F-006 │ │
│ └───────────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │
│ │ 依赖 │ │
│ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 除零检查 │ │
│ │ natural_play_cnt = 0 → null │ │
│ │ total_play_cnt = 0 → null │ │
│ │ 看后搜人数 = 0 → null │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**计算公式**:
<!-- MODIFIED: 更新为 Python 实现 -->
```python
# 预估自然CPM (F-004)
def calculate_natural_cpm(estimated_video_cost: float, natural_play_cnt: int) -> float | None:
if natural_play_cnt > 0:
return round((estimated_video_cost / natural_play_cnt) * 1000, 2)
return None
# 预估自然看后搜人数 (F-005)
def calculate_natural_search_uv(
natural_play_cnt: int,
total_play_cnt: int,
after_view_search_uv: int
) -> float | None:
if total_play_cnt > 0:
return round((natural_play_cnt / total_play_cnt) * after_view_search_uv, 2)
return None
# 预估自然看后搜人数成本 (F-006)
def calculate_natural_search_cost(
estimated_video_cost: float,
estimated_natural_search_uv: float | None
) -> float | None:
if estimated_natural_search_uv and estimated_natural_search_uv > 0:
return round(estimated_video_cost / estimated_natural_search_uv, 2)
return None
```
**实现要点**:
<!-- MODIFIED: 更新为 Python 实现方式 -->
- 结果保留2位小数`round(value, 2)`
- 除零检查分母为0时返回 None
- None 值在前端显示为 "-"
- 批量计算时使用列表推导式或 map 函数
- 使用 Python 3.10+ 的 `float | None` 类型注解
---
### 4.3 数据展示模块
**功能**: 以表格形式展示查询结果,支持视频链接跳转
**技术选型**:
| 组件 | 技术 | 选型理由 |
|------|------|----------|
| 表格组件 | HTML Table + Tailwind | 简单直接,样式灵活 |
| 数据格式化 | Intl API | 数字格式化、日期格式化 |
**架构设计**:
```
┌─────────────────────────────────────────────────────────────┐
│ 展示模块 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 结果表格组件 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 表头 │ │ 数据行 │ │ 链接列 │ │ 分页 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 空状态组件 │ │
│ │ "未找到匹配数据" │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**实现要点**:
- 26个字段使用中文列名
- 视频链接使用 `<a target="_blank" rel="noopener noreferrer">`
- 数字字段格式化:千分位分隔
- 空值显示为 "-"
- 支持横向滚动(表格宽度超出时)
---
### 4.4 数据导出模块
**功能**: 将查询结果导出为 Excel 或 CSV 文件
**技术选型**:
| 组件 | 技术 | 选型理由 |
|------|------|----------|
| Excel 生成 | xlsx (SheetJS) | 成熟稳定,支持 .xlsx 格式 |
| CSV 生成 | 原生实现 | 简单格式,无需额外库 |
**接口设计**:
| 接口 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 数据导出 | GET | /api/export | 参数format=xlsx/csvdata=查询条件 |
**实现要点**:
- 使用中文列名作为表头
- Excel 格式使用 xlsx 库生成
- CSV 格式需处理逗号转义
- 文件名格式:`kol_data_${timestamp}.xlsx`
- 响应头设置 `Content-Disposition: attachment`
- 限制单次导出最大 1000 条
---
<!-- MODIFIED: 更新为批量调用策略,关联 F-010 -->
### 4.5 品牌 API 批量集成
**功能**: 后端批量调用品牌API获取品牌名称关联 F-010
**接口详情**:
| 项目 | 内容 |
|------|------|
| 地址 | https://api.internal.intelligrow.cn/v1/yuntu/brands/{brand_id} |
| 方法 | GET |
| 响应 | 品牌名称 |
**批量调用策略**:
```
查询结果 (N条数据)
┌─────────────────────────────────────┐
│ 1. 提取唯一 brand_id │
│ - 过滤空值 │
│ - 去重处理 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 2. 批量并发请求 │
│ - 并发限制: 10 个请求 │
│ - 单请求超时: 3 秒 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 3. 构建映射表 │
│ Map<brand_id, brand_name> │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 4. 填充查询结果 │
│ - 成功: 显示品牌名称 │
│ - 失败: 降级显示 brand_id │
└─────────────────────────────────────┘
```
**实现要点**:
<!-- MODIFIED: 更新为 Python FastAPI + httpx 异步实现 -->
- **在后端调用**: 查询API获取数据库结果后立即调用品牌API
- **去重处理**: 提取查询结果中所有唯一的 brand_id避免重复请求
- **并发控制**: 使用 asyncio.gather 或 asyncio.Semaphore 限制最大 10 个并发请求
- **超时设置**: 单个请求超时 3 秒,避免阻塞整体响应
- **降级策略**: API 调用失败时,显示原始 brand_id
- **结果合并**: 将品牌名称填充到查询结果后返回前端
<!-- MODIFIED: 更新为 Python + httpx 异步实现 -->
```python
import asyncio
import httpx
from typing import Dict, List
# 品牌名称批量获取
async def get_brand_names(brand_ids: List[str]) -> Dict[str, str]:
unique_ids = list(set(filter(None, brand_ids)))
brand_map: Dict[str, str] = {}
# 并发控制:限制 10 个并发
CONCURRENCY_LIMIT = 10
TIMEOUT_SECONDS = 3.0
async with httpx.AsyncClient(timeout=TIMEOUT_SECONDS) as client:
# 使用信号量控制并发数
semaphore = asyncio.Semaphore(CONCURRENCY_LIMIT)
async def fetch_brand(brand_id: str) -> tuple[str, str]:
async with semaphore:
try:
response = await client.get(
f"https://api.internal.intelligrow.cn/v1/yuntu/brands/{brand_id}"
)
if response.status_code == 200:
data = response.json()
return brand_id, data.get("name", brand_id)
except Exception:
pass # 降级处理
return brand_id, brand_id # 失败时返回原ID
# 批量并发请求
results = await asyncio.gather(
*[fetch_brand(brand_id) for brand_id in unique_ids],
return_exceptions=True
)
# 构建映射表
for result in results:
if isinstance(result, tuple):
brand_id, brand_name = result
brand_map[brand_id] = brand_name
return brand_map
# 在查询 API 中使用
async def handle_query(request: QueryRequest) -> QueryResponse:
# 1. 查询数据库
videos = await query_videos(request)
# 2. 提取品牌ID并批量获取品牌名称
brand_ids = [v.brand_id for v in videos if v.brand_id]
brand_map = await get_brand_names(brand_ids)
# 3. 填充品牌名称
for video in videos:
if video.brand_id:
video.brand_name = brand_map.get(video.brand_id, video.brand_id)
else:
video.brand_name = None
# 4. 计算预估指标并返回
return calculate_and_format(videos)
```
## 5. 风险管理
| 风险 | 可能性 | 影响 | 应对措施 | 负责人 |
|------|--------|------|----------|--------|
| 数据库连接不稳定 | 低 | 高 | 使用 Prisma 连接池,实现重试机制 | 后端开发 |
| 大批量查询性能问题 | 中 | 中 | 限制单次查询上限,优化数据库索引 | 后端开发 |
<!-- MODIFIED: 更新品牌API风险说明增加批量调用考虑 -->
| 品牌 API 不可用/超时 | 中 | 中 | 并发限制(10)、单请求超时(3s)、降级显示 brand_id | 后端开发 |
| 导出数据量过大 | 中 | 中 | 限制单次导出 1000 条,分批导出提示 | 后端开发 |
| 数据同步延迟 | 中 | 中 | 显示数据更新时间,建立同步监控 | 运维 |
## 6. 里程碑
```
M1 M2 M3 M4
│ │ │ │
▼ ▼ ▼ ▼
◆───────────────◆───────────────◆───────────────◆
│ │ │ │
基础架构 核心功能 功能完善 正式上线
搭建完成 开发完成 测试完成 部署完成
```
| 里程碑 | 目标 | 交付物 | 验收标准 |
|--------|------|--------|----------|
| M1 | 基础架构搭建完成 | 项目骨架、数据库连接、基础UI | 项目可运行,数据库可连接 |
| M2 | 核心功能开发完成 | 查询、计算、展示、导出功能 | 所有 P0/P1 功能可用 |
| M3 | 功能完善测试完成 | 错误处理、性能优化、链接跳转 | 测试通过,性能达标 |
| M4 | 正式上线部署完成 | Docker/PM2 部署配置 | 生产环境可访问 |
## 7. 资源需求
<!-- MODIFIED: 更新为前后端分离架构的人员配置 -->
| 角色 | 人数 | 职责 | 参与阶段 |
|------|------|------|----------|
| 前端开发 | 1 | Next.js 开发、UI 组件、API 调用 | Phase 1-3 |
| 后端开发 | 1 | FastAPI 开发、API 设计、数据库操作 | Phase 1-3 |
| 运维/DevOps | 0.5 | 前后端分离部署、CORS 配置、监控告警 | Phase 3 |
**注**: 也可由全栈开发承担前后端工作
## 8. 目录结构
<!-- MODIFIED: 更新为前后端分离架构的目录结构 -->
```
kol-insight/
├── frontend/ # 前端项目Next.js
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # 首页(查询页面)
│ │ │ ├── layout.tsx # 根布局
│ │ │ └── globals.css # 全局样式
│ │ ├── components/
│ │ │ ├── QueryForm.tsx # 查询表单组件
│ │ │ ├── ResultTable.tsx # 结果表格组件
│ │ │ └── ExportButton.tsx # 导出按钮组件
│ │ ├── lib/
│ │ │ ├── api.ts # API 调用封装
│ │ │ └── utils.ts # 工具函数
│ │ └── types/
│ │ └── index.ts # 类型定义
│ ├── .env.local # 环境变量后端API地址
│ ├── .env.example # 环境变量示例
│ ├── package.json
│ ├── tsconfig.json
│ ├── tailwind.config.ts
│ └── next.config.js
├── backend/ # 后端项目FastAPI
│ ├── app/
│ │ ├── main.py # FastAPI 应用入口
│ │ ├── config.py # 配置管理
│ │ ├── database.py # 数据库连接
│ │ ├── models/
│ │ │ └── kol_video.py # SQLAlchemy 模型
│ │ ├── schemas/
│ │ │ ├── query.py # Pydantic 请求/响应模型
│ │ │ └── video.py # 视频数据模型
│ │ ├── api/
│ │ │ ├── v1/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── query.py # 查询接口
│ │ │ │ └── export.py # 导出接口
│ │ │ └── deps.py # 依赖注入
│ │ ├── services/
│ │ │ ├── query_service.py # 查询业务逻辑
│ │ │ ├── calculator.py # 计算逻辑
│ │ │ ├── brand_api.py # 品牌 API 集成
│ │ │ └── export_service.py # 导出服务
│ │ └── core/
│ │ ├── security.py # 安全相关
│ │ └── logger.py # 日志配置
│ ├── alembic/ # 数据库迁移
│ │ └── versions/
│ ├── tests/ # 测试
│ ├── .env # 环境变量
│ ├── .env.example # 环境变量示例
│ ├── requirements.txt # 依赖列表(或 pyproject.toml
│ └── alembic.ini # Alembic 配置
├── docker-compose.yml # Docker 编排(可选)
└── README.md # 项目文档
```
## 9. 数据库 Schema
<!-- MODIFIED: 更新为 SQLAlchemy 模型定义 -->
```python
# backend/app/models/kol_video.py
from sqlalchemy import Column, String, Integer, Float, DateTime, Index
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class KolVideo(Base):
__tablename__ = "kol_videos"
# 主键
item_id = Column(String, primary_key=True)
# 基础信息
title = Column(String, nullable=True)
viral_type = Column(String, nullable=True)
video_url = Column(String, nullable=True)
star_id = Column(String, nullable=False, index=True)
star_unique_id = Column(String, nullable=False, index=True)
star_nickname = Column(String, nullable=False, index=True)
publish_time = Column(DateTime, nullable=True)
# 曝光指标
natural_play_cnt = Column(Integer, default=0)
heated_play_cnt = Column(Integer, default=0)
total_play_cnt = Column(Integer, default=0)
# 互动指标
total_interact = Column(Integer, default=0)
like_cnt = Column(Integer, default=0)
share_cnt = Column(Integer, default=0)
comment_cnt = Column(Integer, default=0)
# 效果指标
new_a3_rate = Column(Float, nullable=True)
after_view_search_uv = Column(Integer, default=0)
return_search_cnt = Column(Integer, default=0)
# 商业信息
industry_id = Column(String, nullable=True)
industry_name = Column(String, nullable=True)
brand_id = Column(String, nullable=True)
estimated_video_cost = Column(Float, default=0)
# 索引定义(补充)
__table_args__ = (
Index('idx_star_id', 'star_id'),
Index('idx_star_unique_id', 'star_unique_id'),
Index('idx_star_nickname', 'star_nickname'),
)
```
**对应的 Alembic 迁移 SQL**:
```sql
-- 创建表和索引
CREATE TABLE kol_videos (
item_id VARCHAR PRIMARY KEY,
title VARCHAR,
viral_type VARCHAR,
video_url VARCHAR,
star_id VARCHAR NOT NULL,
star_unique_id VARCHAR NOT NULL,
star_nickname VARCHAR NOT NULL,
publish_time TIMESTAMP,
natural_play_cnt INTEGER DEFAULT 0,
heated_play_cnt INTEGER DEFAULT 0,
total_play_cnt INTEGER DEFAULT 0,
total_interact INTEGER DEFAULT 0,
like_cnt INTEGER DEFAULT 0,
share_cnt INTEGER DEFAULT 0,
comment_cnt INTEGER DEFAULT 0,
new_a3_rate FLOAT,
after_view_search_uv INTEGER DEFAULT 0,
return_search_cnt INTEGER DEFAULT 0,
industry_id VARCHAR,
industry_name VARCHAR,
brand_id VARCHAR,
estimated_video_cost FLOAT DEFAULT 0
);
CREATE INDEX idx_star_id ON kol_videos(star_id);
CREATE INDEX idx_star_unique_id ON kol_videos(star_unique_id);
CREATE INDEX idx_star_nickname ON kol_videos(star_nickname);
```
<!-- NEW START -->
## 10. 前后端分离部署
### 10.1 部署架构
```
┌─────────────────────────────────────────────────┐
│ 前端部署 (Next.js) │
│ - Vercel / Docker + Nginx │
│ - 端口: 3000 │
│ - 环境变量: NEXT_PUBLIC_API_URL │
└─────────────────────────────────────────────────┘
│ HTTP API 调用
┌─────────────────────────────────────────────────┐
│ 后端部署 (FastAPI) │
│ - Docker + Uvicorn │
│ - 端口: 8000 │
│ - 环境变量: DATABASE_URL, CORS_ORIGINS │
└─────────────────────────────────────────────────┘
│ PostgreSQL
┌─────────────────────────────────────────────────┐
│ 数据库 (PostgreSQL) │
│ - 端口: 5432 │
└─────────────────────────────────────────────────┘
```
### 10.2 Docker Compose 配置示例
```yaml
version: '3.8'
services:
# 后端服务
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/kol_insight
- CORS_ORIGINS=http://localhost:3000,https://your-frontend-domain.com
depends_on:
- db
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
# 前端服务
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
depends_on:
- backend
# 数据库服务
db:
image: postgres:14
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=kol_insight
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
postgres_data:
```
### 10.3 CORS 配置(后端)
```python
# backend/app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
app = FastAPI(title="KOL Insight API", version="1.0.0")
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS, # ["http://localhost:3000"]
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
### 10.4 API 调用封装(前端)
```typescript
// frontend/src/lib/api.ts
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1';
export async function queryVideos(request: QueryRequest): Promise<QueryResponse> {
const response = await fetch(`${API_BASE_URL}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error('Query failed');
}
return response.json();
}
export async function exportData(format: 'xlsx' | 'csv'): Promise<Blob> {
const response = await fetch(`${API_BASE_URL}/export?format=${format}`);
if (!response.ok) {
throw new Error('Export failed');
}
return response.blob();
}
```
### 10.5 FastAPI 自动生成 API 文档
FastAPI 自动生成交互式 API 文档,无需额外配置:
- Swagger UI: `http://localhost:8000/docs`
- ReDoc: `http://localhost:8000/redoc`
- OpenAPI JSON: `http://localhost:8000/openapi.json`
前端开发人员可直接通过这些文档了解 API 接口定义和测试 API。
<!-- NEW END -->