diff --git a/src/main/java/com/umc/TheGoods/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/umc/TheGoods/apiPayload/code/status/ErrorStatus.java index fb6a629d..c20073ce 100644 --- a/src/main/java/com/umc/TheGoods/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/umc/TheGoods/apiPayload/code/status/ErrorStatus.java @@ -32,6 +32,10 @@ public enum ErrorStatus implements BaseErrorCode { ORDER_ITEM_UPDATE_FAIL(HttpStatus.BAD_REQUEST, "ORDER4008", "주문 상품 정보 변경이 불가합니다."), NULL_AMOUNT_ERROR(HttpStatus.BAD_REQUEST, "ORDER4009", "주문 수량은 null일 수 없습니다."), + // 장바구니 관련 에러 + CART_NOT_FOUND(HttpStatus.NOT_FOUND, "CART4001", "해당 장바구니 내역을 찾을 수 없습니다."), + NOT_CART_OWNER(HttpStatus.BAD_REQUEST, "CART4002", "본인의 장바구니 내역이 아닙니다. 접근할 수 없습니다."), + // test TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "테스트"), diff --git a/src/main/java/com/umc/TheGoods/domain/order/Cart.java b/src/main/java/com/umc/TheGoods/domain/order/Cart.java index 3b205935..78f974d8 100644 --- a/src/main/java/com/umc/TheGoods/domain/order/Cart.java +++ b/src/main/java/com/umc/TheGoods/domain/order/Cart.java @@ -59,4 +59,9 @@ public void setItemOption(ItemOption itemOption) { this.itemOption = itemOption; itemOption.getCartList().add(this); } + + public Cart updateAmount(Integer amount) { + this.amount = amount; + return this; + } } diff --git a/src/main/java/com/umc/TheGoods/repository/cart/CartRepository.java b/src/main/java/com/umc/TheGoods/repository/cart/CartRepository.java index 6427136b..c314e87e 100644 --- a/src/main/java/com/umc/TheGoods/repository/cart/CartRepository.java +++ b/src/main/java/com/umc/TheGoods/repository/cart/CartRepository.java @@ -1,7 +1,17 @@ package com.umc.TheGoods.repository.cart; +import com.umc.TheGoods.domain.item.Item; +import com.umc.TheGoods.domain.item.ItemOption; +import com.umc.TheGoods.domain.member.Member; import com.umc.TheGoods.domain.order.Cart; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface CartRepository extends JpaRepository { + + Optional findByMemberAndItemAndItemOption(Member member, Item item, ItemOption itemOption); + + Optional findByMemberAndItem(Member member, Item item); + } diff --git a/src/main/java/com/umc/TheGoods/service/CartService/CartCommandService.java b/src/main/java/com/umc/TheGoods/service/CartService/CartCommandService.java index 33e8222c..ad8b9316 100644 --- a/src/main/java/com/umc/TheGoods/service/CartService/CartCommandService.java +++ b/src/main/java/com/umc/TheGoods/service/CartService/CartCommandService.java @@ -1,9 +1,12 @@ package com.umc.TheGoods.service.CartService; import com.umc.TheGoods.domain.member.Member; +import com.umc.TheGoods.domain.order.Cart; import com.umc.TheGoods.web.dto.cart.CartRequestDTO; public interface CartCommandService { void addCart(CartRequestDTO.cartAddDTO request, Member member); + + Cart updateCart(CartRequestDTO.cartUpdateDTO request, Member member); } diff --git a/src/main/java/com/umc/TheGoods/service/CartService/CartCommandServiceImpl.java b/src/main/java/com/umc/TheGoods/service/CartService/CartCommandServiceImpl.java index 1e957628..e5ead4e5 100644 --- a/src/main/java/com/umc/TheGoods/service/CartService/CartCommandServiceImpl.java +++ b/src/main/java/com/umc/TheGoods/service/CartService/CartCommandServiceImpl.java @@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -50,14 +51,25 @@ public void addCart(CartRequestDTO.cartAddDTO request, Member member) { throw new OrderHandler(ErrorStatus.LACK_OF_STOCK); } - // cart 테이블에 해당 장바구니 내역 이미 존재하면, 해당 내역의 담은 수량만 업데이트하는 부분 추가 필요 - - // cart 엔티티 생성 및 연관관계 매핑 - Cart cart = CartConverter.toCart(cartOptionAddDTO.getAmount()); - cart.setMember(member); - cart.setItem(item); - cart.setItemOption(itemOption); - return cart; + // cart 테이블에 해당 장바구니 내역 이미 존재하면, 해당 내역의 담은 수량만 업데이트 + Optional existCart = cartRepository.findByMemberAndItemAndItemOption(member, item, itemOption); + if (!existCart.isEmpty()) { // 장바구니 내역 이미 존재하는 경우 + // 재고 수량과 비교 + if (cartOptionAddDTO.getAmount() + existCart.get().getAmount() > itemOption.getStock()) { + throw new OrderHandler(ErrorStatus.LACK_OF_STOCK); + } + + // 장바구니 내역의 수량 업데이트 + existCart.get().updateAmount(existCart.get().getAmount() + cartOptionAddDTO.getAmount()); + return existCart.get(); + } else { // 신규 장바구니 내역 생성 + // cart 엔티티 생성 및 연관관계 매핑 + Cart cart = CartConverter.toCart(cartOptionAddDTO.getAmount()); + cart.setMember(member); + cart.setItem(item); + cart.setItemOption(itemOption); + return cart; + } }).collect(Collectors.toList()); // 엔티티 저장 @@ -79,16 +91,45 @@ public void addCart(CartRequestDTO.cartAddDTO request, Member member) { throw new OrderHandler(ErrorStatus.LACK_OF_STOCK); } - // cart 테이블에 해당 장바구니 내역 이미 존재하면, 해당 내역의 담은 수량만 업데이트하는 부분 추가 필요 + Optional existCart = cartRepository.findByMemberAndItem(member, item); + if (!existCart.isEmpty()) { // 장바구니 내역 이미 존재하는 경우 + // 재고 수량과 비교 + if (request.getAmount() + existCart.get().getAmount() > item.getStock()) { + throw new OrderHandler(ErrorStatus.LACK_OF_STOCK); + } + + // 장바구니 내역의 수량 업데이트 + existCart.get().updateAmount(request.getAmount()); + } else { // 장바구니 내역 신규 생성 + + // cart 엔티티 생성 및 연관관계 매핑 + Cart cart = CartConverter.toCart(request.getAmount()); + cart.setMember(member); + cart.setItem(item); + + // 엔티티 저장 + cartRepository.save(cart); + } + + } + } - // cart 엔티티 생성 및 연관관계 매핑 - Cart cart = CartConverter.toCart(request.getAmount()); - cart.setMember(member); - cart.setItem(item); + @Override + public Cart updateCart(CartRequestDTO.cartUpdateDTO request, Member member) { + // cart 존재 여부 검증은 annotation에서 진행 + Cart cart = cartRepository.findById(request.getCartId()).get(); - // 엔티티 저장 - cartRepository.save(cart); + // 해당 cart 내역을 수정할 권한 있는지 검증 + if (!cart.getMember().equals(member)) { + throw new OrderHandler(ErrorStatus.NOT_CART_OWNER); } + + // 재고 수량과 비교 + if (request.getAmount() > cart.getItem().getStock()) { + throw new OrderHandler(ErrorStatus.LACK_OF_STOCK); + } + + return cart.updateAmount(request.getAmount()); } } diff --git a/src/main/java/com/umc/TheGoods/service/CartService/CartQueryService.java b/src/main/java/com/umc/TheGoods/service/CartService/CartQueryService.java new file mode 100644 index 00000000..2c7c1ff3 --- /dev/null +++ b/src/main/java/com/umc/TheGoods/service/CartService/CartQueryService.java @@ -0,0 +1,6 @@ +package com.umc.TheGoods.service.CartService; + +public interface CartQueryService { + + boolean isExistCart(Long cartId); +} diff --git a/src/main/java/com/umc/TheGoods/service/CartService/CartQueryServiceImpl.java b/src/main/java/com/umc/TheGoods/service/CartService/CartQueryServiceImpl.java new file mode 100644 index 00000000..7da21d24 --- /dev/null +++ b/src/main/java/com/umc/TheGoods/service/CartService/CartQueryServiceImpl.java @@ -0,0 +1,20 @@ +package com.umc.TheGoods.service.CartService; + +import com.umc.TheGoods.repository.cart.CartRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CartQueryServiceImpl implements CartQueryService { + + private final CartRepository cartRepository; + + + @Override + public boolean isExistCart(Long cartId) { + return cartRepository.existsById(cartId); + } +} diff --git a/src/main/java/com/umc/TheGoods/validation/annotation/ExistCart.java b/src/main/java/com/umc/TheGoods/validation/annotation/ExistCart.java new file mode 100644 index 00000000..cf7e7776 --- /dev/null +++ b/src/main/java/com/umc/TheGoods/validation/annotation/ExistCart.java @@ -0,0 +1,19 @@ +package com.umc.TheGoods.validation.annotation; + +import com.umc.TheGoods.validation.validator.CartExistValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = CartExistValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistCart { + String message() default "해당 장바구니 내역을 찾을 수 없습니다."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/com/umc/TheGoods/validation/validator/CartExistValidator.java b/src/main/java/com/umc/TheGoods/validation/validator/CartExistValidator.java new file mode 100644 index 00000000..b5f43642 --- /dev/null +++ b/src/main/java/com/umc/TheGoods/validation/validator/CartExistValidator.java @@ -0,0 +1,32 @@ +package com.umc.TheGoods.validation.validator; + +import com.umc.TheGoods.apiPayload.code.status.ErrorStatus; +import com.umc.TheGoods.service.CartService.CartQueryService; +import com.umc.TheGoods.validation.annotation.ExistCart; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +@Component +@RequiredArgsConstructor +public class CartExistValidator implements ConstraintValidator { + + private final CartQueryService cartQueryService; + + @Override + public void initialize(ExistCart constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Long value, ConstraintValidatorContext context) { + boolean isValid = cartQueryService.isExistCart(value); + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.CART_NOT_FOUND.getMessage()).addConstraintViolation(); + } + return isValid; + } +} diff --git a/src/main/java/com/umc/TheGoods/web/controller/CartController.java b/src/main/java/com/umc/TheGoods/web/controller/CartController.java index 43a50396..8a81f15e 100644 --- a/src/main/java/com/umc/TheGoods/web/controller/CartController.java +++ b/src/main/java/com/umc/TheGoods/web/controller/CartController.java @@ -14,10 +14,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; -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.RestController; +import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @@ -56,5 +53,22 @@ public ApiResponse add(@RequestBody @Valid CartRequestDTO.cartAddDTO req return ApiResponse.onSuccess("장바구니 담기 성공"); } + @PutMapping + public ApiResponse update(@RequestBody @Valid CartRequestDTO.cartUpdateDTO request, + Authentication authentication) { + // 비회원인 경우 처리 불가 + if (authentication == null) { + throw new MemberHandler(ErrorStatus._UNAUTHORIZED); + } + + // request에서 member id 추출해 Member 엔티티 찾기 + MemberDetail memberDetail = (MemberDetail) authentication.getPrincipal(); + Member member = memberQueryService.findMemberById(memberDetail.getMemberId()).orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND)); + + cartCommandService.updateCart(request, member); + + return ApiResponse.onSuccess("장바구니 옵션 수정 성공"); + } + } diff --git a/src/main/java/com/umc/TheGoods/web/dto/cart/CartRequestDTO.java b/src/main/java/com/umc/TheGoods/web/dto/cart/CartRequestDTO.java index d23a4dce..c7eb7309 100644 --- a/src/main/java/com/umc/TheGoods/web/dto/cart/CartRequestDTO.java +++ b/src/main/java/com/umc/TheGoods/web/dto/cart/CartRequestDTO.java @@ -1,5 +1,6 @@ package com.umc.TheGoods.web.dto.cart; +import com.umc.TheGoods.validation.annotation.ExistCart; import com.umc.TheGoods.validation.annotation.ExistItem; import com.umc.TheGoods.validation.annotation.ExistItemOption; import lombok.Getter; @@ -39,4 +40,16 @@ public static class cartOptionAddDTO { } + @Getter + public static class cartUpdateDTO { + @NotNull + @ExistCart + Long cartId; + + @Min(1) + @Max(100000) + @NotNull + Integer amount; + } + }