ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于Java的机场航班起降与协调管理系统毕业设计全解析

基于Java的机场航班起降与协调管理系统毕业设计全解析

简介:基于Java的机场航班起降与协调管理系统毕业设计完整资料包,面向计算机专业毕业生、Java Web学习者及需要快速搭建课设项目的人群。资源围绕航班调度与协调管理场景,涵盖需求分析、系统架构设计、编码实现、测试优化及答辩展示全流程,适合用于毕业设计参考或项目实战练手。压缩包仅84.93MB,包含项目报告、答辩PPT、源代码、数据库脚本、系统截图及部署演示视频等核心内容,虽未统计具体文件数量,但各类型资料分工明确:源代码支撑二次开发,数据库文件用于环境还原,部署视频辅助复现运行过程。目前已有495人学习浏览,实用性得到初步验证。通过该项目,读者可系统掌握Java Web开发中的MVC模式、Servlet与JSP交互、Spring及MyBatis框架应用、关系型数据库设计等关键技能,同时学习如何撰写规范的技术文档、制作答辩材料并完成项目部署,对提升工程实践与求职竞争力有直接帮助。

1. 机场航班起降与协调管理系统:毕设选题的价值与含金量

基于 Java 的机场航班起降与协调管理系统毕业设计,听起来像是一堆增删改查,但真正的难点集中在两个地方:跑道的时间冲突和停机位的实时释放。我见过不少同学把航班表、跑道表、停机位表建好就以为完事了,等到做调度功能时才发现,同一个跑道在相近时间被分配给两个航班,系统也照单全收。这套系统的价值在于让“协调”两个字落地——用代码约束同一资源在同一时刻只能给一个航班使用。标题里的这个毕设包,包含了项目报告、答辩 PPT、源代码、数据库、截图和部署视频,这提醒你交付的不只是一份能跑的代码,而是一套完整的项目叙事:需求怎么拆、表怎么建、算法怎么写、文档怎么组织、答辩怎么演示。适合想用 Spring Boot 方向练手、又希望选题有业务深度的同学,也适合想在答辩时讲出“冲突检测”这种真实业务难点的人。

2. 系统架构与数据模型:把起降协调拆成表、状态机和角色

整体架构我一般建议用 Spring Boot + MyBatis + MySQL + Thymeleaf,前端用服务端渲染,不搞前后端分离。理由很实际:Spring Boot 内嵌 Tomcat,打包成一个 jar 就能在其他机器上跑起来,答辩演示时少很多环境坑;MyBatis 把 SQL 写在 XML 里,老师一问“这个冲突检测怎么实现的”,你可以直接展示 SQL 和 Java 代码,比 JPA 自动生成的逻辑更好讲清楚;MySQL 则是最稳妥的毕设数据库,导出 sql 文件就能复现整个项目结构。下面先把数据模型立住,再谈实现。

2.1 角色与用例:机场人员如何操作系统

系统里我设计了三种角色,对应真实机场中的不同岗位。系统管理员维护用户、跑道、停机位等基础数据;机场调度员是核心使用者,负责创建航班计划、分配跑道和停机位、处理延误和备降;查询员只能查看航班动态,不能修改任何数据。角色划分越清晰,后期的权限控制和页面按钮显示就越容易做,答辩时也可以顺手提一句“不同角色看到的菜单不同”。

用例上,调度员的主要操作包括:新增航班计划、查询当天起降航班、为航班分配跑道、为航班分配停机位、处理延误/备降/取消、查看协调记录。系统管理员还有用户管理和基础数据管理。这些用例基本对应了你项目中需要开发的页面和接口,建议在项目报告的用例图里全部画出来,答辩时用一张图说明功能范围。

2.2 核心数据表设计:航班、跑道、停机位、调度单

航班表是核心中的核心,字段要能支撑起降和协调两个关键词。我这里给出一份可以直接用的建表 SQL,你可以在自己的数据库里执行。

