1. 项目概述
这个电影票销售管理系统采用SpringBoot+Vue前后端分离架构,是一个典型的毕业设计级别项目。我在实际开发过程中发现,这类系统虽然业务逻辑不算复杂,但完整实现了从选座购票到后台管理的全流程功能,特别适合计算机专业学生用来练手全栈开发技术。
系统最核心的价值在于:通过一个真实可用的商业场景,把SpringBoot后端开发、Vue前端工程化、数据库设计等关键技术点都串联起来。相比那些玩具级的Demo项目,这个系统包含了用户认证、支付对接、座位锁定等真实业务场景必须处理的细节问题。
2. 技术架构解析
2.1 SpringBoot后端设计
后端采用经典的MVC分层架构:
- Controller层:处理HTTP请求,参数校验
- Service层:核心业务逻辑实现
- Repository层:数据库访问
- Model层:实体类定义
我特别推荐使用Spring Data JPA而不是MyBatis,因为对于毕业设计来说:
- 开发效率更高,基本不用写SQL
- 自动生成数据库表结构
- 内置分页、排序等常用功能
关键配置示例:
java复制@SpringBootApplication
@EnableJpaRepositories
public class TicketApplication {
public static void main(String[] args) {
SpringApplication.run(TicketApplication.class, args);
}
}
2.2 Vue前端工程化
前端采用Vue CLI创建的工程化项目结构:
code复制src/
├── api/ # 接口封装
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── views/ # 页面组件
└── main.js # 入口文件
特别要注意的是axios的全局配置:
javascript复制axios.defaults.baseURL = 'http://localhost:8080'
axios.interceptors.request.use(config => {
if (localStorage.token) {
config.headers.Authorization = 'Bearer ' + localStorage.token
}
return config
})
3. 核心功能实现
3.1 影院座位可视化选座
这是系统最具挑战性的功能点,我采用了两种实现方案对比:
方案A:基于Canvas绘制
- 优点:性能好,适合大规模座位
- 缺点:交互逻辑需要自己实现
方案B:基于CSS Grid布局
- 优点:开发简单,响应式好
- 缺点:超过200个座位性能下降
最终选择方案B,因为:
- 毕业设计场景下座位数有限
- 更符合Vue的组件化思想
- 移动端适配更方便
关键代码片段:
vue复制<template>
<div class="seat-map">
<div v-for="row in seats" :key="row.id" class="seat-row">
<div
v-for="seat in row.seats"
:key="seat.id"
:class="['seat', seat.status]"
@click="selectSeat(seat)"
></div>
</div>
</div>
</template>
3.2 支付流程设计
支付流程的时序特别重要,我总结的最佳实践是:
- 前端生成订单 -> 2. 后端创建待支付订单 -> 3. 调用支付接口 -> 4. 支付成功回调 -> 5. 更新订单状态
最容易出错的是支付超时处理,我的解决方案是:
java复制@Scheduled(fixedRate = 60000)
public void checkPaymentTimeout() {
List<Order> unpaidOrders = orderRepository.findByStatusAndCreateTimeBefore(
OrderStatus.PENDING,
LocalDateTime.now().minusMinutes(15)
);
unpaidOrders.forEach(order -> {
order.setStatus(OrderStatus.CANCELLED);
unlockSeats(order.getSeatIds());
});
orderRepository.saveAll(unpaidOrders);
}
4. 数据库设计要点
4.1 核心表结构
sql复制CREATE TABLE movie (
id BIGINT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
duration INT NOT NULL,
rating VARCHAR(10)
);
CREATE TABLE screening (
id BIGINT PRIMARY KEY,
movie_id BIGINT NOT NULL,
theater_id BIGINT NOT NULL,
start_time DATETIME NOT NULL,
price DECIMAL(10,2) NOT NULL
);
CREATE TABLE seat (
id BIGINT PRIMARY KEY,
theater_id BIGINT NOT NULL,
row_num INT NOT NULL,
col_num INT NOT NULL,
UNIQUE KEY (theater_id, row_num, col_num)
);
4.2 事务处理示例
订票必须使用事务,这是我在测试环境踩过的坑:
java复制@Transactional
public Order createOrder(Long userId, Long screeningId, List<Long> seatIds) {
// 1. 检查座位是否可用
List<Seat> seats = seatRepository.findAllById(seatIds);
if (seats.stream().anyMatch(s -> s.getStatus() != SeatStatus.AVAILABLE)) {
throw new SeatNotAvailableException();
}
// 2. 锁定座位
seats.forEach(seat -> seat.setStatus(SeatStatus.LOCKED));
seatRepository.saveAll(seats);
// 3. 创建订单
Order order = new Order();
order.setUserId(userId);
order.setScreeningId(screeningId);
order.setSeatIds(seatIds);
order.setStatus(OrderStatus.PENDING);
return orderRepository.save(order);
}
5. 部署与上线
5.1 后端部署要点
推荐使用Docker部署,这是我的Dockerfile:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/ticket-system-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
启动命令:
bash复制docker build -t ticket-system .
docker run -d -p 8080:8080 --name ticket ticket-system
5.2 前端部署优化
生产环境一定要做这些优化:
- 启用gzip压缩
- 配置合适的缓存策略
- 使用CDN加速静态资源
nginx配置示例:
nginx复制server {
listen 80;
server_name ticket.example.com;
location / {
root /var/www/ticket;
try_files $uri $uri/ /index.html;
gzip on;
gzip_types text/plain application/javascript text/css;
}
location /api {
proxy_pass http://localhost:8080;
}
}
6. 常见问题排查
6.1 跨域问题解决方案
开发环境最常见的跨域问题,我的终极解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
同时前端axios需要配置:
javascript复制axios.defaults.withCredentials = true
6.2 Vue路由刷新404问题
这是单页应用的经典问题,解决方法:
- 配置nginx的try_files
- 或者使用hash模式路由
vue-router配置:
javascript复制const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
7. 项目扩展建议
如果想把这个毕业设计做得更出彩,可以考虑:
- 增加推荐算法(基于用户历史记录推荐电影)
- 实现分布式锁处理并发订票
- 加入影院地图导航功能
- 开发微信小程序版本
我特别推荐实现分布式锁,这是面试时的加分项:
java复制public boolean lockSeats(List<Long> seatIds) {
String lockKey = "seat_lock:" + String.join(",", seatIds.stream()
.map(String::valueOf)
.collect(Collectors.toList()));
return redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", Duration.ofMinutes(5));
}
这个项目我前后迭代了3个版本,最大的体会是:真实的业务场景远比课本上的例子复杂,特别是要处理好各种边界条件和异常情况。建议学弟学妹们在开发时,一定要先画好流程图和状态转换图,这样能避免很多后期返工。
