资讯详情

资讯详情

机票预订系统详细设计说明书:从模块划分到数据库事务控制

简介《机票预订系统详细设计说明书》是一份面向软件工程课程设计、毕业设计或实际项目开发的完整设计文档重点解决机票查询、预订、支付、退订与退款等模块的程序化实现问题。文档以JavaJSP为主要技术路线开发平台采用MyEclipse 7.0数据库使用MySQL设计内容覆盖程序系统结构、核心算法、数据表设计、模块接口、用户界面以及系统测试方案层次完整、便于直接参考编码。压缩包内共有1个PDF文档大小约997KB章节从引言、程序系统结构展开对查询预订系统和退订系统分别按程序描述、功能、性能、输入输出、流程逻辑、接口、存储分配、注释设计、限制条件和测试计划进行说明还包含IPO图等关键图示。目前已有1378人学习下载适合需要撰写软件设计说明书、完成课程设计或短时间内搭建机票预订系统整体设计框架的读者参考。1. 为什么详细设计说明书比骨架代码更值得读拿到“机票预订系统详细设计说明书.pdf”这份资料多数人的第一反应是找代码但这份文档的价值恰恰不在代码而在它把“查询预订”和“退订”两个模块从输入项、输出项、算法到流程逻辑都预先规定好了。系统技术栈是 Java JSP AWT MySQL这在今天的 Web 项目里很少见可它的模块切分方式——把预订、退票、航班管理、营运统计分别拉成独立功能点——放到任何机票、酒店、影院订票系统里都能直接复用。对正在写软件工程课设、不知道怎么组织详细设计文档的同学来说这份文档比一段能跑的骨架代码更值得通读一遍。特别是当你在搜索引擎里输入“机票预订系统 详细设计说明书 pdf 下载”这种关键词时它给出的不是零散代码而是一整套可以照着落地的设计蓝图。2. 程序系统的结构先定模块边界再写代码2.1 详细设计里的系统结构图在表达什么文档里的“程序系统结构”没有画成复杂的微服务架构图而是按业务动作把系统分成两个程序查询预订系统和退订系统。查询预订系统承担机票预订、取票通知、查询航班、查询机票、打印机票、营运统计以及后台航班管理退订系统只负责退票链路。这种划分的出发点是角色权限和业务生命周期订是一个完整事务退是另一个完整事务两者通过“机票”和“账单”两个实体产生关联。我在做类似课设时一开始习惯把“查询航班”和“预订机票”拆成两个独立的 Servlet结果发现预订页面要复用查询条件、还要在同一个表单里带回航班 ID代码耦合非常严重。正确做法是把查询航班当作预订的前置步骤放在同一个程序模块里。文档把“查询航班”列在预订功能之下就是为了保证一次请求能完成“查—选—填—订”的完整交互而不是让用户在多个页面之间来回跳转。从权限矩阵也能看出模块归属功能项机场管理员旅行社乘客机票预订可操作可操作不可操作取票通知可操作可操作不可操作查询航班可操作可操作可操作查询机票可操作可操作可操作需身份证号和机票号打印机票可操作不可操作不可操作营运统计可操作不可操作不可操作后台航班管理可操作不可操作不可操作退订机票可操作不可操作通过管理员操作这张表在详细设计阶段直接决定了 JSP 页面和 Servlet 的权限过滤器怎么写。比如打印机票只有管理员能做页面跳转时就要校验 session 里的角色而不是在打印设备上做限制。2.2 MyEclipse、AWT、JSP 混用的架构怎么理解现在的开发者在看到“AWT 开发界面 JSP 逻辑”时通常会困惑因为 AWT 是本地窗口组件JSP 是服务端网页技术两者不在同一个运行层次。实际课设场景里AWT 通常用来写机场工作人员使用的桌面客户端JSP 负责面向旅行社和乘客的浏览器端页面而 MySQL 作为统一数据源被两边同时调用。这种“异构界面 统一数据库”的架构在小型系统里非常常见。只要数据库表设计统一、JDBC 连接逻辑抽成公共类桌面端和 Web 端就能各自独立演化。文档的接口部分也明确写了服务器端用 MySQL 备份命令保存数据模块间通过函数调用和参数传递交互。翻译成今天的工程语言就是“边界清晰、共享数据库、无分布式事务”这对一个课设规模的系统来说是足够合理的取舍。用 AWT 写桌面端还有一个实际好处机票打印机通常连接在机场柜台的工作站上本地桌面程序调用打印机比浏览器页面调打印更稳定这正好对应文档里“打印机票只有机场管理员可操作”的权限设定。所以这个技术组合不是随便选的而是按物理环境定的。2.3 存储分配和注释设计为什么文档要求连注释都规定详细设计说明书不是代码注释的替代品但它在存储分配和注释设计两节明确写了“变量全部在组件内声明”和“模块首部注释、调用函数注释”。这两个看似琐碎的规定实际上是给编码阶段定的规范。我在实际编码时吃过亏JSP 页面里为了省事直接声明全局变量多个请求并发时变量互相覆盖最后排查出来是 JSP 的线程安全问题。文档里“变量结构全部在组件内声明”就是针对这类问题的预防措施。注释设计部分提到调用 MD5 加密函数说明文档已经预设了后续编码要用的工具库编码人员不需要再临时做技术选型。按这个逻辑详细设计的产物不只是一份说明书它同时是编码清单、测试依据和验收标准。模块函数清单、输入输出表、权限矩阵一对照代码能不能交付一眼就能判断。3. 查询预订模块输入项表 事务控制的复现路径3.1 输入项表到底在约束什么查询预订模块的输入项表是整份文档信息密度最高的部分。它把姓名字段定义成 Varchar 类型、6 位以上身份证号码要求 16—20 位联系电话 8 位以上航班号 8 位以上还标注了乘客数据在传输和存储时都要加密处理。这些约束在做表结构时应当直接落成 SQL 检查约束CREATE TABLE passenger ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, id_card VARCHAR(20) NOT NULL UNIQUE, phone VARCHAR(20) NOT NULL, email VARCHAR(50), work_unit VARCHAR(100), CONSTRAINT chk_id_card CHECK (CHAR_LENGTH(id_card) BETWEEN 16 AND 20), CONSTRAINT chk_phone CHECK (CHAR_LENGTH(phone) 8) );逻辑说明CHECK约束把“身份证号码 16—20 位”这种业务规则下推到数据库层避免应用层漏校验。UNIQUE用在身份证号上防止同一身份证重复预订产生脏数据。参数说明name要求 6 位以上所以 VARCHAR(50) 预留了足够空间id_card的 CHECK 约束在 MySQL 8.0.16 之后才真正生效老版本会忽略它因此应用层校验不能省。JSP/Servlet 里用正则再校验一次public boolean validatePassenger(String idCard, String phone) { String idPattern ^\\d{16,20}$; String phonePattern ^\\d{8,15}$; return idCard.matches(idPattern) phone.matches(phonePattern); }参数说明idPattern要求纯数字且长度为 16 到 20 位phonePattern要求 8 到 15 位数字两个校验全通过才返回 true。文档输出项里有“身份证号”和“账单号”的区分这说明查询机票时乘客端只需要身份证号加机票号账单号是系统生成、供管理员退票时使用的内部编号不要在页面表单里让用户填写。3.2 预订的算法流程并发场景下的座位扣减文档里的算法部分只写了登录验证但预订主流程在流程逻辑一节以“乘客订票流程”的方式做了描述。按详细设计的定义补全预订算法应分成四个步骤接收航班号、航班日期、座位等级、乘客信息查询该航班该等级剩余座位数若为 0 则提示无票扣减座位数生成机票记录和账单记录返回机票号和取票信息。第 2 和第 3 步之间存在并发风险。两台旅行社客户端同时提交最后一张票的预订请求时如果先查再扣两个请求都会读到剩余 1 座结果超卖。解决办法是把查询和扣减放进同一个事务并对航班记录加行锁START TRANSACTION; SELECT seat_left FROM flight WHERE flight_no CA1301 AND flight_date 2025-06-01 FOR UPDATE; UPDATE flight SET seat_left seat_left - 1 WHERE flight_no CA1301 AND flight_date 2025-06-01 AND seat_left 0; INSERT INTO ticket (ticket_no, flight_no, flight_date, id_card, seat_class) VALUES (TK20250601001, CA1301, 2025-06-01, 110101199001011234, economy); COMMIT;逻辑说明FOR UPDATE是 MySQL 的悲观锁写法它让第一个事务锁定该航班记录第二个事务必须等第一个提交后才能读取从而避免读到过期的剩余座位数。UPDATE条件里的seat_left 0是第二道防线即使锁失效更新影响行数为 0 也能让程序感知到余票不足。INSERT的 ticket_no 由程序生成规则是“TK 日期 三位流水号”这样查询机票时可以直接按 ticket_no 定位。参数说明flight_date用 DATE 类型存储seat_left必须是INT UNSIGNED避免扣减出负数seat_class建议用ENUM(economy,business)枚举防止等级拼写不一致导致统计出错。提示当seat_left 0条件不成立时UPDATE 影响行数为 0程序要检查executeUpdate()的返回值来判断余票不足不要依赖 SELECT 先查再判。关于取票通知文档说明在预订成功后会向浏览器端发送一条包含费用信息的取票通知。这个动作在实现上不需要真的发短信或邮件它是一次页面跳转携带预订成功参数并展示账单金额。旅行社用特定设备打印该通知管理员凭通知上的机票号确认付款后打印正式机票。所以取票通知本质上是一张“预订单页面”不是外部推送。3.3 航班查询动态条件拼接与营运统计查询航班的输入是出发地、目的地、日期和时间。实现时最常踩的坑是只按等值条件拼 SQL用户少选一个“起飞时间”就查不出结果。比较好的做法是动态拼接查询条件StringBuilder sql new StringBuilder(SELECT * FROM flight WHERE 11 ); ListObject params new ArrayList(); if (departure ! null !departure.isEmpty()) { sql.append(AND departure_city ? ); params.add(departure); } if (arrival ! null !arrival.isEmpty()) { sql.append(AND arrival_city ? ); params.add(arrival); } if (flightDate ! null !flightDate.isEmpty()) { sql.append(AND flight_date ? ); params.add(flightDate); }逻辑说明WHERE 11不是性能优化而是让后面每条AND条件都可以独立追加省去判断“当前是否已有条件”的布尔标志。PreparedStatement的参数占位符必须与params列表顺序一致否则会抛参数索引异常。参数说明出发地和目的地建议用城市三字码存储避免“北京”和“首都”这种同义不同名的问题日期建议精确到天起飞时间单独拆列因为文档性能一节要求“时间要求精确到分”把日期和时间拆开更容易支持“某天之后的航班”这类范围查询。营运统计同样需要注意查询写法。文档要求管理员输入年份和月份查询当月营运情况这一步不要用LIKE %2025-06%去匹配日期列那样无法使用索引。正确写法是范围查询SELECT flight_no, COUNT(*) AS ticket_count, SUM(price) AS revenue FROM ticket WHERE flight_date 2025-06-01 AND flight_date 2025-07-01 GROUP BY flight_no;参数说明和组成半开区间 [月初, 下月初)能覆盖整个六月且不影响 MySQL 对flight_date索引的使用COUNT(*)统计售票数SUM(price)统计营收两个聚合函数在同一句 SQL 里完成运营报表的主体部分。注意这里统计的是实际售出的票退票状态为refunded的记录会包含在内如果想看“有效收入”需要在 WHERE 里加AND status ! refunded。查询预订模块到这里基本完整输入校验、事务控制、动态查询、聚合统计都有了可落地的实现方案下面看退订模块如何处理数据一致性。4. 退订模块删除动作背后的约束与接口设计4.1 退票流程中合法性判断的顺序文档明确写了退订只有管理员有权限乘客需先联系管理员提供身份证号、机票号和账单号查询机票信息确认后执行退票。这个“通过管理员操作”的设计使退票流程天然有了人工确认环节。退票的执行顺序应当先查后删但查和删必须在一个事务里完成。查询时不仅要把 ticket 和 flight 关联起来验证机票属于当前身份证号还要检查机票状态是否已经是“已退票”否则重复退票会把同一条记录处理两次START TRANSACTION; SELECT t.ticket_no, f.flight_no, f.flight_date, f.departure_city, f.arrival_city FROM ticket t JOIN flight f ON t.flight_no f.flight_no AND t.flight_date f.flight_date WHERE t.ticket_no ? AND t.id_card ? AND t.status booked FOR UPDATE; UPDATE flight f JOIN ticket t ON f.flight_no t.flight_no AND f.flight_date t.flight_date SET f.seat_left f.seat_left 1 WHERE t.ticket_no ?; UPDATE ticket SET status refunded WHERE ticket_no ?; COMMIT;逻辑说明第一个SELECT用status booked过滤已经退过的票FOR UPDATE锁住 ticket 行防止两个管理员同时操作同一张票。第一个UPDATE把座位数加回去这里用JOIN语法把 ticket 和 flight 关联省去先查 flight_no 再更新两步操作。最后一个UPDATE是逻辑删把状态改成refunded而不是执行DELETE。参数说明status字段建议用VARCHAR(10)存booked、refunded、paid三种状态退票不物理删除记录是为了保留历史账单和营运统计的原始数据否则“已退票的座位”会在报表里消失导致统计失真。退票的状态流转可以用下面这张表约束状态标识含义可执行操作禁止操作booked已预订未付款支付、取票通知、退票打印机票paid已付款打印机票、退票重复支付refunded已退票查询历史退票、打印机票4.2 表间关系设计外键联合约束退票要同时动 ticket、flight、account 三张表表间关系必须在数据库层面建立。之前用到的 ticket 表完整结构如下CREATE TABLE ticket ( ticket_no VARCHAR(20) PRIMARY KEY, flight_no VARCHAR(10) NOT NULL, flight_date DATE NOT NULL, id_card VARCHAR(20) NOT NULL, seat_class ENUM(economy,business) NOT NULL, status VARCHAR(10) DEFAULT booked, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_id_card_flight (id_card, flight_no, flight_date), CONSTRAINT fk_flight FOREIGN KEY (flight_no, flight_date) REFERENCES flight(flight_no, flight_date) ON UPDATE CASCADE ON DELETE RESTRICT, CONSTRAINT fk_account FOREIGN KEY (id_card) REFERENCES account(id_card) ON UPDATE CASCADE ON DELETE RESTRICT );逻辑说明UNIQUE KEY uk_id_card_flight是防止同一乘客在同一天重复预订同一航班的最强约束即使应用层漏判数据库也会拒绝重复插入。外键的ON UPDATE CASCADE让航班号变更时自动同步到 ticket 表ON DELETE RESTRICT防止管理员直接删除一个还有机票记录的航班逼迫系统先处理票再删航班。参数说明外键复合列(flight_no, flight_date)必须与父表 flight 的对应列建立唯一索引否则建表会报错id_card作为外键引用 account 表主键意味着乘客必须先注册账户才能订票也对应文档“乘客数据加密存储”的描述。4.3 退票模块的接口与事务边界文档在接口设计上保持着与前面一致的风格服务器用 MySQL 备份命令做数据保存模块间用函数调用和参数传递。退票模块对外暴露的核心接口可以设计成一个 Service 方法public RefundResult refundTicket(String ticketNo, String idCard) { if (!authService.hasPermission(sessionUser, refund)) { return RefundResult.denied(无退票权限); } try (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); TicketDao dao new TicketDao(conn); boolean exists dao.lockAndCheckBooked(ticketNo, idCard); if (!exists) { conn.rollback(); return RefundResult.fail(机票不存在或已退票); } dao.restoreSeat(ticketNo); dao.updateStatus(ticketNo, refunded); conn.commit(); return RefundResult.success(); } catch (SQLException e) { log.error(refund failed: {}, e.getMessage()); return RefundResult.fail(系统异常请稍后重试); } }逻辑说明setAutoCommit(false)把 JDBC 默认的自动提交关掉事务内三条 SQL 要么全部成功、要么全部回滚避免出现“座位加回来了但机票状态还是 booked”的中间状态。lockAndCheckBooked内部执行带FOR UPDATE的 SELECT在事务一开始就把票锁住后续 UPDATE 不会被并发干扰。参数说明RefundResult是自定义返回对象包含success布尔值和message字符串dataSource建议使用连接池而不是每次DriverManager.getConnection课设场景用 C3P0 或 HikariCP 均可。文档没有规定退款金额计算规则但按“价格精确到个位”的要求退款金额应从账单表读取实际支付价格而不是重新计算避免四舍五入误差。退票的另一个细节是票虽然退了但账单记录必须保留并标记refunded状态与 ticket 对应。如果直接把账单行删除营运统计里“退票率”这个指标就永远算不出来。所以在设计账单表时要给 bill 表加一个ticket_no外键和status字段让退票动作与账单状态联动。5. 测试计划、默认口令与三个可落地的改进5.1 测试顺序从子单元到子系统文档给出的测试计划分三步先测子单元过程再测模块间接口最后做系统级测试。具体到这个项目子单元测试是validatePassenger的边界值和insertTicket的事务正确性接口测试是模拟带角色 session 的请求系统测试则跑一遍“查询航班 → 预订 → 取票通知 → 打印机票 → 退票 → 座位恢复”的完整链路每步断言数据库状态。这套冒烟用例能覆盖 80% 的连接和事务代码比上来就测并发更划算。5.2 默认口令与三次失败锁定的取舍文档规定第一次使用时用户 ID 和密码是“超级用户 / 123456”连续三次验证错误系统自动关闭。前者建议改成首次登录强制修改密码后者把“关闭系统”改为锁定账号更合理——关闭客户端窗口只会让用户重新打开攻击者依然可以无限次尝试。更好的做法是同一账号连续错误三次后锁定一段时间if (loginFailCount 3) { redis.setex(lock: username, 900, 1); }参数说明setex的第一个参数是锁定键第二个参数 900 表示锁定 15 分钟第三个参数1只做存在标记登录时查询命中就说明账号被锁。没有 Redis 的课设环境可以用数据库字段lock_until存储时间戳登录时判断当前时间是否晚于该字段。5.3 把文档转成可验收清单详细设计说明书最后要转成验收清单才有实际价值。以这份文档为例至少包含五项输入项字段是否有 CHECK 约束预订是否在事务里扣减seat_left并加行锁退票是否只改status而不物理删除管理员操作是否走 session 角色校验打印机票前是否确认付款状态。文档“尚未解决的问题”一节提到对用户 ID 和密码更安全的加密方式未解决这在编码阶段可以直接用 BCrypt 替代 MD5。BCrypt 自带随机盐能直接抵抗彩虹表替换时也要处理细节账户表需要新增salt字段登录校验代码要从md5(password)改为bcrypt.checkpw(password, hash)别只替换加密函数而忘记改表结构。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →