1. 项目概述与技术栈选型
这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0的旅游网站系统,是一个典型的前后端分离架构的Java Web应用。我在实际开发中发现,这种技术组合在当前企业级应用中非常流行,特别是在需要快速迭代的中小型项目中。
技术栈选型的核心考量点:
- SpringBoot2作为后端框架,提供了开箱即用的特性,大幅减少了传统Spring应用的配置复杂度。我选择2.x版本而非最新的3.x,主要是考虑到企业环境的稳定性要求,以及大量现有项目仍在使用Java8的情况。
- Vue3作为前端框架,其Composition API比Options API更适合大型应用的状态管理。特别是在旅游网站这种需要复杂交互的场景下,Vue3的性能优势和更好的TypeScript支持显得尤为重要。
- MyBatis-Plus作为ORM工具,在传统MyBatis基础上提供了更多便捷功能,比如自动分页、Lambda表达式查询等,可以显著减少样板代码量。
- MySQL8.0相比5.7版本,在JSON支持、窗口函数、性能等方面都有显著提升,特别适合旅游网站这种需要处理复杂业务数据和报表的场景。
提示:实际开发中,SpringBoot2.7.x与MyBatis-Plus3.5.x版本组合最为稳定,这也是当前企业项目中使用最广泛的版本组合。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计与模块划分
2.1 前后端分离架构
这个系统采用了典型的前后端分离架构:
code复制前端(Vue3) <-- HTTP/HTTPS --> 后端(SpringBoot2)
↑
↓
MySQL8.0
前端使用Vue3+Element Plus构建管理后台,通过axios与后端进行RESTful API交互。后端采用三层架构:
- Controller层:处理HTTP请求,参数校验
- Service层:业务逻辑实现
- DAO层:通过MyBatis-Plus与数据库交互
2.2 核心功能模块
根据旅游网站的典型需求,系统主要包含以下模块:
| 模块名称 | 主要功能 | 技术实现要点 |
|---|---|---|
| 用户管理 | 注册/登录/权限控制 | JWT+Spring Security |
| 景点管理 | CRUD/搜索/分类 | MyBatis-Plus动态查询 |
| 订单管理 | 创建/支付/退款 | 分布式事务控制 |
| 评论系统 | 发布/审核/显示 | Vue3组件化开发 |
| 数据统计 | 访问量/销售额分析 | ECharts集成 |
3. 开发环境搭建与配置
3.1 后端环境准备
-
JDK选择:
推荐使用JDK11(LTS版本),与SpringBoot2.x兼容性最好。避免使用最新JDK,可能遇到兼容性问题。 -
Maven配置:
在pom.xml中需要特别注意这些依赖:xml复制<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> <scope>runtime</scope> </dependency> -
MySQL8.0配置:
yaml复制spring: datasource: url: jdbc:mysql://localhost:3306/travel_db?useSSL=false&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver
注意:MySQL8.0默认使用caching_sha2_password认证,如果遇到认证问题,可以改为mysql_native_password:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
3.2 前端环境配置
-
Vue3项目初始化:
bash复制npm init vue@latest travel-web cd travel-web npm install -
Element Plus安装:
bash复制
npm install element-plus @element-plus/icons-vue -
axios配置:
在src/utils/request.js中配置基础请求:javascript复制import axios from 'axios' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 }) // 请求拦截器 service.interceptors.request.use(config => { // 添加token等逻辑 return config }) export default service
4. 核心功能实现细节
4.1 景点搜索功能实现
旅游网站的核心功能之一是景点搜索,我们实现了:
- 关键字搜索
- 地理位置筛选
- 价格区间过滤
后端实现:
java复制@GetMapping("/attractions")
public PageResult<AttractionVO> searchAttractions(
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String location,
@RequestParam(required = false) Double minPrice,
@RequestParam(required = false) Double maxPrice,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
LambdaQueryWrapper<Attraction> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(keyword)) {
wrapper.like(Attraction::getName, keyword)
.or().like(Attraction::getDescription, keyword);
}
// 其他条件过滤...
Page<Attraction> pageInfo = new Page<>(page, size);
IPage<Attraction> iPage = attractionMapper.selectPage(pageInfo, wrapper);
return PageResult.success(iPage);
}
前端实现:
vue复制<template>
<el-form :model="searchForm" @submit.prevent="handleSearch">
<el-input v-model="searchForm.keyword" placeholder="输入景点名称"/>
<el-select v-model="searchForm.location" placeholder="选择地区">
<!-- 选项... -->
</el-select>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form>
<el-table :data="attractionList">
<!-- 表格列... -->
</el-table>
<el-pagination
@current-change="handlePageChange"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"/>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { searchAttractions } from '@/api/attraction'
const searchForm = reactive({
keyword: '',
location: '',
minPrice: null,
maxPrice: null
})
const attractionList = ref([])
const pagination = reactive({
current: 1,
size: 10,
total: 0
})
const handleSearch = async () => {
const res = await searchAttractions({
...searchForm,
page: pagination.current,
size: pagination.size
})
attractionList.value = res.data.records
pagination.total = res.data.total
}
</script>
4.2 订单支付流程实现
旅游网站的订单系统需要考虑:
- 库存检查
- 优惠券应用
- 支付接口集成
- 超时取消
状态机设计:
java复制public enum OrderStatus {
PENDING_PAYMENT, // 待支付
PAID, // 已支付
CANCELLED, // 已取消
COMPLETED // 已完成
}
支付核心逻辑:
java复制@Transactional
public String createOrder(OrderCreateDTO dto) {
// 1. 检查库存
Attraction attraction = attractionMapper.selectById(dto.getAttractionId());
if (attraction.getStock() < dto.getQuantity()) {
throw new BusinessException("库存不足");
}
// 2. 创建订单
Order order = new Order();
BeanUtils.copyProperties(dto, order);
order.setOrderNo(generateOrderNo());
order.setStatus(OrderStatus.PENDING_PAYMENT);
orderMapper.insert(order);
// 3. 减少库存
attraction.setStock(attraction.getStock() - dto.getQuantity());
attractionMapper.updateById(attraction);
// 4. 设置定时任务检查支付超时
delayQueue.add(new DelayTask(order.getOrderNo(), 30, TimeUnit.MINUTES));
return order.getOrderNo();
}
5. 性能优化与安全实践
5.1 数据库优化
-
索引设计:
sql复制ALTER TABLE `attraction` ADD INDEX `idx_location_price` (`location`, `price`); ALTER TABLE `order` ADD INDEX `idx_user_status` (`user_id`, `status`); -
查询优化:
- 避免SELECT *
- 使用MyBatis-Plus的@TableField(select = false)标记大字段
- 复杂查询使用@SqlParser(filter = true)避免MyBatis-Plus自动解析
5.2 缓存策略
-
Redis缓存热门景点:
java复制@Cacheable(value = "attraction", key = "#id") public AttractionVO getAttractionById(Long id) { return attractionMapper.selectById(id); } -
前端缓存策略:
javascript复制// 使用vueuse的useStorage实现本地缓存 import { useStorage } from '@vueuse/core' const cachedAttractions = useStorage('attraction-cache', [])
5.3 安全防护
-
XSS防护:
java复制@Bean public FilterRegistrationBean<XssFilter> xssFilter() { FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new XssFilter()); registration.addUrlPatterns("/*"); return registration; } -
CSRF防护:
javascript复制// axios配置中添加CSRF token service.interceptors.request.use(config => { config.headers['X-CSRF-TOKEN'] = getCookie('csrfToken') return config })
6. 部署与监控
6.1 生产环境部署
-
后端打包:
bash复制
mvn clean package -DskipTests -
前端构建:
bash复制
npm run build -
Docker部署示例:
dockerfile复制# SpringBoot应用Dockerfile FROM openjdk:11-jre COPY target/travel-system.jar /app.jar ENTRYPOINT ["java","-jar","/app.jar"]
6.2 监控配置
-
SpringBoot Actuator:
yaml复制management: endpoints: web: exposure: include: health,info,metrics -
前端性能监控:
javascript复制// 使用web-vitals监控前端性能 import {getCLS, getFID, getLCP} from 'web-vitals'; getCLS(console.log); getFID(console.log); getLCP(console.log);
7. 常见问题与解决方案
在实际开发中,我遇到了以下典型问题及解决方法:
-
MyBatis-Plus分页失效:
- 原因:没有配置分页插件
- 解决:
java复制@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }
-
Vue3响应式丢失:
- 现象:解构props后响应性丢失
- 解决:使用toRefs保持响应性
javascript复制const props = defineProps(['attraction']) const { attraction } = toRefs(props)
-
MySQL8时区问题:
- 错误:Server returns invalid timezone
- 解决:JDBC URL添加serverTimezone参数
code复制jdbc:mysql://localhost:3306/db?serverTimezone=Asia/Shanghai
-
跨域问题:
- 现象:前端请求被浏览器拦截
- 解决:SpringBoot配置CORS
java复制@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*"); } }; }
8. 项目文档与扩展建议
8.1 项目文档结构
完整的项目文档应包含:
README.md:项目概述、环境要求API文档:Swagger或Postman集合数据库设计:ER图与表结构说明部署指南:生产环境部署步骤开发规范:代码风格与Git流程
8.2 扩展功能建议
-
推荐系统:
- 基于用户行为的协同过滤
- 使用Redis实现实时推荐
-
国际化支持:
- 后端使用MessageSource
- 前端使用vue-i18n
-
微信小程序端:
- 复用现有API
- 开发uni-app版本
-
大数据分析:
- 用户行为分析
- 景点热度预测
这个旅游网站系统虽然基础功能完整,但在实际商业环境中还需要根据具体业务需求进行更多定制开发。我在实现过程中发现,良好的模块划分和清晰的接口设计对后期扩展至关重要。特别是在订单和支付模块,建议从一开始就考虑分布式事务的处理方案,为未来的微服务化做好准备。
