Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/78_june-777_판…
Browse files Browse the repository at this point in the history
…매금액정산
  • Loading branch information
june-777 committed Aug 20, 2024
2 parents d61ebf4 + cc2ebcb commit ad219ca
Show file tree
Hide file tree
Showing 59 changed files with 3,373 additions and 13 deletions.
22 changes: 22 additions & 0 deletions src/main/java/camp/woowak/lab/menu/domain/Menu.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package camp.woowak.lab.menu.domain;

import org.hibernate.annotations.DynamicUpdate;

import camp.woowak.lab.menu.exception.InvalidMenuPriceUpdateException;
import camp.woowak.lab.menu.exception.NotEnoughStockException;
import camp.woowak.lab.store.domain.Store;
import jakarta.persistence.Column;
Expand All @@ -17,6 +20,7 @@
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@DynamicUpdate
public class Menu {

@Id
Expand Down Expand Up @@ -65,4 +69,22 @@ public void decrementStockCount(int amount) {
}
stockCount -= amount;
}

public long updatePrice(long uPrice) {
if (uPrice <= 0) {
throw new InvalidMenuPriceUpdateException("메뉴의 가격은 0원보다 커야합니다. 입력값 : " + uPrice);
}
this.price = uPrice;

return this.price;
}

public Long getMenuCategoryId() {
return menuCategory.getId();
}

