项目从单体结构重构为 pnpm monorepo (shared/backend/frontend), 新增 YouTube、Instagram、Twitter/X、哔哩哔哩、微博 5 个平台适配器, 包含完整的单元测试和 E2E 测试覆盖。 - 完成 T-031~T-044: 5 个适配器实现、注册、配置和测试 - 重构前后端分离: Hono 后端 + Next.js 前端 - 151 个单元测试 + 21 个 Mock E2E + 25 个真实 E2E - 适配器基于真实 TikHub API 响应结构实现 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
670 lines
38 KiB
Markdown
670 lines
38 KiB
Markdown
# Muse Creative Hotspots — 开发计划
|
||
|
||
## 文档信息
|
||
|
||
| 项目 | 内容 |
|
||
|------|------|
|
||
| 版本 | v1.0 |
|
||
| 创建日期 | 2026-03-02 |
|
||
| 来源文档 | FeatureSummary.md, PRD.md |
|
||
|
||
## 1. 项目概述
|
||
|
||
### 1.1 项目目标
|
||
|
||
构建面向个人创意工作者的全平台热点内容聚合浏览器,MVP 阶段实现抖音 + TikTok + 小红书三个平台的热点内容聚合浏览,包含卡片信息流、筛选排序、内容详情、收藏系统、数据刷新和设置管理。
|
||
|
||
### 1.2 技术栈
|
||
|
||
| 层级 | 技术选型 | 版本 | 说明 |
|
||
|------|----------|------|------|
|
||
| 框架 | Next.js (App Router) | 14+ | 全栈能力,API Routes 做后端代理,Vercel 部署 |
|
||
| UI 库 | Tailwind CSS + shadcn/ui | Tailwind 3.x | 简约现代风格,组件丰富 |
|
||
| 状态管理 | Zustand | 4.x | 轻量状态管理,persist 中间件支持持久化 |
|
||
| 数据请求 | TanStack Query | 5.x | 缓存、自动刷新、loading/error 状态管理 |
|
||
| 本地存储 | localStorage | - | MVP 阶段收藏/设置持久化 |
|
||
| 语言 | TypeScript | 5.x | 类型安全 |
|
||
| 包管理器 | pnpm | 8+ | 快速、节省磁盘 |
|
||
| 部署 | localhost → Vercel | - | 先本地开发,后期线上部署 |
|
||
|
||
### 1.3 开发原则
|
||
|
||
- **渐进式开发**:先跑通数据链路,再完善 UI 和交互
|
||
- **适配器模式**:平台差异封装在适配器内,新增平台零侵入
|
||
- **安全优先**:API Key 仅存在于服务端,前端不暴露
|
||
- **类型驱动**:先定义 TypeScript 类型,再实现逻辑
|
||
- **组件化**:UI 组件遵循单一职责,可独立测试
|
||
|
||
---
|
||
|
||
## 2. 技术架构
|
||
|
||
### 2.1 系统架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 客户端(浏览器) │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ Next.js App (React) │ │
|
||
│ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌───────────┐ │ │
|
||
│ │ │ 首页 │ │ 详情页 │ │ 收藏页 │ │ 设置页 │ │ │
|
||
<!-- MODIFIED: 原内容为 "[id]/",补充 platform 参数(M-001) -->
|
||
│ │ │ page.tsx│ │[plt]/[id]│ │favorites│ │ settings │ │ │
|
||
│ │ └────┬────┘ └────┬─────┘ └────┬────┘ └─────┬─────┘ │ │
|
||
│ │ │ │ │ │ │ │
|
||
│ │ ┌────▼────────────▼─────────────▼──────────────▼─────┐ │ │
|
||
│ │ │ TanStack Query (缓存 + 自动刷新) │ │ │
|
||
│ │ └────────────────────────┬───────────────────────────┘ │ │
|
||
│ │ │ │ │
|
||
│ │ ┌────────────────────────▼───────────────────────────┐ │ │
|
||
│ │ │ Zustand Stores (settings / favorites) │ │ │
|
||
│ │ │ ↕ localStorage │ │ │
|
||
│ │ └────────────────────────────────────────────────────┘ │ │
|
||
│ └───────────────────────────┬───────────────────────────────┘ │
|
||
└──────────────────────────────┼──────────────────────────────────┘
|
||
│ fetch /api/tikhub/[platform]
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Next.js API Routes (服务端) │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ /api/tikhub/[platform]/route.ts │ │
|
||
│ │ ┌──────────┐ ┌──────────────┐ ┌───────────────────┐ │ │
|
||
│ │ │ 请求验证 │→│ 频率限制 │→│ 平台适配器分发 │ │ │
|
||
│ │ │ API Key │ │ 10 req/s │ │ douyin/tiktok/xhs │ │ │
|
||
│ │ └──────────┘ └──────────────┘ └─────────┬─────────┘ │ │
|
||
│ └────────────────────────────────────────────┼─────────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ 平台适配器层 │ │
|
||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||
│ │ │ 抖音 │ │ TikTok │ │ 小红书 │ ... (扩展) │ │
|
||
│ │ │ Adapter │ │ Adapter │ │ Adapter │ │ │
|
||
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ └─────────────┼─────────────┘ │ │
|
||
│ │ ▼ │ │
|
||
│ │ ContentItem[] 统一数据模型 │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────┼───────────────────────────────────┘
|
||
│ Bearer Token
|
||
▼
|
||
┌─────────────────────┐
|
||
│ TikHub API │
|
||
│ api.tikhub.io │
|
||
│ 10 req/s 限制 │
|
||
│ $0.001/请求 │
|
||
└─────────────────────┘
|
||
```
|
||
|
||
### 2.2 模块依赖图
|
||
|
||
```
|
||
┌───────────────────────────────────────────────────────┐
|
||
│ 页面层 (Pages) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────────┐ │
|
||
│ │ 首页 │ │ 详情页 │ │收藏页│ │ 设置页 │ │
|
||
│ └────┬─────┘ └────┬─────┘ └──┬───┘ └────┬─────┘ │
|
||
└───────┼─────────────┼───────────┼───────────┼─────────┘
|
||
│ │ │ │
|
||
▼ ▼ ▼ ▼
|
||
┌───────────────────────────────────────────────────────┐
|
||
│ 组件层 (Components) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ CardGrid │ │ DetailPnl│ │ Toolbar │ ... │
|
||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||
└───────┼─────────────┼──────────────┼──────────────────┘
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌───────────────────────────────────────────────────────┐
|
||
│ 数据层 (Hooks + Stores) │
|
||
│ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||
│ │ TanStack Query │ │ Zustand Stores │ │
|
||
│ │ useContentQuery │ │ useFavoritesStore │ │
|
||
│ │ useDetailQuery │ │ useSettingsStore │ │
|
||
│ └────────┬─────────┘ └────────────┬─────────────┘ │
|
||
└───────────┼─────────────────────────┼─────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌───────────────────────┐ ┌────────────────────────────┐
|
||
│ API 代理层 │ │ 本地存储 │
|
||
│ /api/tikhub/[plat] │ │ localStorage │
|
||
│ │ │ └────────────────────────────┘
|
||
│ ▼ │
|
||
│ 平台适配器层 │
|
||
│ adapters/*.ts │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ContentItem 类型 │
|
||
└───────────────────────┘
|
||
```
|
||
|
||
### 2.3 数据流图
|
||
|
||
```
|
||
用户操作 前端 API Route TikHub
|
||
│ │ │ │
|
||
│ 1.打开首页/切换Tab │ │ │
|
||
├─────────────────────▶│ │ │
|
||
│ │ 2.useContentQuery() │ │
|
||
│ ├───────────────────────▶│ │
|
||
│ │ │ 3.读取 API Key │
|
||
│ │ │ 4.选择适配器 │
|
||
│ │ ├────────────────────▶│
|
||
│ │ │ 5.TikHub原始响应 │
|
||
│ │ │◀────────────────────┤
|
||
│ │ │ 6.适配器转换 │
|
||
│ │ │ → ContentItem[] │
|
||
│ │ 7.返回标准化数据 │ │
|
||
│ │◀───────────────────────┤ │
|
||
│ │ 8.TanStack Query缓存 │ │
|
||
│ │ 9.前端排序/筛选 │ │
|
||
│ 10.渲染卡片网格 │ │ │
|
||
│◀─────────────────────┤ │ │
|
||
│ │ │ │
|
||
│ 11.点击收藏 │ │ │
|
||
├─────────────────────▶│ │ │
|
||
│ │ 12.Zustand更新 │ │
|
||
│ │ 13.localStorage持久化 │ │
|
||
│ 14.收藏状态反馈 │ │ │
|
||
│◀─────────────────────┤ │ │
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 开发阶段
|
||
|
||
### 3.1 阶段时间线
|
||
|
||
```
|
||
Phase 1 Phase 2 Phase 3
|
||
基础架构搭建 核心功能实现 辅助功能 & 联调
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ 项目初始化│ │ 内容展示 │ │ 收藏系统 │
|
||
│ 类型定义 │ ──────▶ │ 筛选排序 │ ──────▶ │ 设置页面 │
|
||
│ API代理层 │ │ 详情页 │ │ 性能优化 │
|
||
│ 适配器 │ │ 刷新机制 │ │ 联调验收 │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
|
||
交付物: 交付物: 交付物:
|
||
• 项目骨架 • 首页卡片信息流 • 收藏功能
|
||
• 类型系统 • 平台Tab切换 • 收藏夹页面
|
||
• 3平台适配器 • 排序功能 • 设置页面
|
||
• API代理可用 • 详情页 • 图片懒加载
|
||
• 全局布局 • 自动/手动刷新 • 全链路验收
|
||
```
|
||
|
||
### 3.2 Phase 1: 基础架构搭建
|
||
|
||
**目标**: 搭建项目骨架,打通 API 代理 → 适配器 → 统一数据模型的完整链路,确保能从 TikHub 获取到标准化的 ContentItem 数据。
|
||
|
||
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|
||
|--------|------|------|------|--------|----------|
|
||
<!-- MODIFIED: 补充 next.config.ts 图片域名配置(S-002) -->
|
||
| T-001 | 项目初始化 | Next.js 14+ App Router + Tailwind + shadcn/ui + pnpm;配置 next.config.ts images.remotePatterns(各平台图片 CDN 域名白名单) | - | P0 | - |
|
||
| T-002 | TypeScript 类型定义 | ContentItem、Platform、PlatformAdapter 接口定义 | T-001 | P0 | F-015 |
|
||
| T-003 | API 代理层实现 | `/api/tikhub/[platform]/route.ts`,Bearer Token 认证,频率限制 | T-001 | P0 | F-014 |
|
||
| T-004 | TikHub API 客户端 | 封装 HTTP 请求,错误处理,10 req/s 限流 | T-003 | P0 | F-014 |
|
||
| T-005 | 平台适配器 — 抖音 | 热搜榜 + 内容详情 API,字段映射为 ContentItem | T-002, T-004 | P0 | F-016 |
|
||
| T-006 | 平台适配器 — TikTok | 趋势内容 + 内容详情 API,字段映射为 ContentItem | T-002, T-004 | P0 | F-016 |
|
||
| T-007 | 平台适配器 — 小红书 | 推荐内容 + 笔记详情 API,字段映射为 ContentItem | T-002, T-004 | P0 | F-016 |
|
||
| T-008 | Zustand Store 基础 | settingsStore(API Key、刷新间隔)+ favoritesStore 骨架 | T-001 | P0 | F-009, F-010 |
|
||
| T-009 | 全局布局组件 | Header(Logo + 平台 Tab + 设置入口)+ 主内容区域 | T-001 | P0 | - |
|
||
|
||
**阶段依赖图:**
|
||
|
||
```
|
||
T-001 (项目初始化)
|
||
├──▶ T-002 (类型定义) ──┐
|
||
├──▶ T-003 (API代理层) │
|
||
│ └──▶ T-004 (API客户端) ──┐
|
||
├──▶ T-008 (Zustand) │ │
|
||
└──▶ T-009 (全局布局) │ │
|
||
▼ ▼
|
||
T-005 (抖音适配器)
|
||
T-006 (TikTok适配器)
|
||
T-007 (小红书适配器)
|
||
```
|
||
|
||
**Phase 1 验收**: `GET /api/tikhub/douyin` 返回标准化 ContentItem[] JSON。
|
||
|
||
---
|
||
|
||
### 3.3 Phase 2: 核心功能实现
|
||
|
||
**目标**: 实现首页卡片信息流、平台 Tab 切换、排序、详情页、自动/手动刷新,完成核心浏览体验。
|
||
|
||
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|
||
|--------|------|------|------|--------|----------|
|
||
| T-010 | TanStack Query 集成 | 配置 QueryClient,封装 `useContentQuery(platform)` hook | T-005~T-007 | P0 | F-001 |
|
||
| T-011 | 内容卡片组件 | ContentCard 组件:封面图、标题、平台图标、数据指标、作者信息 | T-002, T-009 | P0 | F-002 |
|
||
| T-012 | 卡片网格布局 | 响应式网格布局(CSS Grid),支持不同屏幕尺寸 | T-011 | P0 | F-002 |
|
||
| T-013 | 平台 Tab 切换 | 顶部 Tab 栏,切换平台触发数据重新获取,支持"全部"聚合视图 | T-010, T-012 | P0 | F-003 |
|
||
| T-014 | 排序功能 | 工具栏排序控件,支持 play_count/like_count/comment_count/publish_time + asc/desc | T-010 | P0 | F-003 |
|
||
<!-- MODIFIED: 路由改为 /detail/[platform]/[id],语义更清晰(M-001) -->
|
||
| T-015 | 内容详情页 | `/detail/[platform]/[id]` 页面,完整信息展示 + "查看原文"跳转 + 收藏按钮 | T-010 | P0 | F-004 |
|
||
| T-016 | 自动定时刷新 | TanStack Query refetchInterval,读取设置中的刷新间隔,页面不可见时暂停 | T-010, T-008 | P0 | F-005 |
|
||
<!-- MODIFIED: 合并 T-018 到 T-017,T-018 粒度过细(S-003) -->
|
||
| T-017 | 手动刷新 + 刷新时间 | 工具栏刷新按钮,invalidateQueries,loading 状态,防抖处理,重置自动刷新计时器;工具栏显示"上次刷新: HH:MM",刷新后更新 | T-010, T-016 | P0 | F-005, F-006 |
|
||
|
||
**阶段依赖图:**
|
||
|
||
```
|
||
T-010 (TanStack Query) ──┬──▶ T-013 (平台Tab)
|
||
├──▶ T-014 (排序)
|
||
├──▶ T-015 (详情页)
|
||
├──▶ T-016 (自动刷新) ──▶ T-017 (手动刷新+刷新时间)
|
||
│
|
||
T-011 (卡片组件) ──▶ T-012 (网格布局) ──▶ T-013
|
||
```
|
||
|
||
**Phase 2 验收**: 首页展示三个平台的热点内容卡片网格,可切换平台/排序,点击进入详情页,自动/手动刷新正常。
|
||
|
||
---
|
||
|
||
### 3.4 Phase 3: 辅助功能 & 联调
|
||
|
||
**目标**: 完成收藏系统、设置页面、错误处理、性能优化,通过全部 MVP 验收标准。
|
||
|
||
| 任务ID | 任务 | 描述 | 依赖 | 优先级 | 关联功能 |
|
||
|--------|------|------|------|--------|----------|
|
||
| T-019 | 收藏功能实现 | favoritesStore 完善,addFavorite/removeFavorite,persist 到 localStorage | T-008, T-011 | P0 | F-007, F-009 |
|
||
| T-020 | 卡片收藏按钮 | ContentCard + DetailPage 中添加收藏按钮,实心/空心状态切换 | T-019, T-015 | P0 | F-007 |
|
||
| T-021 | 收藏夹页面 | `/favorites` 页面,网格展示收藏内容,支持取消收藏和跳转详情 | T-019, T-012 | P0 | F-008 |
|
||
| T-022 | 设置页面 | `/settings` 页面,API Key 输入框 + 刷新间隔选择 | T-008 | P0 | F-010, F-011 |
|
||
| T-023 | 错误处理 & 空状态 | API 错误提示、Key 未配置引导、数据为空提示、封面图加载失败占位 | T-010~T-022 | P0 | - |
|
||
| T-024 | 图片懒加载 | Next.js Image 组件 + loading="lazy",首屏性能优化 | T-012 | P0 | - |
|
||
| T-025 | 全链路联调 & 验收 | 按 PRD 第8节 MVP 验收标准逐项测试 | T-023, T-024 | P0 | 全部 |
|
||
|
||
**阶段依赖图:**
|
||
|
||
```
|
||
T-019 (收藏Store) ──┬──▶ T-020 (收藏按钮)
|
||
└──▶ T-021 (收藏夹页面)
|
||
|
||
T-022 (设置页面)
|
||
|
||
T-023 (错误处理) ◀── T-019~T-022 全部完成
|
||
|
||
T-024 (图片懒加载)
|
||
|
||
T-025 (联调验收) ◀── T-023 + T-024
|
||
```
|
||
|
||
**Phase 3 验收**: 通过 PRD 第8节全部 MVP 验收标准。
|
||
|
||
---
|
||
|
||
## 4. 技术方案
|
||
|
||
### 4.1 统一数据模型(F-015)
|
||
|
||
**功能**: 定义 ContentItem TypeScript 类型,作为全系统的数据契约。
|
||
|
||
**类型定义**:
|
||
|
||
```typescript
|
||
// src/types/content.ts
|
||
interface ContentItem {
|
||
id: string;
|
||
title: string;
|
||
cover_url?: string;
|
||
video_url?: string;
|
||
author_name: string;
|
||
author_avatar?: string;
|
||
play_count?: number;
|
||
like_count?: number;
|
||
comment_count?: number;
|
||
share_count?: number;
|
||
publish_time: string;
|
||
platform: Platform;
|
||
original_url: string;
|
||
tags?: string[];
|
||
}
|
||
|
||
type Platform =
|
||
| 'douyin' | 'tiktok' | 'xiaohongshu' // MVP
|
||
| 'youtube' | 'instagram' | 'twitter' // P1
|
||
| 'bilibili' | 'weibo' // P1
|
||
| string; // P2 扩展
|
||
|
||
interface PlatformConfig {
|
||
id: Platform;
|
||
name: string;
|
||
icon: string;
|
||
color: string;
|
||
enabled: boolean;
|
||
endpoints: {
|
||
trending: string;
|
||
detail: string;
|
||
};
|
||
}
|
||
|
||
interface PlatformAdapter {
|
||
fetchTrending(count: number): Promise<ContentItem[]>;
|
||
fetchDetail(id: string): Promise<ContentItem>;
|
||
}
|
||
```
|
||
|
||
### 4.2 API 代理层(F-014)
|
||
|
||
**功能**: Next.js API Routes 代理 TikHub 请求,隐藏 API Key。
|
||
|
||
**接口设计**:
|
||
|
||
| 接口 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| 获取热榜 | GET | `/api/tikhub/[platform]?count=20` | 返回 ContentItem[] |
|
||
| 获取详情 | GET | `/api/tikhub/[platform]/detail?id=xxx` | 返回 ContentItem |
|
||
| 保存设置 | POST | `/api/settings` | 保存 API Key(服务端) |
|
||
| 调用统计 | GET | `/api/stats` | 返回当日调用次数 |
|
||
|
||
**架构设计**:
|
||
|
||
```
|
||
┌───────────────────────────────────────────────────┐
|
||
│ /api/tikhub/[platform]/route.ts │
|
||
├───────────────────────────────────────────────────┤
|
||
│ │
|
||
│ 1. 解析 platform 参数 │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ 2. 读取 API Key (环境变量 / settings) │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ 3. 频率限制检查 (10 req/s) │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ 4. 选择 PlatformAdapter │
|
||
│ ┌─────┼─────┐ │
|
||
│ ▼ ▼ ▼ │
|
||
│ douyin tiktok xiaohongshu │
|
||
│ │ │ │ │
|
||
│ └─────┼─────┘ │
|
||
│ ▼ │
|
||
│ 5. 调用 TikHub API + 转换为 ContentItem[] │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ 6. 返回 JSON Response │
|
||
│ │
|
||
└───────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**实现要点**:
|
||
|
||
<!-- MODIFIED: 明确 API Key 运行时存储方案和读取优先级(M-002) -->
|
||
- API Key 读取优先级:① 运行时内存变量(设置页面覆盖值)→ ② `.env.local` 的 `TIKHUB_API_KEY` 环境变量(预配置)
|
||
- 设置页面保存 API Key 时,通过 `POST /api/settings` 将 Key 写入服务端内存变量(进程生命周期内有效),不写入 `.env.local`(运行时无法修改);服务重启后回退到 `.env.local` 配置
|
||
- MVP 阶段(localhost):推荐在 `.env.local` 中预配置 Key,设置页面仅作为运行时覆盖手段
|
||
- 使用简单的内存计数器实现 10 req/s 限流(滑动窗口)
|
||
- 错误码映射:TikHub 401 → 前端提示配置 Key;429 → 提示稍后重试;5xx → 通用错误
|
||
|
||
### 4.3 平台适配器(F-016)
|
||
|
||
**功能**: 各平台 API 调用和数据格式转换。
|
||
|
||
**架构设计**:
|
||
|
||
```
|
||
┌────────────────────────────────────────────────┐
|
||
│ PlatformAdapter 接口 │
|
||
│ fetchTrending(count) → ContentItem[] │
|
||
│ fetchDetail(id) → ContentItem │
|
||
└──────────────────┬─────────────────────────────┘
|
||
│ implements
|
||
┌─────────┼─────────┐
|
||
▼ ▼ ▼
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Douyin │ │ TikTok │ │ Xhs │
|
||
│ Adapter │ │ Adapter │ │ Adapter │
|
||
├──────────┤ ├──────────┤ ├──────────┤
|
||
│ 端点: │ │ 端点: │ │ 端点: │
|
||
│ fetch_hot │ │ trending │ │ fetch_ │
|
||
│ _search_ │ │ _post │ │ feed │
|
||
│ result │ │ │ │ │
|
||
│ │ │ │ │ │
|
||
│ 映射: │ │ 映射: │ │ 映射: │
|
||
│ 平台特定 │ │ 平台特定 │ │ 平台特定 │
|
||
│ → Content │ │ → Content│ │ → Content│
|
||
│ Item │ │ Item │ │ Item │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
```
|
||
|
||
**MVP 平台端点配置**:
|
||
|
||
| 平台 | 热榜端点 | 详情端点 |
|
||
|------|----------|----------|
|
||
| 抖音 | `/api/v1/douyin/web/fetch_hot_search_result` | `/api/v1/douyin/web/fetch_one_video` |
|
||
| TikTok | `/api/v1/tiktok/web/fetch_trending_post` | `/api/v1/tiktok/web/fetch_post_detail` |
|
||
| 小红书 | `/api/v1/xiaohongshu/app/v2/fetch_feed` | `/api/v1/xiaohongshu/app/v2/fetch_note_detail` |
|
||
|
||
**实现要点**:
|
||
|
||
- 每个适配器独立文件:`src/lib/adapters/douyin.ts`、`tiktok.ts`、`xiaohongshu.ts`
|
||
- 适配器注册表:`src/lib/adapters/index.ts` 导出 `getAdapter(platform): PlatformAdapter`
|
||
- 字段映射中缺失字段使用合理默认值(如 `author_name: "未知作者"`)
|
||
|
||
### 4.4 内容展示层(F-002, F-003)
|
||
|
||
**功能**: 卡片网格布局 + 平台 Tab 切换 + 排序。
|
||
|
||
**组件结构**:
|
||
|
||
```
|
||
┌────────────────────────────────────────────────┐
|
||
│ Header │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ Logo [全部][抖音][TikTok][小红书] ⚙️ │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
├────────────────────────────────────────────────┤
|
||
│ Toolbar │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ 排序: [▼最热|最新] 🔄刷新 上次:10:30│ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
├────────────────────────────────────────────────┤
|
||
│ ContentGrid │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │
|
||
│ │ │Content │ │Content │ │Content │ ... │ │
|
||
│ │ │Card │ │Card │ │Card │ │ │
|
||
│ │ └────────┘ └────────┘ └────────┘ │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
└────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**接口设计**:
|
||
|
||
| 组件 | Props | 说明 |
|
||
|------|-------|------|
|
||
| `PlatformTabs` | `platforms`, `active`, `onChange` | 平台切换 Tab 栏 |
|
||
| `SortToolbar` | `sortBy`, `sortOrder`, `onSort`, `onRefresh`, `lastRefresh` | 排序 + 刷新工具栏 |
|
||
| `ContentGrid` | `items: ContentItem[]`, `loading`, `error` | 卡片网格容器 |
|
||
| `ContentCard` | `item: ContentItem`, `onFavorite`, `isFavorited` | 单个内容卡片 |
|
||
|
||
**实现要点**:
|
||
|
||
- 网格布局使用 CSS Grid:`grid-template-columns: repeat(auto-fill, minmax(280px, 1fr))`
|
||
- 排序在前端内存中完成(useMemo),不重新请求 API
|
||
- 平台 Tab 切换触发 TanStack Query 的 queryKey 变更,自动重新获取数据
|
||
<!-- MODIFIED: 补充 rate-limiter 保护说明,与风险管理一致(S-001) -->
|
||
- "全部"视图使用 `Promise.all` 并发请求所有已启用平台,通过 rate-limiter 确保不超 10 req/s
|
||
|
||
### 4.5 收藏系统(F-007, F-008, F-009)
|
||
|
||
**功能**: Zustand + persist 实现收藏功能。
|
||
|
||
**接口设计**:
|
||
|
||
```typescript
|
||
// src/stores/favorites.ts
|
||
interface FavoritesStore {
|
||
items: ContentItem[];
|
||
addFavorite: (item: ContentItem) => void;
|
||
removeFavorite: (id: string, platform: Platform) => void;
|
||
isFavorited: (id: string, platform: Platform) => boolean;
|
||
}
|
||
```
|
||
|
||
**实现要点**:
|
||
|
||
- 使用 Zustand `persist` 中间件,存储到 localStorage key `muse-favorites`
|
||
- 收藏去重:以 `id + platform` 组合作为唯一键
|
||
- 收藏夹页面复用 `ContentGrid` + `ContentCard` 组件
|
||
|
||
### 4.6 设置管理(F-010, F-011)
|
||
|
||
**功能**: API Key 配置 + 刷新间隔设置。
|
||
|
||
**接口设计**:
|
||
|
||
```typescript
|
||
// src/stores/settings.ts
|
||
interface SettingsStore {
|
||
apiKey: string;
|
||
refreshInterval: 5 | 10 | 15 | 30 | 60; // 分钟
|
||
enabledPlatforms: Record<Platform, boolean>;
|
||
displayCount: number;
|
||
setApiKey: (key: string) => void;
|
||
setRefreshInterval: (minutes: number) => void;
|
||
togglePlatform: (platform: Platform) => void;
|
||
setDisplayCount: (count: number) => void;
|
||
}
|
||
```
|
||
|
||
**实现要点**:
|
||
|
||
<!-- MODIFIED: 与 4.2 保持一致,明确 API Key 存储为服务端内存变量(M-002) -->
|
||
- API Key 通过 `/api/settings` POST 接口保存到服务端内存变量(非 localStorage),读取优先级见 4.2 节
|
||
- 刷新间隔变更后,立即更新 TanStack Query 的 refetchInterval
|
||
- 设置页 UI 使用 shadcn/ui 的 Input、Select、Switch 组件
|
||
|
||
---
|
||
|
||
## 5. 项目目录结构
|
||
|
||
```
|
||
src/
|
||
├── app/
|
||
│ ├── layout.tsx # 全局布局 (Header + Main)
|
||
│ ├── page.tsx # 首页 (ContentGrid + Toolbar)
|
||
<!-- MODIFIED: 路由补充 platform 参数(M-001) -->
|
||
│ ├── detail/[platform]/[id]/page.tsx # 详情页
|
||
│ ├── favorites/page.tsx # 收藏夹页面
|
||
│ ├── settings/page.tsx # 设置页面
|
||
│ └── api/
|
||
│ ├── tikhub/
|
||
│ │ └── [platform]/
|
||
│ │ ├── route.ts # 热榜内容代理
|
||
│ │ └── detail/route.ts # 内容详情代理
|
||
│ ├── settings/route.ts # 设置保存接口
|
||
│ └── stats/route.ts # API 调用统计
|
||
├── components/
|
||
│ ├── layout/
|
||
│ │ ├── Header.tsx # 顶部导航
|
||
│ │ ├── PlatformTabs.tsx # 平台 Tab 栏
|
||
│ │ └── SortToolbar.tsx # 排序 + 刷新工具栏
|
||
│ ├── card/
|
||
│ │ ├── ContentCard.tsx # 内容卡片
|
||
│ │ ├── ContentGrid.tsx # 卡片网格容器
|
||
│ │ └── CardSkeleton.tsx # 加载骨架屏
|
||
│ ├── detail/
|
||
│ │ └── DetailPanel.tsx # 详情信息面板
|
||
│ ├── common/
|
||
│ │ ├── EmptyState.tsx # 空状态组件
|
||
│ │ ├── ErrorState.tsx # 错误状态组件
|
||
│ │ └── FavoriteButton.tsx # 收藏按钮
|
||
│ └── ui/ # shadcn/ui 组件
|
||
├── lib/
|
||
│ ├── tikhub.ts # TikHub HTTP 客户端
|
||
│ ├── rate-limiter.ts # 请求频率限制
|
||
│ ├── adapters/
|
||
│ │ ├── index.ts # 适配器注册表
|
||
│ │ ├── douyin.ts # 抖音适配器
|
||
│ │ ├── tiktok.ts # TikTok 适配器
|
||
│ │ └── xiaohongshu.ts # 小红书适配器
|
||
│ ├── platforms.ts # 平台配置
|
||
│ └── utils.ts # 工具函数
|
||
├── hooks/
|
||
│ ├── useContentQuery.ts # 内容查询 hook
|
||
│ └── useDetailQuery.ts # 详情查询 hook
|
||
├── stores/
|
||
│ ├── favorites.ts # 收藏 store
|
||
│ └── settings.ts # 设置 store
|
||
└── types/
|
||
└── content.ts # 类型定义
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 风险管理
|
||
|
||
| 风险 | 可能性 | 影响 | 应对措施 |
|
||
|------|--------|------|----------|
|
||
| TikHub API 端点变更 | 中 | 高 | 适配器模式隔离变更,仅需修改对应适配器文件 |
|
||
| TikHub API 响应格式变化 | 中 | 高 | 字段映射做容错处理,缺失字段使用默认值 |
|
||
<!-- MODIFIED: 统一并发策略描述,移除"串行请求"矛盾说法(S-001) -->
|
||
| API 频率限制触发 (10 req/s) | 高 | 中 | 实现 rate-limiter 请求排队机制,确保并发请求不超 10 req/s;MVP 仅 3 平台,并发风险低 |
|
||
| API 成本失控 | 低 | 中 | 默认 30 分钟刷新间隔;页面不可见暂停刷新;F-017 成本监控 |
|
||
| localStorage 容量限制 (5MB) | 低 | 低 | 收藏数据量预估较小(数百条 ContentItem ≈ 几百 KB) |
|
||
| 跨域图片加载失败 | 高 | 中 | 使用 Next.js Image 组件配置 remotePatterns;加载失败显示占位图 |
|
||
| P1/P2 平台 API 端点不确定 | 高 | 低 | MVP 不涉及;P1 阶段前在 TikHub 控制台确认最新端点 |
|
||
|
||
---
|
||
|
||
## 7. 里程碑
|
||
|
||
```
|
||
M1 M2 M3 M4
|
||
│ │ │ │
|
||
▼ ▼ ▼ ▼
|
||
◆─────────────────◆─────────────────◆─────────────────◆
|
||
│ │ │ │
|
||
Phase 1 完成 Phase 2 完成 Phase 3 完成 MVP 发布
|
||
数据链路打通 核心浏览体验 全功能可用 验收通过
|
||
```
|
||
|
||
| 里程碑 | 目标 | 交付物 | 验收标准 |
|
||
|--------|------|--------|----------|
|
||
| M1 — 数据链路打通 | API 代理层 + 3 平台适配器可用 | T-001 ~ T-009 | `GET /api/tikhub/douyin` 返回标准化 JSON |
|
||
<!-- MODIFIED: T-018 合并到 T-017,范围调整(S-003) -->
|
||
| M2 — 核心浏览体验 | 首页可浏览、可切换、可排序 | T-010 ~ T-017 | 首页展示卡片网格,Tab 切换 + 排序 + 详情页 + 刷新正常 |
|
||
| M3 — 全功能可用 | 收藏 + 设置 + 错误处理 | T-019 ~ T-024 | 收藏功能可用,设置页可配置 Key 和刷新间隔 |
|
||
| M4 — MVP 发布 | 全链路验收通过 | T-025 | 通过 PRD 第8节全部 8 条 MVP 验收标准 |
|
||
|
||
---
|
||
|
||
## 8. 任务与功能映射
|
||
|
||
| 功能ID | 功能名 | 实现任务 |
|
||
|--------|--------|----------|
|
||
| F-001 | 内容获取 | T-010 |
|
||
| F-002 | 卡片信息流展示 | T-011, T-012 |
|
||
| F-003 | 内容筛选与排序 | T-013, T-014 |
|
||
| F-004 | 内容详情页 | T-015 |
|
||
<!-- MODIFIED: T-017 合并了 T-018 的刷新时间展示,补充映射(S-003) -->
|
||
| F-005 | 自动定时刷新 | T-016, T-017 |
|
||
| F-006 | 手动刷新 | T-017 |
|
||
| F-007 | 内容收藏 | T-019, T-020 |
|
||
| F-008 | 收藏夹管理 | T-021 |
|
||
| F-009 | 收藏数据持久化 | T-019 |
|
||
| F-010 | API Key 配置 | T-022 |
|
||
| F-011 | 刷新间隔设置 | T-022 |
|
||
| F-014 | API 请求代理 | T-003, T-004 |
|
||
| F-015 | 统一数据模型 | T-002 |
|
||
| F-016 | 平台适配器 | T-005, T-006, T-007 |
|
||
|
||
> F-012(平台管理)、F-013(展示数量设置)、F-017(API 调用量统计)为 v1.1/v2.0 功能,不在 MVP 任务中。
|
||
|
||
---
|
||
|
||
## 9. 资源需求
|
||
|
||
| 角色 | 人数 | 职责 | 参与阶段 |
|
||
|------|------|------|----------|
|
||
| 全栈开发 | 1 | 前后端全部实现 | Phase 1-3 |
|
||
|
||
> 本项目为个人项目,由单人全栈完成。
|