1. 项目背景与需求分析
2026届计算机相关专业毕业设计选择"客运站网上售票系统"是个非常务实的选题。这个系统本质上是一个典型的B2C电子商务平台,但相比通用电商系统,它在业务规则、实时性要求和数据一致性方面有着独特挑战。
客运行业有几个核心痛点:首先是票务库存的实时性要求极高,一张车票在同一时刻只能被一个用户锁定;其次是复杂的票价计算规则(如节假日浮动、儿童票折扣等);最后是支付环节的时效性(通常锁定座位后15分钟内需完成支付)。这些特性决定了我们不能简单套用普通电商的架构。
从技术栈选择来看,SSM(Spring+SpringMVC+MyBatis)作为后端框架组合,配合Vue.js前端框架,形成了经典的前后端分离架构。这种组合在2026年依然具有教学价值:Spring的IoC/AOP机制能很好处理业务逻辑解耦,MyBatis的灵活性适合处理客运行业特有的复杂查询,而Vue的响应式特性则能优化购票流程的用户体验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术栈选型依据
SSM框架组合中,Spring 5.x版本提供了更完善的模块化支持,特别适合处理客运系统这种业务规则频繁变更的场景。我们通过Spring的声明式事务管理,可以确保票务库存操作的原子性。例如使用@Transactional注解时,需要特别注意隔离级别的设置:
java复制@Transactional(isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRED,
rollbackFor = {TicketException.class, PaymentException.class})
public TicketOrder createOrder(TicketRequest request) {
// 票务库存锁定
ticketService.lockSeats(request);
// 生成订单
orderService.generateOrder(request);
// 支付超时计时
paymentService.startCountdown(request.getOrderId());
}
Vue 3.x的Composition API相比Options API更适合处理复杂的购票状态逻辑。比如在车次选择、座位选择和乘客信息填写这个多步骤流程中,我们可以用reactive实现响应式状态管理:
javascript复制const ticketState = reactive({
selectedSchedule: null,
selectedSeats: [],
passengerInfos: [],
currentStep: 1
})
// 座位选择验证
watch(() => ticketState.selectedSeats, (newVal) => {
if(newVal.length > 5) {
ElMessage.warning('每次最多购买5张票')
ticketState.selectedSeats.pop()
}
})
2.2 系统模块划分
核心模块应包括:
- 票务管理:车次排班、座位库存、票价规则
- 订单处理:订单状态机、支付超时处理
- 用户服务:OAuth2.0认证、权限控制
- 统计报表:基于Apache POI的Excel导出
- 消息通知:WebSocket实时通知
数据库设计要特别注意余票查询的优化。建议采用分库分表策略,按车次日期水平分片。核心表结构示例:
sql复制CREATE TABLE `ticket_schedule` (
`schedule_id` BIGINT PRIMARY KEY,
`bus_id` BIGINT,
`departure_time` DATETIME,
`remaining_seats` INT COMMENT '缓存字段,需配合redis',
INDEX `idx_departure` (`departure_time`)
) ENGINE=InnoDB PARTITION BY RANGE (TO_DAYS(departure_time)) (
PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01'))
);
3. 关键技术实现
3.1 高并发票务处理
解决超卖问题的三种方案对比:
- 悲观锁:SELECT FOR UPDATE(性能差,不推荐)
- 乐观锁:版本号控制(适合低频场景)
- Redis原子操作:INCR/DECR + Lua脚本(最佳方案)
我们采用Redis+Lua的方案,核心脚本如下:
lua复制-- ticket_lock.lua
local key = KEYS[1]
local seats = tonumber(ARGV[1])
local remain = tonumber(redis.call('GET', key))
if remain >= seats then
redis.call('DECRBY', key, seats)
return 1
else
return 0
end
Java调用示例:
java复制public boolean lockSeats(Long scheduleId, int seats) {
String script = ResourceUtils.getScript("ticket_lock.lua");
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(redisScript,
Collections.singletonList("schedule:"+scheduleId),
String.valueOf(seats));
return result == 1;
}
3.2 支付超时处理
采用延迟队列实现订单自动取消。RabbitMQ实现方案:
- 创建交换机和队列时设置x-dead-letter-exchange
- 发布消息时设置TTL
- 死信队列处理超时订单
Spring配置示例:
java复制@Bean
public Queue ticketOrderQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "order.dlx");
args.put("x-dead-letter-routing-key", "order.cancel");
return new Queue("order.create", true, false, false, args);
}
// 发送延迟消息
public void sendDelayMessage(String orderId, long delay) {
rabbitTemplate.convertAndSend("order.create", orderId, message -> {
message.getMessageProperties().setExpiration(String.valueOf(delay));
return message;
});
}
4. 前端工程实践
4.1 购票流程优化
使用Vue Router的导航守卫处理步骤跳转逻辑:
javascript复制const routes = [
{
path: '/book/step1',
component: Step1,
meta: { requiresSelections: false }
},
{
path: '/book/step2',
component: Step2,
meta: { requiresSelections: true }
}
]
router.beforeEach((to, from, next) => {
if(to.meta.requiresSelections && !store.state.selectedSchedule) {
next('/book/step1')
ElMessage.error('请先选择车次')
} else {
next()
}
})
4.2 性能优化技巧
- 车次列表虚拟滚动:使用vue-virtual-scroller
- 表单懒验证:v-model.lazy配合debounce
- 组件按需加载:defineAsyncComponent
javascript复制const SeatMap = defineAsyncComponent(() =>
import('./components/SeatMap.vue').then(module => {
// 预加载相关资源
preloadAssets(['seat.png', 'selected-seat.png']);
return module;
})
)
5. 部署与监控
5.1 Docker集成部署
后端Dockerfile示例:
dockerfile复制FROM openjdk:17-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
COPY ticket_lock.lua /scripts/
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
前端Nginx配置要点:
nginx复制location / {
try_files $uri $uri/ /index.html;
gzip on;
gzip_types text/plain application/xml application/javascript;
# 解决Vue路由history模式404问题
error_page 404 =200 /index.html;
}
5.2 监控方案
- Spring Boot Actuator + Prometheus
- Vue性能监控:web-vitals
- 业务埋点:自定义事件跟踪
javascript复制// 关键按钮点击跟踪
const trackClick = (eventName) => {
if(process.env.NODE_ENV === 'production') {
axios.post('/analytics', {
event: eventName,
timestamp: Date.now(),
path: route.path
})
}
}
6. 毕设答辩要点
6.1 技术亮点阐述
- 分布式锁的实现方案对比
- 最终一致性 vs 强一致性的取舍
- Vue响应式原理在复杂表单中的应用
6.2 常见问题准备
Q:为什么不用Spring Boot?
A:Spring Boot本质上是Spring的封装,用SSM框架更能展示对基础原理的理解。我们的POM文件仍然采用Spring Boot的parent POM来管理依赖版本。
Q:如何保证Redis与MySQL数据一致?
A:采用异步双删策略:1)先删Redis 2)更新MySQL 3)延迟再删Redis。配合binlog监听作为兜底方案。
Q:前端安全性考虑?
A:1)CSP内容安全策略 2)XSS防护:DOMPurify处理动态内容 3)CSRF Token验证
7. 开发工具链推荐
-
后端:
- IDEA Ultimate(学生免费)
- MyBatisX插件(XML跳转)
- Arthas(线上诊断)
-
前端:
- Volar(Vue 3支持)
- Vue DevTools
- Mock.js(接口模拟)
-
协同工具:
- PlantUML(画架构图)
- Swagger UI(API文档)
- Postman(接口测试)
特别提示:客运系统的日期处理要特别注意时区问题,建议所有时间戳统一存储为UTC时间,在前端展示时再转换为本地时间。使用Day.js这类轻量库比Moment.js更合适。
在真实开发中,我们还会遇到很多边界情况,比如:
- 同一用户多个设备同时抢票如何处理
- 支付成功但通知失败的补偿机制
- 车次临时变更的批量退票流程
这些都可以作为论文的"未来展望"部分。建议在GitHub上维护一个issue列表,记录开发过程中发现但暂未解决的问题,这既能体现工程思维,也能为答辩提供素材。