CREATE TABLE flight ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flight_no VARCHAR(10) NOT NULL COMMENT '航班号', aircraft_type VARCHAR(20) NOT NULL COMMENT '机型', origin VARCHAR(30) NULL COMMENT '出发城市', destination VARCHAR(30) NULL COMMENT '到达城市', type TINYINT NOT NULL COMMENT '0=起飞 1=降落', plan_time DATETIME NOT NULL COMMENT '计划起降时间', actual_time DATETIME NULL COMMENT '实际起降时间', status TINYINT NOT NULL DEFAULT 0 COMMENT '0=计划 1=滑行 2=完成 3=延误 4=取消', runway_id BIGINT NULL COMMENT '当前占用跑道ID', gate_id BIGINT NULL COMMENT '当前停机位ID', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_plan_time (plan_time), KEY idx_status (status) ) COMMENT='航班表';

这里有意把时间字段全部用 DATETIME,而不是 VARCHAR。理由是后续排序、区间冲突检测都依赖时间比较,字符串排序会出现“2024-06-02 09:00”排在“2024-06-01 12:00”前面的错误。runway_id 和 gate_id 是逻辑外键,后面会通过业务代码维护,不建议在这里强行加物理外键,因为删除航班时会有一堆关联约束,反而增加开发负担。跑道表记录跑道编号、长度、状态;停机位表还应该有机型容量字段,用来匹配大飞机和小飞机。真正的“协调”记录则放到单独的 schedule_record 表里,记录跑道分配、停机位分配、时间变更原因和时间。

2.3 航班生命周期状态机:从计划到起降

航班不是一条静态记录,它有一个从计划到完成的状态流转过程。状态机的设计直接影响后面的业务逻辑复杂度。我习惯用一张状态表来约束流转关系。

状态码名称含义后续动作
0计划航班已创建,未开始滑行分配跑道和停机位
1滑行飞机推出或落地后滑跑中跑道占用锁定
2完成已起飞离场或已落地到位释放跑道和停机位
3延误计划时间变更重新分配时间窗
4取消航班取消释放已分配资源

状态流转规则是:计划 -> 滑行 -> 完成,这是主链;计划可以跳转到延误或取消;延误处理完可以回到计划。在代码里不要在 Controller 中直接改状态,而是抽取一个changeFlightStatus方法统一处理,凡是进入“完成”或“取消”状态,都要自动释放该航班占用的跑道和停机位。这样能避免“航班状态都取消了,跑道还显示占用”这种低级问题。

2.4 技术选型对比:Spring Boot + MyBatis 为什么够用

有些同学纠结要不要用 Spring Cloud,或者把系统做成分布式。对一个毕业设计来说,单机 Spring Boot 完全够用,而且更好答辩。项目里的“协调”更多是业务层的时间窗冲突检测,不是高并发场景,不需要引入消息队列和分布式锁。MyBatis 相比 JPA 的优势在于可控:跑道冲突的 SQL 可以直接写在 XML 里调试,观察执行计划,老师问细节你也答得上来。前端选 Thymeleaf 是因为它能和 Spring Boot 无缝集成,页面数据渲染由后端完成,部署时不需要单独起 Node 服务。如果你一定要用 Vue,也能做,但需要额外处理跨域和静态资源分离,建议把精力留给核心调度算法。

3. 核心业务实现:CRUD、跑道冲突检测与停机位分配

数据模型建好后,接下来就是写业务代码。这一章挑选三个最关键的部分详细展开:航班管理、跑道冲突检测和停机位分配。航班管理是基础 CRUD,跑道冲突检测是整个系统的灵魂,停机位分配则考验你对业务规则的理解。坐标管理系统里真正的“协调”,就是这几个模块之间如何保证资源不被重复分配。

3.1 航班管理:CRUD 与批量导入

先给一个最简 Controller 骨架,展示如何组织接口。

@RestController @RequestMapping("/api/flight") public class FlightController { @Autowired private FlightService flightService; @PostMapping public Result create(@RequestBody FlightCreateDTO dto) { flightService.create(dto); return Result.success(); } @GetMapping("/list") public Result list(@RequestParam(required = false) Integer type, @RequestParam(required = false) String flightNo) { return Result.success(flightService.list(type, flightNo)); } @PutMapping("/{id}/status") public Result changeStatus(@PathVariable Long id, @RequestParam Integer status) { flightService.changeStatus(id, status); return Result.success(); } }

