diff --git a/src/main/java/com/example/feeda/domain/comment/repository/CommentRepository.java b/src/main/java/com/example/feeda/domain/comment/repository/CommentRepository.java index 5ef9a6b..61835b4 100644 --- a/src/main/java/com/example/feeda/domain/comment/repository/CommentRepository.java +++ b/src/main/java/com/example/feeda/domain/comment/repository/CommentRepository.java @@ -1,13 +1,15 @@ package com.example.feeda.domain.comment.repository; import com.example.feeda.domain.comment.entity.Comment; -import org.springframework.data.jpa.repository.JpaRepository; - +import com.example.feeda.domain.post.entity.Post; import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; public interface CommentRepository extends JpaRepository { List findByPostIdOrderByCreatedAtDesc(Long postId); // 최신순 List findByPostIdOrderByCreatedAtAsc(Long postId); + + Long countByPost(Post findPost); } diff --git a/src/main/java/com/example/feeda/domain/post/controller/PostController.java b/src/main/java/com/example/feeda/domain/post/controller/PostController.java index 3ad4249..3a2943e 100644 --- a/src/main/java/com/example/feeda/domain/post/controller/PostController.java +++ b/src/main/java/com/example/feeda/domain/post/controller/PostController.java @@ -7,11 +7,13 @@ import com.example.feeda.security.jwt.JwtPayload; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -46,8 +48,8 @@ public ResponseEntity createPost(@RequestBody PostRequestDto re @PostMapping("/{id}/likes") public ResponseEntity makeLikes( - @PathVariable Long id, - @AuthenticationPrincipal JwtPayload jwtPayload) { + @PathVariable Long id, + @AuthenticationPrincipal JwtPayload jwtPayload) { Long profileId = jwtPayload.getProfileId(); return new ResponseEntity<>(postService.makeLikes(id, jwtPayload), HttpStatus.OK); @@ -55,8 +57,8 @@ public ResponseEntity makeLikes( @DeleteMapping("/{id}/likes") public ResponseEntity deleteLikes( - @PathVariable Long id, - @AuthenticationPrincipal JwtPayload jwtPayload + @PathVariable Long id, + @AuthenticationPrincipal JwtPayload jwtPayload ) { Long profileId = jwtPayload.getProfileId(); postService.deleteLikes(id, profileId); @@ -73,12 +75,15 @@ public ResponseEntity findPostById(@PathVariable @NotNull Long public ResponseEntity> findAllPost( @RequestParam(defaultValue = "1") @Min(1) int page, @RequestParam(defaultValue = "10") @Min(1) int size, - @RequestParam(defaultValue = "") String keyword + @RequestParam(defaultValue = "") String keyword, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startUpdatedAt, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endUpdatedAt ) { Pageable pageable = PageRequest.of(page - 1, size, Sort.Direction.DESC, "updatedAt"); - return new ResponseEntity<>(postService.findAll(pageable, keyword), HttpStatus.OK); + return new ResponseEntity<>( + postService.findAll(pageable, keyword, startUpdatedAt, endUpdatedAt), HttpStatus.OK); } @GetMapping("/followings") diff --git a/src/main/java/com/example/feeda/domain/post/dto/PostResponseDto.java b/src/main/java/com/example/feeda/domain/post/dto/PostResponseDto.java index 7bfa9fb..89e181d 100644 --- a/src/main/java/com/example/feeda/domain/post/dto/PostResponseDto.java +++ b/src/main/java/com/example/feeda/domain/post/dto/PostResponseDto.java @@ -1,13 +1,8 @@ package com.example.feeda.domain.post.dto; import com.example.feeda.domain.post.entity.Post; -import jakarta.persistence.Column; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import lombok.Getter; - import java.time.LocalDateTime; +import lombok.Getter; @Getter public class PostResponseDto { @@ -22,21 +17,24 @@ public class PostResponseDto { private final Long likes; + private final Long comments; + private final LocalDateTime createdAt; private final LocalDateTime updatedAt; - public PostResponseDto(Post post, Long likes) { + public PostResponseDto(Post post, Long likes, Long comments) { this.id = post.getId(); this.title = post.getTitle(); this.content = post.getContent(); this.category = post.getCategory(); this.likes = likes; + this.comments = comments; this.createdAt = post.getCreatedAt(); this.updatedAt = post.getUpdatedAt(); } - public static PostResponseDto toDto(Post post, Long likes) { - return new PostResponseDto(post, likes); + public static PostResponseDto toDto(Post post, Long likes, Long comments) { + return new PostResponseDto(post, likes, comments); } } diff --git a/src/main/java/com/example/feeda/domain/post/repository/PostRepository.java b/src/main/java/com/example/feeda/domain/post/repository/PostRepository.java index 4d0ec21..ff944dc 100644 --- a/src/main/java/com/example/feeda/domain/post/repository/PostRepository.java +++ b/src/main/java/com/example/feeda/domain/post/repository/PostRepository.java @@ -1,6 +1,7 @@ package com.example.feeda.domain.post.repository; import com.example.feeda.domain.post.entity.Post; +import java.time.LocalDateTime; import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -11,4 +12,8 @@ public interface PostRepository extends JpaRepository { Page findAllByTitleContaining(String title, Pageable pageable); Page findAllByProfile_IdIn(List followingProfileIds, Pageable pageable); + + Page findAllByTitleContainingAndUpdatedAtBetween(String title, + LocalDateTime startUpdatedAt, LocalDateTime endUpdatedAt, + Pageable pageable); } diff --git a/src/main/java/com/example/feeda/domain/post/service/PostService.java b/src/main/java/com/example/feeda/domain/post/service/PostService.java index 05a8436..463d697 100644 --- a/src/main/java/com/example/feeda/domain/post/service/PostService.java +++ b/src/main/java/com/example/feeda/domain/post/service/PostService.java @@ -4,6 +4,7 @@ import com.example.feeda.domain.post.dto.PostRequestDto; import com.example.feeda.domain.post.dto.PostResponseDto; import com.example.feeda.security.jwt.JwtPayload; +import java.time.LocalDate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -14,7 +15,8 @@ PostResponseDto createPost(PostRequestDto postRequestDto, PostResponseDto findPostById(Long id); - Page findAll(Pageable pageable, String keyword); + Page findAll(Pageable pageable, String keyword, LocalDate startUpdatedAt, + LocalDate endUpdatedAt); Page findFollowingAllPost(Pageable pageable, JwtPayload jwtPayload); diff --git a/src/main/java/com/example/feeda/domain/post/service/PostServiceImpl.java b/src/main/java/com/example/feeda/domain/post/service/PostServiceImpl.java index 8dc4a44..83fab0b 100644 --- a/src/main/java/com/example/feeda/domain/post/service/PostServiceImpl.java +++ b/src/main/java/com/example/feeda/domain/post/service/PostServiceImpl.java @@ -1,5 +1,6 @@ package com.example.feeda.domain.post.service; +import com.example.feeda.domain.comment.repository.CommentRepository; import com.example.feeda.domain.follow.entity.Follows; import com.example.feeda.domain.follow.repository.FollowsRepository; import com.example.feeda.domain.post.dto.PostLikeResponseDTO; @@ -12,10 +13,9 @@ import com.example.feeda.domain.profile.entity.Profile; import com.example.feeda.domain.profile.repository.ProfileRepository; import com.example.feeda.security.jwt.JwtPayload; - +import java.time.LocalDate; import java.util.List; import java.util.Optional; - import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -35,27 +35,30 @@ public class PostServiceImpl implements PostService { private final ProfileRepository profileRepository; // 현재 로그인 사용자 정보를 찾기 위해 필요 private final FollowsRepository followsRepository; private final PostLikeRepository postLikeRepository; + private final CommentRepository commentRepository; @Override public PostResponseDto createPost(PostRequestDto postRequestDto, JwtPayload jwtPayload) { Profile profile = profileRepository.findById(jwtPayload.getProfileId()) - .orElseThrow( - () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로필입니다.")); + .orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로필입니다.")); Post post = new Post(postRequestDto.getTitle(), postRequestDto.getContent(), - postRequestDto.getCategory(), profile); + postRequestDto.getCategory(), profile); Post savedPost = postRepository.save(post); - return new PostResponseDto(savedPost, 0L); + return new PostResponseDto(savedPost, 0L, 0L); } @Override @Transactional public PostLikeResponseDTO makeLikes(Long id, JwtPayload jwtPayload) { - Post post = postRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다.")); - Profile profile = profileRepository.findById(jwtPayload.getProfileId()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "프로필이 존재하지 않습니다.")); + Post post = postRepository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다.")); + Profile profile = profileRepository.findById(jwtPayload.getProfileId()).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "프로필이 존재하지 않습니다.")); ; // 중복 좋아요 방지 @@ -71,13 +74,15 @@ public PostLikeResponseDTO makeLikes(Long id, JwtPayload jwtPayload) { @Override public void deleteLikes(Long id, Long profileId) { Post post = postRepository.findById(id) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다.")); + .orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글이 존재하지 않습니다.")); Profile profile = profileRepository.findById(profileId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로필")); + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 프로필")); PostLike postLike = postLikeRepository.findByPostAndProfile(post, profile) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "해당 사용자의 좋아요가 존재하지 않습니다.")); + .orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "해당 사용자의 좋아요가 존재하지 않습니다.")); postLikeRepository.delete(postLike); } @@ -94,16 +99,33 @@ public PostResponseDto findPostById(Long id) { Post findPost = optionalPost.get(); Long likeCount = postLikeRepository.countByPost(findPost); + Long commentCount = commentRepository.countByPost(findPost); - return new PostResponseDto(findPost, likeCount); + return new PostResponseDto(findPost, likeCount, commentCount); } @Override @Transactional(readOnly = true) - public Page findAll(Pageable pageable, String keyword) { + public Page findAll(Pageable pageable, String keyword, + LocalDate startUpdatedAt, LocalDate endUpdatedAt) { + + if ((startUpdatedAt == null && endUpdatedAt != null) || (startUpdatedAt != null + && endUpdatedAt == null)) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, + "startUpdatedAt과 endUpdatedAt은 둘 다 있어야 하거나 둘 다 없어야 합니다."); + } + + if (startUpdatedAt != null) { + return postRepository.findAllByTitleContainingAndUpdatedAtBetween( + keyword, startUpdatedAt.atStartOfDay(), endUpdatedAt.atTime(23, 59, 59), pageable) + .map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post), + commentRepository.countByPost(post))); + } return postRepository.findAllByTitleContaining(keyword, pageable) - .map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post))); + .map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post), + commentRepository.countByPost(post))); } @Override @@ -111,17 +133,18 @@ public Page findAll(Pageable pageable, String keyword) { public Page findFollowingAllPost(Pageable pageable, JwtPayload jwtPayload) { Page followings = followsRepository.findAllByFollowers_Id( - jwtPayload.getProfileId(), pageable); + jwtPayload.getProfileId(), pageable); List followingProfileIds = followings.stream() - .map(following -> following.getFollowings().getId()) - .toList(); + .map(following -> following.getFollowings().getId()) + .toList(); Pageable newPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), - Sort.Direction.DESC, "updatedAt"); + Sort.Direction.DESC, "updatedAt"); return postRepository.findAllByProfile_IdIn(followingProfileIds, newPageable) - .map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post))); + .map(post -> PostResponseDto.toDto(post, postLikeRepository.countByPost(post), + commentRepository.countByPost(post))); } @Override @@ -129,7 +152,7 @@ public Page findFollowingAllPost(Pageable pageable, JwtPayload public PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload jwtPayload) { Post findPost = postRepository.findById(id) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글")); + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글")); if (!findPost.getProfile().getId().equals(jwtPayload.getProfileId())) { throw new ResponseStatusException(HttpStatus.FORBIDDEN, "권한이 없습니다."); @@ -139,8 +162,9 @@ public PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload Post savedPost = postRepository.save(findPost); Long likeCount = postLikeRepository.countByPost(findPost); + Long commentCount = commentRepository.countByPost(findPost); - return new PostResponseDto(savedPost, likeCount); + return new PostResponseDto(savedPost, likeCount, commentCount); } @Override @@ -148,7 +172,7 @@ public PostResponseDto updatePost(Long id, PostRequestDto requestDto, JwtPayload public void deletePost(Long id, JwtPayload jwtPayload) { Post findPost = postRepository.findById(id) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글")); + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "존재하지 않는 게시글")); if (!findPost.getProfile().getId().equals(jwtPayload.getProfileId())) { throw new ResponseStatusException(HttpStatus.FORBIDDEN, "권한이 없습니다.");