Skip to content

Commit bd3febd

Browse files
authored
Merge pull request #971 from Clubber2024/feat/#970-refactor-회원-리뷰-좋아요-여부-삭제
refactor : 회원 리뷰 좋아요 여부 포함 & 좋아요 취소 구현
2 parents df3a7f1 + d1c1fb6 commit bd3febd

13 files changed

Lines changed: 149 additions & 58 deletions

File tree

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/exception/ReviewErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public enum ReviewErrorCode implements BaseErrorCode {
1818
REVIEW_ALREADY_LIKED(HttpStatus.BAD_REQUEST.value(), "REVIEW_400_8", "이미 좋아요를 표시한 리뷰입니다."),
1919
REVIEW_SELF_REPORT_NOT_ALLOWED(HttpStatus.BAD_REQUEST.value(), "REVIEW_400_9", "자신이 작성한 리뷰는 신고할 수 없습니다."),
2020
REVIEW_ALREADY_HIDDEN(HttpStatus.BAD_REQUEST.value(),"REVIEW_400_10", "이미 숨김 처리된 리뷰입니다."),
21-
REVIEW_NOT_FOUND(HttpStatus.NO_CONTENT.value(), "REVIEW_404_1", "존재하지 않는 리뷰입니다.");
21+
REVIEW_NOT_FOUND(HttpStatus.NO_CONTENT.value(), "REVIEW_404_1", "존재하지 않는 리뷰입니다."),
22+
REVIEW_LIKE_NOT_FOUND(HttpStatus.NO_CONTENT.value(), "REVIEW_404_2", "좋아요하지 않은 리뷰입니다");
2223

2324
private final Integer status;
2425
private final String code;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.clubber.domain.domains.review.exception;
2+
3+
import com.clubber.common.exception.BaseException;
4+
5+
public class ReviewLikeNotFoundException extends BaseException {
6+
7+
public static final BaseException EXCEPTION = new ReviewLikeNotFoundException();
8+
9+
private ReviewLikeNotFoundException() {
10+
super(ReviewErrorCode.REVIEW_LIKE_NOT_FOUND);
11+
}
12+
}

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/implement/ReviewReader.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.clubber.domain.domains.review.implement;
22

33
import com.clubber.domain.domains.review.domain.Review;
4+
import com.clubber.domain.domains.review.domain.ReviewLike;
5+
import com.clubber.domain.domains.review.exception.ReviewLikeNotFoundException;
46
import com.clubber.domain.domains.review.exception.ReviewNotFoundException;
7+
import com.clubber.domain.domains.review.repository.ReviewLikeRepository;
58
import com.clubber.domain.domains.review.repository.ReviewRepository;
69
import com.clubber.domain.domains.user.domain.User;
710
import lombok.RequiredArgsConstructor;
@@ -11,9 +14,15 @@
1114
@RequiredArgsConstructor
1215
public class ReviewReader {
1316
private final ReviewRepository reviewRepository;
17+
private final ReviewLikeRepository reviewLikeRepository;
1418

1519
public Review findById(Long id) {
1620
return reviewRepository.findByIdAndIsDeletedFalse(id)
1721
.orElseThrow(() -> ReviewNotFoundException.EXCEPTION);
1822
}
23+
24+
public ReviewLike findUserReviewLike(Review review, User user) {
25+
return reviewLikeRepository.findByUserAndReviewAndIsDeletedFalse(review, user)
26+
.orElseThrow(() -> ReviewLikeNotFoundException.EXCEPTION);
27+
}
1928
}

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/repository/ReviewCustomRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface ReviewCustomRepository {
1515

1616
List<Review> queryReviewByUserOrderByIdDesc(User user);
1717

18-
Page<ClubReviewResponse> queryReviewByClub(Club club, Pageable pageable, ReviewSortType sortType);
18+
Page<ClubReviewResponse> queryReviewByClub(Club club, User loginUser, Pageable pageable, ReviewSortType sortType);
1919

2020
Page<Review> queryReviewByClubAndFilterType(Club club, ReviewFilterType filterType, Pageable pageable);
2121

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/repository/ReviewCustomRepositoryImpl.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public List<Review> queryReviewByUserOrderByIdDesc(User user) {
4444
}
4545

4646
@Override
47-
public Page<ClubReviewResponse> queryReviewByClub(Club club, Pageable pageable, ReviewSortType sortType) {
47+
public Page<ClubReviewResponse> queryReviewByClub(Club club, User loginUser, Pageable pageable, ReviewSortType sortType) {
4848

4949
List<Tuple> tuples = queryFactory
5050
.select(review, reviewLike.count())
@@ -69,6 +69,15 @@ public Page<ClubReviewResponse> queryReviewByClub(Club club, Pageable pageable,
6969
t -> t.get(reviewLike.count())
7070
));
7171

72+
73+
List<Long> likedReviewIds = queryFactory.select(reviewLike.review.id)
74+
.from(reviewLike)
75+
.where(reviewLike.user.id.eq(loginUser.getId())
76+
.and(reviewLike.isDeleted.eq(false))
77+
.and(reviewLike.review.id.in(reviewIds))
78+
)
79+
.fetch();
80+
7281
List<Review> reviews = queryFactory.selectFrom(review)
7382
.join(review.reviewKeywords, reviewKeyword).fetchJoin()
7483
.join(review.user, user).fetchJoin()
@@ -87,7 +96,9 @@ public Page<ClubReviewResponse> queryReviewByClub(Club club, Pageable pageable,
8796
.map(r -> ClubReviewResponse.of(
8897
r,
8998
ReviewUtil.extractKeywords(r),
90-
likeCountMap.getOrDefault(r.getId(), 0L)
99+
likeCountMap.getOrDefault(r.getId(), 0L),
100+
likedReviewIds.contains(r.getId())
101+
91102
))
92103
.toList();
93104

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/repository/ReviewLikeRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import com.clubber.domain.domains.user.domain.User;
66
import org.springframework.data.jpa.repository.JpaRepository;
77

8+
import java.util.Optional;
9+
810
public interface ReviewLikeRepository extends JpaRepository<ReviewLike, Long> {
911
boolean existsByReviewAndUserAndIsDeletedFalse(Review review, User user);
12+
Optional<ReviewLike> findByUserAndReviewAndIsDeletedFalse(Review review, User user);
1013
}

Clubber-Domain/src/main/java/com/clubber/domain/domains/review/vo/ClubReviewResponse.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,21 @@ public class ClubReviewResponse {
3737
@Schema(description = "리뷰 좋아요 수")
3838
private final Long likes;
3939

40+
@Schema(description = "리뷰 좋아요 여부")
41+
private final boolean isLiked;
42+
4043
@Schema(description = "신고 상태")
4144
private final ReportStatus reportStatus;
4245

43-
public static ClubReviewResponse of(Review review, Set<String> keywords, Long likes) {
46+
public static ClubReviewResponse of(Review review, Set<String> keywords, Long likes, boolean isLiked) {
4447
return ClubReviewResponse.builder()
4548
.keywords(keywords)
4649
.reviewId(review.getId())
4750
.userId(review.getUser().getId())
4851
.dateTime(review.getCreatedAt())
4952
.content(review.getContentForPublic())
5053
.likes(likes)
54+
.isLiked(isLiked)
5155
.reportStatus(review.getReportStatus())
5256
.build();
5357
}

Clubber-External/src/main/java/com/clubber/domain/review/controller/ReviewController.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package com.clubber.domain.review.controller;
22

33
import com.clubber.domain.domains.review.domain.ReviewSortType;
4-
import com.clubber.domain.recruit.dto.PostRecruitRequest;
54
import com.clubber.domain.review.dto.*;
5+
import com.clubber.domain.review.service.ReviewLikeService;
66
import com.clubber.domain.review.service.ReviewService;
77
import com.clubber.global.config.swagger.DisableSwaggerSecurity;
88
import io.swagger.v3.oas.annotations.Operation;
99
import io.swagger.v3.oas.annotations.tags.Tag;
1010
import jakarta.validation.Valid;
1111
import lombok.RequiredArgsConstructor;
1212
import org.springframework.data.domain.Pageable;
13-
import org.springframework.data.web.PageableDefault;
1413
import org.springframework.web.bind.annotation.*;
1514

1615
@RestController
@@ -20,6 +19,7 @@
2019
public class ReviewController {
2120

2221
private final ReviewService reviewService;
22+
private final ReviewLikeService reviewLikeService;
2323

2424
@Operation(summary = "동아리 리뷰 동의 여부 반환")
2525
@DisableSwaggerSecurity
@@ -65,7 +65,13 @@ public CreateClubReviewResponse createReviewWithContent(
6565
@Operation(summary = "리뷰 좋아요 등록")
6666
@PostMapping("/like/{id}")
6767
public void createReviewLike(@PathVariable Long id) {
68-
reviewService.createReviewLike(id);
68+
reviewLikeService.createReviewLike(id);
69+
}
70+
71+
@Operation(summary = "리뷰 좋아요 취소(삭제)")
72+
@DeleteMapping("/like/{id}")
73+
public void deleteReviewLike(@PathVariable Long id) {
74+
reviewLikeService.deleteReviewLike(id);
6975
}
7076

7177
@Operation(summary = "리뷰 신고")
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.clubber.domain.review.service;
2+
3+
import com.clubber.domain.domains.review.domain.Review;
4+
import com.clubber.domain.domains.review.domain.ReviewLike;
5+
import com.clubber.domain.domains.review.implement.ReviewReader;
6+
import com.clubber.domain.domains.review.implement.ReviewValidator;
7+
import com.clubber.domain.domains.review.repository.ReviewLikeRepository;
8+
import com.clubber.domain.domains.user.domain.User;
9+
import com.clubber.domain.review.mapper.ReviewMapper;
10+
import com.clubber.domain.user.implement.UserReader;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class ReviewLikeService {
18+
19+
private final UserReader userReader;
20+
private final ReviewReader reviewReader;
21+
private final ReviewValidator reviewValidator;
22+
private final ReviewMapper reviewMapper;
23+
private final ReviewLikeRepository reviewLikeRepository;
24+
25+
@Transactional
26+
public void createReviewLike(Long reviewId) {
27+
User user = userReader.getCurrentUser();
28+
Review review = reviewReader.findById(reviewId);
29+
reviewValidator.validateReviewExists(user, review);
30+
ReviewLike reviewLike = reviewMapper.toReviewLike(review, user);
31+
reviewLikeRepository.save(reviewLike);
32+
}
33+
34+
@Transactional
35+
public void deleteReviewLike(Long reviewId) {
36+
User user = userReader.getCurrentUser();
37+
Review review = reviewReader.findById(reviewId);
38+
ReviewLike userReviewLike = reviewReader.findUserReviewLike(review, user);
39+
userReviewLike.delete();
40+
}
41+
}

Clubber-External/src/main/java/com/clubber/domain/review/service/ReviewService.java

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@
1010
import com.clubber.domain.domains.report.repository.ReportRepository;
1111
import com.clubber.domain.domains.review.domain.Review;
1212
import com.clubber.domain.domains.review.domain.ReviewKeywordCategory;
13-
import com.clubber.domain.domains.review.domain.ReviewLike;
1413
import com.clubber.domain.domains.review.domain.ReviewSortType;
1514
import com.clubber.domain.domains.review.exception.UserAlreadyReviewedException;
1615
import com.clubber.domain.domains.review.implement.ReviewReader;
1716
import com.clubber.domain.domains.review.implement.ReviewValidator;
1817
import com.clubber.domain.domains.review.repository.ReviewKeywordRepository;
19-
import com.clubber.domain.domains.review.repository.ReviewLikeRepository;
2018
import com.clubber.domain.domains.review.repository.ReviewRepository;
2119
import com.clubber.domain.domains.review.vo.ClubReviewResponse;
2220
import com.clubber.domain.domains.review.vo.KeywordCountStatDto;
@@ -49,22 +47,21 @@ public class ReviewService {
4947
private final ReviewReader reviewReader;
5048
private final ReviewValidator reviewValidator;
5149
private final ClubReader clubReader;
52-
private final ReviewLikeRepository reviewLikeRepository;
5350

5451
public List<ReviewKeywordCategoryResponse> getTotalReviewKeywords() {
5552
return Arrays.stream(ReviewKeywordCategory.values())
56-
.map(
57-
(reviewKeywordCategory) -> {
58-
List<EnumMapperVO> enumValues = enumMapper.toEnumValues(
59-
reviewKeywordCategory.getReviewKeywords());
60-
return new ReviewKeywordCategoryResponse(reviewKeywordCategory, enumValues);
61-
})
62-
.toList();
53+
.map(
54+
(reviewKeywordCategory) -> {
55+
List<EnumMapperVO> enumValues = enumMapper.toEnumValues(
56+
reviewKeywordCategory.getReviewKeywords());
57+
return new ReviewKeywordCategoryResponse(reviewKeywordCategory, enumValues);
58+
})
59+
.toList();
6360
}
6461

6562
@Transactional
6663
public CreateClubReviewResponse createReview(Long clubId,
67-
@Valid CreateClubReviewRequest reviewRequest) {
64+
@Valid CreateClubReviewRequest reviewRequest) {
6865
User user = userReader.getCurrentUser();
6966
Club club = clubReader.findById(clubId);
7067

@@ -94,36 +91,33 @@ private void validateReviewExists(Club club, User user) {
9491
@Transactional(readOnly = true)
9592
public GetClubReviewAgreedStatusResponse getClubReviewAgreedStatus(Long clubId) {
9693
Club club = clubRepository.findClubByIdAndIsDeleted(clubId, false)
97-
.orElseThrow(() -> ClubNotFoundException.EXCEPTION);
94+
.orElseThrow(() -> ClubNotFoundException.EXCEPTION);
9895

9996
return GetClubReviewAgreedStatusResponse.from(club);
10097
}
10198

10299
@Transactional(readOnly = true)
103100
public GetClubReviewsKeywordStatsResponse getClubReviewKeywordStats(Long clubId) {
104101
Club club = clubRepository.findClubByIdAndIsDeleted(clubId, false)
105-
.orElseThrow(() -> ClubNotFoundException.EXCEPTION);
102+
.orElseThrow(() -> ClubNotFoundException.EXCEPTION);
106103

107104
club.validateAgreeToReview();
108105

109106
List<KeywordCountStatDto> keywordCountStatDtoList = reviewKeywordRepository.queryReviewKeywordStatsByClubId(
110-
club.getId());
107+
club.getId());
111108

112109
final KeywordStatsVO keywordStatsVO = new KeywordStatsVO(keywordCountStatDtoList);
113110
return reviewMapper.getGetClubReviewsKeywordStatsResponse(club, keywordStatsVO);
114111
}
115112

116113
//동아리 별 리뷰 조회 : Page 별 조회
117114
@Transactional(readOnly = true)
118-
public GetClubReviewsPageResponse getClubReviewsWithContent(Long clubId,
119-
Pageable pageable, ReviewSortType sortType) {
120-
Club club = clubRepository.findClubByIdAndIsDeleted(clubId, false)
121-
.orElseThrow(() -> ClubNotFoundException.EXCEPTION);
122-
115+
public GetClubReviewsPageResponse getClubReviewsWithContent(Long clubId, Pageable pageable, ReviewSortType sortType) {
116+
User user = userReader.getCurrentUser();
117+
Club club = clubReader.findById(clubId);
123118
club.validateAgreeToReview();
124119

125-
Page<ClubReviewResponse> clubReviewResponses = reviewRepository.queryReviewByClub(club,
126-
pageable, sortType);
120+
Page<ClubReviewResponse> clubReviewResponses = reviewRepository.queryReviewByClub(club, user, pageable, sortType);
127121
return reviewMapper.getGetClubReviewsPageResponse(clubReviewResponses, clubId);
128122
}
129123

@@ -151,15 +145,6 @@ public void softDeleteReviewByClubId(Long clubId) {
151145
reviewRepository.softDeleteReviewByClubId(clubId);
152146
}
153147

154-
@Transactional
155-
public void createReviewLike(Long reviewId) {
156-
User user = userReader.getCurrentUser();
157-
Review review = reviewReader.findById(reviewId);
158-
reviewValidator.validateReviewExists(user, review);
159-
ReviewLike reviewLike = reviewMapper.toReviewLike(review, user);
160-
reviewLikeRepository.save(reviewLike);
161-
}
162-
163148

164149
@Transactional
165150
public CreateReviewReportResponse createReviewReport(Long reviewId, CreateReviewReportRequest request) {
@@ -170,10 +155,10 @@ public CreateReviewReportResponse createReviewReport(Long reviewId, CreateReview
170155
reviewValidator.validateReportReason(request.getReportReason(), request.getDetailReason());
171156

172157
Report report = Report.of(review, request.getReportReason(),
173-
request.getDetailReason());
158+
request.getDetailReason());
174159
Report savedReport = reportRepository.save(report);
175160

176-
return CreateReviewReportResponse.of(review,savedReport);
161+
return CreateReviewReportResponse.of(review, savedReport);
177162
}
178163

179164
}

0 commit comments

Comments
 (0)