diff --git a/src/main/java/com/codiary/backend/domain/alert/service/AlertService.java b/src/main/java/com/codiary/backend/domain/alert/service/AlertService.java index 4af8d5f..6a86ae3 100644 --- a/src/main/java/com/codiary/backend/domain/alert/service/AlertService.java +++ b/src/main/java/com/codiary/backend/domain/alert/service/AlertService.java @@ -351,7 +351,7 @@ private void sendMemberNewPostAlert(Post post) { // 이벤트 저장 AlertEvent event = saveEvent( EventCategory.FOLLOWING_MEMBER_NEW_POST, - PostConverter.toSimplePostResponseDto(post), + PostConverter.toPostPreviewDTO(post), receiverIdList ); @@ -383,7 +383,7 @@ private void sendTeamNewPostAlertToTeamFollowers(Post post) { // 이벤트 저장 AlertEvent event = saveEvent( EventCategory.FOLLOWING_TEAM_NEW_POST, - PostConverter.toSimplePostResponseDto(post), + PostConverter.toPostPreviewDTO(post), receiverIdList ); @@ -415,7 +415,7 @@ private void sendTeamNewPostAlertToTeamMembers(Post post) { // 이벤트 저장 AlertEvent event = saveEvent( EventCategory.MY_TEAM_NEW_POST, - PostConverter.toSimplePostResponseDto(post), + PostConverter.toPostPreviewDTO(post), receiverIdList ); diff --git a/src/main/java/com/codiary/backend/domain/post/controller/PostController.java b/src/main/java/com/codiary/backend/domain/post/controller/PostController.java index 6aac8b2..592363e 100644 --- a/src/main/java/com/codiary/backend/domain/post/controller/PostController.java +++ b/src/main/java/com/codiary/backend/domain/post/controller/PostController.java @@ -1,7 +1,6 @@ package com.codiary.backend.domain.post.controller; import com.codiary.backend.domain.alert.service.AlertService; -import com.codiary.backend.domain.category.dto.CategoryResponseDTO; import com.codiary.backend.domain.member.entity.Member; import com.codiary.backend.domain.member.security.CustomMemberDetails; import com.codiary.backend.domain.member.service.MemberCommandService; @@ -20,7 +19,6 @@ import com.codiary.backend.global.apiPayload.exception.GeneralException; import com.codiary.backend.global.jwt.JwtTokenProvider; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; @@ -103,9 +101,12 @@ public ApiResponse deletePost(@PathVariable Long postId) { //특정 게시글 조회 @GetMapping("/{postId}") @Operation(summary = "특정 게시글 조회 API", description = "특정 게시글을 조회합니다.") - public ApiResponse findPost(@PathVariable Long postId){ - Object request; - Post findPost = postQueryService.findById(postId); + public ApiResponse findPost( + @PathVariable Long postId, + @AuthenticationPrincipal CustomMemberDetails memberDetails + ) { + Long memberId = (memberDetails != null) ? memberDetails.getId() : 0; + Post findPost = postQueryService.findById(postId, memberId); return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostPreviewDTO(findPost)); } @@ -186,7 +187,7 @@ public ApiResponse findAdjacentPosts(@PathVaria // 전체 인기글 or 최신글 조회 @Operation(summary = "공개글 리스트 조회", description = "popular/latest 입력 시 인기글/최신글 조회") @GetMapping("/list") - public ApiResponse> getPostList( + public ApiResponse> getPostList( @PageableDefault(size = 9) Pageable pageable ) { Page postPage = postService.getPostList(pageable); @@ -196,12 +197,12 @@ public ApiResponse> getPostList( // 카테고리 인기글 조회 @Operation(summary = "카테고리 인기글/최신글 조회 (popular/latest 입력 (기본 popular)") @GetMapping("/category/{category_id}") - public ApiResponse> getCategoryPopularPosts( + public ApiResponse> getCategoryPopularPosts( @AuthenticationPrincipal CustomMemberDetails memberDetails, @PathVariable("category_id") Long categoryId, @PageableDefault(size = 9, sort = "popular") Pageable pageable ) { - Long memberId = memberDetails.getId(); + Long memberId = (memberDetails != null) ? memberDetails.getId() : 0; Page postPage = postService.getCategoryPosts(memberId, categoryId, pageable); return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostListResponseDto(postPage)); } @@ -221,7 +222,7 @@ public ApiResponse> getCategoryPopul // 게시글 검색 결과 페이지네이션 @Operation(summary = "게시글 검색 결과 페이지네이션", description = "게시글(제목/내용) 키워드 검색 결과를 페이지네이션하여 반환합니다.") @GetMapping("/search") - public ApiResponse> searchPost( + public ApiResponse> searchPost( @RequestParam(value = "keyword", defaultValue = "", required = false) String keyword, @AuthenticationPrincipal CustomMemberDetails memberDetails, @PageableDefault(size = 9) Pageable pageable @@ -249,7 +250,7 @@ public ApiResponse setPostCategory( // 게시글 검색 (저자 이름, 팀 이름, 프로젝트 이름으로 검색) @GetMapping("/search_by_name") @Operation(summary = "게시글 검색 (저자 이름, 팀 이름, 프로젝트 이름으로 검색)") - public ApiResponse> searchByName( + public ApiResponse> searchByName( @RequestParam(value = "author", defaultValue = "", required = false) String authorName, @RequestParam(value = "team", defaultValue = "", required = false) String teamName, @RequestParam(value = "project", defaultValue = "", required = false) String projectName, @@ -292,16 +293,20 @@ public ApiResponse cancelBookmark( @GetMapping("/following/paging") @Operation(summary = "팔로잉한 멤버/팀의 게시글 리스트 페이징 조회 API", description = "팔로잉한 멤버/팀의 게시글 리스트를 페이징으로 조회합니다. **첫 페이지는 0부터 입니다.**") - public ApiResponse findPostByFollowing(@AuthenticationPrincipal CustomMemberDetails memberDetails, - @PageableDefault(size = 9) Pageable pageable) { + public ApiResponse findPostByFollowing( + @AuthenticationPrincipal CustomMemberDetails memberDetails, + @PageableDefault(size = 9) Pageable pageable + ) { Page posts = postQueryService.getPostsByFollowing(memberDetails.getId(), pageable); return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostPreviewListDTO(posts)); } @GetMapping("/bookmark/paging") @Operation(summary = "북마크한 게시글 조회") - public ApiResponse> getBookmarkPost(@AuthenticationPrincipal CustomMemberDetails memberDetails, - @PageableDefault(size = 9) Pageable pageable) { + public ApiResponse> getBookmarkPost( + @AuthenticationPrincipal CustomMemberDetails memberDetails, + @PageableDefault(size = 9) Pageable pageable + ) { return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostListResponseDto(postQueryService.getBookmarkPost(memberDetails.getId(), pageable))); } diff --git a/src/main/java/com/codiary/backend/domain/post/converter/PostConverter.java b/src/main/java/com/codiary/backend/domain/post/converter/PostConverter.java index 778aeae..ab1480d 100644 --- a/src/main/java/com/codiary/backend/domain/post/converter/PostConverter.java +++ b/src/main/java/com/codiary/backend/domain/post/converter/PostConverter.java @@ -15,28 +15,31 @@ public class PostConverter { - public static Page toPostListResponseDto(Page postList) { - return postList.map(PostConverter::toSimplePostResponseDto); + public static Page toPostListResponseDto(Page postList) { + return postList.map(PostConverter::toPostPreviewDTO); } - public static PostResponseDTO.SimplePostResponseDTO toSimplePostResponseDto(Post post) { - return PostResponseDTO.SimplePostResponseDTO.builder() + public static PostResponseDTO.PostPreviewDTO toPostPreviewDTO(Post post) { + return PostResponseDTO.PostPreviewDTO.builder() .id(post.getPostId()) .title(post.getPostTitle()) .body(post.getPostBody()) - .author(post.getMember() != null ? post.getMember().getNickname() : null) - .authorImageUrl((post.getMember() != null && post.getMember().getImage() != null) - ? post.getMember().getImage().getImageUrl() - : "") - .thumbnailImageUrl(post.getThumbnailImage() != null ? post.getThumbnailImage().getFileUrl() : "") - .teamProfileImageUrl((post.getTeam() != null && post.getTeam().getProfileImage() != null) - ? post.getTeam().getProfileImage().getImageUrl() - : "") - .teamBannerImageUrl((post.getTeam() != null && post.getTeam().getBannerImage() != null) - ? post.getTeam().getBannerImage().getImageUrl() - : "") + .thumbnailImageUrl((post.getThumbnailImage() != null) ? post.getThumbnailImage().getFileUrl() : "") .createdAt(post.getCreatedAt()) .updatedAt(post.getUpdatedAt()) + + .authorId(post.getMember().getMemberId()) + .authorName(post.getMember().getNickname()) + .authorImageUrl((post.getMember().getImage() != null) ? post.getMember().getImage().getImageUrl() : "") + + .numberOfCoauthor(post.getAuthorList().size()) + + .teamExist((post.getTeam() != null) ? true : false) + .teamId((post.getTeam() != null) ? post.getTeam().getTeamId() : null) + .teamName((post.getTeam() != null) ? post.getTeam().getName() : null) + .teamProfileImageUrl((post.getTeam() != null && post.getTeam().getProfileImage() != null) + ? post.getTeam().getProfileImage().getImageUrl() + : null) .build(); } @@ -118,45 +121,6 @@ public static PostResponseDTO.UpdatePostResultDTO toUpdatePostResultDTO(Post pos .build(); } - // Post 조회 - public static PostResponseDTO.PostPreviewDTO toPostPreviewDTO(Post post) { - List postCategories = post.getCategoriesList().stream() - .map(Category::getName) - .collect(Collectors.toList()); - - return PostResponseDTO.PostPreviewDTO.builder() - .postId(post.getPostId()) - .memberId(post.getMember().getMemberId()) - .authorNickname(post.getMember().getNickname()) - .teamId(post.getTeam() != null ? post.getTeam().getTeamId() : null) - .projectId(post.getProject() != null ? post.getProject().getProjectId() : null) - .postTitle(post.getPostTitle()) - .postBody(post.getPostBody()) - .postStatus(post.getPostStatus()) - .postCategory(String.join(", ", postCategories)) - .coauthorIds(post.getAuthorList().stream() - .map(author -> author.getMember().getMemberId()) - .collect(Collectors.toSet())) - .postAccess(post.getPostAccess()) - .authorProfileImageUrl((post.getMember().getImage() != null) - ? post.getMember().getImage().getImageUrl() - : "") - .thumbnailImageUrl((post.getThumbnailImage() != null) - ? post.getThumbnailImage().getFileUrl() - : "") - .teamProfileImageUrl((post.getTeam() != null && post.getTeam().getProfileImage() != null) - ? post.getTeam().getProfileImage().getImageUrl() - : "") - .teamBannerImageUrl((post.getTeam() != null && post.getTeam().getBannerImage() != null) - ? post.getTeam().getBannerImage().getImageUrl() - : "") - .postFileList(PostFileConverter.toPostFileListDTO(post.getPostFileList())) - .createdAt(post.getCreatedAt()) - .updatedAt(post.getUpdatedAt()) - .authorNickname(post.getMember().getNickname()) - .build(); - } - // Post 전체 리스트 조회 public static PostResponseDTO.PostPreviewListDTO toPostPreviewListDTO(Page posts) { List postPreviewDTOList = posts.getContent().stream() @@ -173,8 +137,6 @@ public static PostResponseDTO.PostPreviewListDTO toPostPreviewListDTO(Page .build(); } - - // 저자별 Post 조회 public static PostResponseDTO.MemberPostPreviewDTO toMemberPostPreviewDTO(Post post) { List postCategories = post.getCategoriesList().stream() @@ -445,52 +407,13 @@ public static PostResponseDTO.MemberPostInTeamPreviewListDTO toMemberPostInTeamP .build(); } - // 특정 Post의 인접한 Post 조회 (이전, 다음 Post 조회) - public static PostResponseDTO.PostAdjacentDTO.PostAdjacentPreviewDTO toPostAdjacentPreviewDTO(Post post) { - if (post == null) return null; - - List postCategories = post.getCategoriesList().stream() - .map(Category::getName) - .collect(Collectors.toList()); - - return PostResponseDTO.PostAdjacentDTO.PostAdjacentPreviewDTO.builder() - .postId(post.getPostId()) - .memberId(post.getMember().getMemberId()) - .authorNickname(post.getMember().getNickname()) - .teamId(post.getTeam() != null ? post.getTeam().getTeamId() : null) - .projectId(post.getProject() != null ? post.getProject().getProjectId() : null) - .postTitle(post.getPostTitle()) - .postBody(post.getPostBody()) - .postStatus(post.getPostStatus()) - .postCategory(String.join(", ", postCategories)) - .coauthorIds(post.getAuthorList().stream() - .map(author -> author.getMember().getMemberId()) - .collect(Collectors.toSet())) - .postAccess(post.getPostAccess()) - .authorProfileImageUrl((post.getMember().getImage() != null) - ? post.getMember().getImage().getImageUrl() - : "") - .thumbnailImageUrl((post.getThumbnailImage() != null) - ? post.getThumbnailImage().getFileUrl() - : "") - .teamProfileImageUrl((post.getTeam() != null && post.getTeam().getProfileImage() != null) - ? post.getTeam().getProfileImage().getImageUrl() - : "") - .teamBannerImageUrl((post.getTeam() != null && post.getTeam().getBannerImage() != null) - ? post.getTeam().getBannerImage().getImageUrl() - : "") - .postFileList(PostFileConverter.toPostFileListDTO(post.getPostFileList())) - .createdAt(post.getCreatedAt()) - .updatedAt(post.getUpdatedAt()) - .build(); - } - + // 인접 post 조회 public static PostResponseDTO.PostAdjacentDTO toPostAdjacentDTO(Post.PostAdjacent adjacent) { return PostResponseDTO.PostAdjacentDTO.builder() .hadOlder(adjacent.getOlderPost() != null) .hasLater(adjacent.getLaterPost() != null) - .olderPost(toPostAdjacentPreviewDTO(adjacent.getOlderPost())) - .laterPost(toPostAdjacentPreviewDTO(adjacent.getLaterPost())) + .olderPost(toPostPreviewDTO(adjacent.getOlderPost())) + .laterPost(toPostPreviewDTO(adjacent.getLaterPost())) .build(); } diff --git a/src/main/java/com/codiary/backend/domain/post/dto/response/PostResponseDTO.java b/src/main/java/com/codiary/backend/domain/post/dto/response/PostResponseDTO.java index 6b16a55..10a5582 100644 --- a/src/main/java/com/codiary/backend/domain/post/dto/response/PostResponseDTO.java +++ b/src/main/java/com/codiary/backend/domain/post/dto/response/PostResponseDTO.java @@ -16,17 +16,26 @@ public class PostResponseDTO { @Builder @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @JsonInclude(JsonInclude.Include.NON_NULL) - public record SimplePostResponseDTO( + public record PostPreviewDTO( + // post 자체 정보 Long id, String title, String body, - String author, - String authorImageUrl, String thumbnailImageUrl, - String teamProfileImageUrl, - String teamBannerImageUrl, LocalDateTime createdAt, - LocalDateTime updatedAt + LocalDateTime updatedAt, + Integer numberOfBookmark, + // author 정보 + Long authorId, + String authorName, + String authorImageUrl, + // coauthor 정보 + Integer numberOfCoauthor, + // team 정보 + Boolean teamExist, // true 인 경우 아래 내용 작성 + Long teamId, + String teamName, + String teamProfileImageUrl ) { } @@ -89,32 +98,6 @@ public static class UpdatePostResultDTO { LocalDateTime updatedAt; } - - @Builder - @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) - @JsonInclude(JsonInclude.Include.NON_NULL) - public record PostPreviewDTO ( // Post 조회 - Long postId, - Long memberId, - String authorNickname, - String authorProfileImageUrl, - Long teamId, - String teamProfileImageUrl, - String teamBannerImageUrl, - Long projectId, - String postTitle, - String postBody, - String thumbnailImageUrl, - Boolean postStatus, - String postCategory, - Set coauthorIds, - PostAccess postAccess, - PostFileResponseDTO.PostFileListDTO postFileList, - LocalDateTime createdAt, - LocalDateTime updatedAt - ){ - } - @Builder @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @JsonInclude(JsonInclude.Include.NON_NULL) @@ -327,8 +310,8 @@ public record MemberPostInTeamPreviewListDTO( public record PostAdjacentDTO( Boolean hasLater, Boolean hadOlder, - PostAdjacentPreviewDTO laterPost, - PostAdjacentPreviewDTO olderPost + PostPreviewDTO laterPost, + PostPreviewDTO olderPost ) { @Builder @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) diff --git a/src/main/java/com/codiary/backend/domain/post/repository/PostRepository.java b/src/main/java/com/codiary/backend/domain/post/repository/PostRepository.java index 7676e62..c807dae 100644 --- a/src/main/java/com/codiary/backend/domain/post/repository/PostRepository.java +++ b/src/main/java/com/codiary/backend/domain/post/repository/PostRepository.java @@ -14,8 +14,6 @@ public interface PostRepository extends JpaRepository, PostRepositoryCustom { - Page findAllByPostTitleContainingIgnoreCaseOrderByCreatedAtDesc(String postTitle, Pageable pageable); - Page findAllByOrderByCreatedAtDesc(Pageable pageable); Page findByMemberOrderByCreatedAtDescPostIdDesc(Member member, Pageable pageable); Page findByTeamOrderByCreatedAtDescPostIdDesc(Team team, Pageable pageable); Page findByProjectAndMemberOrderByCreatedAtDescPostIdDesc(Project project, Member member, Pageable pageable); @@ -25,15 +23,6 @@ public interface PostRepository extends JpaRepository, PostRepositor Page findByTeamAndMemberOrderByCreatedAtDescPostIdDesc(Team team, Member member, Pageable pageable); Page findByTeamAndAuthorList_MemberOrderByCreatedAtDescPostIdDesc(Team team, Member member, Pageable pageable); - Optional findTopByMemberAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Member member, Long postId); - - Optional findTopByMemberAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Member member, Long postId); - - Optional findTopByTeamAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Team team, Long postId); - - Optional findTopByTeamAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Team team, Long postId); - - boolean existsByTeam(Team team); boolean existsByProject(Project project); boolean existsByMember(Member member); diff --git a/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryCustom.java b/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryCustom.java index ad0eb15..021ca93 100644 --- a/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryCustom.java +++ b/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryCustom.java @@ -3,9 +3,11 @@ import com.codiary.backend.domain.member.entity.Member; import com.codiary.backend.domain.post.entity.Post; import com.codiary.backend.domain.project.entity.Project; +import com.codiary.backend.domain.team.entity.Team; import java.time.LocalDate; import java.util.List; import java.util.Map; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -25,4 +27,18 @@ public interface PostRepositoryCustom { Page findByBookmarkPostList(Member member, Pageable pageable); Page getPostsByName(Long memberId, String authorName, String teamName, String projectName, Pageable pageable); + + Optional findByIdWithTeam(Long postId, Long requesterId); + + Page findAllByPostTitleContainingIgnoreCaseOrderByCreatedAtDesc(String postTitle, Pageable pageable); + + Page findAllByOrderByCreatedAtDesc(Pageable pageable); + + Optional findTopByTeamAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Team team, Long postId); + + Optional findTopByTeamAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Team team, Long postId); + + Optional findTopByMemberAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Member member, Long postId); + + Optional findTopByMemberAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Member member, Long postId); } diff --git a/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryImpl.java b/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryImpl.java index c88f63c..f9e47d2 100644 --- a/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryImpl.java +++ b/src/main/java/com/codiary/backend/domain/post/repository/PostRepositoryImpl.java @@ -14,6 +14,7 @@ import com.codiary.backend.domain.post.entity.Post; import com.codiary.backend.domain.post.enumerate.PostAccess; import com.codiary.backend.domain.project.entity.Project; +import com.codiary.backend.domain.team.entity.Team; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; @@ -25,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -171,6 +173,7 @@ public Page getPostsByCategoryId(Long memberId, Long categoryId, Pageable List posts = queryFactory .selectDistinct(post) .from(post) + .leftJoin(post.team, team).fetchJoin() .where(post.categoriesList.any().categoryId.eq(categoryId).and(canAccess(memberId))) .orderBy(getOrderBy(pageable.getSort())) .offset(pageable.getOffset()) @@ -244,6 +247,7 @@ public Page findByBookmarkPostList(Member member, Pageable pageable) { .distinct() .from(post) .leftJoin(post.bookmarkList, bookmark).fetchJoin() + .leftJoin(post.team, team).fetchJoin() .where(bookmark.member.memberId.eq(member.getMemberId()) .and(post.deletedAt.isNull())) .offset(pageable.getOffset()) @@ -291,18 +295,10 @@ public Page getPostsByName( Long total = queryFactory .select(post.countDistinct()) .from(post) - // member join - .leftJoin(post.member, member) - // team join - .leftJoin(post.team, team) - // project join - .leftJoin(post.project, project) // 조건 탐색 .where( canAccess(memberId).and(booleanBuilder) ) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) .fetchOne(); return new PageImpl<>(posts, pageable, total); @@ -318,4 +314,115 @@ private BooleanBuilder searchBy(String authorName, String teamName, String proje } return new BooleanBuilder(); } + + @Override + public Optional findByIdWithTeam(Long postId, Long requesterId) { + Optional fetchedPost = Optional.ofNullable(queryFactory + .select(post) + .from(post) + .leftJoin(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.postId.eq(postId).and(canAccess(requesterId))) + .fetchFirst() + ); + return fetchedPost; + } + + @Override + public Page findAllByPostTitleContainingIgnoreCaseOrderByCreatedAtDesc(String postTitle, Pageable pageable) { + List posts = queryFactory + .select(post) + .from(post) + .leftJoin(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.postTitle.containsIgnoreCase(postTitle)) + .orderBy(post.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = queryFactory + .select(post.countDistinct()) + .from(post) + .where(post.postTitle.containsIgnoreCase(postTitle)) + .fetchOne(); + + return new PageImpl<>(posts, pageable, total); + } + + @Override + public Page findAllByOrderByCreatedAtDesc(Pageable pageable) { + List posts = queryFactory + .select(post) + .from(post) + .leftJoin(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .orderBy(post.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = queryFactory + .select(post.countDistinct()) + .from(post) + .fetchOne(); + + return new PageImpl<>(posts, pageable, total); + } + + @Override + public Optional findTopByTeamAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Team findTeam, Long postId) { + Optional fetchedPost = Optional.ofNullable(queryFactory + .select(post) + .from(post) + .join(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.team.eq(findTeam).and(post.postId.lt(postId))) + .orderBy(post.createdAt.desc(), post.postId.desc()) + .fetchFirst()); + + return fetchedPost; + } + + @Override + public Optional findTopByTeamAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Team findTeam, Long postId) { + Optional fetchedPost = Optional.ofNullable(queryFactory + .select(post) + .from(post) + .join(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.team.eq(findTeam).and(post.postId.gt(postId))) + .orderBy(post.createdAt.desc(), post.postId.desc()) + .fetchFirst()); + + return fetchedPost; + } + + @Override + public Optional findTopByMemberAndPostIdLessThanOrderByCreatedAtDescPostIdDesc(Member member, Long postId) { + Optional fetchedPost = Optional.ofNullable(queryFactory + .select(post) + .from(post) + .leftJoin(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.member.eq(member).and(post.postId.lt(postId))) + .orderBy(post.createdAt.desc(), post.postId.desc()) + .fetchFirst()); + + return fetchedPost; + } + + @Override + public Optional findTopByMemberAndPostIdGreaterThanOrderByCreatedAtAscPostIdAsc(Member member, Long postId) { + Optional fetchedPost = Optional.ofNullable(queryFactory + .select(post) + .from(post) + .leftJoin(post.team, team).fetchJoin() + .leftJoin(team.profileImage, teamProfileImage).fetchJoin() + .where(post.member.eq(member).and(post.postId.gt(postId))) + .orderBy(post.createdAt.desc(), post.postId.desc()) + .fetchFirst()); + + return fetchedPost; + } } diff --git a/src/main/java/com/codiary/backend/domain/post/service/PostQueryService.java b/src/main/java/com/codiary/backend/domain/post/service/PostQueryService.java index 0e17f24..e7e442f 100644 --- a/src/main/java/com/codiary/backend/domain/post/service/PostQueryService.java +++ b/src/main/java/com/codiary/backend/domain/post/service/PostQueryService.java @@ -12,9 +12,11 @@ import com.codiary.backend.global.apiPayload.code.status.ErrorStatus; import com.codiary.backend.global.apiPayload.exception.GeneralException; import com.codiary.backend.global.apiPayload.exception.handler.PostHandler; - -import java.util.*; - +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -59,11 +61,9 @@ private Member getAuthenticatedMember() { } - public Post findById(Long postId) { - Post post = postRepository.findById(postId) + public Post findById(Long postId, Long requesterId) { + Post post = postRepository.findByIdWithTeam(postId, requesterId) .orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND)); - Member member = getAuthenticatedMember(); - validatePostAccess(post, member); return post; }