SpringBoot+Vue构建榆林旅游网站管理系统的实战经验
1. 项目概述:榆林特色旅游网站管理系统
这个基于SpringBoot+Vue的旅游网站管理系统,是我去年为榆林当地文旅局开发的一个实战项目。系统采用前后端分离架构,后端用Java+SpringBoot+MyBatis实现业务逻辑,前端用Vue构建用户界面,数据库选用MySQL 8.0。整个系统从需求分析到上线部署历时3个月,目前稳定运行在榆林文旅局的服务器上,日均访问量约5000次。
提示:这类旅游管理系统最核心的挑战在于如何平衡高并发访问和数据一致性,特别是在旅游旺季时段的票务预订场景。
2. 技术栈选型解析
2.1 为什么选择SpringBoot
SpringBoot的自动配置特性大幅减少了XML配置。在这个项目中,我们主要利用了:
- 内嵌Tomcat容器(默认配置调优后支持300+并发)
- Starter依赖自动管理(spring-boot-starter-web, spring-boot-starter-data-redis等)
- Actuator端点监控(特别有用的是/health和/metrics)
// 典型Controller示例 @RestController @RequestMapping("/api/scenic") public class ScenicSpotController { @Autowired private ScenicService scenicService; @GetMapping("/{id}") public Result<ScenicDetailVO> getDetail(@PathVariable Integer id) { return Result.success(scenicService.getDetail(id)); } }2.2 Vue前端架构设计
前端采用Vue 3 + Element Plus组合,主要模块包括:
- 游客门户(景点展示、票务预订)
- 管理后台(数据统计、订单管理)
- 微信小程序对接模块
关键技术点:
- 使用Vue Router实现动态路由
- Axios封装全局请求拦截器
- Vuex管理跨组件状态(如用户登录态)
// API请求封装示例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) service.interceptors.request.use( config => { if (store.getters.token) { config.headers['X-Token'] = getToken() } return config }, error => { return Promise.reject(error) } )3. 数据库设计与优化
3.1 MySQL表结构设计
核心表包括:
scenic_spot(景点基础信息)ticket_order(订单表)user(用户账户)comment(评价表)
CREATE TABLE `scenic_spot` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '景点名称', `location` point NOT NULL COMMENT '地理坐标', `cover_img` varchar(255) DEFAULT NULL, `description` text, `open_time` varchar(100) DEFAULT '08:00-18:00', `ticket_price` decimal(10,2) DEFAULT '0.00', `status` tinyint DEFAULT '1' COMMENT '1开放 0关闭', PRIMARY KEY (`id`), SPATIAL KEY `idx_location` (`location`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;3.2 MyBatis实战技巧
我们采用MyBatis-Plus增强功能:
- 自动填充(如create_time/update_time)
- 逻辑删除配置
- 性能分析插件
<!-- 动态SQL示例 --> <select id="selectByCondition" resultType="ScenicSpot"> SELECT * FROM scenic_spot <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%',#{name},'%') </if> <if test="minPrice != null"> AND ticket_price >= #{minPrice} </if> </where> ORDER BY id DESC </select>4. 核心功能实现细节
4.1 景点展示模块
关键技术实现:
- 地理坐标处理:使用MySQL的GIS函数计算距离
- 图片懒加载:Intersection Observer API
- 缓存策略:Redis缓存热门景点数据
// 距离计算示例 @Select("SELECT id, name, ST_Distance_Sphere(location, POINT(#{lng}, #{lat})) as distance " + "FROM scenic_spot WHERE status = 1 ORDER BY distance ASC LIMIT 10") List<NearbyScenicDTO> findNearby(@Param("lng") double longitude, @Param("lat") double latitude);4.2 票务预订系统
关键设计要点:
- 分布式锁防止超卖(Redisson实现)
- 订单状态机设计
- 微信支付/支付宝支付对接
重要:票务库存扣减必须使用CAS操作:
UPDATE ticket_inventory SET stock = stock - 1 WHERE spot_id = #{spotId} AND stock > 05. 部署与性能优化
5.1 生产环境部署方案
我们采用:
- Nginx反向代理(配置gzip压缩)
- Jenkins自动化部署
- Docker容器化(MySQL单独容器)
# Nginx部分配置 server { listen 80; server_name www.yulin-travel.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://springboot:8080; proxy_set_header Host $host; } }5.2 性能调优记录
实际遇到的性能问题及解决方案:
- 景点列表页响应慢 → 添加Redis缓存,TTL设置30分钟
- 订单查询超时 → 优化SQL,添加复合索引(order_no, user_id)
- 图片加载慢 → 启用CDN加速
6. 踩坑经验实录
6.1 跨域问题解决方案
开发环境配置:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST") .allowCredentials(true) .maxAge(3600); } }生产环境推荐:
- Nginx配置CORS头
- 使用网关统一处理
6.2 微信支付回调处理
关键注意事项:
- 验证签名必须做
- 处理幂等性问题
- 记录完整交互日志
@PostMapping("/wxpay/callback") public String wxpayCallback(HttpServletRequest request) { // 1. 验证签名 // 2. 解析支付结果 // 3. 更新订单状态(加分布式锁) // 4. 返回success字符串 }7. 扩展功能实现
7.1 智能推荐算法
基于用户行为的简单推荐实现:
- 记录用户浏览历史
- 协同过滤算法计算相似度
- Redis有序集合存储推荐结果
public List<ScenicSpot> recommendSpots(Integer userId) { // 1. 获取用户最近浏览的5个景点 // 2. 查找也浏览过这些景点的其他用户 // 3. 聚合这些用户浏览过的其他景点 // 4. 按出现频率排序返回 }7.2 后台数据统计
使用ECharts实现的可视化功能:
- 实时访问量监控
- 票务销售趋势图
- 用户地域分布热力图
// ECharts配置示例 option = { tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] }, yAxis: { type: 'value' }, series: [{ data: [120, 200, 150], type: 'line' }] }8. 安全防护措施
8.1 常见攻击防护
实施的安全策略:
- XSS过滤:自定义HttpServletRequestWrapper
- CSRF防护:Spring Security默认启用
- SQL注入:MyBatis参数化查询天然防护
// XSS过滤示例 public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { @Override public String getParameter(String name) { String value = super.getParameter(name); return HtmlUtils.htmlEscape(value); } }8.2 敏感数据保护
关键措施:
- 密码加盐哈希存储(BCryptPasswordEncoder)
- 敏感字段加密(如身份证号)
- 日志脱敏处理
// 密码加密示例 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 使用 user.setPassword(passwordEncoder.encode(rawPassword));9. 项目总结与反思
这个项目让我深刻体会到旅游行业系统的特殊需求:
- 季节性流量波动大,需要弹性扩容方案
- 地理位置数据处理的复杂性
- 票务系统的强一致性要求
如果重做这个项目,我会:
- 更早引入分布式事务解决方案(如Seata)
- 增加压力测试场景覆盖
- 采用微服务架构拆分订单模块
源码中几个值得参考的实现:
- 基于Redis GEO的附近景点查询
- 微信支付完整对接流程
- Vue动态权限路由实现
- 基于AOP的操作日志记录
最后建议:旅游系统开发一定要提前与业务方确认退改规则等业务细节,这些规则会深刻影响系统设计。我们在中期就因此重构了订单状态机。