毕业设计实战:基于Spring Boot的火车订票管理系统全栈开发

43 阅读12分钟

一、项目背景:数字化时代的铁路服务革新

随着我国高铁网络的快速发展和居民出行需求的持续增长,铁路运输已成为最重要的交通方式之一。据统计,2023年全国铁路客运量达36.8亿人次,其中线上购票比例超过80%。传统火车票务管理面临购票流程复杂、改签退票不便、票务信息不透明等痛点,90%的旅客期待通过数字化平台获得更便捷、高效的票务服务。

在"互联网+交通"深度融合的背景下,基于Spring Boot的火车订票管理系统成为连接旅客、铁路部门与票务服务的重要数字化平台。系统采用轻量级B/S架构,整合车次查询、在线购票、订单管理、改签退票等全流程服务,构建"管理员统筹-用户自助-业务协同"的服务生态,为旅客提供全天候、一站式的火车票务解决方案。

二、技术架构:火车订票管理系统的全栈技术选型

项目以"高并发、高可用、易扩展"为核心设计理念,采用业界成熟的Java Web技术栈,确保系统稳定运行与业务连续性:

技术模块具体工具/技术核心作用
后端框架Spring Boot 2.x快速构建微服务,简化配置,提供完整MVC解决方案
数据库MySQL 8.0 + RedisMySQL存储业务数据,Redis缓存热点数据和会话信息
前端技术JSP + Bootstrap + JavaScript构建响应式界面,适配多终端,优化用户体验
架构模式B/S结构跨平台访问,无需安装客户端,浏览器直接使用
消息队列RabbitMQ处理高并发订票请求,实现异步削峰
服务器Tomcat 9.0 + NginxTomcat处理业务,Nginx实现负载均衡
开发工具MyEclipse + Navicat集成开发环境与数据库管理工具

三、项目全流程:6步完成火车订票管理系统开发

3.1 第一步:需求分析——明确系统核心价值

传统火车票务存在"购票难、改签烦、退票慢"三大痛点,本系统聚焦"便捷、智能、可靠",核心需求分为功能性与非功能性两类:

3.1.1 功能性需求

  1. 双角色权限体系

    • 管理员:首页、个人中心、用户管理、车型信息管理、车次信息管理、购票订单管理、改签订单管理、退票订单管理、系统管理;
    • 用户:首页、个人中心、购票订单管理、改签订单管理、退票订单管理;
    • 前台首页:首页、车次信息、火车资讯、个人中心、后台管理。
  2. 核心票务功能

    • 车次管理:车型信息维护、车次信息配置、座位类型管理;
    • 票务服务:在线购票、订单管理、改签申请、退票处理;
    • 用户服务:个人信息维护、订单历史查询、票务状态跟踪;
    • 系统管理:数据统计分析、系统参数配置、轮播图管理。
  3. 业务流程

    • 购票流程:车次查询→选择座位→确认订单→支付完成;
    • 改签流程:选择订单→查询可改签车次→确认改签→差价处理;
    • 退票流程:选择订单→申请退票→审核处理→退款到账。

3.1.2 非功能性需求

  • 系统性能:支持万级用户并发访问,购票响应时间<1秒;
  • 数据安全:用户敏感信息加密存储,交易数据完整性保障;
  • 系统可用:99.9%的系统可用性,完善的容灾备份机制;
  • 事务一致性:票务库存数据强一致性,防止超卖现象。

3.2 第二步:系统设计——构建整体架构

系统采用分层架构模式,结合微服务思想,确保系统的高可用和易扩展:

3.2.1 系统总体架构

  1. 接入层

    • Web服务器:Nginx负载均衡,静态资源缓存;
    • 安全网关:身份认证、权限验证、请求过滤。
  2. 应用层

    • 用户服务:用户注册登录、信息管理;
    • 票务服务:车次查询、余票检查、订单处理;
    • 订单服务:购票、改签、退票业务处理;
    • 支付服务:支付接口集成、交易状态同步。
  3. 数据层

    • MySQL:业务数据持久化存储;
    • Redis:热点数据缓存、会话管理;
    • 消息队列:异步处理高并发请求。

