1. 项目概述
汽车票网上预订系统是一个典型的B/S架构互联网应用,旨在解决传统线下购票方式存在的排队时间长、信息不对称、服务效率低下等问题。作为一名长期从事交通行业信息化建设的开发者,我在实际项目中发现,构建一个稳定可靠的在线票务系统需要兼顾技术实现与业务场景的深度融合。
这个基于SpringBoot的系统采用了前后端分离架构,后端使用Java语言开发,前端采用Vue.js框架,数据库选用MySQL。系统核心功能模块包括:
- 用户端:车次查询、在线预订、订单管理、个人信息维护
- 管理端:车次调度、票务管理、用户管理、新闻发布
- 公共服务:支付对接、消息通知、数据统计
提示:在交通行业系统开发中,高并发场景下的数据一致性和系统响应速度是需要重点考虑的技术难点,这直接关系到用户体验和系统可靠性。
2. 技术架构设计
2.1 整体架构方案
系统采用经典的三层架构设计:
- 表现层:基于Vue.js的前端界面,负责用户交互和数据展示
- 业务逻辑层:SpringBoot构建的RESTful API服务,处理核心业务逻辑
- 数据访问层:MyBatis实现的数据持久化,连接MySQL数据库
这种分层架构的优势在于:
- 职责分离,便于团队协作开发
- 模块化程度高,可维护性强
- 易于扩展和部署
2.2 技术选型考量
SpringBoot框架选择理由:
- 自动配置特性大幅减少XML配置
- 内嵌Tomcat服务器简化部署流程
- 丰富的Starter依赖快速集成常用组件
- Actuator提供完善的系统监控能力
MySQL数据库考量因素:
- 事务支持完善,满足票务系统的ACID要求
- 社区活跃,文档丰富,运维成本低
- 性能满足中小规模票务系统的需求
- 与Java生态集成度高
3. 核心功能实现
3.1 车次查询模块
车次查询是系统的核心功能之一,其技术实现要点包括:
- 数据库设计:
sql复制CREATE TABLE `bus_schedule` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departure_station` varchar(50) NOT NULL,
`arrival_station` varchar(50) NOT NULL,
`departure_time` datetime NOT NULL,
`arrival_time` datetime NOT NULL,
`bus_type` varchar(20) NOT NULL,
`price` decimal(10,2) NOT NULL,
`remaining_seats` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_station_time` (`departure_station`,`arrival_station`,`departure_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 查询接口优化:
- 使用Redis缓存热门线路查询结果
- 分页查询避免大数据量传输
- 建立复合索引提升查询效率
3.2 订单处理流程
订单处理的状态机设计:
java复制public enum OrderStatus {
PENDING_PAYMENT, // 待支付
PAID, // 已支付
CANCELLED, // 已取消
REFUNDED, // 已退款
COMPLETED // 已完成
}
关键业务逻辑实现:
java复制@Transactional
public Order createOrder(OrderDTO orderDTO) {
// 1. 检查车次余票
BusSchedule schedule = scheduleMapper.selectById(orderDTO.getScheduleId());
if (schedule.getRemainingSeats() < orderDTO.getTicketCount()) {
throw new BusinessException("余票不足");
}
// 2. 扣减库存(悲观锁)
int affected = scheduleMapper.reduceSeats(orderDTO.getScheduleId(), orderDTO.getTicketCount());
if (affected == 0) {
throw new ConcurrentOrderException("并发订票冲突");
}
// 3. 生成订单
Order order = convertToOrder(orderDTO);
orderMapper.insert(order);
// 4. 设置支付超时任务
delayQueueService.addPaymentTimeoutTask(order.getId(), 30);
return order;
}
注意:在高并发场景下,必须处理好库存扣减的原子性问题。我们采用了数据库悲观锁+版本号控制的方案,确保不会出现超卖情况。
4. 关键技术难点解决方案
4.1 高并发订票处理
解决方案:
- 使用Redis分布式锁控制并发访问
- 采用异步处理非核心流程(如通知、日志记录)
- 数据库读写分离减轻主库压力
- 引入消息队列削峰填谷
核心代码示例:
java复制public boolean lockSeat(String scheduleId, int count) {
String lockKey = "lock:schedule:" + scheduleId;
String requestId = UUID.randomUUID().toString();
try {
Boolean result = redisTemplate.opsForValue().setIfAbsent(
lockKey, requestId, 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(result)) {
// 执行库存预留逻辑
return reserveSeat(scheduleId, count);
}
return false;
} finally {
// 释放锁时要验证requestId,避免误删其他请求的锁
if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
4.2 支付超时处理
支付超时是票务系统的常见场景,我们采用以下方案:
- 订单创建时写入延时队列
- 定时任务扫描超时订单
- 自动释放库存并取消订单
实现代码:
java复制@Scheduled(fixedRate = 60000)
public void processTimeoutOrders() {
List<Order> timeoutOrders = orderMapper.selectTimeoutOrders(LocalDateTime.now().minusMinutes(30));
timeoutOrders.forEach(order -> {
try {
// 1. 更新订单状态
order.setStatus(OrderStatus.CANCELLED);
orderMapper.updateById(order);
// 2. 恢复库存
scheduleMapper.returnSeats(order.getScheduleId(), order.getTicketCount());
// 3. 记录操作日志
logService.recordTimeoutCancel(order.getId());
} catch (Exception e) {
log.error("处理超时订单失败: {}", order.getId(), e);
}
});
}
5. 系统安全设计
5.1 数据安全措施
- 敏感信息加密:
- 用户密码使用BCrypt加密存储
- 支付信息采用AES对称加密
- 通信链路启用HTTPS
- SQL注入防护:
- 使用MyBatis预编译语句
- 输入参数严格校验
- 启用Druid的SQL防火墙
5.2 业务安全防护
- 防刷票机制:
- IP限流(每分钟最多5次订票请求)
- 用户行为分析识别异常操作
- 关键操作增加图形验证码
- 防重复支付:
java复制public PaymentResult handlePayment(PaymentRequest request) {
// 使用订单ID作为分布式锁key
String lockKey = "payment:lock:" + request.getOrderId();
try {
// 获取分布式锁,防止重复支付
if (!redisLock.acquire(lockKey, 10, TimeUnit.SECONDS)) {
return PaymentResult.fail("支付处理中,请勿重复操作");
}
// 检查订单状态
Order order = orderService.getById(request.getOrderId());
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
return PaymentResult.fail("订单状态异常");
}
// 执行支付逻辑
return paymentGateway.process(request);
} finally {
redisLock.release(lockKey);
}
}
6. 性能优化实践
6.1 数据库优化
- 索引优化:
- 为高频查询字段建立复合索引
- 使用EXPLAIN分析执行计划
- 避免索引失效场景(如函数操作、隐式转换)
- 查询优化:
- 分批处理大数据量操作
- 避免SELECT * 查询
- 合理使用JOIN操作
6.2 缓存策略
多级缓存架构:
- 本地缓存(Caffeine):缓存静态数据、配置信息
- 分布式缓存(Redis):缓存热点数据、会话信息
- CDN缓存:静态资源加速
缓存更新策略:
java复制@CacheEvict(value = "schedules", key = "#scheduleId")
public void updateSchedule(BusSchedule schedule) {
scheduleMapper.updateById(schedule);
// 异步更新相关缓存
eventPublisher.publishEvent(new ScheduleUpdateEvent(schedule.getId()));
}
7. 部署与监控
7.1 系统部署方案
生产环境架构:
- Nginx:负载均衡+静态资源服务
- 应用集群:多节点部署,无状态设计
- MySQL:主从复制+读写分离
- Redis:哨兵模式保证高可用
容器化部署:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
7.2 监控体系建设
- 基础监控:
- Prometheus + Grafana监控系统指标
- ELK收集分析日志
- Spring Boot Actuator暴露健康检查
- 业务监控:
- 关键业务指标埋点(如订票成功率、支付时长)
- 异常交易实时告警
- 业务大盘可视化
8. 项目总结与反思
在实际开发过程中,有几个关键经验值得分享:
-
并发控制:初期采用简单锁机制导致性能瓶颈,后引入分布式锁+乐观锁组合方案,系统吞吐量提升3倍
-
异常处理:支付流程中的网络超时问题曾导致数据不一致,通过引入本地消息表+定时任务补偿机制解决
-
可观测性:完善的日志和监控体系能快速定位线上问题,建议在项目早期就建立
-
测试策略:除了常规功能测试,还需要重点关注:
- 并发压力测试
- 网络异常模拟测试
- 数据一致性验证
这个项目让我深刻体会到,一个成功的票务系统不仅需要扎实的技术实现,更需要深入理解业务场景和用户需求。后续计划引入智能推荐算法优化车次展示,并探索基于微服务的架构升级方案。