Skip to content
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

refactor: 꿀조합 랭킹 API 수정 #38

Merged
merged 10 commits into from
Apr 19, 2024
9 changes: 7 additions & 2 deletions src/main/java/com/funeat/auth/util/AuthArgumentResolver.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.funeat.auth.util;

import com.funeat.auth.dto.LoginInfo;
import java.util.Objects;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import java.util.Objects;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
Expand All @@ -14,6 +14,8 @@
@Component
public class AuthArgumentResolver implements HandlerMethodArgumentResolver {

private static final LoginInfo GUEST = new LoginInfo(-1L);

@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
Expand All @@ -24,8 +26,11 @@ public Object resolveArgument(final MethodParameter parameter, final ModelAndVie
final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) {
final HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
final HttpSession session = Objects.requireNonNull(request).getSession(false);
final String id = String.valueOf(session.getAttribute("member"));
if (Objects.isNull(session)) {
return GUEST;
}

final String id = String.valueOf(session.getAttribute("member"));
return new LoginInfo(Long.valueOf(id));
}
}
27 changes: 18 additions & 9 deletions src/main/java/com/funeat/recipe/application/RecipeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.funeat.recipe.domain.RecipeImage;
import com.funeat.recipe.dto.RankingRecipeDto;
import com.funeat.recipe.dto.RankingRecipesResponse;
import com.funeat.recipe.dto.RecipeAuthorDto;
import com.funeat.recipe.dto.RecipeCommentCondition;
import com.funeat.recipe.dto.RecipeCommentCreateRequest;
import com.funeat.recipe.dto.RecipeCommentResponse;
Expand Down Expand Up @@ -63,10 +62,11 @@
public class RecipeService {

private static final long RANKING_MINIMUM_FAVORITE_COUNT = 1L;
private static final int RANKING_SIZE = 3;
private static final int RANKING_SIZE = 4;
private static final int DEFAULT_PAGE_SIZE = 10;
private static final int RECIPE_COMMENT_PAGE_SIZE = 10;
private static final int DEFAULT_CURSOR_PAGINATION_SIZE = 11;
private static final long GUEST_ID = -1L;

private final MemberRepository memberRepository;
private final ProductRepository productRepository;
Expand Down Expand Up @@ -217,21 +217,30 @@ private List<Recipe> findAllByProductNameContaining(final String query, final Lo
return recipeRepository.findAllByProductNameContaining(query, lastRecipeId, size);
}

public RankingRecipesResponse getTop3Recipes() {
public RankingRecipesResponse getTop4Recipes(final Long memberId) {
final List<Recipe> recipes = recipeRepository.findRecipesByFavoriteCountGreaterThanEqual(RANKING_MINIMUM_FAVORITE_COUNT);

final List<RankingRecipeDto> dtos = recipes.stream()
.sorted(Comparator.comparing(Recipe::calculateRankingScore).reversed())
.limit(RANKING_SIZE)
.map(recipe -> {
final List<RecipeImage> findRecipeImages = recipeImageRepository.findByRecipe(recipe);
final RecipeAuthorDto author = RecipeAuthorDto.toDto(recipe.getMember());
return RankingRecipeDto.toDto(recipe, findRecipeImages, author);
})
.collect(Collectors.toList());
.map(recipe -> createRankingRecipeDto(memberId, recipe))
.toList();
return RankingRecipesResponse.toResponse(dtos);
}

private RankingRecipeDto createRankingRecipeDto(final Long memberId, final Recipe recipe) {
final List<RecipeImage> findRecipeImages = recipeImageRepository.findByRecipe(recipe);

if (memberId == GUEST_ID) {
return RankingRecipeDto.toDto(recipe, findRecipeImages, false);
}

final Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId));
final boolean favorite = recipeFavoriteRepository.existsByMemberAndRecipeAndFavoriteTrue(member, recipe);
return RankingRecipeDto.toDto(recipe, findRecipeImages, favorite);
}

