Skip to content

Commit 410b2a1

Browse files
authored
Merge pull request #21 from junoade/week4-feature-order
feat: Order 서비스 개선
2 parents 4ec2d62 + 46cbaa4 commit 410b2a1

File tree

18 files changed

+553
-64
lines changed

18 files changed

+553
-64
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.domain.order.OrderItemModel;
4+
5+
import java.util.List;
6+
7+
public class OrderCommand {
8+
/**
9+
* 주문 유즈케이스에 대한 입력 DTO 입니다.
10+
* 선조회/실행에서 동일한 DTO를 사용합니다.
11+
* @param userId
12+
* @param orderLineRequests
13+
*/
14+
public record Order (
15+
String userId,
16+
List<OrderLine> orderLineRequests
17+
) { }
18+
19+
/**
20+
* 주문 유즈케이스에서 주문 항목에 대한 입력 DTO 입니다.
21+
* 선조회/실행에서 동일한 DTO를 사용합니다.
22+
* @param productId
23+
* @param quantity
24+
*/
25+
public record OrderLine(
26+
Long productId,
27+
int quantity
28+
) {
29+
public static OrderLine from(OrderItemModel item) {
30+
return new OrderLine(
31+
item.getProduct().getId(),
32+
item.getQuantity()
33+
);
34+
}
35+
}
36+
}

apps/commerce-api/src/main/java/com/loopers/application/order/OrderLineCommand.java

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.loopers.application.order;
2+
3+
import java.util.List;
4+
5+
public class OrderResult {
6+
public record PreOrderResult(
7+
String userId,
8+
int requiringPoints,
9+
List<OrderCommand.OrderLine> successLines,
10+
List<OrderCommand.OrderLine> failedLines
11+
) {
12+
public static PreOrderResult of(String userId,
13+
int successPoint,
14+
List<OrderCommand.OrderLine> successLines,
15+
List<OrderCommand.OrderLine> failedLines) {
16+
return new PreOrderResult(userId, successPoint, successLines, failedLines);
17+
}
18+
}
19+
20+
public record PlaceOrderResult(
21+
String userId,
22+
Long orderId,
23+
int normalPrice,
24+
int errorPrice,
25+
List<OrderCommand.OrderLine> successLines,
26+
List<OrderCommand.OrderLine> failedLines
27+
) {
28+
public static PlaceOrderResult of(String userId,
29+
Long orderId,
30+
int normalPrice,
31+
int errorPrice,
32+
List<OrderCommand.OrderLine> successLines,
33+
List<OrderCommand.OrderLine> failedLines) {
34+
return new PlaceOrderResult(
35+
userId,
36+
orderId,
37+
normalPrice,
38+
errorPrice,
39+
successLines,
40+
failedLines);
41+
}
42+
}
43+
44+
}

apps/commerce-api/src/main/java/com/loopers/application/order/StockDecreaseResult.java

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.loopers.application.order;
2+
3+
import java.util.List;
4+
5+
public record StockResult(
6+
List<OrderCommand.OrderLine> successLines,
7+
List<OrderCommand.OrderLine> failedLines,
8+
int requiringPrice,
9+
int errorPrice
10+
) {
11+
public static StockResult of(
12+
List<OrderCommand.OrderLine> successLines,
13+
List<OrderCommand.OrderLine> failedLines,
14+
int requiringPrice,
15+
int errorPrice
16+
) {
17+
return new StockResult(successLines,
18+
failedLines,
19+
requiringPrice,
20+
errorPrice);
21+
}
22+
}
Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package com.loopers.application.order;
22

