diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java index 134085c55..725365c48 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/AuctionService.java @@ -22,6 +22,7 @@ import com.ddang.ddang.user.application.exception.UserNotFoundException; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -83,7 +84,7 @@ public ReadAuctionDto readByAuctionId(final Long auctionId) { "지정한 아이디에 대한 경매를 찾을 수 없습니다." )); - return ReadAuctionDto.from(findAuction); + return ReadAuctionDto.of(findAuction, LocalDateTime.now()); } public ReadAuctionsDto readAllByCondition( @@ -94,19 +95,19 @@ public ReadAuctionsDto readAllByCondition( readAuctionSearchCondition ); - return ReadAuctionsDto.from(auctions); + return ReadAuctionsDto.of(auctions, LocalDateTime.now()); } public ReadAuctionsDto readAllByUserId(final Long userId, final Pageable pageable) { final Slice auctions = auctionRepository.findAuctionsAllByUserId(userId, pageable); - return ReadAuctionsDto.from(auctions); + return ReadAuctionsDto.of(auctions, LocalDateTime.now()); } public ReadAuctionsDto readAllByBidderId(final Long userId, final Pageable pageable) { final Slice auctions = auctionRepository.findAuctionsAllByBidderId(userId, pageable); - return ReadAuctionsDto.from(auctions); + return ReadAuctionsDto.of(auctions, LocalDateTime.now()); } @Transactional diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java index 82e2af4b1..d6ca7fd44 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionDto.java @@ -1,6 +1,7 @@ package com.ddang.ddang.auction.application.dto; import com.ddang.ddang.auction.domain.Auction; +import com.ddang.ddang.auction.domain.AuctionStatus; import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.image.application.util.ImageIdProcessor; import com.ddang.ddang.image.domain.AuctionImage; @@ -27,10 +28,11 @@ public record ReadAuctionDto( Long sellerProfileId, String sellerName, double sellerReliability, - boolean isSellerDeleted + boolean isSellerDeleted, + AuctionStatus auctionStatus ) { - public static ReadAuctionDto from(final Auction auction) { + public static ReadAuctionDto of(final Auction auction, final LocalDateTime targetTime) { return new ReadAuctionDto( auction.getId(), auction.getTitle(), @@ -50,7 +52,8 @@ public static ReadAuctionDto from(final Auction auction) { ImageIdProcessor.process(auction.getSeller().getProfileImage()), auction.getSeller().getName(), auction.getSeller().getReliability(), - auction.getSeller().isDeleted() + auction.getSeller().isDeleted(), + auction.findAuctionStatus(targetTime) ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionsDto.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionsDto.java index 4e815f2ca..8635508a6 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionsDto.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/application/dto/ReadAuctionsDto.java @@ -1,15 +1,16 @@ package com.ddang.ddang.auction.application.dto; import com.ddang.ddang.auction.domain.Auction; +import java.time.LocalDateTime; import java.util.List; import org.springframework.data.domain.Slice; public record ReadAuctionsDto(List readAuctionDtos, boolean isLast) { - public static ReadAuctionsDto from(final Slice auctions) { + public static ReadAuctionsDto of(final Slice auctions, final LocalDateTime targetTime) { final List readAuctionDtos = auctions.getContent() .stream() - .map(ReadAuctionDto::from) + .map(auction -> ReadAuctionDto.of(auction, targetTime)) .toList(); return new ReadAuctionsDto(readAuctionDtos, !auctions.hasNext()); diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/Auction.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/Auction.java index 2f390df37..1f7d1cb16 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/Auction.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/Auction.java @@ -122,6 +122,19 @@ public void addAuctionImages(final List auctionImages) { } } + public AuctionStatus findAuctionStatus(final LocalDateTime targetTime) { + if (targetTime.isBefore(closingTime) && lastBid == null) { + return AuctionStatus.UNBIDDEN; + } + if (targetTime.isBefore(closingTime) && lastBid != null) { + return AuctionStatus.ONGOING; + } + if (targetTime.isAfter(closingTime) && lastBid == null) { + return AuctionStatus.FAILURE ; + } + return AuctionStatus.SUCCESS; + } + public boolean isOwner(final User user) { return this.seller.equals(user); } @@ -132,6 +145,7 @@ public boolean isClosed(final LocalDateTime targetTime) { public boolean isInvalidFirstBidPrice(final BidPrice bidPrice) { final BidPrice startBidPrice = new BidPrice(startPrice.getValue()); + return startBidPrice.isGreaterThan(bidPrice); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/AuctionStatus.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/AuctionStatus.java new file mode 100644 index 000000000..e86081ed7 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/domain/AuctionStatus.java @@ -0,0 +1,8 @@ +package com.ddang.ddang.auction.domain; + +public enum AuctionStatus { + UNBIDDEN, + ONGOING, + FAILURE, + SUCCESS +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/AuctionDetailResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/AuctionDetailResponse.java index 05342861e..1876234a2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/AuctionDetailResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/AuctionDetailResponse.java @@ -47,7 +47,7 @@ public static AuctionDetailResponse from(final ReadAuctionDto dto) { dto.description(), dto.startPrice(), dto.lastBidPrice(), - processAuctionStatus(dto.closingTime(), dto.lastBidPrice()), + dto.auctionStatus().name(), dto.bidUnit(), dto.registerTime(), dto.closingTime(), @@ -69,18 +69,4 @@ private static List convertDirectRegionsResponse(final Rea .map(DirectRegionResponse::from) .toList(); } - - // TODO 2차 데모데이 이후 enum으로 처리 - private static String processAuctionStatus(final LocalDateTime closingTime, final Integer lastBidPrice) { - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice == null) { - return "UNBIDDEN"; - } - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice != null) { - return "ONGOING"; - } - if (LocalDateTime.now().isAfter(closingTime) && lastBidPrice == null) { - return "FAILURE"; - } - return "SUCCESS"; - } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/CreateAuctionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/CreateAuctionResponse.java index f4ac4a740..cf1844aba 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/CreateAuctionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/CreateAuctionResponse.java @@ -1,6 +1,7 @@ package com.ddang.ddang.auction.presentation.dto.response; import com.ddang.ddang.auction.application.dto.CreateInfoAuctionDto; +import com.ddang.ddang.auction.domain.AuctionStatus; import com.ddang.ddang.image.presentation.util.ImageBaseUrl; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; @@ -19,8 +20,7 @@ public static CreateAuctionResponse from(final CreateInfoAuctionDto dto) { dto.title(), convertAuctionImageUrl(dto.auctionImageId()), dto.startPrice(), - // TODO 2차 데모데이 이후 enum으로 처리 - "UNBIDDEN", + AuctionStatus.UNBIDDEN.name(), 0 ); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java index 300652bc1..d0f0033f1 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/presentation/dto/response/ReadAuctionResponse.java @@ -4,8 +4,6 @@ import com.ddang.ddang.image.presentation.util.ImageBaseUrl; import com.ddang.ddang.image.presentation.util.ImageUrlCalculator; -import java.time.LocalDateTime; - public record ReadAuctionResponse( Long id, String title, @@ -21,7 +19,7 @@ public static ReadAuctionResponse from(final ReadAuctionDto dto) { dto.title(), convertImageUrl(dto.auctionImageIds().get(0)), processAuctionPrice(dto.startPrice(), dto.lastBidPrice()), - processAuctionStatus(dto.closingTime(), dto.lastBidPrice()), + dto.auctionStatus().name(), dto.auctioneerCount() ); } @@ -38,18 +36,4 @@ private static int processAuctionPrice(final Integer startPrice, final Integer l return lastBidPrice; } - - // TODO 2차 데모데이 이후 enum으로 처리 - private static String processAuctionStatus(final LocalDateTime closingTime, final Integer lastBidPrice) { - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice == null) { - return "UNBIDDEN"; - } - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice != null) { - return "ONGOING"; - } - if (LocalDateTime.now().isAfter(closingTime) && lastBidPrice == null) { - return "FAILURE"; - } - return "SUCCESS"; - } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/AuctionDetailResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/AuctionDetailResponse.java index 9be4aec99..b92b71090 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/AuctionDetailResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/AuctionDetailResponse.java @@ -46,7 +46,7 @@ public static AuctionDetailResponse from(final ReadAuctionDto dto) { dto.description(), dto.startPrice(), dto.lastBidPrice(), - processAuctionStatus(dto.closingTime(), dto.lastBidPrice()), + dto.auctionStatus().name(), dto.bidUnit(), dto.registerTime(), dto.closingTime(), @@ -68,18 +68,4 @@ private static List convertDirectRegionsResponse(final Rea .map(DirectRegionResponse::from) .toList(); } - - // TODO 2차 데모데이 이후 enum으로 처리 - private static String processAuctionStatus(final LocalDateTime closingTime, final Integer lastBidPrice) { - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice == null) { - return "UNBIDDEN"; - } - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice != null) { - return "ONGOING"; - } - if (LocalDateTime.now().isAfter(closingTime) && lastBidPrice == null) { - return "FAILURE"; - } - return "SUCCESS"; - } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/ReadAuctionResponse.java b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/ReadAuctionResponse.java index d4ac989cf..944fd2d52 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/ReadAuctionResponse.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/user/presentation/dto/response/ReadAuctionResponse.java @@ -1,7 +1,6 @@ package com.ddang.ddang.user.presentation.dto.response; import com.ddang.ddang.auction.application.dto.ReadAuctionDto; -import java.time.LocalDateTime; public record ReadAuctionResponse( Long id, @@ -18,7 +17,7 @@ public static ReadAuctionResponse of(final ReadAuctionDto dto, final String base dto.title(), convertImageUrl(dto, baseUrl), processAuctionPrice(dto.startPrice(), dto.lastBidPrice()), - processAuctionStatus(dto.closingTime(), dto.lastBidPrice()), + dto.auctionStatus().name(), dto.auctioneerCount() ); } @@ -34,18 +33,4 @@ private static int processAuctionPrice(final Integer startPrice, final Integer l return lastBidPrice; } - - // TODO 2차 데모데이 이후 enum으로 처리 - private static String processAuctionStatus(final LocalDateTime closingTime, final Integer lastBidPrice) { - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice == null) { - return "UNBIDDEN"; - } - if (LocalDateTime.now().isBefore(closingTime) && lastBidPrice != null) { - return "ONGOING"; - } - if (LocalDateTime.now().isAfter(closingTime) && lastBidPrice == null) { - return "FAILURE"; - } - return "SUCCESS"; - } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/domain/AuctionTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/domain/AuctionTest.java index 7bf115eff..8913ca23d 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/domain/AuctionTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/domain/AuctionTest.java @@ -1,5 +1,7 @@ package com.ddang.ddang.auction.domain; +import static org.assertj.core.api.Assertions.assertThat; + import com.ddang.ddang.bid.domain.Bid; import com.ddang.ddang.bid.domain.BidPrice; import com.ddang.ddang.configuration.JpaConfiguration; @@ -10,6 +12,9 @@ import com.ddang.ddang.region.domain.Region; import com.ddang.ddang.user.domain.User; import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -19,12 +24,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.util.ReflectionTestUtils; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; - @DataJpaTest @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") @@ -551,4 +550,97 @@ class AuctionTest { // then assertThat(actual).isEmpty(); } + + @Test + void 경매를_진행중이며_입찰자가_없는_경우_UNBIDDEN을_반환한다() { + // given + final User seller = User.builder() + .name("회원1") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + final LocalDateTime now = LocalDateTime.now(); + final Auction auction = Auction.builder() + .title("경매") + .seller(seller) + .closingTime(now.plusDays(2)) + .build(); + + // when + final AuctionStatus actual = auction.findAuctionStatus(now); + + // then + assertThat(actual).isEqualTo(AuctionStatus.UNBIDDEN); + } + + @Test + void 경매가_마감되었고_입찰자가_없는_경우_FAILURE를_반환한다() { + // given + final User seller = User.builder() + .name("회원1") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + final LocalDateTime now = LocalDateTime.now(); + final Auction auction = Auction.builder() + .title("경매") + .seller(seller) + .closingTime(now.minusDays(2)) + .build(); + + // when + final AuctionStatus actual = auction.findAuctionStatus(now); + + assertThat(actual).isEqualTo(AuctionStatus.FAILURE); + } + + @Test + void 경매가_진행중이며_입찰자가_있는_경우_ONGOING을_반환한다() { + // given + final User seller = User.builder() + .name("회원1") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + final LocalDateTime now = LocalDateTime.now(); + final Auction auction = Auction.builder() + .title("경매") + .seller(seller) + .closingTime(now.plusDays(2)) + .build(); + auction.updateLastBid(new Bid(auction, seller, new BidPrice(1500))); + + // when + final AuctionStatus actual = auction.findAuctionStatus(now); + + // then + assertThat(actual).isEqualTo(AuctionStatus.ONGOING); + } + + @Test + void 경매가_마감되었고_입찰자가_있는_경우_SUCCESS를_반환한다() { + // given + final User seller = User.builder() + .name("회원1") + .profileImage(new ProfileImage("upload.png", "store.png")) + .reliability(4.7d) + .oauthId("12345") + .build(); + final LocalDateTime now = LocalDateTime.now(); + final Auction auction = Auction.builder() + .title("경매") + .seller(seller) + .closingTime(now.minusDays(2)) + .build(); + auction.updateLastBid(new Bid(auction, seller, new BidPrice(1500))); + + // when + final AuctionStatus actual = auction.findAuctionStatus(now); + + // then + assertThat(actual).isEqualTo(AuctionStatus.SUCCESS); + } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java index 7d066051f..2cd63fe1a 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/auction/presentation/AuctionControllerTest.java @@ -35,6 +35,7 @@ import com.ddang.ddang.auction.application.dto.ReadRegionsDto; import com.ddang.ddang.auction.application.exception.AuctionNotFoundException; import com.ddang.ddang.auction.configuration.DescendingSortPageableArgumentResolver; +import com.ddang.ddang.auction.domain.AuctionStatus; import com.ddang.ddang.auction.presentation.dto.request.CreateAuctionRequest; import com.ddang.ddang.auction.presentation.dto.request.ReadAuctionSearchCondition; import com.ddang.ddang.authentication.configuration.AuthenticationInterceptor; @@ -454,7 +455,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final ReadChatRoomDto chatRoomDto = new ReadChatRoomDto(1L, true); @@ -568,7 +570,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final ReadAuctionDto auction2 = new ReadAuctionDto( 2L, @@ -589,7 +592,8 @@ void setUp() { 1L, "판매자", 3.5d, - true + true, + AuctionStatus.UNBIDDEN ); final PrivateClaims privateClaims = new PrivateClaims(1L); final ReadAuctionsDto readAuctionsDto = new ReadAuctionsDto(List.of(auction2, auction1), true); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserAuctionControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserAuctionControllerTest.java index ef47d62c1..3f74763f0 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserAuctionControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/user/presentation/UserAuctionControllerTest.java @@ -23,6 +23,7 @@ import com.ddang.ddang.auction.application.dto.ReadRegionDto; import com.ddang.ddang.auction.application.dto.ReadRegionsDto; import com.ddang.ddang.auction.configuration.DescendingSortPageableArgumentResolver; +import com.ddang.ddang.auction.domain.AuctionStatus; import com.ddang.ddang.authentication.configuration.AuthenticationInterceptor; import com.ddang.ddang.authentication.configuration.AuthenticationPrincipalArgumentResolver; import com.ddang.ddang.authentication.domain.TokenDecoder; @@ -101,7 +102,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final ReadAuctionDto auction2 = new ReadAuctionDto( 2L, @@ -122,7 +124,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final PrivateClaims privateClaims = new PrivateClaims(1L); final ReadAuctionsDto readAuctionsDto = new ReadAuctionsDto(List.of(auction2, auction1), true); @@ -203,7 +206,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final ReadAuctionDto auction2 = new ReadAuctionDto( 2L, @@ -224,7 +228,8 @@ void setUp() { 1L, "판매자", 3.5d, - false + false, + AuctionStatus.UNBIDDEN ); final PrivateClaims privateClaims = new PrivateClaims(1L); final ReadAuctionsDto readAuctionsDto = new ReadAuctionsDto(List.of(auction2, auction1), true);