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

feat: 꿀조합 저장 기능 추가 #67

Merged
merged 4 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/com/funeat/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.funeat.member.exception.MemberErrorCode.MEMBER_UPDATE_ERROR;

import com.funeat.member.domain.bookmark.RecipeBookmark;
import com.funeat.member.domain.favorite.RecipeFavorite;
import com.funeat.member.domain.favorite.ReviewFavorite;
import com.funeat.member.exception.MemberException.MemberUpdateException;
Expand Down Expand Up @@ -34,6 +35,9 @@ public class Member {
@OneToMany(mappedBy = "member")
private List<RecipeFavorite> recipeFavorites;

@OneToMany(mappedBy = "member")
private List<RecipeBookmark> recipeBookmarks;

protected Member() {
}

Expand Down Expand Up @@ -71,6 +75,10 @@ public List<RecipeFavorite> getRecipeFavorites() {
return recipeFavorites;
}

public List<RecipeBookmark> getRecipeBookmarks() {
return recipeBookmarks;
}

public void modifyProfile(final String nickname, final String profileImage) {
if (!StringUtils.hasText(nickname) || Objects.isNull(profileImage)) {
throw new MemberUpdateException(MEMBER_UPDATE_ERROR);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.funeat.member.domain.bookmark;

import com.funeat.member.domain.Member;
import com.funeat.recipe.domain.Recipe;
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 jakarta.persistence.UniqueConstraint;

@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "recipe_id"}))
public class RecipeBookmark {

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "recipe_id")
private Recipe recipe;

private Boolean bookmark;

protected RecipeBookmark() {
}

public RecipeBookmark(final Member member, final Recipe recipe, final Boolean bookmark) {
this.member = member;
this.recipe = recipe;
this.bookmark = bookmark;
}

public static RecipeBookmark create(final Member member, final Recipe recipe, final Boolean bookmark) {
return new RecipeBookmark(member, recipe, bookmark);
}

public void updateBookmark(final Boolean bookmark) {
this.bookmark = bookmark;
}

public Long getId() {
return id;
}

public Member getMember() {
return member;
}

public Recipe getRecipe() {
return recipe;
}

public Boolean getBookmark() {
return bookmark;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum MemberErrorCode {
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다. 회원 id를 확인하세요.", "5001"),
MEMBER_UPDATE_ERROR(HttpStatus.BAD_REQUEST, "닉네임 또는 이미지를 확인하세요.", "5002"),
MEMBER_DUPLICATE_FAVORITE(HttpStatus.CONFLICT, "이미 좋아요를 누른 상태입니다.", "5003"),
MEMBER_DUPLICATE_BOOKMARK(HttpStatus.CONFLICT, "이미 북마크를 누른 상태입니다.", "5004"),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ public MemberDuplicateFavoriteException(final MemberErrorCode errorCode, final L
super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), memberId));
}
}

public static class MemberDuplicateBookmarkException extends MemberException {
public MemberDuplicateBookmarkException(final MemberErrorCode errorCode, final Long memberId) {
super(errorCode.getStatus(), new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), memberId));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.funeat.member.persistence;

import com.funeat.member.domain.Member;
import com.funeat.member.domain.bookmark.RecipeBookmark;
import com.funeat.recipe.domain.Recipe;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface RecipeBookmarkRepository extends JpaRepository<RecipeBookmark, Long> {

Optional<RecipeBookmark> findByMemberAndRecipe(final Member member, final Recipe recipe);
}
31 changes: 31 additions & 0 deletions src/main/java/com/funeat/recipe/application/RecipeService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.funeat.recipe.application;

import static com.funeat.member.exception.MemberErrorCode.MEMBER_DUPLICATE_BOOKMARK;
import static com.funeat.member.exception.MemberErrorCode.MEMBER_DUPLICATE_FAVORITE;
import static com.funeat.member.exception.MemberErrorCode.MEMBER_NOT_FOUND;
import static com.funeat.product.exception.ProductErrorCode.PRODUCT_NOT_FOUND;
Expand All @@ -11,12 +12,15 @@
import com.funeat.common.ImageUploader;
import com.funeat.common.dto.PageDto;
import com.funeat.member.domain.Member;
import com.funeat.member.domain.bookmark.RecipeBookmark;
import com.funeat.member.domain.favorite.RecipeFavorite;
import com.funeat.member.dto.MemberRecipeDto;
import com.funeat.member.dto.MemberRecipesResponse;
import com.funeat.member.exception.MemberException.MemberDuplicateBookmarkException;
import com.funeat.member.exception.MemberException.MemberDuplicateFavoriteException;
import com.funeat.member.exception.MemberException.MemberNotFoundException;
import com.funeat.member.persistence.MemberRepository;
import com.funeat.member.persistence.RecipeBookmarkRepository;
import com.funeat.member.persistence.RecipeFavoriteRepository;
import com.funeat.product.domain.Product;
import com.funeat.product.domain.ProductRecipe;
Expand All @@ -28,6 +32,7 @@
import com.funeat.recipe.dto.RankingRecipeDto;
import com.funeat.recipe.dto.RankingRecipesResponse;
import com.funeat.recipe.dto.RecipeAuthorDto;
import com.funeat.recipe.dto.RecipeBookmarkRequest;
import com.funeat.recipe.dto.RecipeCommentCondition;
import com.funeat.recipe.dto.RecipeCommentCreateRequest;
import com.funeat.recipe.dto.RecipeCommentResponse;
Expand Down Expand Up @@ -74,20 +79,23 @@ public class RecipeService {
private final RecipeRepository recipeRepository;
private final RecipeImageRepository recipeImageRepository;
private final RecipeFavoriteRepository recipeFavoriteRepository;
private final RecipeBookmarkRepository recipeBookmarkRepository;
private final CommentRepository commentRepository;
private final ImageUploader imageUploader;

public RecipeService(final MemberRepository memberRepository, final ProductRepository productRepository,
final ProductRecipeRepository productRecipeRepository, final RecipeRepository recipeRepository,
final RecipeImageRepository recipeImageRepository,
final RecipeFavoriteRepository recipeFavoriteRepository,
final RecipeBookmarkRepository recipeBookmarkRepository,
final CommentRepository commentRepository, final ImageUploader imageUploader) {
this.memberRepository = memberRepository;
this.productRepository = productRepository;
this.productRecipeRepository = productRecipeRepository;
this.recipeRepository = recipeRepository;
this.recipeImageRepository = recipeImageRepository;
this.recipeFavoriteRepository = recipeFavoriteRepository;
this.recipeBookmarkRepository = recipeBookmarkRepository;
this.commentRepository = commentRepository;
this.imageUploader = imageUploader;
}
Expand Down Expand Up @@ -196,6 +204,29 @@ private RecipeFavorite createAndSaveRecipeFavorite(final Member member, final Re
}
}

