Skip to content

Commit

Permalink
Merge pull request #30 from uju-in/LIME-67--review-like-feat
Browse files Browse the repository at this point in the history
[LIME-67] 리뷰 좋아요 기능 추가
  • Loading branch information
Curry4182 authored Jan 30, 2024
2 parents 5374dfb + aa70aef commit d733c12
Show file tree
Hide file tree
Showing 20 changed files with 458 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.programmers.lime.domains.review.api.dto.response.ReviewGetByCursorResponse;
import com.programmers.lime.domains.review.api.dto.response.ReviewGetResponse;
import com.programmers.lime.domains.review.api.dto.response.ReviewModifyResponse;
import com.programmers.lime.domains.review.application.ReviewLikeService;
import com.programmers.lime.domains.review.application.ReviewService;
import com.programmers.lime.domains.review.application.dto.ReviewGetByCursorServiceResponse;
import com.programmers.lime.domains.review.application.dto.ReviewGetServiceResponse;
Expand All @@ -39,6 +40,7 @@
public class ReviewController {

private final ReviewService reviewService;
private final ReviewLikeService reviewLikeService;

@Operation(summary = "아이템 리뷰 등록", description = "itemId, ReviewCreateRequest을 이용하여 아이템 리뷰를 등록 합니다.")
@PostMapping()
Expand Down Expand Up @@ -106,4 +108,26 @@ public ResponseEntity<ReviewGetResponse> getReview(

return ResponseEntity.ok(response);
}

@Operation(summary = "아이템 리뷰 좋아요", description = "reviewId을 이용하여 아이템 리뷰를 좋아요 합니다.")
@PostMapping("/{reviewId}/like")
public ResponseEntity<Void> likeReview(
@PathVariable final Long itemId,
@PathVariable final Long reviewId
) {
reviewLikeService.like(itemId, reviewId);

return ResponseEntity.ok().build();
}

@Operation(summary = "아이템 리뷰 좋아요 취소", description = "reviewId을 이용하여 아이템 리뷰를 좋아요 취소 합니다.")
@DeleteMapping("/{reviewId}/like")
public ResponseEntity<Void> cancelLikedReview(
@PathVariable final Long itemId,
@PathVariable final Long reviewId
) {
reviewLikeService.unlike(itemId, reviewId);

return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.programmers.lime.domains.review.application;

import org.springframework.stereotype.Service;

import com.programmers.lime.domains.review.implementation.ReviewLikeAppender;
import com.programmers.lime.domains.review.implementation.ReviewLikeRemover;
import com.programmers.lime.domains.review.implementation.ReviewLikeValidator;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class ReviewLikeService {

private final ReviewLikeAppender reviewLikeAppender;
private final ReviewLikeRemover reviewLikeRemover;
private final ReviewLikeValidator reviewLikeValidator;

public void like(
final Long memberId,
final Long reviewId
) {
reviewLikeValidator.validateReviewLike(memberId, reviewId);
reviewLikeAppender.append(memberId, reviewId);
}

public void unlike(
final Long memberId,
final Long reviewId
) {
reviewLikeValidator.validateReviewUnlike(memberId, reviewId);
reviewLikeRemover.deleteByMemberIdAndReviewId(memberId, reviewId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public enum ErrorCode {
REVIEW_NOT_EQUAL_ITEM("REVIEW_002", "리뷰 아이디와 아이템 아이디가 일치하지 않습니다."),
REVIEW_NOT_MINE("REVIEW_003", "리뷰 작성자와 로그인한 회원아이디가 일치하지 않습니다."),
REVIEW_BAD_SORT_CONDITION("REVIEW_004", "잘못된 리뷰 정렬 조건 입니다."),
ALREADY_REVIEW_LIKED("REVIEW_005", "이미 리뷰를 좋아요 했습니다."),
NOT_REVIEW_LIKED("REVIEW_006", "좋아요를 누르지 않은 리뷰입니다."),

// Vote
VOTE_NOT_FOUND("VOTE_001", "투표를 찾을 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.programmers.lime.domains.review.domain;

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 lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "review_likes")
public class ReviewLike {

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

@Column(name = "member_id")
private Long memberId;

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

public ReviewLike(
final Long memberId,
final Review review
) {
this.memberId = memberId;
this.review = review;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.programmers.lime.domains.review.model.MemberInfoWithReviewId;
import com.programmers.lime.domains.review.model.ReviewCursorIdInfo;
import com.programmers.lime.domains.review.model.ReviewCursorSummary;
import com.programmers.lime.domains.review.model.ReviewImageInfo;
import com.programmers.lime.domains.review.model.ReviewInfo;
import com.programmers.lime.domains.review.model.ReviewSortCondition;
import com.programmers.lime.domains.review.model.ReviewSummary;
import com.programmers.lime.domains.review.repository.ReviewRepository;
Expand Down Expand Up @@ -59,9 +61,14 @@ private List<ReviewCursorSummary> getReviewCursorSummaries(
.toList();

// 리뷰 아이디를 이용하여 리뷰 정보를 가져옴
Map<Long, ReviewSummary> reviewSummaryMap = reviewRepository.getReviewSummaries(reviewIds)
Map<Long, ReviewInfo> reviewInfoMap = reviewRepository.getReviewInfo(reviewIds)
.stream()
.collect(Collectors.toMap(ReviewSummary::reviewId, Function.identity()));
.collect(Collectors.toMap(ReviewInfo::reviewId, Function.identity()));

// 리뷰 아이디를 이용하여 리뷰 이미지 정보를 가져옴
Map<Long, ReviewImageInfo> reviewImageInfoMap = reviewRepository.getReviewImageInfos(reviewIds)
.stream()
.collect(Collectors.toMap(ReviewImageInfo::reviewId, Function.identity()));

// 리뷰 아이디를 이용하여 멤버 정보를 가져옴
Map<Long, MemberInfoWithReviewId> memberInfoMap = reviewRepository.getMemberInfos(reviewIds)
Expand All @@ -71,7 +78,9 @@ private List<ReviewCursorSummary> getReviewCursorSummaries(

return reviewCursorIdInfos.stream()
.map(reviewCursorIdInfo -> {
ReviewSummary reviewSummary = reviewSummaryMap.get(reviewCursorIdInfo.reviewId());
ReviewInfo reviewInfo = reviewInfoMap.get(reviewCursorIdInfo.reviewId());
ReviewImageInfo reviewImageInfo = reviewImageInfoMap.get(reviewCursorIdInfo.reviewId());
ReviewSummary reviewSummary = ReviewSummary.of(reviewInfo, reviewImageInfo.imageUrls());
MemberInfoWithReviewId memberInfoWithReviewId = memberInfoMap.get(reviewCursorIdInfo.reviewId());
MemberInfo memberInfo = memberInfoWithReviewId.memberInfo();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.programmers.lime.domains.review.implementation;

import org.springframework.stereotype.Component;

import com.programmers.lime.domains.review.domain.Review;
import com.programmers.lime.domains.review.domain.ReviewLike;
import com.programmers.lime.domains.review.repository.ReviewLikeRepository;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ReviewLikeAppender {

private final ReviewLikeRepository reviewLikeRepository;

private final ReviewReader reviewReader;

public void append(
final Long memberId,
final Long reviewId
) {
Review review = reviewReader.read(reviewId);

ReviewLike reviewLike = new ReviewLike(
memberId,
review
);

reviewLikeRepository.save(reviewLike);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.programmers.lime.domains.review.implementation;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.programmers.lime.domains.review.repository.ReviewLikeRepository;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ReviewLikeReader {

private final ReviewLikeRepository reviewLikeRepository;

public boolean alreadyLiked(
final Long memberId,
final Long reviewId
) {
if (memberId == null) {
return false;
}

return reviewLikeRepository.existsByMemberIdAndReviewId(memberId, reviewId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.programmers.lime.domains.review.implementation;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.programmers.lime.domains.review.domain.Review;
import com.programmers.lime.domains.review.repository.ReviewLikeRepository;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ReviewLikeRemover {

private final ReviewReader reviewReader;
private final ReviewLikeRepository reviewLikeRepository;

@Transactional
public void deleteByMemberIdAndReviewId(
final Long memberId,
final Long reviewId
) {
Review review = reviewReader.read(reviewId);
reviewLikeRepository.deleteReviewLikeByMemberIdAndReview(memberId, review);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.programmers.lime.domains.review.implementation;

import org.springframework.stereotype.Component;

import com.programmers.lime.error.BusinessException;
import com.programmers.lime.error.ErrorCode;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ReviewLikeValidator {

private final ReviewLikeReader reviewLikeReader;

public void validateReviewLike(
final Long memberId,
final Long reviewId
) {
if (reviewLikeReader.alreadyLiked(memberId, reviewId)) {
throw new BusinessException(ErrorCode.ALREADY_REVIEW_LIKED);
}
}

public void validateReviewUnlike(
final Long memberId,
final Long reviewId
) {
if (!reviewLikeReader.alreadyLiked(memberId, reviewId)) {
throw new BusinessException(ErrorCode.NOT_REVIEW_LIKED);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.programmers.lime.domains.review.model;

import java.util.List;

import lombok.Builder;

@Builder
public record ReviewImageInfo(
Long reviewId,
List<String> imageUrls
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.programmers.lime.domains.review.model;

import java.time.LocalDateTime;

import lombok.Builder;

@Builder
public record ReviewInfo(

Long reviewId,

int rate,

String content,

Long likeCount,

LocalDateTime createdAt,

LocalDateTime updatedAt
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,25 @@ public record ReviewSummary(

List<String> imageUrls,

Long likeCount,

LocalDateTime createdAt,

LocalDateTime updatedAt
) {

public static ReviewSummary of(
final ReviewInfo reviewInfo,
final List<String> imageUrls
) {
return ReviewSummary.builder()
.reviewId(reviewInfo.reviewId())
.rate(reviewInfo.rate())
.content(reviewInfo.content())
.imageUrls(imageUrls)
.likeCount(reviewInfo.likeCount())
.createdAt(reviewInfo.createdAt())
.updatedAt(reviewInfo.updatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.programmers.lime.domains.review.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.programmers.lime.domains.review.domain.Review;
import com.programmers.lime.domains.review.domain.ReviewLike;

public interface ReviewLikeRepository extends JpaRepository<ReviewLike, Long> {

void deleteReviewLikeByMemberIdAndReview(
final Long memberId,
final Review review
);

boolean existsByMemberIdAndReviewId(
final Long memberId,
final Long reviewId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

import com.programmers.lime.domains.review.model.MemberInfoWithReviewId;
import com.programmers.lime.domains.review.model.ReviewCursorIdInfo;
import com.programmers.lime.domains.review.model.ReviewImageInfo;
import com.programmers.lime.domains.review.model.ReviewInfo;
import com.programmers.lime.domains.review.model.ReviewSortCondition;
import com.programmers.lime.domains.review.model.ReviewSummary;

public interface ReviewRepositoryForCursor {
List<ReviewCursorIdInfo> findAllByCursor(
Expand All @@ -17,7 +18,9 @@ List<ReviewCursorIdInfo> findAllByCursor(

List<MemberInfoWithReviewId> getMemberInfos(List<Long> reviewIds);

List<ReviewSummary> getReviewSummaries(List<Long> reviewIds);
List<ReviewImageInfo> getReviewImageInfos(List<Long> reviewIds);

List<ReviewInfo> getReviewInfo(List<Long> reviewIds);

int getReviewCount(Long itemId);
}
Loading

0 comments on commit d733c12

Please sign in to comment.