-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #54 from jisung-in/feature/43-my-review-read-api
[Feature] 사용자 별점 조회 API 구현
- Loading branch information
Showing
13 changed files
with
694 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.jisungin.api.user; | ||
|
||
import com.jisungin.api.ApiResponse; | ||
import com.jisungin.api.oauth.Auth; | ||
import com.jisungin.api.oauth.AuthContext; | ||
import com.jisungin.api.user.request.UserRatingGetAllRequest; | ||
import com.jisungin.application.PageResponse; | ||
import com.jisungin.application.review.response.RatingFindAllResponse; | ||
import com.jisungin.application.user.UserService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.ModelAttribute; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RequestMapping("/v1/users") | ||
@RequiredArgsConstructor | ||
@RestController | ||
public class UserController { | ||
|
||
private final UserService userService; | ||
|
||
@GetMapping("/ratings") | ||
public ApiResponse<PageResponse<RatingFindAllResponse>> getUserRatings( | ||
@ModelAttribute UserRatingGetAllRequest request, | ||
@Auth AuthContext authContext | ||
) { | ||
PageResponse<RatingFindAllResponse> response = userService.getUserRatings( | ||
authContext.getUserId(), request.toService()); | ||
return ApiResponse.ok(response); | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
src/main/java/com/jisungin/api/user/request/UserRatingGetAllRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.jisungin.api.user.request; | ||
|
||
import com.jisungin.application.user.request.UserRatingGetAllServiceRequest; | ||
import com.jisungin.domain.review.RatingOrderType; | ||
import lombok.*; | ||
|
||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
public class UserRatingGetAllRequest { | ||
|
||
private Integer page; | ||
|
||
private Integer size; | ||
|
||
private String order; | ||
|
||
private Double rating; | ||
|
||
@Builder | ||
public UserRatingGetAllRequest(Integer page, Integer size, String order, String rating) { | ||
this.page = page != null ? page : 1; | ||
this.size = size != null ? size : 10; | ||
this.order = order != null ? order : "date"; | ||
this.rating = rating != null ? Double.parseDouble(rating) : null; | ||
} | ||
|
||
public UserRatingGetAllServiceRequest toService() { | ||
return UserRatingGetAllServiceRequest.builder() | ||
.page(page) | ||
.size(size) | ||
.orderType(RatingOrderType.fromName(order)) | ||
.rating(rating) | ||
.build(); | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/com/jisungin/application/review/response/RatingFindAllResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.jisungin.application.review.response; | ||
|
||
import com.querydsl.core.annotations.QueryProjection; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class RatingFindAllResponse { | ||
|
||
private String isbn; | ||
|
||
private String title; | ||
|
||
private String image; | ||
|
||
private Double rating; | ||
|
||
@Builder | ||
@QueryProjection | ||
public RatingFindAllResponse(String isbn, String title, String image, Double rating) { | ||
this.isbn = isbn; | ||
this.title = title; | ||
this.image = image; | ||
this.rating = rating; | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/com/jisungin/application/user/UserService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.jisungin.application.user; | ||
|
||
import com.jisungin.application.PageResponse; | ||
import com.jisungin.application.review.response.RatingFindAllResponse; | ||
import com.jisungin.application.user.request.UserRatingGetAllServiceRequest; | ||
import com.jisungin.domain.review.repository.ReviewRepository; | ||
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.*; | ||
|
||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
@Service | ||
public class UserService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
private final ReviewRepository reviewRepository; | ||
|
||
public PageResponse<RatingFindAllResponse> getUserRatings(Long userId, UserRatingGetAllServiceRequest request) { | ||
User user = userRepository.findById(userId) | ||
.orElseThrow(() -> new BusinessException(USER_NOT_FOUND)); | ||
|
||
return reviewRepository.findAllRatingOrderBy( | ||
user.getId(), request.getOrderType(), request.getRating(), request.getSize(), request.getOffset()); | ||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/com/jisungin/application/user/request/UserRatingGetAllServiceRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.jisungin.application.user.request; | ||
|
||
import com.jisungin.domain.review.RatingOrderType; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
import static java.lang.Math.*; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class UserRatingGetAllServiceRequest { | ||
|
||
private static final int MAX_SIZE = 2_000; | ||
|
||
private Integer page; | ||
|
||
private Integer size; | ||
|
||
private RatingOrderType orderType; | ||
|
||
private Double rating; | ||
|
||
@Builder | ||
public UserRatingGetAllServiceRequest(Integer page, Integer size, RatingOrderType orderType, Double rating) { | ||
this.page = page; | ||
this.size = size; | ||
this.orderType = orderType; | ||
this.rating = rating; | ||
} | ||
|
||
public int getOffset() { | ||
return (max(1, page) - 1) * min(size, MAX_SIZE); | ||
} | ||
|
||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/com/jisungin/domain/review/RatingOrderType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.jisungin.domain.review; | ||
|
||
import java.util.Locale; | ||
|
||
public enum RatingOrderType { | ||
|
||
DATE, | ||
RATING_ASC, | ||
RATING_DESC, | ||
RATING_AVG_ASC, | ||
RATING_AVG_DESC; | ||
|
||
public static RatingOrderType fromName(String name) { | ||
return RatingOrderType.valueOf(name.toUpperCase(Locale.ENGLISH)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryCustom.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.jisungin.domain.review.repository; | ||
|
||
import com.jisungin.application.PageResponse; | ||
import com.jisungin.application.review.response.RatingFindAllResponse; | ||
import com.jisungin.domain.review.RatingOrderType; | ||
|
||
public interface ReviewRepositoryCustom { | ||
|
||
PageResponse<RatingFindAllResponse> findAllRatingOrderBy( | ||
Long userId, RatingOrderType ratingSortType, Double rating, int size, int offset); | ||
|
||
} |
88 changes: 88 additions & 0 deletions
88
src/main/java/com/jisungin/domain/review/repository/ReviewRepositoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package com.jisungin.domain.review.repository; | ||
|
||
import com.jisungin.application.PageResponse; | ||
import com.jisungin.application.review.response.QRatingFindAllResponse; | ||
import com.jisungin.application.review.response.RatingFindAllResponse; | ||
import com.jisungin.domain.review.RatingOrderType; | ||
import com.querydsl.core.types.OrderSpecifier; | ||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.util.List; | ||
|
||
import static com.jisungin.domain.book.QBook.book; | ||
import static com.jisungin.domain.review.QReview.review; | ||
import static com.jisungin.domain.review.RatingOrderType.*; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class ReviewRepositoryImpl implements ReviewRepositoryCustom { | ||
|
||
private final JPAQueryFactory queryFactory; | ||
|
||
@Override | ||
public PageResponse<RatingFindAllResponse> findAllRatingOrderBy( | ||
Long userId, RatingOrderType ratingSortType, Double rating, int size, int offset) { | ||
log.info("--------------start--------------"); | ||
// 리뷰 조회, 쿼리 1회 | ||
List<RatingFindAllResponse> ratings = getRatings(userId, ratingSortType, rating, size, offset); | ||
|
||
return PageResponse.<RatingFindAllResponse>builder() | ||
.queryResponse(ratings) | ||
.totalCount(getTotalCount(userId, rating)) // 해당 유저의 리뷰 총 개수, 쿼리 1회 | ||
.size(size) | ||
.build(); | ||
} | ||
|
||
private List<RatingFindAllResponse> getRatings( | ||
Long userId, RatingOrderType ratingSortType, Double rating, int size, int offset) { | ||
return queryFactory | ||
.select(new QRatingFindAllResponse( | ||
review.book.isbn, review.book.title, review.book.imageUrl, review.rating)) | ||
.from(review) | ||
.leftJoin(book).on(review.book.eq(book)) | ||
.where(review.user.id.eq(userId), ratingCondition(rating)) | ||
.groupBy(review.book.isbn) | ||
.orderBy(createSpecifier(ratingSortType), review.id.asc()) | ||
.offset(offset) | ||
.limit(size) | ||
.fetch(); | ||
} | ||
|
||
private long getTotalCount(Long userId, Double rating) { | ||
return queryFactory | ||
.select(review.count()) | ||
.from(review) | ||
.where(review.user.id.eq(userId), ratingCondition(rating)) | ||
.fetchOne(); | ||
} | ||
|
||
private OrderSpecifier createSpecifier(RatingOrderType ratingSortType) { | ||
if (ratingSortType.equals(RATING_ASC)) { | ||
return review.rating.asc(); | ||
} | ||
if (ratingSortType.equals(RATING_DESC)) { | ||
return review.rating.desc(); | ||
} | ||
if (ratingSortType.equals(RATING_AVG_ASC)) { | ||
return review.rating.avg().asc(); | ||
} | ||
if (ratingSortType.equals(RATING_AVG_DESC)) { | ||
return review.rating.avg().desc(); | ||
} | ||
|
||
return review.createDateTime.desc(); | ||
} | ||
|
||
// 만약 별점 필터링 조건이 존재하면 해당하는 별점만 가져온다. | ||
private BooleanExpression ratingCondition(Double rating) { | ||
if (rating == null) { | ||
return null; | ||
} | ||
|
||
return review.rating.eq(rating); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/test/java/com/jisungin/api/user/UserControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.jisungin.api.user; | ||
|
||
import com.jisungin.ControllerTestSupport; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.springframework.http.MediaType.*; | ||
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.*; | ||
|
||
class UserControllerTest extends ControllerTestSupport { | ||
|
||
@DisplayName("사용자의 모든 리뷰 별점을 조회한다.") | ||
@Test | ||
void getUserRatingAll() throws Exception { | ||
//given | ||
//when //then | ||
mockMvc.perform(get("/v1/users/ratings?page=1&size=4&order=rating_asc&rating=") | ||
.contentType(APPLICATION_JSON) | ||
) | ||
.andExpect(status().isOk()) | ||
.andExpect(jsonPath("$.code").value("200")) | ||
.andExpect(jsonPath("$.status").value("OK")) | ||
.andExpect(jsonPath("$.message").value("OK")) | ||
.andDo(print()); | ||
} | ||
|
||
} |
Oops, something went wrong.