@Transactional
public Long writeCommentOfRecipe(final Long memberId, final Long recipeId,
final RecipeCommentCreateRequest request) {
Expand Down
23 changes: 11 additions & 12 deletions src/main/java/com/funeat/recipe/dto/RankingRecipeDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ public class RankingRecipeDto {
private final Long id;
private final String image;
private final String title;
private final RecipeAuthorDto author;
private final Long favoriteCount;
private final String author;
private final Boolean favoriteCount;
70825 marked this conversation as resolved.
Show resolved Hide resolved
private final LocalDateTime createdAt;

public RankingRecipeDto(final Long id, final String image, final String title, final RecipeAuthorDto author,
final Long favoriteCount, final LocalDateTime createdAt) {
public RankingRecipeDto(final Long id, final String image, final String title, final String author,
final Boolean favoriteCount, final LocalDateTime createdAt) {
this.id = id;
this.image = image;
this.title = title;
Expand All @@ -24,14 +24,13 @@ public RankingRecipeDto(final Long id, final String image, final String title, f
this.createdAt = createdAt;
}

public static RankingRecipeDto toDto(final Recipe recipe, final List<RecipeImage> images,
final RecipeAuthorDto author) {
public static RankingRecipeDto toDto(final Recipe recipe, final List<RecipeImage> images, final Boolean favorite) {
if (images.isEmpty()) {
return new RankingRecipeDto(recipe.getId(), null, recipe.getTitle(), author,
recipe.getFavoriteCount(), recipe.getCreatedAt());
return new RankingRecipeDto(recipe.getId(), null, recipe.getTitle(),
recipe.getMember().getNickname(), favorite, recipe.getCreatedAt());
}
return new RankingRecipeDto(recipe.getId(), images.get(0).getImage(), recipe.getTitle(), author,
recipe.getFavoriteCount(), recipe.getCreatedAt());
return new RankingRecipeDto(recipe.getId(), images.get(0).getImage(), recipe.getTitle(),
recipe.getMember().getNickname(), favorite, recipe.getCreatedAt());
}

public Long getId() {
Expand All @@ -46,11 +45,11 @@ public String getTitle() {
return title;
}

public RecipeAuthorDto getAuthor() {
public String getAuthor() {
return author;
}

public Long getFavoriteCount() {
public Boolean getFavoriteCount() {
return favoriteCount;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
import com.funeat.recipe.dto.RecipeFavoriteRequest;
import com.funeat.recipe.dto.SearchRecipeResultsResponse;
import com.funeat.recipe.dto.SortingRecipesResponse;
import jakarta.validation.Valid;
import java.net.URI;
import java.util.List;
import jakarta.validation.Valid;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -78,8 +77,8 @@ public ResponseEntity<Void> likeRecipe(@AuthenticationPrincipal final LoginInfo
}

@GetMapping("/api/ranks/recipes")
public ResponseEntity<RankingRecipesResponse> getRankingRecipes() {
final RankingRecipesResponse response = recipeService.getTop3Recipes();
public ResponseEntity<RankingRecipesResponse> getRankingRecipes(@AuthenticationPrincipal final LoginInfo loginInfo) {
final RankingRecipesResponse response = recipeService.getTop4Recipes(loginInfo.getId());

return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ ResponseEntity<Void> likeRecipe(@AuthenticationPrincipal final LoginInfo loginIn
@PathVariable final Long recipeId,
@RequestBody final RecipeFavoriteRequest request);

@Operation(summary = "꿀조합 랭킹 조회", description = "전체 꿀조합들 중에서 랭킹 TOP3를 조회한다.")
@Operation(summary = "꿀조합 랭킹 조회", description = "전체 꿀조합들 중에서 랭킹 TOP4를 조회한다.")
@ApiResponse(
responseCode = "200",
description = "꿀조합 랭킹 조회 성공."
)
@GetMapping
ResponseEntity<RankingRecipesResponse> getRankingRecipes();
ResponseEntity<RankingRecipesResponse> getRankingRecipes(@AuthenticationPrincipal final LoginInfo loginInfo);

@Operation(summary = "꿀조합 검색 결과 조회", description = "검색어가 포함된 상품이 있는 꿀조합 목록을 조회한다.")
@ApiResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@
import static org.assertj.core.api.SoftAssertions.assertSoftly;

import com.funeat.acceptance.common.AcceptanceTest;
import com.funeat.member.domain.Member;
import com.funeat.recipe.domain.Recipe;
import com.funeat.recipe.dto.DetailProductRecipeDto;
import com.funeat.recipe.dto.RankingRecipeDto;
import com.funeat.recipe.dto.RecipeAuthorDto;
Expand All @@ -84,7 +82,6 @@
import io.restassured.response.Response;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -527,7 +524,7 @@ class getSortingRecipes_성공_테스트 {
class getRankingRecipes_성공_테스트 {

@Test
void 전체_꿀조합들_중에서_랭킹_TOP3를_조회할_수_있다() {
void 전체_꿀조합들_중에서_랭킹_TOP4를_조회할_수_있다() {
70825 marked this conversation as resolved.
Show resolved Hide resolved
// given
final var 카테고리 = 카테고리_간편식사_생성();
단일_카테고리_저장(카테고리);
Expand All @@ -537,16 +534,17 @@ class getRankingRecipes_성공_테스트 {
레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지2), 레시피추가요청_생성(상품));
레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지3), 레시피추가요청_생성(상품));
레시피_작성_요청(로그인_쿠키_획득(멤버1), 여러개_사진_명세_요청(이미지4), 레시피추가요청_생성(상품));
여러명이_레시피_좋아요_요청(List.of(멤버1), 레시피1, 좋아요O);
여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2), 레시피2, 좋아요O);
여러명이_레시피_좋아요_요청(List.of(멤버1), 레시피3, 좋아요O);
여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2), 레시피3, 좋아요O);
여러명이_레시피_좋아요_요청(List.of(멤버1, 멤버2, 멤버3), 레시피4, 좋아요O);

// when
final var 응답 = 레시피_랭킹_조회_요청();

// then
STATUS_CODE를_검증한다(응답, 정상_처리);
레시피_랭킹_조회_결과를_검증한다(응답, List.of(레시피4, 레시피2, 레시피3));
레시피_랭킹_조회_결과를_검증한다(응답, List.of(레시피4, 레시피2, 레시피3, 레시피1));
}
}

Expand Down Expand Up @@ -770,12 +768,6 @@ class getRecipeComment_실패_테스트 {
});
}

private List<RankingRecipeDto> 예상_레시피_랭킹_변환(final List<Recipe> recipes, final Member member) {
return recipes.stream()
.map(it -> RankingRecipeDto.toDto(it, Collections.emptyList(), RecipeAuthorDto.toDto(member)))
.collect(Collectors.toList());
}

private void 레시피_랭킹_조회_결과를_검증한다(final ExtractableResponse<Response> response, final List<Long> recipeIds) {
final var actual = response.jsonPath()
.getList("recipes", RankingRecipeDto.class);
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/com/funeat/common/ServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.funeat.comment.persistence.CommentRepository;
import com.funeat.member.application.TestMemberService;
import com.funeat.member.domain.Member;
import com.funeat.member.domain.favorite.RecipeFavorite;
import com.funeat.member.domain.favorite.ReviewFavorite;
import com.funeat.member.persistence.MemberRepository;
import com.funeat.member.persistence.RecipeFavoriteRepository;
Expand Down Expand Up @@ -206,6 +207,10 @@ public abstract class ServiceTest {
productRecipeRepository.saveAll(productRecipes);
}

protected void 단일_꿀조합_좋아요_저장(final RecipeFavorite recipeFavorite) {
recipeFavoriteRepository.save(recipeFavorite);
}

protected void 복수_배너_저장(final Banner... bannerToSave) {
final List<Banner> banners = List.of(bannerToSave);

Expand Down
Loading
Loading