资讯动态

Spring Boot+Vue航空机票预定系统:从数据库设计到部署全解析

发布时间:2026/9/14 5:29:16 来源:尧图企业网站定制
1. 项目概述与技术选型1.1 这个系统到底解决什么问题航空机票预定系统是典型的Java全栈课程设计、毕业设计题目每年都有大量计算机相关专业的学生选这个方向。它的业务场景非常成熟——用户注册登录、查询航班、选择舱位、下单支付、查看订单、后台管理航班和航线整个流程就是一个简化的航司在线销售系统。用Spring Boot Vue这套组合来做在目前的环境下几乎是课设毕设的标准答案。Spring Boot负责后端接口和业务逻辑Vue负责前端页面和交互MySQL存数据整体是一个前后端分离的架构。这种架构的好处是分工明确前端只管渲染和请求后端只管处理数据和返回结果两个部分可以并行开发也很容易讲清楚。很多同学拿到这种项目其实最大的痛点不是不会写代码而是不知道从哪下手。网上能搜到一堆零散的代码片段但真正能跑通、能讲清楚、能通过答辩的完整项目并不多。我这里就结合这个航票项目的实际实现把几个关键环节——数据库设计、后端接口、前端联调、常见坑点——从头到尾捋一遍。1.2 技术栈为什么这么选后端用Spring Boot核心理由是它对Java开发者实在太友好了。内嵌Tomcat不用单独部署服务器自动配置省掉一大堆XML配置配合Spring MVC开发RESTful接口写起来非常顺手。Maven管理依赖打成一个JAR包就能跑部署环节也简单。前端用VueVue在国内课程设计中的地位不用多说学习曲线比React平缓模板语法容易理解加上Element UI的组件库能很快搭出看起来还不错的界面。Vue Router管理页面跳转Vuex或Pinia管理全局状态Axios发请求这套组合是Vue项目最常见的配置。数据库选MySQL原因就更直接了——免费、通用、资料多Navicat或者DataGrip都能连网上教程一搜一大把。关于数据库的配置后面我会单独讲几个容易踩的坑。1.3 项目功能模块拆解这个系统的功能可以从用户端和管理端两个视角来看。用户端比较核心的功能包括注册登录、航班检索按起降城市、日期、舱位类型筛选、航班详情查看余票、价格、起降时间、提交订单、在线支付或模拟支付、订单管理和退票处理。航班检索是核心中的核心机票预订系统的体验好不好就看搜索这块做得到不到位。管理端的功能则围绕数据维护展开航线管理、航班计划管理每天有哪些航班、每个航班有多少座位、价格管理、订单查看与统计、用户管理。有些实现里还会加上公告发布和简单的数据图表看板这属于加分项不是必须的。整个项目做完你的模块划分越清晰答辩的时候就越容易讲清楚。不要一个Controller写几百行功能混在一起这种代码一被追问细节就容易露馅。2. 数据库设计与核心表结构2.1 数据表整体规划思路数据库设计是这个项目最先要做的事因为它决定了后面所有业务逻辑怎么写。数据库设计也是很考察逻辑能力的一环评审老师经常会针对表设计提问。对于一个航班预订系统最核心的表大概是这几张用户表、航线条目表城市、机场信息、航班表具体的航班号、起降时间、机型、舱位表每个航班有哪些舱位类型、价格、余票、订单表、支付记录表。有些实现会把机场和城市拆开有些直接把航线和航班合并成一张表。我在这类课设项目中比较推荐拆成六张表左右不要太碎也不要太少。表太少意味着字段堆砌、逻辑耦合表太多则增加了联表查询的复杂度、答辩时也不好讲。2.2 用户表与航班表的关键字段用户表的设计相对标准字段基本就是用户名、密码、手机号、邮箱、注册时间这些。需要注意两点一是密码字段最好不要明文存储用Spring Security的BCrypt或者MD5加盐都行二是给用户名加一个唯一索引避免注册时重复。航班相关的表是核心设计上要区分两种数据。一种是航线基本数据比如从北京到上海这个航线概念它不具体指某一天哪个航班另一种是某一天的具体航班比如2025年6月15日北京飞上海、航班号为CA1501的这个具体班次。前者设计成航线表或者航班基础表后者设计成航班计划表两张表之间用外键关联。航班表里比较关键的字段包括航班号、出发城市、到达城市、出发机场、到达机场、计划起飞时间、计划到达时间、机型、舱位信息。舱位这里有两种做法一种是把经济舱、商务舱、头等舱的票价和余票直接做成航班表里的一组字段另一种是独立一张舱位表通过航班ID关联。我建议做成独立的舱位表。原因是每个航班的舱位配置不完全一样独立表更灵活而且下单时锁定余票的逻辑也更好实现不会把航班表搞得特别宽。2.3 订单表与支付表的闭环设计订单表是整个项目里最能体现逻辑严谨性的地方。订单编码、用户ID、关联的航班ID、舱位类型、乘客姓名、证件号码、联系电话、下单价格、订单状态、创建时间这些是最基本的字段。订单状态这个字段在设计时就要想清楚需要哪几个值。常见的是待支付、已支付、已出票、已取消、已退票。为什么强调状态很重要因为很多课设项目只写了已支付和未支付两个状态导致后面退票、取消、超时这些逻辑完全没法实现演示的时候很容易卡壳。支付记录表同样不可少。即使你的项目里支付是模拟的也要有一张独立的支付表或者至少在订单表里记录支付流水号、支付时间、支付渠道这些信息。这样做一方面是流程完整另一方面是老师提问“支付失败了怎么处理”、“如何防止重复支付”的时候你能说清楚。数据库阶段还有两个容易被忽略的细节。一是字符集尽量用utf8mb4不要用utf8否则后面存用户昵称这类带生僻字或特殊符号的数据时会报错或乱码。二是时间字段的类型和Java后端LocalDateTime的映射要处理好否则接口返回时间格式乱七八糟。MySQL 8.0以上版本配合新版的JDBC驱动这部分体验已经好很多了。3. 后端核心接口实现与细节3.1 Spring Boot项目结构与启动配置项目结构上我推荐按Controller、Service、Mapper、Entity、Config这些包来分。Controller只负责接收参数和返回结果Service层写业务逻辑Mapper层写数据库操作。这个分层的逻辑在最开始就要建好不要图省事把所有逻辑堆在Controller里。启动配置方面application.yml是最常用到的文件。数据源、MyBatis、端口号、日志级别都在这里配置。比较典型的一段配置长这样spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/flight_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 server: port: 8088 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: autourl里带serverTimezoneAsia/Shanghai这个参数非常重要。很多同学new项目跑起来之后数据库连接一直报时区错误就是因为漏了它。端口号建议改一下不要用默认的8080。因为Spring Boot项目如果同时开多个8080很容易被占用改成8088或者8081能省掉不少排查端口冲突的时间。3.2 JWT认证机制用户登录这块现在课程设计里用得最多的是JWT方案。简单说用户登录成功后后端根据用户名生成一个签名后的token字符串返回给前端前端之后的每个请求都在请求头里带上这个token后端用拦截器或过滤器校验token是否有效、当前用户是谁。JWT本身不复杂但有两个细节值得注意。第一是密码加密推荐用Spring Security里自带的BCryptPasswordEncoder。使用BCrypt每次加密的结果是不同的所以校验时不能用equals而是用encoder.matches(inputPassword, encodedPassword)这个方法。很多同学在这一步会翻车因为拿明文去和BCrypt密文直接equals永远返回false。第二是拦截器的放行配置。登录、注册、航班查询这些接口要放行而创建订单、查看个人订单这些接口必须校验token。如果放行配置写错会导致前端没登录也能访问下单接口项目逻辑直接穿帮。3.3 航班查询接口的联表与过滤航班查询是这个项目的核心功能接口设计上有一个关键点需要想清楚用户搜索的时候输入的是城市但航班表里存的是城市怎么把航线和具体航班关联起来。实际实现中航线表保存北京-上海这样的线路信息明确出发城市、到达城市。航班表关联航线ID同时记录具体的起飞时间、到达时间和日期。搜索的时候用户输入出发城市和到达城市先去航线表找到对应的航线ID再拿航线ID和日期去航班表里查当天有哪些航班。如果用的MyBatis-Plus可以用LambdaQueryWrapper来处理条件查询写起来比XML要简洁得多。例如这个搜索的核心逻辑LambdaQueryWrapperFlight wrapper new LambdaQueryWrapper(); wrapper.eq(Flight::getDepCity, request.getDepCity()) .eq(Flight::getArrCity, request.getArrCity()) .eq(Flight::getFlightDate, request.getFlightDate()); if (request.getMinPrice() ! null) { wrapper.ge(Flight::getMinPrice, request.getMinPrice()); }这段代码里响应参数直接用了个自定义的request对象来接收请求体而不是堆一长串的查询参数这样接口的参数结构更清晰也方便前端传值。余票的展示也很重要。用户看到的航班列表页需要显示每个航班经济舱、商务舱的余票数。如果在数据库里单独存一个余票字段其实是有风险的因为余票是动态变化的每次下单都要减一。这时候存一个座位总数再配合订单表统计已售数量相减得到余票数据上更合理但也稍复杂。课设中直接在航班表中加余票字段也是可以接受的方案但需要注意下单和扣票必须放在一个事务里。3.4 下单事务与超时取消逻辑下单是整个系统里最考验代码功底的地方。一次下单涉及的操作至少包括校验用户是否登录、校验航班是否存在、校验余票是否充足、生成订单记录、扣减余票、生成待支付订单。这五步任何一步失败之前的所有操作都要回滚。Spring的Transactional注解就是用来解决这个问题的。在Service方法上加这个注解方法内部的所有数据库操作就会在同一个事务里执行任何一个环节抛异常后续操作自动回滚。还有一个值得做的逻辑是订单超时取消。用户下单后如果一直不支付座位就会被白白占着。比较简单的实现方式是订单创建的时候记录创建时间写一个定时任务比如每五分钟扫描一次把创建超过十五分钟且状态还是待支付的订单取消并把余票加回去。Spring Boot里的Scheduled注解可以很容易实现这个定时任务这也是项目答辩中的一个加分点。4. 前端页面实现与接口对接4.1 Vue项目搭建与请求封装Vue前端工程建议用Vite来创建。相比老一代的Vue CLIVite启动速度快、依赖安装简洁、开发体验好很多。创建命令很简单npm create vitelatest flight-frontend -- --template vue创建完成后按需安装项目需要的依赖router、axios、element-plus这几个是必不可少的。npm install vue-router4 axios element-plus安装过程中经常有人踩的坑是npm下载太慢或者报各种奇怪的错因为依赖源的问题比较大淘宝镜像源实测下来效果最好。在项目根目录创建一个.npmrc文件写入registryhttps://registry.npmmirror.com重新安装一遍依赖问题基本就解决了。axios封装这块主要是对请求进行统一的拦截处理。所有请求自动带上token后端返回401的时候自动跳转到登录页。常见的封装方式如下const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config })baseURL设成/api之后开发阶段还需要配置Vite的代理转发把请求转给后端8088端口。这个配置在vite.config.js里完成。4.2 核心页面与路由权限控制前端页面通常需要做这几个视图首页或航班搜索页、注册登录页、航班列表页、下单确认页、订单管理页、个人中心页、后台管理页。航班搜索页是整个系统的门户。用户在这里选择出发城市、到达城市、出发日期点击搜索后跳转到航班列表页同时把搜索条件用query参数的方式带过去。航班列表页通过读取路由的query参数来调后端接口而不是把条件存在全局状态里。这样做的好处是刷新页面之后搜索条件还在用户可以直接复制搜索结果页的链接分享给别人。路由权限控制是Vue项目里比较关键的点。简单实现是在每个需要登录的路由配置上加上meta.requiresAuth标记然后在路由的beforeEach钩子里做判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })登录、注册页如果已经登录了也可以做个反向拦截直接跳回首页细节处理到位答辩时观感会好很多。4.3 航班列表与下单页的实现要点航班列表页是整个项目里信息密度最高的页面。每一行代表一个航班要展示的信息包括航班号、出发到达机场、起飞到达时间、舱位余票情况、价格、预订按钮。实操中我建议把起飞时间到到达时间用两个时间组件并排展示中间用箭头连接比上下两行显示更直观。下单页需要用户填写乘客信息。这里有一个实操中常见的体验问题机票订单的乘客姓名可以支持多个至少应该支持多个乘客同时下单。如果你在订单表里只存了一个乘客姓名做不了多人同时预订。更合理的做法是设计成订单主表和乘客子表一个订单对应多个乘客。这涉及到数据库设计如果已经做好了就是展示优势的点。下单页还要展示本次航班的完整信息、单价、总价、舱位类型让用户在下单前可以再次确认。确认无误后提交订单后端返回订单编号前端把页面跳转到支付页支付页提供模拟支付成功和模拟支付失败两个按钮对应调后端的支付接口。这种设计虽然模拟成分很高但是流程是完整的给老师演示的时候也比较有说服力。支付成功之后跳转订单详情页展示电子客票信息、乘客信息、航班信息。订单详情页的数据通过订单编号从后端接口获取页面里可以做一张PDF风格的虚拟机票卡片视觉上加分明显实操上就是一个带边框和阴影的卡片组件。5. 项目运行调试与常见问题排查5.1 数据库连接与初始化问题数据库相关的报错是整个项目启动阶段出现频率最高的一类问题。错误提示Access denied for user rootlocalhost通常是数据库密码错误检查application.yml里的username和password是否和本地MySQL一致。连接不上的时候先用Navicat或DataGrip命令行直接试一下能连就说明配置没问题把url里database的库名改成实际创建的库名就好。还有一类容易卡住的问题是SQL脚本导入失败。很多项目提供的.sql文件可能包含了额外的语句比如创建库的语句、外键约束、存储过程之类的。导入的时候要注意直接执行整个.sql文件可能因为字符集或者已有库冲突而报错。正确的做法是用CREATE DATABASE自己建好库、设置好字符集然后选择库、导入.sql文件。5.2 接口跨域与前端代理配置开发环境下前端跑在5173端口后端跑在8088端口两个端口不同就存在跨域问题。解决方案有两种。一种是在后端写一个WebMvcConfigurer配置类重写addCorsMappings方法允许所有来源的跨域请求Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true); } }另一种是通过Vite的代理转发。在前端vite.config.js里配置proxyserver: { proxy: { /api: { target: http://localhost:8088, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } }这两种方案都可行。如果是本项目提供的前端工程一般已经配好了代理只需要确认target端口和后端启动端口一致。很多同学改了后端端口却忘了改前端代理配置导致请求全部404排查半天找不到原因。跨域问题要明确一点它只是浏览器的限制用Postman测试接口是永远测不出来的。所以凡是遇到浏览器请求失败、Postman正常的情况优先怀疑跨域或代理配置。5.3 打包部署与静态资源问题项目做完要演示总不能一直开着两个窗口——一个后端黑窗口、一个npm run dev窗口。稳妥的做法是前端构建之后把生成的dist目录里的静态资源放到后端resources/static目录下这样直接启动后端项目就能在同一个端口访问所有功能打包成一个JAR包拿去演示非常方便。前端打包执行npm run build构建产物会生成在dist目录下。把dist里的文件复制到后端src/main/resources/static目录重新启动后端浏览器访问8088端口就能看到完整的前端页面。这一步经常遇到的问题有三个。第一前端打包后接口路径是相对路径 /api/xxx需要在后端配置里确保有对应的接口前缀或者前端构建后的请求地址拼上后端端口。如果前端用的代理proxy只在开发环境有效打包后要记得检查请求地址是否指向了后端实际地址。第二vue-router如果在项目里用了history模式打包后直接访问子路由页面会404因为服务端没有对应的路由回退配置。课设项目我建议直接用hash模式也就是创建router时createWebHashHistory这样打包后不会有刷新404的问题。第三字体和图片等静态资源路径处理。Element UI的字体文件、项目里自定义的图片打包后可能出现路径报错或404。如果遇到需要在vite.config.js里把assetsDir和base配置对通常base设为./就能解决大部分相对路径问题。5.4 订单与余票数据一致性问题讲一个比较隐蔽的坑。很多同学在测试下单功能的时候会用同一个账号反复下单同一趟航班订单表里堆了一堆待支付订单但余票并没有减少。这里的问题往往出在下单时没有做幂等处理也没有在事务内做余票扣减。解决思路是下单接口前先判断该用户对同一航班是否已有未支付订单如果有则直接提示先去支付或者取消原订单。此外在数据库层面扣减余票的SQL要带上条件比如UPDATE flight SET remain_seat remain_seat - 1 WHERE id ? AND remain_seat 0这样即使并发情况下两个请求同时下单数据库的原子操作也会保证数据不会扣到负数。5.5 时间与时区问题导致的bug航班表里的起飞和到达时间如果前端展示的时间和数据库里存的时间差了八小时那就是时区问题。解决方式是MySQL连接url里加上serverTimezoneAsia/Shanghai后端实体类里的时间字段用LocalDateTime类型。同时要注意如果前端Vue框架里做了时间格式化要确保格式化的时区也是东八区否则页面展示会差八个小时。这个问题在前后端联调时极易出现因为开发的时候大家都用本地时间感觉不到差异。一旦部署到云端服务器上服务器时区如果不是东八区整个航班列表的时间全部偏移乘客名单和订单日期也会跟着错乱。踩过一次坑后我现在在项目初始化时就会把时区问题一次性处理到位不给自己留排查时间。6. 文档撰写与答辩加分项6.1 课程设计文档的结构参考项目附带文档是这套源码里比较有价值的部分因为很多同学最不擅长的就是写文档。合理的课程设计文档结构大概按这种顺序来摘要或引言、需求分析、系统设计含架构图和数据库设计、详细设计核心功能模块说明、系统实现、系统测试、总结和参考文献。需求分析环节建议至少包含功能需求和非功能需求两部分。功能需求里把用户管理员两个角色的操作流程描述清楚非功能需求写系统性能、安全性、跨平台性等方面。系统设计环节的架构图建议画一张分层架构图把表现层Vue、业务层Spring Boot、数据层MySQL的关系画清楚。系统测试部分不仅仅是列出测试用例和结果最好能说清楚测试环境、测试工具、覆盖的核心场景以及修复了哪些bug。这部分写得充实能直接体现完整的开发流程。6.2 答辩时容易问到的几个知识点答辩阶段老师问的问题往往不在项目代码里而在项目背后的基础概念里。盘点几个这个项目最可能被问到的问题提前准备一下。Spring Boot的自动配置原理是什么这个问题在Spring Boot项目答辩中几乎必问核心回答是SpringBootApplication注解组合了EnableAutoConfiguration等注解启动时会扫描META-INF/spring.factories里配置的自动配置类然后根据条件注解按需装配Bean。JWT和传统Session的区别是什么JWT是无状态的token本身包含了用户信息和过期时间服务端不需要保存会话状态适合前后端分离的项目。Session则依赖服务端存储和管理。为什么使用MyBatis-Plus而不直接用MyBatisMyBatis-Plus对单表CRUD做了封装不用写XML就能完成大部分数据操作开发效率更高同时保留了XML自定义查询的能力来处理复杂SQL。不过需要说明的是MyBatis-Plus针对的是单表操作多表关联查询的能力弱一些。余额扣减和订单创建的原子性如何保证用数据库事务让扣减余票和创建订单在同一次数据库连接中完成防止中间过程出错导致数据不一致。6.3 让项目看起来更完整的几个小优化项目做完基本功能之后有几个小的优化项投入产出比非常高能让整体观感上一个档次。第一个是给用户模块加一个简单的个人中心页面展示基本信息支持修改密码。实现成本很低但让项目功能闭环感更强。第二个是后台管理页面加一个简单的数据统计区域用ECharts做一个柱状图展示最近一周的订单量变化或者做一个饼图展示各航线订单占比。ECharts接入成本很低几分钟就能画出一张图视觉上非常加分。第三个是系统初始数据要造得好看。不要只有两三趟航班至少准备十几条航线、几十个航班的数据覆盖各种早中晚时段和不同价格区间。你演示搜索功能的时候如果搜完结果只有一行话整体效果会很差。准备数据这件事从数据库设计完后就可以开始做边做边调。7. 写在最后的一点个人体会航空机票预定系统这个题目的含金量在于它覆盖了Java全栈开发的核心链路数据库建模、后端接口、前端页面、前后端联调、打包部署、文档撰写。做这一个项目练下来基本等于把大学期间后端相关的知识点全部串起来了。如果时间允许建议大家在拿到源码之后不要直接跑起来就完事而是自己从头到尾把工程建一遍、把核心表结构设计一遍、把下单流程完完整整走一遍哪怕报错也没关系。这个过程中遭遇的问题、解决的思路就是你答辩时最真实的底气。我自己带过不少做这类课设的学生发现最容易拉分的并不是某个功能写得多炫而是对整个系统能否讲得通透。很多时候代码是别人的、数据库是别人的但如果时间紧张必须借助参考项目也强烈建议至少把核心代码逐行读懂把数据库每一张表、每个字段的含义搞清楚。答辩时被问到如何设计表结构你能从头讲清楚每张表为什么存在、各自关联关系是什么这个项目就算真正吃透了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价