1. 企业级Web电影院购票系统架构解析
这个基于SpringBoot+Vue+MyBatis+MySQL的全栈项目,是典型的现代企业级应用架构。我在实际开发中发现,电影院票务系统相比普通电商系统有几个特殊需求:高并发选座锁座、放映场次时效性管理、座位图的动态渲染。这套技术栈的组合恰好能完美应对这些挑战。
SpringBoot提供了快速构建后端服务的能力,Vue的响应式特性特别适合实时更新座位状态,MyBatis的灵活SQL编写便于处理复杂的票务关联查询,而MySQL作为成熟的关系型数据库,既能保证事务一致性,又能通过优化满足高并发需求。下面我会结合具体场景,拆解各技术点的实战应用。
2. 核心模块设计与技术实现
2.1 系统分层架构
典型的三层架构设计:
- 表现层:Vue 3 + Element Plus
- 业务逻辑层:SpringBoot 2.7 + Spring Security
- 数据持久层:MyBatis-Plus + MySQL 8.0
特别的是增加了影院专用层:
code复制影院现场终端接口层
↓
API网关层
↓
业务微服务层(用户/影片/排片/订单)
↓
数据访问层
↓
MySQL集群 + Redis缓存
2.2 高并发选座解决方案
选座是系统最核心的难点,我们采用分布式锁+乐观锁方案:
java复制// 伪代码示例:选座核心逻辑
@Transactional
public SeatSelectionResult selectSeats(SelectionRequest request) {
// 1. 获取分布式锁(Redisson实现)
RLock lock = redissonClient.getLock("seat_lock:"+request.getScheduleId());
try {
if(lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 2. 检查座位状态
List<Seat> seats = seatMapper.selectBatchIds(request.getSeatIds());
if(seats.stream().anyMatch(s -> s.getStatus() != SeatStatus.AVAILABLE)) {
throw new BusinessException("座位已被选中");
}
// 3. 乐观锁更新
int updated = seatMapper.updateSeatStatus(
request.getSeatIds(),
SeatStatus.LOCKED,
request.getUserId());
if(updated != request.getSeatIds().size()) {
throw new ConcurrentUpdateException("选座冲突");
}
// 4. 生成订单预留记录
orderService.createTempOrder(request);
return new SeatSelectionResult(true);
}
} finally {
lock.unlock();
}
return new SeatSelectionResult(false);
}
关键参数说明:
- 锁等待时间3秒:避免长时间阻塞
- 锁持有时间10秒:足够完成选座操作
- 乐观锁版本号:防止更新冲突
2.3 放映计划管理设计
影院排片模块采用树形结构设计:
code复制Cinema (影院)
└── Auditorium (影厅)
└── Schedule (放映计划)
├── Movie (影片)
├── PriceRule (定价规则)
└── SeatPlan (座位方案)
对应的MySQL表结构优化:
sql复制CREATE TABLE `schedule` (
`id` bigint NOT NULL AUTO_INCREMENT,
`movie_id` bigint NOT NULL COMMENT '影片ID',
`auditorium_id` int NOT NULL COMMENT '影厅ID',
`start_time` datetime NOT NULL COMMENT '开场时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`price_rule_id` int DEFAULT NULL COMMENT '动态定价规则',
`seat_plan_json` json DEFAULT NULL COMMENT '座位图配置',
`version` int DEFAULT '0' COMMENT '乐观锁版本号',
PRIMARY KEY (`id`),
KEY `idx_time_range` (`start_time`,`end_time`),
KEY `idx_movie` (`movie_id`),
KEY `idx_auditorium` (`auditorium_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
2.4 前端座位图渲染优化
Vue实现动态座位图的性能关键点:
- 使用Canvas渲染基础座位图(减少DOM节点)
- 状态变更采用WebSocket推送
- 防抖处理频繁的选座操作
核心Vue组件代码结构:
vue复制<template>
<div class="seat-map-container">
<canvas ref="canvas" @click="handleCanvasClick"></canvas>
<div class="seat-legend">
<span v-for="(item,index) in legendItems" :key="index">
<span :class="['seat-sample', item.class]"></span>
{{ item.label }}
</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
seats: [], // 座位状态数组
socket: null,
debounceTimer: null
}
},
mounted() {
this.initWebSocket();
this.renderSeatMap();
},
methods: {
initWebSocket() {
this.socket = new WebSocket(`wss://${location.host}/ws/seats`);
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.updateSeats(data.updatedSeats);
};
},
handleCanvasClick(event) {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
const seat = this.getSeatAtPosition(event.offsetX, event.offsetY);
if (seat) this.selectSeat(seat);
}, 150);
},
// 其他方法...
}
}
</script>
3. 企业级特性实现
3.1 多影院分级管理
采用RBAC权限模型扩展:
java复制@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private RoleType type; // 角色类型
@ManyToOne
@JoinColumn(name = "cinema_id")
private Cinema cinema; // 关联到具体影院
// 其他字段...
}
public enum RoleType {
SYSTEM_ADMIN, // 系统管理员
CINEMA_ADMIN, // 影院管理员
CINEMA_STAFF, // 影院员工
CUSTOMER // 普通用户
}
权限控制示例:
java复制@PreAuthorize("hasRole('CINEMA_ADMIN') and @securityService.isCinemaAdmin(#cinemaId)")
@PostMapping("/schedules")
public ResponseEntity<?> createSchedule(@RequestBody ScheduleDTO dto,
@RequestParam Long cinemaId) {
// 创建排片逻辑
}
3.2 票务统计与分析
使用MyBatis的复杂查询配合MySQL窗口函数:
xml复制<select id="selectSalesStatistics" resultType="map">
SELECT
m.title AS movieName,
c.name AS cinemaName,
COUNT(*) AS ticketCount,
SUM(o.actual_price) AS totalAmount,
RANK() OVER(PARTITION BY c.id ORDER BY SUM(o.actual_price) DESC) AS rankInCinema
FROM
orders o
JOIN schedule s ON o.schedule_id = s.id
JOIN movie m ON s.movie_id = m.id
JOIN cinema c ON s.cinema_id = c.id
WHERE
o.status = 'COMPLETED'
AND o.create_time BETWEEN #{startDate} AND #{endDate}
GROUP BY
m.id, c.id
ORDER BY
totalAmount DESC
LIMIT 100
</select>
3.3 支付与票务验证
支付流程状态机设计:
code复制[待支付] --超时未支付--> [已取消]
[待支付] --支付成功--> [已支付] --出票成功--> [已完成]
[待支付] --支付失败--> [支付失败]
[已支付] --出票失败--> [退款中] --退款成功--> [已退款]
对应的订单状态变更实现:
java复制public enum OrderStatus {
PENDING_PAYMENT(1, "待支付") {
@Override
public boolean canChangeTo(OrderStatus newStatus) {
return newStatus == CANCELLED ||
newStatus == PAID;
}
},
PAID(2, "已支付") {
@Override
public boolean canChangeTo(OrderStatus newStatus) {
return newStatus == COMPLETED ||
newStatus == REFUNDING;
}
},
// 其他状态...
public abstract boolean canChangeTo(OrderStatus newStatus);
}
4. 性能优化实战
4.1 MySQL查询优化
- 为高频查询添加适当索引:
sql复制ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);
ALTER TABLE schedule ADD INDEX idx_movie_time (movie_id, start_time);
- 大表分片策略:
- 订单表按月份水平分表:orders_202301, orders_202302...
- 使用ShardingSphere实现透明访问
- 慢查询监控配置:
ini复制# my.cnf配置
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
4.2 缓存策略设计
采用多级缓存架构:
code复制请求 → Nginx本地缓存 → Redis集群 → 数据库
Spring Cache配置示例:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.disableCachingNullValues()
.serializeValuesWith(SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withInitialCacheConfigurations(
Map.of(
"movieDetail",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
)
)
.transactionAware()
.build();
}
}
4.3 前端性能优化
- 路由懒加载:
javascript复制const routes = [
{
path: '/movie/:id',
component: () => import('./views/MovieDetail.vue'),
}
];
- 关键CSS内联:
html复制<head>
<style>
/* 关键渲染路径CSS */
.seat-map-container { ... }
</style>
</head>
- 图片优化:
- 使用WebP格式
- 实现懒加载
vue复制<img v-lazy="movie.posterUrl" alt="movie poster">
5. 安全防护方案
5.1 常见攻击防护
- CSRF防护配置:
java复制@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
// 其他配置...
}
}
- XSS防护:
- 前端使用DOMPurify过滤输入
javascript复制import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
- 后端统一响应处理:
java复制@ControllerAdvice
public class XssProtectionAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
return XssUtils.clean(body);
}
}
5.2 敏感数据保护
- 密码加密存储:
java复制@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- 支付信息脱敏:
java复制public String maskCardNumber(String cardNumber) {
if (cardNumber == null || cardNumber.length() < 8) {
return cardNumber;
}
return cardNumber.substring(0, 4) +
" **** **** " +
cardNumber.substring(cardNumber.length() - 4);
}
- 日志脱敏处理:
java复制@Aspect
@Component
public class SensitiveDataAspect {
@Around("execution(* com..controller.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
// 参数脱敏处理
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof String) {
String arg = (String) args[i];
if (isSensitive(arg)) {
args[i] = maskSensitiveData(arg);
}
}
}
Object result = pjp.proceed(args);
// 响应脱敏处理
return maskSensitiveDataInResponse(result);
}
}
6. 部署与监控
6.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3.8'
services:
app:
image: cinema-ticket:${TAG:-latest}
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:mysql://mysql:3306/cinema
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: cinema
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
6.2 监控告警配置
- Spring Boot Actuator集成:
yaml复制management:
endpoint:
health:
show-details: always
metrics:
enabled: true
prometheus:
enabled: true
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
- 自定义业务指标:
java复制@RestController
public class OrderController {
private final Counter orderCounter;
public OrderController(MeterRegistry registry) {
this.orderCounter = Counter.builder("cinema.orders.created")
.description("Total created orders")
.tags("region", System.getProperty("region", "unknown"))
.register(registry);
}
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
// 业务逻辑...
orderCounter.increment();
return order;
}
}
- Grafana监控看板配置:
- 关键指标:
- 订单创建速率
- 平均响应时间
- 错误率
- 活跃会话数
- 数据库连接池使用率
7. 项目扩展方向
7.1 多终端适配
- 小程序端实现方案:
- 使用Uniapp跨平台开发
- 共享后端API接口
- 特殊处理:
javascript复制// 微信支付适配 function wxPay(order) { uni.requestPayment({ provider: 'wxpay', orderInfo: order.payParams, success: () => { /* 支付成功 */ }, fail: (err) => { /* 处理失败 */ } }); }
- 自助取票机通信协议:
java复制// 取票指令协议
public class TicketCommand {
private String machineId;
private String commandId;
private CommandType type;
private String verificationCode;
private LocalDateTime timestamp;
public enum CommandType {
PRINT, CANCEL, QUERY
}
}
7.2 智能推荐系统
基于用户行为的协同过滤实现:
java复制public List<Movie> recommendMovies(Long userId) {
// 1. 获取用户历史行为
List<UserBehavior> behaviors = behaviorService.getByUser(userId);
// 2. 找到相似用户
Map<Long, Double> similarUsers = findSimilarUsers(behaviors);
// 3. 生成推荐列表
return similarUsers.entrySet().stream()
.flatMap(entry -> behaviorService.getByUser(entry.getKey()).stream())
.filter(b -> !behaviors.contains(b))
.collect(Collectors.groupingBy(
b -> movieService.getById(b.getMovieId()),
Collectors.summingDouble(b -> similarUsers.get(b.getUserId()))
))
.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(10)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
7.3 影院物联网集成
- 影厅环境监控集成:
java复制@RestController
@RequestMapping("/api/iot")
public class IotController {
@PostMapping("/environment")
public void receiveEnvData(@RequestBody EnvData data) {
// 处理温湿度数据
if (data.getTemperature() > 28) {
alertService.sendAlert(
"高温预警",
String.format("影厅%s温度过高: %.1f℃",
data.getAuditoriumId(),
data.getTemperature())
);
}
}
}
- 设备状态数据结构:
java复制public class DeviceStatus {
private String deviceId;
private DeviceType type;
private String auditorium;
private Status status;
private Map<String, Object> metrics;
public enum DeviceType {
PROJECTOR, SOUND_SYSTEM, AIR_CONDITIONER
}
public enum Status {
ONLINE, OFFLINE, MALFUNCTION
}
}
8. 开发经验与避坑指南
8.1 MyBatis使用技巧
- 动态SQL最佳实践:
xml复制<select id="findSchedules" resultType="Schedule">
SELECT * FROM schedule
<where>
<if test="movieId != null">
AND movie_id = #{movieId}
</if>
<if test="cinemaId != null">
AND cinema_id = #{cinemaId}
</if>
<if test="startTime != null and endTime != null">
AND start_time BETWEEN #{startTime} AND #{endTime}
</if>
<choose>
<when test="status != null">
AND status = #{status}
</when>
<otherwise>
AND status = 'PUBLISHED'
</otherwise>
</choose>
</where>
ORDER BY start_time
</select>
- 批量插入优化方案:
java复制@Insert("<script>" +
"INSERT INTO ticket (order_id, schedule_id, seat_num, price) VALUES " +
"<foreach collection='tickets' item='ticket' separator=','>" +
"(#{ticket.orderId}, #{ticket.scheduleId}, #{ticket.seatNum}, #{ticket.price})" +
"</foreach>" +
"</script>")
@Options(useGeneratedKeys = true, keyProperty = "id")
void batchInsert(@Param("tickets") List<Ticket> tickets);
8.2 Vue性能优化经验
- 大型表单处理技巧:
vue复制<template>
<form @submit.prevent="handleSubmit">
<fieldset v-for="(field, index) in visibleFields" :key="field.id">
<label :for="`field-${index}`">{{ field.label }}</label>
<input :id="`field-${index}`"
v-model.lazy="formData[field.name]"
@change="validateField(field.name)">
</fieldset>
</form>
</template>
<script>
export default {
data() {
return {
allFields: [], // 全部字段配置
formData: {},
visibleFields: [] // 当前可见字段
}
},
methods: {
updateVisibleFields() {
// 根据条件动态计算可见字段
this.visibleFields = this.allFields.filter(f =>
this.shouldShowField(f)
);
},
validateField(fieldName) {
// 单独验证字段
}
}
}
</script>
- 状态管理优化:
- 按模块划分Vuex store
- 使用getter缓存计算结果
javascript复制// store/modules/tickets.js
const getters = {
availableSeats: (state) => {
return state.seats.filter(seat =>
seat.status === 'AVAILABLE'
);
},
// 带缓存的getter
recommendedMovies: (state) => {
return memoize((userId) => {
return state.allMovies.filter(movie =>
state.userPreferences[userId]?.includes(movie.genre)
);
});
}
};
8.3 SpringBoot调试技巧
- 条件化配置示例:
java复制@Configuration
@ConditionalOnProperty(name = "features.cache.enabled", havingValue = "true")
public class CacheConfig {
// 缓存相关bean配置
}
@RestController
@RequestMapping("/api/test")
@Profile("dev")
public class DevTestController {
// 仅开发环境可访问的测试接口
}
- 自定义健康检查:
java复制@Component
public class CinemaHealthIndicator implements HealthIndicator {
private final SeatService seatService;
@Override
public Health health() {
try {
int lockedSeats = seatService.countLockedSeats();
return Health.up()
.withDetail("lockedSeats", lockedSeats)
.build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
9. 测试策略设计
9.1 测试金字塔实施
- 单元测试覆盖要点:
java复制@SpringBootTest
public class TicketServiceTest {
@Autowired
private TicketService ticketService;
@MockBean
private SeatMapper seatMapper;
@Test
public void testSelectSeatConcurrency() {
// 模拟座位数据
Seat seat = new Seat();
seat.setStatus(SeatStatus.AVAILABLE);
when(seatMapper.selectById(any())).thenReturn(seat);
// 并发测试
int threadCount = 10;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicInteger successCount = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
executor.execute(() -> {
try {
if (ticketService.selectSeat(1L, 1L)) {
successCount.incrementAndGet();
}
} finally {
latch.countDown();
}
});
}
latch.await();
assertEquals(1, successCount.get());
}
}
- API测试关键检查点:
- 身份验证
- 参数校验
- 业务规则验证
- 错误处理
- 性能基准
9.2 压力测试方案
使用JMeter测试购票流程:
code复制1. 用户登录(获取token)
2. 查询放映场次
3. 选择座位
4. 创建订单
5. 模拟支付
关键指标监控:
- 平均响应时间 < 500ms
- 错误率 < 0.1%
- 90%线 < 800ms
- 吞吐量 > 1000 TPS
10. 项目演进路线
10.1 技术债清理计划
- 待优化项清单:
- 订单历史查询慢:需要分表
- 座位选择竞争:改进分布式锁策略
- 支付回调处理:增加幂等性保证
- 日志系统:迁移到ELK
- 重构优先级评估矩阵:
| 模块 | 业务价值 | 维护成本 | 风险 | 优先级 |
|---|---|---|---|---|
| 订单查询 | 高 | 高 | 中 | P0 |
| 支付回调 | 高 | 中 | 高 | P0 |
| 日志系统 | 中 | 高 | 低 | P1 |
| 推荐算法 | 低 | 低 | 低 | P2 |
10.2 技术演进方向
- 短期优化:
- 引入Spring Cloud Gateway统一API入口
- 实现配置中心统一管理
- 完善监控告警体系
- 中期规划:
- 核心模块服务化拆分
- 多语言客户端SDK开发
- 建立AB测试基础设施
- 长期愿景:
- 构建影院数字化平台
- 开发开放API生态系统
- 实现AI驱动的运营决策
11. 项目文档体系
11.1 代码注释规范
- 类级别注释示例:
java复制/**
* 订单核心服务实现
*
* <p>主要功能:
* <ul>
* <li>订单创建与状态管理</li>
* <li>支付流程控制</li>
* <li>票务验证处理</li>
* </ul>
*
* @author cinema-team
* @version 1.2
* @see Order
* @see PaymentService
*/
@Service
public class OrderServiceImpl implements OrderService {
// ...
}
- 方法注释要求:
java复制/**
* 创建电影票订单
*
* @param request 包含场次、座位、用户等订单信息
* @return 生成的订单详情
* @throws BusinessException 当座位不可用或参数非法时抛出
* @throws ConcurrentUpdateException 当并发选座冲突时抛出
* @see SeatService#lockSeats
* @see PaymentService#createPayment
*/
@Override
public Order createOrder(OrderRequest request) {
// ...
}
11.2 API文档生成
Swagger配置示例:
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.cinema.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.securitySchemes(Arrays.asList(apiKey()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("影院票务系统API")
.description("包含用户、影片、排片、订单等核心接口")
.version("1.0")
.build();
}
private ApiKey apiKey() {
return new ApiKey("JWT", "Authorization", "header");
}
}
12. 团队协作规范
12.1 Git工作流
采用Git Flow变种:
code复制main - 生产环境对应分支
release/* - 预发布分支
develop - 集成测试分支
feature/* - 功能开发分支
hotfix/* - 紧急修复分支
提交消息规范:
code复制[类型] 简要描述 (#关联issue)
详细说明(可选)
- 变更点1
- 变更点2
类型包括:
- feat:新功能
- fix:错误修复
- docs:文档变更
- style:代码样式
- refactor:重构
- test:测试相关
- chore:构建/工具变更
12.2 代码审查清单
- 功能性检查:
- 是否实现了需求所有功能点
- 边界条件是否处理完善
- 错误场景是否有妥善处理
- 代码质量检查:
- 是否符合编码规范
- 是否有重复代码
- 单元测试覆盖率是否达标
- 安全审查:
- 是否有SQL注入风险
- 敏感数据是否加密
- 权限控制是否完善
13. 项目交付物说明
13.1 源码结构说明
标准Maven多模块项目:
code复制cinema-parent
├── cinema-common // 通用工具类
├── cinema-domain // 领域模型
├── cinema-dao // 数据访问层
├── cinema-service // 业务逻辑层
├── cinema-web // Web接口层
├── cinema-admin // 管理后台
└── cinema-mobile // 移动端适配
13.2 数据库迁移脚本
使用Flyway管理数据库变更:
code复制src/main/resources/db/migration/
├── V1__Initial_schema.sql
├── V2__Add_indexes.sql
├── V3__Alter_table_orders.sql
└── V4__Add_trigger_for_audit.sql
13.3 部署包内容
标准交付物包含:
code复制cinema-deploy/
├── bin/ // 启动脚本
├── config/ // 配置文件
├── lib/ // 应用jar包
├── sql/ // 数据库脚本
├── static/ // 前端资源
└── docs/ // 部署文档
14. 常见问题解决方案
14.1 选座冲突处理
典型场景:
- 用户A和B同时选择同一座位
- 网络延迟导致状态不同步
解决方案:
java复制// 伪代码:增强版选座逻辑
public synchronized SeatSelectionResult selectSeat(SelectionRequest request) {
// 1. 查询当前座位状态(带锁)
Seat seat = seatMapper.selectForUpdate(request.getSeatId());
// 2. 检查状态
if (seat.getStatus() != SeatStatus.AVAILABLE) {
return SeatSelectionResult.failed("座位不可用");
}
// 3. 更新状态
seat.setStatus(SeatStatus.LOCKED);
seat.setLockUserId(request.getUserId());
seat.setLockTime(LocalDateTime.now());
seatMapper.update(seat);
// 4. 设置过期时间
redisTemplate.opsForValue().set(
"seat:lock:" + request.getSeatId(),
request.getUserId(),
10, TimeUnit.MINUTES);
return SeatSelectionResult.success();
}
14.2 支付超时处理
定时任务配置:
java复制@Scheduled(fixedRate = 60000) // 每分钟执行
public void checkPaymentTimeout() {
// 查询待支付超时订单
List<Order> timeoutOrders = orderMapper.selectTimeoutOrders(
LocalDateTime.now().minusMinutes(15));
timeoutOrders.forEach(order -> {
// 释放座位
seatService.unlockSeats(order.getSeatIds());
// 更新订单状态
order.setStatus(OrderStatus.CANCELLED);
order.setCancelReason("支付超时");
orderMapper.update(order);
// 发送通知
notificationService.sendPaymentTimeout(order.getUserId());
});
}
14.3 高并发场景优化
- 缓存策略优化:
- 使用Redis集群
- 热点数据预加载
- 本地缓存配合
- 数据库优化:
- 读写分离
- 连接池调优
- 查询优化
- 限流措施:
java复制@RestController
@RequestMapping("/api")
public class ApiController {
@RateLimiter(value = 1000, key = "#userId")
@PostMapping("/order")
public Order createOrder(@RequestParam Long userId) {
// 订单创建逻辑
}
}
15. 项目二次开发建议
15.1 定制化开发方向
- 会员积分系统:
- 积分赚取规则
- 积分兑换体系
- 会员等级特权
- 营销活动模块:
- 优惠券系统
- 限时折扣
- 套餐组合
- 数据分析平台:
- 上座率分析
- 用户行为分析
- 票房预测
15.2 国际化支持方案
- 多语言实现:
java复制@RestController
@RequestMapping("/api/movie")
public class MovieController {
@GetMapping("/{id}")
public MovieDetail getMovie(
@PathVariable Long id,
@RequestHeader("Accept-Language") String language) {
return movieService.getMovieDetail(id, language);
}
}
- 时间处理规范:
java复制public class DateTimeUtils {
public static String formatForLocale(LocalDateTime time, Locale locale) {
DateTimeFormatter formatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(locale);
return time.format(formatter);
}
}
16. 技术选型对比
16.1 持久层框架比较
| 特性 | MyBatis | JPA/Hibernate |
|---|---|---|
| SQL控制 | 完全控制 | 自动生成 |
| 学习曲线 | 较低 | 较高 |
| 性能调优 | 更灵活 | 较复杂 |
| 动态查询 | XML/注解 | Criteria API |
| 缓存机制 | 二级缓存 | 多级缓存 |
| 适合场景 | 复杂SQL项目 | 快速开发项目 |
16.2 前端框架选择
Vue vs React对比:
| 维度 | Vue | React |
|---|---|---|
| 学习曲线 | 平缓 | 较陡峭 |
| 模板语法 | HTML-based | JSX |
| 状态管理 | Vuex | Redux |
| 性能 | 优秀 | 优秀 |
| 生态规模 | 丰富 | 非常丰富 |
| 适合团队 | 中小型 | 中大型 |
17. 学习资源推荐
17.1 技术栈学习路径
- SpringBoot进阶:
- 官方文档:spring.io/projects/spring-boot
- 《Spring Boot实战》
- 源码分析:自动配置原理
- Vue深度掌握:
- 官方教程:vuejs.org/guide
- Vue Mastery课程
- 源码解读:响应式原理
- MyBatis优化:
- MyBatis-Spring整合文档
- 《MyBatis从入门到精通》
- 插件开发:分页/审计
17.2 项目实践建议
- 初期:
- 从简单CRUD开始
- 理解现有架构
- 参与bug修复
- 中期:
- 负责独立模块
- 性能优化实践
- 参与设计讨论
- 后期:
- 主导功能开发
- 架构改进提案