@Transactional
public void bookmarkRecipe(final Long memberId, final Long recipeId, final RecipeBookmarkRequest request) {
final Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND, memberId));
final Recipe recipe = recipeRepository.findByIdForUpdate(recipeId)
.orElseThrow(() -> new RecipeNotFoundException(RECIPE_NOT_FOUND, recipeId));

final RecipeBookmark recipeBookmark = recipeBookmarkRepository.findByMemberAndRecipe(member, recipe)
.orElseGet(() -> createAndSaveRecipeBookmark(member, recipe, request.bookmark()));

recipeBookmark.updateBookmark(request.bookmark());
}

private RecipeBookmark createAndSaveRecipeBookmark(final Member member, final Recipe recipe,
final Boolean bookmark) {
try {
final RecipeBookmark recipeBookmark = RecipeBookmark.create(member, recipe, bookmark);
return recipeBookmarkRepository.save(recipeBookmark);
} catch (final DataIntegrityViolationException e) {
throw new MemberDuplicateBookmarkException(MEMBER_DUPLICATE_BOOKMARK, member.getId());
}
}

public SearchRecipeResultsResponse getSearchResults(final String query, final Long lastRecipeId) {
final List<Recipe> findRecipes = findAllByProductNameContaining(query, lastRecipeId);
final int resultSize = getResultSize(findRecipes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.funeat.recipe.dto;

import jakarta.validation.constraints.NotNull;

public record RecipeBookmarkRequest (
@NotNull(message = "북마크를 확인해주세요")
Boolean bookmark
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.funeat.common.logging.Logging;
import com.funeat.recipe.application.RecipeService;
import com.funeat.recipe.dto.RankingRecipesResponse;
import com.funeat.recipe.dto.RecipeBookmarkRequest;
import com.funeat.recipe.dto.RecipeCommentCondition;
import com.funeat.recipe.dto.RecipeCommentCreateRequest;
import com.funeat.recipe.dto.RecipeCommentsResponse;
Expand Down Expand Up @@ -77,6 +78,16 @@ public ResponseEntity<Void> likeRecipe(@AuthenticationPrincipal final LoginInfo
return ResponseEntity.noContent().build();
}

@Logging
@PatchMapping(value = "/api/recipes/{recipeId}/bookmark")
public ResponseEntity<Void> bookmarkRecipe(@AuthenticationPrincipal final LoginInfo loginInfo,
@PathVariable final Long recipeId,
@RequestBody @Valid final RecipeBookmarkRequest request) {
recipeService.bookmarkRecipe(loginInfo.getId(), recipeId, request);

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

@GetMapping("/api/ranks/recipes")
public ResponseEntity<RankingRecipesResponse> getRankingRecipes(@AuthenticationPrincipal final LoginInfo loginInfo) {
final RankingRecipesResponse response = recipeService.getTop4Recipes(loginInfo.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.funeat.auth.dto.LoginInfo;
import com.funeat.auth.util.AuthenticationPrincipal;
import com.funeat.recipe.dto.RankingRecipesResponse;
import com.funeat.recipe.dto.RecipeBookmarkRequest;
import com.funeat.recipe.dto.RecipeCommentCondition;
import com.funeat.recipe.dto.RecipeCommentCreateRequest;
import com.funeat.recipe.dto.RecipeCommentsResponse;
Expand All @@ -14,7 +15,6 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
Expand All @@ -28,6 +28,8 @@
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Tag(name = "07.Recipe", description = "꿀조합 관련 API 입니다.")
public interface RecipeController {

Expand Down Expand Up @@ -69,6 +71,16 @@ ResponseEntity<Void> likeRecipe(@AuthenticationPrincipal final LoginInfo loginIn
@PathVariable final Long recipeId,
@RequestBody final RecipeFavoriteRequest request);

@Operation(summary = "꿀조합 저장", description = "꿀조합을 저장 또는 취소를 한다.")
@ApiResponse(
responseCode = "204",
description = "꿀조합 저장(또는 저장 취소) 성공."
)
@PatchMapping
ResponseEntity<Void> bookmarkRecipe(@AuthenticationPrincipal final LoginInfo loginInfo,
@PathVariable final Long recipeId,
@RequestBody final RecipeBookmarkRequest request);

@Operation(summary = "꿀조합 랭킹 조회", description = "전체 꿀조합들 중에서 랭킹 TOP4를 조회한다.")
@ApiResponse(
responseCode = "200",
Expand Down
Loading