Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NB-48, NB-155] 메인 화면에서의 게시글 조회 구현 #31

Merged
merged 4 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions src/main/java/com/soyeon/nubim/domain/comment/CommentMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,15 @@
import com.soyeon.nubim.domain.comment.dto.CommentCreateResponseDto;
import com.soyeon.nubim.domain.comment.dto.CommentResponseDto;
import com.soyeon.nubim.domain.post.Post;
import com.soyeon.nubim.domain.post.PostService;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserService;
import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class CommentMapper {
private final PostService postService;
private final UserService userService;

public Comment toEntity(CommentCreateRequestDto commentCreateRequestDto, User user) {
Post post = postService.findPostByIdOrThrow(commentCreateRequestDto.getPostId());

public Comment toEntity(CommentCreateRequestDto commentCreateRequestDto, User user, Post post) {
return Comment.builder()
.user(user)
.post(post)
Expand All @@ -39,10 +33,10 @@ public CommentCreateResponseDto toCommentCreateResponseDto(Comment comment) {
.build();
}

public CommentResponseDto toCommentResponseDto(Comment comment) {
public CommentResponseDto toCommentResponseDto(Comment comment, UserSimpleResponseDto userSimpleResponseDto) {
return CommentResponseDto.builder()
.commentId(comment.getCommentId())
.userId(comment.getUser().getUserId())
.user(userSimpleResponseDto)
.postId(comment.getPost().getPostId())
.parentCommentId(null) // TODO : 대댓글 구현 시 수정
.content(comment.getCommentContent())
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/soyeon/nubim/domain/comment/CommentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@
import com.soyeon.nubim.domain.comment.dto.CommentCreateRequestDto;
import com.soyeon.nubim.domain.comment.dto.CommentCreateResponseDto;
import com.soyeon.nubim.domain.comment.dto.CommentResponseDto;
import com.soyeon.nubim.domain.post.Post;
import com.soyeon.nubim.domain.post.PostService;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserMapper;
import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class CommentService {
private final CommentRepository commentRepository;
private final PostService postService;
private final CommentMapper commentMapper;
private final UserMapper userMapper;

public CommentCreateResponseDto createComment(CommentCreateRequestDto commentCreateRequestDto, User user) {
Comment comment = commentMapper.toEntity(commentCreateRequestDto, user);
Post post = postService.findPostByIdOrThrow(commentCreateRequestDto.getPostId());
Comment comment = commentMapper.toEntity(commentCreateRequestDto, user, post);
commentRepository.save(comment); // TODO : 글자 수 검증 필요

return commentMapper.toCommentCreateResponseDto(comment);
Expand All @@ -27,6 +34,9 @@ public CommentCreateResponseDto createComment(CommentCreateRequestDto commentCre
public Page<CommentResponseDto> findCommentsByPostIdAndPageable(Long postId, Pageable pageable) {
Page<Comment> commentList = commentRepository.findByPostPostId(postId, pageable);

return commentList.map(commentMapper::toCommentResponseDto);
return commentList.map(comment -> {
UserSimpleResponseDto userSimpleResponseDto = userMapper.toUserSimpleResponseDto(comment.getUser());
return commentMapper.toCommentResponseDto(comment, userSimpleResponseDto);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.soyeon.nubim.domain.comment.dto;

import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -9,7 +11,7 @@
@Builder
public class CommentResponseDto {
private Long commentId;
private Long userId;
private UserSimpleResponseDto user;
private Long postId;
private Long parentCommentId;
private String content;
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/soyeon/nubim/domain/post/CustomPageImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.soyeon.nubim.domain.post;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;

import lombok.Getter;
import lombok.Setter;

/**
* 랜덤 시드값 전송을 위한 커스텀 클래스
*/
@Setter
@Getter
public class CustomPageImpl<T> extends PageImpl<T> {
private Float randomSeed;

public CustomPageImpl(Page<T> page) {
super(page.getContent(), page.getPageable(), page.getTotalElements());
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/soyeon/nubim/domain/post/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -56,5 +57,6 @@ public class Post extends BaseEntity {

@Builder.Default
@OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
@OrderBy("createdAt DESC")
private List<Comment> comments = new ArrayList<>();
}
32 changes: 29 additions & 3 deletions src/main/java/com/soyeon/nubim/domain/post/PostControllerV1.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

import com.soyeon.nubim.domain.post.dto.PostCreateRequestDto;
import com.soyeon.nubim.domain.post.dto.PostCreateResponseDto;
import com.soyeon.nubim.domain.post.dto.PostMainResponseDto;
import com.soyeon.nubim.domain.post.dto.PostSimpleResponseDto;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

Expand All @@ -35,8 +37,10 @@ public class PostControllerV1 {
private final PostService postService;
private final UserService userService;

private static final int DEFAULT_PAGE_SIZE = 10;
private static final int DEFAULT_SIMPLE_PAGE_SIZE = 10;
private static final int DEFAULT_MAIN_PAGE_SIZE = 5;
private static final String DEFAULT_ORDER_BY = "createdAt";
private static final int DEFAULT_RECENT_CRITERIA_DAYS = 3;

@PostMapping
public ResponseEntity<PostCreateResponseDto> createPost(
Expand Down Expand Up @@ -73,10 +77,10 @@ public ResponseEntity<Page<PostSimpleResponseDto>> getPostsByUserId(

PageRequest pageRequest;
if (sort.equals("desc")) {
pageRequest = PageRequest.of(page.intValue(), DEFAULT_PAGE_SIZE,
pageRequest = PageRequest.of(page.intValue(), DEFAULT_SIMPLE_PAGE_SIZE,
Sort.by(Sort.Direction.DESC, DEFAULT_ORDER_BY));
} else if (sort.equals("asc")) {
pageRequest = PageRequest.of(page.intValue(), DEFAULT_PAGE_SIZE,
pageRequest = PageRequest.of(page.intValue(), DEFAULT_SIMPLE_PAGE_SIZE,
Sort.by(Sort.Direction.ASC, DEFAULT_ORDER_BY));
} else {
return ResponseEntity.badRequest().build();
Expand All @@ -94,4 +98,26 @@ public ResponseEntity<Void> deletePost(@PathVariable Long postId) {
return ResponseEntity.ok().build();
}

@Operation(description = "메인 화면에서 노출되는 게시글 조회")
@GetMapping("/main-posts")
public ResponseEntity<Page<PostMainResponseDto>> getMainPosts(
@RequestParam(defaultValue = "0") Long page,
@RequestParam(defaultValue = "follow") @Parameter(description = "[ follow, random ]") String type,
@RequestParam(required = false) Float randomSeed) {
User user = userService.getCurrentUser();

if (type.equals("follow")) { // 팔로우 기반 게시글 조회
PageRequest pageRequest = PageRequest.of(
page.intValue(), DEFAULT_MAIN_PAGE_SIZE, Sort.by(Sort.Direction.DESC, DEFAULT_ORDER_BY));

return ResponseEntity.ok(
postService.findRecentPostsOfFollowees(user, pageRequest, DEFAULT_RECENT_CRITERIA_DAYS));
} else if (type.equals("random")) { // 랜덤 추천 게시글 조회
PageRequest pageRequest = PageRequest.of(page.intValue(), DEFAULT_MAIN_PAGE_SIZE);

return ResponseEntity.ok(postService.findRandomPosts(pageRequest, randomSeed, user));
} else {
return ResponseEntity.badRequest().build();
}
}
}
42 changes: 32 additions & 10 deletions src/main/java/com/soyeon/nubim/domain/post/PostMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,22 @@
import org.springframework.stereotype.Component;

import com.soyeon.nubim.domain.album.Album;
import com.soyeon.nubim.domain.album.AlbumNotFoundException;
import com.soyeon.nubim.domain.album.AlbumService;
import com.soyeon.nubim.domain.comment.Comment;
import com.soyeon.nubim.domain.comment.dto.CommentResponseDto;
import com.soyeon.nubim.domain.post.dto.PostCreateRequestDto;
import com.soyeon.nubim.domain.post.dto.PostCreateResponseDto;
import com.soyeon.nubim.domain.post.dto.PostDetailResponseDto;
import com.soyeon.nubim.domain.post.dto.PostMainResponseDto;
import com.soyeon.nubim.domain.post.dto.PostSimpleResponseDto;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserService;
import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class PostMapper {
private final UserService userService;
private final AlbumService albumService;

public Post toEntity(PostCreateRequestDto postCreateRequestDto, User authorUser) {
Album linkedAlbum = albumService
.findById(postCreateRequestDto.getAlbumId())
.orElseThrow(() -> new AlbumNotFoundException(postCreateRequestDto.getAlbumId()));
public Post toEntity(PostCreateRequestDto postCreateRequestDto, User authorUser, Album linkedAlbum) {

return Post.builder()
.postTitle(postCreateRequestDto.getPostTitle())
Expand Down Expand Up @@ -63,13 +57,41 @@ public PostDetailResponseDto toPostDetailResponseDto(Post post) {
}

public PostSimpleResponseDto toPostSimpleResponseDto(Post post) {
UserSimpleResponseDto userSimpleResponseDto = UserSimpleResponseDto.builder()
.userId(post.getUser().getUserId())
.profileImageUrl(post.getUser().getProfileImageUrl())
.nickname(post.getUser().getNickname())
.build();

return PostSimpleResponseDto.builder()
.postId(post.getPostId())
.postTitle(post.getPostTitle())
.postContent(post.getPostContent())
.numberOfComments((long)post.getComments().size())
.user(userSimpleResponseDto)
.albumId(post.getAlbum().getAlbumId())
.createdAt(post.getCreatedAt())
.updatedAt(post.getUpdatedAt())
.build();
}

public PostMainResponseDto toPostMainResponseDto(Post post, CommentResponseDto representativeComment) {
UserSimpleResponseDto userSimpleResponseDto = UserSimpleResponseDto.builder()
.userId(post.getUser().getUserId())
.profileImageUrl(post.getUser().getProfileImageUrl())
.nickname(post.getUser().getNickname())
.build();

return PostMainResponseDto.builder()
.postId(post.getPostId())
.postTitle(post.getPostTitle())
.postContent(post.getPostContent())
.numberOfComments((long)post.getComments().size())
.representativeComment(representativeComment)
.user(userSimpleResponseDto)
.albumId(post.getAlbum().getAlbumId())
.createdAt(post.getCreatedAt())
.updatedAt(post.getUpdatedAt())
.build();
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/soyeon/nubim/domain/post/PostRepository.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
package com.soyeon.nubim.domain.post;

import java.time.LocalDateTime;
import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.soyeon.nubim.domain.user.User;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByUserUserId(Long userId);

Page<Post> findByUserUserId(Long postPostId, Pageable pageable);

@Query("SELECT p FROM Post p WHERE p.user IN " +
"(SELECT uf.followee FROM UserFollow uf WHERE uf.follower = :user) " +
"AND p.createdAt >= :criteriaDays AND p.isDeleted = false")
Page<Post> findRecentPostsByFollowees(User user, LocalDateTime criteriaDays, Pageable pageable);

@Query(value = "SELECT SETSEED(:seed)", nativeQuery = true)
void setSeed(Float seed);

@Query(value = "SELECT p FROM Post p WHERE p.user != :user ORDER BY function('RANDOM')")
Page<Post> findRandomPostsExceptMine(Pageable pageable, User user);
}
60 changes: 59 additions & 1 deletion src/main/java/com/soyeon/nubim/domain/post/PostService.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
package com.soyeon.nubim.domain.post;

import java.time.LocalDateTime;
import java.util.Random;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.soyeon.nubim.domain.album.Album;
import com.soyeon.nubim.domain.album.AlbumNotFoundException;
import com.soyeon.nubim.domain.album.AlbumService;
import com.soyeon.nubim.domain.comment.Comment;
import com.soyeon.nubim.domain.comment.CommentMapper;
import com.soyeon.nubim.domain.comment.dto.CommentResponseDto;
import com.soyeon.nubim.domain.post.dto.PostCreateRequestDto;
import com.soyeon.nubim.domain.post.dto.PostCreateResponseDto;
import com.soyeon.nubim.domain.post.dto.PostDetailResponseDto;
import com.soyeon.nubim.domain.post.dto.PostMainResponseDto;
import com.soyeon.nubim.domain.post.dto.PostSimpleResponseDto;
import com.soyeon.nubim.domain.post.exceptions.PostNotFoundException;
import com.soyeon.nubim.domain.post.exceptions.UnauthorizedAccessException;
import com.soyeon.nubim.domain.user.User;
import com.soyeon.nubim.domain.user.UserMapper;
import com.soyeon.nubim.domain.user.dto.UserSimpleResponseDto;

import lombok.RequiredArgsConstructor;

Expand All @@ -19,6 +31,11 @@
public class PostService {
private final PostRepository postRepository;
private final PostMapper postMapper;
private final AlbumService albumService;

private static final Random random = new Random();
private final CommentMapper commentMapper;
private final UserMapper userMapper;

public PostDetailResponseDto findPostDetailById(Long id) {
Post post = postRepository.findById(id).orElseThrow(() -> new PostNotFoundException(id));
Expand All @@ -39,7 +56,10 @@ public Page<PostSimpleResponseDto> findAllPostsByUserIdOrderByCreatedAt(Long use
}

public PostCreateResponseDto createPost(PostCreateRequestDto postCreateRequestDto, User authorUser) {
Post post = postMapper.toEntity(postCreateRequestDto, authorUser);
Album linkedAlbum = albumService
.findById(postCreateRequestDto.getAlbumId())
.orElseThrow(() -> new AlbumNotFoundException(postCreateRequestDto.getAlbumId()));
Post post = postMapper.toEntity(postCreateRequestDto, authorUser, linkedAlbum);
postRepository.save(post);
return postMapper.toPostCreateResponseDto(post);
}
Expand Down Expand Up @@ -68,4 +88,42 @@ public void validatePostOwner(Long postId, User author) {
throw new UnauthorizedAccessException(postId);
}
}

public Page<PostMainResponseDto> findRecentPostsOfFollowees(
User user, Pageable pageable, int recentCriteriaDays) {
return postRepository.findRecentPostsByFollowees(user, LocalDateTime.now().minusDays(recentCriteriaDays),
pageable)
.map(post -> postMapper.toPostMainResponseDto(post, findRecentCommentByPostOrNull(post)));
}

public Page<PostMainResponseDto> findRandomPosts(Pageable pageable, Float randomSeed, User user) {
float seed = getOrGenerateRandomSeed(randomSeed);
postRepository.setSeed(seed);

CustomPageImpl<PostMainResponseDto> customPage = new CustomPageImpl<>(
postRepository.findRandomPostsExceptMine(pageable, user)
.map(post -> postMapper.toPostMainResponseDto(post, findRecentCommentByPostOrNull(post)))
);
customPage.setRandomSeed(seed);

return customPage;
}

private Float getOrGenerateRandomSeed(Float seed) {
if (seed == null) {
return random.nextFloat();
}
return seed;
}

private CommentResponseDto findRecentCommentByPostOrNull(Post post) {
Comment lastCommentByPost = post.getComments().stream().findFirst().orElse(null);
if (lastCommentByPost == null) {
return null;
} else {
UserSimpleResponseDto userSimpleResponseDto = userMapper.toUserSimpleResponseDto(
lastCommentByPost.getUser());
return commentMapper.toCommentResponseDto(lastCommentByPost, userSimpleResponseDto);
}
}
}
Loading