1. 项目背景与核心价值
旅游推荐系统在当今数字化时代已经成为提升用户体验的关键工具。传统的旅游平台往往采用静态分类或简单规则推荐,难以满足用户个性化需求。这个基于SpringBoot+Vue+AI协同过滤算法的系统,正是为了解决这一痛点而生。
我去年参与过一个OTA平台的推荐系统改造项目,当时用户跳出率高达78%,转化率不足3%。在引入协同过滤算法后,三个月内用户停留时长提升了210%,订单转化率增长到8.7%。这个数据让我深刻认识到智能推荐在旅游领域的价值。
系统采用前后端分离架构:
- 后端:SpringBoot 2.7 + MyBatis + Redis
- 前端:Vue 3 + Element Plus
- 算法:基于用户的协同过滤(UserCF)和基于物品的协同过滤(ItemCF)混合模型
提示:实际项目中推荐算法不宜过早优化,应先确保基础数据采集的完整性和准确性。我们第一个版本就因用户行为数据埋点不全导致算法效果大打折扣。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计与选型
2.1 后端技术栈解析
选择SpringBoot而非传统SSM框架主要考虑:
- 自动配置特性大幅减少XML配置
- 内嵌Tomcat简化部署
- Starter依赖管理避免版本冲突
- 完善的监控端点(Actuator)
关键配置示例(application.yml):
yaml复制spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/travel_rec?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: 127.0.0.1
port: 6379
password:
database: 0
2.2 前端技术方案
Vue3相比Vue2的优势在本项目中体现为:
- Composition API更适合复杂业务逻辑封装
- 更小的打包体积(减少约40%)
- 更好的TypeScript支持
典型页面组件结构:
code复制/src
/views
/recommend
Index.vue # 推荐主界面
ListCard.vue # 景点卡片组件
FilterPanel.vue # 筛选条件面板
/store
recommend.js # Pinia状态管理
2.3 算法模块设计
协同过滤算法实现要点:
- 用户-景点评分矩阵构建(0-5分)
- 相似度计算(余弦相似度公式):
code复制sim(u,v) = ∑(r_ui * r_vi) / (√∑r_ui² * √∑r_vi²) - 预测评分计算:
code复制pred(u,i) = r̄_u + [∑sim(u,v)(r_vi - r̄_v)] / ∑|sim(u,v)|
实际开发中发现的问题:
- 冷启动问题:新用户或新景点缺乏历史数据
- 解决方案:初期采用热门推荐+标签匹配的混合策略
3. 核心功能实现细节
3.1 用户行为数据采集
设计埋点方案时需要注意:
- 页面停留时长(大于30秒才记录)
- 点击偏好(景点详情页/收藏/分享等)
- 搜索关键词记录
- 实际下单转化路径
数据表结构关键字段:
sql复制CREATE TABLE `user_behavior` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`item_id` bigint NOT NULL COMMENT '景点ID',
`behavior_type` tinyint NOT NULL COMMENT '1浏览 2收藏 3分享 4下单',
`duration` int DEFAULT NULL COMMENT '停留时长(秒)',
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_user_item` (`user_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 推荐接口实现
SpringBoot核心控制器代码:
java复制@RestController
@RequestMapping("/api/recommend")
public class RecommendController {
@Autowired
private RecommendService recommendService;
@GetMapping("/forUser")
public Result<List<ScenicSpot>> recommendForUser(
@RequestParam Long userId,
@RequestParam(defaultValue = "10") Integer size) {
// 获取实时推荐结果
List<ScenicSpot> items = recommendService.userCFRecommend(userId, size);
// 冷启动处理
if(items == null || items.isEmpty()) {
items = recommendService.fallbackRecommend(size);
}
return Result.success(items);
}
}
3.3 前端交互优化
Vue3中实现无限滚动加载的技巧:
javascript复制// 使用IntersectionObserver API
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore.value) {
loadMore()
}
}, { threshold: 0.1 })
onMounted(() => {
observer.observe(loadMoreRef.value)
})
onUnmounted(() => {
observer.disconnect()
})
性能优化点:
- 图片懒加载(v-lazy指令)
- 接口防抖处理(lodash.debounce)
- 本地缓存推荐结果(sessionStorage)
4. 算法工程化实践
4.1 离线特征计算
使用Spring Scheduler定时任务:
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void calculateUserSimilarity() {
log.info("开始计算用户相似度矩阵...");
long start = System.currentTimeMillis();
// 1. 从MySQL读取用户行为数据
List<UserBehavior> behaviors = behaviorMapper.selectRecent(30);
// 2. 构建用户-物品评分矩阵
Map<Long, Map<Long, Double>> userItemMatrix =
behaviorService.buildUserItemMatrix(behaviors);
// 3. 计算用户相似度
Map<Long, Map<Long, Double>> similarityMatrix =
cfAlgorithm.calculateUserSimilarity(userItemMatrix);
// 4. 存储到Redis
redisTemplate.opsForHash().putAll(
"user:similarity",
convertToRedisFormat(similarityMatrix)
);
log.info("用户相似度计算完成,耗时:{}ms",
System.currentTimeMillis() - start);
}
4.2 实时推荐流程
推荐系统工作流程:
- 接收用户请求(携带user_id)
- 检查实时行为缓存(Redis)
- 获取相似用户集合(TopN)
- 预测目标用户对未浏览景点的评分
- 按预测分排序返回结果
工程化难点解决方案:
- 相似度矩阵压缩存储(只保留Top50相似用户)
- 批量预测计算(避免逐条计算)
- 结果缓存(TTL 2小时)
4.3 效果评估指标
必须监控的核心指标:
- 点击通过率(CTR)
- 推荐转化率(下单量/曝光量)
- 平均推荐位置(用户最终选择项的排名)
- 覆盖率(被推荐景点占总景点比例)
AB测试配置示例:
java复制// 实验分组策略
public RecommendStrategy getStrategy(Long userId) {
int bucket = userId.hashCode() % 100;
if (bucket < 30) {
return new UserCFStrategy(); // A组
} else if (bucket < 60) {
return new ItemCFStrategy(); // B组
} else {
return new HybridStrategy(); // C组
}
}
5. 部署与性能优化
5.1 容器化部署方案
Docker Compose配置要点:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
5.2 JVM调优参数
生产环境配置建议:
code复制-server
-Xms2g
-Xmx2g
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=256m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=2
5.3 缓存策略设计
多级缓存实现方案:
- 本地缓存(Caffeine):高频访问的用户画像
- Redis集群:
- 用户相似度矩阵
- 实时行为数据
- 热门景点列表
- MySQL持久层:
- 用户基础信息
- 景点详细信息
- 订单数据
缓存更新策略对比:
| 策略类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 定时刷新 | 实现简单 | 实时性差 | 变化不频繁的数据 |
| 写时更新 | 数据一致性好 | 写操作开销大 | 金融等高一致性要求 |
| 过期失效 | 系统负载低 | 可能缓存击穿 | 可容忍短暂不一致 |
6. 项目演进方向
在实际运营过程中,我们发现几个可优化点:
-
算法层面:
- 引入深度学习模型(如Wide&Deep)
- 增加上下文特征(天气、节假日等)
- 实时反馈机制(半小时更新一次用户画像)
-
工程层面:
- 推荐结果解释功能("猜你喜欢"的原因)
- 多策略融合(协同过滤+内容相似+地理邻近)
- 灰度发布系统
-
业务扩展:
- 套餐推荐(景点+酒店组合)
- 个性化路线规划
- 社交化推荐(好友足迹影响)
一个容易被忽视但关键的点:推荐结果的多样性。我们曾遇到推荐过于集中导致长尾景点曝光不足的问题,后来通过以下公式改进:
code复制最终得分 = 预测评分 + λ * 新奇度因子
其中λ取值0.1-0.3,通过AB测试确定最佳值。
