一站式聚合搜索 · 多数据源并发检索 · Elasticsearch 全文检索
Vue 3 + Spring Boot + Elasticsearch + Redis 全栈项目
Allso 是一个聚合搜索平台,支持用户在一个入口同时搜索 文章、图片、用户 三类数据源。项目采用前后端分离架构,后端通过 数据源注册机制 + CompletableFuture 并发查询 实现可扩展的聚合搜索,使用 Elasticsearch + IK 中文分词 实现文章全文检索,并支持 全量同步 + 增量同步 + Canal binlog 三种数据同步模式。
| 特性 | 说明 |
|---|---|
| 🚀 多数据源聚合搜索 | 一次请求并发检索文章、图片、用户,毫秒级响应 |
| 🔎 Elasticsearch 全文检索 | IK 中文分词,支持标题、内容、标签多字段组合查询 |
| 🧩 数据源注册机制 | 策略模式 + 注册表模式,新增数据源零侵入扩展 |
| ⚡ CompletableFuture 并发 | 全类型搜索时多数据源并行执行,大幅降低响应时间 |
| 🔄 多模式数据同步 | 全量同步(启动时)+ 增量同步(定时任务)+ Canal binlog(预留) |
| 🛡️ 完善的权限体系 | 自定义 @AuthCheck 注解 + AOP 拦截,支持用户/管理员角色 |
| 📦 腾讯云 COS 集成 | 图片/文件上传至对象存储,支持业务隔离 |
| 🎨 响应式前端界面 | Vue 3 + Ant Design Vue,紫蓝渐变现代风格 |
| 技术 | 版本 | 用途 |
|---|---|---|
| Spring Boot | 2.7.2 | 核心框架 |
| Spring Data Elasticsearch | - | 全文检索 |
| MyBatis-Plus | 3.5.2 | 增强 ORM |
| Spring Data Redis | - | 缓存 / 分布式 Session |
| MySQL | 8.x | 关系型数据库 |
| Elasticsearch | 7.17.25 | 搜索引擎 |
| Knife4j | 3.0.3 | API 接口文档 |
| 腾讯云 COS SDK | 5.6.89 | 对象存储 |
| Jsoup | 1.15.3 | 网页爬虫(图片搜索) |
| Hutool | 5.8.8 | Java 工具库 |
| EasyExcel | 3.1.1 | Excel 导入导出 |
| Canal Client | 1.1.0 | MySQL binlog 同步 |
| 技术 | 版本 | 用途 |
|---|---|---|
| Vue.js | ^3.2.13 | 核心框架 |
| Ant Design Vue | ^3.3.0-beta.4 | UI 组件库 |
| Vue Router | ^4.0.3 | 路由管理 |
| Axios | ^1.3.4 | HTTP 请求 |
| TypeScript | ~4.5.5 | 类型系统 |
| Vue CLI | ~5.0.0 | 构建工具链 |
flowchart TB
subgraph Frontend["🎨 前端 (Vue 3 + Ant Design Vue)"]
UI[搜索页面 IndexPage]
UI -->|分类切换| Tab[文章 / 图片 / 用户]
end
subgraph Backend["⚙️ 后端 (Spring Boot)"]
API[SearchController<br/>聚合搜索入口]
Facade[SearchFacade<br/>门面模式]
Registry[DataSourceRegistry<br/>数据源注册表]
subgraph DataSources["数据源层 (策略模式)"]
DS1[PostDataSource<br/>→ Elasticsearch]
DS2[UserDataSource<br/>→ MySQL]
DS3[PictureDataSource<br/>→ 必应爬虫]
end
API --> Facade
Facade -->|CompletableFuture 并发| Registry
Registry --> DataSources
end
subgraph Storage["💾 数据层"]
ES[(Elasticsearch<br/>全文检索)]
DB[(MySQL<br/>业务数据)]
Redis[(Redis<br/>Session缓存)]
Bing[必应图片<br/>外部爬取]
end
DS1 --> ES
DS2 --> DB
DS3 --> Bing
Backend <-->|Session| Redis
Frontend -->|HTTP API| API
ES -.->|双向同步| DB
style Frontend fill:#e6f7ff,stroke:#1890ff
style Backend fill:#f6ffed,stroke:#52c41a
style Storage fill:#fff7e6,stroke:#fa8c16
Allso/
├── src/main/java/com/chp/allso/ # 后端源码
│ ├── annotation/ # 自定义注解
│ │ └── AuthCheck.java # 权限校验注解
│ ├── aop/ # 切面编程
│ │ ├── AuthInterceptor.java # 权限拦截器
│ │ └── LogInterceptor.java # 日志拦截器
│ ├── controller/ # 控制层
│ │ ├── SearchController.java # 🔑 聚合搜索入口
│ │ ├── PostController.java # 文章接口
│ │ ├── UserController.java # 用户接口
│ │ ├── PictureController.java # 图片接口
│ │ └── FileController.java # 文件上传
│ ├── datasource/ # 🔑 数据源注册机制
│ │ ├── DataSource.java # 统一接口
│ │ ├── DataSourceRegistry.java # 注册表
│ │ ├── PostDataSource.java # ES 文章源
│ │ ├── UserDataSource.java # MySQL 用户源
│ │ └── PictureDataSource.java # 必应图片源
│ ├── esdao/ # Elasticsearch 数据访问层
│ ├── manager/ # 管理层(门面模式)
│ │ └── SearchFacade.java # 🔑 搜索门面
│ ├── service/ # 业务逻辑层
│ ├── mapper/ # MyBatis 数据访问
│ ├── model/ # 数据模型
│ │ ├── entity/ # 数据库实体
│ │ ├── dto/ # 数据传输对象
│ │ └── vo/ # 视图对象
│ ├── job/ # 定时任务 / 数据同步
│ │ ├── cycle/IncSyncPostToEs.java # 增量同步
│ │ └── once/FullSyncPostToEs.java # 全量同步
│ └── config/ # 配置类
│
├── allso-frontend/ # 前端源码
│ └── src/
│ ├── pages/IndexPage.vue # 🔑 搜索主页
│ ├── components/ # 业务组件
│ │ ├── PostList.vue # 文章列表
│ │ ├── PictureList.vue # 图片列表
│ │ └── UserList.vue # 用户列表
│ ├── plugins/myAxios.ts # Axios 封装
│ └── router/index.ts # 路由配置
│
├── sql/ # SQL 脚本
│ ├── create_table.sql # 建表语句
│ └── post_es_mapping.json # ES 索引映射
├── Dockerfile # Docker 部署
└── pom.xml # Maven 依赖
🔑 标记为核心文件
erDiagram
USER ||--o{ POST : "发布"
USER ||--o{ POST_THUMB : "点赞"
USER ||--o{ POST_FAVOUR : "收藏"
POST ||--o{ POST_THUMB : "被点赞"
POST ||--o{ POST_FAVOUR : "被收藏"
USER {
bigint id PK
varchar userAccount "登录账号"
varchar userPassword "MD5加密"
varchar userName "昵称"
varchar userAvatar "头像"
varchar userProfile "简介"
varchar userRole "user/admin/ban"
datetime createTime
datetime updateTime
tinyint isDelete "逻辑删除"
}
POST {
bigint id PK
varchar title "标题"
text content "内容"
varchar tags "标签JSON"
int thumbNum "点赞数"
int favourNum "收藏数"
bigint userId "作者ID"
datetime createTime
datetime updateTime
tinyint isDelete "逻辑删除"
}
POST_THUMB {
bigint id PK
bigint postId "帖子ID"
bigint userId "用户ID"
datetime createTime
}
POST_FAVOUR {
bigint id PK
bigint postId "帖子ID"
bigint userId "用户ID"
datetime createTime
}
MySQL post 表 |
Elasticsearch post 索引 |
说明 |
|---|---|---|
id (bigint) |
id (long) |
主键 |
title (varchar) |
title (text · ik_max_word) |
标题,中文分词 |
content (text) |
content (text · ik_max_word) |
内容,中文分词 |
tags (varchar JSON) |
tags (List<String>) |
标签,DB为字符串/ES为数组 |
userId (bigint) |
userId (keyword) |
作者ID |
createTime (datetime) |
createTime (date) |
创建时间 |
thumbNum / favourNum |
— | 仅存 MySQL,查询时回查 |
sequenceDiagram
participant U as 用户
participant F as 前端 IndexPage
participant C as SearchController
participant SF as SearchFacade
participant R as DataSourceRegistry
participant ES as Elasticsearch
participant DB as MySQL
participant B as 必应图片
U->>F: 输入关键词 + 选择分类
F->>C: POST /api/search/all
alt 指定类型搜索 (type = post/user/picture)
C->>SF: searchAll(request)
SF->>R: getDataSourceByType(type)
R-->>SF: 返回对应数据源
SF->>ES: 查询文章 (BoolQuery)
SF->>DB: 查询用户
SF->>B: 爬取图片
else 全类型搜索 (type 为空)
C->>SF: searchAll(request)
par CompletableFuture 并发
SF->>ES: 文章查询任务
and
SF->>DB: 用户查询任务
and
SF->>B: 图片爬取任务
end
Note over SF: allOf().join() 等待全部完成
end
SF-->>C: SearchVO 聚合结果
C-->>F: 统一响应
F-->>U: 渲染对应列表
flowchart LR
subgraph 同步模式
direction TB
M1["📦 全量同步<br/>FullSyncPostToEs<br/>实现 CommandLineRunner<br/>项目启动时执行"]
M2["🔄 增量同步<br/>IncSyncPostToEs<br/>@Scheduled 每60秒<br/>同步近5分钟变更"]
M3["📡 Canal binlog<br/>监听 MySQL 变更<br/>实时同步(预留)"]
end
DB[(MySQL)] --> M1 --> ES[(Elasticsearch)]
DB --> M2 --> ES
DB -.->|binlog| M3 -.-> ES
ES -->|"查询时回查 DB"| DB
Note["ES 查询结果会回查 MySQL<br/>获取最新点赞数、收藏数"]
基础路径:
/api· 接口文档:http://localhost:8102/api/doc.html
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
| POST | /search/all |
统一搜索入口,支持全类型/指定类型 | 公开 |
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
| POST | /post/add |
创建文章 | 登录 |
| POST | /post/edit |
编辑文章 | 作者/管理员 |
| POST | /post/delete |
删除文章 | 作者/管理员 |
| GET | /post/get/vo |
获取文章详情 | 公开 |
| POST | /post/list/page/vo |
分页查询(MySQL) | 公开 |
| POST | /post/search/page/vo |
分页搜索(Elasticsearch) | 公开 |
| POST | /post/my/list/page/vo |
我的文章 | 登录 |
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
| POST | /user/register |
用户注册 | 公开 |
| POST | /user/login |
用户登录 | 公开 |
| POST | /user/logout |
退出登录 | 公开 |
| GET | /user/get/login |
获取当前登录用户 | 公开 |
| GET | /user/get/vo |
根据ID获取用户信息 | 公开 |
| POST | /user/update/my |
更新个人信息 | 登录 |
| POST | /user/add |
创建用户 | 管理员 |
| POST | /user/delete |
删除用户 | 管理员 |
| POST | /user/update |
更新用户 | 管理员 |
| POST | /user/list/page |
分页查询用户 | 管理员 |
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
| POST | /post_thumb/ |
点赞 / 取消点赞 | 登录 |
| POST | /post_favour/ |
收藏 / 取消收藏 | 登录 |
| POST | /post_favour/my/list/page |
我的收藏列表 | 登录 |
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
| POST | /picture/list/page/vo |
搜索图片(必应爬虫) | 公开 |
| POST | /file/upload |
文件上传(腾讯云COS) | 登录 |
| 环境 | 要求 |
|---|---|
| JDK | 1.8 |
| Node.js | 16+ |
| MySQL | 8.0+ |
| Redis | 6.0+ |
| Elasticsearch | 7.17.25(需与后端版本一致) |
# 创建数据库
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS allso DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 导入建表语句
mysql -u root -p allso < sql/create_table.sql启动 Redis 和 Elasticsearch 服务。
编辑 src/main/resources/application.yml,替换占位符:
spring:
datasource:
url: jdbc:mysql://localhost:3306/allso
username: root
password: <你的数据库密码> # 替换 <Yours>
redis:
host: localhost
port: 6379
elasticsearch:
uris: http://localhost:9200
cos:
client:
accessKey: <你的腾讯云AK> # 替换 <Yours>
secretKey: <你的腾讯云SK> # 替换 <Yours>
region: ap-guangzhou
bucket: <你的存储桶名># 在项目根目录执行
mvnw spring-boot:run启动后访问接口文档:http://localhost:8102/api/doc.html
cd allso-frontend
npm install # 或 yarn install
npm run serve # 或 yarn serve访问 http://localhost:8080 即可使用。
# 构建镜像
docker build -t allso-backend .
# 运行容器(需确保 MySQL/Redis/ES 可连通)
docker run -d --name allso -p 8102:8102 allso-backend前端采用紫蓝渐变背景 + 圆角卡片设计,包含三大搜索分类:
| 分类 | 组件 | 布局 | 数据来源 |
|---|---|---|---|
| 📝 文章 | PostList | 水平列表,头像+标题+内容 | Elasticsearch |
| 🖼️ 图片 | PictureList | 响应式网格(1/2/4/6列) | 必应爬虫 |
| 👤 用户 | UserList | 响应式网格卡片 | MySQL |
路由采用 Hash 模式,支持通过 URL 参数共享搜索状态:
/#/post?text=关键词
classDiagram
class DataSource~T~ {
<<interface>>
+doSearch(searchText, pageNum, pageSize) Page~T~
}
class DataSourceRegistry {
-Map dataSources
+getDataSourceByType(type) DataSource
+init()
}
class PostDataSource {
+doSearch() 查询 Elasticsearch
}
class UserDataSource {
+doSearch() 查询 MySQL
}
class PictureDataSource {
+doSearch() 爬取必应图片
}
DataSource <|.. PostDataSource
DataSource <|.. UserDataSource
DataSource <|.. PictureDataSource
DataSourceRegistry o--> DataSource
新增数据源只需实现
DataSource接口并注册到DataSourceRegistry,无需修改任何搜索逻辑。
| 模式 | 应用位置 | 作用 |
|---|---|---|
| 门面模式 | SearchFacade |
封装多数据源搜索复杂逻辑 |
| 策略模式 | DataSource<T> 接口 |
可替换的搜索策略 |
| 注册表模式 | DataSourceRegistry |
动态分发数据源 |
| AOP | AuthInterceptor / LogInterceptor |
权限校验、日志监控 |
本项目(Allso - 聚合搜索平台)由 chp 独立设计并开发,相关知识产权归作者所有。
- 本项目仅用于 学术交流 目的,不得用于任何商业用途。
- 未经作者书面许可,禁止将本项目代码、设计文档、论文内容等用于商业出版、售卖或二次分发。
- 引用本项目代码或设计思想时,请注明出处并保留原作者信息。
- 作者保留对本项目的最终解释权。