diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index efba31b..78edcb7 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -13,6 +13,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 + - name: Add SSH host key run: | mkdir -p ~/.ssh @@ -24,6 +33,17 @@ jobs: echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/private_key.pem chmod 600 ~/.ssh/private_key.pem + - name: Build with Gradle + run: ./gradlew build -x test + + - name: Upload JAR to Remote Server + run: | + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/private_key.pem + chmod 600 ~/.ssh/private_key.pem + scp -i ~/.ssh/private_key.pem build/libs/todo-0.0.1-SNAPSHOT.jar ${{ secrets.USER }}@${{ secrets.HOST }}:${{ secrets.APP_DIR }} + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + - name: Deploy to Remote Server run: | ssh -i ~/.ssh/private_key.pem ${{ secrets.USER }}@${{ secrets.HOST }} ' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4189900..5208407 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,18 +26,4 @@ jobs: run: ./gradlew test - name: Build with Gradle - run: ./gradlew build -x test - - - name: Add SSH host key - run: | - mkdir -p ~/.ssh - ssh-keyscan -H ${{ secrets.HOST }} >> ~/.ssh/known_hosts - chmod 644 ~/.ssh/known_hosts - - - name: Upload JAR to Remote Server - run: | - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/private_key.pem - chmod 600 ~/.ssh/private_key.pem - scp -i ~/.ssh/private_key.pem build/libs/todo-0.0.1-SNAPSHOT.jar ${{ secrets.USER }}@${{ secrets.HOST }}:${{ secrets.APP_DIR }} - env: - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} \ No newline at end of file + run: ./gradlew build -x test \ No newline at end of file diff --git a/src/main/java/com/codeit/todo/repository/GoalRepository.java b/src/main/java/com/codeit/todo/repository/GoalRepository.java index 2bdc7ef..ddd6415 100644 --- a/src/main/java/com/codeit/todo/repository/GoalRepository.java +++ b/src/main/java/com/codeit/todo/repository/GoalRepository.java @@ -1,6 +1,8 @@ package com.codeit.todo.repository; import com.codeit.todo.domain.Goal; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; @@ -10,4 +12,8 @@ public interface GoalRepository extends JpaRepository { List findByUser_UserId(int userId); Optional findByGoalIdAndUser_UserId(int goalId, int userId); + + Slice findByUser_UserId(int userId, Pageable pageable); + + Slice findByGoalIdAndUser_UserId(Integer goalId, int userId, Pageable pageable); } diff --git a/src/main/java/com/codeit/todo/repository/TodoRepository.java b/src/main/java/com/codeit/todo/repository/TodoRepository.java index 45682f7..9fc7177 100644 --- a/src/main/java/com/codeit/todo/repository/TodoRepository.java +++ b/src/main/java/com/codeit/todo/repository/TodoRepository.java @@ -20,5 +20,8 @@ public interface TodoRepository extends JpaRepository { Slice findByGoal_GoalIdAndTodoIdLessThanOrderByTodoIdDesc(int goalId, Integer lastTodoId, Pageable pageable); @Query("select t from Todo t where t.goal.goalId in :goalIds and :today between t.startDate and t.endDate") - List findTodosBetweenDates(@Param("goalIds") List goalIds, @Param("today") LocalDate today); + List findTodosByGoalIdsBetweenDates(@Param("goalIds") List goalIds, @Param("today") LocalDate today); + + @Query("select t from Todo t where t.goal.goalId = :goalId and :today between t.startDate and t.endDate") + List findTodosByGoalIdBetweenDates(@Param("goalId") int goalId, @Param("today") LocalDate today); } diff --git a/src/main/java/com/codeit/todo/service/todo/TodoService.java b/src/main/java/com/codeit/todo/service/todo/TodoService.java index 94d8e84..a24d730 100644 --- a/src/main/java/com/codeit/todo/service/todo/TodoService.java +++ b/src/main/java/com/codeit/todo/service/todo/TodoService.java @@ -1,9 +1,6 @@ package com.codeit.todo.service.todo; -import com.codeit.todo.web.dto.request.todo.CreateTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoWithGoalRequest; -import com.codeit.todo.web.dto.request.todo.UpdateTodoRequest; +import com.codeit.todo.web.dto.request.todo.*; import com.codeit.todo.web.dto.response.todo.*; import org.springframework.data.domain.Slice; @@ -15,7 +12,7 @@ public interface TodoService { CreateTodoResponse saveTodo(int userId, CreateTodoRequest request); - List findTodoListWithGoals(int userId, ReadTodoWithGoalRequest request); + Slice findTodoListWithGoals(int userId, ReadDashBoardTodoWithGoalRequest request); Slice findTodoListWithGoal(int userId, int goalId, ReadTodoWithGoalRequest request); diff --git a/src/main/java/com/codeit/todo/service/todo/impl/TodoServiceImpl.java b/src/main/java/com/codeit/todo/service/todo/impl/TodoServiceImpl.java index 4a17ae3..007644e 100644 --- a/src/main/java/com/codeit/todo/service/todo/impl/TodoServiceImpl.java +++ b/src/main/java/com/codeit/todo/service/todo/impl/TodoServiceImpl.java @@ -11,12 +11,10 @@ import com.codeit.todo.repository.TodoRepository; import com.codeit.todo.service.storage.StorageService; import com.codeit.todo.service.todo.TodoService; -import com.codeit.todo.web.dto.request.todo.CreateTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoWithGoalRequest; -import com.codeit.todo.web.dto.request.todo.UpdateTodoRequest; +import com.codeit.todo.web.dto.request.todo.*; import com.codeit.todo.web.dto.response.complete.ReadCompleteResponse; import com.codeit.todo.web.dto.response.todo.*; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.PageRequest; @@ -108,19 +106,39 @@ public CreateTodoResponse saveTodo(int userId, CreateTodoRequest request) { @Transactional(readOnly = true) @Override - public List findTodoListWithGoals(int userId, ReadTodoWithGoalRequest request) { - List goals = goalRepository.findByUser_UserId(userId); + public Slice findTodoListWithGoals(int userId, @Valid ReadDashBoardTodoWithGoalRequest request) { + int pageSize = request.size(); + Pageable pageable = PageRequest.of(0, pageSize); - return goals.stream() + Slice goals; + if (Objects.isNull(request.lastGoalId()) || request.lastGoalId() <= 0) { + goals = goalRepository.findByUser_UserId(userId, pageable); + } else { + goals = goalRepository.findByGoalIdAndUser_UserId(request.lastGoalId(), userId, pageable); + } + + List responses = goals.getContent().stream() .map(goal -> { - Pageable pageable = PageRequest.of(0, request.size()); - Slice todos = todoRepository.findByGoal_GoalIdOrderByTodoIdDesc(goal.getGoalId(), pageable); + List todos = todoRepository.findTodosByGoalIdBetweenDates(goal.getGoalId(), LocalDate.now()); - List responses = getTodoResponses(todos); + List todosResponses = todos.stream() + .map(todo -> { + List completes = completeRepository.findByTodo_TodoId(todo.getTodoId()); - return ReadTodosWithGoalsResponse.from(goal, responses); + List completeResponses = completes.stream() + .map(ReadCompleteResponse::from) + .toList(); - }).toList(); + return ReadTodosResponse.from(todo, completeResponses); + }).toList(); + + double goalProgress = calculateGoalProgress(todos); + + return ReadTodosWithGoalsResponse.from(goal, todosResponses, goalProgress); + }) + .toList(); + + return new SliceImpl<>(responses, pageable, goals.hasNext()); } @Transactional(readOnly = true) @@ -234,7 +252,7 @@ private List getTodayTodos(LocalDate today, int userId) { .map(Goal::getGoalId) .toList(); - return todoRepository.findTodosBetweenDates(goalIds, today); + return todoRepository.findTodosByGoalIdsBetweenDates(goalIds, today); } private List getTodoResponses(Slice todos) { @@ -249,4 +267,19 @@ private List getTodoResponses(Slice todos) { return ReadTodosResponse.from(todo, completeResponses); }).toList(); } + + private double calculateGoalProgress(List todos) { + long totalCompletes = 0; + long completedCompletes = 0; + + for (Todo todo : todos) { + List completes = completeRepository.findByTodo_TodoId(todo.getTodoId()); + totalCompletes += completes.size(); + completedCompletes += completes.stream() + .filter(complete -> COMPLETE.equals(complete.getCompleteStatus())) + .count(); + } + + return totalCompletes > 0 ? (completedCompletes / (double) totalCompletes) * 100 : 0; + } } diff --git a/src/main/java/com/codeit/todo/web/controller/TodoController.java b/src/main/java/com/codeit/todo/web/controller/TodoController.java index c410cf6..7839d5a 100644 --- a/src/main/java/com/codeit/todo/web/controller/TodoController.java +++ b/src/main/java/com/codeit/todo/web/controller/TodoController.java @@ -2,10 +2,7 @@ import com.codeit.todo.repository.CustomUserDetails; import com.codeit.todo.service.todo.TodoService; -import com.codeit.todo.web.dto.request.todo.CreateTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoRequest; -import com.codeit.todo.web.dto.request.todo.ReadTodoWithGoalRequest; -import com.codeit.todo.web.dto.request.todo.UpdateTodoRequest; +import com.codeit.todo.web.dto.request.todo.*; import com.codeit.todo.web.dto.response.Response; import com.codeit.todo.web.dto.response.goal.ReadGoalsResponse; import com.codeit.todo.web.dto.response.todo.*; @@ -51,8 +48,8 @@ public Response> getTodoList( @ApiResponse(responseCode = "200", description = "조회 성공") }) @GetMapping("/goals") - public Response> getTodoWithGoalList( - @Valid @ModelAttribute ReadTodoWithGoalRequest request, + public Response> getTodoWithGoalList( + @Valid @ModelAttribute ReadDashBoardTodoWithGoalRequest request, @AuthenticationPrincipal CustomUserDetails userDetails ) { int userId = userDetails.getUserId(); diff --git a/src/main/java/com/codeit/todo/web/dto/request/todo/ReadDashBoardTodoWithGoalRequest.java b/src/main/java/com/codeit/todo/web/dto/request/todo/ReadDashBoardTodoWithGoalRequest.java new file mode 100644 index 0000000..487bcc0 --- /dev/null +++ b/src/main/java/com/codeit/todo/web/dto/request/todo/ReadDashBoardTodoWithGoalRequest.java @@ -0,0 +1,10 @@ +package com.codeit.todo.web.dto.request.todo; + +import jakarta.validation.constraints.Min; + +public record ReadDashBoardTodoWithGoalRequest( + Integer lastGoalId, + @Min(3) + int size +) { +} diff --git a/src/main/java/com/codeit/todo/web/dto/response/todo/ReadTodosWithGoalsResponse.java b/src/main/java/com/codeit/todo/web/dto/response/todo/ReadTodosWithGoalsResponse.java index 90ca0fa..15728da 100644 --- a/src/main/java/com/codeit/todo/web/dto/response/todo/ReadTodosWithGoalsResponse.java +++ b/src/main/java/com/codeit/todo/web/dto/response/todo/ReadTodosWithGoalsResponse.java @@ -10,13 +10,15 @@ public record ReadTodosWithGoalsResponse( int goalId, String goalTitle, String goalColor, + double progress, List todos ) { - public static ReadTodosWithGoalsResponse from(Goal goal, List responses) { + public static ReadTodosWithGoalsResponse from(Goal goal, List responses, double goalProgress) { return ReadTodosWithGoalsResponse.builder() .goalId(goal.getGoalId()) .goalTitle(goal.getGoalTitle()) .goalColor(goal.getColor()) + .progress(goalProgress) .todos(responses) .build(); }