diff --git a/src/main/java/learningFlow/learningFlow_BE/apiPayload/code/status/ErrorStatus.java b/src/main/java/learningFlow/learningFlow_BE/apiPayload/code/status/ErrorStatus.java index d74127ce..4aefc931 100644 --- a/src/main/java/learningFlow/learningFlow_BE/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/learningFlow/learningFlow_BE/apiPayload/code/status/ErrorStatus.java @@ -75,6 +75,8 @@ public enum ErrorStatus implements BaseErrorCode { // Resource 에러 RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "RESOURCE4001", "존재하지 않는 리소스 입니다."), + USER_COLLECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "USER COLLECTION4001", "존재하지 않는 리소스 입니다."), + // User Progress USER_PROGRESS_NOT_FOUND(HttpStatus.NOT_FOUND, "USER-PROGRESS4001", "진도 값이 null 인 상태입니다."); private final HttpStatus httpStatus; diff --git a/src/main/java/learningFlow/learningFlow_BE/converter/CollectionConverter.java b/src/main/java/learningFlow/learningFlow_BE/converter/CollectionConverter.java index d8e05c15..e329aac4 100644 --- a/src/main/java/learningFlow/learningFlow_BE/converter/CollectionConverter.java +++ b/src/main/java/learningFlow/learningFlow_BE/converter/CollectionConverter.java @@ -102,7 +102,7 @@ public static CollectionResponseDTO.CollectionPreviewDTO toCollectionPreviewDTO( .likesCount(collection.getBookmarkCount()) //북마크 -> 좋아요로 이름만 변경 .isLiked(currentUser != null && currentUser.hasBookmarked(collection.getId())) //북마크 -> 좋아요로 이름만 변경 .progressRatePercentage(learningInfo.getProgressRate()) - .progressRatio(calculateProgressRatio(collection, learningInfo)) + .progressRatio(learningInfo.getProgressRatio()) .learningStatus(learningInfo.getLearningStatus()) .startDate(learningInfo.getStartDate()) .completedDate(learningInfo.getCompletedDate()) diff --git a/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java b/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java index c2f2bc27..93a28e10 100644 --- a/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java +++ b/src/main/java/learningFlow/learningFlow_BE/converter/ResourceConverter.java @@ -63,10 +63,11 @@ public static List episodeInformationLis return episodeInformationList; } - public static ResourceResponseDTO.ProgressResponseDTO toSaveProgressResponse(ResourceRequestDTO.ProgressRequestDTO request) { + public static ResourceResponseDTO.ProgressResponseDTO toSaveProgressResponse(ResourceRequestDTO.ProgressRequestDTO request, Boolean isCompleted) { return ResourceResponseDTO.ProgressResponseDTO.builder() .progress(request.getProgress()) .resourceType(request.getResourceType()) + .isCompleted(isCompleted) .build(); } @@ -76,6 +77,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(userProgress != null && userProgress.getIsComplete()) + // added: progress 값 설정 - 해당 에피소드의 진도율 + .progress(userProgress != null ? userProgress.getCurrentProgress() : null) + .build(); + } + public static ResourceResponseDTO.SearchResultResourceDTO convertToResourceDTO( CollectionEpisode episode, UserEpisodeProgress progress @@ -101,18 +122,27 @@ public static ResourceResponseDTO.RecentlyWatchedEpisodeDTO convertToRecentlyWat UserEpisodeProgress userEpisodeProgress ) { return ResourceResponseDTO.RecentlyWatchedEpisodeDTO.builder() - .resourceId(getResourceId(userCollection)) + .episodeId(getEpisodeId(userCollection)) .collectionId(userCollection.getCollection().getId()) .collectionTitle(userCollection.getCollection().getTitle()) .resourceSource(extractResourceSource(getResourceUrl(userCollection))) .episodeNumber(userCollection.getUserCollectionStatus()) .episodeName(getEpisodeName(userCollection)) .progressRatio(calculateProgressRatio(userCollection)) - .currentProgress(userEpisodeProgress.getCurrentProgress()) - .totalProgress(userEpisodeProgress.getTotalProgress()) + .currentProgress(userEpisodeProgress != null ? userEpisodeProgress.getCurrentProgress() : 0) + .totalProgress(userEpisodeProgress != null ? userEpisodeProgress.getTotalProgress() : 0) .build(); } + private static Long getEpisodeId(UserCollection userCollection) { + // added: episodeId를 찾는 메소드 추가 + return userCollection.getCollection().getEpisodes().stream() + .filter(episode -> episode.getEpisodeNumber().equals(userCollection.getUserCollectionStatus())) + .findFirst() + .map(CollectionEpisode::getId) + .orElse(null); + } + public static List convertToResourceDTOWithToday( List episodes, int nextEpisodeNumber, @@ -131,6 +161,29 @@ public static List convertToResourc .toList(); } + public static ResourceResponseDTO.RecentlyWatchedEpisodeDTO convertToRecentlyWatchedEpisodeDTO( + UserCollection userCollection, + UserEpisodeProgress userEpisodeProgress, + CollectionEpisode currentEpisode, // added: 파라미터 추가 + int totalEpisodes, // added: 파라미터 추가 + double progressPercentage // added: 파라미터 추가 + ) { + return ResourceResponseDTO.RecentlyWatchedEpisodeDTO.builder() + .episodeId(currentEpisode.getId()) + .collectionId(userCollection.getCollection().getId()) + .collectionTitle(userCollection.getCollection().getTitle()) + .resourceSource(extractResourceSource(currentEpisode.getResource().getUrl())) + .episodeNumber(userCollection.getUserCollectionStatus()) + .episodeName(currentEpisode.getEpisodeName()) + .progressRatio(String.format("%d / %d회차 (%.0f%%)", + userCollection.getUserCollectionStatus(), + totalEpisodes, + progressPercentage)) + .currentProgress(userEpisodeProgress != null ? userEpisodeProgress.getCurrentProgress() : 0) + .totalProgress(userEpisodeProgress != null ? userEpisodeProgress.getTotalProgress() : 0) + .build(); + } + public static String extractResourceSource(String url) { String lowerCaseUrl = url.toLowerCase(); diff --git a/src/main/java/learningFlow/learningFlow_BE/domain/BaseEntity.java b/src/main/java/learningFlow/learningFlow_BE/domain/BaseEntity.java index 782cad7f..892cc432 100644 --- a/src/main/java/learningFlow/learningFlow_BE/domain/BaseEntity.java +++ b/src/main/java/learningFlow/learningFlow_BE/domain/BaseEntity.java @@ -7,6 +7,7 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import java.time.LocalDateTime; diff --git a/src/main/java/learningFlow/learningFlow_BE/domain/UserCollection.java b/src/main/java/learningFlow/learningFlow_BE/domain/UserCollection.java index ff1f9b0f..20d80879 100644 --- a/src/main/java/learningFlow/learningFlow_BE/domain/UserCollection.java +++ b/src/main/java/learningFlow/learningFlow_BE/domain/UserCollection.java @@ -75,5 +75,9 @@ public void updateUserCollection(Integer userCollectionStatus) { this.userCollectionStatus = userCollectionStatus; this.completedTime = LocalDate.now(); } + public void CompleteUserCollection(){ + this.status = UserCollectionStatus.COMPLETED; + this.completedTime = LocalDate.now(); + } } diff --git a/src/main/java/learningFlow/learningFlow_BE/repository/UserCollectionRepository.java b/src/main/java/learningFlow/learningFlow_BE/repository/UserCollectionRepository.java index 367ea49b..9cd5b1f0 100644 --- a/src/main/java/learningFlow/learningFlow_BE/repository/UserCollectionRepository.java +++ b/src/main/java/learningFlow/learningFlow_BE/repository/UserCollectionRepository.java @@ -14,7 +14,7 @@ public interface UserCollectionRepository extends JpaRepository { Optional findByUserAndCollection(User user, Collection collection); - List findByUserAndStatusOrderByCompletedTimeDesc(User user, UserCollectionStatus status); + List findByUserAndStatusOrderByUpdatedAtDesc(User user, UserCollectionStatus status); Optional findFirstByUserAndStatusOrderByUpdatedAtDesc(User user, UserCollectionStatus status); @Modifying 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 = "