零代码AI编程实战-热搜从0到1技术方案

【零代码AI编程实战】系列

1、 【零代码AI编程实战】AI灯塔导航-成果展示篇

2、 【零代码AI编程实战】AI灯塔导航-从0到1实现篇

3、 【零代码AI编程实战】AI灯塔导航-总结篇

3、 【零代码AI编程实战】热搜从0到1技术方案(本篇)

前言:信息爆炸时代,如何快速获取全网热点资讯?本文将深入解析AI灯塔导航系统中热搜卡片功能的完整实现方案,从后端数据抓取到前端界面展示,带你领略一个功能完整的实时热搜系统的技术魅力。

🎯 一、热搜成果展示(文末有惊喜)

在AI灯塔导航系统中,热搜卡片是一个核心功能模块,旨在为用户提供一站式的热点资讯聚合服务。不同于传统的单一平台热搜,我们的系统整合了56个主流平台的热搜数据,包括:

  • 社交媒体:微博、知乎、B站、抖音、快手等
  • 搜索引擎:百度热搜榜
  • 技术社区:GitHub、CSDN、掘金、V2EX等
  • 新闻资讯:今日头条、澎湃新闻、腾讯新闻等
  • 游戏娱乐:原神、崩坏3、英雄联盟等

这种多平台聚合的设计理念,让用户无需在多个平台间切换,就能快速掌握全网热点动态,目前卡片暂时开放了7个平台数据。

🏗️ 二、热搜整体架构图

功能架构图

数据流时序图

sequenceDiagram participant U as 用户 participant C as 前端组件 participant R as Redux Store participant A as API客户端 participant S as 后端服务 participant D as 数据库 participant E as 外部平台 U->>C: 点击平台标签 C->>R: dispatch(fetchHotSearch) R->>A: 发起API请求 A->>S: GET /api/hot-search/:platform alt 缓存有效 S->>D: 查询缓存数据 D-->>S: 返回缓存数据 else 缓存失效 S->>E: 调用平台抓取器 E-->>S: 返回最新数据 S->>D: 更新缓存 end S-->>A: 返回热搜数据 A-->>R: 更新Redux状态 R-->>C: 触发组件重渲染 C-->>U: 显示热搜列表

🏗️ 三、热搜接口实现

后端服务架构

graph TB subgraph "API层" A[GET /api/hot-search] --> B[获取所有平台] C[GET /api/hot-search/:platform] --> D[获取指定平台] E[GET /api/hot-search/platforms] --> F[获取平台列表] end subgraph "控制器层" G[hotSearchController.js] --> H[请求验证] G --> I[参数处理] G --> J[响应格式化] end subgraph "服务层" K[hotSearchService.js] --> L[缓存检查] K --> M[数据获取] K --> N[数据存储] K --> O[错误处理] end subgraph "数据层" P[HotSearch.js模型] --> Q[MongoDB集合] R[平台抓取器] --> S[56个平台模块] T[定时调度器] --> U[自动更新] end subgraph "缓存策略" V[内存缓存
1小时TTL] --> W[快速响应] X[数据库缓存
持久化] --> Y[数据可靠性] end A --> G C --> G E --> G G --> K K --> P K --> R K --> V K --> X style G fill:#f8cecc style K fill:#fff2cc style P fill:#d5e8d4 style V fill:#e1d5e7

1、数据抓取策略

数据抓取采用了模块化设计,每个平台都有独立的抓取逻辑:

javascript 复制代码
// 平台抓取器映射
const platformGetters = {
  weibo: getWeiboHotSearch,      // 微博热搜
  baidu: getBaiduHotSearch,      // 百度热搜
  bilibili: getBilibiliHotSearch, // B站热门
  zhihu: getZhihuHotSearch,      // 知乎热榜
  douyin: getDouyinHotSearch,    // 抖音热点
  // ... 更多平台
};
javascript 复制代码
/**
 * 今日头条抓取逻辑
 */
const axios = require('axios');

/**
 * 获取今日头条数据
 */
