毕业设计实战:基于SpringBoot+Vue的旅游推荐系统,从需求到实现全流程拆解,附避坑指南!
谁懂啊!当初做旅游推荐系统毕设时,光"协同过滤推荐算法"和"景点关联"就卡了我半个月——一开始算法没调好,结果推荐的景点全是冷门地,导师直接让我"重新设计推荐逻辑"😫 今天把从需求分析到功能实现的全流程经验分享出来,跟着做就能轻松搞定毕设!
一、先搞懂"旅游推荐系统要做什么"!需求分析是关键
刚开始我跳过需求分析就写代码,花三周做了个"深度学习推荐模型",结果导师一句"核心是景点管理、路线规划、用户互动,不是复杂算法"直接打回重改!
1. 核心用户&功能拆解(实战总结版)
旅游推荐系统有三类核心用户:管理员、普通用户,功能要区分清楚:
管理员端(系统管理):
- 景点管理:审核景点信息(名称、类型、门票、图片)、设置景点推荐等级
- 路线管理:设计旅游路线(景点组合、预算估算、最佳时间)、管理路线分类
- 用户管理:管理用户账号、查看用户收藏记录、分析用户偏好
- 内容管理:发布公告、审核论坛帖子、回复用户留言
- 数据统计:景点热度分析、用户活跃度统计、推荐效果评估
普通用户端(核心功能):
- 景点浏览:查看景点列表(按类型/热度/评分筛选)、查看景点详情(图片、门票、评价)
- 路线推荐:获取个性化旅游路线推荐、查看路线详情(景点列表、预算、时间安排)
- 互动功能:收藏景点/路线、发布景点留言/路线留言、参与论坛讨论
- 个性化推荐:基于用户浏览历史和收藏记录推荐相关景点和路线
- 个人中心:管理收藏记录、查看浏览历史、修改个人信息
2. 需求分析避坑指南(血泪教训!)
- 别闭门造车!找喜欢旅游的同学测试提意见:有同学说"想看到路线的时间安排",我才加了"路线行程表"功能
- 一定要画用例图!用DrawIO画"用户-收藏景点""系统-个性化推荐"等核心用例
- 写需求规格文档!约束条件要写清楚:“景点图片必须上传”“路线预算要合理”“推荐算法要可解释”
3. 可行性分析要专业
导师最爱问"可行吗",从3个角度回答:
- 技术可行:SpringBoot+Vue+MySQL+推荐算法,技术栈成熟
- 经济可行:开发工具全免费,地图API可用免费额度
- 操作可行:界面参考马蜂窝/携程,用户上手快
二、技术选型别追新!实用最重要
刚开始我用Spark+机器学习库,结果部署复杂,本地都跑不起来😫 后来换成SpringBoot+Vue2+MySQL+简单推荐算法,真香!
1. 技术栈对比(附避坑提醒)
| 技术工具 | 为什么选它 | 避坑提醒! |
|---|---|---|
| SpringBoot 2.7 | 快速开发,集成方便 | 别用3.0!生态还不完善 |
| Vue 2 | 生态成熟,Element UI好用 | 别用Vue 3组合式API!增加复杂度 |
| Element UI | 旅游类组件丰富(卡片、轮播图) | 按需引入,控制包大小 |
| MySQL 8.0 | 关系型数据存储稳定 | 一定要设计好表关联 |
| 推荐算法 | 协同过滤(简单实现) | 别用太复杂的算法,毕设够用就行 |
| ECharts | 数据可视化展示 | 用于展示旅游数据统计 |
2. 开发环境搭建
# 后端spring init --dependencies=web,mybatis,mysql,lombok travel-recommend# 前端vue create travel-frontendcdtravel-frontend vueaddelementnpminstallaxios vue-router vuex echarts3. 架构图要画!答辩加分
用DrawIO画前后端分离+推荐引擎架构:
- 前端:Vue + Element UI + ECharts
- 后端:SpringBoot + MyBatis
- 推荐模块:基于内容的推荐 + 协同过滤
- 数据层:MySQL + Redis(缓存用户行为)
三、数据库设计:推荐算法的基础
这部分是推荐系统的核心,我当初用户行为表设计不合理,推荐效果很差。
1. 核心实体&ER图
核心表设计:
- 用户表(user):id、用户名、头像、联系方式、旅游偏好标签
- 景点表(attraction):id、名称、类型、图片、门票、地址、评分、描述
- 旅游路线表(route):id、名称、类型、预算、适合季节、行程天数、包含景点
- 收藏表(collection):id、用户ID、收藏对象ID、收藏类型(景点/路线)、收藏时间
- 浏览记录表(browse_history):id、用户ID、浏览对象ID、浏览类型、浏览时长、浏览时间
- 留言表(comment):id、用户ID、对象ID、留言类型、内容、评分、时间
- 用户偏好表(user_preference):id、用户ID、偏好标签(如:自然风光、历史遗迹、美食)、权重
2. 建表SQL示例(关键表)
-- 景点表(核心表)CREATETABLE`attraction`(`id`intNOTNULLAUTO_INCREMENT,`name`varchar(100)NOTNULLCOMMENT'景点名称',`type`intNOTNULLCOMMENT'景点类型(1-自然风光,2-历史遗迹,3-城市地标,4-主题公园)',`image`varchar(500)DEFAULTNULLCOMMENT'景点图片(JSON数组)',`ticket_price`decimal(10,2)DEFAULTNULLCOMMENT'门票价格',`address`varchar(200)DEFAULTNULLCOMMENT'地址',`province`varchar(50)DEFAULTNULLCOMMENT'省份',`city`varchar(50)DEFAULTNULLCOMMENT'城市',`latitude`decimal(10,6)DEFAULTNULLCOMMENT'纬度',`longitude`decimal(10,6)DEFAULTNULLCOMMENT'经度',`description`textCOMMENT'景点描述',`open_time`varchar(100)DEFAULTNULLCOMMENT'开放时间',`best_season`varchar(50)DEFAULTNULLCOMMENT'最佳季节',`visit_duration`intDEFAULTNULLCOMMENT'建议游览时长(小时)',`popularity`intDEFAULT0COMMENT'热度(基于浏览、收藏计算)',`average_score`decimal(3,2)DEFAULT0.00COMMENT'平均评分',`status`intDEFAULT1COMMENT'状态(1-正常,0-下架)',`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,`update_time`datetimeDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,PRIMARYKEY(`id`),KEY`idx_type`(`type`),KEY`idx_city`(`city`),KEY`idx_popularity`(`popularity`),KEY`idx_score`(`average_score`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;-- 旅游路线表(推荐核心)CREATETABLE`travel_route`(`id`intNOTNULLAUTO_INCREMENT,`name`varchar(100)NOTNULLCOMMENT'路线名称',`type`intNOTNULLCOMMENT'路线类型(1-经典线路,2-深度游,3-亲子游,4-背包客)',`budget`decimal(10,2)NOTNULLCOMMENT'预算估算',`days`intNOTNULLCOMMENT'行程天数',`suitable_season`varchar(100)DEFAULTNULLCOMMENT'适合季节',`cover_image`varchar(200)DEFAULTNULLCOMMENT'封面图',`description`textCOMMENT'路线描述',`attraction_ids`varchar(500)NOTNULLCOMMENT'包含景点ID(JSON数组)',`attraction_names`varchar(1000)DEFAULTNULLCOMMENT'包含景点名称(用于展示)',`day_plan`textCOMMENT'每日行程安排(JSON)',`view_count`intDEFAULT0COMMENT'浏览次数',`collect_count`intDEFAULT0COMMENT'收藏次数',`average_score`decimal(3,2)DEFAULT0.00COMMENT'平均评分',`status`intDEFAULT1COMMENT'状态(1-正常,0-下架)',`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),KEY`idx_type`(`type`),KEY`idx_days`(`days`),KEY`idx_budget`(`budget`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;-- 用户行为表(推荐算法基础)CREATETABLE`user_behavior`(`id`intNOTNULLAUTO_INCREMENT,`user_id`intNOTNULLCOMMENT'用户ID',`item_id`intNOTNULLCOMMENT'项目ID(景点或路线)',`item_type`intNOTNULLCOMMENT'项目类型(1-景点,2-路线)',`behavior_type`intNOTNULLCOMMENT'行为类型(1-浏览,2-收藏,3-评论,4-评分)',`behavior_value`decimal(3,2)DEFAULTNULLCOMMENT'行为值(如评分)',`duration`intDEFAULTNULLCOMMENT'停留时长(秒)',`behavior_time`datetimeNOTNULLCOMMENT'行为时间',`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),KEY`idx_user_item`(`user_id`,`item_id`,`item_type`),KEY`idx_user_behavior`(`user_id`,`behavior_type`),KEY`idx_item_behavior`(`item_id`,`item_type`,`behavior_type`),KEY`idx_time`(`behavior_time`),CONSTRAINT`fk_behavior_user`FOREIGNKEY(`user_id`)REFERENCES`user`(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;-- 收藏表(用户偏好分析)CREATETABLE`collection`(`id`intNOTNULLAUTO_INCREMENT,`user_id`intNOTNULLCOMMENT'用户ID',`item_id`intNOTNULLCOMMENT'收藏项目ID',`item_type`intNOTNULLCOMMENT'收藏类型(1-景点,2-路线)',`collection_time`datetimeNOTNULLCOMMENT'收藏时间',`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),UNIQUEKEY`uk_user_item`(`user_id`,`item_id`,`item_type`),KEY`idx_user`(`user_id`),KEY`idx_item`(`item_id`,`item_type`),CONSTRAINT`fk_collection_user`FOREIGNKEY(`user_id`)REFERENCES`user`(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;3. 推荐算法设计(毕设够用版)
方案一(简单版):基于热度的推荐
-- 热门景点推荐(按收藏数+评分)SELECTa.*,(a.popularity*0.6+a.average_score*100*0.4)asrecommend_scoreFROMattraction aWHEREa.status=1ORDERBYrecommend_scoreDESCLIMIT10;方案二(进阶版):基于内容的推荐
// 根据用户收藏的景点类型推荐相似景点publicList<Attraction>contentBasedRecommend(IntegeruserId){// 1. 获取用户收藏的景点List<Integer>collectedAttractionIds=collectionMapper.selectCollectedAttractions(userId);// 2. 分析用户偏好类型Map<Integer,Integer>typePreferences=newHashMap<>();for(IntegerattractionId:collectedAttractionIds){Attractionattraction=attractionMapper.selectById(attractionId);typePreferences.merge(attraction.getType(),1,Integer::sum);}// 3. 按偏好类型推荐(排除已收藏的)List<Integer>preferredTypes=typePreferences.entrySet().stream().sorted(Map.Entry.<Integer,Integer>comparingByValue().reversed()).map(Map.Entry::getKey).collect(Collectors.toList());returnattractionMapper.selectRecommendByTypes(preferredTypes,collectedAttractionIds,10);}方案三(高级版):协同过滤推荐
// 基于用户的协同过滤(简化版)publicList<Attraction>collaborativeFilteringRecommend(IntegeruserId){// 1. 找到相似用户(收藏了相同景点)List<Integer>similarUserIds=collectionMapper.findSimilarUsers(userId,5);// 2. 获取相似用户收藏但当前用户未收藏的景点List<Integer>collectedAttractionIds=collectionMapper.selectCollectedAttractions(userId);// 3. 计算推荐分数(基于相似用户的收藏数)Map<Integer,Integer>attractionScores=newHashMap<>();for(IntegersimilarUserId:similarUserIds){List<Integer>similarUserCollections=collectionMapper.selectCollectedAttractions(similarUserId);for(IntegerattractionId:similarUserCollections){if(!collectedAttractionIds.contains(attractionId)){attractionScores.merge(attractionId,1,Integer::sum);}}}// 4. 按分数排序返回returnattractionScores.entrySet().stream().sorted(Map.Entry.<Integer,Integer>comparingByValue().reversed()).limit(10).map(entry->attractionMapper.selectById(entry.getKey())).collect(Collectors.toList());}四、功能实现:核心模块详解
1. 个性化推荐模块(必做!亮点)
推荐策略组合:
- 新用户:热门推荐 + 地域推荐
- 老用户:协同过滤 + 基于内容推荐
- 实时推荐:基于当前浏览记录
实现代码示例:
@ServicepublicclassRecommendationService{@AutowiredprivateAttractionMapperattractionMapper;@AutowiredprivateCollectionMappercollectionMapper;@AutowiredprivateUserBehaviorMapperuserBehaviorMapper;// 综合推荐入口publicList<Attraction>getRecommendations(IntegeruserId,Stringcity){List<Attraction>recommendations=newArrayList<>();// 检查用户是否有行为记录booleanhasBehavior=userBehaviorMapper.hasUserBehavior(userId);if(!hasBehavior){// 新用户推荐:热门 + 同城recommendations.addAll(getPopularRecommendations(city,5));recommendations.addAll(getLocalRecommendations(city,5));}else{// 老用户推荐:协同过滤 + 基于内容recommendations.addAll(getCollaborativeFilteringRecommendations(userId,6));recommendations.addAll(getContentBasedRecommendations(userId,4));}// 去重并返回returnrecommendations.stream().distinct().limit(10).collect(Collectors.toList());}// 热门推荐privateList<Attraction>getPopularRecommendations(Stringcity,intlimit){returnattractionMapper.selectPopularByCity(city,limit);}// 同城推荐privateList<Attraction>getLocalRecommendations(Stringcity,intlimit){returnattractionMapper.selectByCity(city,limit);}// 更多推荐方法...}2. 旅游路线规划模块(核心功能)
路线生成逻辑:
- 用户选择兴趣点(自然风光、美食、购物等)
- 系统推荐匹配的景点
- 根据地理位置和开放时间智能排序
- 生成合理的行程安排和预算估算
路线详情数据结构:
{"id":1,"name":"北京经典三日游","days":3,"budget":1500.00,"dayPlan":[{"day":1,"title":"皇家园林之旅","attractions":[{"id":1,"name":"故宫","duration":4,"time":"09:00-13:00"},{"id":2,"name":"景山公园","duration":2,"time":"14:00-16:00"}],"transport":"地铁1号线","tips":"故宫需提前预约"}]}3. 景点详情展示模块
功能要点:
- 图片轮播展示
- 基本信息(门票、开放时间、地址)
- 地图位置展示(集成百度/高德地图)
- 用户评价和评分
- 相关推荐(相似景点、组合路线)
前端实现:
<template> <div class="attraction-detail"> <!-- 图片轮播 --> <el-carousel :interval="4000" type="card" height="400px"> <el-carousel-item v-for="(img, index) in attraction.images" :key="index"> <img :src="img" class="carousel-image" /> </el-carousel-item> </el-carousel> <!-- 基本信息 --> <div class="basic-info"> <h1>{{ attraction.name }}</h1> <div class="tags"> <el-tag type="success">{{ attraction.typeName }}</el-tag> <el-tag v-if="attraction.ticketPrice > 0">门票¥{{ attraction.ticketPrice }}</el-tag> <el-tag v-else type="info">免费</el-tag> </div> <!-- 收藏按钮 --> <el-button :type="isCollected ? 'danger' : 'default'" @click="toggleCollection" icon="el-icon-star"> {{ isCollected ? '已收藏' : '收藏' }} </el-button> </div> <!-- 地图位置 --> <div class="map-section"> <h3>位置信息</h3> <div id="map-container" style="height: 300px;"></div> </div> <!-- 相关推荐 --> <div class="recommendations"> <h3>猜你喜欢</h3> <attraction-list :attractions="relatedAttractions" /> </div> </div> </template>4. 用户行为分析模块
行为收集:
// 用户行为埋点@PostMapping("/trackBehavior")publicResulttrackBehavior(@RequestBodyUserBehaviorDTOdto){UserBehaviorbehavior=newUserBehavior();behavior.setUserId(dto.getUserId());behavior.setItemId(dto.getItemId());behavior.setItemType(dto.getItemType());behavior.setBehaviorType(dto.getBehaviorType());behavior.setBehaviorValue(dto.getValue());behavior.setDuration(dto.getDuration());behavior.setBehaviorTime(newDate());userBehaviorMapper.insert(behavior);// 实时更新景点热度if(dto.getItemType()==1){// 景点attractionMapper.incrementPopularity(dto.getItemId());}returnResult.success("行为记录成功");}五、前端页面设计要点
1. 首页设计
- 搜索框:支持景点/路线搜索,支持城市筛选
- 轮播图:推荐热门景点和路线
- 推荐区域:个性化推荐卡片展示
- 分类导航:按景点类型快速筛选
2. 景点列表页
- 筛选器:按类型、门票价格、评分、距离筛选
- 排序:默认按热度,支持按评分、价格排序
- 展示模式:列表模式/网格模式切换
- 分页加载:滚动加载更多
3. 路线推荐页
- 路线筛选:按天数、预算、适合人群筛选
- 行程预览:展示路线概览(景点数量、总预算)
- 一键收藏:收藏感兴趣的路线
- 行程详情:点击查看详细行程安排
4. 个人中心页
- 我的收藏:景点收藏、路线收藏分开展示
- 浏览历史:最近浏览记录,可清除
- 推荐偏好:展示系统分析的用户偏好标签
- 个人信息:头像、昵称、旅游偏好设置
六、测试:这些场景必须测!
1. 功能测试用例
表1:推荐功能测试
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 新用户访问 | 新注册用户访问首页 | 看到热门景点和本地推荐 |
| 收藏后推荐 | 用户收藏几个自然景点 | 推荐更多自然类景点 |
| 浏览行为影响 | 用户浏览多个历史遗迹 | 推荐相关历史景点 |
表2:路线规划测试
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 生成路线 | 选择兴趣点生成路线 | 路线包含相关景点,时间安排合理 |
| 路线收藏 | 收藏一条路线 | 在个人中心能看到收藏 |
| 路线分享 | 点击分享按钮 | 生成分享链接或二维码 |
2. 性能测试
- 推荐响应时间:推荐算法应在500ms内返回结果
- 并发访问:模拟100个用户同时访问景点详情页
- 大数据量:测试10000个景点数据时的查询性能
3. 推荐效果评估(加分项)
- 准确率:推荐内容与用户实际兴趣的匹配度
- 覆盖率:推荐系统能覆盖多少景点/路线
- 新颖性:推荐的景点/路线是否多样
七、部署与答辩准备
1. 项目打包部署
# 后端打包mvn clean package -DskipTests# 前端打包npmrun build# 部署# 1. 上传jar包和dist文件夹# 2. 导入SQL脚本(含测试数据)# 3. 启动后端服务# 4. 配置Nginx反向代理2. 答辩准备(3个加分技巧)
演示流程要生动:
- 用户注册→设置偏好→查看个性化推荐
- 浏览景点→收藏→查看推荐变化
- 规划路线→查看行程安排
重点讲技术亮点:
- 推荐算法的设计与实现(展示算法逻辑图)
- 用户行为数据的收集与分析
- 路线规划的智能算法
准备常见问题:
- Q:推荐算法怎么实现的?
A:采用混合推荐策略,新用户用热门推荐,老用户用协同过滤+基于内容推荐 - Q:如何保证推荐准确性?
A:①收集用户行为数据 ②定期评估推荐效果 ③支持用户反馈 - Q:系统如何扩展?
A:①推荐算法可替换 ②支持插件化推荐策略 ③数据存储可分库分表
- Q:推荐算法怎么实现的?
八、毕设文档结构
旅游推荐系统/ ├── 毕业论文.docx # 完整论文(含算法章节) ├── 开题报告.docx # 研究背景、创新点 ├── 中期检查表.docx # 进度汇报 ├── 源码/ │ ├── backend/ # SpringBoot后端 │ ├── frontend/ # Vue前端 │ ├── algorithm/ # 推荐算法实现 │ └── database/ # 数据库脚本 ├── 演示视频.mp4 # 10分钟功能演示(重点展示推荐) ├── 答辩PPT.pptx # 15分钟答辩展示 └── 算法说明文档.md # 推荐算法详细说明最后:真心话时间
旅游推荐系统是技术含量较高的毕设选题,能很好展示你的算法能力和工程能力。关键是推荐算法要设计合理,用户交互要友好,数据可视化要做好。
需要完整源码(含推荐算法实现)、数据库脚本(含旅游测试数据)、答辩PPT模板的同学,评论区扣"旅游推荐",我发你。遇到具体技术问题(比如推荐算法、地图集成),也可以留言讨论!
这篇干货整理了我所有经验教训,点赞收藏,毕设路上不迷路!祝大家顺利毕业!🏞️✨