From e4ef3c93e0b66b34a69b46cddc81cb92ae6cea1f Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Mon, 13 Jan 2025 20:48:22 +0900 Subject: [PATCH 1/6] =?UTF-8?q?#293=20Feat:=20post=20=EC=B6=95=EC=95=BD?= =?UTF-8?q?=EB=90=98=EC=96=B4=20=EB=82=98=EC=98=A4=EB=8A=94=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/alert/service/AlertService.java | 6 +- .../post/controller/PostController.java | 13 +- .../domain/post/converter/PostConverter.java | 117 +++--------------- .../post/dto/response/PostResponseDTO.java | 51 +++----- 4 files changed, 45 insertions(+), 142 deletions(-) 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 8029d81..72aabe2 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 @@ -19,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; @@ -185,7 +184,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); @@ -195,7 +194,7 @@ 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 @@ -220,7 +219,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 +248,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, @@ -300,8 +299,8 @@ public ApiResponse findPostByFollowing(@Auth @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..d860df4 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,29 @@ 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().getFileUrl()) .createdAt(post.getCreatedAt()) .updatedAt(post.getUpdatedAt()) + + .authorId(post.getMember().getMemberId()) + .authorName(post.getMember().getNickname()) + .authorImageUrl(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().getImageUrl() : null) .build(); } @@ -118,45 +119,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 +135,6 @@ public static PostResponseDTO.PostPreviewListDTO toPostPreviewListDTO(Page .build(); } - - // 저자별 Post 조회 public static PostResponseDTO.MemberPostPreviewDTO toMemberPostPreviewDTO(Post post) { List postCategories = post.getCategoriesList().stream() @@ -445,52 +405,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) From 99a7b80153258bb8b566b48f21e3a55751a0a648 Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Tue, 14 Jan 2025 01:00:46 +0900 Subject: [PATCH 2/6] #293 Feat: integrate preview response --- .../post/controller/PostController.java | 24 ++++++++++------ .../domain/post/converter/PostConverter.java | 8 ++++-- .../post/repository/PostRepositoryCustom.java | 3 ++ .../post/repository/PostRepositoryImpl.java | 28 +++++++++++++------ .../domain/post/service/PostQueryService.java | 14 +++++----- 5 files changed, 49 insertions(+), 28 deletions(-) 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 db8bab2..33d4fa0 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; @@ -102,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)); } @@ -200,7 +202,7 @@ public ApiResponse> getCategoryPopularPosts @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)); } @@ -291,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 d860df4..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 @@ -24,20 +24,22 @@ public static PostResponseDTO.PostPreviewDTO toPostPreviewDTO(Post post) { .id(post.getPostId()) .title(post.getPostTitle()) .body(post.getPostBody()) - .thumbnailImageUrl(post.getThumbnailImage().getFileUrl()) + .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().getImageUrl()) + .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().getImageUrl() : null) + .teamProfileImageUrl((post.getTeam() != null && post.getTeam().getProfileImage() != null) + ? post.getTeam().getProfileImage().getImageUrl() + : null) .build(); } 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..05e9576 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 @@ -6,6 +6,7 @@ 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 +26,6 @@ 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); } 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 dd5db6e..600e6b8 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 @@ -3,9 +3,11 @@ import static com.codiary.backend.domain.member.entity.QFollow.follow; import static com.codiary.backend.domain.member.entity.QMember.member; import static com.codiary.backend.domain.member.entity.QMemberImage.memberImage; +import static com.codiary.backend.domain.post.entity.QBookmark.bookmark; import static com.codiary.backend.domain.post.entity.QPost.post; import static com.codiary.backend.domain.project.entity.QProject.project; import static com.codiary.backend.domain.team.entity.QTeam.team; +import static com.codiary.backend.domain.team.entity.QTeamFollow.teamFollow; import static com.codiary.backend.domain.team.entity.QTeamProfileImage.teamProfileImage; import com.codiary.backend.domain.member.entity.Member; @@ -23,6 +25,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; @@ -30,14 +33,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import static com.codiary.backend.domain.post.entity.QBookmark.bookmark; -import static com.codiary.backend.domain.member.entity.QFollow.follow; -import static com.codiary.backend.domain.member.entity.QMember.member; -import static com.codiary.backend.domain.post.entity.QPost.post; -import static com.codiary.backend.domain.project.entity.QProject.project; -import static com.codiary.backend.domain.team.entity.QTeam.team; -import static com.codiary.backend.domain.team.entity.QTeamFollow.teamFollow; - @RequiredArgsConstructor public class PostRepositoryImpl implements PostRepositoryCustom { private final JPAQueryFactory queryFactory; @@ -126,7 +121,7 @@ public Page getPostList(Pageable pageable) { List posts = queryFactory .selectDistinct(post) .from(post) - .join(post.team, team).fetchJoin() + .leftJoin(post.team, team).fetchJoin() .where(post.postAccess.eq(PostAccess.ENTIRE)) .orderBy(orderSpecifiers) .offset(pageable.getOffset()) @@ -177,6 +172,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()) @@ -250,6 +246,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()) @@ -324,4 +321,17 @@ 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; + } } 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; } From e497cfddc157218e21ea05eccb28cf2a3b7993e2 Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Sat, 18 Jan 2025 22:10:47 +0900 Subject: [PATCH 3/6] =?UTF-8?q?#293=20Feat:=20=EC=95=9E=EB=92=A4=20?= =?UTF-8?q?=ED=8F=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=ED=94=84?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/repository/PostRepository.java | 11 -- .../post/repository/PostRepositoryCustom.java | 13 +++ .../post/repository/PostRepositoryImpl.java | 107 ++++++++++++++++-- 3 files changed, 112 insertions(+), 19 deletions(-) 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 05e9576..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,6 +3,7 @@ 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; @@ -28,4 +29,16 @@ public interface PostRepositoryCustom { 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 600e6b8..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; @@ -294,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); @@ -334,4 +327,102 @@ public Optional findByIdWithTeam(Long postId, Long requesterId) { ); 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; + } } From ae4d581678035f9fec87e1136fe67eb347a4d282 Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Sun, 9 Feb 2025 17:54:30 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codiary/backend/domain/post/controller/PostController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 159292d..096e415 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 @@ -107,7 +107,7 @@ public ApiResponse findPost( @PathVariable Long postId, @AuthenticationPrincipal CustomMemberDetails memberDetails) { Long memberId = memberDetails.getId(); - Post findPost = postQueryService.findById(postId); + Post findPost = postQueryService.findById(postId, memberId); List bookmarkedPostIds = bookmarkService.getBookmarkedPostIdsByMemberId(memberId); boolean isBookmarked = bookmarkedPostIds.contains(postId); int bookmarkCount = bookmarkService.getBookmarkCountByPostId(postId); From 2a85eb4d113a0d2a1719ab301312ac06ff2532ae Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Sun, 9 Feb 2025 17:55:06 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/converter/PostConverter.java | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) 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 f9c0215..2af8c09 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 @@ -468,36 +468,25 @@ public static PostResponseDTO.PostPreviewDTO toPostPreviewDTOWithBookmark(Post p .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())) + .id(post.getPostId()) + .title(post.getPostTitle()) + .body(post.getPostBody()) + .thumbnailImageUrl((post.getThumbnailImage() != null) ? post.getThumbnailImage().getFileUrl() : "") .createdAt(post.getCreatedAt()) .updatedAt(post.getUpdatedAt()) - .isBookmarked(isBookmarked) - .bookmarkCount(bookmarkCount) + + .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(); } From c22171dc1f5554a609135b9e7fe98006900eb6d3 Mon Sep 17 00:00:00 2001 From: lee-haeseung Date: Sun, 9 Feb 2025 18:07:11 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostController.java | 4 +- .../domain/post/converter/PostConverter.java | 39 +++++++++++++++++++ .../post/dto/response/PostResponseDTO.java | 27 +++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) 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 096e415..99cce4b 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 @@ -103,7 +103,7 @@ public ApiResponse deletePost(@PathVariable Long postId) { // 특정 게시글 조회 (북마크 여부 포함) @GetMapping("/{postId}") @Operation(summary = "특정 게시글 조회 API", description = "특정 게시글을 조회하며, 사용자가 해당 게시글을 북마크했는지 여부를 반환합니다.") - public ApiResponse findPost( + public ApiResponse findPost( @PathVariable Long postId, @AuthenticationPrincipal CustomMemberDetails memberDetails) { Long memberId = memberDetails.getId(); @@ -111,7 +111,7 @@ public ApiResponse findPost( List bookmarkedPostIds = bookmarkService.getBookmarkedPostIdsByMemberId(memberId); boolean isBookmarked = bookmarkedPostIds.contains(postId); int bookmarkCount = bookmarkService.getBookmarkCountByPostId(postId); - return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostPreviewDTOWithBookmark(findPost, isBookmarked, bookmarkCount)); + return ApiResponse.onSuccess(SuccessStatus.POST_OK, PostConverter.toPostWithBookmarkDTO(findPost, isBookmarked, bookmarkCount)); } 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 2af8c09..8fb4ea4 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 @@ -490,4 +490,43 @@ public static PostResponseDTO.PostPreviewDTO toPostPreviewDTOWithBookmark(Post p .build(); } + + public static PostResponseDTO.PostWithBookmarkDTO toPostWithBookmarkDTO(Post post, boolean isBookmarked, int bookmarkCount) { + List postCategories = post.getCategoriesList().stream() + .map(Category::getName) + .collect(Collectors.toList()); + + return PostResponseDTO.PostWithBookmarkDTO.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()) + .isBookmarked(isBookmarked) + .bookmarkCount(bookmarkCount) + .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 10a5582..ae8c88f 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 @@ -348,4 +348,31 @@ public record BookmarkDTO( Long postId ) { } + + @Builder + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record PostWithBookmarkDTO( + 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, + Boolean isBookmarked, + Integer bookmarkCount + ) { + } }