Skip to content

Commit

Permalink
Merge pull request #21 from marinesnow34/cart
Browse files Browse the repository at this point in the history
Feat: 장바구니 추가 구현
  • Loading branch information
marinesnow34 authored Nov 17, 2023
2 parents 4c516af + dabacc0 commit 937fa5b
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 5 deletions.
12 changes: 12 additions & 0 deletions src/main/java/com/readyvery/readyverydemo/domain/Cart.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -26,6 +27,7 @@
@Table(name = "CART")
@AllArgsConstructor
@Slf4j
@Builder
public class Cart extends BaseTimeEntity {

@Id
Expand All @@ -34,6 +36,7 @@ public class Cart extends BaseTimeEntity {
private Long id;

// 장바구니 유저 연관관계 매핑

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_idx")
private UserInfo userInfo;
Expand All @@ -43,7 +46,16 @@ public class Cart extends BaseTimeEntity {
@JoinColumn(name = "store_idx")
private Store store;

@Column
@Builder.Default
private Boolean isOrdered = false;

@Column
@Builder.Default
private Boolean isDeleted = false;

// 장바구니 장바구니 아이템 연관관계 매핑
@Builder.Default
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
private List<CartItem> cartItems = new ArrayList<CartItem>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -26,6 +27,7 @@
@Table(name = "CART_ITEM")
@AllArgsConstructor
@Slf4j
@Builder
public class CartItem extends BaseTimeEntity {

@Id
Expand All @@ -48,6 +50,7 @@ public class CartItem extends BaseTimeEntity {
private Cart cart;

// 장바구니 아이템 - 장바구니 옵션 연관관계 매핑
@Builder.Default
@OneToMany(mappedBy = "cartItem", cascade = CascadeType.ALL)
private List<CartOption> cartOptions = new ArrayList<CartOption>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -21,6 +22,7 @@
@Table(name = "CART_OPTION")
@AllArgsConstructor
@Slf4j
@Builder
public class CartOption extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.readyvery.readyverydemo.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.readyvery.readyverydemo.domain.CartItem;

public interface CartItemRepository extends JpaRepository<CartItem, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.readyvery.readyverydemo.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.readyvery.readyverydemo.domain.CartOption;

public interface CartOptionRepository extends JpaRepository<CartOption, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.readyvery.readyverydemo.domain.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.readyvery.readyverydemo.domain.Cart;
import com.readyvery.readyverydemo.domain.UserInfo;

public interface CartRepository extends JpaRepository<Cart, Long> {
Optional<Cart> findByUserInfoAndIsDeletedFalse(UserInfo userInfo);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.readyvery.readyverydemo.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.readyvery.readyverydemo.domain.FoodieOption;

public interface FoodieOptionRepository extends JpaRepository<FoodieOption, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
@Getter
public enum ExceptionCode {
STORE_NOT_FOUND(404, "Store does not exists."),

USER_NOT_FOUND(404, "User does not exists."),

FOODY_NOT_FOUND(404, "Foody does not exists.");

FOODY_NOT_FOUND(404, "Foody does not exists."),
FOODY_NOT_IN_STORE(400, "Foody does not exists in store."),
INVALID_OPTION_COUNT(400, "Invalid option count."),
INVALID_OPTION(400, "Invalid option."),
OPTION_NOT_FOUND(404, "Option does not exists.");

private int status;
private String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.readyvery.readyverydemo.security.jwt.dto.CustomUserDetails;
import com.readyvery.readyverydemo.src.order.dto.CartAddReq;
import com.readyvery.readyverydemo.src.order.dto.CartAddRes;
import com.readyvery.readyverydemo.src.order.dto.FoodyDetailRes;

import lombok.RequiredArgsConstructor;
Expand All @@ -26,4 +32,11 @@ public ResponseEntity<FoodyDetailRes> getFoody(
FoodyDetailRes foodyDetailRes = orderService.getFoody(storeId, foodyId, inout);
return new ResponseEntity<>(foodyDetailRes, HttpStatus.OK);
}

@PostMapping("/cart")
public ResponseEntity<CartAddRes> addCart(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody CartAddReq cartAddReq) {
CartAddRes cartAddRes = orderService.addCart(userDetails, cartAddReq);
return new ResponseEntity<>(cartAddRes, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.readyvery.readyverydemo.src.order;

import com.readyvery.readyverydemo.security.jwt.dto.CustomUserDetails;
import com.readyvery.readyverydemo.src.order.dto.CartAddReq;
import com.readyvery.readyverydemo.src.order.dto.CartAddRes;
import com.readyvery.readyverydemo.src.order.dto.FoodyDetailRes;

public interface OrderService {
FoodyDetailRes getFoody(Long storeId, Long foodyId, Long inout);

CartAddRes addCart(CustomUserDetails userDetails, CartAddReq cartAddReq);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
package com.readyvery.readyverydemo.src.order;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

import com.readyvery.readyverydemo.domain.Cart;
import com.readyvery.readyverydemo.domain.CartItem;
import com.readyvery.readyverydemo.domain.CartOption;
import com.readyvery.readyverydemo.domain.Foodie;
import com.readyvery.readyverydemo.domain.FoodieOption;
import com.readyvery.readyverydemo.domain.FoodieOptionCategory;
import com.readyvery.readyverydemo.domain.Store;
import com.readyvery.readyverydemo.domain.UserInfo;
import com.readyvery.readyverydemo.domain.repository.CartItemRepository;
import com.readyvery.readyverydemo.domain.repository.CartOptionRepository;
import com.readyvery.readyverydemo.domain.repository.CartRepository;
import com.readyvery.readyverydemo.domain.repository.FoodieOptionRepository;
import com.readyvery.readyverydemo.domain.repository.FoodieRepository;
import com.readyvery.readyverydemo.domain.repository.StoreRepository;
import com.readyvery.readyverydemo.domain.repository.UserRepository;
import com.readyvery.readyverydemo.global.exception.BusinessLogicException;
import com.readyvery.readyverydemo.global.exception.ExceptionCode;
import com.readyvery.readyverydemo.security.jwt.dto.CustomUserDetails;
import com.readyvery.readyverydemo.src.order.dto.CartAddReq;
import com.readyvery.readyverydemo.src.order.dto.CartAddRes;
import com.readyvery.readyverydemo.src.order.dto.FoodyDetailRes;
import com.readyvery.readyverydemo.src.order.dto.OrderMapper;

Expand All @@ -14,7 +34,13 @@
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final CartRepository cartRepository;
private final CartItemRepository cartItemRepository;
private final CartOptionRepository cartOptionRepository;
private final FoodieRepository foodieRepository;
private final FoodieOptionRepository foodieOptionRepository;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
private final OrderMapper orderMapper;

@Override
Expand All @@ -24,4 +50,115 @@ public FoodyDetailRes getFoody(Long storeId, Long foodyId, Long inout) {
);
return orderMapper.foodieToFoodyDetailRes(foodie, inout);
}

@Override
public CartAddRes addCart(CustomUserDetails userDetails, CartAddReq cartAddReq) {
UserInfo user = getUserInfo(userDetails);
Store store = getStore(cartAddReq.getStoreId());
Foodie foodie = getFoody(cartAddReq.getFoodieId());

verifyFoodieInStore(store, foodie);
verifyCartAddReq(foodie, cartAddReq);

Cart cart = cartRepository.findByUserInfoAndIsDeletedFalse(user).orElseGet(() -> makeCart(user, store));
CartItem cartItem = makeCartItem(cart, foodie, cartAddReq.getCount());
List<CartOption> cartOptions = cartAddReq.getOptions().stream()
.map(option -> makeCartOption(cartItem, option))
.toList();

cartRepository.save(cart);
cartItemRepository.save(cartItem);
cartOptionRepository.saveAll(cartOptions);

return orderMapper.cartToCartAddRes(cartItem);
}

private CartOption makeCartOption(CartItem cartItem, Long option) {
FoodieOption foodieOption = getFoodieOption(option);
return CartOption.builder()
.cartItem(cartItem)
.foodieOption(foodieOption)
.build();
}

private FoodieOption getFoodieOption(Long option) {
return foodieOptionRepository.findById(option).orElseThrow(
() -> new BusinessLogicException(ExceptionCode.OPTION_NOT_FOUND)
);
}

private CartItem makeCartItem(Cart cart, Foodie foodie, Long count) {
return CartItem.builder()
.cart(cart)
.foodie(foodie)
.count(count)
.build();
}

private Cart makeCart(UserInfo user, Store store) {
return Cart.builder()
.userInfo(user)
.store(store)
.build();
}

private UserInfo getUserInfo(CustomUserDetails userDetails) {
return userRepository.findById(userDetails.getId()).orElseThrow(
() -> new BusinessLogicException(ExceptionCode.USER_NOT_FOUND)
);
}

private Store getStore(Long storeId) {
return storeRepository.findById(storeId).orElseThrow(
() -> new BusinessLogicException(ExceptionCode.STORE_NOT_FOUND)
);
}

private void verifyCartAddReq(Foodie foodie, CartAddReq cartAddReq) {
verifyOption(foodie, cartAddReq.getOptions());
verifyEssentialOption(foodie, cartAddReq.getOptions());
}

private Foodie getFoody(Long foodieId) {
return foodieRepository.findById(foodieId).orElseThrow(
() -> new BusinessLogicException(ExceptionCode.FOODY_NOT_FOUND)
);
}

private void verifyFoodieInStore(Store store, Foodie foodie) {
boolean isFoodieInStore = foodie.getFoodieCategory().getStore().equals(store);
if (!isFoodieInStore) {
throw new BusinessLogicException(ExceptionCode.FOODY_NOT_IN_STORE);
}
}

private void verifyOption(Foodie foodie, List<Long> opotions) {
// foodie안에 있는 옵션들을 Set으로 만들기
Set<Long> optionSet = foodie.getFoodieOptionCategory().stream()
.flatMap(foodieOptionCategory -> foodieOptionCategory.getFoodieOptions().stream())
.map(FoodieOption::getId)
.collect(Collectors.toSet());

// 옵션들이 올바른지 확인
opotions.stream()
.filter(option -> !optionSet.contains(option))
.findAny()
.ifPresent(option -> {
throw new BusinessLogicException(ExceptionCode.INVALID_OPTION);
});
}

private void verifyEssentialOption(Foodie foodie, List<Long> options) {
foodie.getFoodieOptionCategory().stream()
.filter(FoodieOptionCategory::isRequired)
.collect(Collectors.toSet()).stream()
.filter(foodieOptionCategory -> foodieOptionCategory.getFoodieOptions().stream()
.map(FoodieOption::getId)
.filter(options::contains)
.count() != 1) // 필수 값이 1개가 아닌 경우
.findAny()
.ifPresent(foodieOptionCategory -> {
throw new BusinessLogicException(ExceptionCode.INVALID_OPTION_COUNT);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.readyvery.readyverydemo.src.order.dto;

import java.util.List;

import lombok.Getter;

@Getter
public class CartAddReq {
private Long storeId;
private Long foodieId;
private List<Long> options;
private Long count;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.readyvery.readyverydemo.src.order.dto;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CartAddRes {
private Long cartItemId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.springframework.stereotype.Component;

import com.readyvery.readyverydemo.domain.CartItem;
import com.readyvery.readyverydemo.domain.Foodie;
import com.readyvery.readyverydemo.domain.FoodieOption;
import com.readyvery.readyverydemo.domain.FoodieOptionCategory;
Expand Down Expand Up @@ -56,4 +57,10 @@ private FoodyOptionDto foodyOptionToOptionDto(FoodieOption option) {
.price(option.getPrice())
.build();
}

public CartAddRes cartToCartAddRes(CartItem cartItem) {
return CartAddRes.builder()
.cartItemId(cartItem.getId())
.build();
}
}
3 changes: 2 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
spring.datasource.url=jdbc:mysql://localhost:3306/readyvery
spring.datasource.username=root
spring.datasource.password=12345678
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.fail-on-empty-beans=false
# JWT Configuration
jwt.secretKey=Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkw
Expand Down

0 comments on commit 937fa5b

Please sign in to comment.