From 1661b7ec8cf4b49d87a5e003596ccb5444f566d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=ED=9B=88=EA=B8=B0?= Date: Sun, 8 Mar 2026 00:51:24 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:Feat:=20=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9E=90=EB=8F=99=20=EC=A0=84=ED=99=98=20?= =?UTF-8?q?Scheduler=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/sportizebe/SportizeBeApplication.java | 2 ++ .../match/repository/MatchRoomRepository.java | 21 ++++++++++++ .../match/scheduler/MatchStatusScheduler.java | 32 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/main/java/com/be/sportizebe/domain/match/scheduler/MatchStatusScheduler.java diff --git a/src/main/java/com/be/sportizebe/SportizeBeApplication.java b/src/main/java/com/be/sportizebe/SportizeBeApplication.java index 56556ae..a4f322d 100644 --- a/src/main/java/com/be/sportizebe/SportizeBeApplication.java +++ b/src/main/java/com/be/sportizebe/SportizeBeApplication.java @@ -3,8 +3,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; @EnableJpaAuditing +@EnableScheduling @SpringBootApplication public class SportizeBeApplication { diff --git a/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java b/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java index 5a52644..921d0a6 100644 --- a/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java +++ b/src/main/java/com/be/sportizebe/domain/match/repository/MatchRoomRepository.java @@ -3,6 +3,7 @@ import com.be.sportizebe.domain.match.entity.MatchRoom; import com.be.sportizebe.domain.match.repository.projection.MatchNearProjection; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -44,4 +45,24 @@ List findNear( @Param("radiusM") int radiusM, @Param("sportsName") String sportsName ); + + // scheduledAt이 지난 OPEN/FULL → CLOSED + @Modifying + @Query(value = """ + UPDATE match_rooms + SET status = 'CLOSED' + WHERE status IN ('OPEN', 'FULL') + AND scheduled_at <= NOW() + """, nativeQuery = true) + int closeStartedMatches(); + + // scheduledAt + durationMinutes가 지난 CLOSED → COMPLETED + @Modifying + @Query(value = """ + UPDATE match_rooms + SET status = 'COMPLETED' + WHERE status = 'CLOSED' + AND scheduled_at + (duration_minutes * interval '1 minute') <= NOW() + """, nativeQuery = true) + int completeFinishedMatches(); } diff --git a/src/main/java/com/be/sportizebe/domain/match/scheduler/MatchStatusScheduler.java b/src/main/java/com/be/sportizebe/domain/match/scheduler/MatchStatusScheduler.java new file mode 100644 index 0000000..aec78f6 --- /dev/null +++ b/src/main/java/com/be/sportizebe/domain/match/scheduler/MatchStatusScheduler.java @@ -0,0 +1,32 @@ +package com.be.sportizebe.domain.match.scheduler; + +import com.be.sportizebe.domain.match.repository.MatchRoomRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Component +@RequiredArgsConstructor +public class MatchStatusScheduler { + + private final MatchRoomRepository matchRoomRepository; + + /** + * 1분마다 실행 + * 1) scheduledAt 도달 → OPEN/FULL → CLOSED + * 2) scheduledAt + durationMinutes 도달 → CLOSED → COMPLETED + */ + @Scheduled(fixedRate = 60_000) + @Transactional + public void updateMatchStatuses() { + int closed = matchRoomRepository.closeStartedMatches(); + int completed = matchRoomRepository.completeFinishedMatches(); + + if (closed > 0 || completed > 0) { + log.info("[MatchScheduler] CLOSED: {}건, COMPLETED: {}건", closed, completed); + } + } +}