SpringBoot+Vue实现旅游推荐系统开发指南
1. 项目概述基于SpringBootVue的家乡特色旅游宣传推荐系统是一个典型的毕业设计级别全栈项目采用前后端分离架构实现。系统核心目标是利用信息化手段推广地方旅游资源通过个性化推荐算法展示家乡特色景点、美食和文化活动。这个选题巧妙结合了技术实践与社会价值既满足了计算机专业毕业设计的复杂度要求又能为地方旅游经济带来实际效益。我在指导类似项目时发现学生常面临三个主要痛点技术栈整合困难、旅游数据获取不易、推荐算法实现门槛高。本系统通过成熟的SpringBootVue技术组合配合公开数据集和简化版推荐逻辑有效降低了实现难度。下面我将从技术选型到部署上线的完整生命周期拆解这个项目的关键实现细节。2. 技术架构设计2.1 前后端技术选型后端技术栈SpringBoot 2.7.x提供RESTful API和后台管理功能MyBatis-Plus 3.5.x简化数据库操作Redis 6.x缓存热门景点数据MySQL 8.0存储结构化数据Swagger 3.0API文档生成选择SpringBoot而非传统SSM框架主要考虑其自动配置特性可以快速搭建项目骨架。实测在IDEA中使用Spring Initializr创建项目仅需5分钟即可完成基础环境搭建。MyBatis-Plus的Lambda查询方式比原生MyBatis更符合现代Java开发习惯。前端技术栈Vue 3.x组合式API开发模式Element PlusUI组件库AxiosHTTP请求库ECharts 5.x数据可视化Vue Router 4.x前端路由管理Vue 3的Composition API相比Options API更适合复杂交互场景。我曾对比过使用Vue 2和Vue 3实现相同景点的轮播组件Vue 3版本的代码量减少约30%逻辑复用性明显提升。2.2 系统模块划分com.tourism ├── config # 配置类 ├── controller # 控制器层 ├── service # 业务逻辑层 ├── mapper # 数据访问层 ├── entity # 实体类 ├── util # 工具类 └── recommend # 推荐算法模块前端采用典型Vue CLI项目结构src/ ├── api/ # 接口定义 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 ├── store/ # Vuex状态管理 ├── utils/ # 工具函数 └── views/ # 页面组件3. 核心功能实现3.1 旅游数据管理采用MySQL存储结构化数据主要表设计如下CREATE TABLE scenic_spot ( id int NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 景点名称, location point NOT NULL COMMENT 地理位置, description text COMMENT 景点描述, cover_img varchar(255) COMMENT 封面图URL, tags json DEFAULT NULL COMMENT 标签数组, open_time varchar(50) COMMENT 开放时间, ticket_price decimal(10,2) COMMENT 门票价格, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), SPATIAL INDEX idx_location (location) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意地理位置字段使用MySQL的POINT类型配合SPATIAL索引可实现周边景点查询。实际部署时需要确保MySQL版本≥5.7且启用GIS功能。3.2 个性化推荐算法实现基于内容的推荐算法核心逻辑public ListScenicSpot recommendSpots(User user) { // 1. 获取用户历史行为标签 SetString userTags getUserPreferenceTags(user.getId()); // 2. 查询所有景点并计算相似度 ListScenicSpot allSpots spotMapper.selectList(null); return allSpots.stream() .map(spot - { double score calculateSimilarity(userTags, spot.getTags()); return new RecommendItem(spot, score); }) .sorted(Comparator.comparingDouble(RecommendItem::getScore).reversed()) .limit(10) .map(RecommendItem::getSpot) .collect(Collectors.toList()); } private double calculateSimilarity(SetString userTags, JsonNode spotTags) { // Jaccard相似度计算 SetString spotTagSet new HashSet(); spotTags.forEach(node - spotTagSet.add(node.asText())); SetString intersection new HashSet(userTags); intersection.retainAll(spotTagSet); SetString union new HashSet(userTags); union.addAll(spotTagSet); return union.isEmpty() ? 0 : (double)intersection.size() / union.size(); }3.3 前端交互实现景点详情页的关键Vue组件template div classdetail-container el-carousel :interval5000 arrowalways el-carousel-item v-for(img, index) in spot.images :keyindex el-image :srcimg fitcover stylewidth:100%;height:400px/ /el-carousel-item /el-carousel div classinfo-section h1{{ spot.name }}/h1 div classmeta spani classel-icon-location/i {{ formatLocation(spot.location) }}/span spani classel-icon-time/i {{ spot.openTime }}/span el-tag v-for(tag, idx) in spot.tags :keyidx typeinfo stylemargin-right:8px {{ tag }} /el-tag /div div classaction-bar el-button typeprimary iconel-icon-star-off clickhandleCollect 收藏 /el-button el-button iconel-icon-share clickhandleShare 分享 /el-button /div /div /div /template script setup import { ref, onMounted } from vue import { useRoute } from vue-router import { getSpotDetail } from /api/tourism const route useRoute() const spot ref({ name: , images: [], tags: [], location: null, openTime: }) onMounted(async () { const { id } route.params const { data } await getSpotDetail(id) spot.value data }) const formatLocation (point) { // 实现坐标转地址逻辑 } /script4. 系统部署方案4.1 开发环境搭建后端环境JDK 1.8Maven 3.6MySQL 8.0Redis 6.x# 克隆项目 git clone https://github.com/example/tourism-system.git # 导入IDEA后执行 mvn clean install # 配置application.yml中的数据库连接信息 spring: datasource: url: jdbc:mysql://localhost:3306/tourism?useSSLfalse username: root password: 123456前端环境Node.js 16.xnpm 8.xcd tourism-web # 安装依赖 npm install # 启动开发服务器 npm run dev4.2 生产环境部署采用Docker容器化部署方案# backend/Dockerfile FROM openjdk:8-jdk-alpine VOLUME /tmp COPY target/tourism-system-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT [java,-jar,/app.jar]# frontend/Dockerfile FROM nginx:alpine COPY dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80使用docker-compose编排version: 3 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: tourism ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql redis: image: redis:6-alpine ports: - 6379:6379 backend: build: ./backend ports: - 8080:8080 depends_on: - mysql - redis frontend: build: ./frontend ports: - 80:80 volumes: mysql_data:5. 常见问题与优化建议5.1 开发阶段问题排查跨域问题解决方案Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .maxAge(3600); } }Vue路由刷新404问题location / { try_files $uri $uri/ /index.html; }5.2 性能优化方案接口缓存Cacheable(value spots, key #id) GetMapping(/spots/{id}) public Result getSpotDetail(PathVariable Integer id) { return Result.success(spotService.getById(id)); }图片懒加载el-image :srcimgUrl lazy :preview-src-listpreviewList /el-image数据库查询优化// 使用MyBatis-Plus的QueryWrapper优化分页查询 PageScenicSpot page new Page(current, size); QueryWrapperScenicSpot wrapper new QueryWrapperScenicSpot() .select(id,name,cover_img,description) .orderByDesc(create_time); return spotMapper.selectPage(page, wrapper);5.3 扩展功能建议微信小程序端使用uni-app框架复用现有Vue组件集成微信支付功能实现门票购买数据分析看板使用ECharts实现访问量热力图用户行为分析报表增强推荐算法引入协同过滤算法结合实时位置信息推荐周边景点这个项目我在实际教学中发现学生最容易在三个环节出错Vuex状态管理配置、SpringBoot文件上传处理、MySQL空间查询实现。建议在开发时优先完成这些核心模块的单元测试可以节省大量调试时间。