Skip to content

Commit

Permalink
Merge pull request #77 from Kusitms-POPTATO-DEV/refactor/scheduler
Browse files Browse the repository at this point in the history
Refactor: scheduler
  • Loading branch information
yeonjookang authored Nov 5, 2024
2 parents 852398d + 6887b4f commit 8ebbe6d
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 24 deletions.
33 changes: 20 additions & 13 deletions src/main/java/server/poptato/todo/application/TodoScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,48 @@
@RequiredArgsConstructor
public class TodoScheduler {
private final TodoRepository todoRepository;
@Scheduled(cron = "0 0 0 * * *") // 매일 자정에 실행
@Scheduled(cron = "0 0 0 * * *")
@Transactional
public void updateTodoType() {
// 1. TODAY 상태에서 INCOMPLETE인 할 일들을 YESTERDAY로 전환 (사용자별로 처리)
Map<Long, List<Todo>> todayIncompleteTodosByUser = todoRepository.findByTypeAndTodayStatus(Type.TODAY, TodayStatus.INCOMPLETE)
.stream()
.collect(Collectors.groupingBy(Todo::getUserId)); // 사용자별로 그룹화
List<Long> updatedTodoIds = new ArrayList<>();
Map<Long, List<Todo>> userIdAndIncompleteTodaysMap = changeIncompleteTodayToYesterday(updatedTodoIds);
List<Todo> yesterdayIncompleteTodos = changeIncompleteYesterdayToBacklog(updatedTodoIds);
save(userIdAndIncompleteTodaysMap, yesterdayIncompleteTodos);
}

List<Long> updatedTodoIds = new ArrayList<>(); // YESTERDAY로 전환된 할일들의 ID 저장
private Map<Long, List<Todo>> changeIncompleteTodayToYesterday(List<Long> updatedTodoIds) {
Map<Long, List<Todo>> userIdAndIncompleteTodaysMap = todoRepository.findByTypeAndTodayStatus(Type.TODAY, TodayStatus.INCOMPLETE)
.stream()
.collect(Collectors.groupingBy(Todo::getUserId));

todayIncompleteTodosByUser.forEach((userId, todos) -> {
userIdAndIncompleteTodaysMap.forEach((userId, todos) -> {
Integer minBacklogOrder = todoRepository.findMinBacklogOrderByUserIdOrZero(userId);
int startingOrder = minBacklogOrder - 1;

for (Todo todo : todos) {
todo.setType(Type.YESTERDAY);
todo.setBacklogOrder(startingOrder--);
updatedTodoIds.add(todo.getId()); // YESTERDAY로 전환된 항목의 ID 추가
updatedTodoIds.add(todo.getId());
}
});
return userIdAndIncompleteTodaysMap;
}

// 2. YESTERDAY 상태에서 INCOMPLETE인 할 일들을 BACKLOG로 전환 (BacklogOrder 유지)
// 첫 번째 단계에서 YESTERDAY로 전환된 항목을 제외하고 처리
private List<Todo> changeIncompleteYesterdayToBacklog(List<Long> updatedTodoIds) {
List<Todo> yesterdayIncompleteTodos = todoRepository.findByTypeAndTodayStatus(Type.YESTERDAY, TodayStatus.INCOMPLETE)
.stream()
.filter(todo -> !updatedTodoIds.contains(todo.getId())) // 이미 YESTERDAY로 바뀐 항목 제외
.filter(todo -> !updatedTodoIds.contains(todo.getId()))
.collect(Collectors.toList());

yesterdayIncompleteTodos.forEach(todo -> {
todo.setType(Type.BACKLOG);
todo.setTodayStatus(null);
});
return yesterdayIncompleteTodos;
}

// 3. 저장
for (Todo todo : todayIncompleteTodosByUser.values().stream().flatMap(List::stream).collect(Collectors.toList())) {
private void save(Map<Long, List<Todo>> userIdAndIncompleteTodaysMap, List<Todo> yesterdayIncompleteTodos) {
for (Todo todo : userIdAndIncompleteTodaysMap.values().stream().flatMap(List::stream).collect(Collectors.toList())) {
todoRepository.save(todo);
}
for (Todo todo : yesterdayIncompleteTodos) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ default List<Todo> findCompletedTodays(Long userId, Type type, LocalDate todayDa
default Page<Todo> findHistories(Long userId, LocalDate today, Pageable pageable) {
return findByUserIdAndCompletedStatusAndDifferentTodayDate(userId, TodayStatus.COMPLETED, today, pageable);
}

List<Todo> findByType(Type type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,73 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.transaction.annotation.Transactional;
import server.poptato.todo.domain.entity.Todo;
import server.poptato.todo.domain.repository.TodoRepository;
import server.poptato.todo.domain.value.Type;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;

@Transactional
@SpringBootTest
class TodoSchedulerTest {
@Autowired
TodoRepository todoRepository;
@Autowired
TodoScheduler todoScheduler;

@Test
@DisplayName("updateType 메서드가 매일 자정에 실행되어야 한다")
public void shouldTrigger_updateType_atEveryMidNight() throws ParseException {
// Given - 상황 설정
String cronExpression = "0 0 0 * * *"; // 자정에 실행되는 cron 표현식
@DisplayName("스케줄러가 매일 자정에 성공적으로 실행된다.")
public void scheduler_cron_Success() throws ParseException {
//given
String cronExpression = "0 0 0 * * *";
CronTrigger trigger = new CronTrigger(cronExpression);
Date startTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse("2023/12/19 23:59:50");
SimpleTriggerContext context = new SimpleTriggerContext();
context.update(startTime, startTime, startTime);

// 예상되는 실행 시간 목록
String expectedTime = "2023/12/20 00:00:00";

Date nextExecutionTime = trigger.nextExecutionTime(context);

// Then - 결과 검증
//when & then
String actualTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(nextExecutionTime);

// 타입 불일치를 해결하기 위해 문자열 비교를 위한 Matcher 사용
Assertions.assertThat(actualTime).isEqualTo(expectedTime); // 여기서 `is`는 문자열을 비교할 때 사용
Assertions.assertThat(actualTime).isEqualTo(expectedTime);
context.update(nextExecutionTime, nextExecutionTime, nextExecutionTime);
}

@Test
@DisplayName("updateTodoType 메서드가 성공적으로 실행된다.")
void updateTodoType_Success() {
//when
todoScheduler.updateTodoType();

//then
List<Todo> yesterdayTasks = todoRepository.findByType(Type.YESTERDAY);
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 1")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 2")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 4")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 5")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 7")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 8")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 10")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 12")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 14")));
assertTrue(yesterdayTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 16")));

List<Todo> backlogTasks = todoRepository.findByType(Type.BACKLOG);
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 1")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 2")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 3")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 4")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 5")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 6")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 7")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 8")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 9")));
assertTrue(backlogTasks.stream().anyMatch(todo -> todo.getContent().equals("할 일 10")));
}
}

0 comments on commit 8ebbe6d

Please sign in to comment.