3.2.2 核心数据库设计

系统设计多个核心业务表,确保票务数据的准确性和业务连续性:

表名核心字段作用
checixinxi(车次信息表)id、checimingcheng、huochemingcheng、chepai、qidianzhan、zhongdianzhan、tujing、riqi、chufashijian、shizhang、zuoweileixing、jiage、piaoshu存储车次详细信息与票务数据
chexingxinxi(车型信息表)id、huochebianhao、huochemingcheng、shisu、zuoweishu、chepai存储火车车型配置信息
goupiaodingdan(购票订单表)id、dingdanbianhao、checimingcheng、chepai、qidianzhan、zhongdianzhan、chufashijian、zuoweileixing、jiage、piaoshu、zongjiage、goumairiqi、yonghuming、xingming、shouji、shenfenzheng存储用户购票订单记录
gaiqiandingdan(改签订单表)id、dingdanbianhao、checimingcheng、chepai、qidianzhan、zhongdianzhan、zongjiage、gaiqianriqi、yonghuming、xingming、shouji存储用户改签申请记录

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现系统核心功能,重点解决"高并发购票""订单管理""票务业务"等核心场景:

3.3.1 车次信息管理功能实现

@RestController
@RequestMapping("/api/train")
public class TrainInfoController {
    
    @Autowired
    private TrainService trainService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 查询车次信息(支持多条件筛选)
     */
    @GetMapping("/schedule")
    public ResponseEntity<?> getTrainSchedule(
            @RequestParam String qidianzhan,
            @RequestParam String zhongdianzhan,
            @RequestParam String riqi,
            @RequestParam(required = false) String zuoweileixing) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(qidianzhan) || StringUtils.isEmpty(zhongdianzhan)) {
                return ResponseEntity.badRequest().body("出发站和到达站不能为空");
            }
            
            // 构建查询条件
            TrainQuery query = new TrainQuery();
            query.setQidianzhan(qidianzhan);
            query.setZhongdianzhan(zhongdianzhan);
            query.setRiqi(riqi);
            query.setZuoweileixing(zuoweileixing);
            
            // 先查缓存
            String cacheKey = "train_schedule:" + qidianzhan + "_" + zhongdianzhan + "_" + riqi;
            List<TrainSchedule> cachedResult = (List<TrainSchedule>) redisTemplate.opsForValue().get(cacheKey);
            
            if (cachedResult != null) {
                return ResponseEntity.ok(cachedResult);
            }
            
            // 缓存未命中,查询数据库
            List<TrainSchedule> result = trainService.getTrainSchedule(query);
            
            // 写入缓存,有效期5分钟
            redisTemplate.opsForValue().set(cacheKey, result, Duration.ofMinutes(5));
            
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("查询车次信息失败:" + e.getMessage());
        }
    }
    
    /**
     * 获取车次余票信息
     */
    @GetMapping("/ticket/remaining")
    public ResponseEntity<?> getRemainingTickets(
            @RequestParam String checimingcheng,
            @RequestParam String riqi,
            @RequestParam String zuoweileixing) {
        try {
            // 从Redis获取实时余票
            String ticketKey = "ticket_remaining:" + checimingcheng + "_" + riqi + "_" + zuoweileixing;
            Integer remaining = (Integer) redisTemplate.opsForValue().get(ticketKey);
            
            if (remaining == null) {
                // 缓存未命中,查询数据库
                remaining = trainService.getRemainingTicketsFromDB(checimingcheng, riqi, zuoweileixing);
                redisTemplate.opsForValue().set(ticketKey, remaining, Duration.ofSeconds(30));
            }
            
            Map<String, Object> result = new HashMap<>();
            result.put("checimingcheng", checimingcheng);
            result.put("zuoweileixing", zuoweileixing);
            result.put("remaining", remaining);
            
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("查询余票失败:" + e.getMessage());
        }
    }
    
    /**
     * 管理员添加车次信息
     */
    @PostMapping("/admin/add")
    public ResponseEntity<?> addTrainInfo(@RequestBody TrainAddDTO addDTO) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(addDTO.getChecimingcheng()) || 
                StringUtils.isEmpty(addDTO.getHuochemingcheng())) {
                return ResponseEntity.badRequest().body("车次名称和火车名称不能为空");
            }
            
            // 检查车次是否已存在
            boolean exists = trainService.checkTrainExists(addDTO.getChecimingcheng());
            if (exists) {
                return ResponseEntity.badRequest().body("该车次信息已存在");
            }
            
            TrainInfo trainInfo = new TrainInfo();
            trainInfo.setChecimingcheng(addDTO.getChecimingcheng());
            trainInfo.setHuochemingcheng(addDTO.getHuochemingcheng());
            trainInfo.setChepai(addDTO.getChepai());
            trainInfo.setQidianzhan(addDTO.getQidianzhan());
            trainInfo.setZhongdianzhan(addDTO.getZhongdianzhan());
            trainInfo.setTujing(addDTO.getTujing());
            trainInfo.setRiqi(addDTO.getRiqi());
            trainInfo.setChufashijian(addDTO.getChufashijian());
            trainInfo.setShizhang(addDTO.getShizhang());
            trainInfo.setZuoweileixing(addDTO.getZuoweileixing());
            trainInfo.setJiage(addDTO.getJiage());
            trainInfo.setPiaoshu(addDTO.getPiaoshu());
            trainInfo.setAddtime(new Date());
            
            trainService.addTrainInfo(trainInfo);
            
            // 清理相关缓存
            redisTemplate.delete("train_schedule:*");
            
            return ResponseEntity.ok("车次信息添加成功");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("添加车次信息失败:" + e.getMessage());
        }
    }
}

