1. 项目背景与核心价值
汽车票网上预订系统是传统客运行业数字化转型的典型场景。我去年参与过某省级客运集团的系统重构,亲眼见证了从线下窗口排队到线上自主选座的转变过程。这种系统看似简单,实则涉及高并发票务处理、座位实时锁定、支付超时回滚等复杂业务逻辑,是检验开发者全栈能力的绝佳练手项目。
这个基于SpringBoot+Vue的实现方案特别适合作为教学案例,原因有三:首先,它覆盖了企业级应用的标准技术栈(Java后端+MySQL+Vue前端);其次,票务业务逻辑具有明确的边界和完整的CRUD操作;最重要的是,系统包含典型的分布式事务场景——比如用户选座后15分钟内未支付需要自动释放座位,这对理解事务隔离级别和定时任务调度非常有帮助。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构解析
2.1 后端技术栈设计
SpringBoot 2.7.x + MyBatis-Plus的组合是经过生产验证的黄金搭档。在客运系统的实际开发中,我们特别看重以下几点:
- 多数据源配置:车站信息、票务数据、用户账户往往需要分库存储。通过
@DS注解实现动态数据源切换:
java复制@Service
@DS("ticket_db") // 默认使用票务库
public class TicketServiceImpl {
@DS("user_db") // 方法级别切换用户库
public User getUserDetail(Long userId) {
//...
}
}
- 分布式锁设计:使用Redisson实现座位锁定,避免超卖:
java复制RLock lock = redissonClient.getLock("seat_lock:" + scheduleId);
try {
if(lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 业务处理
}
} finally {
lock.unlock();
}
- 定时任务补偿:通过Spring Scheduler实现未支付订单自动释放:
properties复制# application.yml
schedule:
order:
expire-minutes: 15
cleanup-cron: 0 */5 * * * ?
2.2 前端技术方案
Vue 3 + Element Plus的组合提供了良好的开发体验。在票务系统中需要特别注意:
- 实时座位状态:通过WebSocket实现座位图动态刷新
javascript复制const socket = new WebSocket('wss://your-domain.com/ws/seats');
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
// 更新座位状态
updateSeatMap(data);
};
- 购票流程防抖:避免用户重复提交
javascript复制import { debounce } from 'lodash-es';
const submitOrder = debounce(() => {
// 提交逻辑
}, 1000);
- 移动端适配:使用vw单位实现响应式布局
css复制.seat {
width: calc(10vw - 2px);
height: calc(10vw - 2px);
}
3. 数据库设计要点
3.1 核心表结构
sql复制CREATE TABLE `bus_schedule` (
`id` BIGINT PRIMARY KEY,
`departure_station_id` INT NOT NULL,
`arrival_station_id` INT NOT NULL,
`departure_time` DATETIME NOT NULL,
`bus_type` ENUM('NORMAL','DELUXE') DEFAULT 'NORMAL',
`total_seats` INT NOT NULL,
`price` DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `seat` (
`id` BIGINT PRIMARY KEY,
`schedule_id` BIGINT NOT NULL,
`seat_number` VARCHAR(10) NOT NULL,
`status` ENUM('AVAILABLE','LOCKED','SOLD') DEFAULT 'AVAILABLE',
`lock_time` DATETIME,
`order_id` BIGINT,
UNIQUE KEY `idx_schedule_seat` (`schedule_id`,`seat_number`)
);
3.2 性能优化实践
- 读写分离:查询类请求走从库
java复制@Slave // 自定义注解
public List<ScheduleVO> querySchedules(ScheduleQuery query) {
//...
}
- 热点数据缓存:使用Redis缓存热门线路
java复制@Cacheable(value = "hotRoutes", key = "#departureId+'-'+#arrivalId")
public List<ScheduleVO> getHotRoutes(Long departureId, Long arrivalId) {
//...
}
- 分表策略:按月份拆分订单表
java复制@TableName("order_#{#date.format('yyyyMM')}")
public class Order {
//...
}
4. 典型业务场景实现
4.1 购票状态机设计
mermaid复制stateDiagram-v2
[*] --> AVAILABLE
AVAILABLE --> LOCKED: 用户选座
LOCKED --> AVAILABLE: 超时未支付
LOCKED --> SOLD: 支付成功
SOLD --> [*]: 行程结束
对应代码实现:
java复制public enum SeatStatus {
AVAILABLE {
@Override
public boolean canLock() { return true; }
},
LOCKED {
@Override
public boolean canUnlock() { return true; }
@Override
public boolean canSell() { return true; }
},
SOLD {
@Override
public boolean canRefund() { return true; }
};
public boolean canLock() { return false; }
public boolean canUnlock() { return false; }
// 其他状态方法...
}
4.2 支付超时处理
使用RabbitMQ实现延迟队列:
java复制@Bean
public Queue delayQueue() {
return QueueBuilder.durable("order.delay.queue")
.withArgument("x-dead-letter-exchange", "order.exchange")
.withArgument("x-dead-letter-routing-key", "order.release")
.build();
}
@RabbitListener(queues = "order.release.queue")
public void handleOrderRelease(OrderMessage message) {
// 检查订单状态并释放座位
}
5. 项目部署与监控
5.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
5.2 监控配置
SpringBoot Admin监控关键指标:
java复制@Configuration
@EnableAdminServer
public class AdminServerConfig {
@Bean
public MailNotifier mailNotifier() {
return new MailNotifier(/*...*/);
}
}
6. 常见问题解决方案
-
座位超卖问题:
- 解决方案:乐观锁+Redis原子操作
java复制// 使用Redis的DECR原子操作 Long remain = redisTemplate.opsForValue() .decrement("schedule:remain:" + scheduleId); if (remain < 0) { throw new BusinessException("余票不足"); } -
分布式事务一致性:
- 采用本地消息表+定时任务补偿
sql复制CREATE TABLE `transaction_log` ( `id` BIGINT PRIMARY KEY, `business_type` VARCHAR(50) NOT NULL, `business_id` BIGINT NOT NULL, `status` TINYINT NOT NULL, `retry_count` INT DEFAULT 0 ); -
高并发查询优化:
- 使用Caffeine做JVM缓存
java复制@Bean public CacheManager cacheManager() { CaffeineCacheManager manager = new CaffeineCacheManager(); manager.setCaffeine(Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES)); return manager; }
7. 扩展功能建议
-
智能推荐线路:
java复制public List<ScheduleVO> recommendSchedules(Long userId) { // 基于用户历史订单和协同过滤算法 } -
电子客票核验:
java复制@PostMapping("/verify") public Result verifyTicket(@RequestBody QrCode qrCode) { // 使用JWT验证二维码有效性 } -
行程提醒服务:
java复制@Scheduled(cron = "0 0/30 * * * ?") public void sendReminders() { // 查询1小时内发车的订单 // 发送短信/APP推送 }
在真实项目中,我们还会考虑加入灰度发布、熔断降级等机制。这个示例项目已经包含了企业级应用的核心要素,开发者可以在此基础上继续扩展完善。建议先从理解状态机和分布式事务的实现入手,这对提升Java后端开发能力非常有帮助。
