Skip to content

feat(item): enforce purchase limits#54

Open
popeye0618 wants to merge 1 commit into
feat/product-catalog-bundlefrom
feat/item-purchase-limits
Open

feat(item): enforce purchase limits#54
popeye0618 wants to merge 1 commit into
feat/product-catalog-bundlefrom
feat/item-purchase-limits

Conversation

@popeye0618
Copy link
Copy Markdown
Member

개요

매칭권/옵션권 구매 한도 조회와 구매 요청 시 한도 초과 검증을 추가합니다.

@codex

변경 사항

  • 보유 수량과 진행 중인 구매 요청 수량을 합산해 구매 한도를 검증합니다.
  • 내 구매 한도 조회 API와 응답 DTO를 추가했습니다.
  • 한도 초과 결제 오류 코드를 추가했습니다.
  • 구매 한도 검증 및 조회 테스트를 추가했습니다.

테스트

  • 테스트를 실행했습니다. ./gradlew :item-service:test --tests com.comatching.item.domain.product.service.ShopServiceImplTest
  • 관련 수동 검증을 완료했습니다.
  • 테스트가 필요 없는 변경입니다.

체크리스트

  • 브랜치명이 규칙을 따릅니다.
  • 커밋 메시지가 컨벤션을 따릅니다.
  • 이슈와 PR이 연결되어 있습니다.
  • 작업 완료 후 merge 가능한 상태입니다.

스크린샷 / 참고 자료

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 풀 리퀘스트는 매칭권 및 옵션권에 대한 구매 한도 제한 기능을 도입합니다. ShopService에 구매 한도 조회 및 검증 로직이 추가되었으며, 이를 위해 Repository 레이어에 수량 합산 쿼리가 구현되었습니다. 리뷰에서는 서비스 클래스 내 하드코딩된 설정값을 @ConfigurationProperties로 전환할 것과, 루프 내 반복적인 DB 조회를 배치 쿼리로 개선하여 성능 및 락 점유 시간을 최적화할 것을 제안했습니다. 또한, 구매 한도 검증 시 보너스 리워드 포함 여부에 대한 정책 일관성 검토가 필요하다는 의견이 있었습니다.

Comment on lines +39 to +46
private static final Map<ItemType, Integer> PURCHASE_LIMITS = Map.of(
ItemType.MATCHING_TICKET, 30,
ItemType.OPTION_TICKET, 90
);
private static final List<ItemType> LIMITED_ITEM_TYPES = List.of(
ItemType.MATCHING_TICKET,
ItemType.OPTION_TICKET
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

구매 한도 정책(PURCHASE_LIMITS, LIMITED_ITEM_TYPES)이 서비스 클래스 내부에 하드코딩되어 있습니다. Repository Style Guide 3.1절에 따라 이러한 설정값은 @ConfigurationProperties를 사용하여 외부 설정(yml)으로 관리하는 것을 권장합니다. 또한 LIMITED_ITEM_TYPESPURCHASE_LIMITS.keySet()으로 대체하여 중복 관리를 제거할 수 있습니다.

References
  1. 설정 값은 @value 남용 대신 @ConfigurationProperties 사용을 권장합니다. (link)

Comment on lines +120 to +127
LIMITED_ITEM_TYPES.stream()
.map(itemType -> PurchaseLimitResponse.ItemPurchaseLimitResponse.of(
itemType,
itemRepository.sumUsableQuantityByMemberIdAndItemType(memberId, itemType),
orderRepository.sumActivePendingQuantityByMemberIdAndItemType(memberId, itemType, now),
PURCHASE_LIMITS.get(itemType)
))
.toList()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

아이템 타입별로 루프를 돌며 DB 조회를 반복하고 있습니다(N+1 조회). 현재는 대상 타입이 2개뿐이라 영향이 적지만, 확장성을 고려하여 IN 절을 사용하는 단일 쿼리로 모든 타입의 수량을 한 번에 조회하도록 개선하는 것이 좋습니다.

Comment on lines +134 to +146
for (ItemType itemType : LIMITED_ITEM_TYPES) {
int requestedQuantity = requestedQuantityByType.getOrDefault(itemType, 0);
if (requestedQuantity == 0) {
continue;
}

long ownedQuantity = itemRepository.sumUsableQuantityByMemberIdAndItemType(memberId, itemType);
long pendingQuantity = orderRepository.sumActivePendingQuantityByMemberIdAndItemType(memberId, itemType, now);
int maxQuantity = PURCHASE_LIMITS.get(itemType);
if (ownedQuantity + pendingQuantity + requestedQuantity > maxQuantity) {
throw new BusinessException(PaymentErrorCode.PURCHASE_LIMIT_EXCEEDED);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

validatePurchaseLimit 메서드 내의 반복문에서도 개별 DB 조회가 발생하고 있습니다. requestPurchase는 분산 락이 걸리는 임계 영역(Critical Section)이므로, 루프 외부에서 데이터를 일괄 조회하여 락 점유 시간을 최소화하는 것이 성능상 유리합니다.

Comment on lines +151 to +153
for (ProductReward reward : product.getRewards()) {
requestedQuantityByType.merge(reward.getItemType(), reward.getQuantity(), Integer::sum);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

현재 getRequestedQuantityByTypeproduct.getRewards()만 집계하고 있습니다. 만약 보너스 리워드(bonusRewards)도 실제 사용자에게 지급되는 아이템이라면, 구매 한도 검증 시 보너스 수량도 합산되어야 정책상 일관성을 유지할 수 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant