1. 项目背景与意义
喀纳斯景区位于新疆阿勒泰地区,以其壮丽的自然风光和独特的图瓦人文化闻名于世。然而,传统旅游信息获取渠道分散,游客往往需要辗转多个平台才能完成景点查询、路线规划、住宿预订等操作,体验割裂且效率低下。与此同时,景区管理者缺乏统一的数据入口,难以对游客流量、热门景点、消费偏好进行有效分析,运营决策多依赖经验判断。
本课题旨在设计并实现一个基于SpringBoot+Vue的喀纳斯旅游网站,将景区介绍、线路推荐、酒店预订、票务信息、用户评价等功能整合于一体。从游客角度看,平台提供了从“种草”到“出行”的一站式服务,显著降低信息搜集成本;从管理者角度看,系统沉淀的订单与行为数据可为精准营销和资源调度提供依据。因此,本项目兼具实际应用价值与工程实践意义,是Web全栈开发能力的综合体现。
2. 技术栈选型
系统采用前后端分离架构,前端与后端通过RESTful API进行数据交互,职责清晰、便于独立部署与扩展。
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 前端框架 | Vue 3 + Element Plus | 组件化开发,UI美观,生态成熟 |
| 前端构建 | Vite | 开发热更新快,生产构建高效 |
| 状态管理 | Pinia | 轻量级Vue状态管理方案 |
| 路由 | Vue Router | 前端页面路由与导航守卫 |
| 后端框架 | Spring Boot 2.7 | 简化配置,快速构建微服务 |
| 持久层 | MyBatis-Plus | 通用Mapper,减少SQL编写 |
| 数据库 | MySQL 8.0 | 存储用户、景点、订单等业务数据 |
| 权限认证 | Spring Security + JWT | 无状态认证,支持前后端分离 |
| 接口文档 | Swagger / Knife4j | 在线调试与接口管理 |
选择SpringBoot作为后端核心,得益于其“约定优于配置”的设计理念,能够快速整合MyBatis、Security等组件;Vue的响应式数据绑定与组件复用机制则让前端开发更加高效,二者结合是当前企业级Web项目的主流组合。
3. 系统功能模块设计
根据业务需求,系统划分为前台展示与后台管理两大端,具体功能模块如下:
- 用户模块:注册、登录、个人信息维护、密码修改。
- 景点模块:景点列表展示、详情查看、按分类或关键词搜索。
- 线路模块:旅游线路推荐、行程安排查看、线路收藏。
- 酒店模块:酒店信息展示、房型查询、在线预订。
- 订单模块:订单创建、支付模拟、订单查询与取消。
- 评论模块:用户对景点或线路发表评价、回复。
- 后台管理:管理员对景点、线路、酒店、订单、用户进行增删改查与数据统计。
前台面向普通游客,强调浏览体验与操作便捷;后台面向运营人员,注重数据管理与权限控制。两端共用同一套后端服务,通过角色权限进行功能隔离。
4. 数据库设计
数据库共设计六张核心业务表,表结构如下:
| 表名 | 主要字段 | 说明 |
|---|---|---|
| user | id, username, password, nickname, avatar, phone, role | 用户表,role区分管理员与普通用户 |
| scenic | id, name, category, description, image, address, price | 景点信息表 |
| route | id, title, days, itinerary, cover, price | 旅游线路表 |
| hotel | id, name, address, star, room_type, price, image | 酒店及房型表 |
| orders | id, order_no, user_id, scenic_id, hotel_id, total_price, status, create_time | 订单主表 |
| comment | id, user_id, target_type, target_id, content, create_time | 评论表,支持多态关联 |
订单表通过user_id关联用户,通过scenic_id和hotel_id分别关联景点与酒店,形成一对多的业务关系。评论表采用target_type字段区分评论对象,提高表复用性。
5. 核心代码实现
本节选取后端登录认证、景点分页查询以及前端景点列表展示三个核心环节进行代码说明。
5.1 后端登录接口与JWT签发
用户登录成功后,后端生成JWT令牌返回给前端,前端在后续请求中携带该令牌以通过Spring Security过滤器链的认证。
@RestController @RequestMapping("/api/auth") public class AuthController { @Resource private UserService userService; @Resource private JwtUtil jwtUtil; @PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { // 1. 校验用户名密码 User user = userService.login(dto.getUsername(), dto.getPassword()); if (user == null) { return Result.error("用户名或密码错误"); } // 2. 生成JWT令牌 String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole()); // 3. 返回令牌与用户基本信息 Map<String, Object> data = new HashMap<>(); data.put("token", token); data.put("user", user); return Result.success(data); } }JwtUtil工具类负责令牌的生成与解析,内部使用HMAC-SHA256算法对用户身份信息进行签名,并设置合理的过期时间,防止令牌长期有效带来的安全风险。
5.2 景点分页查询接口
景点列表采用MyBatis-Plus的分页插件实现,前端传入当前页码与每页条数,后端返回分页结果对象。
@RestController @RequestMapping("/api/scenic") public class ScenicController { @Resource private ScenicService scenicService; @GetMapping("/page") public Result page(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(required = false) String keyword) { Page<Scenic> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<Scenic> wrapper = new LambdaQueryWrapper<>(); // 按名称模糊查询 if (StringUtils.hasText(keyword)) { wrapper.like(Scenic::getName, keyword); } wrapper.orderByDesc(Scenic::getId); Page<Scenic> result = scenicService.page(page, wrapper); return Result.success(result); } }LambdaQueryWrapper以类型安全的方式构造查询条件,避免了字符串拼接SQL带来的注入风险,同时代码可读性更强。
5.3 前端景点列表展示
前端通过Axios调用后端分页接口,将返回的景点数据渲染为卡片列表,并支持关键词搜索与分页切换。
<template> <div class="scenic-list"> <el-input v-model="keyword" placeholder="搜索景点" @keyup.enter="loadData" /> <el-row :gutter="20"> <el-col :span="8" v-for="item in list" :key="item.id"> <el-card class="scenic-card"> <img :src="item.image" class="scenic-img" /> <h3>{{ item.name }}</h3> <p>{{ item.description }}</p> <el-tag>¥{{ item.price }}</el-tag> </el-card> </el-col> </el-row> <el-pagination :current-page="pageNum" :page-size="pageSize" :total="total" @current-change="handlePageChange" /> </div> </template> <script setup> import { ref, onMounted } from "vue"; import request from "@/utils/request"; const list = ref([]); const keyword = ref(""); const pageNum = ref(1); const pageSize = ref(9); const total = ref(0); const loadData = async () => { const res = await request.get("/api/scenic/page", { params: { pageNum: pageNum.value, pageSize: pageSize.value, keyword: keyword.value } }); list.value = res.data.records; total.value = res.data.total; }; const handlePageChange = (page) => { pageNum.value = page; loadData(); }; onMounted(loadData); </script>上述代码展示了Vue 3组合式API的典型用法,通过ref定义响应式数据,通过onMounted在组件挂载后自动加载首屏数据,分页组件与后端分页参数保持同步。
6. 系统测试与总结
系统开发完成后,分别进行了功能测试与性能测试。功能测试覆盖用户注册登录、景点搜索、线路查看、酒店预订、订单管理等核心流程,测试用例全部通过;性能测试模拟200个并发用户访问景点列表接口,平均响应时间约180ms,满足日常使用需求。
本项目完整实践了SpringBoot+Vue前后端分离开发的全流程,涵盖需求分析、数据库设计、接口开发、前端页面实现与部署上线等环节。通过本课题,不仅加深了对Spring Security、MyBatis-Plus、Vue Router等主流框架的理解,也积累了从零搭建一个可用Web系统的工程经验。后续可进一步引入Redis缓存热点景点数据、接入真实支付网关、增加基于用户行为的智能推荐,持续提升系统的性能与用户体验。