FlightCreateDTO是入参对象,里面只放前端传来的字段,避免把实体类直接暴露给前端。create 方法里需要校验两件事:航班号不能重复,plan_time 不能早于当前时间。批量导入时,我一般用 POI 读取 Excel,逐行解析成 Flight 对象,然后批量插入数据库,中间任何一行失败都要回滚整批数据,否则会出现导入一半成功一半失败的情况。这个逻辑可以在 Service 层加@Transactional注解。

3.2 跑道分配:时间窗冲突检测算法

跑道分配本质上是一个时间区间重叠判断问题。假设起飞航班需要占用跑道 15 分钟,降落航班需要占用 10 分钟,那么两个航班的时间窗只要重叠,就不能共用同一条跑道。区间重叠的 Java 判断代码很简单。

public boolean isOverlap(LocalDateTime start1, LocalDateTime end1, LocalDateTime start2, LocalDateTime end2) { return start1.isBefore(end2) && start2.isBefore(end1); }

这段代码用的是左闭右开区间 [start, end)。意思是如果前一个航班在 10:00 结束,后一个航班在 10:00 开始,这两个航班不冲突;只有后一个航班开始时间早于前一个结束时间,才判定为重叠。分配跑道时,先把所有已占用的跑道时间段查出来,过滤掉有空闲的跑道,选第一条空闲的分配给航班。

public synchronized AssignResult assignRunway(FlightApply apply) { List<RunwayOccupy> occupies = runwayOccupyMapper .findOverlap(apply.getStartTime(), apply.getEndTime()); List<Long> freeRunways = runwayMapper.findAll() .stream().filter(r -> occupies.stream() .noneMatch(o -> o.getRunwayId().equals(r.getId()))) .map(Runway::getId).collect(Collectors.toList()); if (freeRunways.isEmpty()) { return AssignResult.fail("当前跑道全部占用,建议调整航班时间"); } Long runwayId = freeRunways.get(0); runwayOccupyMapper.insert(apply.getId(), runwayId, apply.getStartTime(), apply.getEndTime()); flightMapper.updateRunway(apply.getId(), runwayId); return AssignResult.success(runwayId); }

方法上加了synchronized,作用是保证在同一时间只有一个线程能执行分配逻辑,防止两个请求同时查到同一条空闲跑道。虽然放到真实集群环境会有问题,但单机部署的毕业设计足够用。findOverlap的 SQL 要自己写,核心条件是start < #{end} AND end > #{start},这样数据库层就已经过滤掉大部分不冲突的记录了。分配完成后要更新航班表的 runway_id,同时向占用表插入一条记录,这两个操作必须在同一个事务里。

3.3 停机位分配:优先级与释放

停机位分配比跑道多一个约束条件:机型必须匹配。大飞机不能停小停机位,所以停机位表里要有一个 capacity 字段,分配时先过滤掉容量不够的停机位,再按优先级排序。优先级策略我一般用两个因子:计划起飞时间早的优先;航班号字典序小的优先。用 SQL 排序比在 Java 里排序更简单。

释放停机位的逻辑相对独立,因为航班可能被取消、可能临时更换停机位,甚至备降后需要重新分配。释放时要同时更新停机位状态和航班表的 gate_id 字段。

@Transactional public void releaseGate(Long flightId) { Flight flight = flightMapper.findById(flightId); if (flight.getGateId() != null) { gateMapper.updateStatus(flight.getGateId(), 0); flightMapper.updateGate(flightId, null); } }

这里的updateStatus(gateId, 0)把停机位状态改为空闲,updateGate把航班关联的停机位置空。注意@Transactional必须加,因为两个更新操作任意一个失败,都会导致数据不一致。一个常见事故是:停机位释放了,但航班表仍保留旧 gate_id,后续查航班详情时就会看到一条“幽灵停机位”。

3.4 协调管理:延误、备降与取消流程

“协调”这两个字主要体现在异常情况处理上。延误时,航班要重新分配时间窗,并清空原来的跑道占用;备降时,航班可能没有计划时间,系统要立即寻找空闲跑道和停机位;取消时,要释放所有资源。这些操作我统一放到一个 CoordinationService 里,每个方法都加事务。示例:延误处理流程是更新航班状态为 3、删除旧的跑道占用记录、按新时间重新分配跑道。如果新时间冲突,就返回失败提示,要求调度员手动换一个时间。