3.3.2 购票订单功能实现

@Service
@Transactional
public class TicketOrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private TrainService trainService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    /**
     * 用户购票(高并发处理)
     */
    public OrderResult purchaseTicket(TicketPurchaseDTO purchaseDTO) {
        String userKey = "user_purchase:" + purchaseDTO.getYonghuming();
        String ticketKey = "ticket_lock:" + purchaseDTO.getChecimingcheng() + "_" + 
                          purchaseDTO.getRiqi() + "_" + purchaseDTO.getZuoweileixing();
        
        // 用户购票频率控制
        if (redisTemplate.hasKey(userKey)) {
            throw new RuntimeException("操作过于频繁,请稍后再试");
        }
        
        // 分布式锁,防止超卖
        Boolean lockAcquired = redisTemplate.opsForValue()
            .setIfAbsent(ticketKey, "locked", Duration.ofSeconds(10));
        
        if (!lockAcquired) {
            throw new RuntimeException("系统繁忙,请重试");
        }
        
        try {
            // 检查余票
            Integer remaining = trainService.getRemainingTicketsFromDB(
                purchaseDTO.getChecimingcheng(), purchaseDTO.getRiqi(), purchaseDTO.getZuoweileixing());
            
            if (remaining < purchaseDTO.getPiaoshu()) {
                throw new RuntimeException("余票不足");
            }
            
            // 生成订单
            TicketOrder order = new TicketOrder();
            order.setDingdanbianhao(generateOrderNumber());
            order.setChecimingcheng(purchaseDTO.getChecimingcheng());
            order.setChepai(purchaseDTO.getChepai());
            order.setQidianzhan(purchaseDTO.getQidianzhan());
            order.setZhongdianzhan(purchaseDTO.getZhongdianzhan());
            order.setChufashijian(purchaseDTO.getChufashijian());
            order.setZuoweileixing(purchaseDTO.getZuoweileixing());
            order.setJiage(purchaseDTO.getJiage());
            order.setPiaoshu(purchaseDTO.getPiaoshu());
            order.setZongjiage(purchaseDTO.getJiage() * purchaseDTO.getPiaoshu());
            order.setGoumairiqi(new Date());
            order.setYonghuming(purchaseDTO.getYonghuming());
            order.setXingming(purchaseDTO.getXingming());
            order.setShouji(purchaseDTO.getShouji());
            order.setShenfenzheng(purchaseDTO.getShenfenzheng());
            order.setStatus("待支付");
            
            orderMapper.insertOrder(order);
            
            // 减少余票
            trainService.decreaseRemainingTickets(
                purchaseDTO.getChecimingcheng(), purchaseDTO.getRiqi(), 
                purchaseDTO.getZuoweileixing(), purchaseDTO.getPiaoshu());
            
            // 更新缓存
            redisTemplate.opsForValue().set(userKey, "locked", Duration.ofSeconds(5));
            redisTemplate.delete("ticket_remaining:" + purchaseDTO.getChecimingcheng() + 
                               "_" + purchaseDTO.getRiqi() + "_" + purchaseDTO.getZuoweileixing());
            
            // 发送订单创建消息
            rabbitTemplate.convertAndSend("order.exchange", "order.create", order);
            
            return new OrderResult(true, "购票成功", order.getDingdanbianhao());
            
        } finally {
            // 释放锁
            redisTemplate.delete(ticketKey);
        }
    }
    
    /**
     * 改签处理
     */
    public ChangeOrder changeTicket(ChangeRequestDTO changeDTO) {
        // 验证原订单
        TicketOrder originalOrder = orderMapper.selectOrderByNumber(changeDTO.getOriginalOrderNumber());
        if (originalOrder == null) {
            throw new RuntimeException("原订单不存在");
        }
        
        if (!"已支付".equals(originalOrder.getStatus())) {
            throw new RuntimeException("只有已支付的订单可以改签");
        }
        
        // 检查新车次余票
        Integer remaining = trainService.getRemainingTicketsFromDB(
            changeDTO.getNewChecimingcheng(), changeDTO.getNewRiqi(), changeDTO.getNewZuoweileixing());
        
        if (remaining < originalOrder.getPiaoshu()) {
            throw new RuntimeException("新车次余票不足");
        }
        
        // 创建改签订单
        ChangeOrder changeOrder = new ChangeOrder();
        changeOrder.setDingdanbianhao(changeDTO.getOriginalOrderNumber());
        changeOrder.setChecimingcheng(changeDTO.getNewChecimingcheng());
        changeOrder.setChepai(changeDTO.getNewChepai());
        changeOrder.setQidianzhan(changeDTO.getNewQidianzhan());
        changeOrder.setZhongdianzhan(changeDTO.getNewZhongdianzhan());
        changeOrder.setZongjiage(calculateChangePrice(originalOrder, changeDTO));
        changeOrder.setGaiqianriqi(new Date());
        changeOrder.setYonghuming(originalOrder.getYonghuming());
        changeOrder.setXingming(originalOrder.getXingming());
        changeOrder.setShouji(originalOrder.getShouji());
        changeOrder.setStatus("待审核");
        
        orderMapper.insertChangeOrder(changeOrder);
        
        return changeOrder;
    }
    
    /**
     * 生成订单编号
     */
    private String generateOrderNumber() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String timeStr = sdf.format(new Date());
        Random random = new Random();
        return "TK" + timeStr + String.format("%04d", random.nextInt(10000));
    }
    
    /**
     * 计算改签差价
     */
    private Double calculateChangePrice(TicketOrder originalOrder, ChangeRequestDTO changeDTO) {
        // 获取新车次价格
        Double newPrice = trainService.getTicketPrice(
            changeDTO.getNewChecimingcheng(), changeDTO.getNewZuoweileixing());
        
        Double difference = (newPrice - originalOrder.getJiage()) * originalOrder.getPiaoshu();
        return Math.max(difference, 0.0); // 只计算需要补的差价
    }
}

