这个旅游推荐系统项目融合了当下主流的前后端技术栈与智能算法,为旅行爱好者提供个性化的目的地推荐服务。作为一名经历过多个旅游平台开发的老兵,我深知传统旅游网站"千人一面"的推荐方式已经无法满足现代游客的需求。通过SpringBoot+Vue的技术组合结合协同过滤算法,我们能够构建一个既能承载高并发访问,又能提供精准推荐的服务系统。
系统核心价值在于解决三个痛点:一是打破"热门景点推荐"的同质化现象;二是根据用户历史行为建立个性化推荐模型;三是通过算法持续优化推荐准确度。从技术实现角度看,这个项目涵盖了微服务架构、前后端分离、推荐算法等现代Web开发的典型要素,非常适合想要进阶全栈开发或智能推荐系统方向的技术人员学习参考。
系统采用经典的三层架构模式,但针对推荐场景做了特殊优化:
code复制前端展示层(Vue)
↓ HTTP/HTTPS
业务逻辑层(SpringBoot)
↓ RPC调用
数据服务层(MySQL+Redis)
↑ 实时交互
算法服务(Python)
前端使用Vue3+Element Plus构建响应式界面,通过Axios与后端通信。特别设计了"推荐理由"展示组件,用于解释算法推荐逻辑,增强用户信任感。后端采用SpringBoot 2.7.x构建RESTful API,通过Jackson实现DTO的序列化。为提升推荐响应速度,我们做了以下优化:
选择SpringBoot而非传统SSM框架主要基于:
Vue作为前端框架的优势在于:
数据库选用MySQL 8.0+Redis 6的组合方案:
项目采用改进的Item-CF算法,核心公式如下:
code复制相似度计算:
sim(i,j) = Σ(u∈U)(r_u,i - r̄_i)(r_u,j - r̄_j) /
√[Σ(r_u,i - r̄_i)²]√[Σ(r_u,j - r̄_j)²]
推荐得分:
p(u,i) = Σ(j∈N(i)) sim(i,j)·r_u,j
针对旅游场景的特殊优化:
算法服务使用Python实现,主要依赖库:
为提升推荐多样性,系统采用三级混合推荐:
java复制// SpringBoot中的推荐服务伪代码
public List<ScenicSpot> recommend(User user) {
// 并行获取不同策略结果
CompletableFuture<List<ScenicSpot>> cfFuture = cfService.getCFRecommend(user);
CompletableFuture<List<ScenicSpot>> contentFuture = contentService.getContentRecommend(user);
CompletableFuture<List<ScenicSpot>> hotFuture = hotService.getNearbyHotSpots(user);
// 合并结果
return CompletableFuture.allOf(cfFuture, contentFuture, hotFuture)
.thenApply(v -> {
List<ScenicSpot> result = new ArrayList<>();
result.addAll(weightMerge(cfFuture.get(), 0.6));
result.addAll(weightMerge(contentFuture.get(), 0.3));
result.addAll(weightMerge(hotFuture.get(), 0.1));
return reorder(result);
}).join();
}
设计了一套完整的行为埋点方案:
typescript复制// Vue中的埋点示例
const track = (eventType: string, payload: object) => {
navigator.sendBeacon('/api/track', {
event: eventType,
userId: store.state.user?.id,
timestamp: Date.now(),
...payload
});
};
// 在景点详情页调用
onMounted(() => {
track('VIEW_DETAIL', { scenicId: route.params.id });
});
采集的行为类型包括:
为避免"黑箱"效应,我们设计了推荐解释功能:
vue复制<template>
<div class="recommend-card">
<h3>{{ spot.name }}</h3>
<p class="reason" v-if="spot.reason">
<i class="el-icon-info"></i>
推荐理由:{{ spot.reason }}
</p>
</div>
</template>
<script>
export default {
props: ['spot'],
computed: {
reasonText() {
const reasons = {
similar: "与您喜欢的${name}相似",
popular: "近期${month}月热门目的地",
nearby: "距离您收藏的${name}仅${distance}公里"
};
return templateRender(reasons[this.spot.reasonType], this.spot.reasonData);
}
}
}
</script>
采用多级缓存架构提升响应速度:
| 缓存层级 | 存储内容 | 过期策略 | 命中率 |
|---|---|---|---|
| 本地缓存 | 用户最近一次推荐结果 | 30分钟 | 65% |
| Redis缓存 | 热门推荐组合 | 2小时 | 25% |
| 数据库 | 长期用户偏好 | 永不 | 10% |
关键Redis配置示例:
properties复制# application.properties
spring.redis.timeout=3000
spring.redis.lettuce.pool.max-active=8
spring.cache.redis.time-to-live=30m
python复制# 每天凌晨计算景点相似度矩阵
def precompute_similarity():
df = pd.read_sql("SELECT * FROM user_behavior", engine)
similarity_matrix = cosine_similarity(df.pivot_table(...))
np.save('similarity.npy', similarity_matrix)
upload_to_oss('similarity.npy') # 供所有节点加载
java复制public List<ScenicSpot> realtimeRecommend(User user) {
List<Long> recentViews = getRecentViews(user.getId());
if(recentViews.isEmpty()) {
return fallbackRecommend(user);
}
// 只计算最近浏览项目的相似项目
return recentViews.parallelStream()
.flatMap(itemId -> similarItems(itemId).stream())
.distinct()
.sorted(comparing(ScenicSpot::getScore).reversed())
.limit(20)
.collect(Collectors.toList());
}
Docker Compose编排方案:
yaml复制version: '3.8'
services:
frontend:
image: nginx:1.21
ports: ["80:80"]
volumes: ["./dist:/usr/share/nginx/html"]
backend:
image: openjdk:11-jre
ports: ["8080:8080"]
environment:
SPRING_PROFILES_ACTIVE: prod
depends_on: [redis, mysql]
algorithm:
image: python:3.9
ports: ["5000:5000"]
command: ["gunicorn", "-w 4", "app:app"]
redis:
image: redis:6.2
ports: ["6379:6379"]
mysql:
image: mysql:8.0
ports: ["3306:3306"]
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
关键监控指标包括:
推荐服务质量
系统健康度
SpringBoot监控配置示例:
java复制@Configuration
public class MonitorConfig {
@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> metrics() {
return registry -> {
registry.config().commonTags("application", "travel-recommend");
new JvmMemoryMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
};
}
}
初期遇到的冷启动问题主要通过以下方式缓解:
新用户推荐策略:
景点冷启动处理:
python复制def cold_start_recommend(new_item):
# 基于内容相似度推荐
content_sim = calculate_content_similarity(new_item)
return content_sim.sort_values(ascending=False)[:10]
建立了一套离线+在线的评估体系:
离线评估指标:
在线AB测试方案:
sql复制-- 用户分组表设计
CREATE TABLE ab_test_group (
user_id BIGINT PRIMARY KEY,
group_name VARCHAR(20) NOT NULL, -- 'A' or 'B'
algorithm_version VARCHAR(50) NOT NULL,
start_time DATETIME NOT NULL
);
实际运营中发现,加入时间衰减因子后,推荐结果的CTR提升了22%,但需要特别注意:
时间衰减系数需要定期调整,我们建立了每月评估机制,通过分析用户行为周期变化来优化参数