备降处理要有优先级,因为备降航班通常时间敏感,我建议先占用一个空闲停机位,再处理跑道。取消处理最简单,释放资源后把状态置为 4。这里要注意,所有涉及状态变更的接口都要在协调记录表里插入一条日志,包括操作人、原状态、新状态、原因和处理时间。答辩时老师看到审计日志,会觉得你的系统完整性高出不少层次。

4. 项目报告、答辩PPT与部署视频:从能跑到能答辩的完整交付

这个标题最后带了一长串交付物清单,项目报告、答辩 PPT、源代码、数据库、截图和部署视频,这意味着你不能只写代码。很多同学代码能跑,但报告写得像软件工程教材目录,PPT 全是文字,视频录制时环境起不来,结果答辩评分上不去。其实这部分比代码更容易拉开差距,因为它是老师判断你是否理解自己项目的重要依据。

4.1 项目报告的结构与干货点

项目报告建议按这个章节框架写:绪论,需求分析,系统设计,详细设计与实现,系统测试,总结与展望。绪论里重点写机场调度背景和“是否可以用算法减少跑道占用冲突”的选题意义;需求分析要画出角色用例图和功能列表;系统设计要包含架构图、E-R 图和数据库表清单;详细设计与实现要写清跑道冲突检测算法、停机位分配策略;系统测试要有测试用例表。干货点一定放在“跑道冲突检测算法”这一节,把区间重叠公式、时间窗边界条件、并发处理方式写清楚,这部分是老师最爱问的技术点。

报告忌写成名词堆砌。不要大段复制 Spring Boot 官方文档,而是要写“本系统的航班表为什么用 DATETIME 不用 VARCHAR”“停机位释放逻辑如何避免资源泄漏”。这类结合具体问题的话才能让老师相信系统是你亲手做的。

4.2 答辩PPT:每一页讲什么、演示怎么串

答辩 PPT 控制在 12 到 15 页,每页只讲一个核心点。我通常这样安排:第一页封面写题目;第二页背景和意义;第三页需求分析;第四页技术选型;第五页功能结构;第六页数据库 E-R 图;第七页核心算法——跑道冲突检测;第八页核心代码展示;第九页系统截图;第十页测试结果;第十一页总结;第十二页致谢。演示时先花两分钟讲需求,再用三分钟讲架构和数据表,接下来五分钟进入代码演示,最后留一分钟总结。代码演示环节不要照着代码念,要当场演示一个冲突场景:给两个航班分配同一条跑道,系统如何拒绝第二个航班。这个演示做完,基本可以回答“系统怎么体现协调”这个问题。

4.3 截图与部署视频的录制规范

截图要清晰展示三个层次:系统主界面、数据库表结构、核心代码。截图分辨率建议 1920x1080,窗口别最小化,标签页要能看清接口路径。部署视频更讲究,录制前先把 MySQL、JDK、Maven 环境检查一遍,脚本跑通后再录。视频里要展示从启动数据库,到执行 SQL 初始化脚本,再到运行 jar 包,最后浏览器访问的全过程。不要在视频里临场去查错误日志,更不要把编译器的大红报错录进去。我见过有人录了 20 分钟视频,其中 10 分钟在解决环境问题,这种视频宁可重录也不要交给老师。

5. 避坑记录:航班调度系统里最容易翻车的5个细节

做这类系统最大的风险不是代码跑不起来,而是数据不一致。下面这 5 个坑是我在带毕设过程中反复看到的,每一个都能让原本看起来正常的系统在演示时突然崩溃。

5.1 时间字段用 String 存取,排序和区间判断全乱

现象:航班列表按计划时间排序时,结果顺序完全不对,比如“2024-06-02 09:00”出现在“2024-06-01 12:00”前面。原因:数据库字段用了 VARCHAR,Java 里用 String 接收,排序按照字典序比较。解决:数据库字段用 DATETIME,Java 实体用 LocalDateTime,MyBatis 映射时显式指定 jdbcType。

<result column="plan_time" property="planTime" jdbcType="TIMESTAMP"/>

这样查询列表 ORDER BY plan_time 才能按时间排序,跑道冲突检测的isBefore和isAfter才不会出错。

5.2 跑道分配并发冲突:两个航班同时分配到同一个跑道