3.3.3 用户服务功能实现

@RestController
@RequestMapping("/api/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderService orderService;
    
    /**
     * 用户注册
     */
    @PostMapping("/register")
    public ResponseEntity<?> userRegister(@RequestBody UserRegisterDTO registerDTO) {
        try {
            // 参数验证
            if (StringUtils.isEmpty(registerDTO.getYonghuming()) || 
                StringUtils.isEmpty(registerDTO.getMima())) {
                return ResponseEntity.badRequest().body("用户名和密码不能为空");
            }
            
            // 验证用户名是否已存在
            boolean exists = userService.checkUserExists(registerDTO.getYonghuming());
            if (exists) {
                return ResponseEntity.badRequest().body("用户名已存在");
            }
            
            // 身份证格式验证
            if (!isValidIdCard(registerDTO.getShenfenzheng())) {
                return ResponseEntity.badRequest().body("身份证格式不正确");
            }
            
            // 手机号格式验证
            if (!isValidPhone(registerDTO.getShouji())) {
                return ResponseEntity.badRequest().body("手机号格式不正确");
            }
            
            User user = new User();
            user.setYonghuming(registerDTO.getYonghuming());
            user.setMima(passwordEncoder.encode(registerDTO.getMima())); // 密码加密
            user.setXingming(registerDTO.getXingming());
            user.setXingbie(registerDTO.getXingbie());
            user.setTouxiang(registerDTO.getTouxiang());
            user.setShenfenzheng(registerDTO.getShenfenzheng());
            user.setShouji(registerDTO.getShouji());
            user.setAddtime(new Date());
            
            userService.registerUser(user);
            
            return ResponseEntity.ok("用户注册成功");
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("注册失败:" + e.getMessage());
        }
    }
    
    /**
     * 获取用户订单列表
     */
    @GetMapping("/orders")
    public ResponseEntity<?> getUserOrders(
            @RequestParam String yonghuming,
            @RequestParam(required = false) String status,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        try {
            OrderQuery query = new OrderQuery();
            query.setYonghuming(yonghuming);
            query.setStatus(status);
            query.setPage(page);
            query.setSize(size);
            
            PageResult<OrderVO> result = orderService.getUserOrders(query);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("获取订单列表失败:" + e.getMessage());
        }
    }
    
    /**
     * 申请退票
     */
    @PostMapping("/refund/apply")
    public ResponseEntity<?> applyRefund(@RequestBody RefundApplyDTO applyDTO) {
        try {
            RefundOrder refundOrder = orderService.applyRefund(applyDTO);
            return ResponseEntity.ok(refundOrder);
        } catch (Exception e) {
            return ResponseEntity.internalServerError()
                .body("退票申请失败:" + e.getMessage());
        }
    }
    
    /**
     * 身份证验证
     */
    private boolean isValidIdCard(String idCard) {
        if (idCard == null || idCard.length() != 18) {
            return false;
        }
        // 更详细的身份证验证逻辑
        return idCard.matches("\\d{17}[\\dXx]");
    }
    
    /**
     * 手机号验证
     */
    private boolean isValidPhone(String phone) {
        return phone != null && phone.matches("1[3-9]\\d{9}");
    }
}

