Discover API
本文档详细介绍了 /api/discover
接口的功能、返回数据结构及其实现代码,并说明了前后端交互时传递消息的处理逻辑,帮助开发人员全面了解该接口的设计思想和实现细节。
1. 接口说明
- 接口地址:
GET /api/discover
- 接口功能:返回博客或新闻内容的列表,主要用于展示摘要信息,便于用户快速了解相关内容。
客户端只需通过 GET 请求访问 /api/discover
接口,即可获得格式化后的 JSON 数据列表。
2. 返回数据格式
接口返回一个 JSON 对象,其中主要包含一个 blogs
数组。数组中的每个元素代表一条博客或新闻记录,结构示例如下:
{
"blogs": [
{
"content": "Summer is the perfect time to fast-track your education and save money! ...",
"description": null,
"url": "https://www.kapiolani.hawaii.edu/event-directory/summer-session-2025/",
"title": "Kapiʻolani CC Summer Session: Save Money, Graduate Faster!",
"similarity": null,
"thumbnail": "https://www.kapiolani.hawaii.edu/wp-content/uploads/studentgreatlawn2025_1920.jpg"
}
]
}
字段说明
content
文章部分内容预览,用于帮助用户快速了解文章概要。description
文章摘要或描述,当前返回为null
,后续可扩展为详细的内容简介。url
原始内容的链接地址,点击后可跳转到详细页面或原文。title
文章标题,简明扼要地反映博客或新闻的主题。thumbnail
缩略图链接,用于前端展示图片资源。similarity
当前记录的相似度信息,暂时返回null
,后续可能用于扩展相关性评分功能。
3. 后端实现代码
下面的 Java 代码展示了 /api/discover
接口的实现。代码使用自定义注解进行路由映射与请求方法定义,实现了将博客或新闻内容封装为 JSON 数据返回给客户端的功能。
接口控制器代码
package com.litongjava.max.search.controller;
import java.util.ArrayList;
import java.util.List;
import com.litongjava.annotation.Get;
import com.litongjava.annotation.RequestPath;
import com.litongjava.max.search.vo.DiscoverResultVo;
import com.litongjava.model.web.WebPageContent;
@RequestPath("/api/discover")
public class ApiDiscoverController {
@Get
public DiscoverResultVo index() {
List<WebPageContent> blogs = new ArrayList<>();
WebPageContent webPageContent = new WebPageContent()
.setUrl("https://www.kapiolani.hawaii.edu/event-directory/summer-session-2025/")
.setTitle("Kapiʻolani CC Summer Session: Save Money, Graduate Faster!")
.setThumbnail("https://www.kapiolani.hawaii.edu/wp-content/uploads/studentgreatlawn2025_1920.jpg")
.setContent("Summer is the perfect time to fast-track your education and save money! ...");
blogs.add(webPageContent);
DiscoverResultVo discoverResultVo = new DiscoverResultVo(blogs);
return discoverResultVo;
}
}
代码解析
路由映射
类上使用@RequestPath("/api/discover")
注解,标识该控制器处理/api/discover
路径下的请求;方法上使用@Get
注解,表明该方法响应 GET 请求。业务逻辑
- 创建一个
List<WebPageContent>
用于存放博客内容数据。 - 构造
WebPageContent
对象,并依次设置文章的 URL、标题、缩略图和内容。 - 将对象添加至列表中,再使用该列表构造一个
DiscoverResultVo
实例返回给前端。
- 创建一个
4. 使用说明
调用方式
前端通过发送 GET 请求至/api/discover
接口,即可获取格式化后的博客或新闻内容列表。扩展方向
- 后续可在返回数据中增加
similarity
、description
等字段,进一步丰富信息内容。 - 支持分页或数量控制参数,以适应大规模数据返回场景。
- 根据业务需求调整数据查询和缓存策略,提升接口响应效率和扩展性。
- 后续可在返回数据中增加
5. 前后端交互示例
在实际业务中,前端会向后端发送一条消息,用于请求对指定网页进行内容汇总。下面是前端传递消息的示例:
{
"type": "message",
"userId": "7130314803843072",
"message": {
"messageId": "7142056710082560",
"chatId": "7142056709832704",
"content": "Summary: https://www.kapiolani.hawaii.edu/event-directory/summer-session-2025/"
},
"files": [],
"focusMode": "webSearch",
"copilotEnabled": true,
"optimizationMode": "speed",
"history": []
}
在后端处理逻辑中,会解析该消息,提取出 URL,并调用 MaxWebPageService.fetch
方法对该网页进行汇总。下面展示部分核心业务逻辑代码:
List<WebPageContent> webPageContents = null;
// 使用 MaxSearchSearchService 对 Tavily Search API 进行调用
if (quesiton != null && quesiton.startsWith("Summary: ")) {
webPageContents = new ArrayList<>(1);
String[] split = quesiton.split(" ");
if (split.length > 1 && split[1].startsWith("http")) {
WebPageContent webpageContent = Aop.get(MaxWebPageService.class).fetch(split[1]);
webPageContents.add(webpageContent);
}
} else {
webPageContents = maxSearchSearchService.search(quesiton);
}
6. 网页内容抓取服务
MaxWebPageService
类提供了网页内容抓取的功能,实现如下:
package com.litongjava.max.search.services;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import com.google.common.util.concurrent.Striped;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.model.web.WebPageContent;
import com.litongjava.tio.utils.snowflake.SnowflakeIdUtils;
public class MaxWebPageService {
private static final Striped<Lock> locks = Striped.lock(1024);
public WebPageContent fetch(String url) {
String sql = "select title, markdown from web_page_cache where url=?";
Row row = Db.findFirst(sql, url);
if (row == null) {
Lock lock = locks.get(url);
lock.lock();
row = Db.findFirst(sql, url);
if (row == null) {
try {
Connection connect = Jsoup.connect(url);
Document document = connect.get();
String title = document.title();
String text = document.body().text();
Row set = Row.by("id", SnowflakeIdUtils.id())
.set("url", url)
.set("title", title)
.set("type", "html")
.set("markdown", text);
Db.save("web_page_cache", set);
return new WebPageContent().setTitle(title).setContent(text).setUrl(url);
} catch (IOException e) {
return new WebPageContent().setTitle("IOException").setContent(e.getMessage()).setUrl(url);
} finally {
lock.unlock();
}
}
}
String title = row.getString("title");
String markdown = row.getString("markdown");
return new WebPageContent().setTitle(title).setContent(markdown).setUrl(url);
}
}
代码解析
缓存查询
首先通过数据库查询判断指定 URL 的网页内容是否已被缓存,如果存在则直接返回缓存数据,避免重复抓取。线程锁机制
采用分布式锁(Striped<Lock>
),确保同一 URL 的抓取操作不会并发执行,从而保证数据一致性和线程安全。网页抓取与数据保存
使用 Jsoup 连接并解析网页内容,获取网页标题和正文,并将结果存入数据库缓存中,供后续请求直接返回。
7. 总结
Discover API 通过简洁明了的设计,实现了博客或新闻内容的提取、封装与返回,为前端展示提供了稳定、灵活的数据接口。该接口具有以下特点:
- 清晰的接口设计:通过 RESTful 风格实现接口调用,便于维护和扩展。
- 灵活的数据结构:返回的 JSON 格式清晰,易于前端解析与展示。
- 完善的业务逻辑:不仅支持简单的内容列表返回,还支持根据前端消息进行网页内容抓取和缓存。
- 良好的扩展性:后续可根据实际业务需求扩展更多字段(如相似度评分、详细描述)和功能(如分页、数量控制)。
通过本篇文档,开发人员能够快速理解 Discover API 的设计思想及实现细节,并在实际项目中进行有效应用与扩展。