现象:同时提交两个航班分配请求,关闭控制台日志看,两个请求都拿到了同一条空闲跑道,最终生成两条占用记录。原因:分配逻辑没有锁保护,线程 A 查完空闲列表后,线程 B 也执行了同样的查询,两个线程都以为跑道可用。解决:在分配方法上加synchronized,或者在跑道占用表加唯一索引。我给一个双保险写法。

CREATE UNIQUE INDEX uk_runway_time ON runway_occupy(runway_id, start_time);

加上唯一索引后,即使业务层漏了锁,数据库也会拒绝同一跑道同一开始时间的重复记录,避免脏数据。

5.3 MySQL 连接参数忘加时区和编码

现象:项目启动后查询航班时间全部相差 8 小时,页面中文显示乱码。原因:JDBC URL 缺少serverTimezone和characterEncoding参数。解决:把连接字符串写成下面这样。

jdbc:mysql://localhost:3306/airport_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

这里serverTimezone=Asia/Shanghai解决的是 MySQL 服务器时区与本地不一致的时间偏移,characterEncoding=utf8解决乱码。注意 MySQL 版本不同,时区参数可能需要配置数据库端变量。

5.4 删除航班时外键约束阻断

现象:删除一条已经分配过跑道的航班记录,系统直接抛异常,提示违反外键约束。原因:schedule_record 或 runway_occupy 表里保存了 flight_id,主外键关系阻止了直接删除。解决:删除航班前先删除关联记录,或者给外键配置ON DELETE CASCADE。两个方案我更推荐前者,因为级联删除容易误删,但代码里维护更可控。示例:

DELETE FROM runway_occupy WHERE flight_id = #{flightId}; DELETE FROM schedule_record WHERE flight_id = #{flightId}; DELETE FROM flight WHERE id = #{flightId};

把这三个操作放进同一个事务,保证要么全部成功,要么全部回滚。

5.5 答辩演示时数据缺失,现场查不到航班

现象:演示到一半,老师问某个航班号,你输入后返回空列表,页面一片空白。原因:开发时随手造的数据在测试过程中被删了,或者没有准备完整的演示数据。解决:写一个data.sql初始化脚本,包含至少 10 条航班、5 条跑道、5 个停机位、多条正常和延误记录,在 Spring Boot 启动时自动加载。

spring.sql.init.mode=always spring.sql.init.schema-locations=classpath:schema.sql spring.sql.init.data-locations=classpath:data.sql

我习惯在演示前重新执行一次数据库初始化,确保数据是完整可复现的,而不是依赖开发时的手工改动。这个习惯能避免很多现场事故。

6. 进阶技巧:定时任务自动释放停机位,让系统更像真实调度系统

基础功能做完后,想让系统在答辩时更有说服力,建议加一个 Spring 定时任务,自动释放长时间未释放的停机位。真实机场不会让调度员一直盯着屏幕手动手动操作,系统应该能主动识别异常状态。我用@Scheduled实现一个简单版本。

@Component public class GateReleaseTask { @Autowired private FlightMapper flightMapper; @Autowired private GateMapper gateMapper; @Scheduled(fixedDelay = 60000) public void releaseExpiredGates() { LocalDateTime threshold = LocalDateTime.now().minusHours(2); List<Flight> flights = flightMapper.findLandedUnreleased(threshold); for (Flight f : flights) { gateMapper.release(f.getGateId()); flightMapper.updateGate(f.getId(), null); } } }

fixedDelay = 60000表示上一次任务执行完后再等 60 秒运行,不会因为任务耗时产生重叠执行。threshold是边界参数,你可以根据机场规则改成 30 分钟或更长。findLandedUnreleased这条 SQL 要查状态是“完成”且 gate_id 不为空、落地时间超过 2 小时的航班,你可以自己在 Mapper XML 里写。加上这个定时任务,系统就具备了“自动协调”的能力,答辩时你可以说:停机位不会长期被占用,系统会按规则自动回收。

我习惯在交付前检查三件事:数据库初始化脚本能否干净执行、jar 包能否在另一台机器上跑起来、演示数据是否足够撑起三分钟讲解。把这些写进自己的交付清单后,我很少再遇到现场翻车的情况。这个方向真的值得做,哪怕标题只写“毕业设计”,但把协调调度逻辑做完后,你收获的算法能力和工程思维,是普通增删改查项目给不了的。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进