Skip to content

Commit

Permalink
[feat] 판매 금액 정산 - 모든 점주 및 OrderPayment 목록 조회, OrderPayment 상태 일괄 갱신 (#93
Browse files Browse the repository at this point in the history
)

* [refactor] 메뉴 가격 데이터 타입을 Long 으로 통일

* [refactor] 메뉴 가격 데이터 타입을 Long 으로 통일

* [feat] 모든 점주를 조회하는 기능

* [feat] 각 점주의 정산해야할 OrderPayment 목록을 조회하는 기능

* [feat] PointPayment -> OrderPayment 이름 변경 및 생성자 추가

* [feat] 정산해야할 OrderPayment 가 올바른지 검증하는 기능

- Vendor가 동일한지 검증
- OrderPaymentStatus 상태 검증

* [feat] OrderPayment 목록의 OrderPaymentStatus 상태를 ADJUSTMENT_SUCCESS 로 갱신하는 기능

* [style] 주문 금액 정산 기능 뼈대 코드 TODO 작성

* [feat] VendorId와 OrderPaymentStatus 기준으로 OrderPayment 목록을 조회하는 기능

* [feat] OrderPaymentStatus 를 일괄 업데이트하는 기능

- clearAutomatically 옵션 true 설정
영속성 컨텍스트에서 관리하는 엔티티와 DB 데이터의 동기화 이슈를 예방하기 위함

* [feat] OrderPaymentStatus 상태 Enum

* [feat] @Getter Lombok 추가

* [style] 에러 코드 수정

* [fix] 주문 가격 타입을 long 으로 변경하여 컴파일 오류 수정

* [fix] OrderItem 패키지 이동 변경사항 반영하여 컴파일 오류 수정
  • Loading branch information
