diff --git a/src/main/java/org/kakaoshare/backend/domain/cart/dto/inquiry/CartResponse.java b/src/main/java/org/kakaoshare/backend/domain/cart/dto/inquiry/CartResponse.java index dc15fe20c..7d967617c 100644 --- a/src/main/java/org/kakaoshare/backend/domain/cart/dto/inquiry/CartResponse.java +++ b/src/main/java/org/kakaoshare/backend/domain/cart/dto/inquiry/CartResponse.java @@ -21,6 +21,7 @@ public class CartResponse { private String optionName; private String optionDetailName; private Long totalPrice; + private boolean isSelected; public static CartResponse from(Cart cart){ Long totalPrice = cart.calculateTotalPrice(); @@ -38,6 +39,7 @@ public static CartResponse from(Cart cart){ .totalPrice(totalPrice) .optionName(cart.getOption() != null ? cart.getOption().getName() : null) .optionDetailName(cart.getOptionDetail() != null ? cart.getOptionDetail().getName() : null) + .isSelected(cart.isSelected()) .build(); } } diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/controller/FundingController.java b/src/main/java/org/kakaoshare/backend/domain/funding/controller/FundingController.java index 60b3eee62..12439882b 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/controller/FundingController.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/controller/FundingController.java @@ -1,16 +1,14 @@ package org.kakaoshare.backend.domain.funding.controller; -import java.util.List; import lombok.RequiredArgsConstructor; import org.kakaoshare.backend.common.dto.PageResponse; - -import org.kakaoshare.backend.domain.funding.dto.FundingCheckRequest; -import org.kakaoshare.backend.domain.funding.dto.inquiry.request.FriendFundingInquiryRequest; import org.kakaoshare.backend.domain.funding.dto.FriendFundingItemRequest; +import org.kakaoshare.backend.domain.funding.dto.FundingCheckRequest; import org.kakaoshare.backend.domain.funding.dto.FundingResponse; import org.kakaoshare.backend.domain.funding.dto.ProgressResponse; import org.kakaoshare.backend.domain.funding.dto.RegisterRequest; import org.kakaoshare.backend.domain.funding.dto.RegisterResponse; +import org.kakaoshare.backend.domain.funding.dto.inquiry.request.FriendFundingInquiryRequest; import org.kakaoshare.backend.domain.funding.dto.preview.request.FundingPreviewRequest; import org.kakaoshare.backend.domain.funding.dto.preview.response.FundingPreviewResponse; import org.kakaoshare.backend.domain.funding.entity.FundingStatus; @@ -24,18 +22,21 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RequiredArgsConstructor @RestController @RequestMapping("/api/v1") public class FundingController { + private static final int DEFAULT_FUNDING_SIZE = 20; + private static final int DEFAULT_TOP_CONTRIBUTORS_SIZE = 5; + private final FundingService fundingService; private final FundingDetailService fundingDetailService; - private static final int FUNDING_DEFAULT_SIZE = 20; @PostMapping("/funding/{productId}") public ResponseEntity registerFunding(@PathVariable("productId") Long productId, @@ -74,7 +75,7 @@ public ResponseEntity getFriendsActiveFundingItems(@LoggedInMember String pro @GetMapping("/members/funding/products") public ResponseEntity getMyAllFundingProducts(@LoggedInMember String providerId, @RequestParam(name = "status", required = false) FundingStatus status, - @PageableDefault(size = FUNDING_DEFAULT_SIZE) final Pageable pageable) { + @PageableDefault(size = DEFAULT_FUNDING_SIZE) final Pageable pageable) { PageResponse response = fundingService.getMyFilteredFundingProducts(providerId, status, pageable); return ResponseEntity.ok(response); } @@ -85,14 +86,10 @@ public ResponseEntity preview(@RequestBody final FundingPreviewRequest fundin return ResponseEntity.ok(fundingPreviewResponse); } - @GetMapping("/{fundingId}/contributors") - public ResponseEntity getTopContributors(@PathVariable Long fundingId, - @PageableDefault(size = 5) Pageable pageable, - @RequestHeader("Authorization") String accessToken) { - - accessToken = accessToken.substring("Bearer ".length()); - PageResponse contributors = fundingDetailService.getTopContributors(fundingId, pageable, - accessToken); - return ResponseEntity.ok(contributors); + @GetMapping("/funding/{fundingId}/contributors") + public ResponseEntity getTopContributors(@PathVariable final Long fundingId, + @PageableDefault(size = DEFAULT_TOP_CONTRIBUTORS_SIZE) final Pageable pageable) { + final PageResponse response = fundingDetailService.getTopContributors(fundingId, pageable); + return ResponseEntity.ok(response); } } diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/dto/rank/response/TopContributorResponse.java b/src/main/java/org/kakaoshare/backend/domain/funding/dto/rank/response/TopContributorResponse.java new file mode 100644 index 000000000..bf7b63b63 --- /dev/null +++ b/src/main/java/org/kakaoshare/backend/domain/funding/dto/rank/response/TopContributorResponse.java @@ -0,0 +1,8 @@ +package org.kakaoshare.backend.domain.funding.dto.rank.response; + +public record TopContributorResponse( + String profileUrl, + String name, + Double rate +) { +} diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/entity/Funding.java b/src/main/java/org/kakaoshare/backend/domain/funding/entity/Funding.java index 43f3868a9..1189d1f1c 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/entity/Funding.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/entity/Funding.java @@ -128,7 +128,7 @@ public boolean attributable() { } public boolean isAttributableAmount(final int attributeAmount) { - return getRemainAmount() >= attributeAmount; + return (100 <= attributeAmount && attributeAmount <= getRemainAmount() - 100) || attributeAmount == getRemainAmount(); } public boolean canceled() { diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/entity/FundingDetail.java b/src/main/java/org/kakaoshare/backend/domain/funding/entity/FundingDetail.java index 30f5c9d31..53d271e2e 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/entity/FundingDetail.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/entity/FundingDetail.java @@ -71,13 +71,6 @@ public FundingDetail(final Member member, this.rate = calculateRate(this.amount); } - public void increaseAmountAndRate(final Long amount) { - if (amount != null) { - this.rate += calculateRate(amount); - this.amount += amount; - } - } - public void partialCancel(final Long amount) { this.rate -= calculateRate(amount); this.amount -= amount; diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/exception/FundingDetailErrorCode.java b/src/main/java/org/kakaoshare/backend/domain/funding/exception/FundingDetailErrorCode.java index b30e4a71c..26719947f 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/exception/FundingDetailErrorCode.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/exception/FundingDetailErrorCode.java @@ -6,7 +6,8 @@ @Getter public enum FundingDetailErrorCode implements ErrorCode { - NOT_FOUND(HttpStatus.NOT_FOUND, "기여한 펀딩 내역을 찾을 수 없습니다."); + NOT_FOUND(HttpStatus.NOT_FOUND, "기여한 펀딩 내역을 찾을 수 없습니다."), + INVALID_CANCEL_AMOUNT(HttpStatus.BAD_REQUEST, "환불 금액은 기여 금액보다 클 수 없습니다."); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepository.java b/src/main/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepository.java index 858571ad6..4ca24faa6 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepository.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepository.java @@ -1,5 +1,6 @@ package org.kakaoshare.backend.domain.funding.repository; +import org.kakaoshare.backend.domain.funding.dto.rank.response.TopContributorResponse; import org.kakaoshare.backend.domain.funding.entity.Funding; import org.kakaoshare.backend.domain.funding.entity.FundingDetail; import org.kakaoshare.backend.domain.funding.repository.query.FundingDetailRepositoryCustom; @@ -21,7 +22,9 @@ public interface FundingDetailRepository extends JpaRepository findAllByFundingId(@Param("fundingId") final Long fundingId); - @Query("SELECT fd FROM FundingDetail fd WHERE fd.funding.fundingId = :fundingId ORDER BY fd.amount DESC") - Page findTopContributorsByFundingId(@Param("fundingId") Long fundingId, Pageable pageable); - + @Query("SELECT NEW org.kakaoshare.backend.domain.funding.dto.rank.response.TopContributorResponse(fd.member.profileImageUrl, fd.member.name, fd.rate) " + + "FROM FundingDetail fd " + + "LEFT JOIN fd.member m ON m.memberId = fd.member.memberId " + + "WHERE fd.funding.fundingId = :fundingId") + Page findTopContributorsByFundingId(@Param("fundingId") Long fundingId, Pageable pageable); } diff --git a/src/main/java/org/kakaoshare/backend/domain/funding/service/FundingDetailService.java b/src/main/java/org/kakaoshare/backend/domain/funding/service/FundingDetailService.java index 011daea56..0e38bd1a5 100644 --- a/src/main/java/org/kakaoshare/backend/domain/funding/service/FundingDetailService.java +++ b/src/main/java/org/kakaoshare/backend/domain/funding/service/FundingDetailService.java @@ -3,29 +3,22 @@ import com.querydsl.core.util.StringUtils; import lombok.RequiredArgsConstructor; import org.kakaoshare.backend.common.dto.PageResponse; -import org.kakaoshare.backend.domain.friend.service.KakaoFriendService; import org.kakaoshare.backend.domain.funding.dto.inquiry.ContributedFundingHistoryDto; import org.kakaoshare.backend.domain.funding.dto.inquiry.request.ContributedFundingHistoryRequest; import org.kakaoshare.backend.domain.funding.dto.inquiry.response.ContributedFundingHistoryResponse; -import org.kakaoshare.backend.domain.funding.dto.inquiry.response.FundingContributorResponse; -import org.kakaoshare.backend.domain.funding.entity.FundingDetail; +import org.kakaoshare.backend.domain.funding.dto.rank.response.TopContributorResponse; import org.kakaoshare.backend.domain.funding.repository.FundingDetailRepository; import org.kakaoshare.backend.domain.funding.vo.FundingHistoryDate; -import org.kakaoshare.backend.domain.member.dto.oauth.profile.detail.KakaoFriendListDto; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class FundingDetailService { private final FundingDetailRepository fundingDetailRepository; - private final KakaoFriendService kakaoFriendService; public PageResponse lookUp(final String providerId, final ContributedFundingHistoryRequest contributedFundingHistoryRequest, @@ -45,36 +38,8 @@ private Page getFundingDetailHistoryDto(final Stri return fundingDetailRepository.findHistoryByCondition(providerId, date, status, pageable); } - public PageResponse getTopContributors(Long fundingId, Pageable pageable, String accessToken) { - Page fundingDetails = fundingDetailRepository.findTopContributorsByFundingId(fundingId, pageable); - - List friendsList = kakaoFriendService.getFriendsList(accessToken); - - List responses = fundingDetails.stream() - .map(detail -> { - KakaoFriendListDto friendProfile = findFriendProfile(detail.getMember().getProviderId(), friendsList); - return FundingContributorResponse.of( - friendProfile.getProfileThumbnailImage(), - friendProfile.getProfileNickname(), - detail.getAmount(), - calculateContributionPercentage(detail) - ); - }).toList(); - - Page contributorResponses = new PageImpl<>(responses, pageable, fundingDetails.getTotalElements()); - return PageResponse.from(contributorResponses); - } - - private KakaoFriendListDto findFriendProfile(String providerId, List friendsList) { - return friendsList.stream() - .filter(friend -> friend.getId().equals(providerId)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No matching friend found for providerId: " + providerId)); - } - - - - private double calculateContributionPercentage(FundingDetail detail) { - return 100.0 * detail.getAmount() / detail.getFunding().getGoalAmount(); + public PageResponse getTopContributors(final Long fundingId, final Pageable pageable) { + final Page page = fundingDetailRepository.findTopContributorsByFundingId(fundingId, pageable); + return PageResponse.from(page); } } diff --git a/src/main/java/org/kakaoshare/backend/domain/payment/service/PaymentService.java b/src/main/java/org/kakaoshare/backend/domain/payment/service/PaymentService.java index f8a37e6ae..38cf690db 100644 --- a/src/main/java/org/kakaoshare/backend/domain/payment/service/PaymentService.java +++ b/src/main/java/org/kakaoshare/backend/domain/payment/service/PaymentService.java @@ -74,6 +74,7 @@ import java.util.Objects; import java.util.function.Function; +import static org.kakaoshare.backend.domain.funding.exception.FundingDetailErrorCode.INVALID_CANCEL_AMOUNT; import static org.kakaoshare.backend.domain.funding.exception.FundingErrorCode.INVALID_ATTRIBUTE_AMOUNT; import static org.kakaoshare.backend.domain.funding.exception.FundingErrorCode.INVALID_STATUS; import static org.kakaoshare.backend.domain.member.exception.MemberErrorCode.NOT_FOUND; @@ -170,7 +171,7 @@ public PaymentFundingSuccessResponse approveFunding(final String providerId, final Funding funding = findFundingById(fundingOrderDetail.fundingId()); final Member member = findMemberByProviderId(providerId); final Long amount = payment.getTotalPrice(); - saveOrReflectFundingDetail(payment, funding, member, amount); + saveFundingDetail(payment, funding, member); funding.increaseAccumulateAmount(amount); // TODO: 5/10/24 결제 후 목표 금액 달성 시 @@ -224,12 +225,19 @@ public void cancelFundingDetail(final String providerId, final PaymentFundingDetailCancelRequest paymentFundingCancelRequest) { final Long fundingDetailId = paymentFundingCancelRequest.fundingDetailId(); final FundingDetail fundingDetail = findFundingDetailById(fundingDetailId); + final Long amount = paymentFundingCancelRequest.amount(); + validateCancelAmount(fundingDetail, amount); validateAlreadyCanceled(fundingDetail, FundingDetail::canceled); validateMemberFundingDetail(providerId, fundingDetail); - final Long amount = paymentFundingCancelRequest.amount(); refundFundingDetails(amount, fundingDetail); } + private void validateCancelAmount(final FundingDetail fundingDetail, final Long amount) { + if (fundingDetail.getAmount() < amount) { + throw new FundingDetailException(INVALID_CANCEL_AMOUNT); + } + } + private Order findOrderByPaymentId(final Long paymentId) { return orderRepository.findByPaymentId(paymentId) .orElseThrow(() -> new OrderException(OrderErrorCode.NOT_FOUND)); @@ -480,11 +488,8 @@ private FundingDetail findFundingDetailById(final Long fundingDetailId) { .orElseThrow(() -> new FundingDetailException(FundingDetailErrorCode.NOT_FOUND)); } - private void saveOrReflectFundingDetail(final Payment payment, final Funding funding, final Member member, final Long amount) { - fundingDetailRepository.findByFundingAndMember(funding, member) - .ifPresentOrElse( - fundingDetail -> fundingDetail.increaseAmountAndRate(amount), - () -> fundingDetailRepository.save(new FundingDetail(member, funding, payment)) - ); + private void saveFundingDetail(final Payment payment, final Funding funding, final Member member) { + final FundingDetail fundingDetail = new FundingDetail(member, funding, payment); + fundingDetailRepository.save(fundingDetail); } } diff --git a/src/main/java/org/kakaoshare/backend/domain/receipt/entity/ReceiptOption.java b/src/main/java/org/kakaoshare/backend/domain/receipt/entity/ReceiptOption.java index 39523fb06..5e24b03b6 100644 --- a/src/main/java/org/kakaoshare/backend/domain/receipt/entity/ReceiptOption.java +++ b/src/main/java/org/kakaoshare/backend/domain/receipt/entity/ReceiptOption.java @@ -20,9 +20,9 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table( - indexes = {@Index(name = "idx_receipt_option_receipt_id",columnList = "receipt_id",unique = true)} + indexes = {@Index(name = "idx_receipt_option_receipt_id", columnList = "receipt_id", unique = false)} ) -public class ReceiptOption { +public class ReceiptOption extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java b/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java index 53867e6fc..12a92a69a 100644 --- a/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java +++ b/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java @@ -1,15 +1,32 @@ package org.kakaoshare.backend.logging.config; +import lombok.RequiredArgsConstructor; +import org.kakaoshare.backend.logging.interceptor.LoggingInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StopWatch; import org.springframework.web.context.annotation.RequestScope; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -public class LoggingConfig { +@RequiredArgsConstructor +public class LoggingConfig implements WebMvcConfigurer { + private static final String METRIC_URL_PREFIX = "/actuator"; + private static final String FAVICON_URL = "/favicon.ico"; + + private final LoggingInterceptor loggingInterceptor; + @Bean @RequestScope public StopWatch stopWatch() { return new StopWatch(); } + + @Override + public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(loggingInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(METRIC_URL_PREFIX, FAVICON_URL); + } } diff --git a/src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java similarity index 81% rename from src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java rename to src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java index 1916275ef..530c6c8a9 100644 --- a/src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java +++ b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java @@ -1,20 +1,19 @@ -package org.kakaoshare.backend.logging.filter; +package org.kakaoshare.backend.logging.interceptor; + import com.querydsl.core.util.StringUtils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.kakaoshare.backend.logging.util.ApiQueryCounter; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StopWatch; -import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.context.request.WebRequestInterceptor; +import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; -import java.io.IOException; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -26,8 +25,7 @@ @Component @Slf4j -@RequiredArgsConstructor -public class LoggingFilter extends OncePerRequestFilter { +public class LoggingInterceptor extends WebRequestHandlerInterceptorAdapter { private static final String REQUEST_LOG_NO_BODY_FORMAT = "REQUEST :: METHOD: {}, URL: {}, HAS_AUTHORIZATION: {}"; private static final String REQUEST_LOG_FORMAT = REQUEST_LOG_NO_BODY_FORMAT + ", BODY: {}"; private static final String RESPONSE_LOG_NO_BODY_FORMAT = "RESPONSE :: STATUS_CODE: {}, METHOD: {}, URL: {}, QUERY_COUNT: {}, TIME_TAKEN: {}ms"; @@ -38,28 +36,32 @@ public class LoggingFilter extends OncePerRequestFilter { private static final String PARAM_DELIMITER = "&"; private static final String KEY_VALUE_DELIMITER = "="; - private static final String METRIC_URL_PREFIX = "/actuator"; - private static final String FAVICON_URL = "/favicon.ico"; - private final StopWatch apiTimer; private final ApiQueryCounter apiQueryCounter; - @Override - protected void doFilterInternal(final HttpServletRequest request, - final HttpServletResponse response, - final FilterChain filterChain) throws ServletException, IOException { - final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); - final ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); - final String requestURI = cachingRequest.getRequestURI(); - if (requestURI.contains(METRIC_URL_PREFIX) || requestURI.contains(FAVICON_URL)) { - doFilter(request, response, filterChain); - return; - } + @Autowired + public LoggingInterceptor(final WebRequestInterceptor requestInterceptor, + final StopWatch apiTimer, + final ApiQueryCounter apiQueryCounter) { + super(requestInterceptor); + this.apiTimer = apiTimer; + this.apiQueryCounter = apiQueryCounter; + } + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { apiTimer.start(); - filterChain.doFilter(cachingRequest, cachingResponse); - apiTimer.stop(); + return true; + } + @Override + public void afterCompletion(final HttpServletRequest request, + final HttpServletResponse response, + final Object handler, + final Exception ex) throws Exception { + apiTimer.stop(); + final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); + final ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); logRequestAndResponse(cachingRequest, cachingResponse); cachingResponse.copyBodyToResponse(); } diff --git a/src/main/resources/db/migration/V10__modify_receipt_option_idx.sql b/src/main/resources/db/migration/V10__modify_receipt_option_idx.sql new file mode 100644 index 000000000..d8d7e9395 --- /dev/null +++ b/src/main/resources/db/migration/V10__modify_receipt_option_idx.sql @@ -0,0 +1,8 @@ +ALTER TABLE receipt_option + DROP CONSTRAINT `FK_RECEIPT_OPTION_ON_RECEIPT`; +ALTER TABLE receipt_option + DROP INDEX `idx_receipt_option_receipt_id`; +ALTER TABLE receipt_option + ADD CONSTRAINT `FK_RECEIPT_OPTION_ON_RECEIPT` FOREIGN KEY (`receipt_id`) REFERENCES `receipt` (`receipt_id`); +ALTER TABLE receipt_option + ADD INDEX `idx_receipt_option_receipt_id` (`receipt_id`); \ No newline at end of file diff --git a/src/main/resources/testdata/initial_5_payment.sql b/src/main/resources/testdata/initial_5_payment.sql new file mode 100644 index 000000000..9925c99d6 --- /dev/null +++ b/src/main/resources/testdata/initial_5_payment.sql @@ -0,0 +1,4 @@ +INSERT INTO payment (created_at, delivery_price, payment_id, purchase_price, total_price, payment_number, method) +VALUES (NOW(), 0, 1, 3000, 3000, '1', 'KAKAO_PAY'), + (NOW(), 0, 2, 2000, 2000, '2', 'KAKAO_PAY'), + (NOW(), 0, 3, 1000, 1000, '3', 'KAKAO_PAY'); \ No newline at end of file diff --git a/src/main/resources/testdata/initial_6_funding_detail.sql b/src/main/resources/testdata/initial_6_funding_detail.sql new file mode 100644 index 000000000..8505d37f4 --- /dev/null +++ b/src/main/resources/testdata/initial_6_funding_detail.sql @@ -0,0 +1,4 @@ +INSERT INTO funding_detail (created_at, amount, rate, funding_id, payment_id, member_id, status) +VALUES (NOW(), 3000, 30.0, 1, 1, 1, 'PROGRESS'), + (NOW(), 2000, 20.0, 1, 1, 2, 'PROGRESS'), + (NOW(), 1000, 10.0, 1, 1, 3, 'PROGRESS'); \ No newline at end of file diff --git a/src/test/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepositoryTest.java b/src/test/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepositoryTest.java new file mode 100644 index 000000000..cbc6050fd --- /dev/null +++ b/src/test/java/org/kakaoshare/backend/domain/funding/repository/FundingDetailRepositoryTest.java @@ -0,0 +1,32 @@ +package org.kakaoshare.backend.domain.funding.repository; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.kakaoshare.backend.common.RepositoryTest; +import org.kakaoshare.backend.domain.funding.dto.rank.response.TopContributorResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import java.util.List; + +@RepositoryTest +class FundingDetailRepositoryTest { + @Autowired + FundingDetailRepository fundingDetailRepository; + + @Test + @DisplayName("최대 기여자 조회") + public void findTopContributorsByFundingId() throws Exception { + final Long fundingId = 1L; + final Pageable pageable = PageRequest.of(0, 5, Sort.by(Sort.Direction.DESC, "rate")); + final Page page = fundingDetailRepository.findTopContributorsByFundingId(fundingId, pageable); + final List content = page.getContent(); + Assertions.assertThat(content.get(0).rate()).isEqualTo(30.0); + Assertions.assertThat(content.get(1).rate()).isEqualTo(20.0); + Assertions.assertThat(content.get(2).rate()).isEqualTo(10.0); + } +} \ No newline at end of file diff --git a/src/test/java/org/kakaoshare/backend/domain/funding/service/FundingDetailServiceTest.java b/src/test/java/org/kakaoshare/backend/domain/funding/service/FundingDetailServiceTest.java index dcc3c2b7a..d6d37e4e0 100644 --- a/src/test/java/org/kakaoshare/backend/domain/funding/service/FundingDetailServiceTest.java +++ b/src/test/java/org/kakaoshare/backend/domain/funding/service/FundingDetailServiceTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; @@ -12,6 +13,7 @@ import org.kakaoshare.backend.domain.funding.dto.inquiry.ContributedFundingHistoryDto; import org.kakaoshare.backend.domain.funding.dto.inquiry.request.ContributedFundingHistoryRequest; import org.kakaoshare.backend.domain.funding.dto.inquiry.response.ContributedFundingHistoryResponse; +import org.kakaoshare.backend.domain.funding.dto.rank.response.TopContributorResponse; import org.kakaoshare.backend.domain.funding.repository.FundingDetailRepository; import org.kakaoshare.backend.domain.funding.vo.FundingHistoryDate; import org.kakaoshare.backend.domain.member.entity.Member; @@ -156,6 +158,25 @@ public void lookUpWhenInvalidDateRange(final String status) throws Exception { .isInstanceOf(DateException.class); } + + @Test + @DisplayName("최대 기여자 조회") + public void getTopContributors() throws Exception { + final Long fundingId = 1L; + final List content = List.of( + new TopContributorResponse("profileUrl1", "테스터1", 30.0), + new TopContributorResponse("profileUrl1", "테스터2", 20.0), + new TopContributorResponse("profileUrl1", "테스터3", 10.0) + ); + + final Page page = new PageImpl<>(content, pageable, content.size()); + doReturn(page).when(fundingDetailRepository).findTopContributorsByFundingId(fundingId, pageable); + + final PageResponse expect = PageResponse.from(page); + final PageResponse actual = fundingDetailService.getTopContributors(fundingId, pageable); + assertThat(actual).usingRecursiveComparison().isEqualTo(expect); + } + private ProductDto getProductDto(final Product product) { return new ProductDto( product.getProductId(), diff --git a/src/test/java/org/kakaoshare/backend/domain/payment/service/PaymentServiceTest.java b/src/test/java/org/kakaoshare/backend/domain/payment/service/PaymentServiceTest.java index dfe838114..240ce5457 100644 --- a/src/test/java/org/kakaoshare/backend/domain/payment/service/PaymentServiceTest.java +++ b/src/test/java/org/kakaoshare/backend/domain/payment/service/PaymentServiceTest.java @@ -30,7 +30,11 @@ import org.kakaoshare.backend.domain.payment.dto.kakaopay.Amount; import org.kakaoshare.backend.domain.payment.dto.preview.PaymentPreviewRequest; import org.kakaoshare.backend.domain.payment.dto.preview.PaymentPreviewResponse; -import org.kakaoshare.backend.domain.payment.dto.ready.request.*; +import org.kakaoshare.backend.domain.payment.dto.ready.request.PaymentFundingReadyRequest; +import org.kakaoshare.backend.domain.payment.dto.ready.request.PaymentGiftReadyItem; +import org.kakaoshare.backend.domain.payment.dto.ready.request.PaymentGiftReadyReceiver; +import org.kakaoshare.backend.domain.payment.dto.ready.request.PaymentGiftReadyRequest; +import org.kakaoshare.backend.domain.payment.dto.ready.request.PaymentReadyProductDto; import org.kakaoshare.backend.domain.payment.dto.ready.response.KakaoPayReadyResponse; import org.kakaoshare.backend.domain.payment.dto.ready.response.PaymentReadyResponse; import org.kakaoshare.backend.domain.payment.dto.success.request.PaymentSuccessRequest; @@ -49,13 +53,19 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.time.LocalDateTime; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.kakaoshare.backend.fixture.BrandFixture.STARBUCKS; import static org.kakaoshare.backend.fixture.FundingFixture.SAMPLE_FUNDING; -import static org.kakaoshare.backend.fixture.MemberFixture.*; +import static org.kakaoshare.backend.fixture.MemberFixture.HAN; +import static org.kakaoshare.backend.fixture.MemberFixture.KAKAO; +import static org.kakaoshare.backend.fixture.MemberFixture.KIM; import static org.kakaoshare.backend.fixture.ProductFixture.CAKE; import static org.kakaoshare.backend.fixture.ProductFixture.COFFEE; import static org.mockito.ArgumentMatchers.any; @@ -361,7 +371,6 @@ public void approveFunding() throws Exception { final Funding funding = SAMPLE_FUNDING.생성(1L, creator, cake); doReturn(fundingOrderDetail).when(redisUtils).remove(orderDetailsKey, FundingOrderDetail.class); doReturn(Optional.of(funding)).when(fundingRepository).findById(funding.getFundingId()); - doReturn(Optional.empty()).when(fundingDetailRepository).findByFundingAndMember(funding, contributor); doReturn(Optional.of(contributor)).when(memberRepository).findMemberByProviderId(providerId); final ProductSummaryResponse productSummaryResponse = ProductSummaryResponse.from(cake);