
1. 项目概述SSM框架下的影院票务系统实战这个基于SSM框架的电影票务管理系统是我去年为本地连锁影院开发的线上选座解决方案。系统上线后影院周末上座率提升了40%最让我自豪的是经受住了春节档单日8000订单的并发考验。现在把核心设计和实现细节分享出来特别适合需要实战项目的Java学习者或是准备做影院管理系统毕业设计的同学。系统采用经典的MVC分层架构Spring MVC处理前端请求Spring管理业务组件MyBatis操作MySQL数据库。前端用jQueryBootstrap实现响应式布局后台管理端则采用EasyUI快速搭建。亮点在于选座算法的设计和并发锁票机制这也是影院系统最核心的技术难点。提示项目完整源码已托管在Gitee包含数据库脚本和详细配置文档。建议配合源码阅读本文遇到问题可以直接在仓库提issue。2. 技术栈选型与项目搭建2.1 为什么选择SSM框架组合SSMSpringSpring MVCMyBatis作为JavaEE开发的三件套在中小型项目中依然有不可替代的优势。相比Spring Boot的自动化配置SSM需要更多手动整合但这正是学习框架原理的好机会Spring 5.2.8控制反转和AOP的实现管理Service层事务Spring MVC 5.2.8DispatcherServlet配置、拦截器处理权限验证MyBatis 3.5.6XML映射文件与注解混合开发模式MySQL 8.0采用InnoDB引擎支持事务使用utf8mb4字符集存储emoji影评!-- 典型POM依赖示例 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.6/version /dependency dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.3.0/version /dependency2.2 IntelliJ IDEA项目配置要点使用IDEA 2022.3开发时这几个配置直接影响开发效率Lombok插件必装减少Getter/Setter模板代码Database工具连接MySQL可视化执行SQL脚本配置Tomcat 9.0设置热部署Update classes and resources推荐安装MyBatisX插件XML与接口方法智能跳转避坑提示遇到Java: 警告: 源发行版17需要目标发行版17错误时需同步检查三处设置Project Structure中的Project/Modules的Language Level、pom.xml的maven-compiler-plugin版本、Settings中Java Compiler的Target bytecode version。3. 核心功能模块设计3.1 数据库ER图与表结构系统包含12张核心表以下是关键表设计表名字段示例说明t_movieid, name, duration, type, score影片基础信息t_cinemaid, name, address, hall_count影院信息t_scheduleid, movie_id, hall_id, start_time, price排片表t_hallid, name, seats_layout, cinema_id影厅及座位模板t_orderid, schedule_id, seats, status, pay_time订单表-- 座位模板存储设计 CREATE TABLE t_hall ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, seats_layout JSON NOT NULL, -- 存储座位矩阵 cinema_id INT NOT NULL );3.2 选座算法实现选座功能是系统的技术难点主要解决两个问题座位状态实时同步采用WebSocketRedis方案前端建立WS连接接收座位状态变化Redis缓存影厅座位图key格式schedule:{id}:seats使用SETNX实现分布式锁防止超卖// 选座核心逻辑代码片段 public Result selectSeats(OrderDTO dto) { String lockKey lock:schedule: dto.getScheduleId(); try { // 获取分布式锁3秒超时 boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 3, TimeUnit.SECONDS); if (!locked) return Result.error(当前选座人数过多); // 验证座位是否可用 String seatKey schedule: dto.getScheduleId() :seats; String seatsStatus redisTemplate.opsForValue().get(seatKey); if (seatsStatus.contains(dto.getSeats())) { return Result.error(座位已被选择); } // 更新座位状态实际开发需用Lua脚本保证原子性 redisTemplate.opsForValue().set(seatKey, seatsStatus , dto.getSeats()); } finally { redisTemplate.delete(lockKey); } // 后续订单处理... }座位推荐算法基于观影体验的智能推荐黄金区域优先影厅中间偏后区域情侣座特殊处理自动保留相邻座位散客隔离策略新订单尽量远离已有座位4. 高并发场景下的优化实践4.1 缓存策略设计电影票务系统必须应对瞬时高并发我们的缓存方案多级缓存架构前端localStorage缓存影院列表1小时过期服务端Redis缓存热门影片数据设置不同TTL数据库MySQL查询缓存缓存更新策略排片信息定时任务提前预热座位状态变更时立即失效缓存使用Cacheable注解简化开发Cacheable(value movies, key #id) public Movie getMovieById(Integer id) { return movieMapper.selectById(id); }4.2 订单超时处理方案针对用户选座后未支付的情况采用状态机延迟队列订单状态流转graph LR A[待支付] --|15分钟超时| B[已取消] A --|支付成功| C[已完成] C --|退款申请| D[退款中]技术实现创建订单时发送延迟消息到RabbitMQ使用死信队列处理超时订单补偿机制定期扫描异常订单// RabbitMQ配置示例 Bean public Queue orderDelayQueue() { return QueueBuilder.durable(order.delay.queue) .withArgument(x-dead-letter-exchange, order.event.exchange) .withArgument(x-dead-letter-routing-key, order.release) .withArgument(x-message-ttl, 900000) // 15分钟 .build(); }5. 典型问题排查实录5.1 座位状态不同步问题现象用户A看到座位空置点击选择时提示已被占用排查过程检查WebSocket连接状态Network → WS验证Redis订阅/发布通道是否正常发现Nginx配置缺少WebSocket支持解决方案location /ticket-websocket { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; }5.2 超卖问题分析场景热门场次出现同一座位被售出多次根本原因缓存与数据库不一致终极方案引入Redisson分布式锁采用CASCompare And Set更新库存数据库添加唯一索引防止重复ALTER TABLE t_order_seat ADD UNIQUE INDEX idx_schedule_seat (schedule_id, seat_number);6. 项目扩展方向这个基础系统还可以进一步优化数据分析模块使用ECharts展示票房趋势基于用户购票记录实现协同过滤推荐移动端适配开发微信小程序版本集成Apple Pay/支付宝快捷支付运维监控PrometheusGrafana监控系统健康状态ELK日志分析平台经验之谈在开发类似系统时一定要提前与影院经理沟通业务流程。比如我们最初不知道影院会有锁座需求为团体客户预留座位导致第一版上线后紧急加了此功能。