民航网上订票系统是典型的电子商务应用,它需要处理高并发查询、实时座位锁定、支付对接等核心业务场景。这个毕业设计项目选择Java+Spring Boot+MySQL的技术栈,既考虑了技术成熟度,又能充分展示全栈开发能力。我在实际开发过程中发现,一个完整的订票系统远不止CRUD那么简单,它涉及到事务控制、缓存优化、接口安全等多个技术难点。
对于计算机相关专业的同学来说,这个项目能全面锻炼需求分析、系统设计、编码实现和测试部署的综合能力。系统最终实现了航班查询、在线选座、订单管理、支付对接等核心功能模块,采用前后端分离架构,前端使用Vue.js,后端基于Spring Boot构建RESTful API。
Spring Boot的自动配置特性让初学者能快速搭建项目骨架,避免了传统SSH框架复杂的XML配置。我在技术选型时主要考虑了以下几点:
实际开发中,我使用了Spring Boot 2.7.3版本,这个版本既稳定又支持Java 17的新特性。通过spring-boot-starter-web快速构建了RESTful接口,用spring-boot-starter-data-jpa简化了数据库操作。
民航订票系统的数据库设计有几个关键挑战:
我最终设计的核心表包括:
特别注意了以下几点:
sql复制-- 舱位表设计示例
CREATE TABLE cabin (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
flight_id BIGINT NOT NULL,
cabin_type VARCHAR(20) NOT NULL, -- 经济舱/商务舱等
price DECIMAL(10,2) NOT NULL,
total_seats INT NOT NULL,
available_seats INT NOT NULL,
version INT DEFAULT 0, -- 乐观锁版本号
FOREIGN KEY (flight_id) REFERENCES flight(id)
);
采用分层架构设计:
考虑到毕业设计的实际情况,我简化了生产环境常用的微服务架构,但保留了清晰的模块划分:
航班查询是最频繁的操作,我通过多级缓存显著提升了性能:
java复制@Bean
public Caffeine<Object, Object> caffeineConfig() {
return Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000);
}
实测下来,查询响应时间从最初的800ms降低到了120ms左右。
订票系统最关键的难点在于防止超卖。我实现了两种方案:
方案一:数据库乐观锁
java复制@Transactional
public boolean bookSeats(Long cabinId, int seats) {
Cabin cabin = cabinRepository.findById(cabinId)
.orElseThrow(() -> new BusinessException("舱位不存在"));
if (cabin.getAvailableSeats() < seats) {
return false;
}
int updated = cabinRepository.updateAvailableSeats(
cabinId,
cabin.getAvailableSeats() - seats,
cabin.getVersion());
return updated > 0;
}
方案二:Redis分布式锁
对于高并发场景,我额外实现了基于Redisson的分布式锁:
java复制RLock lock = redissonClient.getLock("cabin_lock:" + cabinId);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 执行业务逻辑
}
} finally {
lock.unlock();
}
支付流程采用了状态机模式,确保订单状态转换的严谨性:
核心状态转换代码:
java复制public enum OrderStatus {
INIT,
PAYING,
PAID,
CANCELLED,
COMPLETED
}
public void payOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException("订单不存在"));
if (order.getStatus() != OrderStatus.INIT) {
throw new BusinessException("订单状态异常");
}
order.setStatus(OrderStatus.PAYING);
orderRepository.save(order);
// 调用支付接口
boolean payResult = paymentService.pay(order);
if (payResult) {
order.setStatus(OrderStatus.PAID);
// 扣减库存
cabinService.reduceInventory(order.getCabinId(), order.getSeatCount());
} else {
order.setStatus(OrderStatus.CANCELLED);
}
orderRepository.save(order);
}
在开发过程中遇到了几个典型的事务失效问题:
java复制// 错误示例
public class OrderService {
public void createOrder() {
this.validateStock(); // 事务不会生效
}
@Transactional
public void validateStock() {
// ...
}
}
解决方案:将事务方法拆分到不同类,或使用AopContext.currentProxy()
java复制@Transactional
public void updateOrder() {
try {
// 可能抛出异常的操作
} catch (Exception e) {
log.error("错误", e); // 事务不会回滚
}
}
解决方案:在catch块中抛出RuntimeException,或手动回滚:
java复制TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
航班系统涉及大量日期时间计算,常见问题包括:
java复制// 存储统一用UTC
ZonedDateTime utcTime = ZonedDateTime.now(ZoneOffset.UTC);
// 展示时转换
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm")
.withZone(ZoneId.of("Asia/Shanghai"));
java复制Duration flightDuration = Duration.between(departureTime, arrivalTime);
long hours = flightDuration.toHours();
long minutes = flightDuration.toMinutes() % 60;
Web接口需要防范常见安全威胁:
java复制private final RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个请求
@GetMapping("/flights")
public ResponseEntity<List<Flight>> searchFlights() {
if (!rateLimiter.tryAcquire()) {
throw new BusinessException("请求过于频繁");
}
// ...
}
毕业设计虽然不需要真正的生产部署,但我还是准备了相关配置:
bash复制java -jar -Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m booking-system.jar
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
使用JMeter进行压力测试,主要指标:
| 场景 | 并发用户数 | 平均响应时间 | 错误率 |
|---|---|---|---|
| 航班查询 | 500 | 238ms | 0% |
| 下单操作 | 100 | 520ms | 1.2% |
| 支付流程 | 50 | 1200ms | 0.8% |
发现的主要瓶颈是数据库连接数不足,通过调整连接池参数和增加Redis缓存后性能提升了40%。
这个基础版本完成后,还可以考虑以下扩展:
java复制public BigDecimal calculateDynamicPrice(Cabin cabin, LocalDate departureDate) {
long daysBefore = ChronoUnit.DAYS.between(LocalDate.now(), departureDate);
double occupancyRate = 1 - (cabin.getAvailableSeats() / (double)cabin.getTotalSeats());
// 基础算法:离起飞越近、上座率越高,价格越高
double factor = 1 + (1 / (daysBefore + 1)) + (occupancyRate * 0.5);
return cabin.getBasePrice().multiply(BigDecimal.valueOf(factor));
}
我在开发过程中最大的体会是:理论知识和实际开发之间存在巨大鸿沟。比如课本上讲事务隔离级别可能只需要几分钟,但真正解决一个分布式事务问题可能需要几天时间。这个项目让我对Spring Boot生态有了更深入的理解,特别是声明式事务和缓存机制的实际应用。