3.4 第四步:前端界面实现——双角色适配界面

基于JSP + Bootstrap构建适配管理员与用户的差异化界面,遵循"铁路风格、简洁实用"的设计原则:

3.4.1 用户功能界面

  • 首页:铁路主题轮播图,快速购票入口,热门车次推荐,公告信息展示;
  • 车次查询:出发站/到达站选择,日期选择,车次类型筛选,实时余票显示;
  • 购票流程:车次选择→座位选择→乘客信息→订单确认→支付完成;
  • 订单管理:购票订单查看,改签申请,退票操作,订单状态跟踪;
  • 个人中心:个人信息维护,常用乘客管理,订单历史查询。

3.4.2 管理员功能界面

  • 车次管理:车型信息配置,车次时刻表维护,票价设置;
  • 订单管理:购票订单查询,改签审核,退票处理;
  • 用户管理:用户信息查看,账户状态管理;
  • 数据统计:票务销售统计,车次上座率分析,营收报表;
  • 系统设置:轮播图管理,系统参数配置,数据备份。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3.5 第五步:系统测试——确保系统稳定可靠

通过全方位测试策略,验证火车订票管理系统的高并发性能与业务准确性:

3.5.1 功能测试

设计覆盖核心业务场景的测试用例:

测试场景测试用例预期结果实际结果是否通过
用户注册填写完整信息提交注册成功,跳转登录页注册成功,跳转登录页
车次查询输入北京-上海,选择日期显示相关车次信息显示相关车次信息
购票流程选择车次、座位、填写乘客信息生成待支付订单生成待支付订单
改签申请对已支付订单申请改签改签申请提交成功改签申请提交成功
退票操作申请退票并确认退票申请待审核退票申请待审核

