Skip to content

Commit

Permalink
Merge pull request #59 from jisung-in/feature/57-review-like-api
Browse files Browse the repository at this point in the history
[Feature] 리뷰 좋아요 API 구현
  • Loading branch information
AHNYUNKI authored Apr 3, 2024
2 parents f55764b + c06f3a8 commit afb3168
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.jisungin.api.reviewlike;

import com.jisungin.api.ApiResponse;
import com.jisungin.api.oauth.Auth;
import com.jisungin.api.oauth.AuthContext;
import com.jisungin.application.reviewlike.ReviewLikeService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RequestMapping("/v1/reviews")
@RequiredArgsConstructor
@RestController
public class ReviewLikeController {

private final ReviewLikeService reviewLikeService;

@PostMapping("/{reviewId}/likes")
public ApiResponse<Void> likeReview(
@PathVariable Long reviewId,
@Auth AuthContext authContext
) {
reviewLikeService.likeReview(authContext.getUserId(), reviewId);
return ApiResponse.ok(null);
}

@DeleteMapping("/{reviewId}/likes")
public ApiResponse<Void> unlikeReview(
@PathVariable Long reviewId,
@Auth AuthContext authContext
) {
reviewLikeService.unlikeReview(authContext.getUserId(), reviewId);
return ApiResponse.ok(null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.jisungin.application.reviewlike;

import com.jisungin.domain.review.Review;
import com.jisungin.domain.review.repository.ReviewRepository;
import com.jisungin.domain.reviewlike.ReviewLike;
import com.jisungin.domain.reviewlike.repository.ReviewLikeRepository;
import com.jisungin.domain.user.User;
import com.jisungin.domain.user.repository.UserRepository;
import com.jisungin.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.jisungin.exception.ErrorCode.*;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class ReviewLikeService {

private final ReviewLikeRepository reviewLikeRepository;

private final UserRepository userRepository;

private final ReviewRepository reviewRepository;

@Transactional
public void likeReview(Long userId, Long reviewId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException(USER_NOT_FOUND));

Review review = reviewRepository.findById(reviewId)
.orElseThrow(() -> new BusinessException(REVIEW_NOT_FOUND));

// 좋아요가 이미 존재할 경우, 400 에러
if (reviewLikeRepository.findByUserAndReview(user, review).isPresent()) {
throw new BusinessException(LIKE_EXIST);
}

// 없는 경우, 리뷰 좋아요 저장
ReviewLike reviewLike = ReviewLike.likeReview(user, review);
reviewLikeRepository.save(reviewLike);
}

@Transactional
public void unlikeReview(Long userId, Long reviewId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new BusinessException(USER_NOT_FOUND));

Review review = reviewRepository.findById(reviewId)
.orElseThrow(() -> new BusinessException(REVIEW_NOT_FOUND));

ReviewLike reviewLike = reviewLikeRepository.findByUserAndReview(user, review)
.orElseThrow(() -> new BusinessException(REVIEW_LIKE_NOT_FOUND));

reviewLikeRepository.delete(reviewLike);
}

}
40 changes: 40 additions & 0 deletions src/main/java/com/jisungin/domain/reviewlike/ReviewLike.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.jisungin.domain.reviewlike;

import com.jisungin.domain.review.Review;
import com.jisungin.domain.user.User;
import jakarta.persistence.*;
import lombok.*;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@ToString
public class ReviewLike {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "review_like_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "review_id")
private Review review;

@Builder
private ReviewLike(User user, Review review) {
this.user = user;
this.review = review;
}

public static ReviewLike likeReview(User user, Review review) {
return ReviewLike.builder()
.user(user)
.review(review)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.jisungin.domain.reviewlike.repository;

import com.jisungin.domain.review.Review;
import com.jisungin.domain.reviewlike.ReviewLike;
import com.jisungin.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ReviewLikeRepository extends JpaRepository<ReviewLike, Long> {

Optional<ReviewLike> findByUserAndReview(User user, Review review);

}
3 changes: 2 additions & 1 deletion src/main/java/com/jisungin/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public enum ErrorCode {
TALK_ROOM_LIKE_NOT_FOUND(404, "토크방 좋아요를 찾을 수 없습니다."),
LIKE_EXIST(400, "이미 좋아요를 눌렀습니다."),
REQUEST_TIME_OUT(408, "요청 시간이 만료 되었습니다."),
COMMENT_LIKE_NOT_FOUND(404, "의견 좋아요를 찾을 수 없습니다.");
COMMENT_LIKE_NOT_FOUND(404, "의견 좋아요를 찾을 수 없습니다."),
REVIEW_LIKE_NOT_FOUND(404, "리뷰 좋아요를 찾을 수 없습니다.");


private final int code;
Expand Down
8 changes: 7 additions & 1 deletion src/test/java/com/jisungin/ControllerTestSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.jisungin.api.commentlike.CommentLikeController;
import com.jisungin.api.oauth.AuthContext;
import com.jisungin.api.review.ReviewController;
import com.jisungin.api.reviewlike.ReviewLikeController;
import com.jisungin.api.talkroom.TalkRoomController;
import com.jisungin.api.talkroomlike.TalkRoomLikeController;
import com.jisungin.api.user.UserController;
Expand All @@ -14,6 +15,7 @@
import com.jisungin.application.comment.CommentService;
import com.jisungin.application.commentlike.CommentLikeService;
import com.jisungin.application.review.ReviewService;
import com.jisungin.application.reviewlike.ReviewLikeService;
import com.jisungin.application.talkroom.TalkRoomService;
import com.jisungin.application.talkroomlike.TalkRoomLikeService;
import com.jisungin.application.user.UserService;
Expand All @@ -29,7 +31,8 @@
TalkRoomLikeController.class,
CommentLikeController.class,
UserController.class,
BookController.class
BookController.class,
ReviewLikeController.class
})
public abstract class ControllerTestSupport {

Expand Down Expand Up @@ -66,4 +69,7 @@ public abstract class ControllerTestSupport {
@MockBean
protected BestSellerService bestSellerService;

@MockBean
protected ReviewLikeService reviewLikeService;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.jisungin.api.reviewlike;

import com.jisungin.ControllerTestSupport;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class ReviewLikeControllerTest extends ControllerTestSupport {

@DisplayName("유저가 리뷰 좋아요를 누른다.")
@Test
void likeReview() throws Exception {
//given
Long reviewId = 1L;

//when //then
mockMvc.perform(post("/v1/reviews/{reviewId}/likes", reviewId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("200"))
.andExpect(jsonPath("$.status").value("OK"))
.andExpect(jsonPath("$.message").value("OK"))
.andDo(print());
}

@DisplayName("유저가 리뷰 좋아요를 취소한다.")
@Test
void unlikeReview() throws Exception {
//given
Long reviewId = 1L;

//when //then
mockMvc.perform(delete("/v1/reviews/{reviewId}/likes", reviewId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("200"))
.andExpect(jsonPath("$.status").value("OK"))
.andExpect(jsonPath("$.message").value("OK"))
.andDo(print());
}

}
Loading

0 comments on commit afb3168

Please sign in to comment.