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] Product API 개발 #42

Merged
merged 6 commits into from
Aug 13, 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
30 changes: 19 additions & 11 deletions src/main/java/com/kakaoteck/golagola/domain/buyer/entity/Buyer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kakaoteck.golagola.domain.buyer.dto.BuyerRequest;
import com.kakaoteck.golagola.domain.cart.entity.Cart;
import com.kakaoteck.golagola.domain.cart.entity.CartProduct;
import com.kakaoteck.golagola.domain.order.entity.Order;
import com.kakaoteck.golagola.domain.product.entity.Product;
import com.kakaoteck.golagola.domain.review.entity.Review;
Expand All @@ -25,8 +26,8 @@
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Builder
@Table(name = "buyer_table")
public class Buyer extends BaseEntity implements UserDetails {

Expand Down Expand Up @@ -63,7 +64,7 @@ public class Buyer extends BaseEntity implements UserDetails {
@Column(nullable = false)
private LocalDate registerDate;

@OneToOne(mappedBy = "buyer", cascade = CascadeType.ALL)
@OneToOne(mappedBy = "buyer", cascade = CascadeType.ALL, orphanRemoval = true)
private Cart cart;

@OneToMany(mappedBy = "buyer", cascade = CascadeType.ALL)
Expand Down Expand Up @@ -114,28 +115,36 @@ public void updateProfile(BuyerRequest.MyPagePutDto request) {
}

public void assignCart(Cart cart) {
if (cart != null) {
this.cart = cart;
this.cart = cart;
if (cart.getBuyer() != this) {
cart.assignBuyer(this);
}
}

public void addProductToCart(Product product) {
// Cart를 자동으로 초기화
@PrePersist
public void initializeCart() {
if (this.cart == null) {
this.cart = new Cart(); // 새로운 Cart 객체 생성
this.cart.assignBuyer(this); // Cart와 Buyer 간의 양방향 연관관계 설정
Cart newCart = new Cart();
newCart.assignBuyer(this);
this.cart = newCart;
}
this.cart.getProductList().add(product);
}

public Cart getOrCreateCart() {
if (this.cart == null) {
this.cart = new Cart(); // 새로운 Cart 객체 생성
this.cart.assignBuyer(this); // Cart와 Buyer 간의 양방향 연관관계 설정
Cart newCart = new Cart();
newCart.assignBuyer(this);
this.cart = newCart;
}
return this.cart;
}

public void addProductToCart(Product product) {
Cart cart = this.getOrCreateCart();
cart.addProduct(product);
}

public static Buyer from(Long buyerId, String nickname, String realName, Gender gender, String email, String password,
String address, String phoneNum, Role role, LocalDate registerDate) {
return Buyer.builder()
Expand All @@ -152,4 +161,3 @@ public static Buyer from(Long buyerId, String nickname, String realName, Gender
.build();
}
}

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.kakaoteck.golagola.domain.cart.dto;

import com.kakaoteck.golagola.domain.product.dto.ProductResponse;
import com.kakaoteck.golagola.domain.product.entity.Product;
import lombok.Builder;

import java.util.List;

@Builder
public record CartResponse(
List<Product> productList
List<ProductResponse.ProductDto> productList
) {

}
15 changes: 11 additions & 4 deletions src/main/java/com/kakaoteck/golagola/domain/cart/entity/Cart.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Entity
Expand All @@ -22,18 +23,24 @@ public class Cart {
@JoinColumn(name = "buyer_id")
private Buyer buyer;

@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
private List<Product> productList;
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private List<CartProduct> cartProducts = new ArrayList<>();

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

public void assignBuyer(Buyer buyer) {
if (buyer != null) {
this.buyer = buyer;
this.buyer = buyer;
if (buyer.getCart() != this) {
buyer.assignCart(this);
}
}

public void addProduct(Product product) {
CartProduct cartProduct = new CartProduct();
cartProduct.assignCart(this);
cartProduct.assignProduct(product);
this.cartProducts.add(cartProduct);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.kakaoteck.golagola.domain.cart.entity;

import com.kakaoteck.golagola.domain.product.entity.Product;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
@Table(name = "cart_product_table")
public class CartProduct {

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "cart_id")
private Cart cart;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "product_id")
private Product product;

public void assignCart(Cart cart) {
this.cart = cart;
if (!cart.getCartProducts().contains(this)) {
cart.getCartProducts().add(this);
}
}

public void assignProduct(Product product) {
this.product = product;
if (!product.getCartProducts().contains(this)) {
product.getCartProducts().add(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.kakaoteck.golagola.domain.cart.repository;

import com.kakaoteck.golagola.domain.cart.entity.Cart;
import com.kakaoteck.golagola.domain.cart.entity.CartProduct;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CartProductRepository extends JpaRepository<CartProduct, Long> {
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package com.kakaoteck.golagola.domain.cart.service;

import com.kakaoteck.golagola.domain.buyer.dto.BuyerResponse;
import com.kakaoteck.golagola.domain.buyer.entity.Buyer;
import com.kakaoteck.golagola.domain.cart.dto.CartRequest;
import com.kakaoteck.golagola.domain.cart.dto.CartResponse;
import com.kakaoteck.golagola.domain.cart.entity.Cart;
import com.kakaoteck.golagola.domain.cart.entity.CartProduct;
import com.kakaoteck.golagola.domain.cart.repository.CartProductRepository;
import com.kakaoteck.golagola.domain.product.dto.ProductResponse;
import com.kakaoteck.golagola.domain.product.entity.Product;
import com.kakaoteck.golagola.domain.product.repository.ProductRepository;
import com.kakaoteck.golagola.global.common.code.status.ErrorStatus;
import com.kakaoteck.golagola.global.common.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Hibernate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
Expand All @@ -24,58 +26,78 @@
public class CartService {

private final ProductRepository productRepository;
private final CartProductRepository cartProductRepository;

public CartResponse getCartProducts(Buyer buyer) {
// Cart가 null이 아닌 경우에만 productList를 가져오고, 그렇지 않으면 빈 리스트를 반환
List<Product> productList = Collections.emptyList();
if (buyer.getCart() != null) {
productList = buyer.getCart().getProductList() != null ? buyer.getCart().getProductList() : Collections.emptyList();
}
Cart cart = buyer.getCart();

// Lazy 로딩을 명시적으로 초기화
Hibernate.initialize(cart.getCartProducts());

// CartProduct 객체에서 Product를 추출하여 리스트 생성
List<ProductResponse.ProductDto> products = cart.getCartProducts().stream()
.map(cartProduct -> {
Product product = cartProduct.getProduct();
return ProductResponse.ProductDto.builder()
.productId(product.getProductId())
.productName(product.getProductName())
.productExplanation(product.getProductExplanation())
.productPrice(product.getProductPrice())
.build();
})
.collect(Collectors.toList());

return CartResponse.builder()
.productList(productList)
.productList(products)
.build();
}

public CartResponse addCartProduct(Buyer buyer, Long productId) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new GeneralException(ErrorStatus._NOT_FOUND_PRODUCT));

// Buyer의 cart에 product를 추가
buyer.addProductToCart(product);
Cart cart = buyer.getOrCreateCart();

return CartResponse.builder()
.productList(buyer.getCart().getProductList())
// 장바구니에 이미 동일한 상품이 있는지 확인
boolean productExistsInCart = cart.getCartProducts().stream()
.anyMatch(cp -> cp.getProduct().getProductId().equals(productId));

if (productExistsInCart) {
throw new GeneralException(ErrorStatus._PRODUCT_ALREADY_IN_CART); // 이미 존재한다면 예외를 던집니다.
}

// 장바구니에 상품 추가
CartProduct cartProduct = CartProduct.builder()
.cart(cart)
.product(product)
.build();

cartProductRepository.save(cartProduct);

return getCartProducts(buyer);
}

public CartResponse deleteCartProduct(Buyer buyer, Long productId) {
if (buyer.getCart() == null || buyer.getCart().getProductList() == null) {
throw new GeneralException(ErrorStatus._NOT_FOUND_PRODUCT_IN_CART);
}
Cart cart = buyer.getCart();

// Buyer의 cart에서 해당 productId에 해당하는 제품을 찾음
List<Product> productList = buyer.getCart().getProductList();
Product productToRemove = productList.stream()
.filter(product -> product.getProductId().equals(productId))
CartProduct cartProduct = cart.getCartProducts().stream()
.filter(cp -> cp.getProduct().getProductId().equals(productId))
.findFirst()
.orElseThrow(() -> new GeneralException(ErrorStatus._NOT_FOUND_PRODUCT_IN_CART));

// 제품을 장바구니에서 삭제
productList.remove(productToRemove);
cartProductRepository.delete(cartProduct);

return CartResponse.builder()
.productList(productList)
.build();
return getCartProducts(buyer);
}

public String emptyCart(Buyer buyer) {
if (buyer.getCart() == null || buyer.getCart().getProductList() == null) {
Cart cart = buyer.getCart();

if (cart.getCartProducts().isEmpty()) {
throw new GeneralException(ErrorStatus._CART_IS_ALREADY_EMPTY);
}

// 장바구니를 비움
buyer.getCart().getProductList().clear();
cartProductRepository.deleteAll(cart.getCartProducts());

return "장바구니에 있는 모든 제품이 삭제되었습니다.";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.kakaoteck.golagola.domain.product.controller;

import com.kakaoteck.golagola.domain.product.dto.ProductRequest;
import com.kakaoteck.golagola.domain.product.dto.ProductResponse;
import com.kakaoteck.golagola.domain.product.service.ProductService;
import com.kakaoteck.golagola.domain.seller.dto.SellerResponse;
import com.kakaoteck.golagola.domain.seller.entity.Seller;
import com.kakaoteck.golagola.global.common.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/product")
@CrossOrigin("*")
public class ProductController {

private final ProductService productService;

@Operation(summary = "상품 등록", description = "상품을 등록합니다. 판매자 전용")
@PostMapping()
public ApiResponse<String> postProduct(
@AuthenticationPrincipal Seller seller,
@RequestBody ProductRequest request
) {
return ApiResponse.onSuccess(productService.postProduct(seller, request));
}

@Operation(summary = "상품 수정", description = "상품 정보를 수정합니다.")
@PutMapping("/{productId}")
public ApiResponse<ProductResponse> modifyProduct(
@AuthenticationPrincipal Seller seller,
@PathVariable Long productId,
@RequestBody ProductRequest request
) {
return ApiResponse.onSuccess(productService.modifyProduct(seller, productId, request));
}

@Operation(summary = "상품 삭제", description = "상품을 삭제합니다. 판매자 전용")
@DeleteMapping("/{productId}")
public ApiResponse<String> deleteProduct(
@AuthenticationPrincipal Seller seller,
@PathVariable Long productId
) {
productService.deleteProduct(seller, productId);
return ApiResponse.onSuccess("상품이 성공적으로 삭제되었습니다.");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.kakaoteck.golagola.domain.product.dto;

import com.kakaoteck.golagola.global.common.enums.Category;
import com.kakaoteck.golagola.global.common.enums.DetailCategory;
import lombok.Builder;

@Builder
public record ProductRequest(
String productName,
String productExplanation,
String productImage,
Long productPrice,
Long productInventory,
Category category,
DetailCategory detailCategory,
Long discount,
Long productQuantity
) {
}
Loading