Files
agent-park/docs/plans/2026-01-25-keyword-cloud-system-design.md
T

18 KiB
Raw Blame History

季度 AI 热点词云系统设计文档

创建日期: 2026-01-25 功能类型: 数据驱动可视化 技术栈: n8n + Next.js + PostgreSQL + Prisma


一、功能概述

季度 AI 热点词云是一个自动化数据驱动的可视化词云系统,通过 n8n 工作流从 Google Trends 采集 AI 相关热点词汇,经 AI 清洗和规则引擎处理后,自动入库并在前端展示。

核心目标

  1. 内容营销: 吸引访客回访查看每季度更新,提供社交分享素材
  2. 教育参考: 帮助新手理解 AI 技术演进历程
  3. 数据洞察: 展示 AI 领域热点变化趋势

用户旅程

  1. 用户访问 /keyword-cloud 页面
  2. 看到当前季度的 AI 热点词云(如 2024-Q1)
  3. 鼠标悬停在词汇上,查看详细描述和要点
  4. 点击左右箭头切换不同季度,浏览历史热点
  5. 可视化展示:词汇大小代表搜索热度,颜色代表分类

二、系统架构

架构图

┌─────────────┐    ┌─────────────┐    ┌──────────────┐
│ Google      │    │ n8n         │    │ PostgreSQL   │
│ Trends API  │───▶│ Workflow    │───▶│ Database     │
└─────────────┘    └─────────────┘    └──────────────┘
                          │
                          ▼
                   ┌─────────────┐
                   │ Next.js     │
                   │ Frontend    │
                   └─────────────┘

数据流向

  1. 定时触发: n8n Schedule 每季度末自动执行
  2. 数据采集: Google Trends API 获取热门搜索词
  3. AI 清洗: 过滤无关词汇,生成描述和要点
  4. 规则匹配: 根据热度分数分配视觉样式
  5. 数据入库: 写入 PostgreSQL 数据库
  6. 前端展示: Next.js 从数据库读取并渲染词云

三、数据库模型

3.1 Quarter 表(季度元数据)

model Quarter {
  id           Int       @id @default(autoincrement())
  quarter      String    @unique             // "2023-Q1", "2023-Q2"
  title        String    @db.Text            // "2023年第一季度"
  titleEn      String?   @db.Text            // "Q1 2023"
  subtitle     String?   @db.Text            // "聊天界面的黎明"
  subtitleEn   String?   @db.Text            // "The dawn of chat interface"
  displayOrder Int      @default(0)         // 前端排序
  isActive     Boolean  @default(true)       // 是否显示
  keywords     Keyword[]
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  @@index([quarter])
  @@index([displayOrder])
}

3.2 Keyword 表(关键词核心数据)

model Keyword {
  id             Int      @id @default(autoincrement())
  word           String                       // "ChatGPT"
  trendScore     Int                          // 0-100, 从 Google Trends 获取

  // 外键关联
  quarterId      Int
  quarter        Quarter  @relation(fields: [quarterId], references: [id], onDelete: Cascade)

  // 内容字段(支持中英双语)
  description    String   @db.Text            // AI 生成的一句话描述
  descriptionEn  String?  @db.Text            // 英文描述
  detailPoints   Json                          // JSON 数组: ["要点1", "要点2", "要点3"]
  detailPointsEn Json?                         // 英文版要点

  // 视觉样式配置
  visualConfig   Json                         // {color, size, rotation, border}

  // 元数据
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt

  @@index([quarterId])
  @@index([trendScore])
  @@index([word])
}

visualConfig 字段结构示例:

{
  "color": "secondary",
  "size": "text-5xl",
  "rotation": "rotate-1",
  "border": "border-4"
}

3.3 VisualStyleRule 表(视觉样式规则配置)

model VisualStyleRule {
  id          Int      @id @default(autoincrement())
  name        String   @unique              // "热门大词-金色"

  // 分数区间
  minScore    Int                           // 90
  maxScore    Int                           // 100

  // 视觉属性
  color       String                        // "primary", "secondary", "accent"
  size        String                        // "text-5xl", "text-3xl", "text-xl"
  border      String                        // "border-4", "border-2"
  rotation    String?                       // "rotate-1", "rotate-2", null

  // 控制
  priority    Int      @default(0)          // 优先级,分数重叠时按优先级
  enabled     Boolean  @default(true)       // 是否启用

  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([enabled])
  @@index([minScore, maxScore])
}

3.4 KeywordCloudErrorLog 表(错误日志)