june-777 authored Aug 18, 2024
1 parent e19b259 commit 922ed68
Show file tree
Hide file tree
Showing 18 changed files with 253 additions and 73 deletions.
4 changes: 2 additions & 2 deletions src/main/java/camp/woowak/lab/menu/domain/Menu.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class Menu {
private String name;

@Column(nullable = false)
private Integer price;
private Long price;

@Column(nullable = false)
private Long stockCount;
Expand All @@ -44,7 +44,7 @@ public class Menu {
private String imageUrl;

public Menu(Store store, MenuCategory menuCategory, String name,
Integer price, Long stockCount, String imageUrl
Long price, Long stockCount, String imageUrl
) {
MenuValidator.validate(store, menuCategory, name, price, stockCount, imageUrl);
this.store = store;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/camp/woowak/lab/menu/domain/MenuValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class MenuValidator {
private static final int MAX_NAME_LENGTH = 10;

public static void validate(final Store store, final MenuCategory menuCategory, final String name,
final Integer price, final Long stockCount, final String imageUrl) {
final Long price, final Long stockCount, final String imageUrl) {
validateNotNull(store, menuCategory, name, price, stockCount, imageUrl);
validateNotBlank(name, imageUrl);
validateNameLength(name);
Expand Down Expand Up @@ -40,7 +40,7 @@ private static void validateNameLength(final String name) {
}
}

private static void validatePriceNegative(final Integer price) {
private static void validatePriceNegative(final Long price) {
if (price <= 0) {
throw new InvalidMenuCreationException(INVALID_PRICE, "메뉴의 가격은 양수만 가능합니다. cur: " + price);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public enum MenuErrorCode implements ErrorCode {
INVALID_PRICE(HttpStatus.BAD_REQUEST, "m_6", "메뉴의 가격 범위를 벗어났습니다."),
INVALID_STOCK_COUNT(HttpStatus.BAD_REQUEST, "m_7", "메뉴의 재고 개수는 1개 이상이어야 합니다."),

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

private final int status;
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/camp/woowak/lab/order/domain/vo/OrderItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
@Embeddable
public class OrderItem {
private Long menuId;
private int price;
private long price;
private int quantity;
private int totalPrice;
private long totalPrice;

protected OrderItem() {
}

public OrderItem(Long menuId, int price, int quantity) {
public OrderItem(Long menuId, long price, int quantity) {
this.menuId = menuId;
this.price = price;
this.quantity = quantity;
this.totalPrice = price * quantity;
}

public int getTotalPrice() {
public long getTotalPrice() {
return totalPrice;
}
}
70 changes: 70 additions & 0 deletions src/main/java/camp/woowak/lab/payment/domain/OrderPayment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package camp.woowak.lab.payment.domain;

import java.time.LocalDateTime;

import camp.woowak.lab.customer.domain.Customer;
import camp.woowak.lab.order.domain.Order;
import camp.woowak.lab.vendor.domain.Vendor;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class OrderPayment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Order order;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sendor_id", nullable = false)
private Customer sender;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "recipient_id", nullable = false)
private Vendor recipient;

@Enumerated(value = EnumType.STRING)
private OrderPaymentStatus orderPaymentStatus;

private LocalDateTime createdAt;

public OrderPayment(Order order, Customer sender, Vendor recipient,
OrderPaymentStatus orderPaymentStatus, LocalDateTime createdAt
) {
this.order = order;
this.sender = sender;
this.recipient = recipient;
this.orderPaymentStatus = orderPaymentStatus;
this.createdAt = createdAt;
}

public void validateReadyToAdjustment(final Vendor adjustmentTarget) {
if (isEqualsRecipient(adjustmentTarget) && orderPaymentStatusIsSuccess()) {
return;
}
throw new IllegalArgumentException();
}

private boolean isEqualsRecipient(Vendor recipient) {
return this.recipient.equals(recipient);
}

private boolean orderPaymentStatusIsSuccess() {
return this.orderPaymentStatus.equals(OrderPaymentStatus.ORDER_SUCCESS);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package camp.woowak.lab.payment.domain;

public enum OrderPaymentStatus {
ORDER_SUCCESS,
ADJUSTMENT_SUCCESS,
}
24 changes: 0 additions & 24 deletions src/main/java/camp/woowak/lab/payment/domain/PointPayment.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package camp.woowak.lab.payment.repository;

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

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

import camp.woowak.lab.payment.domain.OrderPayment;
import camp.woowak.lab.payment.domain.OrderPaymentStatus;

public interface OrderPaymentRepository extends JpaRepository<OrderPayment, Long> {

@Query("SELECT op FROM OrderPayment op "
+ "WHERE op.recipient.id = :recipientId "
+ "AND op.orderPaymentStatus = :orderPaymentStatus")
List<OrderPayment> findByRecipientIdAndOrderPaymentStatus(@Param("recipientId") UUID recipientId,
@Param("orderPaymentStatus") OrderPaymentStatus orderPaymentStatus);

@Modifying(clearAutomatically = true)
@Query("UPDATE OrderPayment op "
+ "SET op.orderPaymentStatus = :newStatus "
+ "WHERE op.id IN :ids")
int updateOrderPaymentStatus(@Param("ids") List<Long> ids, @Param("newStatus") OrderPaymentStatus newStatus);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package camp.woowak.lab.payment.service;

import java.util.List;

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

import camp.woowak.lab.menu.repository.MenuRepository;
import camp.woowak.lab.order.domain.vo.OrderItem;
import camp.woowak.lab.payment.domain.OrderPayment;
import camp.woowak.lab.payment.domain.OrderPaymentStatus;
import camp.woowak.lab.payment.repository.OrderPaymentRepository;
import camp.woowak.lab.vendor.domain.Vendor;
import camp.woowak.lab.vendor.exception.NotFoundVendorException;
import camp.woowak.lab.vendor.repository.VendorRepository;
import lombok.RequiredArgsConstructor;

/**
* 정산을 담당하는 서비스
*/
@Service
@RequiredArgsConstructor
public class OrderPaymentAdjustmentService {

private final VendorRepository vendorRepository;
private final MenuRepository menuRepository;
private final OrderPaymentRepository orderPaymentRepository;

/**
* @throws NotFoundVendorException vendorId에 해당하는 점주를 찾을 수 없을 떄
*/
@Transactional
public void adjustment() {
// 1. 모든 점주 조회
List<Vendor> vendors = findAllVendors();
for (Vendor vendor : vendors) {
// 2. 각 점주의 정산해야할 OrderPayment 목록을 조회
List<OrderPayment> orderPayments = findOrderPaymentsToAdjustment(vendor);

// 3. 각 OrderPayment 의 Order 주문 금액을 계산
Long totalOrderPrice = calculateTotalOrderPrice(orderPayments);

// 4. 총 주문 금액에서 수수료 5%를 계산
Double commission = calculateCommission(totalOrderPrice);

// 5. 수수료를 제외한 금액을 점주에게 송금

// 6. 송금을 성공하면, OrderPayment 목록의 OrderPaymentStatus 상태를 ADJUSTMENT_SUCCESS 로 갱신
updateOrderPaymentStatus(orderPayments);
}
}

// 모든 점주 조회
private List<Vendor> findAllVendors() {
return vendorRepository.findAll();
}

// 각 점주의 정산해야할 OrderPayment 목록을 조회
private List<OrderPayment> findOrderPaymentsToAdjustment(final Vendor vendor) {
List<OrderPayment> orderPayments = orderPaymentRepository.findByRecipientIdAndOrderPaymentStatus(
vendor.getId(), OrderPaymentStatus.ORDER_SUCCESS);

for (OrderPayment orderPayment : orderPayments) {
// 정산해야할 OrderPayment 가 맞는지 검증
orderPayment.validateReadyToAdjustment(vendor);
}

return orderPayments;
}

// TODO: OrderPayment 목록의 Order 총 주문 금액을 계산한다.
// 메뉴의 가격은 계속 바뀔 수 있어서, 정산 시점에 주문 - 메뉴 정보로 금액을 다시 계산하는 부분이 제약이 있음
private long calculateTotalOrderPrice(final List<OrderPayment> orderPayments) {
Long totalOrderPrice = 0L;

return totalOrderPrice;
}

// TODO: 각 OrderPayment 의 Order 주문 금액을 계산한다.
private Long calculateOrderPrice(List<OrderItem> orderItems) {
long totalOrderPrice = 0L;

return totalOrderPrice;
}

// 총 주문 금액에서 수수료 5%를 계산한다.
private Double calculateCommission(Long totalOrderPrice) {

return 0.0;
}

// OrderPayment 목록의 OrderPaymentStatus 상태를 ADJUSTMENT_SUCCESS 로 갱신
private void updateOrderPaymentStatus(List<OrderPayment> orderPayments) {
orderPaymentRepository.updateOrderPaymentStatus(
orderPayments.stream().map(OrderPayment::getId).toList(),
OrderPaymentStatus.ADJUSTMENT_SUCCESS);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public record MenuLineItem(
String categoryName,

@NotNull(message = "음식 상품 가격은 필수값입니다.")
Integer price
Long price

) {
}
6 changes: 3 additions & 3 deletions src/test/java/camp/woowak/lab/cart/domain/CartTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void setUp() {
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);
menu = createSavedMenu(1L, store, new MenuCategory(store, "중식"), "짜장면", 9000L);
}

@Nested
Expand All @@ -70,7 +70,7 @@ void storeNotOpenExceptionTest() {
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);
Menu closedMenu = createSavedMenu(2L, closedStore, new MenuCategory(closedStore, "중식"), "짬뽕", 9000L);

//when & then
assertThatThrownBy(() -> cart.addMenu(closedMenu))
Expand All @@ -87,7 +87,7 @@ void otherStoreMenuExceptionTest() {
Store otherStore = createSavedStore(2L, vendor, "otherStore", 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);
Menu otherStoreMenu = createSavedMenu(2L, otherStore, new MenuCategory(otherStore, "중식"), "짬뽕", 9000L);

//when & then
assertThatThrownBy(() -> cart.addMenu(otherStoreMenu))
Expand Down
14 changes: 7 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 @@ -82,7 +82,7 @@ void setUp() {

menuCategory = createMenuCategory(store, "중식");

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

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

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

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

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

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

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

Expand All @@ -202,7 +202,7 @@ private MenuCategory createMenuCategory(Store store, String name) {
return menuCategoryRepository.saveAndFlush(menuCategory1);
}

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

Expand Down
Loading

0 comments on commit 922ed68

Please sign in to comment.