diff --git a/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatAsyncSaveService.java b/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatAsyncSaveService.java index 20631ae..8249ec6 100644 --- a/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatAsyncSaveService.java +++ b/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatAsyncSaveService.java @@ -1,7 +1,10 @@ package com.lottery.api.service; import com.lottery.entity.Seat; +import com.lottery.entity.SeatStatus; +import com.lottery.interceptor.SeatRepository; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -16,6 +19,23 @@ public class SeatAsyncSaveService { @PersistenceContext private EntityManager entityManager; + @Autowired + private SeatRepository seatRepository; + + + @Async("seatExecutor") // 复用你现有的线程池配置 + @Transactional(propagation = Propagation.REQUIRES_NEW) // 确保独立事务提交 + public void updateStatusByUniqueId(String lockedBy, SeatStatus status) { + int affected = seatRepository.updateStatusByUniqueId(lockedBy, status); + + // 🔥 关键:记录更新结果,便于排查 + if (affected == 0) { + log.warn("座位更新失败: uniqueId={}, 可能不存在或已被释放", lockedBy); + // 可选:集成告警/监控 + } else { + log.debug("座位释放成功: uniqueId={}, affected={}", lockedBy, affected); + } + } @Async("taskExecutor") // 指定线程池 @Transactional(propagation = Propagation.REQUIRES_NEW) diff --git a/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatSelectionService.java b/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatSelectionService.java index 189956a..5759067 100644 --- a/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatSelectionService.java +++ b/lottery-system/lottery-service/src/main/java/com/lottery/api/service/SeatSelectionService.java @@ -345,14 +345,8 @@ public class SeatSelectionService { // 4. 删除用户 - 座位映射 redisTemplate.delete(userSeatKey); - // 5. 异步更新数据库 - Seat releasedSeat = Seat.builder() - .tableNo(tableNo) - .seatNo(seatNo) - .uniqueId(seatId) - .status(SeatStatus.AVAILABLE) - .build(); - seatAsyncSaveService.save(releasedSeat); // 确保你的 asyncSaveService 支持更新 + // 5. 异步更新数据库 🔥 直接调用 repository 的 UPDATE 方法 + seatAsyncSaveService.updateStatusByUniqueId(userId, SeatStatus.AVAILABLE); // 6. 广播状态变更 SeatUpdateEvent event = SeatUpdateEvent.builder() diff --git a/lottery-system/lottery-service/src/main/java/com/lottery/config/AsyncConfig.java b/lottery-system/lottery-service/src/main/java/com/lottery/config/AsyncConfig.java index 740a550..1f85636 100644 --- a/lottery-system/lottery-service/src/main/java/com/lottery/config/AsyncConfig.java +++ b/lottery-system/lottery-service/src/main/java/com/lottery/config/AsyncConfig.java @@ -31,4 +31,22 @@ public class AsyncConfig { executor.initialize(); return executor; } + @Bean("seatExecutor") // ← 关键:Bean 名称必须匹配 + public Executor seatExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + // 核心参数(根据业务 QPS 调整) + executor.setCorePoolSize(4); // 核心线程数 + executor.setMaxPoolSize(10); // 最大线程数 + executor.setQueueCapacity(100); // 队列容量 + executor.setKeepAliveSeconds(60); // 空闲线程存活时间 + executor.setThreadNamePrefix("seat-async-"); // 线程名前缀,便于日志追踪 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略 + + executor.setWaitForTasksToCompleteOnShutdown(true); // 优雅关闭 + executor.setAwaitTerminationSeconds(30); + + executor.initialize(); // 🔥 必须调用,否则配置不生效 + return executor; + } } \ No newline at end of file diff --git a/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/AuthInterceptor.java b/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/AuthInterceptor.java index 5bbe353..35005e5 100644 --- a/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/AuthInterceptor.java +++ b/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/AuthInterceptor.java @@ -70,7 +70,10 @@ public class AuthInterceptor implements HandlerInterceptor { if ("/api/health".equals(request.getRequestURI())) { return true; } - if ("/ws/seat".equals(request.getRequestURI())){ + if ("/ws/seat/*".equals(request.getRequestURI())){ + return true; + } + if ("/api/seat/cancel".equals(request.getRequestURI())){ return true; } diff --git a/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/SeatRepository.java b/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/SeatRepository.java index b269f44..132318a 100644 --- a/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/SeatRepository.java +++ b/lottery-system/lottery-service/src/main/java/com/lottery/interceptor/SeatRepository.java @@ -24,16 +24,19 @@ public interface SeatRepository extends JpaRepository { Optional findByUniqueId(String uniqueId); /** - * 更新座位状态 👇 必须加 @Modifying + @Query - */ - @Modifying - @Query("UPDATE Seat s SET s.status = :status WHERE s.uniqueId = :uniqueId") - int updateStatusByUniqueId(@Param("uniqueId") String uniqueId, @Param("status") SeatStatus status); - - /** * 释放用户的所有锁定座位 */ @Modifying @Query("UPDATE Seat s SET s.status = 'AVAILABLE', s.lockedBy = NULL WHERE s.lockedBy = :lockedBy") int releaseSeatsByUser(@Param("lockedBy") String lockedBy); + + + + @Modifying(clearAutomatically = true, flushAutomatically = true) + @Query("UPDATE Seat s SET s.status = :status, " + + "s.lockedBy = null, " + + "s.lockedAt = null " + + "WHERE s.lockedBy = :lockedBy") + int updateStatusByUniqueId(@Param("lockedBy") String lockedBy, + @Param("status") SeatStatus status); } \ No newline at end of file