每年到了毕业设计选题的时候,总有一批人围着"管理系统"打转:图书管理、酒店管理、班级管理……不是说这些题不能做,而是做出来基本都是同一套增删改查,答辩时老师一眼就能看穿工作量。相比之下,基于Spring Boot的机票预定系统是个很值得认真对待的题目——它看起来也是"管理数据",实际上是一个带库存约束、带支付回调、带订单状态流转的交易系统,能把你大学四年攒下的后端基础、数据库设计和工程化能力完整地串起来。这篇我打算把整个选题从拆业务、建表、选型到源码讲解、论文写作和答辩准备的全部细节摊开讲,适合正在选毕设题目、或者已经选了机票预定系统但不知道从哪下手的同学。
1. 为什么我建议选"机票预定系统"这个题目
1.1 看上去是CRUD,其实是一套有业务规则的交易系统
很多人第一次看到"机票预定系统"会觉得:不就是航班增删改查、订单增删改查吗?这么想就把它看小了。
图书管理、班级管理这类系统属于典型的"记录型系统",核心操作就是数据的增删改查,业务规则比较薄。机票预定不一样,它背后有真实的商业约束:每个航班每个舱位的库存是有限的,用户提交订单时要实时扣减座位;订单不是创建出来就结束了,它要在待支付、已支付、已出票、已取消、已退票这些状态之间按规则流转;支付回调可能重复到达,你不能因为回调来了两次就给同一个订单出两次票。
这些规则意味着你在设计时不能只考虑"怎么把数据存进去",还要考虑"数据之间的一致性怎么保证""并发下会不会出错"。这样一套业务逻辑做下来,项目的深度就和普通管理系统拉开了明显差距。答辩的时候,老师也更容易从你的项目里找到技术点去问,而问出来的东西恰好是你能讲清楚的东西。
1.2 一个题目能覆盖前后端分离的全部关键环节
选题选得好不好,核心看它能不能覆盖足够多的技术点,同时每个技术点又在合理的复杂度范围内。
机票预定系统这套业务天然适合做成前后端分离:前端用Vue负责页面渲染和交互,后端用Spring Boot提供RESTful API,中间通过JSON交换数据。相比传统的Thymeleaf模板渲染方案,前后端分离能把"接口设计""跨域处理""Token鉴权"这些实际开发中高频遇到的概念全部带出来。
说完技术覆盖,再说说数据库设计。机票预定系统的核心表至少有用户表、航班表、舱位表、订单表、乘客表、支付流水表六张,表与表之间有清晰的关联关系。这比单表单库的管理系统更能体现数据库设计能力——你在论文里可以画ER图、写表结构说明、讲索引设计思路,这些都是实实在在的篇幅和亮点。
1.3 什么样的人适合选这个题目
我的判断标准很简单:想真正通过毕设学到东西、并且愿意花时间把系统做完整的人。
如果你是Java方向,Spring Boot是必选项,那就直接选这个。它上手门槛不算高,Spring Boot本身把大量配置自动化了,你不需要像读Spring源码那样痛苦。但想把它做好,你又必须懂事务、懂SQL、懂状态流转,这些恰恰是面试时最常被追问的内容。
如果你技术基础比较薄弱,也完全可以选择。项目里复杂的部分可以分模块攻坚——先用MyBatis-Plus把基础CRUD做出来,再慢慢补库存扣减、支付回调这些硬骨头。反过来,如果你的目标是拿高分,这个题目也有足够的向上空间:加Redis缓存热门航线、加消息队列处理出票通知、加ElasticSearch做航班搜索,都是可以往文档里写、往答辩里讲的加分项。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先拆业务再建表:核心模块与数据库设计
2.1 业务模块先画清楚:前台、后台、会员、支付
设计数据库之前,一定要先把业务模块拆明白。我建议你第一件事不是打开Navicat建表,而是拿张纸把系统分成两块:前台用户能干什么,后台管理员能干什么。
前台用户的操作流是一条很清晰的主线:注册登录、搜索航班、选择航班和舱位、填写乘机人信息、提交订单、支付、查看订单详情、申请退票。这条链路从用户进入系统到完成交易,每一步都有明确的输入输出。
后台管理员的操作集中在运营侧:管理航班信息(增加班次、调整时间、停飞)、管理舱位和票价、查看订单列表、处理出票、查看基础统计报表。
模块拆清楚后,你会发现每个模块背后都对应着一组表和一组接口。而且这些模块之间的关系是有层次的:用户和订单有关,订单和航班有关,订单和乘客有关,支付流水和订单有关。这种层次感很容易写成论文里的系统功能结构图或者模块设计说明。
2.2 数据库表设计:六张核心表与关键字段
业务模块定下来之后,表结构就顺理成章了。这里我直接给出一套适合毕设的完整表设计,你可以照着建。
| 表名 | 核心字段 | 说明 |
|---|---|---|
| user | id, username, password, phone, email, real_name, id_card, level, status | 用户表,密码建议存BCrypt加密后的密文 |
| flight | id, flight_no, origin, destination, departure_time, arrival_time, aircraft_type, base_price, status | 航班表,base_price存基础票价,实际售价按舱位折扣计算 |
| flight_seat_type | id, flight_id, seat_type, discount_rate, stock, price | 舱位表,同一个航班拆成经济舱、公务舱、头等舱等多个库存记录 |
order |
id, order_no, user_id, flight_id, seat_type_id, total_price, status, create_time, pay_time, expire_time | 订单表,order_no加唯一索引,status存状态码 |
| order_passenger | id, order_id, name, id_card, phone | 订单乘客表,一个订单可以对应多个乘机人 |
| payment_record | id, pay_no, order_id, user_id, amount, pay_channel, status, callback_time, callback_content | 支付流水表,记录每次支付请求和回调结果 |
这套设计的核心思想是"一主一从一流水":订单表是业务主表,订单乘客表是从表,支付流水表记录交易痕迹。三个表配合起来,能覆盖一个订单从创建到支付再到出票的完整生命周期。
关于航班表和舱位表的关系,我要多说一句。很多学生图省事,直接在航班表里放一个"剩余票数"字段,这样确实简单,但一旦需要区分经济舱、公务舱就麻烦了。拆出独立的舱位表之后,一个航班对应多条舱位记录,每条记录有自己的折扣、库存和最终票价,扩展性明显更好。论文里可以解释这种设计是"为了支持同一航班多舱位销售模型",这句话写在设计说明里是很加分的。
2.3 订单状态的流转设计:别把状态写成散沙
订单状态是机票预定系统里最容易设计混乱的地方。我见过不少人的方案是订单里放一个status字段,然后项目里到处都是散落的"如果状态等于3就怎么怎么样"这种代码,最后自己都分不清3代表什么。
我的建议是先在文档里把状态机和流转规则定死,再动手写代码。一套合理的状态设计是:
- 0 待支付:订单创建成功,座位已锁定,等待用户支付
- 1 已支付:用户完成支付,等待出票
- 2 已出票:系统出票完成,交易完成
- 3 已取消:订单在支付前被用户取消或超时未支付
- 4 已退票:已支付订单申请退票退款
状态之间的跳转关系必须明确。待支付可以走到已支付,也可以走到已取消;已支付可以走到已出票,也可以走到已退票;但待支付绝对不允许直接跳到已出票。这个规则看着简单,写代码时却是最容易被忽略的。
实现时我推荐用状态条件更新来防跳转,比如把订单从未支付改成已支付时,用类似 UPDATE \order` SET status = 1 WHERE id = ? AND status = 0` 的SQL,如果影响行数是0,说明订单状态已经不是待支付了,本次操作应该终止。这样即使并发情况下有两条请求同时处理同一个订单,也只有一条能成功。用这种写法,你在答辩时能讲出一个"如何防止订单状态被错误流转"的完整思路。
2.4 价格计算逻辑:最容易被忽略的细节
机票价格不是简单地在航班表里存一个"票价"就完事的。真实场景下,同一个航班的经济舱和公务舱价格不同,提前购票的折扣也不同,还需要额外计算机建燃油费。
一个简单但合理的价格模型可以这样设计:航班的base_price是基础票价,舱位表里的discount_rate是折扣系数,那么该舱位的票价就是 base_price * discount_rate;总价则在票价基础上再加机建燃油费。机建燃油费可以单独存一个配置表,或者简单一点,在航班表里加一个fuel_fee字段,不同航线的基建燃油费用不同。
计算时有一个硬性要求:金额必须用BigDecimal,不能用double或float。浮点数在计算金额时会出现精度丢失问题,比如 0.1 + 0.2 的结果不是精确的0.3,这在支付场景下是绝对不能接受的。这个细节你在论文测试章节可以专门写一笔"金额计算采用BigDecimal保证精度",答辩时也是一句话就能说清的规范点。
3. 技术选型要稳:这份组合方案直接照抄
3.1 推荐技术栈与版本组合
毕设选题最忌讳的就是在技术选型上追新。我的建议是,除非导师明确要求,否则就用最稳、资料最全、你最容易查得到解决方案的组合。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 8 | 兼容性最好,几乎不会遇到环境问题 |
| Spring Boot | 2.7.x | 教程最多、踩坑记录最全,毕设首选 |
| MyBatis-Plus | 3.5.x | 内置CRUD方法,同时支持自定义SQL |
| MySQL | 5.7 或 8.0 | 两者都可以,8.0默认字符集更友好 |
| Vue | 2.7 或 3.x | 看你熟悉哪种,2.7企业项目存量最大 |
| Element UI / Element Plus | 对应版本 | 做管理后台的表格和表单非常快 |
| Maven | 3.6+ | 构建工具,版本别太老就行 |
这个组合不是我拍脑袋定的,而是我实际辅导多个学生做毕设后验证下来的稳定组合。它最大的好处是:你遇到的绝大多数问题,在中文技术社区里都能搜到现成答案。
3.2 Spring Boot版本怎么选:不要盲目追新
写这段想特别提醒一下想上Spring Boot 3.x的同学。Spring Boot 3.0是个大版本升级,它强制要求JDK 17以上,同时把包名从javax换成了jakarta,这意味着网上大量基于Spring Boot 2.x的示例代码,直接复制过来连编译都过不了。比如javax.servlet.http.HttpServletRequest,在3.x里要换成jakarta.servlet.http.HttpServletRequest。
毕设的核心目标是稳定出活。如果你选JDK 8加Spring Boot 2.7.x,几乎所有的开源组件都能平滑集成,网上资料也多是这一套。如果你的开发机本身就装了JDK 17,那用Spring Boot 3.x也不是不行,但你要有心理准备,排查问题时会多花不少时间。
顺带提一个和版本无关的知识点:Spring Boot为什么能"开箱即用"?因为启动类上的@SpringBootApplication注解里包含了@EnableAutoConfiguration,它会通过自动装配机制去加载spring-boot-autoconfigure目录下的默认配置。你引入一个依赖,Spring Boot发现classpath里有对应类,就会自动帮你配好相关Bean。这个原理在答辩里被问到的概率极高,建议你把"自动装配"四个字理解透了再进答辩教室。
3.3 ORM选型:为什么MyBatis-Plus更适合毕设
在Spring Boot项目里操作数据库,主流选择是Spring Data JPA、MyBatis和MyBatis-Plus。我的建议是写毕设用MyBatis-Plus,原因是它正好卡在"省事"和"可控"之间。
MyBatis-Plus继承了MyBatis的SQL掌控能力,同时又内置了selectById、insert、updateById这些常用方法,连基本的CRUD SQL都不用写。它还有条件构造器,查询条件可以用Lambda表达式拼,比如new LambdaQueryWrapper<Flight>().eq(Flight::getOrigin, "北京"),写起来很直白,不容易因为SQL拼接出错。
分页是另一个容易踩坑的地方。MyBatis-Plus的分页需要单独配置PaginationInnerInterceptor插件,不配的话调用分页方法虽然不报错,但结果不会真正分页。这个坑几乎每个月都有人踩,如果你的项目用了MyBatis-Plus,记得在配置类里把分页拦截器加上。
3.4 前后端分离的协作细节:API、跨域、Token
前后端分离之后,前后端就只通过HTTP接口通信。这意味着你要约定一套统一的接口风格。我的建议是做一个统一的返回体,比如Result类,包含code、msg、data三个字段,所有接口都返回这个结构。这样前端拿数据时可以直接按固定格式解析,后端加异常处理时也统一。
Controller接口示例很简单:
java复制@RestController
@RequestMapping("/api/flight")
public class FlightController {
@GetMapping("/search")
public Result<List<FlightVO>> search(
@RequestParam String origin,
@RequestParam String destination,
@RequestParam String date) {
return Result.ok(flightService.searchFlights(origin, destination, date));
}
}
接口定好之后,接下来要考虑两个细节:跨域和鉴权。
跨域的意思是,前端跑在8080端口,后端跑在8081端口,浏览器会认为这两个来源不同,直接请求会被拦截。解决方式有几种,最简单的就是后端写一个CORS配置类,把允许的来源和请求头放开。答辩被问到"跨域怎么解决的",你至少要能说出CORS配置的关键词。
鉴权这块,我建议用JWT而不是Session。JWT是一串自包含的Token,用户登录成功后由后端签发,前端拿到之后存在localStorage里,每次请求时在请求头加上Authorization: Bearer <token>。后端通过一个拦截器校验Token的有效性,解析出用户信息再放行。相比Session方案,JWT不需要在后端保存会话状态,天然适合前后端分离的架构。
4. 把拉开分差的硬核点做扎实:库存、事务、幂等、鉴权
4.1 超卖问题:库存扣减的正确姿势
机票预定系统里最经典的问题就是超卖。想象一下:某个航班经济舱只剩最后一个座位,用户A和用户B同时提交订单。如果代码是这样写的——先从数据库查出库存是1,判断大于0,然后执行库存减1——那这两个请求可能都通过了判断,各自以为买到了最后一个座位,实际上库存已经变成-1了。
解决超卖问题,核心思路是让"判断库存是否充足"和"扣减库存"这两个操作变成原子操作。最简单的写法就是一条SQL条件更新:
sql复制UPDATE flight_seat_type
SET stock = stock - 1
WHERE id = #{seatTypeId} AND stock > 0;
这条SQL的意思是:只有当库存大于0的时候才执行减1操作,并且数据库会保证同一时刻只有一条这样的更新能成功。我们通过int rows = mapper.decreaseStock(seatTypeId, 1)拿到受影响行数,如果rows等于0,就说明库存不够,直接抛出业务异常,订单创建失败。
这是三层递进里的第一层,也是最推荐毕设使用的方案。如果你的项目想做得更漂亮一点,可以在此基础上加乐观锁版本号,用一个version字段配合UPDATE ... SET stock = stock - 1, version = version + 1 WHERE id = ? AND version = ?来实现。万一更新失败就重新查版本号再来一次。这种方案在文档里可以作为"系统并发优化"的进阶描述,但实现的时候第一层已经够用。
4.2 事务边界:下单方法里到底该放哪些操作
机票预定系统的下单流程涉及多个写操作:扣减库存、创建订单、生成支付流水。这些操作必须保证"要么全部成功,要么全部失败",任何一个环节出错,前面已经改掉的数据都要回滚,否则就会出现扣了库存但没生成订单的脏数据。
这时就要用Spring的声明式事务,在Service方法上标注@Transactional注解。有一个细节必须注意:最好写成@Transactional(rollbackFor = Exception.class),因为Spring默认只对RuntimeException回滚,如果你抛的是受检异常而不指定rollbackFor,事务不会回滚。
一个典型的下单Service核心方法是这样的:
java复制@Transactional(rollbackFor = Exception.class)
public OrderCreateResult createOrder(OrderCreateDTO dto) {
// 1. 校验用户、航班、舱位是否有效
// 2. 原子扣减库存,失败则抛异常
int rows = flightSeatTypeMapper.decreaseStock(dto.getSeatTypeId(), 1);
if (rows == 0) {
throw new BizException("该舱位余票不足");
}
// 3. 创建一个待支付订单
Order order = buildOrder(dto);
orderMapper.insert(order);
return OrderCreateResult.of(order);
}
事务的边界不要划得太大。有个典型的反面案例是把"支付回调"这种外部操作也放在同一个事务里——外部接口调用可能长时间不返回,事务会一直占着数据库连接,并发一高系统就卡死了。下单事务里只做数据库本地操作,支付相关的动作放到回调接口里单独处理。
还有一个小坑:事务方法在同一个类里被其他方法直接调用时会失效,这是因为Spring的事务基于代理,同类调用不会经过代理对象。解决办法是把调用的方法放到另一个Service里,或者注入自身再调用。这个点如果你能在答辩时主动讲出来,老师对你的工程经验会很认可。
4.3 支付回调的幂等处理:同一个通知进来三次也不能重复出票
真实支付场景中,支付平台的回调通知并不保证只发送一次,可能因为网络重试反复推送。如果你的回调接口里"查到订单就改成已支付、已出票",那同一个订单被回调两次,就会出两次票,这是严重的生产事故。
解决思路叫"幂等处理"。核心做法是:在回调处理开头先根据订单号查一次订单状态,如果已经是已支付或已出票,直接返回成功,不再做任何更新操作。更稳妥的方式是再用状态条件更新写一次,比如:
sql复制UPDATE `order`
SET status = 1, pay_time = NOW()
WHERE id = #{orderId} AND status = 0;
只有影响行数为1时,才说明订单确实是从待支付状态变成已支付状态,本次回调才真正需要做出票动作。这样不管回调推几次,业务上只处理一次。
在数据库层面还可以给支付流水表的order_id加唯一索引,保证同一笔订单只可能有一条成功的支付流水记录。这样数据库、业务逻辑双重保障,答辩时你就可以理直气壮地说"我的系统做了幂等处理"。
4.4 JWT鉴权:把"未登录"挡在Controller之外
前后端分离后,后端不能再用Session自动维护登录状态了,一般会采用JWT。它的工作流程是:用户登录成功后,后端把用户标识、过期时间等信息签名生成一串Token返回给前端;前端后续请求都带上这个Token;后端写一个拦截器,对需要登录的接口先解析Token。
关于密码存储,一个常见的低级错误是明文存数据库。正确做法是使用BCrypt加密,BCryptPasswordEncoder在Spring Security里可以顺手引入,没有引入Spring Security也可以单独用 spring-security-crypto 这个依赖。BCrypt的特点是同一个密码每次加密结果都不同,所以不能用"加密结果相等"来判断密码一致,要用它提供的matches方法校验。
拦截器的实现逻辑不复杂:先判断请求路径是否需要拦截,需要的话从请求头里取Token,解析不出来或者过期就返回401,让前端跳回登录页。解析成功后,把用户信息放到ThreadLocal或者Request属性里,后面的Controller就可以直接取用户名了。
一个实用的提醒:登录接口本身、注册接口、航班搜索接口这些应该放行,不需要鉴权。但下单、查看订单、退票这类接口必须登录后才能访问。接口的放行规则要梳理清楚,不然会出现"没登录也能下单"这种让人尴尬的漏洞。
5. 源码、文档与代码讲解:一条线贯穿到答辩
5.1 拿到源码后,按什么顺序读
很多同学拿到一套毕设源码后,习惯从第一个类开始逐行读,结果读了两天还在启动类附近打转,越读越没信心。我建议换个顺序,按"从外到内、从主链路到分支链路"的方式去读。
第一步,看 pom.xml,搞清楚项目引了哪些依赖。看到spring-boot-starter-web、mybatis-plus-boot-starter、jjwt、mysql-connector这些,基本就知道技术栈是什么了。第二步,看 application.yml,了解端口、数据库、控制台日志是怎么配置的。第三步,看数据库初始化脚本,对照第二章第2.2节里的表结构,先认识每张表是干什么的。第四步,看实体类和Mapper接口,这时你会发现字段和数据库表是一一对应的。第五步,进入Service层和Controller层,把每个模块的增删改查方法串起来。
读代码时有一个诀窍:不要按类读,要按业务场景读。比如挑"用户搜索航班"这个场景,从前端页面那个搜索按钮出发,找到对应的JS方法、API调用地址、后端Controller方法、Service方法、Mapper方法,一路读下去。一条链路读通了,整个项目的运行方式就懂了一半。
5.2 能讲清楚一条业务链路比背代码重要
答辩时最忌讳的就是背代码。"这块是查询航班信息的,用了MyBatis-Plus的selectList"这种话没有信息量,老师听完等于没听。
我建议你准备两条核心链路,反复讲给室友听,直到能不看代码讲明白。
第一条是"航班搜索链路":用户在前端页面输入出发地、目的地、日期,点击搜索后前端通过axios发起GET请求,后端Controller接收参数后调用Service查询数据库,把符合条件的航班列表封装成一个统一返回体返回,前端拿到数据后渲染到表格里。这条链路能展示你对前后端数据交互的理解。
第二条是"下单支付链路":用户选择航班和舱位、填写乘机人后提交订单,后端在一个事务里完成库存扣减、订单创建、支付流水生成,返回待支付订单号;用户点击模拟支付后,前端调用支付接口,后端完成支付状态更新和出票。这条链路能展示你对事务、状态流转、业务规则的整体把握。
讲解的时候要自然带上"为什么"。比如讲到库存扣减,你可以说"这里我没有用先查再改,而是直接用条件更新SQL,是怕并发下超卖",这种带着设计思考的讲解,比背十行代码有用得多。
5.3 文档报告:每个章节该写什么,怎么写
论文文档一般按六章结构走:绪论、需求分析、系统设计、系统实现、系统测试、总结展望。我告诉你每章的重点在哪里。
绪论部分要交代背景和意义,但不要写"随着互联网的发展"这种空话。你可以从"民航旅客量逐年增长、线上购票成为主流方式"切入,引出"设计一个基于Spring Boot的前后端分离机票预定系统"的具体目标。
需求分析章节最重要的是用例图,要把前台用户、后台管理员两类角色能做的所有操作画清楚。文字部分对应写功能需求列表,比如"用户可以按出发地、目的地、日期搜索航班""用户可以在订单支付前取消订单"等,每一条都对应后面系统设计中的一个模块。
系统设计章节是重头戏,包括总体架构图、功能模块划分、数据库ER图、关键接口设计。这一章篇幅最足,要舍得放图,ER图画清楚,表结构说明写完整。
系统实现章节不要变成代码粘贴本。每实现一个功能模块,先用一两段话说明实现思路,然后贴一段核心代码,配一张运行截图。比如实现下单模块,就贴库存扣减和创建订单的方法,再贴一张订单列表截图。
系统测试章节要写测试用例表。格式一般是"编号、测试项、操作步骤、预期结果、实际结果",挑十来个主要功能写进去,比如正常搜索、无结果搜索、库存不足下单、重复支付回调等,这样整章看起来非常扎实。
5.4 答辩高频问题与应对思路
答辩老师基本不看你代码,他们会通过几个问题快速判断你是不是真的把系统做明白了。结合机票预定场景,有几个问题出现的概率非常高,我提前帮你理一下思路。
"你项目中有几张表?表关系是怎样的?"这个问题要能够不看文档就答出来,每张表的核心字段和主外键关系都要心里有数。
"库存是怎么做到不超卖的?"这是在问并发控制。把4.1节讲的原子更新SQL讲清楚,再说一下为什么不用先查再改,基本就能过关。
"如果并发量非常大,你的方案会有什么瓶颈?"这是在考验你的扩展思路。你可以说当前方案依赖数据库的行锁,并发再高时可以引入Redis预扣库存、通过消息队列异步处理订单和出票。重点不是你真的实现了,而是思路要清晰。
"为什么用JWT不用Session?"要答出"前后端分离项目,Session跨域不好维护,JWT无状态、不占用服务端内存"这几个关键点。
"项目里用了哪些设计模式?"建议至少准备两个,比如Service层用模板方法统一订单处理流程、用策略模式处理不同舱位的价格计算。不一定要真的写得多优雅,但要能用自己的话说清模式解决的是什么问题。
回答所有问题的总原则是:不要背概念,要结合你代码里的真实实现来讲。哪怕答案朴素一点,只要是自己亲手做的,老师能感受到。
5.5 现场演示的细节准备
最后提醒演示环节的细节。提前准备一份有真实感的数据:北京到上海、广州到深圳、成都到重庆几条热门航线,每个航班配上经济舱、公务舱两种舱位,库存别全部填满,留几个接近没票的航班,方便演示"余票不足"的报错场景。
演示前把你的启动顺序理清楚:启动MySQL、启动Spring Boot后端、启动Vue前端。如果有端口冲突或者数据库连不上的情况,提前在本地把环境跑顺。浏览器里把前端页面开好,不要现场敲命令。
演示的流程按主链路走:注册一个新账号,登录,搜索航班,选一个航班下单,模拟支付,查看订单状态变为已出票,再演示一次取消订单,最后切到管理员账号看看订单管理页面。全程控制在十分钟左右,流畅、完整,比讲一堆细节更有效果。
还有一个容易被忽略的操作:演示前准备一份重置数据的SQL脚本。如果演示过程中不小心把数据弄乱了,一键恢复测试数据,比现场手忙脚乱地改数据库靠谱得多。
我个人在这类项目上带过不少学生,发现真正拉开成绩差距的从来不是题目本身,而是你有没有把一套系统的完整链路想明白。机票预定系统这个题,数据库表关系清晰,业务规则足够复杂,技术选型又稳妥,非常适合作为一次完整软件工程训练的载体。你把这套东西从头到尾做下来,文档和代码讲解都按上面的思路准备,到答辩时心里是有底的。最后再补一句:源码可以借鉴,但一定要把每一段关键代码为什么这么写讲到七七八八,这个项目才算真正属于你。