const getToutiaoHotSearch = async () => {
  try {
    console.log('今日头条: 开始获取数据...');

    const url = 'https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc';
    
    console.log('今日头条: 发送API请求...');
    const response = await axios.get(url, {
      timeout: 15000,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Referer': 'https://www.toutiao.com/',
        'Cache-Control': 'no-cache',
        'Pragma': 'no-cache'
      }
    });

    console.log('今日头条: API请求成功');
    
    const list = response.data.data;
    
    if (!list || !Array.isArray(list) || list.length === 0) {
      throw new Error('无法获取今日头条数据');
    }
    
    console.log(`今日头条: 获取到 ${list.length} 条数据`);

    const data = list.map((v) => ({
      id: v.ClusterIdStr,
      title: v.Title,
      cover: v.Image.url,
      timestamp: undefined, 
      hot: Number(v.HotValue),
      url: `https://www.toutiao.com/trending/${v.ClusterIdStr}/`,
      mobileUrl: `https://api.toutiaoapi.com/feoffline/amos_land/new/html/main/index.html?topic_id=${v.ClusterIdStr}`,
    }));

    console.log('今日头条: 数据处理完成');
    
    return {
      platform: 'toutiao',
      name: 'toutiao',
      title: '今日头条',
      type: '热榜',
      link: 'https://www.toutiao.com/',
      total: data.length,
      data: data
    };
  } catch (error) {
    console.log('今日头条: 发生错误:', error.message);
    console.log('今日头条: 错误堆栈:', error.stack);
    throw new Error(`获取今日头条失败: ${error.message}`);
  }
};

module.exports = {
  getToutiaoHotSearch
}; 

2、缓存机制设计

为了提升用户体验和减少服务器压力,我们实现了双层缓存策略

  1. 内存缓存:1小时TTL,快速响应
  2. 数据库缓存:MongoDB持久化存储,数据可靠性
javascript 复制代码
const CACHE_TTL = 60 * 60 * 1000; // 1小时缓存

const isCacheValid = (updateTime) => {
  if (!updateTime) return false;
  const now = new Date();
  const update = new Date(updateTime);
  return (now - update) < CACHE_TTL;
};

🔌 3、接口实现详解

RESTful API设计

我们设计了完整的RESTful API体系,支持多种数据获取方式:

1. 获取所有平台热搜数据

http 复制代码
GET /api/hot-search

2. 获取指定平台热搜数据

http 复制代码
GET /api/hot-search/:platform?refresh=true

3. 获取支持的平台列表

http 复制代码
GET /api/hot-search/platforms

数据模型设计

javascript 复制代码
// 热搜条目模式
const hotSearchItemSchema = new mongoose.Schema({
  id: { type: String, required: true },
  title: { type: String, required: true, trim: true },
  desc: { type: String, default: '' },
  url: { type: String, required: true },
  mobileUrl: { type: String, default: '' },
  timestamp: { type: String, default: '' },
  hot: { type: String, default: '' },
  author: { type: String, default: '' }
});

// 热搜数据模式
const hotSearchSchema = new mongoose.Schema({
  platform: { type: String, required: true, trim: true },
  name: { type: String, required: true, trim: true },
  title: { type: String, required: true, trim: true },
  type: { type: String, required: true, trim: true },
  description: { type: String, default: '' },
  link: { type: String, default: '' },
  total: { type: Number, default: 0 },
  data: [hotSearchItemSchema],
  updateTime: { type: Date, default: Date.now },
  fromCache: { type: Boolean, default: false }
});

错误处理与容错机制

javascript 复制代码
exports.getHotSearchByPlatform = async (req, res) => {
  try {
    const { platform } = req.params;
    const { refresh } = req.query;
    const forceRefresh = refresh === 'true';

    const data = await hotSearchService.getHotSearchByPlatform(platform, forceRefresh);
    
    res.status(200).json({
      success: true,
      code: 200,
      ...data
    });
  } catch (error) {
    logger.error(`获取热搜数据失败: ${error.message}`);
    res.status(500).json({
      success: false,
      code: 500,
      error: '获取热搜数据失败',
      message: error.message
    });
  }
};

🎨 四、界面实现与交互

前端组件详细架构

graph LR subgraph "热搜卡片组件架构" A[HotSearch.js
主组件] --> B[CommonCard.js
通用卡片] B --> C[HotSearchContent.js
内容组件] C --> D[平台标签栏] C --> E[热搜列表] C --> F[加载状态] C --> G[错误处理] end subgraph "状态管理" H[hotSearchSlice.js] --> I[平台数据] H --> J[加载状态] H --> K[错误信息] H --> L[活跃平台] end subgraph "服务层" M[hotSearchService.js] --> N[API调用] M --> O[数据处理] M --> P[错误处理] end subgraph "样式系统" Q[HotSearchContent.css] --> R[深色主题] Q --> S[高斯模糊] Q --> T[响应式布局] Q --> U[动画效果] end A --> H C --> M C --> Q style A fill:#e1d5e7 style H fill:#fff2cc style M fill:#f8cecc style Q fill:#d5e8d4

