1. 项目背景与核心需求
在数字化出行需求日益增长的今天,一个能够整合交通规划与票务预订的智能系统显得尤为重要。我最近完成了一个基于Vue+SpringBoot的交通旅游计划飞机订票系统,这个项目不仅实现了基础的机票预订功能,更重要的是将旅游行程规划与交通票务无缝衔接,为用户提供了一站式的出行解决方案。
这个系统主要解决了三个核心痛点:
- 传统订票平台功能单一,无法满足用户"行程规划+票务预订"的复合需求
- 旅游计划与交通票务数据割裂,导致用户需要反复切换不同平台
- 动态价格变动时缺乏智能提醒和替代方案推荐
从技术架构来看,系统采用了经典的前后端分离模式:
- 前端:Vue 2.6 + Element UI + Vue Router + Vuex
- 后端:Spring Boot 2.3 + MyBatis Plus + Redis + RabbitMQ
- 数据库:MySQL 8.0主从集群
- 基础设施:Docker容器化部署
提示:选择Vue 2.x而非3.x版本是考虑到企业级项目中第三方组件库的兼容性更成熟,Element UI对2.x的支持更为全面。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 前端架构设计与关键技术实现
2.1 Vue项目工程化配置
项目采用Vue CLI 4.5作为脚手架工具,通过vue.config.js实现了多项定制化配置:
javascript复制module.exports = {
devServer: {
proxy: {
'/api': {
target: process.env.VUE_APP_BASE_API,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].title = '交通旅游计划系统'
return args
})
}
}
关键配置项说明:
- 开发环境代理解决跨域问题
- 动态页面标题配置
- 多环境变量管理(.env.development/.env.production)
2.2 核心功能模块实现
2.2.1 行程规划模块
采用高德地图JS API实现可视化行程规划:
javascript复制// 初始化地图
this.map = new AMap.Map('map-container', {
zoom: 10,
center: [116.397428, 39.90923]
})
// 添加路线规划插件
AMap.plugin('AMap.Driving', () => {
this.driving = new AMap.Driving({
map: this.map,
panel: 'route-panel'
})
})
实现要点:
- 通过地理编码服务将用户输入转换为坐标
- 使用路径规划API计算多方案路线
- 结合时间成本算法推荐最优路线
2.2.2 机票预订模块
机票查询组件采用了虚拟滚动技术优化大数据量渲染:
vue复制<template>
<el-table
:data="flightData"
style="width: 100%"
height="500"
row-key="id"
@row-click="handleSelect">
<!-- 列定义 -->
</el-table>
</template>
<script>
export default {
data() {
return {
flightData: [] // 分页加载的数据
}
},
methods: {
loadMore() {
// 滚动到底部时触发分页加载
}
}
}
</script>
性能优化技巧:
- 使用Web Worker处理大数据排序
- 实现机票价格波动监控(WebSocket推送)
- 本地缓存用户常用搜索条件
3. 后端服务架构设计
3.1 SpringBoot应用分层
采用经典的三层架构设计:
code复制com.travel.system
├── config # 配置类
├── controller # 表现层
├── service # 业务逻辑层
│ ├── impl # 实现类
├── dao # 数据访问层
├── entity # 实体类
├── dto # 数据传输对象
├── util # 工具类
└── exception # 异常处理
3.2 机票业务核心实现
3.2.1 机票查询服务
java复制@Service
public class FlightServiceImpl implements FlightService {
@Autowired
private FlightMapper flightMapper;
@Override
public PageInfo<FlightVO> queryFlights(FlightQueryDTO queryDTO) {
PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize());
List<Flight> flights = flightMapper.selectByQuery(queryDTO);
return new PageInfo<>(convertToVO(flights));
}
// 价格波动监控
@Scheduled(fixedRate = 300000)
public void monitorPriceChanges() {
// 定时任务实现
}
}
关键技术点:
- MyBatis动态SQL构建复杂查询条件
- PageHelper实现物理分页
- 定时任务监控价格变化
3.2.2 分布式锁解决超卖问题
java复制public boolean bookTicket(Long flightId, Integer userId) {
String lockKey = "flight:lock:" + flightId;
String requestId = UUID.randomUUID().toString();
try {
// 获取分布式锁
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("系统繁忙,请稍后再试");
}
// 检查余票
Integer remain = flightMapper.selectRemainSeats(flightId);
if (remain <= 0) {
throw new BusinessException("票已售罄");
}
// 扣减库存
int updated = flightMapper.decreaseSeats(flightId);
if (updated == 0) {
throw new BusinessException("库存不足");
}
// 创建订单
return createOrder(flightId, userId);
} finally {
// 释放锁
if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
注意:分布式锁必须设置过期时间并确保解锁操作的原子性,避免死锁和误删问题。
4. 系统集成与部署方案
4.1 前后端联调配置
后端接口规范:
java复制@RestController
@RequestMapping("/api/flight")
public class FlightController {
@GetMapping("/search")
public Result<PageInfo<FlightVO>> searchFlights(
@Valid FlightQueryDTO queryDTO) {
// 业务逻辑
}
@PostMapping("/book")
public Result<String> bookFlight(
@RequestBody @Valid BookRequest request) {
// 预订逻辑
}
}
前端axios封装:
javascript复制const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000
})
// 请求拦截器
service.interceptors.request.use(config => {
config.headers['Authorization'] = getToken()
return config
})
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
return Promise.reject(new Error(res.message || 'Error'))
}
return res
}
)
4.2 Docker容器化部署
后端Dockerfile示例:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/travel-system.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
前端Nginx配置:
nginx复制server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
部署架构:
code复制前端Nginx ——> 后端SpringBoot ——> MySQL集群
↑
↓
Redis缓存集群
↑
↓
RabbitMQ消息队列
5. 性能优化实战经验
5.1 缓存策略设计
多级缓存架构:
- 前端本地缓存:常用航线数据
- Nginx缓存:静态资源和部分API响应
- Redis缓存:热点航班信息
- MySQL查询缓存
缓存更新策略对比:
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 定时刷新 | 实现简单 | 实时性差 | 变化不频繁的数据 |
| 主动更新 | 实时性强 | 系统复杂度高 | 核心业务数据 |
| 延迟双删 | 兼顾一致性 | 实现复杂 | 高并发场景 |
5.2 数据库优化实践
索引设计原则:
- 为所有外键字段创建索引
- 复合索引遵循最左前缀原则
- 为高频查询条件创建覆盖索引
示例:
sql复制CREATE INDEX idx_flight_route ON flight(departure_city, arrival_city);
CREATE INDEX idx_flight_time ON flight(departure_time) USING BTREE;
SQL优化技巧:
- 避免SELECT *,只查询必要字段
- 使用EXPLAIN分析执行计划
- 大数据量表采用分库分表策略
6. 安全防护方案
6.1 常见攻击防护
XSS防护配置:
java复制@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
CSRF防护策略:
- 敏感操作使用POST请求
- 启用Spring Security的CSRF保护
- 关键表单添加CSRF Token
6.2 敏感数据保护
机票价格加密存储示例:
java复制public class PriceEncryptor {
private static final String KEY = "your-encryption-key";
public static String encrypt(BigDecimal price) {
// AES加密实现
}
public static BigDecimal decrypt(String cipherText) {
// AES解密实现
}
}
日志脱敏处理:
java复制@Around("execution(* com.travel.system.controller.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
// 对参数进行脱敏处理
return joinPoint.proceed(args);
}
在实际开发中,我发现机票预订系统最关键的三个技术点是:分布式事务处理、高并发库存控制和实时价格推送。特别是在促销期间,系统需要处理比平时高10倍的并发量,这时候Redis集群和消息队列的合理配置就显得尤为重要。建议在类似项目中,提前做好压力测试,使用JMeter模拟至少5000并发用户的全流程操作,才能发现系统中的潜在瓶颈。