model KeywordCloudErrorLog {
  id           Int      @id @default(autoincrement())
  quarter      String                        // "2023-Q1"
  keyword      String?                       // "ChatGPT"
  errorType    String                        // "INVALID_DATA", "API_ERROR", "DB_ERROR"
  errorMessage String   @db.Text             // 详细错误信息
  rawData      Json?                         // 原始数据便于调试
  createdAt    DateTime @default(now())

  @@index([quarter])
  @@index([errorType])
}

四、n8n 工作流设计

4.1 工作流概览

5 个核心节点实现从数据采集到入库的完整流程。

4.2 节点详细配置

节点 1: Schedule Trigger(定时触发)

  • 类型: n8n-nodes-base.scheduleTrigger
  • Cron 表达式: 0 0 23 28-31 * * (每季度末最后一天的 23:00)
  • 月份判断: Function 节点检查当前月份(3/6/9/12),如果不是则跳过
  • 输出: 当前季度的标识(如 "2024-Q1"
  • 类型: @gamal.dev/n8n-nodes-google-trends
  • 配置参数:
    • keywords: ["AI", "artificial intelligence", "machine learning", "GPT", "LLM"]
    • timeRange: 当前季度的 3 个月(如 2024-01-01 to 2024-03-31
    • category: "Science > Computer Science > AI"
    • geo: "GB"
  • 输出示例:
    {
      "keyword": "ChatGPT",
      "trendScore": 95,
      "rising": true
    }
    

节点 3: AI 清洗和内容生成

  • 类型: @n8n/n8n-nodes-langchain.lmChatChain
  • Prompt 模板:
    你是一个 AI 领域专家。以下是 Google Trends 采集的热门关键词列表:
    {{ $json.all() }}
    
    任务:
    1. 过滤掉与 AI/机器学习无关的关键词
    2. 为每个关键词生成中文描述(10-50字)
    3. 生成 3 条详细要点(每条 10-30 字,客观描述,避免营销用语)
    
    输出格式(JSON 数组):
    [
      {
        "word": "ChatGPT",
        "trendScore": 95,
        "description": "OpenAI 开发的对话式人工智能助手",
        "detailPoints": ["支持多轮对话", "基于 GPT-3.5 架构", "2023年用户突破1亿"]
      }
    ]
    

节点 4: 规则引擎匹配

  • 类型: n8n-nodes-base.code
  • 逻辑:
    1. HTTP Request 获取 VisualStyleRule 表数据
      • 方法: GET
      • URL: {{ $env.API_URL }}/api/keyword-cloud/rules
    2. priority 排序规则
    3. 遍历每个关键词,匹配第一个符合的规则(minScore <= trendScore <= maxScore
    4. 将视觉配置注入数据

节点 5: 批量写入数据库

  • 类型: n8n-nodes-base.httpRequest
  • 方法: POST
  • URL: {{ $env.API_URL }}/api/keyword-cloud/keywords
  • 认证: Bearer Token(环境变量 API_KEY
  • Body:
    {
      "quarter": "2024-Q1",
      "keywords": [
        {
          "word": "ChatGPT",
          "trendScore": 95,
          "description": "...",
          "detailPoints": ["...", "...", "..."],
          "visualConfig": {...}
        }
      ]
    }
    
  • 批量处理: 超过 20 个关键词时分批提交

4.3 错误处理

  • 每个节点设置 continueOnFail: true
  • 错误日志写入 KeywordCloudErrorLog
  • 关键错误发送通知(Email/Slack

五、API 端点设计

5.1 GET /api/keyword-cloud/quarters

获取季度列表。

查询参数:

  • isActive (可选): 只返回激活的季度

响应示例:

{
  "quarters": [
    {
      "id": 1,
      "quarter": "2024-Q1",
      "title": "2024年第一季度",
      "titleEn": "Q1 2024",
      "subtitle": "聊天界面的黎明",
      "subtitleEn": "The dawn of chat interface",
      "displayOrder": 0,
      "keywordCount": 15
    }
  ]
}

排序: 按 displayOrder ASC

5.2 GET /api/keyword-cloud/keywords/[quarter]

获取指定季度的关键词列表。

路径参数:

  • quarter: 季度标识(如 "2024-Q1"

响应示例:

{
  "quarter": "2024-Q1",
  "title": "2024年第一季度",
  "keywords": [
    {
      "id": 1,
      "word": "ChatGPT",
      "trendScore": 95,
      "description": "OpenAI 开发的对话式 AI 助手",
      "detailPoints": ["支持多轮对话", "基于 GPT-3.5", "2023用户破亿"],
      "visualConfig": {
        "color": "secondary",
        "size": "text-5xl",
        "border": "border-4",
        "rotation": "rotate-1"
      }
    }
  ]
}

排序: 按 trendScore DESC

5.3 POST /api/keyword-cloud/keywords

n8n 工作流写入关键词数据。

认证: Bearer Token (WEBHOOK_API_KEY)

请求体:

{
  "quarter": "2024-Q1",
  "keywords": [
    {
      "word": "ChatGPT",
      "trendScore": 95,
      "description": "...",
      "detailPoints": ["...", "...", "..."],
      "visualConfig": {...}
    }
  ]
}

响应:

{
  "success": true,
  "created": 15,
  "failed": 2,
  "errors": [
    { "word": "Invalid", "error": "trendScore out of range" }
  ]
}

逻辑:

  1. 验证 API Key
  2. 查找或创建 Quarter 记录
  3. 批量创建 Keyword 记录(Prisma createMany
  4. 失败记录写入 KeywordCloudErrorLog
  5. 返回成功/失败统计

5.4 GET /api/keyword-cloud/rules

获取视觉样式规则配置(n8n 规则引擎使用)。

查询参数:

  • enabled (可选): 只返回启用的规则

响应示例:

{
  "rules": [
    {
      "id": 1,
      "name": "热门大词-金色",
      "minScore": 90,
      "maxScore": 100,
      "visualConfig": {
        "color": "primary",
        "size": "text-5xl",
        "border": "border-4",
        "rotation": "rotate-1"
      },
      "priority": 0,
      "enabled": true
    }
  ]
}

排序: 按 priority ASC

5.5 POST /api/keyword-cloud/rules

创建新的视觉样式规则(管理员功能)。

请求体: 同单个规则对象

验证:

  • minScore < maxScore
  • 必填字段检查
  • 颜色值必须是预定义的颜色类别

六、前端组件设计

6.1 路由结构

src/app/[locale]/keyword-cloud/
├── page.tsx              # 主页面
└── components/
    ├── KeywordCloud.tsx       # 词云容器组件
    ├── CloudWord.tsx          # 单个词汇组件
    ├── QuarterNavigator.tsx   # 季度切换导航
    ├── WordPopover.tsx        # 弹出框详情
    └── ProgressIndicator.tsx  # 进度条

6.2 核心组件

KeywordCloud.tsx

词云容器组件,负责数据获取和布局。

interface KeywordCloudProps {
  quarter: string;
}

function KeywordCloud({ quarter }: KeywordCloudProps) {
  const { data, isLoading } = useKeywordData(quarter);

  return (
    <div className="bg-white/50 dark:bg-black/20 backdrop-blur-sm border-4 border-black p-8 md:p-12 shadow-hard">
      <ProgressIndicator currentQuarter={quarter} totalQuarters={4} />
      <QuarterNavigator current={quarter} />
      <div className="word-cluster py-12">
        {data?.keywords.map((keyword) => (
          <CloudWord key={keyword.id} data={keyword} />
        ))}
      </div>
      <FunFactBubble fact={data.funFact} />
    </div>
  );
}

CloudWord.tsx

单个词汇组件,应用视觉样式和悬停交互。

interface CloudWordProps {
  data: Keyword;
}

function CloudWord({ data }: CloudWordProps) {
  const { word, visualConfig, description, detailPoints } = data;
  const { color, size, border, rotation } = visualConfig;
  const colorClass = colorMap[color];

  return (
    <span className={cn(
      "cloud-word",
      border,
      "border-black",
      colorClass,
      "px-6 py-3",
      "rounded-full",
      size,
      "font-black",
      "shadow-hard",
      rotation,
      "transition-all",
      "hover:scale-105",
      "cursor-pointer"
    )}>
      {word}
      <WordPopover title={word} description={description} points={detailPoints} />
    </span>
  );
}

WordPopover.tsx

弹出框组件,显示词汇的详细信息。

interface WordPopoverProps {
  title: string;
  description: string;
  points: string[];
}

function WordPopover({ title, description, points }: WordPopoverProps) {
  return (
    <div className="popover">
      <div className="bg-primary text-black font-display font-bold p-2 border-b-2 border-black text-sm uppercase">
        {title}
      </div>
      <div className="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
        <div className="flex gap-2 items-start">
          <span></span>
          <span>{description}</span>
        </div>
        {points.map((point, i) => (
          <div key={i} className="flex gap-2 items-start">
            <span></span>
            <span>{point}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

QuarterNavigator.tsx

季度切换导航组件。

function QuarterNavigator({ current }: { current: string }) {
  const quarters = ["2023-Q1", "2023-Q2", "2023-Q3", "2023-Q4"];
  const currentIndex = quarters.indexOf(current);

  return (
    <div className="flex items-center justify-between gap-8">
      <button
        disabled={currentIndex === 0}
        className="nav-button"
        onClick={() => navigate(quarters[currentIndex - 1])}
      >
        <span className="material-symbols-outlined text-4xl">chevron_left</span>
      </button>

      <div className="text-center">
        <div className="bg-primary border-4 border-black px-10 py-4 shadow-hard font-display font-bold text-5xl">
          {current}
        </div>
      </div>

      <button
        disabled={currentIndex === quarters.length - 1}
        className="nav-button"
        onClick={() => navigate(quarters[currentIndex + 1])}
      >
        <span className="material-symbols-outlined text-4xl">chevron_right</span>
      </button>
    </div>
  );
}

6.3 数据获取

// src/hooks/useKeywordCloud.ts
export async function getKeywordData(quarter: string) {
  const response = await fetch(`${API_URL}/api/keyword-cloud/keywords/${quarter}`);
  return response.json();
}

6.4 样式系统

复用项目现有的 Tailwind 配置和样式类:

  • .cloud-word: 词云词汇的基础样式
  • .popover: 弹出框样式(包括箭头)
  • .word-cluster: 词汇容器布局
  • .nav-button: 导航按钮样式

响应式断点:md:, lg:


七、错误处理和监控

7.1 分层错误处理

n8n 工作流层:

  • 每个节点 continueOnFail: true
  • 失败记录写入 KeywordCloudErrorLog
  • 关键错误发送通知

API 层:

  • Zod schema 验证
  • 部分成功响应模式
  • HTTP 状态码规范

前端层:

  • 友好的错误提示
  • Error Boundary
  • 重试机制

7.2 监控策略

  • n8n 执行日志监控
  • 定期检查 KeywordCloudErrorLog
  • API 响应时间监控
  • 前端错误追踪

八、测试策略

8.1 单元测试(Vitest

  • API 路由处理逻辑
  • 规则引擎匹配算法
  • 数据验证 schemas

8.2 E2E 测试

  • 季度切换功能
  • 弹出框交互
  • 响应式布局

8.3 n8n 工作流测试

  • 使用测试环境 API 手动触发
  • 验证生成的数据质量
  • 检查规则匹配结果

九、部署指南

9.1 环境变量

# .env.local
DATABASE_URL="..."
WEBHOOK_API_KEY="..."
N8N_WEBHOOK_URL="https://your-n8n-instance.com/..."

9.2 数据库迁移

pnpm prisma migrate dev --name add_keyword_cloud_tables

9.3 n8n 部署

  1. 使用自托管 n8n 或 n8n Cloud
  2. 配置环境变量(API_URL, API_KEY
  3. 设置 Cron 定时任务
  4. 测试工作流执行

9.4 初始化数据

  1. 创建第一个 Quarter 记录(2023-Q1
  2. 配置 3-5 条 VisualStyleRule:
    • 90-100: 热门大词(金色, text-5xl, border-4
    • 70-89: 中等词汇(蓝色, text-3xl, border-2
    • 50-69: 小词汇(紫色, text-xl, border-2
    • 0-49: 长尾词(灰色, text-base, border-2

9.5 监控设置

  • n8n 执行日志告警
  • 错误日志定期检查
  • API 性能监控

十、未来扩展

  1. 预测功能: 基于历史数据预测下一个热点词汇
  2. 趋势分析: 展示词汇热度的季度变化曲线
  3. 用户贡献: 允许用户提交词汇建议
  4. 多维度: 按技术栈、应用领域等维度分类
  5. 导出功能: 导出季度报告(PDF/图片)

附录

A. 颜色系统

const colorMap = {
  primary: "bg-primary",      // Gold (#FFD700)
  secondary: "bg-secondary",  // Blue (#7FB5FF)
  accent: "bg-accent",        // Purple (#C39BD3)
  gray: "bg-gray-100",        // Gray
};

B. 字体大小映射

const sizeMap = {
  hot: "text-5xl",      // 90-100 分
  medium: "text-3xl",   // 70-89 分
  small: "text-xl",     // 50-69 分
  tiny: "text-base",    // 0-49 分
};

C. 参考资源