1、组件架构设计

热搜卡片采用了分层组件设计,确保代码的可维护性和复用性:

javascript 复制代码
// 主组件:HotSearch.js
const HotSearch = ({ onSave }) => {
  const handleAdd = () => {
    const websiteData = {
      name: '热搜',
      iconColor: '#ffffff',
      textIcon: '热搜',
      url: 'https://www.aidengta.cn',
      dataType: DATA_TYPE.HOT_SEARCH,
      id: `hotsearch_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
      preferredIconType: PREFERRED_ICON_TYPE.TEXT,
      category: '热搜卡片',
      description: '聚合百度,B站,微博,知乎,头条等热搜!'
    };

    if (onSave) {
      onSave(websiteData);
    }
  };

  return (
    <CommonCard
      title="热搜"
      subtitle="聚合百度,B站,微博,知乎,头条,抖音,掘金等热搜!"
      onAdd={handleAdd}
      addButtonText="添加"
      scrollDirection="vertical"
    >
      <HotSearchContent />
    </CommonCard>
  );
};

2、状态管理实现

使用Redux进行状态管理,确保数据流的清晰和可预测:

javascript 复制代码
// Redux Slice
const hotSearchSlice = createSlice({
  name: 'hotSearch',
  initialState: {
    platforms: {},
    supportedPlatforms: [],
    loading: {},
    errors: {}
  },
  reducers: {
    setActivePlatform: (state, action) => {
      state.activePlatform = action.payload;
    },
    setPlatformData: (state, action) => {
      const { platform, data } = action.payload;
      state.platforms[platform] = data;
    }
  }
});

3、交互体验优化

1. 平台切换交互

javascript 复制代码
const handlePlatformChange = (e, platform) => {
  e.stopPropagation();
  // 如果是重复点击同一个平台,强制刷新数据
  if (activePlatform === platform) {
    dispatch(fetchHotSearchByPlatform({ platform, refresh: true }));
  } else {
    // 切换到新平台
    setActivePlatformLocal(platform);
    dispatch(setActivePlatform(platform));
    
    // 如果该平台数据不存在,则获取数据
    if (!platformData) {
      dispatch(fetchHotSearchByPlatform({ platform }));
    }
  }
};

2. 热搜项目点击处理

javascript 复制代码
const handleTopicClick = (e, topic) => {
  e.stopPropagation();
  let url = topic.url || topic.link || topic.href;
  
  // 如果没有直接链接,尝试构建链接
  if (!url && topic.title) {
    const searchUrl = getSearchUrl(activePlatform, topic.title);
    if (searchUrl) {
      url = searchUrl;
    }
  }
  
  if (url) {
    window.open(url, '_blank', 'noopener,noreferrer');
  }
};

4、视觉设计特色

热搜卡片采用了深色主题设计,配合高斯模糊效果,营造出现代感十足的视觉体验:

css 复制代码
/* 热搜内容区域容器 - 深灰色渐变背景 + 高斯模糊 */
.hot-search-content__area {
  background: linear-gradient(135deg, rgba(9, 9, 9, 0.9) 0%, rgba(44, 62, 80, 0.8) 50%, rgba(52, 73, 94, 0.7) 100%);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-radius: 8px;
  padding: 8px;
}

/* 平台标签按钮 - 紧凑设计 + 高斯模糊 */
.hot-search-content__tab {
  background: rgba(52, 73, 94, 0.6);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: none;
  padding: 2px 10px;
  border-radius: 11px;
  font-size: 12px;
  color: #bdc3c7;
  cursor: pointer;
  transition: all 0.2s ease;
}

📱 5、Grid布局与响应式设计

Grid布局实现

使用了支持拖动排序和栅栏比例的开源库react-grid-layout,热搜卡片支持在网格布局中多列多行占用,通过CSS Grid实现灵活的布局控制:

css 复制代码
// 根据应用数据动态生成布局 - 支持智能碰撞检测和自动换行
  const generateLayout = (appsData) => {
    const layout = [];
    const cols = gridCols; // 使用动态列数
    
    // 使用二维数组来跟踪已占用的网格位置
    const grid = Array(20).fill(null).map(() => Array(cols).fill(false));
    
    appsData.forEach((app) => {
      // 根据 dataType 确定网格占用大小
      let itemWidth = 1;
      let itemHeight = 1;
      
      if (app.dataType === DATA_TYPE.HOT_SEARCH) {
        // 详情模式占用3列,其他模式占用4列
        itemWidth = websiteDisplayMode === 'detailed' ? 2 : 4;
        itemHeight = 2; // 占用2行
      }
      
      // 找到第一个可用的位置
      let found = false;
      let itemX = 0;
      let itemY = 0;
      
      for (let y = 0; y < grid.length && !found; y++) {
        for (let x = 0; x <= cols - itemWidth && !found; x++) {
          // 检查这个位置是否有足够的空间
          let canPlace = true;
          for (let dy = 0; dy < itemHeight && canPlace; dy++) {
            for (let dx = 0; dx < itemWidth && canPlace; dx++) {
              if (y + dy >= grid.length || x + dx >= cols || grid[y + dy][x + dx]) {
                canPlace = false;
              }
            }
          }
          
          if (canPlace) {
            itemX = x;
            itemY = y;
            found = true;
            
            // 标记这个位置为已占用
            for (let dy = 0; dy < itemHeight; dy++) {
              for (let dx = 0; dx < itemWidth; dx++) {
                if (y + dy < grid.length && x + dx < cols) {
                  grid[y + dy][x + dx] = true;
                }
              }
            }
          }
        }
      }
      
      if (found) {
        layout.push({
          i: app.complexId || app.id,
          x: itemX,
          y: itemY,
          w: itemWidth,
          h: itemHeight,
          static: false
        });
      } else {
        // 如果找不到合适的位置,放在最后一行
        logger.warn(`[generateLayout] 无法为 ${app.name} 找到合适位置,放在最后一行`);
        layout.push({
          i: app.complexId || app.id,
          x: 0,
          y: Math.max(...layout.map(item => item.y + item.h), 0),
          w: itemWidth,
          h: itemHeight,
          static: false
        });
      }
    });

    // 添加按钮跟随在最后一个item后面,除非一行已满才到下一行
    if (layout.length > 0) {
      // 找到最后一个item的位置和尺寸
      const lastItem = layout[layout.length - 1];
      const lastItemEndX = lastItem.x + lastItem.w;
      const lastItemY = lastItem.y;
      
      // 检查当前行是否还有空间
      if (lastItemEndX + 1 <= cols) {
        // 当前行还有空间,放在最后一个item后面
        layout.push({
          i: 'add-button',
          x: lastItemEndX,
          y: lastItemY,
          w: 1,
          h: 1,
          static: true // 设置为静态,不参与拖动
        });
      } else {
        // 当前行已满,放到下一行开头
        layout.push({
          i: 'add-button',
          x: 0,
          y: lastItemY + lastItem.h,
          w: 1,
          h: 1,
          static: true // 设置为静态,不参与拖动
        });
      }
    } else {
      // 如果没有其他item,放在(0,0)位置
      layout.push({
        i: 'add-button',
        x: 0,
        y: 0,
        w: 1,
        h: 1,
        static: true // 设置为静态,不参与拖动
      });
    }
    
    console.log('generateLayout 布局:', layout);
    return layout;
  };

6、响应式设计策略

1. 断点设计

css 复制代码
/* 桌面端 */
@media (min-width: 1024px) {
  .hot-search-content__tabs {
    gap: 4px;
    margin-bottom: 8px;
  }
  
  .hot-search-content__tab {
    padding: 2px 10px;
    font-size: 12px;
    min-width: 46px;
  }
}

/* 平板端 */
@media (max-width: 768px) {
  .hot-search-content__area {
    padding: 10px;
  }
  
  .hot-search-content__tabs {
    gap: 3px;
    margin-bottom: 10px;
  }
  
  .hot-search-content__tab {
    padding: 3px 10px;
    font-size: 11px;
    min-width: 45px;
  }
}

/* 移动端 */
@media (max-width: 480px) {
  .hot-search-content__item {
    gap: 6px;
    padding: 5px 0;
  }
  
  .hot-search-content__title {
    font-size: 11px;
  }
  
  .hot-search-content__count {
    font-size: 10px;
    min-width: 45px;
  }
}

2. 滚动优化

css 复制代码
/* 自定义滚动条样式 */
.hot-search-content__list::-webkit-scrollbar {
  width: 4px;
}

.hot-search-content__list::-webkit-scrollbar-track {
  background: rgba(52, 73, 94, 0.1);
  border-radius: 2px;
}

.hot-search-content__list::-webkit-scrollbar-thumb {
  background: rgba(52, 73, 94, 0.6);
  border-radius: 2px;
}

.hot-search-content__list::-webkit-scrollbar-thumb:hover {
  background: rgba(52, 73, 94, 0.8);
}

7、事件处理优化

为了防止在拖拽布局时误触热搜项目,我们实现了完善的事件处理机制:

javascript 复制代码
// 阻止事件冒泡到 react-grid-layout
const handleItemClick = (e) => {
  e.stopPropagation();
  e.preventDefault();
  // 处理点击逻辑
};

// 阻止滚动事件冒泡
const handleWheel = (e) => {
  e.stopPropagation();
  e.preventDefault();
};

🚀 五、技术总结与思考

技术亮点

  1. 模块化架构:每个平台独立的抓取逻辑,便于维护和扩展
  2. 双层缓存:内存+数据库缓存,平衡性能与可靠性
  3. 响应式设计:适配多种设备尺寸,提供一致的用户体验
  4. 事件处理优化:完善的事件冒泡控制,避免布局冲突
  5. 视觉设计:深色主题+高斯模糊,现代感十足

性能优化策略

  1. 数据缓存:1小时TTL,减少重复请求
  2. 懒加载:按需获取平台数据
  3. 事件防抖:避免频繁的API调用
  4. CSS优化:使用transform和opacity实现动画

扩展性考虑

  1. 新平台接入:只需添加对应的抓取器即可
  2. 数据格式统一:标准化的数据结构,便于扩展
  3. 组件复用:CommonCard组件可复用于其他功能模块

🎉 六、结语

AI灯塔导航项目,热搜卡片功能 的实现,展现了现代Web开发中前后端分离模块化设计响应式布局等核心技术的综合运用,希望大家喜欢这个功能。

AI时代,效率为王。希望这个AI导航网站能帮助到你,pc浏览器访问 www.aidengta.cn/ 即刻体验,无需登录无广告无收费!


最后你敢相信吗?热搜这个功能,我用 AI 编程,只用了一周下班的零散时间(每天半小时左右),全程没亲手敲过一行代码 ------ 从 UI 设计、数据获取,到接口实现、前端落地,每一步都是靠 Cursor 主导完成。更绝的是,就连这篇博客,大部分内容也是 Cursor 帮我写的。下面就贴出当时让它写博客的 prompt:

markdown 复制代码
你是一个技术博客专家,针对实现热搜卡片功能,帮我写一篇技术博客,
从背景/数据来源/接口实现/界面实现/总结等方面阐述下,
要留有放图片的地方,适当加些架构图,运用安装的draw.io输出,
最后在项目根目录blog文件夹输出一个md文件。
要求:
1. 内容结构:从以下几个维度展开
   - 背景介绍
   - 数据来源与架构
   - 接口实现详解
   - 界面实现与交互
   - Grid布局与响应式设计
   - 技术总结与思考

2. 写作风格:生动有趣、易懂

3. 视觉元素:
   - 预留图片位置
   - 适当添加架构图
   - 使用draw.io工具输出图表

4. 输出位置:在项目根目录下的blog文件夹中生成一个md文件

5. 语言:使用中文

6. 架构图要求:
   - 每个模块用一个大虚线边框框住
   - 用箭头指向各个模块的调用顺序
   - 箭头要移到模块外部,避免遮挡内容
   - 使用深色背景+白色文字,确保可读性
   - 包含详细的技术栈和性能指标
   
请复述我的需求,我觉得没问题再执行

更多精彩AI资讯与编程首发,关注公众号"程序员码歌":这里将长期专注分享AI工具与AI编程实战,为开发者与AI爱好者提供帮助,让您每次阅读都有些许收获!

相关推荐
GitLqr3 小时前
AI洞察 | Hunyuan-MT 翻译模型开源,谷歌终端嵌入模型登场
github·ai编程·gemini
kk不中嘞3 小时前
浅谈前端框架
前端·vue.js·react.js·前端框架
服务端技术栈4 小时前
历时 1 个多月,我的第一个微信小程序「图片转 Excel」终于上线了!
前端·后端·微信小程序
一个很老的小萌新4 小时前
json 解析 [{“id“:1,“name“:“apple“},{“id“:2,“name“:“banana“}]
java·前端·json
yanlele4 小时前
前端面试第 78 期 - 2025.09.07 更新 Nginx 专题面试总结(12 道题)
前端·javascript·面试
影子信息4 小时前
el-tree 点击父节点无效,只能选中子节点
前端·javascript·vue.js
拜无忧4 小时前
完美圆角,渐变边框,兼容chrome 60,两层背景的视觉差
前端·css
徐小夕4 小时前
用Vue3写了一款协同文档编辑器,效果简直牛!
前端·javascript·vue.js
wangbing11254 小时前
界面规范8-文字
前端·javascript·html