Skip to content

Commit 4f60205

Browse files
authored
Merge pull request #33 from DevKor-github/feature/#31
feat: TimePoll 스케줄러 구현 (집계/독촉/최후통첩/자동확정) (#31)
2 parents 3194d40 + cdbbd52 commit 4f60205

1 file changed

Lines changed: 185 additions & 0 deletions

File tree

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package com.workingdead.meet.scheduler;
2+
3+
import com.workingdead.meet.entity.TimePoll;
4+
import com.workingdead.meet.entity.TimePollStatus;
5+
import com.workingdead.meet.repository.TimePollRepository;
6+
import com.workingdead.meet.service.TimePollService;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.scheduling.annotation.Scheduled;
10+
import org.springframework.stereotype.Component;
11+
12+
import java.time.Duration;
13+
import java.time.Instant;
14+
import java.util.List;
15+
16+
@Slf4j
17+
@Component
18+
@RequiredArgsConstructor
19+
public class TimePollScheduler {
20+
21+
private final TimePollRepository timePollRepository;
22+
private final TimePollService timePollService;
23+
24+
// 독촉 단계별 경과 시간 (분)
25+
private static final long AGGREGATE_MINUTES = 3;
26+
private static final long REMIND_1_MINUTES = 30;
27+
private static final long REMIND_2_MINUTES = 120; // 2시간
28+
private static final long REMIND_3_MINUTES = 360; // 6시간
29+
private static final long REMIND_4_MINUTES = 720; // 12시간
30+
private static final long ULTIMATUM_MINUTES = 1440; // 24시간
31+
private static final long AUTO_FINALIZE_MINUTES = 60; // 최후통첩 후 60분
32+
33+
/**
34+
* 1분마다 실행 - 모든 진행 중인 투표를 체크
35+
*/
36+
@Scheduled(fixedRate = 60000)
37+
public void checkTimePoll() {
38+
List<TimePoll> ongoingPolls = timePollRepository.findByStatusAndCreatedAtBefore(
39+
TimePollStatus.ONGOING, Instant.now());
40+
41+
for (TimePoll poll : ongoingPolls) {
42+
try {
43+
processOngoingPoll(poll);
44+
} catch (Exception e) {
45+
log.error("[TimePollScheduler] Failed to process pollId={}: {}", poll.getId(), e.getMessage());
46+
}
47+
}
48+
49+
// 최후통첩 상태 - 자동 확정 체크
50+
List<TimePoll> ultimatumPolls = timePollRepository.findByStatusAndUltimatumSentAtBefore(
51+
TimePollStatus.ULTIMATUM,
52+
Instant.now().minus(Duration.ofMinutes(AUTO_FINALIZE_MINUTES)));
53+
54+
for (TimePoll poll : ultimatumPolls) {
55+
try {
56+
processAutoFinalize(poll);
57+
} catch (Exception e) {
58+
log.error("[TimePollScheduler] Auto-finalize failed pollId={}: {}", poll.getId(), e.getMessage());
59+
}
60+
}
61+
}
62+
63+
/**
64+
* ONGOING 상태 투표 처리
65+
* - 3분 집계
66+
* - 30분 / 2시간 / 6시간 / 12시간 독촉
67+
* - 24시간 최후통첩
68+
*/
69+
private void processOngoingPoll(TimePoll poll) {
70+
long minutesElapsed = Duration.between(poll.getCreatedAt(), Instant.now()).toMinutes();
71+
List<String> pendingNames = timePollService.getPendingNames(poll.getId());
72+
73+
// 미투표자가 없으면 스킵 (전원 투표 완료는 submit에서 처리)
74+
if (pendingNames.isEmpty()) {
75+
return;
76+
}
77+
78+
// --- 3분 집계 ---
79+
if (minutesElapsed >= AGGREGATE_MINUTES && poll.getLastReminderStep() < 1) {
80+
sendAggregateMessage(poll);
81+
poll.setLastReminderStep(1);
82+
timePollRepository.save(poll);
83+
return;
84+
}
85+
86+
// 3분 전인데 과반 이상 투표 시 집계
87+
if (minutesElapsed < AGGREGATE_MINUTES && poll.getLastReminderStep() < 1) {
88+
if (timePollService.isMajoritySubmitted(poll.getId())) {
89+
sendAggregateMessage(poll);
90+
poll.setLastReminderStep(1);
91+
timePollRepository.save(poll);
92+
}
93+
return;
94+
}
95+
96+
// --- 24시간 최후통첩 ---
97+
if (minutesElapsed >= ULTIMATUM_MINUTES && poll.getLastReminderStep() < 6) {
98+
sendUltimatumMessage(poll, pendingNames);
99+
poll.setLastReminderStep(6);
100+
poll.setStatus(TimePollStatus.ULTIMATUM);
101+
poll.setUltimatumSentAt(Instant.now());
102+
timePollRepository.save(poll);
103+
return;
104+
}
105+
106+
// --- 독촉 메시지 ---
107+
if (minutesElapsed >= REMIND_4_MINUTES && poll.getLastReminderStep() < 5) {
108+
sendReminderMessage(poll, pendingNames, 5);
109+
} else if (minutesElapsed >= REMIND_3_MINUTES && poll.getLastReminderStep() < 4) {
110+
sendReminderMessage(poll, pendingNames, 4);
111+
} else if (minutesElapsed >= REMIND_2_MINUTES && poll.getLastReminderStep() < 3) {
112+
sendReminderMessage(poll, pendingNames, 3);
113+
} else if (minutesElapsed >= REMIND_1_MINUTES && poll.getLastReminderStep() < 2) {
114+
sendReminderMessage(poll, pendingNames, 2);
115+
}
116+
}
117+
118+
/**
119+
* 최후통첩 60분 경과 - 자동 확정
120+
*/
121+
private void processAutoFinalize(TimePoll poll) {
122+
List<String> pendingNames = timePollService.getPendingNames(poll.getId());
123+
124+
log.info("[TimePollScheduler] Auto-finalize pollId={}, pending={}", poll.getId(), pendingNames);
125+
126+
timePollService.finalize(poll.getId());
127+
128+
// TODO: "00시로 확정됨" 통보 메시지 발송
129+
}
130+
131+
/**
132+
* 집계 메시지 발송
133+
*/
134+
private void sendAggregateMessage(TimePoll poll) {
135+
var status = timePollService.getStatus(poll.getId());
136+
137+
if (status.getSubmittedCount() == 0) {
138+
log.info("[TimePollScheduler] Aggregate - no votes yet. pollId={}", poll.getId());
139+
} else {
140+
log.info("[TimePollScheduler] Aggregate - sharing status. pollId={}, voteCount={}",
141+
poll.getId(), status.getSubmittedCount());
142+
}
143+
144+
// TODO: send chatbot message
145+
}
146+
147+
/**
148+
* 독촉 메시지 발송
149+
*/
150+
private void sendReminderMessage(TimePoll poll, List<String> pendingNames, int step) {
151+
String mentions = String.join(", ", pendingNames);
152+
153+
switch (step) {
154+
case 2 -> // 30min
155+
log.info("[TimePollScheduler] Reminder 30min. pollId={}, pending={}",
156+
poll.getId(), mentions);
157+
case 3 -> // 2h
158+
log.info("[TimePollScheduler] Reminder 2h. pollId={}, pending={}",
159+
poll.getId(), mentions);
160+
case 4 -> // 6h
161+
log.info("[TimePollScheduler] Reminder 6h. pollId={}, pending={}",
162+
poll.getId(), mentions);
163+
case 5 -> // 12h
164+
log.info("[TimePollScheduler] Reminder 12h. pollId={}, pending={}",
165+
poll.getId(), mentions);
166+
}
167+
168+
poll.setLastReminderStep(step);
169+
timePollRepository.save(poll);
170+
171+
// TODO: send chatbot message
172+
}
173+
174+
/**
175+
* 최후통첩 메시지 발송
176+
*/
177+
private void sendUltimatumMessage(TimePoll poll, List<String> pendingNames) {
178+
String mentions = String.join(", ", pendingNames);
179+
180+
log.info("[TimePollScheduler] Ultimatum sent. pollId={}, pending={}, autoFinalize in 60min",
181+
poll.getId(), mentions);
182+
183+
// TODO: send chatbot message with buttons
184+
}
185+
}

0 commit comments

Comments
 (0)