Skip to content

Commit 4411cae

Browse files
authored
Merge pull request #103 from TicketPing/feature/order
[refactor #102] 결제 완료 후 좌석 상태 변경 카프카로 변환
2 parents 31129e6 + 9681dc9 commit 4411cae

File tree

25 files changed

+204
-101
lines changed

25 files changed

+204
-101
lines changed

common/messaging/src/main/java/messaging/events/OrderCompletedEvent.java

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package messaging.events;
2+
3+
import java.util.UUID;
4+
5+
public record OrderCompletedForQueueTokenRemovalEvent(
6+
String userId,
7+
String performanceId
8+
) {
9+
public static OrderCompletedForQueueTokenRemovalEvent create(UUID userId, UUID performanceId) {
10+
return new OrderCompletedForQueueTokenRemovalEvent(
11+
userId.toString(),
12+
performanceId.toString()
13+
);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package messaging.events;
2+
3+
import java.util.UUID;
4+
5+
public record OrderCompletedForSeatReservationEvent(
6+
String scheduleId,
7+
String seatId
8+
) {
9+
public static OrderCompletedForSeatReservationEvent create(UUID scheduleId, UUID seatId) {
10+
return new OrderCompletedForSeatReservationEvent(
11+
scheduleId.toString(),
12+
seatId.toString()
13+
);
14+
}
15+
}

common/messaging/src/main/java/messaging/topics/OrderTopic.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
@RequiredArgsConstructor
88
public enum OrderTopic {
99

10-
COMPLETED("order-completed");
10+
COMPLETED_FOR_SEAT_RESERVATION("order-completed-for-seat-reservation"),
11+
COMPLETED_FOR_QUEUE_TOKEN_REMOVAL("order-completed-for-queue-token-removal");
1112

1213
private final String topic;
1314

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package com.ticketPing.order.application.client;
22

33
import org.springframework.http.ResponseEntity;
4-
import org.springframework.web.bind.annotation.PathVariable;
5-
import org.springframework.web.bind.annotation.RequestParam;
64
import performance.OrderSeatResponse;
7-
import performance.SeatResponse;
85
import response.CommonResponse;
96

107
import java.util.UUID;
@@ -13,6 +10,4 @@ public interface PerformanceClient {
1310
ResponseEntity<CommonResponse<OrderSeatResponse>> getOrderInfo(UUID userId, UUID scheduleId, UUID seatId);
1411

1512
ResponseEntity<CommonResponse<Object>> extendPreReserveTTL(UUID scheduleId, UUID seatId);
16-
17-
ResponseEntity<CommonResponse<SeatResponse>> updateSeatState(UUID seatId, Boolean seatState);
1813
}

services/order/src/main/java/com/ticketPing/order/application/service/EventApplicationService.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.ticketPing.order.application.service;
22

3+
import messaging.events.OrderCompletedForQueueTokenRemovalEvent;
4+
import messaging.events.OrderCompletedForSeatReservationEvent;
35
import messaging.utils.EventSerializer;
4-
import messaging.events.OrderCompletedEvent;
56
import lombok.RequiredArgsConstructor;
67
import org.springframework.kafka.core.KafkaTemplate;
78
import org.springframework.stereotype.Service;
@@ -13,9 +14,14 @@ public class EventApplicationService {
1314

1415
private final KafkaTemplate<String, String> kafkaTemplate;
1516

16-
public void publishOrderCompletedEvent(OrderCompletedEvent event) {
17+
public void publishForSeatReservation(OrderCompletedForSeatReservationEvent event) {
1718
String message = EventSerializer.serialize(event);
18-
kafkaTemplate.send(OrderTopic.COMPLETED.getTopic(), message);
19+
kafkaTemplate.send(OrderTopic.COMPLETED_FOR_SEAT_RESERVATION.getTopic(), message);
20+
}
21+
22+
public void publishForQueueTokenRemoval(OrderCompletedForQueueTokenRemovalEvent event) {
23+
String message = EventSerializer.serialize(event);
24+
kafkaTemplate.send(OrderTopic.COMPLETED_FOR_QUEUE_TOKEN_REMOVAL.getTopic(), message);
1925
}
2026

2127
}

services/order/src/main/java/com/ticketPing/order/application/service/OrderService.java

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@
66
import com.ticketPing.order.domain.model.entity.Order;
77
import com.ticketPing.order.domain.model.entity.OrderSeat;
88
import com.ticketPing.order.domain.model.enums.OrderStatus;
9-
import com.ticketPing.order.infrastructure.repository.OrderRepository;
10-
import messaging.events.OrderCompletedEvent;
9+
import com.ticketPing.order.domain.repository.OrderRepository;
1110
import exception.ApplicationException;
1211
import lombok.RequiredArgsConstructor;
1312
import lombok.extern.slf4j.Slf4j;
1413
import lombok.val;
14+
import messaging.events.OrderCompletedForQueueTokenRemovalEvent;
15+
import messaging.events.OrderCompletedForSeatReservationEvent;
1516
import org.springframework.stereotype.Service;
1617
import org.springframework.transaction.annotation.Transactional;
18+
import performance.OrderSeatResponse;
1719

1820
import java.util.List;
1921
import java.util.UUID;
20-
import caching.repository.RedisRepository;
21-
import performance.OrderSeatResponse;
2222

23-
import static caching.enums.RedisKeyPrefix.AVAILABLE_SEATS;
24-
import static com.ticketPing.order.common.exception.OrderExceptionCase.*;
23+
import static com.ticketPing.order.common.exception.OrderExceptionCase.DUPLICATED_ORDER;
24+
import static com.ticketPing.order.common.exception.OrderExceptionCase.ORDER_NOT_FOUND;
2525

2626
@Service
2727
@RequiredArgsConstructor
@@ -30,11 +30,8 @@ public class OrderService {
3030

3131
private final OrderRepository orderRepository;
3232
private final EventApplicationService eventApplicationService;
33-
private final RedisRepository redisRepository;
3433
private final PerformanceClient performanceClient;
3534

36-
private final static String TTL_PREFIX = "{Seat}:seat_ttl:";
37-
3835
@Transactional
3936
public OrderResponse createOrder(UUID scheduleId, UUID seatId, UUID userId) {
4037
validateDuplicateOrder(seatId);
@@ -48,30 +45,18 @@ public List<OrderResponse> getUserOrders(UUID userId) {
4845
return orders.stream().map(OrderResponse::from).toList();
4946
}
5047

51-
public OrderResponse validateOrderAndExtendTTL(UUID orderId, UUID userId) {
52-
Order order = validateOrder(orderId, userId);
48+
public void validateOrderAndExtendTTL(UUID orderId, UUID userId) {
49+
Order order = validateAndGetOrder(orderId, userId);
5350
performanceClient.extendPreReserveTTL(order.getScheduleId(), order.getOrderSeat().getSeatId());
54-
return OrderResponse.from(order);
5551
}
5652

5753
@Transactional
58-
public void updateOrderStatus(UUID orderId, UUID paymentId) {
54+
public void completeOrder(UUID orderId, UUID paymentId) {
5955
Order order = findOrderById(orderId);
60-
order.complete();
61-
62-
UUID performanceId = order.getPerformanceId();
63-
UUID scheduleId = order.getScheduleId();
64-
UUID seatId = order.getOrderSeat().getSeatId();
65-
66-
performanceClient.updateSeatState(order.getOrderSeat().getSeatId(), true);
56+
order.complete(paymentId);
6757

68-
String ttlKey = TTL_PREFIX + scheduleId + ":" + seatId + ":" + orderId;
69-
redisRepository.deleteKey(ttlKey);
70-
71-
String counterKey = AVAILABLE_SEATS.getValue() + performanceId;
72-
redisRepository.decrement(counterKey);
73-
74-
publishOrderCompletedEvent(order.getUserId(), performanceId);
58+
publishForSeatReservation(order.getScheduleId(), order.getOrderSeat().getSeatId());
59+
publishForQueueTokenRemoval(order.getUserId(), order.getPerformanceId());
7560
}
7661

7762
@Transactional
@@ -85,31 +70,39 @@ private Order saveOrderWithOrderSeat(UUID userId, OrderSeatResponse orderData) {
8570
return savedOrder;
8671
}
8772

88-
private Order findOrderById(UUID orderId){
89-
return orderRepository.findById(orderId)
90-
.orElseThrow(() -> new ApplicationException(ORDER_NOT_FOUND));
91-
}
92-
9373
private void validateDuplicateOrder(UUID seatId) {
94-
List<Order> duplicateOrders = orderRepository.findByOrderSeatSeatId(seatId)
95-
.stream()
96-
.filter(o -> o.getOrderStatus().equals(OrderStatus.PENDING) || o.getOrderStatus().equals(OrderStatus.COMPLETED))
97-
.toList();
74+
boolean hasDuplicate = orderRepository.existsByOrderSeatSeatIdAndOrderStatusIn(
75+
seatId, List.of(OrderStatus.PENDING, OrderStatus.COMPLETED));
9876

99-
if(!duplicateOrders.isEmpty())
100-
throw new ApplicationException(SEAT_ALREADY_TAKEN);
77+
if (hasDuplicate) {
78+
throw new ApplicationException(DUPLICATED_ORDER);
79+
}
10180
}
10281

103-
private Order validateOrder(UUID orderId, UUID userId) {
104-
Order order = findOrderById(orderId);
105-
if(!order.getOrderStatus().equals(OrderStatus.PENDING) || !order.getUserId().equals(userId))
82+
private Order validateAndGetOrder(UUID orderId, UUID userId) {
83+
Order order = orderRepository.findByIdAndOrderStatus(orderId, OrderStatus.PENDING)
84+
.orElseThrow(() -> new ApplicationException(OrderExceptionCase.INVALID_ORDER));
85+
86+
if (!order.getUserId().equals(userId)) {
10687
throw new ApplicationException(OrderExceptionCase.INVALID_ORDER);
88+
}
89+
10790
return order;
10891
}
10992

110-
private void publishOrderCompletedEvent(UUID userId, UUID performanceId) {
111-
val event = OrderCompletedEvent.create(userId, performanceId);
112-
eventApplicationService.publishOrderCompletedEvent(event);
93+
private Order findOrderById(UUID orderId){
94+
return orderRepository.findById(orderId)
95+
.orElseThrow(() -> new ApplicationException(ORDER_NOT_FOUND));
96+
}
97+
98+
private void publishForSeatReservation(UUID scheduleId, UUID seatId) {
99+
val event = OrderCompletedForSeatReservationEvent.create(scheduleId, seatId);
100+
eventApplicationService.publishForSeatReservation(event);
101+
}
102+
103+
private void publishForQueueTokenRemoval(UUID userId, UUID performanceId) {
104+
val event = OrderCompletedForQueueTokenRemovalEvent.create(userId, performanceId);
105+
eventApplicationService.publishForQueueTokenRemoval(event);
113106
}
114107

115108
}

services/order/src/main/java/com/ticketPing/order/common/exception/OrderExceptionCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public enum OrderExceptionCase implements ErrorCase {
1212
ORDER_NOT_FOUND(HttpStatus.NOT_FOUND, "예매정보를 찾을 수 없습니다."),
1313
INVALID_TTL_NAME(HttpStatus.BAD_REQUEST, "유효하지 않은 TTL 명입니다."),
1414
NOT_FOUND_ORDER_ID_IN_TTL(HttpStatus.NOT_FOUND,"TTL 정보에서 order_id를 찾을 수 없습니다."),
15-
SEAT_ALREADY_TAKEN(HttpStatus.BAD_REQUEST, "중복된 주문입니다."),
15+
DUPLICATED_ORDER(HttpStatus.BAD_REQUEST, "중복된 주문입니다."),
1616
INVALID_ORDER(HttpStatus.BAD_REQUEST, "유효하지 않은 주문입니다."),
1717
SEAT_CACHE_NOT_FOUND(HttpStatus.NOT_FOUND, "레디스에 공연관련 정보가 캐싱되어 있지 않습니다."),
1818
ORDER_STATUS_UNKNOWN(HttpStatus.CONFLICT,"저장된 enum 상태값을 사용해야 합니다."),

services/order/src/main/java/com/ticketPing/order/domain/model/entity/Order.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ public void updateOrderSeat(OrderSeat orderSeat) {
5858
this.orderSeat = orderSeat;
5959
}
6060

61-
public void complete(){
61+
public void complete(UUID paymentId){
6262
this.orderStatus = OrderStatus.COMPLETED;
63+
this.paymentId = paymentId;
6364
}
6465

6566
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.ticketPing.order.domain.repository;
2+
3+
import com.ticketPing.order.domain.model.entity.Order;
4+
import com.ticketPing.order.domain.model.enums.OrderStatus;
5+
6+
import java.util.List;
7+
import java.util.Optional;
8+
import java.util.UUID;
9+
10+
public interface OrderRepository {
11+
Order save(Order order);
12+
13+
Optional<Order> findById(UUID orderId);
14+
15+
Optional<Order> findByIdAndOrderStatus(UUID orderId, OrderStatus orderStatus);
16+
17+
List<Order> findByUserId(UUID userId);
18+
19+
boolean existsByOrderSeatSeatIdAndOrderStatusIn(UUID seatId, List<OrderStatus> pending);
20+
}

0 commit comments

Comments
 (0)