Skip to content
Merged
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
9 changes: 8 additions & 1 deletion gateway-service/src/main/resources/application-aws.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,14 @@ spring:
- id: user-service-protected
uri: http://user-service:9000
predicates:
- Path=/api/auth/signup/profile, /api/auth/logout, /api/auth/password/change, /api/auth/withdraw, /api/members/**
- Path=/api/auth/signup/profile, /api/auth/logout, /api/auth/password/change, /api/auth/withdraw, /api/members/**, /api/notices/**
filters:
- AuthorizationHeaderFilter

- id: admin-user-notice
uri: http://user-service:9000
predicates:
- Path=/api/v1/admin/users, /api/v1/admin/users/**, /api/v1/admin/notices, /api/v1/admin/notices/**
filters:
- AuthorizationHeaderFilter

Expand Down
6 changes: 3 additions & 3 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@ spring:
- id: admin-user-notice
uri: http://localhost:9000
predicates:
- Path=/api/v1/admin/users/**, /api/v1/admin/notices/**
- Path=/api/v1/admin/users, /api/v1/admin/users/**, /api/v1/admin/notices, /api/v1/admin/notices/**
filters:
- AuthorizationHeaderFilter

- id: admin-payment-product-items
uri: http://localhost:9006
predicates:
- Path=/api/v1/admin/shop/**, /api/v1/admin/payment/**
- Path=/api/v1/admin/shop/**, /api/v1/admin/payment/**, /api/v1/admin/roulette/**
filters:
- AuthorizationHeaderFilter

Expand Down Expand Up @@ -149,4 +149,4 @@ management:
http.server.requests: true
spring.kafka.listener: true
slo:
http.server.requests: 50ms,100ms,200ms,500ms,1s,2s
http.server.requests: 50ms,100ms,200ms,500ms,1s,2s
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class RouletteHistory {
private RouletteType rouletteType;

@Column(name = "participated_at",nullable = false)
private final LocalDateTime participatedAt = LocalDateTime.now();
private LocalDateTime participatedAt;

@Column(name = "participation_date", nullable = false)
private LocalDate participationDate;
Expand All @@ -65,12 +65,14 @@ public RouletteHistory(
Long memberId,
RouletteReward reward,
RouletteType rouletteType,
boolean rewardGranted
boolean rewardGranted,
LocalDateTime participatedAt
) {
this.memberId = memberId;
this.reward = reward;
this.rouletteType = rouletteType;
this.participationDate = participatedAt.toLocalDate();
this.participatedAt = participatedAt != null ? participatedAt : LocalDateTime.now();
this.participationDate = this.participatedAt.toLocalDate();
this.rewardGranted = rewardGranted;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.comatching.item.domain.roulette.repository;

import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

Expand All @@ -16,11 +16,10 @@
import jakarta.persistence.LockModeType;

public interface RouletteHistoryRepository extends JpaRepository<RouletteHistory, Long> {
boolean existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
boolean existsByMemberIdAndRouletteTypeAndParticipationDate(
Long memberId,
RouletteType rouletteType,
LocalDateTime startAt,
LocalDateTime endAt
LocalDate participationDate
);

@EntityGraph(attributePaths = "reward")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ public class RouletteServiceImpl implements RouletteService {
@Override
@Transactional
public RouletteSpinResponse spinRoulette(MemberInfo memberInfo, RouletteType rouletteType) {
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
LocalDateTime participatedAt = LocalDateTime.now();
LocalDate participationDate = participatedAt.toLocalDate();
LocalDateTime todayStart = participationDate.atStartOfDay();
LocalDateTime tomorrowStart = todayStart.plusDays(1);

// 오늘 참여한 결과 더이상 불가능
boolean isParticipatedToday = rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
memberInfo.memberId(), rouletteType, todayStart, tomorrowStart);
.existsByMemberIdAndRouletteTypeAndParticipationDate(
memberInfo.memberId(), rouletteType, participationDate);
if (isParticipatedToday) {
throw new BusinessException(ItemErrorCode.ALREADY_PARTICIPATED_ROULETTE);
}
Expand Down Expand Up @@ -77,6 +79,7 @@ public RouletteSpinResponse spinRoulette(MemberInfo memberInfo, RouletteType rou
.reward(rouletteReward)
.rouletteType(rouletteType)
.rewardGranted(rewardGranted)
.participatedAt(participatedAt)
.build());
} catch (DataIntegrityViolationException exception) {
throw new BusinessException(ItemErrorCode.ALREADY_PARTICIPATED_ROULETTE);
Expand All @@ -88,18 +91,19 @@ public RouletteSpinResponse spinRoulette(MemberInfo memberInfo, RouletteType rou
@Override
public RoulettePageResponse roulettePage(MemberInfo memberInfo) {
// 오늘 날짜
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
LocalDate participationDate = LocalDate.now();
LocalDateTime todayStart = participationDate.atStartOfDay();
LocalDateTime tomorrowStart = todayStart.plusDays(1);

// 오늘 무료 룰렛 참여 여부
boolean isFreeParticipated = rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
memberInfo.memberId(), RouletteType.FREE, todayStart, tomorrowStart);
.existsByMemberIdAndRouletteTypeAndParticipationDate(
memberInfo.memberId(), RouletteType.FREE, participationDate);

// 오늘 유료 룰렛 참여 여부
boolean isSpecialParticipated = rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
memberInfo.memberId(), RouletteType.SPECIAL, todayStart, tomorrowStart);
.existsByMemberIdAndRouletteTypeAndParticipationDate(
memberInfo.memberId(), RouletteType.SPECIAL, participationDate);

// 오늘 결제액
long totalPay = orderRepository.sumApprovedPriceByMemberIdAndDecidedAtBetween(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,17 @@ void shouldRequestDistinctUsersAndReturnEveryWinningHistory() {
}

@Test
@DisplayName("당첨 회원 정보가 응답에서 누락되면 대상 사용자 없음 예외가 발생한다")
void shouldThrowWhenWinnerProfileIsMissing() {
@DisplayName("당첨 회원 정보가 응답에서 누락되면 해당 당첨 이력을 제외한다")
void shouldSkipWinnerWhenProfileIsMissing() {
RouletteHistory history = history(101L, 1L, "1만원권 상품권", false);
given(rouletteHistoryRepository
.findAllByReward_RewardTypeAndRewardGrantedFalseOrderByParticipatedAtDesc(RewardType.GIFT_CARD))
.willReturn(List.of(history));
given(userAdminClient.getUsersByIds(List.of(1L))).willReturn(List.of());

assertThatThrownBy(adminRouletteService::getUnpaidGiftCardWinners)
.isInstanceOf(BusinessException.class)
.satisfies(exception -> assertThat(((BusinessException)exception).getErrorCode())
.isEqualTo(ItemErrorCode.TARGET_USER_NOT_FOUND));
List<AdminGiftCardWinnerResponse> responses = adminRouletteService.getUnpaidGiftCardWinners();

assertThat(responses).isEmpty();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,11 @@ void shouldFindTodayFreeHistoryForSameMember() {
void shouldNotFindPastFreeHistory() {
RouletteHistory history = persistHistory(1L, RouletteType.FREE);
entityManager.flush();
LocalDate yesterday = LocalDate.now().minusDays(1);
jdbcTemplate.update(
"UPDATE roulette_history SET participated_at = ? WHERE id = ?",
Timestamp.valueOf(LocalDate.now().minusDays(1).atTime(12, 0)),
"UPDATE roulette_history SET participated_at = ?, participation_date = ? WHERE id = ?",
Timestamp.valueOf(yesterday.atTime(12, 0)),
yesterday,
history.getId());
entityManager.clear();

Expand Down Expand Up @@ -341,10 +343,9 @@ private RouletteHistory persistHistory(Long memberId, RouletteType rouletteType)
}

private boolean existsHistoryToday(Long memberId, RouletteType rouletteType) {
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
return rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
memberId, rouletteType, todayStart, todayStart.plusDays(1));
.existsByMemberIdAndRouletteTypeAndParticipationDate(
memberId, rouletteType, LocalDate.now());
}

private RouletteReward reward(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;

Expand Down Expand Up @@ -65,9 +66,9 @@ class RouletteServiceImplTest {
@DisplayName("오늘 무료 룰렛에 참여하지 않았으면 미참여 상태를 반환한다")
void shouldReturnFreeRouletteAsNotParticipated() {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);

RoulettePageResponse response = rouletteService.roulettePage(MEMBER);
Expand All @@ -81,9 +82,9 @@ void shouldReturnFreeRouletteAsNotParticipated() {
@DisplayName("오늘 무료 룰렛에 이미 참여했으면 참여 상태를 반환한다")
void shouldReturnFreeRouletteAsParticipated() {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(true);

RoulettePageResponse response = rouletteService.roulettePage(MEMBER);
Expand All @@ -92,9 +93,9 @@ void shouldReturnFreeRouletteAsParticipated() {
assertThat(response.isSpecialParticipated()).isFalse();
assertThat(response.totalPay()).isZero();
then(rouletteHistoryRepository).should()
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class));
any(LocalDate.class));
}

@Test
Expand All @@ -104,14 +105,14 @@ void shouldReturnPaymentAndSpecialRouletteAsNotParticipated() {
eq(MEMBER.memberId()), any(LocalDateTime.class), any(LocalDateTime.class)))
.willReturn(3500L);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.SPECIAL),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);

RoulettePageResponse response = rouletteService.roulettePage(MEMBER);
Expand All @@ -128,14 +129,14 @@ void shouldReturnSpecialRouletteAsParticipated() {
eq(MEMBER.memberId()), any(LocalDateTime.class), any(LocalDateTime.class)))
.willReturn(10000L);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.SPECIAL),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(true);

RoulettePageResponse response = rouletteService.roulettePage(MEMBER);
Expand All @@ -152,14 +153,14 @@ void shouldReturnPaymentBelowSpecialRouletteMinimum() {
eq(MEMBER.memberId()), any(LocalDateTime.class), any(LocalDateTime.class)))
.willReturn(3499L);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.SPECIAL),
any(LocalDateTime.class), any(LocalDateTime.class)))
any(LocalDate.class)))
.willReturn(false);

RoulettePageResponse response = rouletteService.roulettePage(MEMBER);
Expand Down Expand Up @@ -260,9 +261,9 @@ void shouldRecordNoPrizeWithoutGrantingItem() {
@DisplayName("이미 무료 룰렛에 참여한 회원은 예외가 발생하고 보상을 처리하지 않는다")
void shouldRejectDuplicatedFreeRouletteParticipation() {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.FREE),
any(LocalDateTime.class), any(LocalDateTime.class))).willReturn(true);
any(LocalDate.class))).willReturn(true);

assertThatThrownBy(() -> rouletteService.spinRoulette(MEMBER, RouletteType.FREE))
.isInstanceOf(BusinessException.class)
Expand All @@ -279,9 +280,9 @@ void shouldRejectDuplicatedFreeRouletteParticipation() {
@DisplayName("이미 스페셜 룰렛에 참여한 회원은 예외가 발생하고 결제액과 보상을 조회하지 않는다")
void shouldRejectDuplicatedSpecialRouletteParticipation() {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.SPECIAL),
any(LocalDateTime.class), any(LocalDateTime.class))).willReturn(true);
any(LocalDate.class))).willReturn(true);

assertThatThrownBy(() -> rouletteService.spinRoulette(MEMBER, RouletteType.SPECIAL))
.isInstanceOf(BusinessException.class)
Expand All @@ -299,9 +300,9 @@ void shouldRejectDuplicatedSpecialRouletteParticipation() {
@DisplayName("오늘 누적 결제액이 3500원 미만이면 예외가 발생하고 추첨하지 않는다")
void shouldRejectSpecialRouletteWhenPaymentIsBelowMinimum() {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(RouletteType.SPECIAL),
any(LocalDateTime.class), any(LocalDateTime.class))).willReturn(false);
any(LocalDate.class))).willReturn(false);
given(orderRepository.sumApprovedPriceByMemberIdAndDecidedAtBetween(
eq(MEMBER.memberId()), any(LocalDateTime.class), any(LocalDateTime.class)))
.willReturn(3499L);
Expand Down Expand Up @@ -411,9 +412,9 @@ void shouldNotDecreaseRemainingCountBelowZero() {

private void givenReward(RouletteType rouletteType, RouletteReward reward) {
given(rouletteHistoryRepository
.existsByMemberIdAndRouletteTypeAndParticipatedAtGreaterThanEqualAndParticipatedAtLessThan(
.existsByMemberIdAndRouletteTypeAndParticipationDate(
eq(MEMBER.memberId()), eq(rouletteType),
any(LocalDateTime.class), any(LocalDateTime.class))).willReturn(false);
any(LocalDate.class))).willReturn(false);
if (rouletteType == RouletteType.SPECIAL) {
given(orderRepository.sumApprovedPriceByMemberIdAndDecidedAtBetween(
eq(MEMBER.memberId()), any(LocalDateTime.class), any(LocalDateTime.class)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ public PagingResponse<AdminUserSummaryResponse> getUsers(String keyword, Pageabl
.map(this::toAdminUserProfileDto);

List<AdminUserProfileDto> users = userPage.getContent();
Map<Long, AdminInventoryCounts> inventoryCountsByMemberId = itemAdminClient.getInventoryCounts(
users.stream()
.map(AdminUserProfileDto::id)
.toList()
);
Map<Long, AdminInventoryCounts> inventoryCountsByMemberId =
users.isEmpty()
? Map.of()
: itemAdminClient.getInventoryCounts(
users.stream()
.map(AdminUserProfileDto::id)
.toList()
);

List<AdminUserSummaryResponse> summaries = users.stream()
.map(user -> AdminUserSummaryResponse.from(
Expand Down
Loading
Loading