Skip to content

Commit 70ffb3f

Browse files
authored
Merge pull request #15 from Kimjipang/round08
Round08
2 parents c8d685f + 47df972 commit 70ffb3f

36 files changed

Lines changed: 501 additions & 23 deletions

apps/commerce-api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ dependencies {
22
// add-ons
33
implementation(project(":modules:jpa"))
44
implementation(project(":modules:redis"))
5+
implementation(project(":modules:kafka"))
56
implementation(project(":supports:jackson"))
67
implementation(project(":supports:logging"))
78
implementation(project(":supports:monitoring"))

apps/commerce-api/src/main/java/com/loopers/CommerceApiApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
77
import org.springframework.cache.annotation.EnableCaching;
88
import org.springframework.cloud.openfeign.EnableFeignClients;
9+
import org.springframework.scheduling.annotation.EnableScheduling;
910

1011
import java.util.TimeZone;
1112

1213
@ConfigurationPropertiesScan
1314
@SpringBootApplication
1415
@EnableCaching
1516
@EnableFeignClients
17+
@EnableScheduling
1618
public class CommerceApiApplication {
1719

1820
@PostConstruct
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.loopers.application.event.payment;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.stereotype.Component;
5+
6+
@Component
7+
@RequiredArgsConstructor
8+
public class PaymentEventHandler {
9+
}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
package com.loopers.application.order;
22

3-
public record OrderCreatedEvent(Long couponId) {
3+
import com.loopers.interfaces.api.payment.PaymentV1Dto;
4+
5+
public record OrderCreatedEvent(
6+
Long couponId,
7+
String orderId,
8+
PaymentV1Dto.CardTypeDto cardType,
9+
String cardNo,
10+
Long amount,
11+
String callbackUrl
12+
) {
413
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.loopers.domain.product.ProductRepository;
1313
import com.loopers.domain.user.UserRepository;
1414
import com.loopers.interfaces.api.order.OrderV1Dto;
15+
import com.loopers.interfaces.api.payment.PaymentV1Dto;
1516
import com.loopers.support.error.CoreException;
1617
import com.loopers.support.error.ErrorType;
1718
import lombok.RequiredArgsConstructor;
@@ -91,7 +92,7 @@ public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
9192
.multiply(BigDecimal.valueOf(100 - rate))
9293
.divide(BigDecimal.valueOf(100));
9394

94-
publisher.publishEvent(new OrderCreatedEvent(couponId));
95+
publisher.publishEvent(new OrderCreatedEvent(couponId, null, null, null, null, null));
9596

9697
Order order = request.toEntity(totalPrice);
9798
Order saved = orderRepository.save(order);
@@ -103,6 +104,15 @@ public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
103104
.map(orderItem -> OrderItemInfo.from(orderItem, orderItem.getOrderPrice()))
104105
.toList();
105106

107+
publisher.publishEvent(new OrderCreatedEvent(
108+
couponId,
109+
"1351039135",
110+
PaymentV1Dto.CardTypeDto.HYUNDAI,
111+
"1234-5678-9814-1451",
112+
100000L,
113+
"http://localhost:8080/api/v1/examples/callback"
114+
));
115+
106116
return new OrderResultInfo(OrderInfo.from(saved), orderItemInfos);
107117
}
108118
}

apps/commerce-api/src/main/java/com/loopers/application/payment/PaymentFacade.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.loopers.application.payment;
22

3+
import com.loopers.domain.payment.Payment;
4+
import com.loopers.domain.payment.PaymentStatus;
35
import com.loopers.infrastructure.payment.client.PgClient;
46
import com.loopers.interfaces.api.ApiResponse;
57
import com.loopers.interfaces.api.payment.PaymentV1Dto;
@@ -11,17 +13,28 @@
1113
@RequiredArgsConstructor
1214
public class PaymentFacade {
1315
private final PgClient pgClient;
16+
private final PaymentRepository paymentRepository;
1417

1518
@CircuitBreaker(name = "pgCircuit", fallbackMethod = "fallback")
16-
public PaymentV1Dto.TransactionResponse requestPayment(String userId, PaymentV1Dto.PaymentRequest request) {
17-
ApiResponse<PaymentV1Dto.TransactionResponse> result = pgClient.requestPayment(userId, request);
19+
public PaymentV1Dto.TransactionResponse requestPayment(String userId, PaymentV1Dto.PgPaymentRequest pgPaymentRequest) {
20+
ApiResponse<PaymentV1Dto.TransactionResponse> result = pgClient.requestPayment(userId, pgPaymentRequest);
1821

1922
PaymentV1Dto.TransactionResponse response = result.data();
2023

24+
PaymentV1Dto.PaymentRequest paymentRequest = new PaymentV1Dto.PaymentRequest(
25+
userId,
26+
pgPaymentRequest.orderId(),
27+
response.transactionKey(),
28+
PaymentStatus.PENDING
29+
);
30+
31+
Payment payment = Payment.create(paymentRequest);
32+
paymentRepository.save(payment);
33+
2134
return response;
2235
}
2336

24-
public PaymentV1Dto.TransactionResponse fallback(String userId, PaymentV1Dto.PaymentRequest request, Throwable throwable) {
37+
public PaymentV1Dto.TransactionResponse fallback(String userId, PaymentV1Dto.PgPaymentRequest request, Throwable throwable) {
2538
return new PaymentV1Dto.TransactionResponse(
2639
null,
2740
PaymentV1Dto.TransactionStatusResponse.FAILED,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.loopers.application.payment;
2+
3+
import com.loopers.domain.payment.Payment;
4+
5+
public interface PaymentRepository {
6+
Payment save(Payment payment);
7+
}

apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package com.loopers.application.product;
22

3-
import com.loopers.domain.actionlog.ActionType;
43
import com.loopers.domain.brand.BrandRepository;
4+
import com.loopers.domain.outbox.AggregateType;
5+
import com.loopers.domain.outbox.OutboxEvent;
6+
import com.loopers.domain.outbox.OutboxType;
7+
import com.loopers.domain.outbox.OutboxRepository;
58
import com.loopers.domain.product.Product;
69
import com.loopers.domain.product.ProductRepository;
10+
import com.loopers.infrastructure.product.ProductViewKafkaProducer;
711
import com.loopers.interfaces.api.product.ProductV1Dto;
812
import com.loopers.support.error.CoreException;
913
import com.loopers.support.error.ErrorType;
1014
import lombok.RequiredArgsConstructor;
1115
import org.springframework.cache.annotation.CacheEvict;
1216
import org.springframework.cache.annotation.Cacheable;
13-
import org.springframework.context.ApplicationEventPublisher;
1417
import org.springframework.stereotype.Component;
1518
import org.springframework.transaction.annotation.Transactional;
1619

@@ -22,8 +25,8 @@
2225
public class ProductFacade {
2326
private final ProductRepository productRepository;
2427
private final BrandRepository brandRepository;
25-
private final ApplicationEventPublisher publisher;
26-
28+
private final OutboxRepository outBoxRepository;
29+
private final ProductViewKafkaProducer kafkaProducer;
2730

2831
@Transactional
2932
public ProductInfo registerProduct(ProductV1Dto.ProductRequest request) {
@@ -49,15 +52,20 @@ public List<ProductInfo> findAllProducts() {
4952
.toList();
5053
}
5154

52-
@Transactional(readOnly = true)
55+
@Transactional
5356
@Cacheable(value = "product", key = "#id")
5457
public ProductInfo findProductById(Long id) {
5558
Product product = productRepository.findById(id).orElseThrow(
5659
() -> new CoreException(ErrorType.NOT_FOUND, "찾고자 하는 상품이 존재하지 않습니다.")
5760
);
5861

59-
// 유저 ID는 임시로 하드 코딩했습니다. 추후 인증/인가 기능이 추가되면 수정할 예정입니다.
60-
publisher.publishEvent(new UserActionEvent(1L, product.getId(), ActionType.PRODUCT_LOOKED_UP));
62+
OutboxEvent outBoxEvent = OutboxEvent.of(
63+
AggregateType.PRODUCT,
64+
product.getId(),
65+
OutboxType.PRODUCT_VIEWED
66+
);
67+
68+
outBoxRepository.save(outBoxEvent);
6169

6270
return ProductInfo.from(product);
6371
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.loopers.domain.outbox;
2+
3+
public enum AggregateType {
4+
PRODUCT,
5+
ORDER,
6+
USER
7+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.loopers.domain.outbox;
2+
3+
import com.loopers.domain.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.EnumType;
7+
import jakarta.persistence.Enumerated;
8+
import jakarta.persistence.Table;
9+
import lombok.AccessLevel;
10+
import lombok.Getter;
11+
import lombok.NoArgsConstructor;
12+
13+
@Entity
14+
@Table(name = "outbox")
15+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
16+
@Getter
17+
public class OutboxEvent extends BaseEntity {
18+
@Enumerated(EnumType.STRING)
19+
@Column(name = "aggregate_type", nullable = false)
20+
private AggregateType aggregateType;
21+
22+
@Column(name = "aggregate_id", nullable = false)
23+
private Long aggregateId;
24+
25+
// @Enumerated(EnumType.STRING)
26+
@Column(name = "event_type", nullable = false)
27+
private OutboxType eventType;
28+
29+
@Enumerated(EnumType.STRING)
30+
@Column(name = "status", nullable = false)
31+
private OutboxStatus status;
32+
33+
public OutboxEvent(AggregateType aggregateType, Long aggregateId, OutboxType eventType, OutboxStatus status) {
34+
this.aggregateType = aggregateType;
35+
this.aggregateId = aggregateId;
36+
this.eventType = eventType;
37+
this.status = status;
38+
}
39+
40+
public static OutboxEvent of(AggregateType aggregateType, Long aggregateId, OutboxType eventType) {
41+
return new OutboxEvent(aggregateType, aggregateId, eventType, OutboxStatus.PENDING);
42+
}
43+
44+
public void markAsProcessed() {
45+
this.status = OutboxStatus.PROCESSED;
46+
}
47+
48+
}

0 commit comments

Comments
 (0)