1. 项目概述
作为一个拥有多年开发经验的Java工程师,我想分享一个基于Spring Boot的电影购票系统的完整实现方案。这个系统是我最近完成的一个毕业设计项目,采用了当前流行的前后端分离架构,前端使用Vue.js,后端采用Spring Boot+MyBatis Plus技术栈,数据库选用MySQL。系统实现了从电影展示、选座购票到订单管理的完整业务流程,为影院和用户提供了一个高效便捷的在线购票平台。
这个系统的核心价值在于:
- 为影院提供了一套完整的数字化管理解决方案
- 为用户提供了便捷的在线购票体验
- 采用了现代化的技术架构,具有良好的扩展性和维护性
- 实现了高并发场景下的票务处理能力
2. 技术选型与架构设计
2.1 技术栈解析
后端技术栈:
- Spring Boot 2.7.x:作为基础框架,提供了自动配置、快速开发等特性
- MyBatis Plus 3.5.x:简化了数据库操作,内置通用CRUD方法
- Spring Security:处理用户认证和授权
- Redis:用于缓存热点数据和分布式锁实现
- RabbitMQ:处理异步消息,如订单超时取消
前端技术栈:
- Vue 3.x:前端框架,提供响应式数据绑定和组件化开发
- Element Plus:UI组件库,快速构建美观界面
- Axios:处理HTTP请求
- Vue Router:前端路由管理
- Vuex:状态管理
数据库:
- MySQL 8.0:关系型数据库,存储业务数据
- Redis 6.x:缓存数据库,提高系统响应速度
2.2 系统架构设计
系统采用典型的分层架构:
code复制┌───────────────────────────────────────┐
│ 客户端层 │
│ (Web、App、小程序等接入端) │
└───────────────────────────────────────┘
↑ ↓
┌───────────────────────────────────────┐
│ 网关层 │
│ (统一认证、限流、路由转发) │
└───────────────────────────────────────┘
↑ ↓
┌───────────────────────────────────────┐
│ 业务应用层 │
│ (用户服务、电影服务、订单服务等) │
└───────────────────────────────────────┘
↑ ↓
┌───────────────────────────────────────┐
│ 数据访问层 │
│ (MyBatis Plus、Redis访问等) │
└───────────────────────────────────────┘
↑ ↓
┌───────────────────────────────────────┐
│ 存储层 │
│ (MySQL、Redis、文件存储等) │
└───────────────────────────────────────┘
这种架构的优势在于:
- 各层职责明确,便于维护和扩展
- 可以针对不同层进行独立优化
- 支持水平扩展,应对高并发场景
3. 核心功能实现
3.1 用户模块
3.1.1 用户注册流程
用户注册采用了标准的邮箱/手机号验证流程:
- 前端收集用户基本信息(用户名、密码、邮箱/手机号等)
- 后端验证信息合法性
- 发送验证码(邮箱或短信)
- 用户输入验证码完成注册
关键代码实现:
java复制@PostMapping("/register")
public Result register(@RequestBody UserRegisterDTO dto) {
// 验证验证码
String cacheCode = redisTemplate.opsForValue().get(REGISTER_CODE_KEY + dto.getEmail());
if (!dto.getCode().equals(cacheCode)) {
return Result.fail("验证码错误");
}
// 检查用户名是否已存在
if (userService.existsUsername(dto.getUsername())) {
return Result.fail("用户名已存在");
}
// 创建用户
User user = new User();
BeanUtils.copyProperties(dto, user);
// 密码加密
user.setPassword(passwordEncoder.encode(dto.getPassword()));
userService.save(user);
// 清除验证码缓存
redisTemplate.delete(REGISTER_CODE_KEY + dto.getEmail());
return Result.success();
}
3.1.2 登录认证实现
系统采用JWT进行无状态认证:
- 用户输入用户名密码
- 服务端验证通过后生成JWT令牌
- 客户端存储令牌并在后续请求中携带
安全考虑:
- 使用HTTPS传输
- JWT设置合理过期时间
- 敏感操作需要二次验证
3.2 电影管理模块
3.2.1 电影信息管理
管理员可以添加、编辑电影信息,包括:
- 基本信息(名称、类型、时长等)
- 演职人员信息
- 海报和预告片
- 上映时间
数据库设计:
sql复制CREATE TABLE `movie` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '电影名称',
`cover_url` varchar(255) DEFAULT NULL COMMENT '封面图URL',
`duration` int DEFAULT NULL COMMENT '时长(分钟)',
`release_date` datetime DEFAULT NULL COMMENT '上映日期',
`description` text COMMENT '剧情简介',
`director` varchar(50) DEFAULT NULL COMMENT '导演',
`actors` varchar(500) DEFAULT NULL COMMENT '主演',
`language` varchar(20) DEFAULT NULL COMMENT '语言',
`status` tinyint DEFAULT '0' COMMENT '状态:0-待上映 1-热映中 2-已下架',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_release_date` (`release_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电影信息表';
3.2.2 排片管理
影院管理员可以设置放映计划:
- 选择影厅
- 设置放映时间
- 设置票价
- 锁定座位(如维修中座位)
关键业务逻辑:
- 同一影厅的放映时间不能重叠
- 票价可以根据时段、座位区域差异化设置
3.3 订单模块
3.3.1 购票流程
- 用户选择电影场次
- 进入选座页面,实时显示可选座位
- 选择座位并确认订单
- 支付订单
- 出票成功
关键技术点:
- 座位锁定使用Redis分布式锁
- 订单状态机管理
- 支付超时自动取消
3.3.2 订单状态设计
java复制public enum OrderStatus {
PENDING_PAYMENT(0, "待支付"),
PAID(1, "已支付"),
COMPLETED(2, "已完成"),
CANCELLED(3, "已取消"),
REFUNDED(4, "已退款");
private final int code;
private final String desc;
// 构造方法、getter省略
}
状态转换规则:
- 待支付 → 已支付(用户支付)
- 待支付 → 已取消(超时未支付)
- 已支付 → 已完成(观影时间过后)
- 已支付 → 已退款(用户申请退款)
4. 高并发场景解决方案
4.1 座位锁定机制
为了防止超卖,系统实现了以下机制:
- 使用Redis缓存场次座位信息
- 选座时先获取分布式锁
- 检查座位状态并临时锁定
- 生成订单后更新座位状态
关键代码:
java复制public boolean lockSeats(Long scheduleId, List<String> seatNumbers) {
String lockKey = "lock:schedule:" + scheduleId;
String uuid = UUID.randomUUID().toString();
try {
// 获取分布式锁
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, uuid, 10, TimeUnit.SECONDS);
if (!locked) {
return false;
}
// 检查并锁定座位
String seatKey = "schedule:seats:" + scheduleId;
for (String seat : seatNumbers) {
Boolean result = redisTemplate.opsForHash().putIfAbsent(seatKey, seat, "locked");
if (result != null && !result) {
// 座位已被锁定,回滚已锁定的座位
rollbackLockedSeats(seatKey, seatNumbers, seat);
return false;
}
}
return true;
} finally {
// 释放锁
if (uuid.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
4.2 订单超时处理
使用RabbitMQ的延迟队列实现订单超时取消:
- 创建订单时发送延迟消息
- 消费者收到消息时检查订单状态
- 如果订单仍为待支付状态,则执行取消操作
配置示例:
java复制@Configuration
public class RabbitMQConfig {
// 订单延迟队列
public static final String ORDER_DELAY_QUEUE = "order.delay.queue";
public static final String ORDER_DELAY_EXCHANGE = "order.delay.exchange";
public static final String ORDER_DELAY_ROUTING_KEY = "order.delay.routingKey";
@Bean
public Queue orderDelayQueue() {
return QueueBuilder.durable(ORDER_DELAY_QUEUE)
.withArgument("x-dead-letter-exchange", ORDER_DLX_EXCHANGE)
.withArgument("x-dead-letter-routing-key", ORDER_DLX_ROUTING_KEY)
.build();
}
@Bean
public DirectExchange orderDelayExchange() {
return new DirectExchange(ORDER_DELAY_EXCHANGE);
}
@Bean
public Binding orderDelayBinding() {
return BindingBuilder.bind(orderDelayQueue())
.to(orderDelayExchange())
.with(ORDER_DELAY_ROUTING_KEY);
}
// 其他配置省略...
}
5. 系统优化实践
5.1 缓存策略
- 电影信息缓存:热映电影信息缓存到Redis,设置合理过期时间
- 场次座位缓存:放映计划开始前2小时加载到Redis
- 多级缓存:本地缓存 + Redis缓存,减少Redis压力
5.2 数据库优化
- 索引优化:为常用查询字段添加合适索引
- 读写分离:查询走从库,写入走主库
- 分表策略:订单表按月分表,避免单表过大
5.3 前端性能优化
- 懒加载:图片和组件按需加载
- CDN加速:静态资源使用CDN分发
- 预加载:用户浏览电影列表时预加载详情页数据
6. 安全防护措施
6.1 常见安全防护
- XSS防护:前端转义HTML特殊字符,后端过滤敏感脚本
- CSRF防护:使用Spring Security的CSRF保护
- SQL注入防护:使用MyBatis预编译语句
- 敏感数据加密:用户密码加盐哈希存储
6.2 支付安全
- 支付密码:重要操作需验证支付密码
- 金额校验:前后端双重校验支付金额
- 对账机制:定期与支付平台对账,发现异常订单
7. 部署方案
7.1 容器化部署
使用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: cinema
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
7.2 监控方案
- Prometheus:收集系统指标
- Grafana:可视化监控数据
- ELK:日志收集和分析
- Sentry:错误追踪
8. 开发心得与建议
在实际开发过程中,我总结了以下几点经验:
- 接口设计先行:前后端先定义好接口规范,可以并行开发
- 异常处理统一:定义全局异常处理器,统一错误响应格式
- 日志记录完善:关键业务操作记录详细日志,便于排查问题
- 测试覆盖全面:单元测试覆盖核心业务逻辑
- 文档及时更新:接口文档随代码变更及时更新
对于初学者,我的建议是:
- 先从核心业务流程开始实现,再考虑优化和扩展
- 合理使用设计模式,但不要过度设计
- 重视代码可读性和可维护性
- 多学习优秀开源项目的设计思路
这个项目从技术选型到最终实现,让我对现代Web应用开发有了更深入的理解。特别是如何处理高并发场景下的数据一致性问题,以及如何设计可扩展的系统架构,这些都是非常有价值的实践经验。