Skip to content

Commit

Permalink
[feat] 점주 주문 리스트 조회 (#94)
Browse files Browse the repository at this point in the history
* [chore] .gitkeep 제거

* [test] OrderRepositoryTest 구현
- OrderRepositoryTest.testFindAllByOwner: 점주가 관리하고 있는 모든 점포의 주문 조회
- OrderRepositoryTest.testFindAllByOwnerFailWithUnauthorized: 점주가 관리하고 있는 모든 점포의 주문 조회 실패
- OrderRepositoryTest.testFindByStore: 특정 매장 주문 조회
- OrderRepositoryTest.testFindByStoreFailWithUnauthorized: 특정 매장 주문 조회 실패 - 권한 없음

* [feat] OrderRepository 구현
- OrderRepository.findAllByOwner: 점주가 관리하는 모든 점포의 주문 조회
- OrderRepository.findByStore: 특정 점포의 주문 조회

* [feat] RetrieveOrderListService 구현
- RetrieveOrderListService.retrieveOrderListOfVendorStores: 점주가 관리하는 모든 점포의 주문 조회
- RetrieveOrderListService.retrieveOrderListOfStore: 특정 점포의 주문 조회

* [test] RetrieveOrderListServiceTest 구현
- RetrieveOrderListServiceTest.testRetrieveOrderList: 점주 주문 리스트 조회 테스트 - 성공
- RetrieveOrderListServiceTest.testRetrieveOrderListOfStore: 점주 특정 매장 주문 리스트 조회 테스트 - 성공
- RetrieveOrderListServiceTest.testRetrieveOrderListOfStoreFailByNotFoundStore: 점주 특정 매장 주문 리스트 조회 테스트 - 실패(매장이 존재하지 않음)
- RetrieveOrderListServiceTest.testRetrieveOrderListOfStoreFailByNotEqualsOwner: 점주 특정 매장 주문 리스트 조회 테스트 - 실패(매장의 주인이 아님)

* [feat] RetrieveOrderListCommand 구현

* [test] OrderApiControllerTest 구현
- OrderApiControllerTest.testRetrieveOrderList: 점주 주문 리스트 조회 테스트 - 성공
- OrderApiControllerTest.testRetrieveOrderListByStore: 점주 매장 주문 리스트 조회 테스트 - 성공
- OrderApiControllerTest.testRetrieveOrderListByStoreFail:점주 매장 주문 리스트 조회 테스트 - 실패(매장이 존재하지 않음)
- OrderApiControllerTest.testRetrieveOrderListByStoreFailWithUnauthorized: 점주 매장 주문 리스트 조회 테스트 - 실패(매장의 주인이 아님)

* [feat] OrderApiController 구현
- OrderApiController.retrieveOrderList: 점주가 관리하는 모든 점포의 주문 조회 요청 처리
- OrderApiController.retrieveOrderListByStore: 특점 점포의 주문 조회 요청 처리

* [feat] RetrieveOrderListResponse 구현
- 주문 리스트 조회 응답

* [feat] OrderExceptionHandler 구현
- 주문 예외 핸들러 구현
- OrderExceptionHandler.handleNotEqualsOwnerException: NotEqualsOwnerException 처리
- OrderExceptionHandler.handleNotFoundStoreException: NotFoundStoreException 처리

* [chore] OrderApiControllerTest 수정
- 사용하지 않는 코드 제거

* [feat] OrderDTO 구현
- 주문 조회 시 반환할 DTO 구현
- OrderDTO.RequesterDTO, OrderDTO.OrderItemDTO, OrderDTO.StoreDTO 를 내부에 두어 필요한 내용만 노출 시킬 수 있도록 함

* [feat] OrderItem 수정
- DTO 변환 시 내부 내용 조회가 필요해 `@Getter` 추가

* [feat] Order 수정
- DTO 변환 시 내부 내용 조회가 필요해 getter 추가

* [test] OrderApiControllerTest 수정
- OrderDTO 생성 후 데이터 검증 테스트 추가

* [feat] VendorFixture 추가
- VendorFixture.createTestVendor: 간단히 TestVendor 를 생성하는 메소드

* [feat] TestOrderDTO 구현
- 테스트에 사용할 TestOrderDTO 구현

* [feat] StoreFixture 구현
- 반복되는 TestStore 생성 코드를 추상화

* [feat] StoreAddress 수정
- DTO 생성을 위해 StoreAddress 필드를 조회할 수 있도록 변경

* [feat] OrderDTO 생성으로 인한 수정
- 반환값을 OrderDTO 로 수정

* [feat] OrderDTO 수정
- requestID(customer_id) 와 StoreID 추가

* [fix] OrderDTO 수정
-  OrderItem price 타입 변경으로 인한 변경

* [fix] OrderRepositoryTest 수정
-  OrderItem price 타입 변경으로 인한 변경
  • Loading branch information
kimhyun5u authored Aug 18, 2024
1 parent 922ed68 commit 3d4fd5c
Show file tree
Hide file tree
Showing 18 changed files with 679 additions and 9 deletions.
8 changes: 8 additions & 0 deletions src/main/java/camp/woowak/lab/order/domain/Order.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ public Long getId() {
return id;
}

public Customer getRequester() {
return requester;
}

public Store getStore() {
return store;
}

public List<OrderItem> getOrderItems() {
return orderItems;
}
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/camp/woowak/lab/order/domain/vo/OrderItem.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package camp.woowak.lab.order.domain.vo;

import jakarta.persistence.Embeddable;
import lombok.Getter;

@Getter
@Embeddable
public class OrderItem {
private Long menuId;
Expand All @@ -18,8 +20,4 @@ public OrderItem(Long menuId, long price, int quantity) {
this.quantity = quantity;
this.totalPrice = price * quantity;
}

public long getTotalPrice() {
return totalPrice;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package camp.woowak.lab.order.repository;

import java.util.List;
import java.util.UUID;

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

import camp.woowak.lab.order.domain.Order;

public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o JOIN FETCH o.store s WHERE s.owner.id = :vendorId")
List<Order> findAllByOwner(UUID vendorId);

@Query("SELECT o FROM Order o JOIN FETCH o.store s WHERE s.id = :storeId AND s.owner.id = :vendorId")
List<Order> findByStore(Long storeId, UUID vendorId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package camp.woowak.lab.order.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import camp.woowak.lab.order.repository.OrderRepository;
import camp.woowak.lab.order.service.command.RetrieveOrderListCommand;
import camp.woowak.lab.order.service.dto.OrderDTO;
import camp.woowak.lab.store.domain.Store;
import camp.woowak.lab.store.exception.NotEqualsOwnerException;
import camp.woowak.lab.store.exception.NotFoundStoreException;
import camp.woowak.lab.store.repository.StoreRepository;

@Service
@Transactional(readOnly = true)
public class RetrieveOrderListService {
private final OrderRepository orderRepository;
private final StoreRepository storeRepository;

public RetrieveOrderListService(OrderRepository orderRepository, StoreRepository storeRepository) {
this.orderRepository = orderRepository;
this.storeRepository = storeRepository;
}

public List<OrderDTO> retrieveOrderListOfVendorStores(RetrieveOrderListCommand command) {
// 점주 매장 주문 조회 권한 검증은 필요없다.
return orderRepository.findAllByOwner(command.vendorId()).stream().map(OrderDTO::new).toList();
}

/**
*
* @throws NotFoundStoreException 매장이 존재하지 않을 경우
* @throws NotEqualsOwnerException 매장의 주인이 아닐 경우
*/
public List<OrderDTO> retrieveOrderListOfStore(RetrieveOrderListCommand command) {
// 점주 매장 주문 조회 권한 검증
// 점주가 소유한 매장인지 확인
Store targetStore = storeRepository.findById(command.storeId())
.orElseThrow(() -> new NotFoundStoreException("해당 매장이 존재하지 않습니다."));

if (!targetStore.isOwnedBy(command.vendorId())) {
throw new NotEqualsOwnerException(command.vendorId() + "는 " + targetStore.getId() + " 매장의 주인이 아닙니다.");
}

return orderRepository.findByStore(command.storeId(), command.vendorId()).stream().map(OrderDTO::new).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.order.service.command;

import java.util.UUID;

public record RetrieveOrderListCommand(Long storeId, UUID vendorId) {
public RetrieveOrderListCommand(UUID vendorId) {
this(null, vendorId);
}
}
79 changes: 79 additions & 0 deletions src/main/java/camp/woowak/lab/order/service/dto/OrderDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package camp.woowak.lab.order.service.dto;

import java.util.List;
import java.util.UUID;

import camp.woowak.lab.customer.domain.Customer;
import camp.woowak.lab.order.domain.Order;
import camp.woowak.lab.order.domain.vo.OrderItem;
import camp.woowak.lab.store.domain.Store;
import lombok.Getter;

@Getter
public class OrderDTO {
private final Long id;
private final RequesterDTO requester;
private final StoreDTO store;
private final List<OrderItemDTO> orderItems;

public OrderDTO(Long id, RequesterDTO requester, StoreDTO store, List<OrderItemDTO> orderItems) {
this.id = id;
this.requester = requester;
this.store = store;
this.orderItems = orderItems;
}

public OrderDTO(Order order) {
this.id = order.getId();
this.requester = new RequesterDTO(order.getRequester());
this.store = new StoreDTO(order.getStore());
this.orderItems = order.getOrderItems().stream().map(OrderItemDTO::new).toList();
}

@Getter
public static class RequesterDTO {
private final UUID id;
private final String name;
private final String email;
private final String phone;

public RequesterDTO(Customer customer) {
this.id = customer.getId();
this.name = customer.getName();
this.email = customer.getEmail();
this.phone = customer.getPhone();
}
}

@Getter
public static class OrderItemDTO {
private final Long menuId;
private final long price;
private final int quantity;
private final long totalPrice;

public OrderItemDTO(OrderItem orderItem) {
this.menuId = orderItem.getMenuId();
this.price = orderItem.getPrice();
this.quantity = orderItem.getQuantity();
this.totalPrice = orderItem.getTotalPrice();
}
}

@Getter
public static class StoreDTO {
private final Long id;
private final String name;
private final String ownerName;
private final String address;
private final String phoneNumber;

public StoreDTO(Store store) {
this.id = store.getId();
this.name = store.getName();
this.ownerName = store.getOwner().getName();
this.address = store.getStoreAddress().getDistrict();
this.phoneNumber = store.getPhoneNumber();
}
}
}
3 changes: 1 addition & 2 deletions src/main/java/camp/woowak/lab/store/domain/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -32,7 +31,7 @@ public class Store {
@JoinColumn(name = "vendor_id", nullable = false)
private Vendor owner;

@OneToOne
@ManyToOne
@JoinColumn(name = "store_category_id", nullable = false)
private StoreCategory storeCategory;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/camp/woowak/lab/store/domain/StoreAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class StoreAddress {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
package camp.woowak.lab.web.api.order;

import org.springframework.http.HttpStatus;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import camp.woowak.lab.order.service.OrderCreationService;
import camp.woowak.lab.order.service.RetrieveOrderListService;
import camp.woowak.lab.order.service.command.OrderCreationCommand;
import camp.woowak.lab.order.service.command.RetrieveOrderListCommand;
import camp.woowak.lab.web.authentication.LoginCustomer;
import camp.woowak.lab.web.authentication.LoginVendor;
import camp.woowak.lab.web.authentication.annotation.AuthenticationPrincipal;
import camp.woowak.lab.web.dto.response.order.OrderCreationResponse;
import camp.woowak.lab.web.dto.response.order.RetrieveOrderListResponse;
import lombok.extern.slf4j.Slf4j;

@RestController
@Slf4j
public class OrderApiController {
private final OrderCreationService orderCreationService;
private final RetrieveOrderListService retrieveOrderListService;

public OrderApiController(OrderCreationService orderCreationService) {
public OrderApiController(OrderCreationService orderCreationService,
RetrieveOrderListService retrieveOrderListService) {
this.orderCreationService = orderCreationService;
this.retrieveOrderListService = retrieveOrderListService;
}

@GetMapping("/orders")
public RetrieveOrderListResponse retrieveOrderList(@AuthenticationPrincipal LoginVendor loginVendor) {
RetrieveOrderListCommand command = new RetrieveOrderListCommand(loginVendor.getId());
return new RetrieveOrderListResponse(retrieveOrderListService.retrieveOrderListOfVendorStores(command));
}

@GetMapping("/orders/stores/{storeId}")
public RetrieveOrderListResponse retrieveOrderListByStore(@AuthenticationPrincipal LoginVendor loginVendor,
@PathVariable(name = "storeId") Long storeId) {
RetrieveOrderListCommand command = new RetrieveOrderListCommand(storeId, loginVendor.getId());
return new RetrieveOrderListResponse(retrieveOrderListService.retrieveOrderListOfStore(command));
}

@PostMapping("/orders")
Expand All @@ -28,5 +50,6 @@ public OrderCreationResponse order(@AuthenticationPrincipal LoginCustomer loginC
Long createdId = orderCreationService.create(command);
log.info("Created order for customer {} with id {}", loginCustomer.getId(), createdId);
return new OrderCreationResponse(createdId);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package camp.woowak.lab.web.api.order;

import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;

import camp.woowak.lab.common.advice.DomainExceptionHandler;
import camp.woowak.lab.common.exception.HttpStatusException;
import camp.woowak.lab.store.exception.NotEqualsOwnerException;
import camp.woowak.lab.store.exception.NotFoundStoreException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@DomainExceptionHandler(basePackageClasses = OrderApiController.class)
public class OrderExceptionHandler {
@ExceptionHandler(value = NotFoundStoreException.class)
public ProblemDetail handleNotFoundStoreException(NotFoundStoreException e) {
log.error("Not Found", e);
return getProblemDetail(HttpStatus.NOT_FOUND, e);
}

@ExceptionHandler(value = NotEqualsOwnerException.class)
public ProblemDetail handleNotEqualsOwnerException(NotEqualsOwnerException e) {
log.error("Not Equals Owner", e);
return getProblemDetail(HttpStatus.UNAUTHORIZED, e);
}

private ProblemDetail getProblemDetail(HttpStatus status, HttpStatusException e) {
return ProblemDetail.forStatusAndDetail(status, e.errorCode().getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package camp.woowak.lab.web.dto.response.order;

import java.util.List;

import camp.woowak.lab.order.service.dto.OrderDTO;

public record RetrieveOrderListResponse(List<OrderDTO> orders) {
}
2 changes: 1 addition & 1 deletion src/test/java/camp/woowak/lab/cart/domain/CartTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CartTest implements CartFixture {
private List<CartItem> cartItemList;
private Cart cart;
private Menu menu;
private int minPrice = 8000;
private final int minPrice = 8000;
private Store store;
private Vendor vendor;

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

import java.time.LocalDate;

import camp.woowak.lab.store.TestStore;
import camp.woowak.lab.store.domain.StoreCategory;
import camp.woowak.lab.vendor.domain.Vendor;

public interface StoreFixture {
default TestStore createTestStore(Long id, Vendor owner) {
return new TestStore(id
, owner, new StoreCategory("양식"), "3K1K 가게", "송파", "02-1234-5678", 5000,
LocalDate.now().atTime(6, 0), LocalDate.now().atTime(23, 0));
}
}
8 changes: 8 additions & 0 deletions src/test/java/camp/woowak/lab/fixture/VendorFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import java.util.UUID;

import camp.woowak.lab.payaccount.domain.PayAccount;
import camp.woowak.lab.payaccount.domain.TestPayAccount;
import camp.woowak.lab.vendor.TestVendor;
import camp.woowak.lab.vendor.domain.Vendor;
import camp.woowak.lab.web.authentication.NoOpPasswordEncoder;
import camp.woowak.lab.web.authentication.PasswordEncoder;

public interface VendorFixture {
Expand All @@ -22,4 +24,10 @@ default Vendor createVendor(PayAccount payAccount, PasswordEncoder passwordEncod
return new Vendor("vendorName", "[email protected]", "vendorPassword", "010-0000-0000", payAccount,
passwordEncoder);
}

default TestVendor createTestVendor() {
return new TestVendor(UUID.randomUUID(), "vendorName", "[email protected]", "vendorPassword",
"010-0000-0000", new TestPayAccount(1L),
new NoOpPasswordEncoder());
}
}
Loading

0 comments on commit 3d4fd5c

Please sign in to comment.