3+
import com.loopers.domain.order.OrderItemModel;
4+
import com.loopers.domain.order.OrderItemStatus;
35
import com.loopers.domain.order.OrderModel;
46
import com.loopers.domain.order.OrderService;
7+
import com.loopers.domain.product.ProductModel;
58
import com.loopers.domain.product.ProductService;
69
import com.loopers.domain.user.UserModel;
710
import com.loopers.domain.user.UserService;
11+
import com.loopers.support.error.CoreException;
12+
import com.loopers.support.error.ErrorType;
813
import lombok.RequiredArgsConstructor;
914
import org.springframework.stereotype.Service;
1015
import org.springframework.transaction.annotation.Transactional;
@@ -20,42 +25,112 @@ public class UserOrderProductFacade {
2025
private final UserService userService;
2126

2227
@Transactional(readOnly = true)
23-
public boolean preOrder(Long userPkId, List<OrderLineCommand> orderLines) {
24-
productService.markCurrentStockStatus(orderLines);
25-
UserModel userModel = userService.getUser(userPkId);
26-
Integer total = productService.getTotalAmountOfAvailStock(orderLines);
27-
return userModel.hasEnoughPoint(total);
28+
public OrderResult.PreOrderResult preOrder(OrderCommand.Order orderCommand) {
29+
UserModel userModel = userService.getUser(orderCommand.userId());
30+
StockResult result = readAllStocks(orderCommand.orderLineRequests());
31+
if(!userModel.hasEnoughPoint(result.requiringPrice())) {
32+
throw new CoreException(ErrorType.BAD_REQUEST, "포인트가 부족합니다.");
33+
}
34+
35+
return OrderResult.PreOrderResult.of(
36+
userModel.getUserId(),
37+
result.requiringPrice(),
38+
result.successLines(),
39+
result.failedLines()
40+
);
2841
}
2942

43+
/**
44+
*
45+
* @param orderCommand
46+
*/
3047
@Transactional
31-
public void placeOrder(Long userPkId, List<OrderLineCommand> orderLines) {
32-
OrderModel order = orderService.putOrder(orderLines);
33-
StockDecreaseResult stockResult = decreaseAllStocks(orderLines);
34-
// TODO 클린 아키텍처 고려하기
35-
orderService.putFailStatus(order, stockResult.failedLines());
36-
Integer totalAmountPoint = stockResult.totalAmount();
37-
38-
// 포인트 부족 시 예외 → 전체 롤백
39-
userService.decreaseUserPoint(userPkId, totalAmountPoint);
48+
public OrderResult.PlaceOrderResult placeOrder(OrderCommand.Order orderCommand) {
49+
UserModel userModel = userService.getUser(orderCommand.userId());
50+
List<OrderItemModel> orderItems = toDomainOrderItem(orderCommand.orderLineRequests());
51+
OrderModel orderModel = orderService.createPendingOrder(userModel, orderItems);
52+
53+
StockResult stockResult = decreaseAllStocks(orderItems);
54+
55+
Integer requiringPoints = stockResult.requiringPrice();
56+
// 포인트 부족 시 롤백
57+
if(!userModel.hasEnoughPoint(requiringPoints)) {
58+
throw new CoreException(ErrorType.BAD_REQUEST, "포인트가 부족합니다. 다시 확인해주세요");
59+
}
60+
61+
userService.decreaseUserPoint(userModel.getId(), requiringPoints);
62+
63+
boolean hasOutOfStockCase = !stockResult.failedLines().isEmpty();
64+
if(hasOutOfStockCase) {
65+
orderService.updateOrderAsPartialSuccess(orderModel, stockResult.requiringPrice() , stockResult.errorPrice());
66+
} else {
67+
orderService.updateOrderAsSuccess(orderModel, stockResult.requiringPrice());
68+
}
69+
70+
71+
return OrderResult.PlaceOrderResult.of(
72+
userModel.getUserId(),
73+
orderModel.getId(),
74+
stockResult.requiringPrice(),
75+
stockResult.errorPrice(),
76+
stockResult.successLines(),
77+
stockResult.failedLines()
78+
);
4079
}
4180

4281
@Transactional
43-
protected StockDecreaseResult decreaseAllStocks(List<OrderLineCommand> lines) {
44-
List<OrderLineCommand> success = new ArrayList<>();
45-
List<OrderLineCommand> failed = new ArrayList<>();
46-
int total = 0;
82+
protected StockResult decreaseAllStocks(List<OrderItemModel> items) {
83+
List<OrderCommand.OrderLine> success = new ArrayList<>();
84+
List<OrderCommand.OrderLine> failed = new ArrayList<>();
85+
int total = 0, fail = 0;
4786

48-
for (OrderLineCommand line : lines) {
87+
for (OrderItemModel item : items) {
88+
ProductModel p = item.getProduct();
89+
boolean ok = productService.decreaseStock(p.getId(), item.getQuantity());
90+
OrderCommand.OrderLine line = OrderCommand.OrderLine.from(item);
91+
int requiringPoint = productService.getPrice(p.getId(), item.getQuantity());
92+
if (ok) {
93+
item.setStatus(OrderItemStatus.SUCCESS);
94+
total += requiringPoint;
95+
success.add(line);
96+
} else {
97+
item.setStatus(OrderItemStatus.FAILED);
98+
fail += requiringPoint;
99+
failed.add(line);
100+
}
101+
}
102+
103+
return StockResult.of(success, failed, total, fail);
104+
}
105+
106+
protected StockResult readAllStocks(List<OrderCommand.OrderLine> lines) {
107+
List<OrderCommand.OrderLine> success = new ArrayList<>();
108+
List<OrderCommand.OrderLine> failed = new ArrayList<>();
109+
int requiringPrice = 0, errorPrice = 0;
110+
111+
for (OrderCommand.OrderLine line : lines) {
49112
// TODO 엔티티 클래스에서 예외 발생시 포인트 계산 제대로 되는지 확인 필요
50-
boolean ok = productService.decreaseStock(line.productId(), line.quantity());
113+
boolean ok = productService.hasStock(line.productId(), line.quantity());
114+
int point = productService.getPrice(line.productId(), line.quantity());
51115
if (ok) {
52116
success.add(line);
53-
total += productService.getPrice(line.productId(), line.quantity());
117+
requiringPrice += point;
54118
} else {
55119
failed.add(line);
120+
errorPrice += point;
56121
}
57122
}
123+
return StockResult.of(success, failed, requiringPrice, errorPrice);
124+
}
125+
126+
127+
public List<OrderItemModel> toDomainOrderItem(List<OrderCommand.OrderLine> lines) {
128+
return lines.stream()
129+
.map(l -> {
130+
ProductModel p = productService.getProductDetail(l.productId());
131+
return OrderItemModel.of(p, l.quantity());
132+
})
133+
.toList();
58134

59-
return StockDecreaseResult.of(success, failed, total);
60135
}
61136
}

apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItemModel.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
@Entity
88
@Table(name = "orders_item",
99
uniqueConstraints =
10-
@UniqueConstraint(name = "uk_order_item",
11-
columnNames = {"orders_id", "product_id"}
12-
)
10+
@UniqueConstraint(name = "uk_order_item",
11+
columnNames = {"orders_id", "product_id"}
12+
)
1313
)
1414
public class OrderItemModel extends BaseEntity {
1515

1616
@Column(length = 50)
1717
private String name;
1818
private Integer price;
19+
private Integer quantity;
20+
21+
@Enumerated(EnumType.STRING)
22+
private OrderItemStatus status;
1923

2024
@ManyToOne(fetch = FetchType.LAZY, optional = false)
2125
@JoinColumn(name = "orders_id", nullable = false)
@@ -24,4 +28,40 @@ public class OrderItemModel extends BaseEntity {
2428
@ManyToOne(fetch = FetchType.LAZY, optional = false)
2529
@JoinColumn(name = "product_id", nullable = false)
2630
private ProductModel product;
31+
32+
protected OrderItemModel() {}
33+
34+
public static OrderItemModel of(ProductModel product, int quantity) {
35+
OrderItemModel orderItem = new OrderItemModel();
36+
37+
orderItem.product = product;
38+
orderItem.quantity = quantity;
39+
orderItem.name = product.getName();
40+
orderItem.price = product.getPrice();
41+
return orderItem;
42+
}
43+
44+
public void setOrders(OrderModel orders) {
45+
this.orders = orders;
46+
}
47+
48+
public void setStatus(OrderItemStatus status) {
49+
this.status = status;
50+
}
51+
52+
public Integer getPrice() {
53+
return price;
54+
}
55+
56+
public String getName() {
57+
return name;
58+
}
59+
60+
public ProductModel getProduct() {
61+
return product;
62+
}
63+
64+
public Integer getQuantity() {
65+
return quantity;
66+
}
2767
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.loopers.domain.order;
2+
3+
public enum OrderItemStatus {
4+
WAITING,
5+
SUCCESS,
6+
FAILED
7+
}

0 commit comments

Comments
 (0)