public String getMenuCategoryName() {
return menuCategory.getName();
}

}
5 changes: 2 additions & 3 deletions src/main/java/camp/woowak/lab/menu/domain/MenuCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
Expand All @@ -22,6 +23,7 @@
@UniqueConstraint(name = "unique_store_name", columnNames = {"store_id", "name"})
}
)
@Getter
public class MenuCategory {

@Id
Expand All @@ -41,8 +43,5 @@ public MenuCategory(Store store, String name) {
this.name = name;
}

public Long getId() {
return id;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.menu.exception;

import camp.woowak.lab.common.exception.BadRequestException;

public class InvalidMenuPriceUpdateException extends BadRequestException {
public InvalidMenuPriceUpdateException(String message) {
super(MenuErrorCode.INVALID_PRICE, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.menu.exception;

import camp.woowak.lab.common.exception.ConflictException;

public class InvalidMenuStockUpdateException extends ConflictException {
public InvalidMenuStockUpdateException(String message) {
super(MenuErrorCode.INVALID_UPDATE_MENU_STOCK, message);
}
}
11 changes: 10 additions & 1 deletion src/main/java/camp/woowak/lab/menu/exception/MenuErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ public enum MenuErrorCode implements ErrorCode {

NOT_FOUND_MENU(HttpStatus.BAD_REQUEST, "m_8", "메뉴를 찾을 수 없습니다."),
NOT_FOUND_MENU_CATEGORY(HttpStatus.BAD_REQUEST, "m_9", "메뉴 카테고리를 찾을 수 없습니다."),
NOT_ENOUGH_STOCK(HttpStatus.BAD_REQUEST, "M4", "재고가 부족합니다.");

MENU_OWNER_NOT_MATCH(HttpStatus.FORBIDDEN, "m_10", "메뉴는 가게의 점주만 수정할 수 있습니다."),

NOT_ENOUGH_STOCK(HttpStatus.BAD_REQUEST, "M4", "재고가 부족합니다."),

INVALID_UPDATE_MENU_STOCK(HttpStatus.CONFLICT, "m_11", "메뉴의 재고를 변경할 수 없습니다."),

NOT_EQUALS_OWNER(HttpStatus.BAD_REQUEST, "m_12", "매장의 점주와 일치하지 않습니다."),
NOT_UPDATABLE_TIME(HttpStatus.CONFLICT, "m_13", "메뉴를 변경할 수 없는 시간입니다."),
;

private final int status;
private final String errorCode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.menu.exception;

import camp.woowak.lab.common.exception.ForbiddenException;

public class MenuOwnerNotMatchException extends ForbiddenException {
public MenuOwnerNotMatchException(String message) {
super(MenuErrorCode.MENU_OWNER_NOT_MATCH, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.menu.exception;

import camp.woowak.lab.common.exception.BadRequestException;

public class NotEqualsOwnerException extends BadRequestException {
public NotEqualsOwnerException(String message) {
super(MenuErrorCode.NOT_EQUALS_OWNER, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.menu.exception;

import camp.woowak.lab.common.exception.ConflictException;

public class NotUpdatableTimeException extends ConflictException {
public NotUpdatableTimeException(String message) {
super(MenuErrorCode.NOT_UPDATABLE_TIME, message);
}
}
17 changes: 17 additions & 0 deletions src/main/java/camp/woowak/lab/menu/repository/MenuRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

Expand All @@ -18,4 +19,20 @@ public interface MenuRepository extends JpaRepository<Menu, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT m FROM Menu m where m.id in :ids")
List<Menu> findAllByIdForUpdate(List<Long> ids);

List<Menu> findByStoreId(Long storeId);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT m FROM Menu m WHERE m.id = :id")
Optional<Menu> findByIdForUpdate(Long id);

/**
*
* 메뉴의 재고를 변경합니다.
* TODO: [논의] @Transactional을 Respository 단에 안둬도되는가?
* Repository 에서 직접 접근할 때 사용자가 실수해서 @Transactional 을 빼먹을 수도 있다.
*/
@Modifying
@Query("UPDATE Menu m SET m.stockCount = :stock WHERE m.id = :id")
int updateStock(@Param("id") Long id, @Param("stock") int stock);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package camp.woowak.lab.menu.service;

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

import camp.woowak.lab.menu.domain.Menu;
import camp.woowak.lab.menu.exception.MenuOwnerNotMatchException;
import camp.woowak.lab.menu.exception.UnauthorizedMenuCategoryCreationException;
import camp.woowak.lab.menu.repository.MenuRepository;
import camp.woowak.lab.menu.service.command.MenuPriceUpdateCommand;
import camp.woowak.lab.order.exception.NotFoundMenuException;
import camp.woowak.lab.store.domain.Store;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class MenuPriceUpdateService {
private final MenuRepository menuRepository;

public MenuPriceUpdateService(MenuRepository menuRepository) {
this.menuRepository = menuRepository;
}

/**
* @throws camp.woowak.lab.menu.exception.InvalidMenuPriceUpdateException 업데이트 하려는 가격이 0 또는 음수인 경우
* @throws UnauthorizedMenuCategoryCreationException 자신의 가게의 메뉴가 아닌 메뉴를 업데이트 하려는 경우
* @throws NotFoundMenuException 존재하지 않는 메뉴의 가격을 업데이트 하려는 경우
*/
@Transactional
public long updateMenuPrice(MenuPriceUpdateCommand cmd) {
Menu menu = menuRepository.findByIdWithStore(cmd.menuId())
.orElseThrow(() -> {
log.info("등록되지 않은 메뉴 {}의 가격 수정을 시도했습니다.", cmd.menuId());
throw new NotFoundMenuException("등록되지 않은 Menu의 가격 수정을 시도했습니다.");
});

Store store = menu.getStore();
if (!store.isOwnedBy(cmd.vendorId())) {
log.info("권한없는 사용자 {}가 점포 {}의 메뉴 가격 수정을 시도했습니다.", cmd.vendorId(), store.getId());
throw new MenuOwnerNotMatchException("권한없는 사용자가 메뉴 가격 수정을 시도했습니다.");
}

long updatedPrice = menu.updatePrice(cmd.updatePrice());
log.info("Store({}) 의 메뉴({}) 가격을 ({})로 수정했습니다.", store.getId(), menu.getId(), cmd.updatePrice());

return updatedPrice;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package camp.woowak.lab.menu.service;

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

import camp.woowak.lab.cart.exception.MenuNotFoundException;
import camp.woowak.lab.menu.domain.Menu;
import camp.woowak.lab.menu.exception.InvalidMenuStockUpdateException;
import camp.woowak.lab.menu.exception.NotEqualsOwnerException;
import camp.woowak.lab.menu.exception.NotUpdatableTimeException;
import camp.woowak.lab.menu.repository.MenuRepository;
import camp.woowak.lab.menu.service.command.UpdateMenuStockCommand;

@Service
public class UpdateMenuStockService {
private final MenuRepository menuRepository;

public UpdateMenuStockService(MenuRepository menuRepository) {
this.menuRepository = menuRepository;
}

/**
*
* @throws MenuNotFoundException 메뉴를 찾을 수 없는 경우 발생한다.
* @throws NotEqualsOwnerException 메뉴를 소유한 가게의 주인이 아닌 경우 발생한다.
* @throws NotUpdatableTimeException 가게가 열려있지 않은 경우 발생한다.
* @throws InvalidMenuStockUpdateException 메뉴의 재고를 변경할 수 없는 경우 발생한다.
*/
@Transactional
public Long updateMenuStock(UpdateMenuStockCommand cmd) {
// 수량을 변경하려는 메뉴를 조회한다.
Menu targetMenu = findMenuByIdForUpdateOrThrow(cmd.menuId());

// 메뉴를 소유한 가게를 조회한다.
if (!targetMenu.getStore().isOwnedBy(cmd.vendorId())) {
throw new NotEqualsOwnerException("메뉴를 소유한 가게의 주인이 아닙니다.");
}

// 가게가 열려있는지 확인한다.
if (targetMenu.getStore().isOpen()) {
throw new NotUpdatableTimeException("가게가 열려 있습니다.");
}

// 메뉴의 재고를 변경한다.
int modifiedRow = menuRepository.updateStock(cmd.menuId(), cmd.stock());
if (modifiedRow != 1) { // 변경된 메뉴의 개수가 1이 아닌 경우 예외를 발생시킨다.
throw new InvalidMenuStockUpdateException("변경의 영향을 받은 메뉴의 개수가 1이 아닙니다.");
}

return targetMenu.getId();
}

private Menu findMenuByIdForUpdateOrThrow(Long menuId) {
return menuRepository.findByIdForUpdate(menuId).orElseThrow(() -> new MenuNotFoundException("메뉴를 찾을 수 없습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package camp.woowak.lab.menu.service.command;

import java.util.UUID;

/**
* @param vendorId 업데이트를 시도하는 vendor의 id
* @param menuId 업데이트 할 menu의 id
* @param updatePrice 업데이트 할 가격
*/
public record MenuPriceUpdateCommand(
UUID vendorId,
Long menuId,
Long updatePrice
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package camp.woowak.lab.menu.service.command;

import java.util.UUID;

public record UpdateMenuStockCommand(Long menuId, int stock, UUID vendorId) {
}
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 @@ -78,6 +78,14 @@ public Order(Customer requester, List<CartItem> cartItems,
this.createdAt = createdAt;
}

public Customer getRequester() {
return requester;
}

public Store getStore() {
return store;
}

public List<OrderItem> getOrderItems() {
return Collections.unmodifiableList(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();
}
}
Loading

0 comments on commit ad219ca

Please sign in to comment.