diff --git a/src/main/java/com/skyhorsemanpower/auction/application/impl/AuctionServiceImpl.java b/src/main/java/com/skyhorsemanpower/auction/application/impl/AuctionServiceImpl.java index 3e07223..31ff4d1 100644 --- a/src/main/java/com/skyhorsemanpower/auction/application/impl/AuctionServiceImpl.java +++ b/src/main/java/com/skyhorsemanpower/auction/application/impl/AuctionServiceImpl.java @@ -6,7 +6,9 @@ import com.skyhorsemanpower.auction.domain.RoundInfo; import com.skyhorsemanpower.auction.kafka.KafkaProducerCluster; import com.skyhorsemanpower.auction.kafka.Topics; -import com.skyhorsemanpower.auction.kafka.dto.AuctionCloseDto; +import com.skyhorsemanpower.auction.kafka.data.MessageEnum; +import com.skyhorsemanpower.auction.kafka.data.dto.AlarmDto; +import com.skyhorsemanpower.auction.kafka.data.dto.AuctionCloseDto; import com.skyhorsemanpower.auction.repository.*; import com.skyhorsemanpower.auction.common.exception.ResponseStatus; import com.skyhorsemanpower.auction.data.dto.*; @@ -20,7 +22,6 @@ import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -140,6 +141,15 @@ public void auctionClose(String auctionUuid) { // 경매글 마감 처리 메시지와 결제 서비스 메시지 동일 토픽으로 진행 producer.sendMessage(Topics.Constant.AUCTION_CLOSE, auctionCloseDto); + // 알람 서비스로 메시지 전달 + AlarmDto alarmDto = AlarmDto.builder().receiverUuids(memberUuids.stream().toList()) + .message(MessageEnum.Constant.AUCTION_CLOSE_MESSAGE) + .eventType("경매") + .build(); + log.info("Kafka Message To Alarm Service >>> {}", alarmDto.toString()); + + producer.sendMessage(Topics.Constant.ALARM, alarmDto); + // 경매 마감 여부 저장 auctionCloseStateRepository.save(AuctionCloseState.builder() .auctionUuid(auctionUuid) diff --git a/src/main/java/com/skyhorsemanpower/auction/config/QuartzConfig.java b/src/main/java/com/skyhorsemanpower/auction/config/QuartzConfig.java index b331c3b..0e5e881 100644 --- a/src/main/java/com/skyhorsemanpower/auction/config/QuartzConfig.java +++ b/src/main/java/com/skyhorsemanpower/auction/config/QuartzConfig.java @@ -1,51 +1,43 @@ package com.skyhorsemanpower.auction.config; -import com.skyhorsemanpower.auction.kafka.dto.InitialAuctionDto; -import com.skyhorsemanpower.auction.quartz.AuctionClose; import lombok.RequiredArgsConstructor; -import org.quartz.*; +import org.quartz.spi.JobFactory; +import org.springframework.boot.autoconfigure.quartz.QuartzProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; -import java.time.Instant; -import java.util.Date; +import javax.sql.DataSource; +import java.util.Properties; @Configuration @RequiredArgsConstructor public class QuartzConfig { - private final Scheduler scheduler; - // 경매 시작과 경매 마감의 상태 변경 스케줄링 - public void schedulerUpdateAuctionStateJob(InitialAuctionDto initialAuctionDto) throws SchedulerException { - // JobDataMap 생성 및 auctionUuid 설정 - JobDataMap jobDataMap = new JobDataMap(); - jobDataMap.put("auctionUuid", initialAuctionDto.getAuctionUuid()); + private final DataSource dataSource; + private final QuartzProperties quartzProperties; + private final ApplicationContext applicationContext; - // Job 생성 - JobDetail auctionCloseJob = JobBuilder - .newJob(AuctionClose.class) - .withIdentity("AuctionCloseJob_" + initialAuctionDto.getAuctionUuid(), - "AuctionCloseGroup") - .usingJobData(jobDataMap) - .withDescription("경매 마감 Job") - .build(); - - Date auctionEndDate = Date.from(Instant.ofEpochMilli(initialAuctionDto.getAuctionStartTime())); - - // Trigger 생성 - Trigger auctionCloseTrigger = TriggerBuilder - .newTrigger() - .withIdentity("AuctionCloseTrigger_" + initialAuctionDto.getAuctionUuid(), - "AuctionCloseGroup") - .withDescription("경매 마감 Trigger") - - // test용 60초 후 시작하는 스케줄러 - .startAt(DateBuilder.futureDate(60, DateBuilder.IntervalUnit.SECOND)) - - //Todo 실제 배포에서는 auctionEndDate을 사용해야 한다. -// .startAt(auctionEndDate) - .build(); + @Bean + public JobFactory springBeanJobFactory() { + SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } - // 스케줄러 생성 및 Job, Trigger 등록 - scheduler.scheduleJob(auctionCloseJob, auctionCloseTrigger); + @Bean + public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) { + Properties properties = new Properties(); + properties.putAll(quartzProperties.getProperties()); + + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setJobFactory(jobFactory); + factory.setDataSource(dataSource); + factory.setQuartzProperties(properties); + factory.setOverwriteExistingJobs(true); + factory.setWaitForJobsToCompleteOnShutdown(true); + return factory; } } diff --git a/src/main/java/com/skyhorsemanpower/auction/config/QuartzJobConfig.java b/src/main/java/com/skyhorsemanpower/auction/config/QuartzJobConfig.java new file mode 100644 index 0000000..9922fe6 --- /dev/null +++ b/src/main/java/com/skyhorsemanpower/auction/config/QuartzJobConfig.java @@ -0,0 +1,65 @@ +package com.skyhorsemanpower.auction.config; + +import com.skyhorsemanpower.auction.kafka.data.dto.InitialAuctionDto; +import com.skyhorsemanpower.auction.quartz.AuctionClose; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.quartz.impl.StdScheduler; +import org.quartz.impl.StdSchedulerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Instant; +import java.util.Date; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class QuartzJobConfig { + private final Scheduler scheduler; + + @Bean + public Scheduler scheduler() throws SchedulerException { + Scheduler scheduler = new StdSchedulerFactory().getScheduler(); + scheduler.start(); + return scheduler; + } + + // 경매 시작과 경매 마감의 상태 변경 스케줄링 + public void schedulerUpdateAuctionStateJob(InitialAuctionDto initialAuctionDto) throws SchedulerException { + // JobDataMap 생성 및 auctionUuid 설정 + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("auctionUuid", initialAuctionDto.getAuctionUuid()); + + // Job 생성 + JobDetail auctionCloseJob = JobBuilder + .newJob(AuctionClose.class) + .withIdentity("AuctionCloseJob_" + initialAuctionDto.getAuctionUuid(), + "AuctionCloseGroup") + .usingJobData(jobDataMap) + .withDescription("경매 마감 Job") + .build(); + + + //todo + // 테스트를 위한경매 마감 시간을 경매 시작 시간으로부터 1분 뒤로 설정 +// Date auctionEndDate = Date.from(Instant.ofEpochMilli(initialAuctionDto.getAuctionStartTime()).plusSeconds(60)); + + // 배포에선 아래 코드 사용해야 함 + Date auctionEndDate = Date.from(Instant.ofEpochMilli(initialAuctionDto.getAuctionEndTime())); + log.info("Auction Close Job Will Start At >>> {}", auctionEndDate); + + // Trigger 생성 + Trigger auctionCloseTrigger = TriggerBuilder + .newTrigger() + .withIdentity("AuctionCloseTrigger_" + initialAuctionDto.getAuctionUuid(), + "AuctionCloseGroup") + .withDescription("경매 마감 Trigger") + .startAt(auctionEndDate) + .build(); + + // 스케줄러 생성 및 Job, Trigger 등록 + scheduler.scheduleJob(auctionCloseJob, auctionCloseTrigger); + } +} diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/KafkaConsumerCluster.java b/src/main/java/com/skyhorsemanpower/auction/kafka/KafkaConsumerCluster.java index 95a7f37..2f5ce13 100644 --- a/src/main/java/com/skyhorsemanpower/auction/kafka/KafkaConsumerCluster.java +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/KafkaConsumerCluster.java @@ -1,9 +1,9 @@ package com.skyhorsemanpower.auction.kafka; import com.skyhorsemanpower.auction.common.DateTimeConverter; -import com.skyhorsemanpower.auction.config.QuartzConfig; +import com.skyhorsemanpower.auction.config.QuartzJobConfig; import com.skyhorsemanpower.auction.domain.RoundInfo; -import com.skyhorsemanpower.auction.kafka.dto.InitialAuctionDto; +import com.skyhorsemanpower.auction.kafka.data.dto.InitialAuctionDto; import com.skyhorsemanpower.auction.repository.RoundInfoRepository; import com.skyhorsemanpower.auction.status.RoundTimeEnum; import lombok.RequiredArgsConstructor; @@ -23,7 +23,7 @@ @Component public class KafkaConsumerCluster { private final RoundInfoRepository roundInfoRepository; - private final QuartzConfig quartzConfig; + private final QuartzJobConfig quartzJobConfig; @KafkaListener(topics = Topics.Constant.INITIAL_AUCTION, groupId = "${spring.kafka.consumer.group-id}") public void initialAuction(@Payload LinkedHashMap message, @@ -46,7 +46,7 @@ public void initialAuction(@Payload LinkedHashMap message, // 경매 마감 스케줄러 등록 try { - quartzConfig.schedulerUpdateAuctionStateJob(initialAuctionDto); + quartzJobConfig.schedulerUpdateAuctionStateJob(initialAuctionDto); } catch (Exception e1) { log.warn(e1.getMessage()); } diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/Topics.java b/src/main/java/com/skyhorsemanpower/auction/kafka/Topics.java index cbab4ba..1e9950f 100644 --- a/src/main/java/com/skyhorsemanpower/auction/kafka/Topics.java +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/Topics.java @@ -12,7 +12,8 @@ public enum Topics { PAYMENT_SERVICE(Constant.PAYMENT_SERVICE), SUCCESSFUL_BID_ALARM(Constant.SUCCESSFUL_BID_ALARM), INITIAL_AUCTION(Constant.INITIAL_AUCTION), - AUCTION_CLOSE(Constant.AUCTION_CLOSE) + AUCTION_CLOSE(Constant.AUCTION_CLOSE), + ALARM(Constant.ALARM) ; public static class Constant { @@ -23,6 +24,7 @@ public static class Constant { public static final String SUCCESSFUL_BID_ALARM = "successful-bid-alarm-topic"; public static final String INITIAL_AUCTION = "initial-auction-topic"; public static final String AUCTION_CLOSE = "auction-close-topic"; + public static final String ALARM ="alarm-topic"; } diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/data/MessageEnum.java b/src/main/java/com/skyhorsemanpower/auction/kafka/data/MessageEnum.java new file mode 100644 index 0000000..1544342 --- /dev/null +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/data/MessageEnum.java @@ -0,0 +1,16 @@ +package com.skyhorsemanpower.auction.kafka.data; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum MessageEnum { + AUCTION_CLOSE_MESSAGE(Constant.AUCTION_CLOSE_MESSAGE); + + public static class Constant { + public static final String AUCTION_CLOSE_MESSAGE = "경매 낙찰되었습니다."; + } + + private final String message; +} diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AlarmDto.java b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AlarmDto.java new file mode 100644 index 0000000..2212fd4 --- /dev/null +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AlarmDto.java @@ -0,0 +1,24 @@ +package com.skyhorsemanpower.auction.kafka.data.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@Getter +@NoArgsConstructor +@ToString +public class AlarmDto { + private List receiverUuids; + private String message; + private String eventType; + + @Builder + public AlarmDto(List receiverUuids, String message, String eventType) { + this.receiverUuids = receiverUuids; + this.message = message; + this.eventType = eventType; + } +} diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionCloseDto.java b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionCloseDto.java similarity index 93% rename from src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionCloseDto.java rename to src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionCloseDto.java index 344e733..511f65c 100644 --- a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionCloseDto.java +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionCloseDto.java @@ -1,4 +1,4 @@ -package com.skyhorsemanpower.auction.kafka.dto; +package com.skyhorsemanpower.auction.kafka.data.dto; import com.skyhorsemanpower.auction.status.AuctionStateEnum; import lombok.Builder; diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionStartDto.java b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionStartDto.java similarity index 90% rename from src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionStartDto.java rename to src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionStartDto.java index 023bf89..af1982f 100644 --- a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/AuctionStartDto.java +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/AuctionStartDto.java @@ -1,4 +1,4 @@ -package com.skyhorsemanpower.auction.kafka.dto; +package com.skyhorsemanpower.auction.kafka.data.dto; import com.skyhorsemanpower.auction.status.AuctionStateEnum; import lombok.Builder; diff --git a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/InitialAuctionDto.java b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/InitialAuctionDto.java similarity index 94% rename from src/main/java/com/skyhorsemanpower/auction/kafka/dto/InitialAuctionDto.java rename to src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/InitialAuctionDto.java index d311416..3fa9906 100644 --- a/src/main/java/com/skyhorsemanpower/auction/kafka/dto/InitialAuctionDto.java +++ b/src/main/java/com/skyhorsemanpower/auction/kafka/data/dto/InitialAuctionDto.java @@ -1,4 +1,4 @@ -package com.skyhorsemanpower.auction.kafka.dto; +package com.skyhorsemanpower.auction.kafka.data.dto; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/com/skyhorsemanpower/auction/presentation/AuctionController.java b/src/main/java/com/skyhorsemanpower/auction/presentation/AuctionController.java index 7273d14..cb20046 100644 --- a/src/main/java/com/skyhorsemanpower/auction/presentation/AuctionController.java +++ b/src/main/java/com/skyhorsemanpower/auction/presentation/AuctionController.java @@ -15,11 +15,13 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; +@Slf4j @RestController @RequiredArgsConstructor @Tag(name = "경매 서비스", description = "경매 서비스 API") @@ -46,7 +48,14 @@ public SuccessResponse offerBiddingPrice( @Operation(summary = "경매 페이지 API", description = "경매 페이지에 보여줄 데이터 실시간 조회") public Flux auctionPage( @PathVariable("auctionUuid") String auctionUuid) { - return roundInfoReactiveRepository.searchRoundInfo(auctionUuid).subscribeOn(Schedulers.boundedElastic()); + return roundInfoReactiveRepository.searchRoundInfo(auctionUuid).subscribeOn(Schedulers.boundedElastic()) + .doOnError(error -> { + log.info("SSE error occured!! >>> {}", error.toString()); + }) + .onErrorResume(error -> { + // 에러 발생 시, 빈 Flux 객체를 반환 + return Flux.empty(); + }); } // 경매 페이지 최초 진입 시 현재 데이터 조회 API diff --git a/src/main/java/com/skyhorsemanpower/auction/quartz/AuctionClose.java b/src/main/java/com/skyhorsemanpower/auction/quartz/AuctionClose.java index 17b16bf..a57868a 100644 --- a/src/main/java/com/skyhorsemanpower/auction/quartz/AuctionClose.java +++ b/src/main/java/com/skyhorsemanpower/auction/quartz/AuctionClose.java @@ -7,7 +7,10 @@ import com.skyhorsemanpower.auction.domain.RoundInfo; import com.skyhorsemanpower.auction.kafka.KafkaProducerCluster; import com.skyhorsemanpower.auction.kafka.Topics; -import com.skyhorsemanpower.auction.kafka.dto.AuctionCloseDto; +import com.skyhorsemanpower.auction.kafka.data.MessageEnum; +import com.skyhorsemanpower.auction.kafka.data.dto.AlarmDto; +import com.skyhorsemanpower.auction.kafka.data.dto.AuctionCloseDto; +import com.skyhorsemanpower.auction.quartz.data.MemberUuidsAndPrice; import com.skyhorsemanpower.auction.repository.AuctionCloseStateRepository; import com.skyhorsemanpower.auction.repository.AuctionHistoryRepository; import com.skyhorsemanpower.auction.repository.RoundInfoRepository; @@ -42,13 +45,13 @@ public void execute(JobExecutionContext context) throws JobExecutionException { String auctionUuid = jobDataMap.getString("auctionUuid"); // auction_close_state 도큐먼트에 acutionUuid 데이터가 있으면(마감됐으면) 바로 return - if(auctionCloseStateRepository.findByAuctionUuid(auctionUuid).isPresent()) { + if (auctionCloseStateRepository.findByAuctionUuid(auctionUuid).isPresent()) { log.info("Auction already close"); return; } // auction_history 도큐먼트를 조회하여 경매 상태를 변경 - if(auctionHistoryRepository.findFirstByAuctionUuidOrderByBiddingTimeDesc(auctionUuid).isEmpty()) { + if (auctionHistoryRepository.findFirstByAuctionUuidOrderByBiddingTimeDesc(auctionUuid).isEmpty()) { log.info("auction_history is not exist! No one bid the auction!"); // 아무도 참여하지 않은 경우에는 auctionUuid와 auctionState(AUCTION_NO_PARTICIPANTS) 전송 @@ -56,8 +59,14 @@ public void execute(JobExecutionContext context) throws JobExecutionException { .auctionUuid(auctionUuid) .auctionState(AuctionStateEnum.AUCTION_NO_PARTICIPANTS) .build(); - log.info("No one bid the auction message >>> {}", noParticipantsAuctionCloseDto); + log.info("No one bid the auction message >>> {}", noParticipantsAuctionCloseDto.toString()); producer.sendMessage(Topics.AUCTION_CLOSE.getTopic(), noParticipantsAuctionCloseDto); + + // 경매 마감 여부 저장 + auctionCloseStateRepository.save(AuctionCloseState.builder() + .auctionUuid(auctionUuid) + .auctionCloseState(true) + .build()); return; } @@ -74,36 +83,12 @@ public void execute(JobExecutionContext context) throws JobExecutionException { long numberOfParticipants = lastRoundInfo.getNumberOfParticipants(); // 마감 로직 - // 마지막 라운드 입찰 이력 - List lastRoundAuctionHistory = auctionHistoryRepository. - findByAuctionUuidAndRoundOrderByBiddingTime(auctionUuid, round); - log.info("Last Round Auction History >>> {}", lastRoundAuctionHistory.toString()); - - // 마지막 - 1 라운드 입찰 이력 - List lastMinusOneRoundAuctionHistory = auctionHistoryRepository. - findByAuctionUuidAndRoundOrderByBiddingTime(auctionUuid, round - 1); - log.info("Before Last Round Auction History >>> {}", lastMinusOneRoundAuctionHistory.toString()); - - // 마지막 라운드 입찰자를 낙찰자로 고정 - Set memberUuids = new HashSet<>(); - for(AuctionHistory auctionHistory : lastRoundAuctionHistory) { - memberUuids.add(auctionHistory.getBiddingUuid()); - } + MemberUuidsAndPrice memberUuidsAndPrice = getMemberUuidsAndPrice( + round, auctionUuid, numberOfParticipants); - // 마지막 직전 라운드 입찰자 중 낙찰자 추가 - for(AuctionHistory auctionHistory : lastMinusOneRoundAuctionHistory) { - // 동일 입찰자 제외하고 추가 - memberUuids.add(auctionHistory.getBiddingUuid()); - - // 낙찰 가능 인원 수 만큼 리스트 추가 - if (memberUuids.size() == numberOfParticipants) break; - } - - log.info("memberUuids >>> {}", memberUuids.toString()); - - // 낙찰가는 마지막 이전 라운드에서 biddingPrice로 결정 - BigDecimal price = lastMinusOneRoundAuctionHistory.get(0).getBiddingPrice(); - log.info("price >>> {}", price); + // 낙찰가와 낙찰자 획득 + Set memberUuids = memberUuidsAndPrice.getMemberUuids(); + BigDecimal price = memberUuidsAndPrice.getPrice(); // 카프카로 경매 서비스 메시지 전달 AuctionCloseDto auctionCloseDto = AuctionCloseDto.builder() @@ -117,10 +102,76 @@ public void execute(JobExecutionContext context) throws JobExecutionException { // 경매글 마감 처리 메시지와 결제 서비스 메시지 동일 토픽으로 진행 producer.sendMessage(Topics.Constant.AUCTION_CLOSE, auctionCloseDto); + // 알람 서비스로 메시지 전달 + AlarmDto alarmDto = AlarmDto.builder().receiverUuids(memberUuids.stream().toList()) + .message(MessageEnum.Constant.AUCTION_CLOSE_MESSAGE) + .eventType("경매") + .build(); + log.info("Auction Close Message To Alarm Service >>> {}", alarmDto.toString()); + + producer.sendMessage(Topics.Constant.ALARM, alarmDto); + // 경매 마감 여부 저장 auctionCloseStateRepository.save(AuctionCloseState.builder() .auctionUuid(auctionUuid) .auctionCloseState(true) .build()); } + + private MemberUuidsAndPrice getMemberUuidsAndPrice(int round, String auctionUuid, long numberOfParticipants) { + Set memberUuids = new HashSet<>(); + BigDecimal price; + + // 마지막 라운드 입찰 이력 + List lastRoundAuctionHistory = auctionHistoryRepository. + findByAuctionUuidAndRoundOrderByBiddingTime(auctionUuid, round); + log.info("Last Round Auction History >>> {}", lastRoundAuctionHistory.toString()); + + // 1라운드에서 경매가 마감된 경우 + if (round == 1) { + log.info("One Round Close"); + // 마지막 라운드 입찰자를 낙찰자로 고정 + for (AuctionHistory auctionHistory : lastRoundAuctionHistory) { + memberUuids.add(auctionHistory.getBiddingUuid()); + } + + log.info("memberUuids >>> {}", memberUuids.toString()); + + // 낙찰가는 마지막 라운드에서 biddingPrice로 결정 + price = lastRoundAuctionHistory.get(0).getBiddingPrice(); + log.info("price >>> {}", price); + } + + // 1라운드 제외한 라운드에서 경매가 마감된 경우 + else { + log.info("{} Round Close", round); + + // 마지막 - 1 라운드 입찰 이력 + List lastMinusOneRoundAuctionHistory = auctionHistoryRepository. + findByAuctionUuidAndRoundOrderByBiddingTime(auctionUuid, round - 1); + log.info("Before Last Round Auction History >>> {}", lastMinusOneRoundAuctionHistory.toString()); + + // 마지막 라운드 입찰자를 낙찰자로 고정 + for (AuctionHistory auctionHistory : lastRoundAuctionHistory) { + memberUuids.add(auctionHistory.getBiddingUuid()); + } + + // 마지막 직전 라운드 입찰자 중 낙찰자 추가 + for (AuctionHistory auctionHistory : lastMinusOneRoundAuctionHistory) { + // 동일 입찰자 제외하고 추가 + memberUuids.add(auctionHistory.getBiddingUuid()); + + // 낙찰 가능 인원 수 만큼 리스트 추가 + if (memberUuids.size() == numberOfParticipants) break; + } + + log.info("memberUuids >>> {}", memberUuids.toString()); + + // 낙찰가는 마지막 이전 라운드에서 biddingPrice로 결정 + price = lastMinusOneRoundAuctionHistory.get(0).getBiddingPrice(); + log.info("price >>> {}", price); + } + + return MemberUuidsAndPrice.builder().memberUuids(memberUuids).price(price).build(); + } } diff --git a/src/main/java/com/skyhorsemanpower/auction/quartz/data/MemberUuidsAndPrice.java b/src/main/java/com/skyhorsemanpower/auction/quartz/data/MemberUuidsAndPrice.java new file mode 100644 index 0000000..df9a29e --- /dev/null +++ b/src/main/java/com/skyhorsemanpower/auction/quartz/data/MemberUuidsAndPrice.java @@ -0,0 +1,14 @@ +package com.skyhorsemanpower.auction.quartz.data; + +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.Set; + +@Getter +@Builder +public class MemberUuidsAndPrice { + private Set memberUuids; + private BigDecimal price; +}