Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public enum BusinessAction {
LOYALTY_MILESTONE
}

public static BusinessActionEvent couponUsed(Long userId, Long couponId, Long orderId,
public static BusinessActionEvent couponUsed(Long userId, Long couponId, String orderId,
BigDecimal originalAmount, BigDecimal discountAmount) {
return new BusinessActionEvent(
userId, BusinessAction.COUPON_USED, couponId, "coupon",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class CouponEventHandler {
private final OrderService orderService;
private final ApplicationEventPublisher eventPublisher;


@TransactionalEventListener(phase = AFTER_COMMIT)
@Async
public void handleCouponUsed(CouponUsedEvent event) {
Expand All @@ -37,16 +38,6 @@ public void handleCouponUsed(CouponUsedEvent event) {

pgClient.requestPayment(event.orderId(), event.cardType(), event.cardNo(), finalPrice);

// ๋ฐ์ดํ„ฐ ํ”Œ๋žซํผ์œผ๋กœ ์ฃผ๋ฌธ ์ƒ์„ฑ ์ด๋ฒคํŠธ ์ „์†ก
Order order = orderService.getOrder(event.orderId());
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
finalPrice.getAmount(),
LocalDateTime.now(),
"ORDER_CREATED"
));

log.info("์ฟ ํฐ ์‚ฌ์šฉ ๋ฐ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ์™„๋ฃŒ - orderId: {}, userId: {}", event.orderId(), event.userId());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.loopers.domain.payment.CardType;

public record CouponUsedEvent(
Long orderId,
String orderId,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

๐Ÿงฉ Analysis chain

๐Ÿ Script executed:

#!/bin/bash
# Find CouponUsedEvent consumers
echo "=== CouponUsedEvent ์†Œ๋น„์ž ๊ฒ€์ƒ‰ ==="
rg -n -C3 --type=java 'CouponUsedEvent' 

echo -e "\n=== orderId ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๊ฒ€์ƒ‰ ==="
fd -e sql -e xml | head -20

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 4738


๐Ÿ Script executed:

#!/bin/bash
# Get full CouponService implementation
echo "=== CouponService.publishCouponUsedEvent ์ „์ฒด ์ฝ”๋“œ ==="
rg -n -A10 'private void publishCouponUsedEvent' --type=java

echo -e "\n=== CouponEventHandler.handleCouponUsed ์ „์ฒด ์ฝ”๋“œ ==="
rg -n -A15 'public void handleCouponUsed' --type=java

echo -e "\n=== BusinessActionEvent.couponUsed ์ •์˜ ์ฐพ๊ธฐ ==="
rg -n -B5 -A10 'couponUsed\(' --type=java | grep -A15 'static.*couponUsed'

echo -e "\n=== ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ํŒŒ์ผ ํ™•์ธ ==="
fd -e sql | xargs rg -l 'orders' -i

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 5305


๐Ÿ Script executed:

#!/bin/bash
# Find where CouponUsedEvent is instantiated
echo "=== CouponUsedEvent ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์œ„์น˜ ==="
rg -n 'new CouponUsedEvent' --type=java

echo -e "\n=== CouponUsedEvent ๋ ˆ์ฝ”๋“œ ์ „์ฒด ์ •์˜ ==="
rg -n -A10 'public record CouponUsedEvent' --type=java

echo -e "\n=== eventPublisher.publishEvent ํ˜ธ์ถœ ์œ„์น˜ ==="
rg -n 'publishEvent\(' --type=java | head -20

echo -e "\n=== CouponEventHandler ์ „์ฒด ํด๋ž˜์Šค ==="
cat apps/commerce-api/src/main/java/com/loopers/application/event/CouponEventHandler.java

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 5058


๐Ÿ Script executed:

#!/bin/bash
# Search for all places where CouponUsedEvent is published or created
echo "=== CouponUsedEvent๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ์ฝ”๋“œ ์ฐพ๊ธฐ ==="
rg -n 'publishEvent.*CouponUsedEvent|eventPublisher.*new CouponUsedEvent' --type=java

# Check if CouponUsedEvent is used anywhere
echo -e "\n=== CouponUsedEvent ๋ชจ๋“  ์‚ฌ์šฉ ์œ„์น˜ ==="
rg -n 'CouponUsedEvent' --type=java

# Check OrderService.cancelPayment to see what type orderId should be
echo -e "\n=== OrderService.cancelPayment ๋ฉ”์„œ๋“œ ์„œ๋ช… ==="
rg -n -A5 'public.*cancelPayment' apps/commerce-api/src/main/java/com/loopers/domain/order/OrderService.java

# Check PgClient.requestPayment to see what type orderId should be
echo -e "\n=== PgClient.requestPayment ๋ฉ”์„œ๋“œ ์„œ๋ช… ==="
rg -n -A5 'requestPayment' --type=java | head -20

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 3639


๐Ÿ Script executed:

#!/bin/bash
# Check git history or comments that might explain CouponUsedEvent
echo "=== CouponUsedEvent ๊ด€๋ จ ์ฝ”๋“œ ๋งฅ๋ฝ ==="
cat apps/commerce-api/src/main/java/com/loopers/application/event/CouponUsedEvent.java

echo -e "\n=== CouponService ์ „์ฒด publishCouponUsedEvent ๋ฉ”์„œ๋“œ ==="
rg -n -B2 -A8 'private void publishCouponUsedEvent' apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponService.java

echo -e "\n=== Order ์—”ํ‹ฐํ‹ฐ์—์„œ orderId ํƒ€์ž… ํ™•์ธ ==="
rg -n 'orderId|orderId' --type=java | grep -i 'order.*class\|private.*orderId\|String orderId\|Long orderId' | head -20

echo -e "\n=== Order ์—”ํ‹ฐํ‹ฐ ํŒŒ์ผ ์ฐพ๊ธฐ ==="
fd -n 'Order.java' --type f | head -5

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 4091


๐Ÿ Script executed:

#!/bin/bash
# Look at Order.java to confirm orderId type
echo "=== Order.java ํŒŒ์ผ ๋‚ด์šฉ ==="
cat apps/commerce-api/src/main/java/com/loopers/domain/order/Order.java | head -50

echo -e "\n=== CouponService ์ „์ฒด ๋‚ด์šฉ ํ™•์ธ ==="
cat apps/commerce-api/src/main/java/com/loopers/domain/coupon/CouponService.java

Repository: Loopers-dev-lab/loopers-spring-java-template

Length of output: 5391


CouponUsedEvent๊ฐ€ ๋ฐœํ–‰๋˜์ง€ ์•Š์•„ ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์Œ

CouponUsedEvent๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ ๋ฐœํ–‰๋˜์ง€ ์•Š๋Š” ๊ตฌ์กฐ์  ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • CouponService.publishCouponUsedEvent() ๋ฉ”์„œ๋“œ๋Š” BusinessActionEvent๋ฅผ ๋ฐœํ–‰ํ•˜๋ฉฐ, CouponUsedEvent๋Š” ์ „ํ˜€ ๋ฐœํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • CouponEventHandler.handleCouponUsed() ๋ฆฌ์Šค๋„ˆ๋Š” ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฐ๋“œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค
  • ์‹ค์ œ ๋ฐœํ–‰๋˜๋Š” BusinessActionEvent๋Š” orderId๋ฅผ null๋กœ ์ „๋‹ฌํ•˜๋ฏ€๋กœ, ์˜๋„ํ•œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง(pgClient.requestPayment(), orderService.cancelPayment())์ด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

CouponUsedEvent๋ฅผ ์‹ค์ œ๋กœ ๋ฐœํ–‰ํ•˜๋„๋ก ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜, CouponEventHandler๋ฅผ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค– Prompt for AI Agents
In
apps/commerce-api/src/main/java/com/loopers/application/event/CouponUsedEvent.java
around line 7, CouponUsedEvent is never actually published so
CouponEventHandler.handleCouponUsed is dead code and BusinessActionEvent is
published with a null orderId; fix by changing
CouponService.publishCouponUsedEvent() to construct and publish a
CouponUsedEvent (copying the orderId and other needed fields out of the
BusinessActionEvent payload) instead of publishing BusinessActionEvent, ensure
the event payload includes a non-null orderId, and keep the existing handler
intact (or if you prefer to keep BusinessActionEvent as the canonical event,
remove the unused CouponUsedEvent class and its handler). Ensure the publish
call uses the correct event type and that downstream handler wiring remains
unchanged.

Long userId,
Long couponIssueId,
Money totalPrice,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.loopers.application.event;

import com.loopers.domain.order.Order;
import com.loopers.domain.order.OrderService;
import com.loopers.domain.payment.Payment;
import com.loopers.domain.payment.PaymentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Slf4j
@RequiredArgsConstructor
@Component
public class DataTransferEventHandler {
private final OrderService orderService;
private final PaymentService paymentService;
private final ApplicationEventPublisher eventPublisher;

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handlePaymentSuccess(PaymentSuccessEvent event) {
log.debug("Handling PaymentSuccessEvent for data transfer: orderId={}", event.orderId());

Payment payment = paymentService.findPaymentByOrderId(event.orderId());

// ๊ฒฐ์ œ ์„ฑ๊ณต ๋ฐ์ดํ„ฐ ์ „์†ก
eventPublisher.publishEvent(new PaymentDataTransferEvent(
event.orderId(),
event.userId(),
event.amount(),
payment.getCardType(),
payment.getStatus(),
com.loopers.application.payment.TransactionStatus.SUCCESS,
event.reason(),
LocalDateTime.now(),
"PAYMENT_SUCCESS"
));

// ์ฃผ๋ฌธ ๊ฒฐ์ œ ์™„๋ฃŒ ๋ฐ์ดํ„ฐ ์ „์†ก (์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ ํ™œ์šฉ)
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
null, // order.getStatus() ๋Œ€์‹  OrderCompletedEvent์—์„œ ์ฒ˜๋ฆฌ
event.totalOrderAmount(),
LocalDateTime.now(),
"ORDER_PAID"
));
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handlePaymentFailure(PaymentFailureEvent event) {
log.debug("Handling PaymentFailureEvent for data transfer: orderId={}", event.orderId());

Payment payment = paymentService.findPaymentByOrderId(event.orderId());

// ๊ฒฐ์ œ ์‹คํŒจ ๋ฐ์ดํ„ฐ ์ „์†ก (์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ ํ™œ์šฉ)
eventPublisher.publishEvent(new PaymentDataTransferEvent(
event.orderId(),
event.userId(),
event.amount(),
payment.getCardType(),
payment.getStatus(),
com.loopers.application.payment.TransactionStatus.FAILED,
event.reason(),
LocalDateTime.now(),
"PAYMENT_FAILED"
));
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreated(OrderCreatedEvent event) {
log.debug("Handling OrderCreatedEvent for data transfer: orderId={}", event.orderId());

Order order = orderService.getOrder(event.orderId());

// ์ฃผ๋ฌธ ์ƒ์„ฑ ๋ฐ์ดํ„ฐ ์ „์†ก
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
order.getTotalPrice(),
LocalDateTime.now(),
"ORDER_CREATED"
));
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCancelled(OrderCancelledEvent event) {
log.debug("Handling OrderCancelledEvent for data transfer: orderId={}", event.orderId());

Order order = orderService.getOrder(event.orderId());

// ์ฃผ๋ฌธ ์ทจ์†Œ ๋ฐ์ดํ„ฐ ์ „์†ก
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
order.getTotalPrice(),
LocalDateTime.now(),
"ORDER_CANCELLED"
));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.loopers.application.event;

public record OrderCancelledEvent(
Long orderId,
String orderId,
Long userId,
String reason
) {}
) {}
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package com.loopers.application.event;

import com.loopers.domain.payment.CardType;

import java.util.List;

public record OrderCreatedEvent(
Long orderId,
String orderId,
Long userId,
Long couponIssueId,
CardType cardType,
String cardNo,
List<OrderItemData> orderItems
) {

public record OrderItemData(
Long productId,
Long quantity,
Long unitPrice
) {}

public record OrderItemData(
Long productId,
Long quantity,
Long unitPrice
) {
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package com.loopers.application.event;

import com.loopers.domain.order.Money;
import com.loopers.domain.order.OrderStatus;

import java.math.BigDecimal;
import java.time.LocalDateTime;

public record OrderDataTransferEvent(
Long orderId,
String orderId,
Long userId,
OrderStatus status,
BigDecimal totalAmount,
Money totalAmount,
LocalDateTime completedAt,
String eventType // "ORDER_CREATED", "ORDER_PAID", "ORDER_CANCELLED"
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.loopers.domain.order.OrderService;
import com.loopers.domain.stock.StockService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
Expand All @@ -16,6 +17,7 @@

import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT;

@Slf4j
@RequiredArgsConstructor
@Component
public class OrderEventHandler {
Expand All @@ -28,43 +30,33 @@ public class OrderEventHandler {
@TransactionalEventListener(phase = AFTER_COMMIT)
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
Order order = orderService.getOrder(event.orderId());
log.debug("์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์‹œ์ž‘ - orderId: {}, couponIssueId: {}",
event.orderId(), event.couponIssueId());

if (event.couponIssueId() != null) {
handleOrderWithCoupon(event, order);
} else {
handleOrderWithoutCoupon(event, order);
}
}
try {
Order order = orderService.getOrder(event.orderId());
Money finalPrice = order.getTotalPrice();

private void handleOrderWithCoupon(OrderCreatedEvent event, Order order) {
// ์ฟ ํฐ ์‚ฌ์šฉ ์ด๋ฒคํŠธ ๋ฐœํ–‰ (๋ณ„๋„ ํŠธ๋žœ์žญ์…˜์—์„œ ์ฒ˜๋ฆฌ)
eventPublisher.publishEvent(new CouponUsedEvent(
event.orderId(),
event.userId(),
event.couponIssueId(),
order.getTotalPrice(),
event.cardType(),
event.cardNo()
));
}
// ์ฟ ํฐ ์ฒ˜๋ฆฌ (์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ)
if (event.couponIssueId() != null) {
log.debug("์ฟ ํฐ ์‚ฌ์šฉ ์ฒ˜๋ฆฌ - couponIssueId: {}", event.couponIssueId());
finalPrice = couponService.useCouponById(
event.couponIssueId(),
event.userId(),
order.getTotalPrice()
);
}

private void handleOrderWithoutCoupon(OrderCreatedEvent event, Order order) {
try {
// ์ฟ ํฐ ์—†์ด ๋ฐ”๋กœ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
pgClient.requestPayment(event.orderId(), event.cardType(), event.cardNo(), order.getTotalPrice());
// ๊ฒฐ์ œ ์š”์ฒญ
pgClient.requestPayment(order.getOrderId(), event.cardType(), event.cardNo(), finalPrice);


log.info("์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์™„๋ฃŒ - orderId: {}, userId: {}, finalPrice: {}",
event.orderId(), event.userId(), finalPrice.getAmount());

// ๋ฐ์ดํ„ฐ ํ”Œ๋žซํผ์œผ๋กœ ์ฃผ๋ฌธ ์ƒ์„ฑ ์ด๋ฒคํŠธ ์ „์†ก
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
order.getTotalPrice().getAmount(),
LocalDateTime.now(),
"ORDER_CREATED"
));
} catch (Exception e) {
// ๊ฒฐ์ œ ์š”์ฒญ ์‹คํŒจ์‹œ ์ฃผ๋ฌธ ์ทจ์†Œ ์ฒ˜๋ฆฌ
log.error("์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์‹คํŒจ - orderId: {}, userId: {}, error: {}",
event.orderId(), event.userId(), e.getMessage());
orderService.cancelPayment(event.orderId());
}
}
Expand All @@ -83,14 +75,5 @@ public void handleOrderCancelled(OrderCancelledEvent event) {
couponService.rollbackCouponUsage(order.getRefCouponIssueId());
}

// ๋ฐ์ดํ„ฐ ํ”Œ๋žซํผ์œผ๋กœ ์ฃผ๋ฌธ ์ทจ์†Œ ์ด๋ฒคํŠธ ์ „์†ก
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
order.getTotalPrice().getAmount(),
LocalDateTime.now(),
"ORDER_CANCELLED"
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.loopers.application.event;

import com.loopers.domain.order.Money;

public record OrderPaidEvent(
String orderId,
Long userId,
Money totalAmount
) {
}
Loading