-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathFoodOrderService.java
More file actions
199 lines (152 loc) · 7.3 KB
/
Copy pathFoodOrderService.java
File metadata and controls
199 lines (152 loc) · 7.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package cgv_23rd.ceos.service;
import cgv_23rd.ceos.dto.food.request.FoodOrderItemRequestDto;
import cgv_23rd.ceos.dto.food.request.FoodOrderRequestDto;
import cgv_23rd.ceos.entity.food.Food;
import cgv_23rd.ceos.entity.food.FoodOrder;
import cgv_23rd.ceos.entity.food.FoodOrderItem;
import cgv_23rd.ceos.entity.food.TheaterFood;
import cgv_23rd.ceos.entity.theater.Theater;
import cgv_23rd.ceos.entity.user.User;
import cgv_23rd.ceos.global.apiPayload.code.GeneralErrorCode;
import cgv_23rd.ceos.global.apiPayload.exception.GeneralException;
import cgv_23rd.ceos.repository.food.FoodOrderRepository;
import cgv_23rd.ceos.repository.food.FoodRepository;
import cgv_23rd.ceos.repository.food.TheaterFoodRepository;
import cgv_23rd.ceos.repository.theater.TheaterRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Comparator;
import java.util.List;
@Service
@RequiredArgsConstructor
public class FoodOrderService {
private final FoodRepository foodRepository;
private final FoodOrderRepository foodOrderRepository;
private final TheaterRepository theaterRepository;
private final TheaterFoodRepository theaterFoodRepository;
private final UserService userService;
// 1. 음식 주문
@Transactional
public Long createFoodOrder(Long userId, FoodOrderRequestDto requestDto) {
User user = userService.getUser(userId);
Theater theater = getTheater(requestDto.theaterId());
FoodOrder foodOrder = FoodOrder.create(user, theater);
for (FoodOrderItemRequestDto itemDto : requestDto.items()) {
Food food = getFood(itemDto.foodId());
theaterFoodRepository.findByTheaterAndFood(theater, food)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.THEATER_FOOD_NOT_FOUND));
foodOrder.addItem(food, itemDto.quantity());
}
foodOrderRepository.save(foodOrder);
return foodOrder.getId();
}
@Transactional(readOnly = true)
public FoodOrder getFoodOrder(Long orderId) {
return foodOrderRepository.findById(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
}
@Transactional(readOnly = true)
public FoodOrder getOwnedFoodOrder(Long userId, Long orderId) {
FoodOrder order = foodOrderRepository.findByIdWithDetails(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!order.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
return order;
}
@Transactional
public void preparePayment(Long userId, Long orderId, String paymentId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
foodOrder.assignPaymentId(paymentId);
}
@Transactional
public void confirmOrderAndDeductStock(Long userId, Long orderId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
if (foodOrder.getStatus() != cgv_23rd.ceos.entity.enums.FoodOrderStatus.대기) {
throw new GeneralException(GeneralErrorCode.PAYMENT_ALREADY_PROCESSED);
}
List<FoodOrderItem> sortedItems = foodOrder.getFoodOrderItems().stream()
.sorted(Comparator.comparing(item -> item.getFood().getId()))
.toList();
for (FoodOrderItem item : sortedItems) {
// 비관적 락으로 재고 조회하여 동시성 제어
TheaterFood theaterFood = theaterFoodRepository.findByTheaterAndFoodWithLock(foodOrder.getTheater(), item.getFood())
.orElseThrow(() -> new GeneralException(GeneralErrorCode.THEATER_FOOD_NOT_FOUND));
theaterFood.decreaseStock(item.getQuantity());
}
foodOrder.markPaymentPaid();
foodOrder.confirm();
}
@Transactional
public void markPaymentFailed(Long userId, Long orderId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
foodOrder.markPaymentFailed();
}
@Transactional
public void markPaymentUnknown(Long userId, Long orderId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
foodOrder.markPaymentUnknown();
}
@Transactional
public void cancelOrderAfterPaymentCancellation(Long userId, Long orderId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
List<FoodOrderItem> sortedItems = foodOrder.getFoodOrderItems().stream()
.sorted(Comparator.comparing(item -> item.getFood().getId()))
.toList();
for (FoodOrderItem item : sortedItems) {
TheaterFood theaterFood = theaterFoodRepository.findByTheaterAndFoodWithLock(foodOrder.getTheater(), item.getFood())
.orElseThrow(() -> new GeneralException(GeneralErrorCode.THEATER_FOOD_NOT_FOUND));
theaterFood.increaseStock(item.getQuantity());
}
foodOrder.markPaymentCancelled();
foodOrder.cancelAfterPaymentCancellation();
}
@Transactional
public void cancelPendingOrder(Long userId, Long orderId) {
FoodOrder foodOrder = foodOrderRepository.findByIdWithLock(orderId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_ORDER_NOT_FOUND));
validateUserExists(userId);
if (!foodOrder.isOwnedBy(userId)) {
throw new GeneralException(GeneralErrorCode.FORBIDDEN);
}
foodOrder.cancel();
}
private Theater getTheater(Long theaterId) {
return theaterRepository.findById(theaterId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.THEATER_NOT_FOUND));
}
private Food getFood(Long foodId) {
return foodRepository.findById(foodId)
.orElseThrow(() -> new GeneralException(GeneralErrorCode.FOOD_NOT_FOUND));
}
private void validateUserExists(Long userId) {
userService.getUser(userId);
}
}