Skip to content

Commit

Permalink
Merge pull request #49 from Kusitms-POPTATO-DEV/feat/48-scheduler
Browse files Browse the repository at this point in the history
Feat#48: 스케줄러 기능 구현
  • Loading branch information
yeonjookang authored Oct 17, 2024
2 parents dea37a6 + 46a3862 commit f95650d
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/main/java/server/poptato/PoptatoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableFeignClients
@EnableJpaAuditing
@EnableScheduling
public class PoptatoApplication {

public static void main(String[] args) {
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/server/poptato/todo/application/TodoScheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package server.poptato.todo.application;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import server.poptato.todo.domain.entity.Todo;
import server.poptato.todo.domain.repository.TodoRepository;
import server.poptato.todo.domain.value.TodayStatus;
import server.poptato.todo.domain.value.Type;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TodoScheduler {
private final TodoRepository todoRepository;
@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)); // 사용자별로 그룹화

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

for (Todo todo : todos) {
todo.setType(Type.YESTERDAY);
todo.setBacklogOrder(startingOrder--);
}
});

// 2. YESTERDAY 상태에서 INCOMPLETE인 할 일들을 BACKLOG로 전환 (BacklogOrder 유지)
List<Todo> yesterdayIncompleteTodos = todoRepository.findByTypeAndTodayStatus(Type.YESTERDAY, TodayStatus.INCOMPLETE);
yesterdayIncompleteTodos.forEach(todo -> {
todo.setType(Type.BACKLOG);
todo.setTodayStatus(null);
});

// 3. 저장
for(Todo todo : todayIncompleteTodosByUser.values().stream().flatMap(List::stream).collect(Collectors.toList())){
todoRepository.save(todo);
}
for(Todo todo : yesterdayIncompleteTodos){
todoRepository.save(todo);
}
}
}

8 changes: 8 additions & 0 deletions src/main/java/server/poptato/todo/domain/entity/Todo.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,12 @@ public void updateTodayStatusToCompleted() {
this.completedDateTime = LocalDateTime.now();
this.todayOrder = null;
}

public void setType(Type type) {
this.type = type;
}

public void setTodayStatus(TodayStatus todayStatus) {
this.todayStatus = todayStatus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ List<Todo> findByUserIdAndTypeAndTodayDateAndTodayStatusOrderByCompletedDateTime
Long userId, Type type, LocalDate todayDate, TodayStatus todayStatus);
Optional<Todo> findById(Long todoId);
void delete(Todo todo);
// 백로그 목록 조회
Page<Todo> findByUserIdAndTypeInOrderByBacklogOrderAsc(Long userId, List<Type> types, Pageable pageable);
Page<Todo> findByUserIdAndCompletedDateTimeIsNotNull(Long userId, Pageable pageable);
Todo save(Todo todo);
Page<Todo> findByUserIdAndTypeInOrderByBacklogOrderDesc(Long userId, List<Type> types, Pageable pageable);
Integer findMaxBacklogOrderByUserIdOrZero(Long userId);
Integer findMaxTodayOrderByUserIdOrZero(Long userId);
List<Todo> findByIdIn(List<Long> ids);
int findMaxBacklogOrderByIdIn(List<Long> ids);
int findMaxTodayOrderByIdIn(List<Long> ids);
Page<Todo> findByUserIdAndTypeAndTodayStatus(Long userId, Type type, TodayStatus todayStatus, Pageable pageable);
List<Todo> findByTypeAndTodayStatus(Type today, TodayStatus incomplete);
Integer findMinBacklogOrderByUserIdOrZero(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
public interface JpaTodoRepository extends TodoRepository, JpaRepository<Todo,Long> {
@Query("SELECT t FROM Todo t WHERE t.userId = :userId AND t.completedDateTime IS NOT NULL")
Page<Todo> findByUserIdAndCompletedDateTimeIsNotNull(Long userId, Pageable pageable);

@Query("SELECT COALESCE(MAX(t.backlogOrder), 0) FROM Todo t WHERE t.userId = :userId AND t.backlogOrder IS NOT NULL")
Integer findMaxBacklogOrderByUserIdOrZero(Long userId);
@Query("SELECT COALESCE(MAX(t.todayOrder), 0) FROM Todo t WHERE t.userId = :userId AND t.todayOrder IS NOT NULL")
Expand All @@ -22,4 +21,6 @@ public interface JpaTodoRepository extends TodoRepository, JpaRepository<Todo,Lo
int findMaxTodayOrderByIdIn(@Param("ids") List<Long> ids);
@Query("SELECT MAX(t.backlogOrder) FROM Todo t WHERE t.id IN :ids")
int findMaxBacklogOrderByIdIn(@Param("ids") List<Long> ids);
@Query("SELECT COALESCE(MIN(t.backlogOrder), 0) FROM Todo t WHERE t.userId = :userId")
Integer findMinBacklogOrderByUserIdOrZero(@Param("userId") Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package server.poptato.todo.application;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@SpringBootTest
class TodoSchedulerTest {
@Autowired
TodoScheduler todoScheduler;
@Test
@DisplayName("updateType 메서드가 매일 자정에 실행되어야 한다")
public void shouldTrigger_updateType_atEveryMidNight() throws ParseException {
// Given - 상황 설정
String cronExpression = "0 0 0 * * *"; // 자정에 실행되는 cron 표현식
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 - 결과 검증
String actualTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(nextExecutionTime);

// 타입 불일치를 해결하기 위해 문자열 비교를 위한 Matcher 사용
Assertions.assertThat(actualTime).isEqualTo(expectedTime); // 여기서 `is`는 문자열을 비교할 때 사용
context.update(nextExecutionTime, nextExecutionTime, nextExecutionTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.Stack;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
Expand Down

0 comments on commit f95650d

Please sign in to comment.