-
Notifications
You must be signed in to change notification settings - Fork 35
[volume-9] Product Ranking with Redis #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: lim-jaein
Are you sure you want to change the base?
Changes from 3 commits
4e675ae
a9095fc
7c78bd0
2c29f5f
730f8f9
4719d09
7576cf7
6996b64
c9d8ea3
bb79cb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,26 @@ | ||
| package com.loopers.application.payment; | ||
|
|
||
| import com.loopers.application.payment.event.PaymentFailedEvent; | ||
| import com.loopers.application.payment.event.PaymentSucceededEvent; | ||
| import com.loopers.domain.order.Order; | ||
| import com.loopers.domain.order.OrderService; | ||
| import com.loopers.messaging.event.OrderPaidEvent; | ||
| import com.loopers.messaging.event.OrderPaidEvent.OrderItemData; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.context.ApplicationEventPublisher; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class PaymentProcessService { | ||
|
|
||
| private final PaymentFacade paymentFacade; | ||
| private final OrderService orderService; // Added OrderService injection | ||
|
|
||
| private final ApplicationEventPublisher eventPublisher; | ||
|
|
||
|
|
@@ -28,12 +35,19 @@ public void process(Long userId, Long orderId) { | |
| } | ||
|
|
||
| @Transactional | ||
| public void processPg(Long userId, Long orderId) { | ||
| public void processPg(Long userId, Long orderId) { // Added userId parameter | ||
| try { | ||
| paymentFacade.payPg(orderId); | ||
| eventPublisher.publishEvent(PaymentSucceededEvent.from(orderId)); | ||
|
|
||
| Order order = orderService.findOrderById(orderId) | ||
| .orElseThrow(() -> new IllegalStateException("๊ฒฐ์ ๋ ์ฃผ๋ฌธ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. orderId: " + orderId)); | ||
|
|
||
| List<OrderItemData> orderItemDataList = order.getItems().stream() | ||
| .map(item -> new OrderItemData(item.getProductId(), item.getQuantity(), item.getUnitPrice().getAmount())) | ||
| .collect(Collectors.toList()); | ||
|
|
||
| eventPublisher.publishEvent(OrderPaidEvent.of(orderId, orderItemDataList)); | ||
|
Comment on lines
+42
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์ค์: OrderItemData ์์ฑ ์ totalPrice๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. Line 46์์ ์ด ๋ถ์ผ์น๋ก ์ธํด:
๐ ์์ ์ ์ List<OrderItemData> orderItemDataList = order.getItems().stream()
- .map(item -> new OrderItemData(item.getProductId(), item.getQuantity(), item.getUnitPrice().getAmount()))
+ .map(item -> new OrderItemData(item.getProductId(), item.getQuantity(), item.getTotalPrice().getAmount()))
.collect(Collectors.toList());๐ค Prompt for AI Agents |
||
| } catch (Exception e) { | ||
| // ์ด์ธ ์๋ฒ ํ์์์ ๋ฑ์ retry -> pending์ํ๋ก ์ค์ผ์ค๋ง ์๋ | ||
| log.error("์ธ๋ถ PG ๊ฒฐ์ ์คํจ, ์ฃผ๋ฌธ ID: {}", orderId, e); | ||
| eventPublisher.publishEvent(PaymentFailedEvent.of(userId, orderId, e)); | ||
| } | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.loopers.application.product.event; | ||
|
|
||
| import com.loopers.domain.common.event.DomainEvent; | ||
| import com.loopers.domain.common.event.DomainEventEnvelop; | ||
| import com.loopers.domain.common.event.DomainEventRepository; | ||
| import com.loopers.messaging.event.ProductViewedEvent; | ||
| import com.loopers.support.json.JsonConverter; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.transaction.event.TransactionPhase; | ||
| import org.springframework.transaction.event.TransactionalEventListener; | ||
|
|
||
| @Slf4j | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class ProductViewedEventHandler { | ||
|
|
||
| private final DomainEventRepository eventRepository; | ||
| private final JsonConverter jsonConverter; | ||
|
|
||
| @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) | ||
| public void handleProductViewedEvent(ProductViewedEvent event) { | ||
| log.info("ProductViewedEvent received: productId={}, occurredAt={}", event.productId(), event.occurredAt()); | ||
|
|
||
| DomainEventEnvelop<ProductViewedEvent> envelop = | ||
| DomainEventEnvelop.of( | ||
| "PRODUCT_VIEWED", | ||
| "PRODUCT", | ||
| event.productId(), | ||
| event | ||
| ); | ||
|
|
||
| eventRepository.save( | ||
| DomainEvent.pending( | ||
| "catalog-events", | ||
| envelop, | ||
| jsonConverter.serialize(envelop) | ||
| ) | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,9 @@ | |
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.math.BigDecimal; | ||
| import java.util.Optional; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| @RequiredArgsConstructor | ||
|
|
@@ -14,12 +17,16 @@ public class ProductMetricsService { | |
| private final ProductMetricsJpaRepository productMetricsRepository; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ ๏ธ Refactor suggestion | ๐ Major ์ถ์ํ ์๋ฐ: ๊ตฌํ์ฒด ์ง์ ์ฃผ์
๐ ์์ ์ ์- private final ProductMetricsJpaRepository productMetricsRepository;
+ private final ProductMetricsRepository productMetricsRepository;import ๋ฌธ๋ ํจ๊ป ์์ ํด์ผ ํฉ๋๋ค: -import com.loopers.infrastructure.productmetrics.ProductMetricsJpaRepository;
+import com.loopers.domain.productmetrics.ProductMetricsRepository;
๐ค Prompt for AI Agents |
||
|
|
||
| @Transactional | ||
| public void increaseSalesCount(Long productId, int quantity) { | ||
| public void increaseSalesCount(Long productId, int quantity, BigDecimal amount) { | ||
| if (quantity <= 0) { | ||
| log.warn("ํ๋งค ์๋์ด 0 ์ดํ์ผ ์ ์์ต๋๋ค. ์๋:{}, ์ํID:{}", quantity, productId); | ||
| return; | ||
| } | ||
| productMetricsRepository.upsertSalesCount(productId, quantity); | ||
| if (amount.compareTo(BigDecimal.ZERO) < 0){ | ||
| log.warn("ํ๋งค ๊ธ์ก์ด 0 ์ดํ์ผ ์ ์์ต๋๋ค. ๊ธ์ก:{}, ์ํID:{}", amount, productId); | ||
| return; | ||
| } | ||
|
Comment on lines
26
to
29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง๊ณผ ๋ก๊ทธ ๋ฉ์์ง ๋ถ์ผ์น ์กฐ๊ฑด์ ๐ ์์ ์ ์- if (amount.compareTo(BigDecimal.ZERO) < 0){
- log.warn("ํ๋งค ๊ธ์ก์ด 0 ์ดํ์ผ ์ ์์ต๋๋ค. ๊ธ์ก:{}, ์ํID:{}", amount, productId);
+ if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) {
+ log.warn("ํ๋งค ๊ธ์ก์ด 0 ๋ฏธ๋ง์ผ ์ ์์ต๋๋ค. ๊ธ์ก:{}, ์ํID:{}", amount, productId);
return;
}๋๋ 0๋ ๊ฑฐ๋ถํด์ผ ํ๋ค๋ฉด: - if (amount.compareTo(BigDecimal.ZERO) < 0){
+ if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {๐ค Prompt for AI Agents |
||
| productMetricsRepository.upsertSalesCount(productId, quantity, amount); | ||
| } | ||
|
|
||
| @Transactional | ||
|
|
@@ -36,4 +43,8 @@ public void decreaseLikeCount(Long productId) { | |
| public void increaseViewCount(Long productId) { | ||
| productMetricsRepository.upsertViewCount(productId); | ||
| } | ||
|
|
||
| public Optional<ProductMetrics> findByProductId(Long productId) { | ||
| return productMetricsRepository.findByProductId(productId); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |||||||||
| import org.springframework.data.jpa.repository.Query; | ||||||||||
| import org.springframework.data.repository.query.Param; | ||||||||||
|
|
||||||||||
| import java.math.BigDecimal; | ||||||||||
| import java.util.Optional; | ||||||||||
|
|
||||||||||
| public interface ProductMetricsJpaRepository extends JpaRepository<ProductMetrics, Long> { | ||||||||||
|
|
@@ -38,15 +39,16 @@ INSERT INTO product_metrics (product_id, like_count, sales_count, view_count, cr | |||||||||
|
|
||||||||||
| @Modifying | ||||||||||
| @Query(value = """ | ||||||||||
| INSERT INTO product_metrics (product_id, like_count, sales_count, view_count, created_at, updated_at) | ||||||||||
| VALUES (:productId, 0, :quantity, 0, NOW(), NOW()) | ||||||||||
| INSERT INTO product_metrics (product_id, like_count, sales_count, sales_amount, view_count, created_at, updated_at) | ||||||||||
| VALUES (:productId, 0, :quantity, amount, 0, NOW(), NOW()) | ||||||||||
|
||||||||||
| INSERT INTO product_metrics (product_id, like_count, sales_count, sales_amount, view_count, created_at, updated_at) | |
| VALUES (:productId, 0, :quantity, amount, 0, NOW(), NOW()) | |
| INSERT INTO product_metrics (product_id, like_count, sales_count, sales_amount, view_count, created_at, updated_at) | |
| VALUES (:productId, 0, :quantity, :amount, 0, NOW(), NOW()) |
๐ค Prompt for AI Agents
In
apps/commerce-streamer/src/main/java/com/loopers/infrastructure/productmetrics/ProductMetricsJpaRepository.java
around lines 42 to 43, the INSERT uses an unbound literal "amount" instead of
the named parameter ":amount", causing SQL parameter binding to fail at runtime;
change "amount" to ":amount" in the VALUES clause and ensure the repository
method supplies a matching ":amount" parameter (correct name and type) when
executing the query.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package com.loopers.application.like.event; | ||
| package com.loopers.messaging.event; | ||
|
|
||
| import java.time.Instant; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package com.loopers.application.like.event; | ||
| package com.loopers.messaging.event; | ||
|
|
||
| import java.time.Instant; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.loopers.messaging.event; | ||
|
|
||
| import java.math.BigDecimal; | ||
| import java.time.Instant; | ||
| import java.util.List; | ||
|
|
||
| public record OrderPaidEvent( | ||
| Long orderId, | ||
| List<OrderItemData> items, | ||
| Instant occurredAt | ||
| ) { | ||
| public static OrderPaidEvent of(Long orderId, List<OrderItemData> items) { | ||
| return new OrderPaidEvent( | ||
| orderId, | ||
| items, | ||
| Instant.now() | ||
| ); | ||
| } | ||
|
|
||
| public record OrderItemData( | ||
| Long productId, | ||
| int quantity, | ||
| BigDecimal unitPrice // ์ํ ๋จ๊ฐ | ||
| ) {} | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐งฉ Analysis chain
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 876
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 235
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 597
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 70
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 6069
๐ Script executed:
# Read the OrderItem class to understand its structure cat -n apps/commerce-api/src/main/java/com/loopers/domain/order/OrderItem.javaRepository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 2326
๐ Script executed:
Repository: Loopers-dev-lab/loopers-spring-java-template
Length of output: 1770
getTotalPrice()๋์getUnitPrice()๋ฅผ ์ฌ์ฉํ์ธ์.OrderItemData์unitPriceํ๋๋ ์ํ ๋จ๊ฐ๋ฅผ ์๋ํ๊ณ ์์ผ๋, ํ์ฌ ์ฝ๋๋item.getTotalPrice().getAmount()๋ฅผ ์ ๋ฌํ๊ณ ์์ต๋๋ค.OrderItem์totalPrice๋ ์๋ ร ๋จ๊ฐ์ด๋ฏ๋ก, ๋ค์ด์คํธ๋ฆผ ์ปจ์๋จธ์์ ์๋ชป๋ ๊ณ์ฐ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.๊ฐ์ ๋ก์ง์ด
PaymentProcessService์์๋item.getUnitPrice().getAmount()๋ก ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํ๋์ด ์์ต๋๋ค. ์ผ๊ด์ฑ์ ์ํด 62๋ฒ ์ค์item.getUnitPrice().getAmount()๋ก ๋ณ๊ฒฝํ์ธ์.๐ค Prompt for AI Agents