Skip to content

Commit

Permalink
[feat] 장바구니에 현재까지 담은 음식 상품의 금액을 볼 수 있는 기능 추가 (#70)
Browse files Browse the repository at this point in the history
* [feat] Cart의 customerId와 menuList에 final 추가

- customerId와 menuList를 불변으로 선언해서 내부 안정성 향상

* [feat] Cart생성자 추가

- Cart Domain을 사용하는 같은 패키지내의 클래스, 혹은 자식 클래스는 List를 커스텀할 수 있도록 생성자 추가.

* [test] CartFixture 및 필요한 TestMenu, TestStore 구현

* [test] Cart 도메인의 addMenu 테스트 추가

- 같은 가게의 메뉴만 담을 수 있다.
- 오픈한 가게의 메뉴만 담을 수 있다.

* [test] Cart 도메인의 totalPrice 테스트 추가

- 빈 장바구니면 0원이 return된다.
- 담긴 메뉴의 총 합이 return된다.

* [chore] totalPrice -> getTotalPrice로 메서드 이름 수정

- 더 명확하게 가져온다는 의도를 메서드명에 명시

* [feat] CartService에 getTotalPrice 메서드 추가

- 현재 담긴 금액을 알 수 있는 메서드 추가

* [test] CartService의  getTotalPrice 테스트 추가

- 빈 장바구니면 0원이 return된다.
- 담긴 메뉴의 총 합이 return된다.

* [feat] CartApiController에 현재 Cart에 담긴 총 금액을 조회하는 API 생성

* [test] CartApiController의 getCartTotalPrice 테스트 추가

- 빈 장바구니면 0원이 return된다.
- 담긴 메뉴의 총 합이 return된다.
  • Loading branch information
Hyeon-Uk authored Aug 15, 2024
1 parent eab39b1 commit ae61e87
Show file tree
Hide file tree
Showing 11 changed files with 391 additions and 27 deletions.
24 changes: 21 additions & 3 deletions src/main/java/camp/woowak/lab/cart/domain/Cart.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,27 @@

@Getter
public class Cart {
private String customerId;
private List<Menu> menuList;
private final String customerId;
private final List<Menu> menuList;

/**
* 생성될 때 무조건 cart가 비어있도록 구현
*
* @param customerId 장바구니 소유주의 ID값입니다.
*/
public Cart(String customerId) {
this(customerId, new LinkedList<>());
}

/**
* 해당 Domain을 사용하는 같은 패키지내의 클래스, 혹은 자식 클래스는 List를 커스텀할 수 있습니다.
*
* @param customerId 장바구니 소유주의 ID값입니다.
* @param menuList 장바구니에 사용될 List입니다.
*/
protected Cart(String customerId, List<Menu> menuList) {
this.customerId = customerId;
this.menuList = new LinkedList<>();
this.menuList = menuList;
}

public void addMenu(Menu menu) {
Expand All @@ -36,6 +46,14 @@ public void addMenu(Menu menu) {
this.menuList.add(menu);
}

public long getTotalPrice() {
return this.menuList.stream()
.map(Menu::getPrice)
.mapToLong(Long::valueOf)
.boxed()
.reduce(0L, Long::sum);
}

private void validateStoreOpenTime(Store store) {
if (!store.isOpen()) {
StoreTime storeTime = store.getStoreTime();
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/camp/woowak/lab/cart/service/CartService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import camp.woowak.lab.cart.exception.MenuNotFoundException;
import camp.woowak.lab.cart.repository.CartRepository;
import camp.woowak.lab.cart.service.command.AddCartCommand;
import camp.woowak.lab.cart.service.command.CartTotalPriceCommand;
import camp.woowak.lab.menu.domain.Menu;
import camp.woowak.lab.menu.repository.MenuRepository;

Expand Down Expand Up @@ -34,6 +35,11 @@ public void addMenu(AddCartCommand command) {
cartRepository.save(customerCart);
}

public long getTotalPrice(CartTotalPriceCommand command) {
return getCart(command.customerId())
.getTotalPrice();
}

private Cart getCart(String customerId) {
return cartRepository.findByCustomerId(customerId)
.orElse(cartRepository.save(new Cart(customerId)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package camp.woowak.lab.cart.service.command;

public record CartTotalPriceCommand(
String customerId
) {
}
14 changes: 14 additions & 0 deletions src/main/java/camp/woowak/lab/web/api/cart/CartApiController.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package camp.woowak.lab.web.api.cart;

import org.springframework.web.bind.annotation.GetMapping;
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 camp.woowak.lab.cart.service.CartService;
import camp.woowak.lab.cart.service.command.AddCartCommand;
import camp.woowak.lab.cart.service.command.CartTotalPriceCommand;
import camp.woowak.lab.web.authentication.LoginCustomer;
import camp.woowak.lab.web.authentication.annotation.AuthenticationPrincipal;
import camp.woowak.lab.web.dto.request.cart.AddCartRequest;
import camp.woowak.lab.web.dto.response.cart.AddCartResponse;
import camp.woowak.lab.web.dto.response.cart.CartTotalPriceResponse;
import lombok.extern.slf4j.Slf4j;

@RestController
Expand All @@ -33,4 +36,15 @@ public AddCartResponse addCart(
log.info("Customer({}) add Menu({}) in Cart", loginCustomer.getId(), addCartRequest.menuId());
return new AddCartResponse(true);
}

@GetMapping("/price")
public CartTotalPriceResponse getCartTotalPrice(
@AuthenticationPrincipal LoginCustomer loginCustomer
) {
CartTotalPriceCommand command = new CartTotalPriceCommand(loginCustomer.getId().toString());
long totalPrice = cartService.getTotalPrice(command);

log.info("Customer({})'s total price in cart is {}", loginCustomer.getId(), totalPrice);
return new CartTotalPriceResponse(totalPrice);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package camp.woowak.lab.web.dto.response.cart;

public record CartTotalPriceResponse(
long totalPrice
) {
}
136 changes: 136 additions & 0 deletions src/test/java/camp/woowak/lab/cart/domain/CartTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package camp.woowak.lab.cart.domain;

import static org.assertj.core.api.Assertions.*;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import camp.woowak.lab.cart.exception.OtherStoreMenuException;
import camp.woowak.lab.cart.exception.StoreNotOpenException;
import camp.woowak.lab.fixture.CartFixture;
import camp.woowak.lab.menu.domain.Menu;
import camp.woowak.lab.menu.domain.MenuCategory;
import camp.woowak.lab.payaccount.domain.PayAccount;
import camp.woowak.lab.store.domain.Store;
import camp.woowak.lab.vendor.domain.Vendor;
import camp.woowak.lab.web.authentication.NoOpPasswordEncoder;

@DisplayName("Cart 클래스는")
class CartTest implements CartFixture {
private final String customerId = UUID.randomUUID().toString();

private List<Menu> cartList;
private Cart cart;
private Menu menu;
private int minPrice = 8000;
private Store store;
private Vendor vendor;

@BeforeEach
void setUp() {
cartList = new LinkedList<>();
cart = new Cart(customerId, cartList);

vendor = createSavedVendor(UUID.randomUUID(), new PayAccount(), new NoOpPasswordEncoder());
store = createSavedStore(1L, vendor, "중화반점", minPrice,
LocalDateTime.now().minusMinutes(10).withSecond(0).withNano(0),
LocalDateTime.now().plusMinutes(10).withSecond(0).withNano(0));
menu = createSavedMenu(1L, store, new MenuCategory(store, "중식"), "짜장면", 9000);
}

@Nested
@DisplayName("addMenu 메서드")
class AddMenuTest {
@Test
@DisplayName("Menu를 받으면 cart에 저장된다.")
void addMenuTest() {
//given

//when
cart.addMenu(menu);

//then
assertThat(cartList.get(0)).isEqualTo(menu);
}

@Test
@DisplayName("열지 않은 가게의 메뉴를 담으면 StoreNotOpenException을 던진다.")
void storeNotOpenExceptionTest() {
//given
Store closedStore = createSavedStore(2L, vendor, "closed", minPrice,
LocalDateTime.now().minusMinutes(30).withSecond(0).withNano(0),
LocalDateTime.now().minusMinutes(10).withSecond(0).withNano(0));
Menu closedMenu = createSavedMenu(2L, closedStore, new MenuCategory(closedStore, "중식"), "짬뽕", 9000);

//when & then
assertThatThrownBy(() -> cart.addMenu(closedMenu))
.isExactlyInstanceOf(StoreNotOpenException.class);
assertThat(cartList).isEmpty();
}

@Test
@DisplayName("다른 가게의 메뉴를 함께 담으면 OtherStoreMenuException을 던진다.")
void otherStoreMenuExceptionTest() {
//given
cart.addMenu(menu);

Store otherStore = createSavedStore(2L, vendor, "closed", minPrice,
LocalDateTime.now().minusMinutes(30).withSecond(0).withNano(0),
LocalDateTime.now().plusMinutes(30).withSecond(0).withNano(0));
Menu otherStoreMenu = createSavedMenu(2L, otherStore, new MenuCategory(otherStore, "중식"), "짬뽕", 9000);

//when & then
assertThatThrownBy(() -> cart.addMenu(otherStoreMenu))
.isExactlyInstanceOf(OtherStoreMenuException.class);
assertThat(cartList).doesNotContain(otherStoreMenu);
assertThat(cartList).contains(menu);
}
}

@Nested
@DisplayName("totalPrice 메서드는")
class GetTotalPriceTest {
@Test
@DisplayName("장바구니가 비어있으면 0원을 return한다.")
void getTotalPriceWithEmptyList() {
//given

//when
long totalPrice = cart.getTotalPrice();

//then
assertThat(totalPrice).isEqualTo(0);
}

@Test
@DisplayName("현재 장바구니에 담긴 모든 메뉴의 총 금액을 return 받는다.")
void getTotalPriceTest() {
//given
MenuCategory menuCategory = new MenuCategory(store, "중식");
int price1 = 1000;
Menu menu1 = createSavedMenu(2L, store, menuCategory, "짜장면1", price1);
cart.addMenu(menu1);

int price2 = 2000;
Menu menu2 = createSavedMenu(3L, store, menuCategory, "짬뽕1", price2);
cart.addMenu(menu2);

int price3 = Integer.MAX_VALUE;
Menu menu3 = createSavedMenu(4L, store, menuCategory, "황제정식", price3);
cart.addMenu(menu3);

//when
long totalPrice = cart.getTotalPrice();

//then
assertThat(totalPrice).isEqualTo((long)price1 + (long)price2 + (long)price3);
}
}
}
69 changes: 62 additions & 7 deletions src/test/java/camp/woowak/lab/cart/service/CartServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import camp.woowak.lab.cart.exception.StoreNotOpenException;
import camp.woowak.lab.cart.repository.CartRepository;
import camp.woowak.lab.cart.service.command.AddCartCommand;
import camp.woowak.lab.cart.service.command.CartTotalPriceCommand;
import camp.woowak.lab.customer.domain.Customer;
import camp.woowak.lab.customer.repository.CustomerRepository;
import camp.woowak.lab.menu.domain.Menu;
Expand Down Expand Up @@ -66,6 +67,7 @@ class CartServiceTest {

private Customer customer;
private Menu menu;
private MenuCategory menuCategory;
private Store store;
private Vendor vendor;

Expand All @@ -77,7 +79,9 @@ void setUp() {

store = createStore(vendor, "중화반점", minOrderPrice, startTime, endTime);

menu = createMenu(store, "짜장면", 90000);
menuCategory = createMenuCategory(store, "중식");

menu = createMenu(store, menuCategory, "짜장면", 90000);
}

@Nested
Expand Down Expand Up @@ -118,7 +122,8 @@ void throwExceptionWhenAddMenuInCartWithClosedStoresMenu() {
LocalDateTime startTime = LocalDateTime.now().minusMinutes(10).withSecond(0).withNano(0);
LocalDateTime endTime = LocalDateTime.now().minusMinutes(1).withSecond(0).withNano(0);
Store closedStore = createStore(vendor, "오픈 전의 중화반점", 8000, startTime, endTime);
Menu menu = createMenu(closedStore, "닫힌 가게의 메뉴", 1000);
MenuCategory closedMenuCategory = createMenuCategory(closedStore, "닫힌 카테고리");
Menu menu = createMenu(closedStore, closedMenuCategory, "닫힌 가게의 메뉴", 1000);

AddCartCommand command = new AddCartCommand(customer.getId().toString(), menu.getId());

Expand All @@ -132,7 +137,8 @@ void throwExceptionWhenAddMenuInCartWithClosedStoresMenu() {
void throwExceptionWhenAddMenuInCartWithOtherStoresMenu() {
//given
Store otherStore = createStore(vendor, "다른집", 8000, startTime, endTime);
Menu otherStoresMenu = createMenu(otherStore, "다른집 짜장면", 9000);
MenuCategory otherMenuCategory = createMenuCategory(otherStore, "다른집 카테고리");
Menu otherStoresMenu = createMenu(otherStore, otherMenuCategory, "다른집 짜장면", 9000);

AddCartCommand command1 = new AddCartCommand(customer.getId().toString(), menu.getId());
cartService.addMenu(command1);
Expand All @@ -144,10 +150,59 @@ void throwExceptionWhenAddMenuInCartWithOtherStoresMenu() {
}
}

private Menu createMenu(Store store, String name, int price) {
MenuCategory menuCategory = new MenuCategory(store, "카테고리1");
menuCategoryRepository.saveAndFlush(menuCategory);
Menu menu1 = new Menu(store, menuCategory,name, price,"imageUrl");
@Nested
@DisplayName("getTotalPrice 메서드")
class GetTotalPrice {
@Nested
@DisplayName("totalPrice 메서드는")
class GetTotalPriceTest {
@Test
@DisplayName("장바구니가 비어있으면 0원을 return한다.")
void getTotalPriceWithEmptyList() {
//given
CartTotalPriceCommand command = new CartTotalPriceCommand(customer.getId().toString());

//when
long totalPrice = cartService.getTotalPrice(command);

//then
assertThat(totalPrice).isEqualTo(0);
}

@Test
@DisplayName("현재 장바구니에 담긴 모든 메뉴의 총 금액을 return 받는다.")
void getTotalPriceTest() {
//given
int price1 = 1000;
Menu menu1 = createMenu(store, menuCategory, "짜장면1", price1);
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu1.getId()));

int price2 = 2000;
Menu menu2 = createMenu(store, menuCategory, "짬뽕1", price2);
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu2.getId()));

int price3 = Integer.MAX_VALUE;
Menu menu3 = createMenu(store, menuCategory, "황제정식", price3);
cartService.addMenu(new AddCartCommand(customer.getId().toString(), menu3.getId()));

CartTotalPriceCommand command = new CartTotalPriceCommand(customer.getId().toString());

//when
long totalPrice = cartService.getTotalPrice(command);

//then
assertThat(totalPrice).isEqualTo((long)price1 + (long)price2 + (long)price3);
}
}
}

private MenuCategory createMenuCategory(Store store, String name) {
MenuCategory menuCategory1 = new MenuCategory(store, name);
return menuCategoryRepository.saveAndFlush(menuCategory1);
}

private Menu createMenu(Store store, MenuCategory menuCategory, String name, int price) {
Menu menu1 = new Menu(store, menuCategory, name, price, "imageUrl");
menuRepository.saveAndFlush(menu1);

return menu1;
Expand Down
31 changes: 31 additions & 0 deletions src/test/java/camp/woowak/lab/fixture/CartFixture.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package camp.woowak.lab.fixture;

import java.time.LocalDateTime;
import java.util.UUID;

import camp.woowak.lab.menu.TestMenu;
import camp.woowak.lab.menu.domain.Menu;
import camp.woowak.lab.menu.domain.MenuCategory;
import camp.woowak.lab.payaccount.domain.PayAccount;
import camp.woowak.lab.store.TestStore;
import camp.woowak.lab.store.domain.Store;
import camp.woowak.lab.vendor.TestVendor;
import camp.woowak.lab.vendor.domain.Vendor;
import camp.woowak.lab.web.authentication.PasswordEncoder;

public interface CartFixture {
default Vendor createSavedVendor(UUID id, PayAccount payAccount, PasswordEncoder passwordEncoder) {
return new TestVendor(
id, "vendorName", "vendorEmail@example.com", "vendorPassword", "010-0000-0000", payAccount,
passwordEncoder);
}

default Store createSavedStore(Long id, Vendor vendor, String name, int minOrderPrice, LocalDateTime startTIme,
LocalDateTime endTime) {
return new TestStore(id, vendor, name, "송파", "010-1234-5678", minOrderPrice, startTIme, endTime);
}

default Menu createSavedMenu(Long id, Store store, MenuCategory menuCategory, String name, int price) {
return new TestMenu(id, store, menuCategory, name, price);
}
}
Loading

0 comments on commit ae61e87

Please sign in to comment.