# KOL Insight - 开发计划 ## 文档信息 | 项目 | 内容 | |------|------| | 版本 | v1.0 | | 创建日期 | 2025-01-28 | | 来源文档 | FeatureSummary.md | ## 1. 项目概述 ### 1.1 项目目标 | 目标 | 指标 | 衡量方式 | |------|------|----------| | 提升查询效率 | 单次可批量查询多个 KOL | 对比手动查询耗时 | | 降低计算错误 | 自动计算预估指标准确率 100% | 人工抽检验证 | | 提高数据可用性 | 支持数据导出 | 导出功能完整性 | ### 1.2 技术栈 | 层级 | 技术选型 | 版本 | 说明 | |------|----------|------|------| | 前端框架 | 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 配置,环境变量管理敏感配置 - **前后端分离**: 前端专注 UI 展示,后端专注业务逻辑和数据处理 - **异步优先**: 后端使用异步编程,提升并发性能 ## 2. 技术架构 ### 2.1 系统架构图 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 客户端层 │ │ ┌───────────────────────────────────────────────────────────────────┐ │ │ │ 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 模块依赖图 ``` ┌────────────────────────────────────────────────────────────────┐ │ 前端模块 (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 数据流图 ``` 用户操作 前端 后端 数据库/外部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: 基础架构搭建 **目标**: 完成项目初始化和基础设施配置 | 任务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 | - | **阶段依赖图:** ``` T-001 (前端初始化) T-002 (后端初始化) │ │ ▼ ▼ T-004 (UI框架) T-003 (数据库) │ │ └──────────┬───────────┘ ▼ T-005 (环境变量) ``` --- ### 3.3 Phase 2: 核心功能开发 **目标**: 实现所有核心功能(查询、计算、展示、导出) | 任务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 | **阶段依赖图:** ``` 后端任务: T-006 (查询API) ──────▶ T-007 (计算逻辑) ──────▶ T-009 (导出API) │ │ │ └──▶ T-008 (品牌API) │ │ │ │ 前端任务: │ │ T-010 (查询表单) ─────────────┼───────────────────────┤ ▼ ▼ T-011 (结果表格) ──────▶ T-012 (导出按钮) ``` --- ### 3.4 Phase 3: 优化与测试 **目标**: 性能优化、错误处理、部署配置 | 任务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 | 全部 | **阶段依赖图:** ``` T-012 (导出按钮) │ ├──────────┬──────────┐ ▼ ▼ ▼ T-013 T-014 T-015 (错误处理) (性能优化) (链接跳转) │ ├──────────┐ ▼ ▼ T-016 T-017 (部署) (测试) ``` ## 4. 技术方案 ### 4.1 数据查询模块 **功能**: 支持三种查询方式(星图ID/达人ID/昵称)批量查询 KOL 视频数据 **技术选型**: | 组件 | 技术 | 选型理由 | |------|------|----------| | ORM | SQLAlchemy 2.0+ | 异步 ORM,类型安全,成熟稳定 | | 异步驱动 | asyncpg | PostgreSQL 异步驱动,高性能 | | 查询优化 | 数据库索引 | 在 star_id, star_unique_id, star_nickname 字段建立索引 | | 输入验证 | Pydantic | FastAPI 内置,类型安全的请求参数验证 | **架构设计**: ``` ┌─────────────────────────────────────────────────────────────┐ │ 查询模块 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 请求解析 │ ──▶ │ 参数验证 │ ──▶ │ 查询构建 │ │ │ │ (type/values)│ │ (Zod) │ │ (Prisma) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 数据库查询 │ │ │ │ PostgreSQL │ │ │ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` **接口设计**: | 接口 | 方法 | 路径 | 说明 | |------|------|------|------| | 批量查询 | POST | /api/v1/query | 支持 star_id/unique_id/nickname 三种类型 | **请求/响应格式**: ```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 ``` **实现要点**: - 使用 SQLAlchemy 的 `select()` 配合 `where()` 条件 - 星图ID/达人ID 使用 `in_()` 查询批量匹配 - 昵称使用 `like()` 进行模糊匹配(使用 `%{value}%`) - 限制单次查询最大返回 1000 条 - SQL 注入防护由 SQLAlchemy 和 Pydantic 自动处理 - 使用异步查询 `session.execute(stmt)` 提升性能 --- ### 4.2 数据计算模块 **功能**: 计算预估自然 CPM、看后搜人数、看后搜成本 **技术选型**: | 组件 | 技术 | 选型理由 | |------|------|----------| | 计算逻辑 | Python | 类型安全(Type Hints),易于维护 | | 数值处理 | Python 原生 | 简单计算,无需额外库,性能优异 | **架构设计**: ``` ┌─────────────────────────────────────────────────────────────┐ │ 计算模块 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ │ │ │ 查询结果 │ │ │ └──────┬──────┘ │ │ │ │ │ ┌────────────┼────────────┐ │ │ ▼ ▼ ▼ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ CPM 计算 │ │看后搜人数 │ │ 成本计算 │ │ │ │ F-004 │ │ F-005 │ │ F-006 │ │ │ └───────────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ │ │ │ │ 依赖 │ │ │ └─────────────┘ │ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ 除零检查 │ │ │ │ natural_play_cnt = 0 → null │ │ │ │ total_play_cnt = 0 → null │ │ │ │ 看后搜人数 = 0 → null │ │ │ └─────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` **计算公式**: ```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 ``` **实现要点**: - 结果保留2位小数:`round(value, 2)` - 除零检查:分母为0时返回 None - None 值在前端显示为 "-" - 批量计算时使用列表推导式或 map 函数 - 使用 Python 3.10+ 的 `float | None` 类型注解 --- ### 4.3 数据展示模块 **功能**: 以表格形式展示查询结果,支持视频链接跳转 **技术选型**: | 组件 | 技术 | 选型理由 | |------|------|----------| | 表格组件 | HTML Table + Tailwind | 简单直接,样式灵活 | | 数据格式化 | Intl API | 数字格式化、日期格式化 | **架构设计**: ``` ┌─────────────────────────────────────────────────────────────┐ │ 展示模块 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 结果表格组件 │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ 表头 │ │ 数据行 │ │ 链接列 │ │ 分页 │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 空状态组件 │ │ │ │ "未找到匹配数据" │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` **实现要点**: - 26个字段使用中文列名 - 视频链接使用 `` - 数字字段格式化:千分位分隔 - 空值显示为 "-" - 支持横向滚动(表格宽度超出时) --- ### 4.4 数据导出模块 **功能**: 将查询结果导出为 Excel 或 CSV 文件 **技术选型**: | 组件 | 技术 | 选型理由 | |------|------|----------| | Excel 生成 | xlsx (SheetJS) | 成熟稳定,支持 .xlsx 格式 | | CSV 生成 | 原生实现 | 简单格式,无需额外库 | **接口设计**: | 接口 | 方法 | 路径 | 说明 | |------|------|------|------| | 数据导出 | GET | /api/export | 参数:format=xlsx/csv,data=查询条件 | **实现要点**: - 使用中文列名作为表头 - Excel 格式使用 xlsx 库生成 - CSV 格式需处理逗号转义 - 文件名格式:`kol_data_${timestamp}.xlsx` - 响应头设置 `Content-Disposition: attachment` - 限制单次导出最大 1000 条 --- ### 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 │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 4. 填充查询结果 │ │ - 成功: 显示品牌名称 │ │ - 失败: 降级显示 brand_id │ └─────────────────────────────────────┘ ``` **实现要点**: - **在后端调用**: 查询API获取数据库结果后,立即调用品牌API - **去重处理**: 提取查询结果中所有唯一的 brand_id,避免重复请求 - **并发控制**: 使用 asyncio.gather 或 asyncio.Semaphore 限制最大 10 个并发请求 - **超时设置**: 单个请求超时 3 秒,避免阻塞整体响应 - **降级策略**: API 调用失败时,显示原始 brand_id - **结果合并**: 将品牌名称填充到查询结果后返回前端 ```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 连接池,实现重试机制 | 后端开发 | | 大批量查询性能问题 | 中 | 中 | 限制单次查询上限,优化数据库索引 | 后端开发 | | 品牌 API 不可用/超时 | 中 | 中 | 并发限制(10)、单请求超时(3s)、降级显示 brand_id | 后端开发 | | 导出数据量过大 | 中 | 中 | 限制单次导出 1000 条,分批导出提示 | 后端开发 | | 数据同步延迟 | 中 | 中 | 显示数据更新时间,建立同步监控 | 运维 | ## 6. 里程碑 ``` M1 M2 M3 M4 │ │ │ │ ▼ ▼ ▼ ▼ ◆───────────────◆───────────────◆───────────────◆ │ │ │ │ 基础架构 核心功能 功能完善 正式上线 搭建完成 开发完成 测试完成 部署完成 ``` | 里程碑 | 目标 | 交付物 | 验收标准 | |--------|------|--------|----------| | M1 | 基础架构搭建完成 | 项目骨架、数据库连接、基础UI | 项目可运行,数据库可连接 | | M2 | 核心功能开发完成 | 查询、计算、展示、导出功能 | 所有 P0/P1 功能可用 | | M3 | 功能完善测试完成 | 错误处理、性能优化、链接跳转 | 测试通过,性能达标 | | M4 | 正式上线部署完成 | Docker/PM2 部署配置 | 生产环境可访问 | ## 7. 资源需求 | 角色 | 人数 | 职责 | 参与阶段 | |------|------|------|----------| | 前端开发 | 1 | Next.js 开发、UI 组件、API 调用 | Phase 1-3 | | 后端开发 | 1 | FastAPI 开发、API 设计、数据库操作 | Phase 1-3 | | 运维/DevOps | 0.5 | 前后端分离部署、CORS 配置、监控告警 | Phase 3 | **注**: 也可由全栈开发承担前后端工作 ## 8. 目录结构 ``` 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 ```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); ``` ## 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 { 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 { 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。