3.5.2 性能与压力测试

  • 并发测试:模拟1000用户同时购票,系统响应正常,无超卖现象;
  • 数据一致性:票务库存数据准确,订单状态同步及时;
  • 安全测试:用户信息加密存储,SQL注入防护有效;
  • 容灾测试:数据库故障时系统降级方案正常运作。

3.6 第六步:问题排查与优化——提升系统体验

开发过程中的核心问题及解决方案:

  1. 问题:高并发场景下票务超卖
    解决方案:Redis分布式锁 + 数据库悲观锁双重保障,消息队列异步处理。

  2. 问题:车次查询响应缓慢
    解决方案:Redis缓存热点数据,数据库查询优化,索引建立。

  3. 问题:改签业务逻辑复杂
    解决方案:状态模式管理订单生命周期,策略模式处理差价计算。

  4. 问题:用户操作频繁导致系统负载高
    解决方案:接口限流,操作频率控制,缓存击穿防护。

四、毕业设计复盘:经验总结与实践建议

4.1 开发过程中的技术挑战

  1. 高并发处理:票务系统需要应对节假日等高峰期的巨大访问压力;
  2. 数据一致性:票务库存、订单状态等关键数据需要强一致性保证;
  3. 业务流程复杂:购票、改签、退票等业务流程涉及多状态转换;
  4. 系统安全性:用户敏感信息、支付数据需要全方位安全防护。

4.2 给后续开发者的建议

  1. 微服务架构:将系统拆分为用户服务、订单服务、票务服务等独立微服务;
  2. 读写分离:MySQL主从复制,读操作分流到从库,提升系统吞吐量;
  3. 分布式事务:引入Seata等分布式事务解决方案,保证数据一致性;
  4. 监控告警:集成Prometheus + Grafana实现系统监控和业务告警;
  5. 容器化部署:使用Docker + Kubernetes实现快速部署和弹性伸缩。

五、项目资源与发展展望

5.1 项目核心资源

本项目提供完整的开发与部署资料:

  • 后端源码:完整的Spring Boot项目源码(含业务逻辑层实现);
  • 前端资源:JSP页面文件、CSS/JS样式、铁路主题素材;
  • 数据库脚本:MySQL建表语句、初始化数据、索引优化方案;
  • 部署文档:环境配置指南、系统部署步骤、性能调优建议;
  • API文档:基于Swagger的RESTful接口文档。

5.2 系统扩展方向

  1. 移动端应用:开发iOS和Android移动APP,支持手机购票;
  2. 智能推荐:基于用户出行历史推荐合适车次和座位;
  3. 多支付渠道:集成微信支付、支付宝、银联等多种支付方式;
  4. 电子客票:实现无纸化乘车,二维码检票功能;
  5. 会员体系:建立铁路会员制度,积分兑换和特权服务;
  6. 智能客服:集成AI客服机器人,自动解答用户咨询;
  7. 大数据分析:用户行为分析,票务销售预测,运力优化建议。

如果本文对您的Spring Boot学习、火车订票管理系统相关毕业设计有帮助,欢迎点赞 + 收藏 + 关注,后续会分享更多交通类管理系统项目实战案例!