-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#42 학생회 게시글 좋아요 기능 구현 #44
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
Changes from 5 commits
9c0c347
970db8d
cef4b68
57730c3
c73b0d0
682f910
c406bca
af9d12f
c513660
49de9e8
6859e89
9a61904
e55cdfd
a5f80d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.campus.campus.domain.councilpost.application.dto.response; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| public record GetLikedPostResponse( | ||
| @Schema(description = "게시글 id", example = "1") | ||
| Long postId, | ||
|
|
||
| @Schema(description = "게시글 이름", example = "투썸 제휴") | ||
| String title, | ||
|
|
||
| @Schema(description = "게시글 장소", example = "투썸 플레이스") | ||
| String place, | ||
|
|
||
| @Schema(description = "시간(끝나는 시간 or 행사날짜)", example = "2026-01-10T18:00:00") | ||
| LocalDateTime dateTime, | ||
|
|
||
| @Schema(description = "썸네일 image url", example = "https://www.example.com.png") | ||
| String thumbnailImageUrl | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.campus.campus.domain.councilpost.application.dto.response; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| public record LikePostResponse( | ||
| @Schema(name = "좋아요 누른 사용자 id", example = "1") | ||
| Long userId, | ||
|
|
||
| @Schema(name = "좋아요 누를 게시글의 id", example = "1") | ||
| Long postId, | ||
|
|
||
| @Schema(name = "좋아요 여부", example = "true") | ||
| boolean liked | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| import java.time.LocalDateTime; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.PageRequest; | ||
|
|
@@ -14,6 +15,8 @@ | |
| import com.campus.campus.domain.council.application.exception.StudentCouncilNotFoundException; | ||
| import com.campus.campus.domain.council.domain.entity.StudentCouncil; | ||
| import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; | ||
| import com.campus.campus.domain.councilpost.application.dto.response.GetLikedPostResponse; | ||
| import com.campus.campus.domain.councilpost.application.dto.response.LikePostResponse; | ||
| import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; | ||
| import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; | ||
| import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; | ||
|
|
@@ -24,11 +27,16 @@ | |
| import com.campus.campus.domain.councilpost.application.exception.PostOciImageDeleteFailedException; | ||
| import com.campus.campus.domain.councilpost.application.exception.ThumbnailRequiredException; | ||
| import com.campus.campus.domain.councilpost.application.mapper.StudentCouncilPostMapper; | ||
| import com.campus.campus.domain.councilpost.domain.entity.LikePost; | ||
| import com.campus.campus.domain.councilpost.domain.entity.PostCategory; | ||
| import com.campus.campus.domain.councilpost.domain.entity.PostImage; | ||
| import com.campus.campus.domain.councilpost.domain.entity.StudentCouncilPost; | ||
| import com.campus.campus.domain.councilpost.domain.repository.LikePostRepository; | ||
| import com.campus.campus.domain.councilpost.domain.repository.PostImageRepository; | ||
| import com.campus.campus.domain.councilpost.domain.repository.StudentCouncilPostRepository; | ||
| import com.campus.campus.domain.user.application.exception.UserNotFoundException; | ||
| import com.campus.campus.domain.user.domain.entity.User; | ||
| import com.campus.campus.domain.user.domain.repository.UserRepository; | ||
| import com.campus.campus.global.oci.application.service.PresignedUrlService; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
@@ -42,6 +50,8 @@ public class StudentCouncilPostService { | |
| private final StudentCouncilPostRepository postRepository; | ||
| private final StudentCouncilRepository studentCouncilRepository; | ||
| private final PostImageRepository postImageRepository; | ||
| private final LikePostRepository likePostRepository; | ||
| private final UserRepository userRepository; | ||
| private final PresignedUrlService presignedUrlService; | ||
| private final StudentCouncilPostMapper studentCouncilPostMapper; | ||
|
|
||
|
|
@@ -130,6 +140,42 @@ public Page<PostListItemResponse> findUpcomingEvents(int page, int size, Long cu | |
| ); | ||
| } | ||
|
|
||
| @Transactional | ||
| public LikePostResponse toggleLikePost(Long userId, Long postId) { | ||
| User user = userRepository.findByIdAndDeletedAtIsNull(userId) | ||
| .orElseThrow(UserNotFoundException::new); | ||
|
|
||
| StudentCouncilPost post = postRepository.findById(postId) | ||
| .orElseThrow(PostNotFoundException::new); | ||
|
|
||
| Optional<LikePost> likePost = likePostRepository.findByUserIdAndPost_Id(userId, postId); | ||
| boolean isLike; | ||
| if (likePost.isPresent()) { | ||
| likePostRepository.delete(likePost.get()); | ||
| isLike = false; | ||
| } else { | ||
| LikePost newLikePost = studentCouncilPostMapper.createLikePost(user, post); | ||
| likePostRepository.save(newLikePost); | ||
| isLike = true; | ||
| } | ||
|
|
||
| return studentCouncilPostMapper.toLikePostResponse(user, post, isLike); | ||
| } | ||
|
||
|
|
||
| @Transactional(readOnly = true) | ||
| public Page<GetLikedPostResponse> findLikedPosts(PostCategory category, int page, int size, Long userId) { | ||
| userRepository.findByIdAndDeletedAtIsNull(userId) | ||
| .orElseThrow(UserNotFoundException::new); | ||
|
|
||
| Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size); | ||
|
|
||
| Page<LikePost> likedPosts = likePostRepository.findLikedPosts(userId, category, pageable); | ||
|
|
||
| return likedPosts.map(likePost -> | ||
| studentCouncilPostMapper.toGetLikedPostResponse(likePost.getPost()) | ||
| ); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void delete(Long councilId, Long postId) { | ||
| studentCouncilRepository.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package com.campus.campus.domain.councilpost.domain.entity; | ||
|
|
||
| import static jakarta.persistence.FetchType.*; | ||
|
|
||
| import com.campus.campus.domain.user.domain.entity.User; | ||
| import com.campus.campus.global.entity.BaseEntity; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.FetchType; | ||
| import jakarta.persistence.GeneratedValue; | ||
| import jakarta.persistence.GenerationType; | ||
| import jakarta.persistence.Id; | ||
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.ManyToOne; | ||
| import jakarta.persistence.Table; | ||
| import jakarta.persistence.UniqueConstraint; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Entity | ||
| @Table(name = "like_posts", uniqueConstraints = { | ||
| @UniqueConstraint(columnNames = {"user_id", "post_id"}) | ||
| }) | ||
| @Getter | ||
| @Builder | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| public class LikePost extends BaseEntity { | ||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "like_post_id") | ||
| private Long likePostId; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "user_id") | ||
| private User user; | ||
|
|
||
| @ManyToOne(fetch = LAZY) | ||
| @JoinColumn(name = "post_id") | ||
| private StudentCouncilPost post; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.campus.campus.domain.councilpost.domain.repository; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
| import org.springframework.data.jpa.repository.EntityGraph; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.data.jpa.repository.Query; | ||
| import org.springframework.data.repository.query.Param; | ||
|
|
||
| import com.campus.campus.domain.councilpost.domain.entity.LikePost; | ||
| import com.campus.campus.domain.councilpost.domain.entity.PostCategory; | ||
|
|
||
| public interface LikePostRepository extends JpaRepository<LikePost, Long> { | ||
| Optional<LikePost> findByUserIdAndPost_Id(Long userId, Long postId); | ||
|
|
||
| @EntityGraph(attributePaths = {"post", "post.writer"}) | ||
| @Query(""" | ||
| SELECT lp FROM LikePost lp | ||
| JOIN lp.post p | ||
| WHERE lp.user.id = :userId | ||
| AND (:category IS NULL OR p.category = :category) | ||
| ORDER BY lp.createdAt DESC | ||
| """) | ||
| Page<LikePost> findLikedPosts(@Param("userId") Long userId, @Param("category") PostCategory category, | ||
| Pageable pageable); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.