ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MySQL汽车票预订系统源码实战:从部署到二次开发

SpringBoot+Vue+MySQL汽车票预订系统源码实战:从部署到二次开发 第一次拿到这类“可运行源码”项目很多人第一反应是直接双击跑起来结果不是端口冲突就是数据库连不上最后摔键盘。这套汽车票网上预订系统本质是一个典型的SpringBoot Vue MySQL前后端分离项目覆盖了线路查询、班次管理、在线购票、订单支付模拟、后台管理这些核心业务。你把它跑通了就等于把Web全栈开发的主干流程走了一遍数据库建模、后端接口设计、前端页面联调、打包部署。这篇文章不打算写成枯燥的骨架介绍而是直接拆解这套系统从零到手的基本盘。我会把技术选型为什么这么定、每个核心模块在干什么、本地怎么一步步跑起来、常见的坑怎么排以及拿到这套源码之后怎么改成自己的东西按实战顺序全部说透。如果你刚学完SpringBoot和Vue基础知识却不知道它们怎么揉进一个完整项目里这篇文章应该能帮你把最后那层纸捅破。1. 项目整体设计与技术选型思路1.1 为什么是SpringBoot而不是传统SSH框架很多人看到SpringBoot第一反应就是“它比SSH/SSM轻”。更准确地说SpringBoot解决的是集成和启动成本的问题。传统SSM项目里你要手动配置Spring的bean扫描、数据源连接、MyBatis的Mapper扫描、Web容器部署任何一个配置写错都要折腾半天。SpringBoot把这些“约定俗成”的东西变成自动配置你只要引入对应的starter依赖它就能根据classpath内容和配置文件自动装配。举例来说只要pom里加了spring-boot-starter-web内嵌的Tomcat就直接启动加了spring-boot-starter-data-jpa或mybatis-spring-boot-starter数据源相关的核心配置就被自动处理。汽车票系统最合适的复杂度恰好在这个区间。它没有高并发秒杀那种变态需求也不需要微服务拆得七零八落一个SpringBoot单体应用把用户管理、班次管理、订单管理全包进来结构简单、部署方便、入门友好。这种选择也意味着你拿到源码后能快速改业务逻辑而不是先被一堆框架配置劝退。提示如果你打开项目发现pom.xml里的SpringBoot版本特别高又伴随一些“依赖冲突”或“自动配置失效”的报错建议先降到2.7.x系列。这套系统的核心功能用2.5到2.7都没问题版本太高反而容易出现Java版本和依赖兼容性问题。1.2 Vue前端与前后端分离的价值这套系统的前端是Vue工程开发时后端跑在8080前端跑在5173靠代理转发接口请求这种做法叫前后端分离。它最大的好处是前端只管页面渲染和用户交互后端只负责提供JSON数据和业务逻辑两边并行开发不互相阻塞。Vue在这个项目里承担的工作很清晰用Vue Router做页面跳转比如首页、线路列表、订单页面、后台管理页面用Axios封装HTTP请求调用后端接口拿数据用组件化方式拆页面比如车辆班次卡片、订单状态标签、分页组件登录状态通过Token或Session维持路由守卫拦截未登录用户。我看到很多初学者拿到这类前端工程会有点懵因为里面文件很多。其实你只要抓住三个关键入口就行package.json里是依赖和脚本src/main.js是应用入口src/router/index.js是路由配置。其余组件文件本质都是围绕这三点展开的。1.3 数据库表设计与核心关系MySQL负责存储汽车票业务的核心数据。标题里既然强调“MySQL可运行”说明这个项目的历史负载和数据结构都是按关系型数据库设计的。先记住核心表大概有这几张用户表user账号、密码、手机号、昵称线路表line始发站、终点站、里程、票价基准班次表schedule某条线路下每天的出发时间、车型、总座位数订单表order哪个用户、哪个班次、买了几张票、支付状态**座位可能单独一张表也可能以“订单号座位号”的冗余字段存在。**这也是这类系统和小商品订单的一个差异点——车票必须绑定具体座位和班次。这种表结构你一定不陌生它本质上是**“线路 - 班次 - 订单”的一对多层级关系**。设计核心就在于班次表存“总座位数”订单表记录“已售座位”查询余票时用总座位数减去已售座位或者直接查座位表中未被占用的座位号。这个逻辑看起来简单实际做并发控制的时候就会遇到经典问题我放到后面专门讲。2. 核心功能模块拆解与业务细节2.1 线路班次与余票查询这套系统最核心的高频操作就是“查线路、看班次、选座位”。前端页面上通常有搜索栏你先选始发站和终点站再选出发日期点击查询后调用后端接口例如GET /api/schedule?from上海to杭州date2025-06-01后端的处理流程我建议你一定要看懂这段逻辑根据线路表查符合条件的线路ID根据线路ID去班次表查当天的所有班次查每个班次当天的售票情况计算余票组装成班次列表返回给前端。这里有一个常见的性能细节线路查询会非常频繁线路表中的“始发站、终点站”字段最好建联合索引。如果你拿到源码发现查询很慢八成就是索引没建好或者查询时有隐式转换。2.2 购票下单与座位锁定用户选了班次、填了乘客信息点击“提交订单”后端这一步做的不只是往订单表插一条数据而是要有几个关键校验用户是否已登录班次是否存在、是否在可售票时间内所选座位是否已经被别人购买一次最多能买几张很多系统限制为3到5张。真正生产级的系统这一步会用到事务保证“扣减余票”和“生成订单”要么同时成功要么同时回滚。这套源码如果是用Transactional注解来控制那已经很规范了如果发现没有事务你需要自己补上。订单创建后状态通常是“待支付”。前端进入支付页调用模拟支付接口后端把状态改成“已支付”这个时候座位才算真正锁定。退票流程则是反向操作把订单状态改成“已退票”同时把座位释放回可售池。注意如果你在这套系统里测试“两个账号同时买同一班次同一个座位”靠普通的事务无法完全避免超卖。生产中更常用的方案是“乐观锁”也就是在班次表加一个version字段更新时检查version是否匹配。这个属于进阶改造点后面扩展时我们细说。2.3 后台管理与基础数据维护网上订票系统离不开后台管理端。这套源码的后台功能一般包含线路管理增删改查线路信息班次管理设置每天多个发车班次调整发车时间和价格订单管理查看所有订单处理异常订单比如改签、退票审核用户管理查看注册用户禁用异常账号。如果前端分了“用户端”和“管理端”两个工程或者用路由权限区分页面那它还涉及权限控制。最简单做法是用户角色字段user/admin后端接口用RequiresPermissions或拦截器校验角色前端通过路由守卫判断能不能进入管理页面。做二次开发时这块通常是你需要最先理解的部分。3. 本机跑通全过程实录3.1 准备工作与版本匹配先把环境对齐不然你后面踩的坑会比我还多。这套系统推荐搭配是JDK 1.8或8个别新版源码可能需要JDK 11/17最好先看pom.xml里的java.versionMySQL 5.7或8.0数据库连接驱动和数据库版本要匹配Maven 3.6以上Node.js 14到16太高的Node版本有时会导致依赖安装报错开发工具后端用IntelliJ IDEA前端用VS Code或IDEA也行。我第一次跑类似项目时最常遇到的问题就是“JDK版本乱套”。如果你发现本地已经装了JDK 17而项目的maven.compiler.source是1.8编译时就会报错“-source 1.8中不支持...”。这里我给个最粗暴的解决方案有多个JDK时在IDEA的Project Structure里把SDK明确指定为项目需要的版本Maven的JAVA_HOME也一起统一。3.2 数据库导入和配置修改数据库部分我会优先讲“为什么”因为你以后换任何项目都绕不开。第一步用命令行或Navicat创建数据库比如叫bus_ticket字符集选择utf8mb4。注意使用utf8mb4而不是utf8因为utf8mb4对中文和表情符号支持更完整。CREATE DATABASE bus_ticket DEFAULT CHARACTER SET utf8mb4;第二步导入项目里自带的bus_ticket.sql文件。如果是命令行导入mysql -u root -p bus_ticket bus_ticket.sql或者在Navicat里右键数据库选择“运行SQL文件”。第三步修改后端配置。SpringBoot项目一般都有一个application.yml或application.properties里面最关键的是数据源配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/bus_ticket?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver这里有两个坑几乎人人都踩过MySQL 8.x的驱动类名是com.mysql.cj.jdbc.DriverMySQL 5.7及以下是com.mysql.jdbc.Driver连接URL末尾必须加serverTimezoneAsia/Shanghai表示时区。MySQL 5.6之前的旧驱动或默认时区不对会报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized简直让人摸不着头脑。还有一个社交平台提问特别多的情况是“docker启动MySQL失败”。如果你用Docker跑数据库多半是端口被占用或容器内存不足。跑之前先确认3306端口是否已被本机MySQL占用解决了再映射端口docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -e MYSQL_DATABASEbus_ticket mysql:8.03.3 后端启动并初步验证后端用IDEA打开项目后先让Maven把依赖拉下来。这一步如果你下载速度很慢可以把Maven的镜像仓库改成阿里云镜像。然后运行启动类BusTicketApplication.java。启动成功后控制台会出现SpringBoot的标志图案并显示Tomcat started on port 8080。如果你想确认后端接口是否正常直接浏览器访问http://localhost:8080/api/schedule/list如果返回JSON数据就说明数据库连接和项目启动都成功了。我看到很多人到这一步就兴奋得不行但先别急着点前端往下看联调。3.4 前端启动与接口联调前端工程目录里找到package.json然后在目录下执行npm install npm run dev如果提示npm install报错多数情况是Node版本问题和依赖安装源问题可以用npm install --registryhttps://registry.npmmirror.com前端启动后Vite默认端口一般是5173。为了防止跨域官方推荐做法是前端通过代理转发请求到后端。你会在vite.config.js里看到类似配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端里发/api/schedule/list实际后端收到的也是/api/schedule/list端口不同也不会跨域。3.5 前端打包并交给SpringBoot托管开发阶段是前后端分离跑但最终部署更常见的方式是“前端打包后交给后端统一托管”。操作也很简单npm run build打包后的文件在dist目录把dist目录里的内容复制到SpringBoot项目的src/main/resources/static目录下。重新启动后端直接访问http://localhost:8080页面就是那套前端。这么做的好处是一个服务搞定所有内容不用再单独部署Nginx和前端静态站。注意一点如果Vue用了history模式的路由直接刷新某个子路由页面会报404因为SpringBoot默认找不到对应的前端路由需要加一个“所有未匹配路径跳转到index.html”的Controller或拦截器。如果你用的是hash模式那就不存在这个404问题。源码里如果用了history模式而刷新404这就是你要改的地方。4. 常见问题与排查技巧实录4.1 数据库连接失败和版本不匹配这类问题出现频率最高。我把它们的报错特征和解决方法整理成一个速查表你按图索骥就行报错现象大概率原因处理办法Table or table doesnt existSQL里表和API里表名不一致检查大小写敏感配置核对SQL脚本和实体类注解的表名Unknown database bus_ticket数据库未创建或名称不对执行CREATE DATABASE确认connection与数据库名一致Access denied for user用户名密码错误或权限不够重置密码或授权GRANT ALL PRIVILEGESPublic Key Retrieval is not allowedMySQL 8.0用caching_sha2_password加密URL后加allowPublicKeyRetrievaltrueThe server time zone value is unrecognized时区未配置URL后加serverTimezoneAsia/Shanghai如果你完全按默认安装MySQL然后使用容易忽略默认配置最常见的Access denied往往不是因为密码错了而是因为安装时选了不同鉴权方式。重新配置好用户权限后这个问题就消失了。4.2 前端调用接口报跨域错误浏览器的控制台出现类似这样的错误Access to XMLHttpRequest at http://localhost:8080/api/login from origin http://localhost:5173 has been blocked by CORS policy这就意味着你直接让5173端口去请求8080端口的接口没有走代理。解决的办法有两个开发阶段检查前端vite.config.js里有没有配置proxy如果项目里后端配了CrossOrigin注解或CorsFilter写上允许的源但这种方式生产环境不够安全不建议推广。我个人的踩坑体验是改了vite.config.js之后必须重启前端开发服务才生效有时候刷新页面没用一卡就是一下午。另外要注意的是登录后的状态保存方式。这套系统如果用Session保存登录态那么跨域场景下Cookie可能携带不完整如果用Token放在请求头里前端就需要在Axios拦截器里统一加上Authorization头。截图或分享的时候注意别把自己的真实Token贴出来。4.3 前端路由刷新404和资源引用问题这个问题在上一节部署时提到过。再细化一下Vue3的Router如果用createWebHistory()打包后部署到SpringBoot静态资源目录刷新/order这种路径时后端就找不到。解决方式是在SpringBoot里加一个转发Controller public class ViewController { RequestMapping(/{path:[^\\.]*}) public String forward() { return forward:/index.html; } }这段代码的含义是所有不带点后缀的路径都交给前端路由去处理。加了这个之后刷新子路由就不会404了。还有一个小坑是publicPath。如果你把前端打包后放到static下一层目录后期部署在服务器某个子路径下那Vite打包资源路径可能会找不到。打包前在vite.config.js里设置base: ./这样资源引用就会变成相对路径直接扔到任意静态目录也能跑。4.4 座位余票并发和事务问题这套源码跑通后很多人喜欢在后台开两个账号模拟抢票。你会发现只凭普通insert和update语句两个请求同时进来时最后很可能出现“同一个座位卖给了两个人”的情况。这不是代码bug而是缺少并发控制。我建议做一次小改造来彻底理解这个问题。在订单提交接口上加上事务注解Transactional public void createOrder(Integer scheduleId, Integer userId, Integer seatNo) { Schedule schedule scheduleMapper.selectById(scheduleId); // 校验座位未售出 Integer count orderMapper.selectSeatCount(scheduleId, seatNo); if (count 0) { throw new RuntimeException(座位已被购买); } // 插入订单 orderMapper.insert(...); }但只加事务还不够因为两个请求可能同时读到count0。更可靠的是给班次表加version字段更新时带上条件的写法就是乐观锁UPDATE schedule SET version version 1 WHERE id ? AND version ?如果更新影响的行数为0说明数据已经被别人改过事务回滚并提示用户重新选择座位。这个思路你在任何电商售票系统里都能看到属于非常经典的“面试级”知识点。5. 从可运行到可生产二次开发与扩展方向5.1 把模拟支付换成真实支付标题里强调“可直接运行”意思是开箱即用但生产环境还差得远。当前这个系统大概率用的是模拟支付接口点一下“支付成功”就完成订单。真实上线的话你需要对接微信支付、支付宝或银联后端引入支付SDK配置商户号、密钥、回调地址前端调用后端生成支付二维码或拉起收银台支付成功后由支付平台异步回调后端后端修改订单状态回调接口必须做签名校验防止伪造通知。这个改造涉及面较大但如果你已经把业务跑通再做支付对接会顺畅很多因为订单状态机的基本框架已经有了。5.2 引入Redis做缓存和限流原本的余票查询每次都会打数据库用户量稍微一多数据库压力立马上来。改造方向很清晰热门线路的班次余票可以缓存到Redis设置过期时间。伪代码大概是public ScheduleVO getScheduleDetail(Long id) { String key schedule: id; ScheduleVO vo redisTemplate.opsForValue().get(key); if (vo ! null) { return vo; } Schedule schedule scheduleMapper.selectById(id); redisTemplate.opsForValue().set(key, schedule, 5, TimeUnit.MINUTES); return schedule; }再进一步把购票接口的请求做限流防止恶意刷票。可以用SpringBoot整合Redis实现简单的计数器限流也可以用Redission的RRateLimiter。这些改造网上资料很多但只有自己动手改了才能真正理解缓存和数据库之间的数据一致性有多麻烦——至少你会明白为什么别人说“缓存虽好失效更坑”。5.3 前端功能增强动态路由、文件上传、音视频播放拿到这套系统作为练习项目后你可以顺手做一些前端增强Vue动态路由根据用户角色动态生成菜单和路由表而不是计划里面写死文件上传让用户上传身份证照片或支付凭证后端用SpringBoot接收MultipartFile并保存音视频播放如果你在做政务或交通一体化项目时想去展示汽车站宣传视频或相关通知Vue里播放m3u8这类格式流媒体需要额外引入hls.js或video.js源码自带的video标签是处理不了的短信通知订单出票后给用户发短信对接阿里云或腾讯云短信SDK注意签名模板审核。这些都是明确、可控、能让简历明显丰富的小功能。5.4 适合用来学习时继续深挖的几个点这套系统完整度不算低所以非常适合做“精读式学习”。我建议你按照以下顺序试着去读源码先读懂数据库所有表和字段画一张简易ER图再看后端Controller层梳理每个接口对应哪个前端页面然后看Service层重点理解订单和班次的业务逻辑最后看前端组件理解数据从按钮点击到后端处理的完整链路。等你把这套链路读完你会发现SpringBoot和Vue不再是一个个孤立的知识点而是一条完整的生产线。以后再跳到别的项目即使技术栈换成微服务或小程序核心思想依然相通。最后再说一个实际操作中的体会当你拿到这类“可直接运行”的项目最快的学习方式不是从零重写而是在跑通的基础上故意改坏它。比如把某个接口的返回值改错观察前端怎么报错把数据库某个字段删掉看后端什么反应。这套“破坏式学习”帮我建立了很强的排错手感比看十遍教程都有用。这套系统跑起来以后建议先试着改一改票价计算规则或者给订单加一个备注字段从最小的改动开始你会很快找到“代码由我掌控”的感觉。
返回列表