Skip to content

Commit c939514

Browse files
authored
refactor: EventOpenScheduler 모든 상태별 스케줄러 추가
1 parent 90c3a60 commit c939514

6 files changed

Lines changed: 1111 additions & 36 deletions

File tree

backend/src/main/java/com/back/api/event/scheduler/EventOpenScheduler.java

Lines changed: 108 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,85 +21,166 @@
2121
@Component
2222
@RequiredArgsConstructor
2323
@Slf4j
24-
@Profile({"perf"})
24+
@Profile({"perf", "dev"})
2525
public class EventOpenScheduler {
2626

2727
private final EventRepository eventRepository;
2828

29-
@Scheduled(cron = "${event.scheduler.open.cron}", zone = "Asia/Seoul") // 매 분 실행
29+
/**
30+
* READY → PRE_OPEN (사전등록 시작)
31+
* preOpenAt 시간이 되면 사전등록 오픈
32+
*/
33+
@Scheduled(cron = "${event.scheduler.status.cron:0 * * * * *}", zone = "Asia/Seoul")
34+
@SchedulerLock(
35+
name = "EventPreOpen",
36+
lockAtMostFor = "2m",
37+
lockAtLeastFor = "10s"
38+
)
39+
public void openPreRegistration() {
40+
processStatusTransition(
41+
EventStatus.READY,
42+
EventStatus.PRE_OPEN,
43+
"PreOpen",
44+
(event, now) -> !event.getPreOpenAt().isAfter(now)
45+
);
46+
}
47+
48+
/**
49+
* PRE_OPEN → PRE_CLOSED (사전등록 마감)
50+
* preCloseAt 시간이 되면 사전등록 마감
51+
*/
52+
@Scheduled(cron = "${event.scheduler.status.cron:0 * * * * *}", zone = "Asia/Seoul")
53+
@SchedulerLock(
54+
name = "EventPreClose",
55+
lockAtMostFor = "2m",
56+
lockAtLeastFor = "10s"
57+
)
58+
public void closePreRegistration() {
59+
processStatusTransition(
60+
EventStatus.PRE_OPEN,
61+
EventStatus.PRE_CLOSED,
62+
"PreClose",
63+
(event, now) -> !event.getPreCloseAt().isAfter(now)
64+
);
65+
}
66+
67+
/**
68+
* QUEUE_READY → OPEN (티켓팅 시작)
69+
* ticketOpenAt 시간이 되면 티켓팅 오픈
70+
*
71+
* 참고: PRE_CLOSED → QUEUE_READY 전환은 QueueShuffleScheduler에서 처리
72+
* (ticketOpenAt 1시간 전에 랜덤 큐 생성 후 QUEUE_READY로 상태 변경)
73+
*/
74+
@Scheduled(cron = "${event.scheduler.open.cron}", zone = "Asia/Seoul")
3075
@SchedulerLock(
3176
name = "EventOpen",
3277
lockAtMostFor = "2m",
3378
lockAtLeastFor = "10s"
3479
)
3580
public void openTicketing() {
81+
processStatusTransition(
82+
EventStatus.QUEUE_READY,
83+
EventStatus.OPEN,
84+
"EventOpen",
85+
(event, now) -> !event.getTicketOpenAt().isAfter(now)
86+
);
87+
}
88+
89+
/**
90+
* OPEN → CLOSED (티켓팅 마감)
91+
* ticketCloseAt 시간이 되면 티켓팅 마감
92+
*/
93+
@Scheduled(cron = "${event.scheduler.status.cron:0 * * * * *}", zone = "Asia/Seoul")
94+
@SchedulerLock(
95+
name = "EventClose",
96+
lockAtMostFor = "2m",
97+
lockAtLeastFor = "10s"
98+
)
99+
public void closeTicketing() {
100+
processStatusTransition(
101+
EventStatus.OPEN,
102+
EventStatus.CLOSED,
103+
"EventClose",
104+
(event, now) -> !event.getTicketCloseAt().isAfter(now)
105+
);
106+
}
107+
108+
/**
109+
* 이벤트 상태 전환 공통 로직
110+
*/
111+
private void processStatusTransition(
112+
EventStatus fromStatus,
113+
EventStatus toStatus,
114+
String jobName,
115+
StatusTransitionCondition condition
116+
) {
36117
String runId = UUID.randomUUID().toString();
37118
long startAt = System.currentTimeMillis();
38119

39120
int processed = 0;
40121
int failed = 0;
41122

42123
try {
43-
// 시작로그
44124
MdcContext.putRunId(runId);
45-
log.info("SCHED_START job=EventOpen");
125+
log.info("SCHED_START job={}", jobName);
46126

47127
LocalDateTime now = LocalDateTime.now();
48128

49-
// QUEUE_READY 상태이면서 ticketOpenAt이 지난 이벤트 조회
50-
List<Event> events = eventRepository.findByStatus(EventStatus.QUEUE_READY);
129+
// fromStatus 상태인 이벤트 조회
130+
List<Event> events = eventRepository.findByStatus(fromStatus);
51131

52132
if (events.isEmpty()) {
53-
log.info("SCHED_END job=QueueEntry processed=0 failed=0 durationMs={}",
54-
System.currentTimeMillis() - startAt);
133+
log.info("SCHED_END job={} processed=0 failed=0 durationMs={}",
134+
jobName, System.currentTimeMillis() - startAt);
55135
return;
56136
}
57137

58138
for (Event event : events) {
59139
try {
60140
MdcContext.putEventId(event.getId());
61141

62-
// ticketOpenAt이 현재 시간보다 이전이거나 같으면 오픈
63-
if (event.getTicketOpenAt().isBefore(now)
64-
|| event.getTicketOpenAt().isEqual(now)) {
65-
66-
// QUEUE_READY → OPEN 상태 변경
67-
event.changeStatus(EventStatus.OPEN);
142+
// 조건 확인
143+
if (condition.shouldTransition(event, now)) {
144+
// 상태 변경
145+
event.changeStatus(toStatus);
68146
eventRepository.save(event);
69147
processed++;
70148

71149
log.info(
72-
"SCHED_EVENT_SUCCESS job=EventOpen eventId={} status=OPEN",
73-
event.getId()
150+
"SCHED_EVENT_SUCCESS job={} eventId={} status={} -> {}",
151+
jobName, event.getId(), fromStatus, toStatus
74152
);
75153
}
76154
} catch (Exception ex) {
77155
failed++;
78156
log.error(
79-
"SCHED_EVENT_FAIL job=EventOpen eventId={} error={}",
80-
event.getId(), ex.toString(), ex
157+
"SCHED_EVENT_FAIL job={} eventId={} error={}",
158+
jobName, event.getId(), ex.toString(), ex
81159
);
82160
} finally {
83161
MdcContext.removeEventId();
84162
}
85163
}
86164

87-
// 종료 로그
88165
log.info(
89-
"SCHED_END job=EventOpen processed={} failed={} durationMs={}",
90-
processed,
91-
failed,
92-
System.currentTimeMillis() - startAt
166+
"SCHED_END job={} processed={} failed={} durationMs={}",
167+
jobName, processed, failed, System.currentTimeMillis() - startAt
93168
);
94169
} catch (Exception ex) {
95170
log.error(
96-
"SCHED_FAIL job=EventOpen durationMs={} error={}",
97-
System.currentTimeMillis() - startAt,
98-
ex.toString(),
99-
ex
171+
"SCHED_FAIL job={} durationMs={} error={}",
172+
jobName, System.currentTimeMillis() - startAt, ex.toString(), ex
100173
);
101174
} finally {
102175
MdcContext.removeRunId();
103176
}
104177
}
178+
179+
/**
180+
* 상태 전환 조건을 정의하는 함수형 인터페이스
181+
*/
182+
@FunctionalInterface
183+
private interface StatusTransitionCondition {
184+
boolean shouldTransition(Event event, LocalDateTime now);
185+
}
105186
}

backend/src/main/java/com/back/api/event/service/EventService.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public class EventService {
3333
private final S3MoveService s3MoveService;
3434
private final S3PresignedService s3PresignedService;
3535

36-
3736
@Transactional
3837
public EventResponse createEvent(EventCreateRequest request) {
3938

@@ -156,9 +155,7 @@ private Event findEventById(Long eventId) {
156155

157156
private void validateEventDates(LocalDateTime preOpenAt, LocalDateTime preCloseAt,
158157
LocalDateTime ticketOpenAt, LocalDateTime ticketCloseAt) {
159-
if (preOpenAt.isBefore(LocalDateTime.now())) {
160-
throw new ErrorException(EventErrorCode.INVALID_EVENT_DATE);
161-
}
158+
162159
if (preOpenAt.isAfter(preCloseAt)) {
163160
throw new ErrorException(EventErrorCode.INVALID_EVENT_DATE);
164161
}
@@ -190,7 +187,6 @@ public List<Event> findEventsByStatus(EventStatus status) {
190187
return eventRepository.findByStatus(status);
191188
}
192189

193-
194190
public List<Event> findEventsByTicketOpenAtBetweenAndStatus(
195191
LocalDateTime start,
196192
LocalDateTime end,
@@ -199,5 +195,4 @@ public List<Event> findEventsByTicketOpenAtBetweenAndStatus(
199195
return eventRepository.findByTicketOpenAtBetweenAndStatus(start, end, status);
200196
}
201197

202-
203198
}

backend/src/main/java/com/back/api/queue/scheduler/QueueShuffleScheduler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424

2525
/*
2626
* 대기열 셔플 스케줄러
27-
* ticketOpenAt 1시간 전 대기열 셔플 자동 실행 -> 이벤트 상태 PREOPEN에서 READY로 변경
27+
* ticketOpenAt 1시간 전 대기열 셔플 자동 실행 -> 이벤트 상태 PRE_CLOSED에서 QUEUE_READY로 변경
2828
*/
2929
@Component
3030
@RequiredArgsConstructor
3131
@Slf4j
32-
@Profile({"perf"})
32+
@Profile({"perf", "dev"})
3333
public class QueueShuffleScheduler {
3434
private static final String JOB_NAME = "QueueShuffle";
3535

@@ -68,7 +68,7 @@ public void autoShuffleQueue() {
6868
List<Event> eventList = eventService.findEventsByTicketOpenAtBetweenAndStatus(
6969
rangeStart,
7070
rangeEnd,
71-
EventStatus.PRE_OPEN
71+
EventStatus.PRE_CLOSED
7272
);
7373

7474
if (eventList.isEmpty()) {

0 commit comments

Comments
 (0)