From b9d643af3c1c15d418a15774b6a646a15dbb8c4f Mon Sep 17 00:00:00 2001 From: MODUGGAGI Date: Thu, 20 Feb 2025 00:16:27 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat=20:=20=EC=BB=AC=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EC=97=90=ED=94=BC=EC=86=8C=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/ResourceConverter.java | 20 +++++++ .../common/AuthTokenCleanupScheduler.java | 3 + .../service/collection/CollectionService.java | 55 ++++++++++++++++--- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java b/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java index c2f2bc27..87d18460 100644 --- a/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java +++ b/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java @@ -76,6 +76,26 @@ public static ResourceResponseDTO.changeEpisodeIsCompleteDTO toChangeEpisodeIsCo .build(); } + public static ResourceResponseDTO.SearchResultResourceDTO convertToResourceDTO( + CollectionEpisode episode, + UserEpisodeProgress userProgress, // added: 사용자 진도 정보 추가 + Integer currentEpisodeNumber // added: 현재 학습 중인 에피소드 번호 추가 + ) { + return ResourceResponseDTO.SearchResultResourceDTO.builder() + .episodeId(episode.getId()) + .episodeName(episode.getEpisodeName()) + .url(episode.getResource().getUrl()) + .resourceSource(extractResourceSource(episode.getResource().getUrl())) + .episodeNumber(episode.getEpisodeNumber()) + // added: today 값 설정 - 다음 학습할 에피소드인지 확인 + .today(episode.getEpisodeNumber().equals(currentEpisodeNumber + 1)) + // added: completed 값 설정 - 현재 에피소드보다 번호가 작으면 완료된 것 + .completed(episode.getEpisodeNumber() <= currentEpisodeNumber) + // added: progress 값 설정 - 해당 에피소드의 진도율 + .progress(userProgress != null ? userProgress.getCurrentProgress() : null) + .build(); + } + public static ResourceResponseDTO.SearchResultResourceDTO convertToResourceDTO( CollectionEpisode episode, UserEpisodeProgress progress diff --git a/src/main/java/learningFlow/learningFlow_BE/service/auth/common/AuthTokenCleanupScheduler.java b/src/main/java/learningFlow/learningFlow_BE/service/auth/common/AuthTokenCleanupScheduler.java index c19634de..40d1e637 100644 --- a/src/main/java/learningFlow/learningFlow_BE/service/auth/common/AuthTokenCleanupScheduler.java +++ b/src/main/java/learningFlow/learningFlow_BE/service/auth/common/AuthTokenCleanupScheduler.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; @@ -16,11 +17,13 @@ public class AuthTokenCleanupScheduler { private final PasswordResetTokenRepository tokenRepository; @Scheduled(cron = "0 0 0 * * *") // 매일 자정에 실행 + @Transactional public void cleanupExpiredTokens() { tokenRepository.deleteByExpiryDateBefore(LocalDateTime.now()); } @Scheduled(cron = "0 0 0 * * *") // 매일 자정에 실행 + @Transactional public void cleanupExpiredEmailVerificationTokens() { emailVerificationTokenRepository.deleteByExpiryDateBefore(LocalDateTime.now()); } diff --git a/src/main/java/learningFlow/learningFlow_BE/service/collection/CollectionService.java b/src/main/java/learningFlow/learningFlow_BE/service/collection/CollectionService.java index 630dd00e..fa041b02 100644 --- a/src/main/java/learningFlow/learningFlow_BE/service/collection/CollectionService.java +++ b/src/main/java/learningFlow/learningFlow_BE/service/collection/CollectionService.java @@ -141,8 +141,8 @@ public CollectionResponseDTO.CollectionLearningInfo getLearningInfo( .learningStatus("BEFORE") .progressRate(null) .resourceDTOList(isDetailView ? - getAllResources(collection, null) : // 상세 조회면 전체 리소스 - getFilteredResources(collection, null, 0)) // 아니면 필터링된 리소스 + getAllResourcesWithProgress(collection, null, 0) : + getFilteredResources(collection, null, 0)) .build(); } @@ -153,8 +153,8 @@ public CollectionResponseDTO.CollectionLearningInfo getLearningInfo( .learningStatus("BEFORE") .progressRate(null) .resourceDTOList(isDetailView ? - getAllResources(collection, user) : // 상세 조회면 전체 리소스 - getFilteredResources(collection, null, 0)) // 아니면 필터링된 리소스 + getAllResourcesWithProgress(collection, null, 0) : + getFilteredResources(collection, null, 0)) .build(); } @@ -166,8 +166,8 @@ public CollectionResponseDTO.CollectionLearningInfo getLearningInfo( .startDate(realUserCollection.getCreatedAt().toLocalDate()) .completedDate(realUserCollection.getCompletedTime()) .resourceDTOList(isDetailView ? - getAllResources(collection) : // 상세 조회면 전체 리소스 - new ArrayList<>()) // 아니면 빈 리스트 + getAllResourcesWithProgress(collection, user, realUserCollection.getUserCollectionStatus()) : + new ArrayList<>()) .build(); } @@ -178,11 +178,34 @@ public CollectionResponseDTO.CollectionLearningInfo getLearningInfo( .startDate(realUserCollection.getCreatedAt().toLocalDate()) .currentEpisode(realUserCollection.getUserCollectionStatus()) .resourceDTOList(isDetailView ? - getAllResources(collection, user) : + getAllResourcesWithProgress(collection, user, realUserCollection.getUserCollectionStatus()) : getFilteredResources(collection, user, realUserCollection.getUserCollectionStatus())) .build(); } + private List getAllResourcesWithProgress( + Collection collection, + User user, + int currentEpisodeNumber + ) { + return collection.getEpisodes().stream() + .sorted(Comparator.comparing(CollectionEpisode::getEpisodeNumber)) + .map(episode -> { + UserEpisodeProgress progress = null; + if (user != null) { + progress = userEpisodeProgressRepository.findById( + new UserEpisodeProgressId(episode.getId(), user.getLoginId()) + ).orElse(null); + } + return ResourceConverter.convertToResourceDTO( + episode, + progress, + currentEpisodeNumber + ); + }) + .toList(); + } + private int calculateProgressRate(UserCollection userCollection) { return (int) Math.round((double) userCollection.getUserCollectionStatus() / userCollection.getCollection().getEpisodes().size() * 100); @@ -216,9 +239,23 @@ private List getFilteredResources( .limit(4) .toList(); } - - return filteredEpisodes.stream() +/* return filteredEpisodes.stream() .map(ResourceConverter::convertToResourceDTO) + .toList();*/ + return filteredEpisodes.stream() + .map(episode -> { + UserEpisodeProgress progress = null; + if (user != null) { + progress = userEpisodeProgressRepository.findById( + new UserEpisodeProgressId(episode.getId(), user.getLoginId()) + ).orElse(null); + } + return ResourceConverter.convertToResourceDTO( + episode, + progress, + currentEpisode + ); + }) .toList(); } From 7312cbb1067e255b75aa010d7f327fa27764baf7 Mon Sep 17 00:00:00 2001 From: MODUGGAGI Date: Thu, 20 Feb 2025 01:58:01 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat=20:=20=EA=B5=AC=EA=B8=80=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/handler/OAuth2LoginSuccessHandler.java | 3 --- src/main/resources/application.yml | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/learningFlow/learningFlow_BE/security/handler/OAuth2LoginSuccessHandler.java b/src/main/java/learningFlow/learningFlow_BE/security/handler/OAuth2LoginSuccessHandler.java index 914667fd..d1019011 100644 --- a/src/main/java/learningFlow/learningFlow_BE/security/handler/OAuth2LoginSuccessHandler.java +++ b/src/main/java/learningFlow/learningFlow_BE/security/handler/OAuth2LoginSuccessHandler.java @@ -86,13 +86,10 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo .build(); response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString()); - response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Expose-Headers", "Authorization, Refresh-Token"); - - // ⭐️ 팝업 창을 닫고 부모 창에 메시지 전달하는 스크립트 (프론트엔드 도메인 사용) String redirectScript = "