简介:火车票售票管理系统数据库课程设计实验报告是一份面向数据库课程设计、毕业设计以及Java/MySQL开发学习者的完整参考文档。报告基于Eclipse+MySQL+Windows 8.1环境,围绕车次管理、车票管理、售票、退票、查询与异常处理,完整梳理了系统开发平台、数据库规划、系统定义、需求分析、数据库逻辑设计(含ER图、数据字典、关系表)、物理设计(索引、视图、安全机制)、应用程序设计(功能模块、界面与事务设计)以及测试运行等环节。压缩包内包含1个doc文档,大小约721KB,文档目录结构清晰,可直接作为课程设计报告模板或开发实施蓝本,尤其适合需要快速理解数据库设计全流程、撰写实验报告或搭建售票类管理系统的读者。目前已有74人学习下载,内容兼顾理论与实践,值得参考借鉴。
1. 火车票售票管理系统:一份能直接改写的数据库课程设计底稿
下午接到“火车票售票管理系统”这个题目时,大多数人的第一反应是赶紧建表,结果被E-R图、主外键、事务处理和课程设计报告格式卡住一整天。这份《数据库课程设计实验报告火车票售票管理系统方案.doc》不是一份能直接运行的商品软件,而是一条完整的课程设计路线:从开发平台、数据库规划、需求分析,到逻辑设计、物理设计、应用程序设计,再到测试和运行,每一步都按数据库教材的流程写全了。它能解决的典型问题是:表结构该定哪些字段、购票和退票怎么保证数据一致、报告和演示程序怎么避免看起来像赶工水货。适合数据相关专业学生补全课程设计,也适合需要快速理解“车次—订单—用户”三类数据关系的从业者参考。
2. 需求分析与表结构:从三张表把数据落住
2.1 数据需求:车次、用户、订单三个数据项的字段依据
这份报告里最值得先抄下来的,是需求分析阶段列出的三个数据项。第一,车次信息:车号、出发地、目的地、发车日期、开出时刻、到达时刻、剩余座位数、票价。第二,订票记录:订单号、身份证号、车号、发车日期、订购日期、订购票数、总价。第三,用户信息:用户名、身份证号、性别、电话。这三个数据项直接映射到三张核心表,不需要再凭感觉加表。
设计表结构时,我一般会先定四个原则:每张表都要有不可为空的主键;车次和发车日期必须能区分“同一车次不同日期”的班次;外键必须是对方表的主键或唯一键;金额字段用 DECIMAL 而不用 FLOAT,避免浮点精度问题。报告里数据字典给出的字段类型比较老,比如 CHAR(10)、CHAR(18),课程设计照抄没问题,但如果想让答辩老师觉得你懂实践,可以适当把价格改成 DECIMAL(10,2),把电话改成 VARCHAR(20)。下面是我基于报告字段落成的 MySQL 建表语句:
CREATE DATABASE train_booking DEFAULT CHARSET utf8mb4; USE train_booking; CREATE TABLE `user` ( UserID CHAR(18) NOT NULL COMMENT '身份证号,主键', UserName VARCHAR(50) NOT NULL COMMENT '用户名', Sex CHAR(2) COMMENT '性别', Phone VARCHAR(12) COMMENT '手机号', PRIMARY KEY (UserID) ) ENGINE=InnoDB COMMENT '用户表'; CREATE TABLE bus_info ( BusID CHAR(10) NOT NULL COMMENT '车号', BusFrom VARCHAR(50) NOT NULL COMMENT '出发地', BusTo VARCHAR(50) NOT NULL COMMENT '目的地', BusDate DATETIME NOT NULL COMMENT '发车日期', BusBegin DATETIME NOT NULL COMMENT '开出时刻', BusEnd DATETIME NOT NULL COMMENT '到达时刻', TicketNum INT NOT NULL COMMENT '剩余票数', Price DECIMAL(10,2) NOT NULL COMMENT '票价', PRIMARY KEY (BusID, BusDate) ) ENGINE=InnoDB COMMENT '车次信息表'; CREATE TABLE order_info ( OrderID CHAR(10) NOT NULL COMMENT '订单号,主键', UserID CHAR(18) NOT NULL COMMENT '身份证号,外键', BusID CHAR(10) NOT NULL COMMENT '车号,外键', BusDate DATETIME NOT NULL COMMENT '发车日期,外键', OrderDate DATETIME NOT NULL COMMENT '订购日期', OrderNum INT NOT NULL COMMENT '订购票数', TotalMoney DECIMAL(10,2) NOT NULL COMMENT '总价', PRIMARY KEY (OrderID), FOREIGN KEY (UserID) REFERENCES `user`(UserID), FOREIGN KEY (BusID, BusDate) REFERENCES bus_info(BusID, BusDate) ) ENGINE=InnoDB COMMENT '订单表';这段建表逻辑里有两个容易忽略的点。第一,业务表别叫user,因为 MySQL 系统库里已经有一张mysql.user,虽然业务库里可以创建同名表,但为了少踩坑,我习惯加反引号,或者直接改成tb_user。第二,bus_info的主键是 (BusID, BusDate) 联合主键,不是只有 BusID。报告的数据字典里写了两个主键字段,很多人只把 BusID 设成主键,后面插入第二天的车次时就会主键冲突。这个坑在后面的避坑章节还会详细说。
参数说明:订单号用 CHAR(10) 是忠于原报告的设计,实际系统里可以用雪花算法生成的字符串或者自增 BIGINT;OrderDate用 DATETIME 记录下单时间;TotalMoney一般应该等于 Price 乘 OrderNum,但报告里直接在订单表存了总价,相当于做了冗余,查报表时不用join车次表,这个取舍可以接受。外键那里用 (BusID, BusDate) 联合外键,MySQL 要求外键引用列在主表里必须有唯一索引,bus_info的主键正好满足。
2.2 事务需求:查询、订票、退票流程决定了操作类型
需求分析不只是写表格字段,还要说清楚事务需求。报告里把事务拆成了三块:查询、订票、退票。查询又分成对车次的查询和对已订车票的查询;车次信息可以按车次精确查、按起止地点模糊查、按发车日期和余票数量过滤;车次信息只允许用户查询,不允许修改。第二块订票,是用户通过查询找到满意车次,输入个人信息后下单,订票记录包括会员名、车号、发车日期、订购日期、订购票数、总价。第三块退票,是用户通过名字或订单号找到订票信息,然后退掉已购车票。
这三块事务直接决定了功能模块的“增删改查”落法:用户侧基本是查和增,退票是删或改状态;管理员侧才是真正的增、删、改。很多课程设计把用户模块做得特别全能,让用户也能改车次、删车次,这是典型的系统边界没想清楚。报告里特意区分了管理员视图和用户视图,管理员能对车次和用户信息做增删改,用户只能查询和购票。这一点在答辩时经常被老师问:你的系统权限是怎么控制的?如果你照着报告写出两层视图,至少能回答出一个“权限分离”的概念。
下面用表格把事务输入和输出对齐,这也是写报告时可以抄的格式:
| 事务 | 用户输入 | 处理后结果 | | 查询车次 | 车次号、出发地、日期、余票条件 | 返回符合条件的车次列表 | | 订票 | 车次、个人信息、订购票数 | 余票扣减、生成订单、返回订票成功 | | 退票 | 名字、身份证号或订单号 | 删除或标记订单、余票恢复、返回退票成功 |
表格形式的好处是,老师一眼能看到你理解了一组事务对数据的影响。特别是订票和退票,它们不是简单的单表操作,而是跨order_info和bus_info的组合事务,这一步想清楚了,后面的应用程序设计才能写对。
2.3 系统边界:管理员和用户权限怎么落到功能上
报告在“系统定义”里把边界写得比较清楚:管理员可以对车票和车次进行删改;用户可以买票,但不能对火车票进行添加操作。管理员视图里有列车管理、用户管理、系统数据处理、个人信息管理、用户请求信息管理;用户视图里有个人信息管理、车次检索、车票购买和退票。
这个边界的价值在于,它决定了数据库权限和代码里的校验逻辑。比如bus_info表,用户角色只能 SELECT,不能 INSERT、UPDATE、DELETE;order_info表,用户角色只能 SELECT 自己的订单和 INSERT 新订单。管理员角色则可以对bus_info做完整增删改。这些权限如果不提前想清楚,等到写页面时就会变成在前端隐藏按钮,数据库层面没有任何约束,属于典型的“表面安全”。报告在安全机制那一节说了“某些表只有具有特定权限才可以访问”,这一点是可落地的,不只是在文档里写一句漂亮话。
从实际开发角度看,系统边界还能帮你控制课程设计的工作量:用户模块只需要查询和新增,管理员模块才需要写完整的增删改页面。不少人一开始就把用户页面做得极其复杂,反而忽略了管理员对车次的维护页面,最后功能表和题目要求对不上。按照报告这条线,先给用户和管理员各画一栏功能清单,再开始写代码,整体会顺很多。
3. 逻辑与物理设计:E-R图、数据字典和索引谁先谁后
3.1 E-R图:三个实体和一个购买过程
报告5.1节给了一组数据和E-R图,实体关系可以理解为:用户购买车票、管理员删改车票、用户退订车票。这里最核心的实体是用户、车次、订单。用户和车次之间是多对多关系,需要一个用户想买多个车次、一个车次会被多个用户买,所以必须引入订单表作为联系实体,把多对多拆成用户到订单的一对多、车次到订单的一对多。
有个地方报告没有画得很细,实际自己做的时候要注意:订单和车次之间不是简单通过BusID关联,而是通过 (BusID, BusDate) 联合关联,因为车次表里同一BusID会出现在多个发车日期上。E-R图里如果只画一条线连到“车次”实体,会让人误以为只要知道BusID就能定位车次。正确做法是,在车次实体上把“发车日期”也标成标识属性,或者在关系连线旁标注“订单包含车次在某日的班次”。这一点能给报告加分。
关系表部分,报告写的是“实体联系:用户购买车票、管理员删改车票、用户退订车票”。翻译成数据库术语就是:用户和订单之间是1:N,车次和订单之间是1:N,管理员和车次之间是1:N但管理员是后台操作角色,不需要在ER图里做成一个持续存储的实体。管理员本身可以作为一个系统用户,但不对应票务业务表的强关联。
3.2 数据字典:从表格到 SQL 的映射
报告5.2节的数据字典给出了三张表的字段名、数据类型、是否可空和说明。字段名是 BusInfo、OrderInfo、User。其中 BusInfo 和 OrderInfo 里都出现了BusDate,并且都标成 NOT NULL。如果你只把数据字典当文档看,会觉得没什么;真去建表时就会发现,OrderInfo里的BusDate既要关联BusInfo的主键,又要参与联合外键,所以建表顺序必须是先建 bus_info,再建 order_info。
做课程设计时,数据字典写到什么程度算合格?我的标准是:每个字段有明确的类型、是否可空、主外键和一句业务含义。报告里少了字段长度对业务含义的解释,比如Phone VARCHAR(12)只能存12位,国内手机号刚好11位,但后面可能加区号或分机,这个长度在真实项目里偏紧。你可以在自己报告里把 Phone 写成 VARCHAR(20),并注明“预留扩展位”,比照抄原报告更显思考。
视图是物理设计里的“可选”环节,报告也标注了本节可选。不过既然要建,我建议至少建一个“可用车次视图”,把TicketNum > 0过滤条件放进去,让后续查询代码更干净。比如:
CREATE VIEW v_available_bus AS SELECT BusID, BusFrom, BusTo, BusDate, BusBegin, BusEnd, TicketNum, Price FROM bus_info WHERE TicketNum > 0;逻辑说明:这个视图隐藏了判断余票大于0的细节,购票页面直接查该视图,不会再出现把余票为0的车次列出来的情况。参数说明:TicketNum > 0是一个动态条件,视图每次查询都会重新执行,不需要担心数据过期;但它不会自动帮你锁行,真正购票时仍然要在事务里用条件UPDATE防止超卖。
3.3 索引与安全机制:别把车次表更新拖慢
报告6.1节说,用户列表以用户的昵称(应该是用户名)为主键索引,车次表以车次为主键索引。这个思路没问题,但不完整。bus_info是联合主键 (BusID, BusDate),本身就带了索引;order_info的 UserID 是外键,MySQL InnoDB 不一定会自动给外键列建高效率索引,需要手动补一个,否则查询用户历史订单时可能走全表扫描。
我给这个场景的建议索引是:
CREATE INDEX idx_bus_from_to ON bus_info(BusFrom, BusTo); CREATE INDEX idx_order_user ON order_info(UserID); CREATE INDEX idx_order_bus ON order_info(BusID, BusDate);逻辑说明:bus_info上建 (BusFrom, BusTo) 联合索引,是为了覆盖按出发地和目的地查询车次的场景;order_info上的两个索引分别支撑“查用户订单”和“查某个车次被订购了多少张票”。参数说明:索引不是越多越好,尤其不要给Sex、Phone这种区分度低的列建单列索引,否则写操作会多维护索引,读操作也未必能用上。
安全机制报告里分了两层:系统安全和数据安全。系统安全要求用户必须注册登录,登录验证用户名和密码;数据安全要求某些表只有特定权限才能访问。对应到 MySQL 里就是建用户、授权。这里给出一个课程设计级别的最小权限配置:
CREATE USER 'train_admin'@'localhost' IDENTIFIED BY 'AdminPass123'; CREATE USER 'train_user'@'localhost' IDENTIFIED BY 'UserPass123'; GRANT SELECT, INSERT, UPDATE, DELETE ON train_booking.* TO 'train_admin'@'localhost'; GRANT SELECT ON train_booking.bus_info TO 'train_user'@'localhost'; GRANT SELECT, INSERT ON train_booking.order_info TO 'train_user'@'localhost';逻辑说明:管理员拥有整个库的增删改查权限;普通用户只能查车次、查订单、新建订单,不能修改车次。参数说明:train_user没被授予order_info的 UPDATE 或 DELETE 权限,如果程序里用 DELETE 实现退票,就会收到权限错误,这正好倒逼你实现退票时用状态更新而不是物理删除。从安全角度,保留退票记录比直接删除更合理,也更接近真实业务。
4. 应用程序设计:购票事务和退票状态机是核心
4.1 登录与注册:身份证号和手机号校验的入门写法
报告7.1.2节写了注册模块包含用户名、密码、身份证号、手机号四项,会有两次密码确认,还会判断身份证号位数并用特定计算方式核对身份证号是否正确。这段内容在课程设计报告里是加分项,因为很多学生只写“注册功能已实现”,完全没提校验规则。
身份证号校验如果只判断 length==18,那等于没写。真正的入门校验是两步:前17位必须是数字,第18位是数字或大写X;再按加权因子算校验码。手机号校验至少判断长度和首位,比如长度11且以1开头。下面是一个适合写在附录或业务层里的Java校验片段:
public boolean checkIdCard(String idCard) { if (idCard == null || idCard.length() != 18) { return false; } char last = idCard.charAt(17); if (!Character.isDigit(last) && last != 'X' && last != 'x') { return false; } for (int i = 0; i < 17; i++) { if (!Character.isDigit(idCard.charAt(i))) { return false; } } return true; }参数说明:这段代码只解决了“格式合法性”,没解决“身份证是否真实存在”。真实项目要调公安接口,课程设计做到位数和校验码已经足够。逻辑说明:先拦非18位,再拦第18位非法字符,最后拦前17位含非数字,顺序很直观。如果你还想更进一步,可以按GB/T 2260标准算前6位地区码是否有效。
4.2 购票:一个包含扣余票和下订单的事务
报告7.1.3节描述购票模块会先按出发地、目的地、出发日期查询,再由用户选择车票购买;因为支付模块没实现,购票成功后的状态是未支付。这里最关键的不是页面,而是“扣余票”和“生成订单”这两个操作必须放在同一个数据库事务里。很多人正是在这里翻车,页面跳转了、订单生成了,但TicketNum没减,下一分钟余票还是原来那几张。
我习惯用JDBC手动控制事务,因为能清楚看到提交点和回滚点:
public boolean buyTicket(Connection conn, String userId, String busId, String busDate, int num, BigDecimal price) { PreparedStatement ps = null; ResultSet rs = null; try { conn.setAutoCommit(false); String deductSql = "UPDATE bus_info SET TicketNum = TicketNum - ? " + "WHERE BusID = ? AND BusDate = ? AND TicketNum >= ?"; ps = conn.prepareStatement(deductSql); ps.setInt(1, num); ps.setString(2, busId); ps.setString(3, busDate); ps.setInt(4, num); int updateRows = ps.executeUpdate(); if (updateRows == 0) { conn.rollback(); return false; } String orderSql = "INSERT INTO order_info " + "(OrderID, UserID, BusID, BusDate, OrderDate, OrderNum, TotalMoney) " + "VALUES (?, ?, ?, ?, NOW(), ?, ?)"; ps = conn.prepareStatement(orderSql); ps.setString(1, generateOrderId()); ps.setString(2, userId); ps.setString(3, busId); ps.setString(4, busDate); ps.setInt(5, num); ps.setBigDecimal(6, price.multiply(BigDecimal.valueOf(num))); ps.executeUpdate(); conn.commit(); return true; } catch (Exception e) { conn.rollback(); throw new RuntimeException("购票失败,事务已回滚", e); } finally { if (rs != null) try { rs.close(); } catch (Exception ignored) {} if (ps != null) try { ps.close(); } catch (Exception ignored) {} } }代码逻辑:先扣减余票,再用影响行数判断是否扣成功。TicketNum >= ?这个条件非常关键,它保证两个用户同时买最后一张票时,只有一个事务能更新成功,另一个会得到updateRows == 0并回滚。参数说明:busDate需要和bus_info.BusDate的格式一致,建议在SQL层用STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s')做转换,而不是直接拼字符串;price应该先从库里查询出来,不要用前端传过来的价格,否则用户可以随便改票价。
这里还要提一下数据库并发锁。InnoDB 的 UPDATE 语句会对命中的行加行锁,所以上面这个事务天然避免了“最后一张票被两个人同时买走”的问题。但如果你的事务顺序不统一,比如甲先更新 order_info 再更新 bus_info,乙先更新 bus_info 再更新 order_info,就可能出现死锁。课程设计里统一“先车次后订单”的更新顺序,是避免数据库死锁最实在的习惯。
4.3 退票:删除记录也要避免重复退票和余票不恢复
报告7.1.4节说,已经退票的车票如果点击退票会提示已经退票,未退票的车票可以成功退票。这个需求听起来简单,实现时容易出现两个典型问题:一是把order_info记录物理删除,导致无法知道这张票曾经被退过;二是不做状态判断,重复点退票把余票加了两次。
我建议直接给订单增加一个PayStatus字段,把退票做成“更新状态”,而不是 DELETE。这比原报告更接近真实票务系统,也方便老师看到你的扩展能力。退票SQL用事务包起来:
START TRANSACTION; SELECT BusID, BusDate, OrderNum FROM order_info WHERE OrderID = ? AND UserID = ? AND PayStatus = 0 FOR UPDATE; -- 如果没有查到数据,说明订单不存在、属于其他用户或已退票 -- 此时应 ROLLBACK 并提示“退票失败” UPDATE bus_info SET TicketNum = TicketNum + 1 WHERE BusID = ? AND BusDate = ?; UPDATE order_info SET PayStatus = 2 WHERE OrderID = ? AND UserID = ?; COMMIT;逻辑说明:第一步用SELECT ... FOR UPDATE锁住订单行,防止同一笔订单被两个并发退票请求同时处理;同时PayStatus = 0条件保证只有未退票的订单才能进入退票流程。第二步恢复余票,第三步把状态改成“已退票”。参数说明:恢复余票数量不能写死为1,应该用查询到的OrderNum,并加括号变成TicketNum = TicketNum + ?。这样一张订单买了几张票就恢复几张,不会越退越多。
5. 避坑:五处会让课程设计翻车的细节
5.1 车次主键设置成车号一列:同一个车次只能卖一次票
现象:G101 车次插入 5月1日 的班次成功后,再插入 5月2日 的班次,数据库报 Duplicate entry 'G101',明明是两个不同日期的车票,却插不进去。
原因:报告数据字典里BusID和BusDate都写着“主键”,很多人理解成两个独立主键,建表时只把BusID设成了 PRIMARY KEY,忽略了发车日期。一个车次编号当然会有很多个发车日期,只用车号做主键,等于把同一车次的每一天都当成了同一条记录。
解决:使用联合主键PRIMARY KEY (BusID, BusDate)。如果一天还要发两班甚至三班,那就得再加一个班次编号,改成PRIMARY KEY (BusID, BusDate, RunNo)。课程设计做到联合主键就足够应付大多数场景。
5.2 余票不扣减:买了几张票,TicketNum 还是原数
现象:界面上明明下单成功了,订单表里也生成了记录,但bus_info.TicketNum一点没变,多下几单票数还是那么多,再一次买票时已经超卖。
原因:购票请求只执行了INSERT INTO order_info,没有在同一个事务里执行UPDATE bus_info SET TicketNum = TicketNum - ?。也有的情况是两个操作都写了,但不在一个事务里,中间发生异常后订单回滚而余票没有回滚,或者反过来。
解决:把扣余票和插订单放到同一个事务,用事务保证原子性。扣票语句必须带上TicketNum >= ?条件,并用影响行数判断扣票是否成功。否则当余票不足时,会把票数扣成负数,这比不扣票还要严重。
5.3 重复退票:同一订单连点两次退票,库存越退越多
现象:用户提交退票请求第一次成功后,网络卡顿或者用户不放心又点了第二次,结果第二次也提示成功,bus_info.TicketNum又被加了一次,后台对不上账。
原因:退票操作用的是“先删除订单,再恢复余票”,第一次退票已经把订单删了,第二次查不到订单,但代码又把恢复余票放在前面执行,或者 DELETE 影响行数为0后没有中断流程。
解决:退票前先查订单,查不到就直接回滚并提示“订单不存在或已退票”。更稳的做法是给订单加PayStatus状态字段,退票时先SELECT ... FOR UPDATE,成功后再恢复余票和更新状态。重复点击第二次时,状态已经是2,会被第一步拦截。
5.4 身份证号只查位数:注册模块放进来大量假数据
现象:用户输入111111111111111111或一串字母也能注册成功,订单表里身份证号一栏什么格式都有,退票时用身份证号精确查询经常查不到。
原因:注册模块只做了length(userId) == 18的位数判断,没判断前17位是不是数字,也没判断第18位是数字或X。手机号同理,只判断“够不够11位”,没有判断是否以1开头。
解决:至少加一个正则校验:前17位为数字,第18位是数字或大写X。如果想更严谨,按身份证规则计算校验码。手机号校验可以简化为“11位且以1开头”。原报告提到了“特定计算方式”,把它落实成代码会比只写文本更有说服力。
5.5 索引建太多:车次表插入和更新越来越慢
现象:给bus_info的每一个字段都建了索引,开始数据少还感觉不到,数据量上到几千条后,管理员新增车次或用户购票时的 UPDATE 明显变慢。
原因:索引能加速查询,但每次 INSERT 和 UPDATE 都要同步维护所有索引,这是一个不小的开销。特别是像Sex、BusFrom这种区分度不算极高的字段,单独建索引收益很低,反而拖慢写操作。
解决:只给高频查询条件且区分度好的列建索引。比如order_info.UserID值得建,因为用户查询自己的订单很频繁;bus_info的 (BusFrom, BusTo) 值得建,因为购票第一步就是按起止地点筛车次。不要为了报告页数多,列一大堆用不上的索引。面试或答辩时能说出“索引不是越多越好”这个观点,比硬堆索引值钱。
6. 验证与扩展:把报告变成自己的系统前先做三件事
6.1 用一条 SQL 验证购票事务的余票一致性
购票逻辑写完后,我会用一条查询同时对账,看看卖出票数和剩余票数之和是否等于初始票数:
SELECT (SELECT SUM(OrderNum) FROM order_info WHERE BusID = 'G101' AND BusDate = '2025-05-01') AS sold_sum, (SELECT TicketNum FROM bus_info WHERE BusID = 'G101' AND BusDate = '2025-05-01') AS left_sum;如果sold_sum + left_sum不等于发车前的满载票数,说明事务顺序或退票恢复逻辑有问题。这个验证习惯能帮你快速定位到底是在扣票环节出错,还是在退票环节把余票多回了。
6.2 补上报告缺失的支付状态
原报告明确说没有实现支付功能,购票成功后的状态是未支付。课程设计不必真做支付,但至少可以加一个状态字段,让“未支付、已支付、已退票”三种状态在数据层可见:
ALTER TABLE order_info ADD COLUMN PayStatus TINYINT NOT NULL DEFAULT 0 COMMENT '0未支付,1已支付,2已退票';加这个字段后,退票模块就可以从 DELETE 改成 UPDATE,订单历史也保留下来了。顺手再把bus_info增加一个SeatType字段,比如硬座、软卧、二等座,算是回应报告里“座位类型设定”的需求。
6.3 退票查询技巧:用订单号加身份证号兜底
退票界面如果只让用户输入姓名查订单,重名一多就退错票。我的做法是强制用户同时输入订单号和注册时的身份证号,用两个条件联合查询:
SELECT OrderID, BusID, BusDate, OrderNum, TotalMoney FROM order_info WHERE OrderID = ? AND UserID = ? AND PayStatus = 0;这个查询同时解决了定位准确和防误退两个问题。从那以后我写每份数据库课程设计,都会强制走一遍“E-R图→关系表→字段约束→事务测试”的流程,尤其是购票和退票两条链路,至少抽查三个数据:余票、订单总票数、退票后的库存。这套流程看着土,但能挡住大多数翻车现场。希望帮到你。
本文还有配套的精品资源,点击获取