1. 项目概述与技术栈选型
这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0的电影订票及评论网站系统,是一个典型的全栈Java Web应用。我在实际开发中发现,这种技术组合特别适合中小型商业项目的快速迭代开发。
为什么选择这个技术栈? 从后端来看:
- SpringBoot2.x提供了完善的自动配置和starter依赖,相比传统SSM框架能减少约60%的配置代码量
- MyBatis-Plus的ActiveRecord模式让数据库操作代码量减少50%以上
- MySQL8.0的窗口函数和CTE特性在处理影院排片数据时特别有用
前端选择Vue3而不是Vue2,主要考虑:
- Composition API对复杂业务逻辑(如座位选择、支付流程)的组织更清晰
- 性能提升明显,特别是在动态组件(如电影详情页的Tab切换)渲染上
- 更好的TypeScript支持,这对大型前端项目很关键
提示:实际开发中我发现,Vue3的setup语法糖确实能提升开发效率,但需要特别注意响应式数据的处理方式与Vue2不同。
2. 系统架构设计与核心模块
2.1 后端架构分层
典型的四层架构,但有些特殊设计:
code复制controller
├── interceptor (JWT验证、权限控制)
service
├── impl
│ ├── transaction (事务管理特别重要)
mapper
├── custom (自定义SQL)
model
├── entity
├── dto (前后端交互对象)
├── vo (视图对象)
特别注意:电影座位锁定服务需要额外设计:
java复制@Transactional
public boolean lockSeats(SeatLockDTO dto) {
// 1. 检查座位状态
// 2. 生成锁定记录(带超时时间)
// 3. 更新座位状态
// 所有操作必须在一个事务内
}
2.2 前端模块划分
Vue3的项目结构建议:
code复制src/
├── api/ (接口封装)
├── components/
│ ├── cinema/ (影院相关)
│ ├── movie/ (电影相关)
│ ├── payment/ (支付流程)
├── composables/ (组合式函数)
├── stores/ (Pinia状态管理)
关键点:支付流程组件需要特别注意:
- 使用Vue3的keep-alive缓存支付表单状态
- 路由守卫检查支付超时
- WebSocket监听支付结果
3. 数据库设计与优化
3.1 核心表结构
sql复制CREATE TABLE `movie` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`cover_url` varchar(255) DEFAULT NULL,
`duration` int DEFAULT NULL COMMENT '分钟',
`release_date` date DEFAULT NULL,
`score` decimal(3,1) DEFAULT NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `idx_title` (`title`) -- 支持全文搜索
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
特别注意的几处设计:
- 影厅座位采用JSON存储而非关联表,提升查询性能:
java复制@TableField(typeHandler = JacksonTypeHandler.class)
private List<Seat> seatMap;
- 订单表分库分键设计:
sql复制CREATE TABLE `order_2023` (
`order_no` varchar(32) NOT NULL COMMENT '按年月分表',
`user_id` bigint NOT NULL,
`sharding_key` tinyint GENERATED ALWAYS AS (user_id % 4) STORED -- 分库键
);
3.2 查询优化实践
电影列表分页查询的优化方案:
java复制public Page<MovieVO> listMovies(PageQuery query) {
return movieMapper.selectPage(new Page<>(query.getPage(), query.getSize()),
Wrappers.<Movie>lambdaQuery()
.orderByDesc(Movie::getReleaseDate)
.last("FORCE INDEX(idx_release_score)") // 强制使用联合索引
);
}
4. 典型业务场景实现
4.1 电影订票流程
完整的订票状态机设计:
mermaid复制stateDiagram
[*] --> 选择场次
选择场次 --> 选择座位: 有可用座位
选择座位 --> 确认订单
确认订单 --> 支付中: 提交订单
支付中 --> 支付成功: 收到回调
支付中 --> 支付超时: 15分钟未支付
支付超时 --> 释放座位
关键代码:座位锁定服务
java复制@Scheduled(fixedRate = 60000)
public void releaseExpiredLocks() {
// 释放超时未支付的座位
seatService.update(
Wrappers.<Seat>lambdaUpdate()
.eq(Seat::getStatus, SeatStatus.LOCKED)
.le(Seat::getLockTime, LocalDateTime.now().minusMinutes(15))
.set(Seat::getStatus, SeatStatus.AVAILABLE)
);
}
4.2 评论系统设计
防刷评策略实现:
- 基于Redis的令牌桶算法限流
- 敏感词过滤AC自动机
- 情感分析过滤恶意评价
java复制public Result addComment(CommentDTO dto) {
// 1. 限流检查
if (!rateLimiter.tryAcquire(userId)) {
throw new BusinessException("评论太频繁");
}
// 2. 敏感词过滤
String filtered = sensitiveFilter.filter(dto.getContent());
// 3. 情感分析
if (sentimentAnalyzer.isNegative(filtered)) {
return Result.fail("请勿发表恶意评论");
}
// 保存评论
return Result.success();
}
5. 部署与性能优化
5.1 容器化部署方案
Docker Compose配置示例:
yaml复制services:
app:
image: openjdk:17-jdk
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
调优参数:
properties复制# Tomcat优化
server.tomcat.max-threads=200
server.tomcat.accept-count=100
# MyBatis-Plus
mybatis-plus.configuration.default-executor-type=reuse
5.2 缓存策略设计
多级缓存架构:
- 本地缓存(Caffeine):高频访问的电影详情
- 分布式缓存(Redis):影院排片数据
- CDN缓存:静态资源
java复制@Cacheable(value = "movie", key = "#id",
cacheManager = "caffeineCacheManager")
public MovieVO getMovieDetail(Long id) {
return movieMapper.selectDetailById(id);
}
6. 常见问题排查
6.1 MyBatis-Plus分页问题
现象:分页查询结果不正确
解决方案:
- 添加分页插件配置:
java复制@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
- 确保Page参数正确传递:
java复制// 错误写法
Page<Movie> page = new Page<>(1, 10);
movieMapper.selectPage(page, null); // 这里page会被修改
// 正确写法
Page<Movie> page = new Page<>(1, 10);
Page<Movie> result = movieMapper.selectPage(page, null);
6.2 Vue3路由传参问题
现象:路由参数在页面刷新后丢失
解决方案:
- 使用query方式传参:
javascript复制router.push({
path: '/movie/detail',
query: { id: movieId }
})
- 或使用state方式:
javascript复制router.push({
path: '/movie/detail',
state: { movieData }
})
7. 安全防护措施
7.1 接口安全设计
- JWT认证流程优化:
java复制public String generateToken(User user) {
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(user.getId().toString())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.compact();
}
- 防XSS处理:
java复制@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
return registration;
}
7.2 支付安全
支付回调验证逻辑:
java复制public boolean verifyPayment(PaymentNotify notify) {
// 1. 验证签名
String sign = generateSign(notify);
if (!sign.equals(notify.getSign())) {
return false;
}
// 2. 验证金额
Order order = orderService.getByNo(notify.getOrderNo());
return order.getAmount().compareTo(notify.getAmount()) == 0;
}
8. 项目文档要点
完善的文档应包含:
- 接口文档(Swagger配置示例):
java复制@Configuration
@EnableOpenApi
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
.paths(PathSelectors.any())
.build();
}
}
- 部署手册:
- 环境要求:JDK17+、MySQL8.0+、Node16+
- 初始化脚本:包含基础数据SQL
- 启动顺序:MySQL → Redis → 后端 → 前端
- 压力测试报告:
- JMeter测试场景设计
- 关键指标:并发用户数、TPS、响应时间
- 瓶颈分析与优化建议
我在实际开发中发现,良好的文档能减少至少30%的维护成本。特别是对电影排片这类复杂业务,详细的流程图和状态说明非常必要。
