-
Notifications
You must be signed in to change notification settings - Fork 8
[6주차] 장바구니 만들기 #9
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package com.example.demo.application; | ||
|
|
||
| import com.example.demo.model.Cart; | ||
| import com.example.demo.model.LineItemId; | ||
| import com.example.demo.model.Product; | ||
| import com.example.demo.model.ProductId; | ||
| import com.example.demo.model.ProductOption; | ||
| import com.example.demo.repository.CartRepository; | ||
| import com.example.demo.repository.ProductRepository; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @Transactional | ||
| public class CartService { | ||
|
|
||
| private final CartRepository cartRepository; | ||
| private final ProductRepository productRepository; | ||
|
|
||
| public CartService(CartRepository cartRepository, ProductRepository productRepository) { | ||
| this.cartRepository = cartRepository; | ||
| this.productRepository = productRepository; | ||
| } | ||
|
|
||
| public void addItemToCart(Long cartId, ProductId productId, ProductOption option, int quantity) { | ||
| Cart cart = cartRepository.findById(cartId) | ||
| .orElseThrow(() -> new IllegalArgumentException("장바구니가 존재하지 않습니다.")); | ||
|
||
| Product product = productRepository.findById(productId) | ||
|
||
| .orElseThrow(() -> new IllegalArgumentException("상품이 존재하지 않습니다.")); | ||
|
||
|
|
||
| cart.addProduct(productId, option, quantity); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| public void removeItemFromCart(Long cartId, LineItemId lineItemId) { | ||
|
||
| Cart cart = cartRepository.findById(cartId) | ||
| .orElseThrow(() -> new IllegalArgumentException("장바구니가 존재하지 않습니다.")); | ||
|
|
||
| cart.removeLineItemById(lineItemId); | ||
| } | ||
|
|
||
| public void clearCart(Long cartId) { | ||
| Cart cart = cartRepository.findById(cartId) | ||
| .orElseThrow(() -> new IllegalArgumentException("장바구니가 존재하지 않습니다.")); | ||
|
|
||
| cart.clearItems(); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public Cart getCart(Long cartId) { | ||
| return cartRepository.findById(cartId) | ||
| .orElseThrow(() -> new IllegalArgumentException("장바구니가 존재하지 않습니다.")); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,48 @@ | ||
| package com.example.demo.controllers; | ||
|
|
||
| import com.example.demo.application.CartService; | ||
| import com.example.demo.controllers.dto.CartResponseDto; | ||
| import com.example.demo.controllers.dto.LineItemResponseDto; | ||
| import com.example.demo.model.Cart; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/cart") | ||
| @RequestMapping("/carts") | ||
|
||
| @RequiredArgsConstructor | ||
| public class CartController { | ||
| @GetMapping | ||
| String detail() { | ||
| return ""; | ||
|
|
||
| private final CartService cartService; | ||
|
|
||
| @GetMapping("/{cartId}") | ||
|
||
| public CartResponseDto getCart(@PathVariable Long cartId) { | ||
|
|
||
| Cart cart = cartService.getCart(cartId); | ||
|
|
||
| return new CartResponseDto( | ||
| cartId, | ||
| cart.getTotalQuantity(), | ||
| cart.getLineItems().stream() | ||
| .map(lineItem -> new LineItemResponseDto( | ||
| lineItem.getProductId().id(), | ||
| lineItem.getProductOption().color(), | ||
| lineItem.getProductOption().size(), | ||
| lineItem.getQuantity() | ||
| ) | ||
| ).toList() | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| @DeleteMapping("/{cartId}") | ||
| public ResponseEntity<Void> deleteCart(@PathVariable Long cartId) { | ||
| cartService.clearCart(cartId); | ||
| return new ResponseEntity<>(HttpStatus.NO_CONTENT); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,44 @@ | ||
| package com.example.demo.controllers; | ||
|
|
||
| import com.example.demo.application.CartService; | ||
| import com.example.demo.controllers.dto.LineItemRequestDto; | ||
| import com.example.demo.model.LineItemId; | ||
| import com.example.demo.model.ProductId; | ||
| import com.example.demo.model.ProductOption; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| 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.ResponseStatus; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/cart/line-items") | ||
| @RequestMapping("/carts/{cartId}/line-items") | ||
|
||
| @RequiredArgsConstructor | ||
| public class LineItemController { | ||
|
|
||
| private final CartService cartService; | ||
|
|
||
| @PostMapping | ||
| @ResponseStatus(HttpStatus.CREATED) | ||
| void create() { | ||
| // | ||
| public void create(@PathVariable Long cartId, @RequestBody LineItemRequestDto requestDto) { | ||
|
|
||
| cartService.addItemToCart( | ||
| cartId, | ||
|
||
| new ProductId(requestDto.getProductId()), | ||
| new ProductOption(requestDto.getColor(), requestDto.getSize()), | ||
| requestDto.getQuantity()); | ||
|
||
|
|
||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @DeleteMapping("/{lineItemId}") | ||
| @ResponseStatus(HttpStatus.NO_CONTENT) | ||
| public void deleteLineItem(@PathVariable Long cartId, @PathVariable String lineItemId) { | ||
| cartService.removeItemFromCart( | ||
| cartId, new LineItemId(lineItemId)); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.example.demo.controllers.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class CartResponseDto { | ||
|
||
| private Long cartId; | ||
| private int totalQuantity; | ||
| private List<LineItemResponseDto> lineItems; | ||
|
|
||
| public CartResponseDto(Long cartId, int totalQuantity, List<LineItemResponseDto> lineItems) { | ||
| this.cartId = cartId; | ||
| this.totalQuantity = totalQuantity; | ||
| this.lineItems = lineItems; | ||
| } | ||
|
|
||
| public Long getCartId() { | ||
| return cartId; | ||
| } | ||
|
|
||
| public int getTotalQuantity() { | ||
| return totalQuantity; | ||
| } | ||
|
|
||
| public List<LineItemResponseDto> getLineItems() { | ||
| return lineItems; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package com.example.demo.controllers.dto; | ||
|
|
||
| public class LineItemRequestDto { | ||
| private String productId; | ||
|
||
| private String color; | ||
| private String size; | ||
| private int quantity; | ||
|
|
||
| public LineItemRequestDto(String productId, String color, String size, int quantity) { | ||
| this.productId = productId; | ||
| this.color = color; | ||
| this.size = size; | ||
| this.quantity = quantity; | ||
| } | ||
|
|
||
| public String getProductId() { | ||
| return productId; | ||
| } | ||
|
|
||
| public String getColor() { | ||
| return color; | ||
| } | ||
|
|
||
| public String getSize() { | ||
| return size; | ||
| } | ||
|
|
||
| public int getQuantity() { | ||
| return quantity; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package com.example.demo.controllers.dto; | ||
|
|
||
| public class LineItemResponseDto { | ||
|
|
||
| private String productId; | ||
| private String color; | ||
| private String size; | ||
| private int quantity; | ||
|
|
||
| public LineItemResponseDto(String productId, String color, String size, int quantity) { | ||
| this.productId = productId; | ||
| this.color = color; | ||
| this.size = size; | ||
| this.quantity = quantity; | ||
| } | ||
|
|
||
| public String getProductId() { | ||
| return productId; | ||
| } | ||
|
|
||
| public String getColor() { | ||
| return color; | ||
| } | ||
|
|
||
| public String getSize() { | ||
| return size; | ||
| } | ||
|
|
||
| public int getQuantity() { | ||
| return quantity; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package com.example.demo.model; | ||
|
|
||
| import jakarta.persistence.CascadeType; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.GeneratedValue; | ||
| import jakarta.persistence.GenerationType; | ||
| import jakarta.persistence.Id; | ||
| import jakarta.persistence.OneToMany; | ||
| import jakarta.persistence.Table; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| @Entity | ||
| @Table(name="carts") | ||
| public class Cart { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
| @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<LineItem> lineItems = new ArrayList<>(); | ||
|
|
||
| private int totalQuantity; | ||
|
|
||
| public Cart() { | ||
| } | ||
|
|
||
| public Cart(List<LineItem> lineItems) { | ||
| this.lineItems = new ArrayList<>(lineItems); | ||
| lineItems.forEach(lineItem -> lineItem.setCart(this)); | ||
| calculateTotalQuantity(); | ||
| } | ||
|
|
||
| public List<LineItem> getLineItems() { | ||
| return Collections.unmodifiableList(lineItems); | ||
| } | ||
|
|
||
| public int getTotalQuantity() { | ||
| return totalQuantity; | ||
| } | ||
|
|
||
| public void addProduct(ProductId productId, ProductOption productOption, int quantity) { | ||
|
|
||
| LineItem lineItem = findLineItem(productId, productOption); | ||
|
|
||
| if (lineItem != null) { | ||
| lineItem.addQuantity(quantity, productOption); | ||
|
||
| calculateTotalQuantity(); | ||
| checkValidQuantity(); | ||
|
||
| return; | ||
| } | ||
|
|
||
| lineItem = new LineItem(productId, productOption, quantity); | ||
| lineItems.add(lineItem); | ||
| lineItem.setCart(this); | ||
|
||
| calculateTotalQuantity(); | ||
| checkValidQuantity(); | ||
|
||
| } | ||
|
|
||
| public void checkValidQuantity() { | ||
| if (totalQuantity > 20) { | ||
| throw new IllegalArgumentException("담을수 있는 수량을 초과했습니다."); | ||
|
||
| } | ||
| } | ||
|
|
||
| private void calculateTotalQuantity() { | ||
| this.totalQuantity = lineItems.stream() | ||
| .mapToInt(LineItem::getQuantity) | ||
| .sum(); | ||
| } | ||
|
|
||
|
|
||
| private LineItem findLineItem(ProductId productId, ProductOption productOption) { | ||
| return lineItems.stream() | ||
| .filter(lineItem -> | ||
| lineItem.isSameProduct(productId,productOption)) | ||
| .findFirst() | ||
| .orElse(null); | ||
| } | ||
|
|
||
| public void clearItems() { | ||
| lineItems.clear(); | ||
| calculateTotalQuantity(); | ||
| } | ||
|
|
||
| public void removeLineItemById(LineItemId lineItemId) { | ||
|
||
| if (lineItemId == null) return; | ||
|
||
| lineItems.removeIf(lineItem ->lineItem.getId().equals(lineItemId)); | ||
|
||
| calculateTotalQuantity(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아마 아주 특별한 일이 일어나지 않는다면 Lombok을 쓸 일은 없을 겁니다. record가 도입된 후로는 영원히 봉인해도 괜찮을 정도라고 생각해요. 저는 최근에 이것의 존재를 아예 잊고 있었습니다.