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

[BE] feat: 꿀조합 작성 기능 구현 #349

Merged
merged 15 commits into from
Aug 9, 2023
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.funeat.review.application;
package com.funeat.common;

import org.springframework.web.multipart.MultipartFile;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.funeat.review.application;
package com.funeat.common;

import java.io.IOException;
import java.nio.file.Files;
Expand All @@ -13,7 +13,7 @@
@Profile("!test")
public class ImageUploader implements ImageService {

@Value("${review.image.path}")
@Value("${image.path}")
private String imagePath;

@Override
Expand Down
8 changes: 7 additions & 1 deletion backend/src/main/java/com/funeat/common/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.funeat.auth.util.AuthArgumentResolver;
import com.funeat.auth.util.AuthHandlerInterceptor;
import com.funeat.recipe.utill.RecipeHandlerInterceptor;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
Expand All @@ -15,20 +16,25 @@ public class WebConfig implements WebMvcConfigurer {
private final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver;
private final AuthArgumentResolver authArgumentResolver;
private final AuthHandlerInterceptor authHandlerInterceptor;
private final RecipeHandlerInterceptor recipeHandlerInterceptor;

public WebConfig(final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver,
final AuthArgumentResolver authArgumentResolver,
final AuthHandlerInterceptor authHandlerInterceptor) {
final AuthHandlerInterceptor authHandlerInterceptor,
final RecipeHandlerInterceptor recipeHandlerInterceptor) {
this.customPageableHandlerMethodArgumentResolver = customPageableHandlerMethodArgumentResolver;
this.authArgumentResolver = authArgumentResolver;
this.authHandlerInterceptor = authHandlerInterceptor;
this.recipeHandlerInterceptor = recipeHandlerInterceptor;
}

@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(authHandlerInterceptor)
.addPathPatterns("/api/products/**/reviews/**")
.addPathPatterns("/api/members");
registry.addInterceptor(recipeHandlerInterceptor)
.addPathPatterns("/api/recipes");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ public class ProductRecipe {
@ManyToOne
@JoinColumn(name = "recipe_id")
private Recipe recipe;

protected ProductRecipe() {
}

public ProductRecipe(final Product product, final Recipe recipe) {
this.product = product;
this.recipe = recipe;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.funeat.recipe.application;

import com.funeat.member.domain.Member;
import com.funeat.member.persistence.MemberRepository;
import com.funeat.product.domain.ProductRecipe;
import com.funeat.product.persistence.ProductRecipeRepository;
import com.funeat.product.persistence.ProductRepository;
import com.funeat.recipe.domain.Recipe;
import com.funeat.recipe.domain.RecipeImage;
import com.funeat.recipe.dto.RecipeCreateRequest;
import com.funeat.recipe.persistence.RecipeImageRepository;
import com.funeat.recipe.persistence.RecipeRepository;
import com.funeat.common.ImageService;
import java.util.List;
import java.util.Objects;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@Transactional(readOnly = true)
public class RecipeService {

private final MemberRepository memberRepository;
private final ProductRepository productRepository;
private final ProductRecipeRepository productRecipeRepository;
private final RecipeRepository recipeRepository;
private final RecipeImageRepository recipeImageRepository;

private final ImageService imageService;
hanueleee marked this conversation as resolved.
Show resolved Hide resolved

public RecipeService(final MemberRepository memberRepository, final ProductRepository productRepository,
final ProductRecipeRepository productRecipeRepository, final RecipeRepository recipeRepository,
final RecipeImageRepository recipeImageRepository,
final ImageService imageService) {
this.memberRepository = memberRepository;
this.productRepository = productRepository;
this.productRecipeRepository = productRecipeRepository;
this.recipeRepository = recipeRepository;
this.recipeImageRepository = recipeImageRepository;
this.imageService = imageService;
}

@Transactional
public Long create(final Long memberId, final List<MultipartFile> images, final RecipeCreateRequest request) {
final Member member = memberRepository.findById(memberId)
.orElseThrow(IllegalArgumentException::new);

final Recipe savedRecipe = recipeRepository.save(new Recipe(request.getName(), request.getContent(), member));
request.getProductIds()
.stream()
.map(it -> productRepository.findById(it)
.orElseThrow(IllegalArgumentException::new))
.forEach(it -> productRecipeRepository.save(new ProductRecipe(it, savedRecipe)));

if (Objects.nonNull(images)) {
images.stream()
.peek(it -> recipeImageRepository.save(new RecipeImage(it.getOriginalFilename(), savedRecipe)))
.forEach(imageService::upload);
}

return savedRecipe.getId();
}
}
33 changes: 22 additions & 11 deletions backend/src/main/java/com/funeat/recipe/domain/Recipe.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.funeat.recipe.domain;

import com.funeat.member.domain.Member;
import com.funeat.member.domain.bookmark.RecipeBookmark;
import com.funeat.member.domain.favorite.RecipeFavorite;
import com.funeat.product.domain.ProductRecipe;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

@Entity
public class Recipe {
Expand All @@ -28,12 +23,28 @@ public class Recipe {
@JoinColumn(name = "member_id")
private Member member;

@OneToMany(mappedBy = "recipe")
private List<ProductRecipe> productRecipes;
protected Recipe() {
}

@OneToMany(mappedBy = "recipe")
private List<RecipeFavorite> recipeFavorites;
public Recipe(final String name, final String content, final Member member) {
this.name = name;
this.content = content;
this.member = member;
}

@OneToMany(mappedBy = "recipe")
private List<RecipeBookmark> recipeBookmarks;
public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getContent() {
return content;
}

public Member getMember() {
return member;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ public class RecipeImage {
@ManyToOne
@JoinColumn(name = "recipe_id")
private Recipe recipe;

protected RecipeImage() {
}

public RecipeImage(final String image, final Recipe recipe) {
this.image = image;
this.recipe = recipe;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.funeat.recipe.dto;

import java.util.List;

public class RecipeCreateRequest {

private final String name;
private final List<Long> productIds;
private final String content;

public RecipeCreateRequest(final String name, final List<Long> productIds, final String content) {
this.name = name;
this.productIds = productIds;
this.content = content;
}

public String getName() {
return name;
}

public List<Long> getProductIds() {
return productIds;
}

public String getContent() {
return content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.funeat.recipe.presentation;

import com.funeat.auth.dto.LoginInfo;
import com.funeat.auth.util.AuthenticationPrincipal;
import com.funeat.recipe.application.RecipeService;
import com.funeat.recipe.dto.RecipeCreateRequest;
import java.net.URI;
import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class RecipeApiController implements RecipeController{

private final RecipeService recipeService;

public RecipeApiController(final RecipeService recipeService) {
this.recipeService = recipeService;
}

@PostMapping(value = "/api/recipes", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Void> writeRecipe(@AuthenticationPrincipal final LoginInfo loginInfo,
@RequestPart(required = false) final List<MultipartFile> images,
@RequestPart final RecipeCreateRequest recipeRequest) {
final Long recipeId = recipeService.create(loginInfo.getId(), images, recipeRequest);

return ResponseEntity.created(URI.create("/api/recipes/" + recipeId)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.funeat.recipe.presentation;

import com.funeat.auth.dto.LoginInfo;
import com.funeat.auth.util.AuthenticationPrincipal;
import com.funeat.recipe.dto.RecipeCreateRequest;
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.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

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

@Operation(summary = "꿀조합 추가", description = "꿀조합을 작성한다.")
@ApiResponse(
responseCode = "201",
description = "꿀조합 작성 성공."
)
@PostMapping
ResponseEntity<Void> writeRecipe(@AuthenticationPrincipal LoginInfo loginInfo,
@RequestPart List<MultipartFile> images,
@RequestPart RecipeCreateRequest recipeRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.funeat.recipe.utill;

import com.funeat.auth.exception.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class RecipeHandlerInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if ("GET".equals(request.getMethod())) {
return true;
}
final HttpSession session = request.getSession();
if (session.getAttribute("member") == null) {
throw new LoginException("login error");
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.funeat.review.application;

import com.funeat.common.ImageService;
import com.funeat.member.domain.Member;
import com.funeat.member.domain.favorite.ReviewFavorite;
import com.funeat.member.persistence.MemberRepository;
Expand Down Expand Up @@ -37,21 +38,20 @@ public class ReviewService {
private final ReviewTagRepository reviewTagRepository;
private final MemberRepository memberRepository;
private final ProductRepository productRepository;
private final ImageService imageService;

private final ReviewFavoriteRepository reviewFavoriteRepository;
private final ImageService imageService;

public ReviewService(final ReviewRepository reviewRepository, final TagRepository tagRepository,
final ReviewTagRepository reviewTagRepository, final MemberRepository memberRepository,
final ProductRepository productRepository, final ImageService imageService,
final ReviewFavoriteRepository reviewFavoriteRepository) {
final ProductRepository productRepository,
final ReviewFavoriteRepository reviewFavoriteRepository, final ImageService imageService) {
this.reviewRepository = reviewRepository;
this.tagRepository = tagRepository;
this.reviewTagRepository = reviewTagRepository;
this.memberRepository = memberRepository;
this.productRepository = productRepository;
this.imageService = imageService;
this.reviewFavoriteRepository = reviewFavoriteRepository;
this.imageService = imageService;
}

@Transactional
Expand Down
5 changes: 2 additions & 3 deletions backend/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ spring:
format_sql: true
show_sql: true

review:
image:
path: { DEV_IMAGE_PATH }
image:
path: { DEV_IMAGE_PATH }
5 changes: 2 additions & 3 deletions backend/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ logging:
level:
org.hibernate.type.descriptor.sql: trace

review:
image:
path:
image:
path:
5 changes: 2 additions & 3 deletions backend/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ spring:
hibernate:
show_sql: true

review:
image:
path: { PROD_IMAGE_PATH }
image:
path: { PROD_IMAGE_PATH }
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ class FuneatApplicationTests {
@Test
void contextLoads() {
}

}
Loading
Loading