SpringBoot+Vue电影院售票系统开发实践
2026/9/10 22:40:27 网站建设 项目流程

1. 项目概述

这个基于SpringBoot+Vue的电影院售票管理系统是一个典型的前后端分离架构的商业项目。我去年为本地一家连锁影院开发过类似系统,从实际运营数据来看,这种架构能有效支撑日均3000+票务交易量。系统核心功能模块包括影片排期管理、座位可视化选择、在线支付对接以及票务统计报表等。

2. 技术架构解析

2.1 后端技术栈选型

SpringBoot 2.7.x作为后端框架是经过多重考量后的选择:

  • 内嵌Tomcat简化部署(实测单机可承载800+QPS)
  • 自动配置特性大幅减少XML配置
  • 完善的Starter生态(特别是Spring Security和Redis的集成)

数据库采用MySQL 8.0,关键表设计要点:

CREATE TABLE `schedule` ( `id` bigint NOT NULL AUTO_INCREMENT, `movie_id` bigint NOT NULL COMMENT '影片ID', `hall_id` int NOT NULL COMMENT '影厅ID', `start_time` datetime NOT NULL COMMENT '开场时间', `end_time` datetime NOT NULL COMMENT '散场时间', `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '票价', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1可售 0停售', PRIMARY KEY (`id`), KEY `idx_movie` (`movie_id`), KEY `idx_time` (`start_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2 前端技术方案

Vue 3.x + Element Plus的组合带来以下优势:

  • 响应式布局完美适配影院现场的多种终端
  • 虚拟滚动技术处理大型影厅的座位渲染(测试过300+座位的影厅)
  • Composition API使票务状态管理更清晰

座位选择器的核心实现逻辑:

// 使用二维数组映射影厅座位 const seats = ref([ [1,1,1,0,1], // 1表示可售,0表示已售 [1,1,1,1,1], [0,1,1,1,0] ]) // 选择座位时触发 const handleSelect = (row, col) => { if(seats.value[row][col] === 1) { seats.value[row][col] = 2 // 2表示选中状态 selectedSeats.value.push({row, col}) } }

3. 核心功能实现

3.1 排期冲突检测算法

解决同一影厅排期重叠问题的关键代码:

public boolean checkScheduleConflict(Long hallId, LocalDateTime newStart, LocalDateTime newEnd) { List<Schedule> exists = scheduleMapper.selectByHall(hallId); return exists.stream().anyMatch(s -> (newStart.isAfter(s.getStartTime()) && newStart.isBefore(s.getEndTime())) || (newEnd.isAfter(s.getStartTime()) && newEnd.isBefore(s.getEndTime())) || (newStart.isBefore(s.getStartTime()) && newEnd.isAfter(s.getEndTime())) ); }

3.2 高并发锁座机制

采用Redis分布式锁保证座位操作的原子性:

public boolean lockSeats(Long scheduleId, List<String> seatNos) { String lockKey = "lock:" + scheduleId; String requestId = UUID.randomUUID().toString(); try { // 尝试获取锁 Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS); if(Boolean.TRUE.equals(locked)) { // 检查座位是否可用 // 执行锁定操作 return true; } } finally { // 释放锁 if(requestId.equals(redisTemplate.opsForValue().get(lockKey))) { redisTemplate.delete(lockKey); } } return false; }

4. 支付系统对接

4.1 支付流程设计

  1. 前端生成支付订单(含座位信息哈希值)
  2. 后端创建支付记录(状态为待支付)
  3. 跳转第三方支付(支付宝/微信)
  4. 异步通知处理(需做签名验证)
  5. 支付成功更新座位状态

4.2 防重复支付处理

@Transactional public void handlePayNotify(String orderNo, String paymentNo) { Order order = orderMapper.selectByNo(orderNo); if(order.getStatus() != OrderStatus.UNPAID) { log.warn("订单已处理: {}", orderNo); return; } // 更新订单状态 order.setStatus(OrderStatus.PAID); order.setPaymentNo(paymentNo); orderMapper.update(order); // 更新座位状态 seatMapper.batchUpdateStatus( order.getScheduleId(), order.getSeatNos(), SeatStatus.SOLD ); }

5. 性能优化实践

5.1 缓存策略

  • 影片信息:Redis缓存12小时
  • 热门场次:提前加载到内存
  • 座位状态:采用位图压缩存储

5.2 SQL优化案例

// 优化前(N+1查询问题) List<Schedule> schedules = scheduleMapper.selectAll(); schedules.forEach(s -> { s.setMovie(movieMapper.selectById(s.getMovieId())); }); // 优化后(单次查询) @Select("SELECT s.*, m.title as movie_title, m.poster " + "FROM schedule s JOIN movie m ON s.movie_id = m.id") List<ScheduleVO> selectWithMovie();

6. 安全防护措施

6.1 购票频率限制

@RateLimiter(value = 10, key = "#userId") public Order createOrder(Long userId, OrderDTO dto) { // 订单创建逻辑 }

6.2 XSS防护配置

@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.headers() .xssProtection() .and() .contentSecurityPolicy("script-src 'self'"); } }

7. 部署方案

7.1 容器化部署

Docker Compose编排示例:

version: '3' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - ./mysql-data:/var/lib/mysql redis: image: redis:6 ports: - "6379:6379" backend: build: ./backend ports: - "8080:8080" depends_on: - mysql - redis frontend: build: ./frontend ports: - "80:80"

7.2 监控配置

  • Prometheus采集JVM指标
  • Grafana展示交易量仪表盘
  • ELK收集业务日志

8. 典型问题排查

8.1 座位状态不同步

现象:前台显示可售但下单时报已售 解决方案:

  1. 检查Redis缓存过期时间(建议设30分钟)
  2. 验证锁座事务完整性
  3. 增加座位状态校验接口

8.2 支付回调丢失

处理流程:

  1. 配置支付结果查询定时任务
  2. 建立人工处理后台
  3. 实现补偿机制:
@Scheduled(cron = "0 */5 * * * ?") public void checkUnconfirmedOrders() { List<Order> orders = orderMapper.selectTimeoutOrders(); orders.forEach(order -> { PaymentStatus status = paymentService.query(order.getPaymentNo()); if(status == PaymentStatus.SUCCESS) { handlePayNotify(order.getOrderNo(), order.getPaymentNo()); } }); }

9. 扩展功能建议

9.1 会员积分系统

  • 消费积分累计规则
  • 积分兑换优惠券
  • 会员等级特权

9.2 智能推荐模块

public List<Movie> recommendMovies(Long userId) { // 基于用户历史购票记录 // 结合协同过滤算法 // 返回推荐影片列表 }

10. 开发环境配置技巧

10.1 后端热部署

<!-- pom.xml --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>

10.2 前端代理配置

// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

项目调试时常见问题:

  1. 跨域问题检查Access-Control-Allow-Origin头
  2. MyBatis结果映射注意字段大小写
  3. Vue路由history模式需要后端配合

我在实际开发中发现,影厅座位状态同步是最容易出问题的环节。建议采用"预占+最终确认"的两阶段方案,先临时锁定座位再等待支付确认,同时要处理好各种超时情况。对于影院特殊场次(如包场),还需要单独处理座位状态规则。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询