diff --git a/build.gradle b/build.gradle index 59f169d8..01d28d45 100644 --- a/build.gradle +++ b/build.gradle @@ -3,16 +3,10 @@ plugins { id 'org.springframework.boot' version '3.2.2' id 'io.spring.dependency-management' version '1.1.4' id 'org.asciidoctor.jvm.convert' version '3.3.2' -} -plugins { id "org.sonarqube" version "4.4.1.3373" -} - -plugins { id 'jacoco' } - def jacocoDir = layout.buildDirectory.dir("reports/") def QDomains = [] for (qPattern in '*.QA'..'*.QZ') { // qPattern = '*.QA', '*.QB', ... '*.QZ' @@ -92,10 +86,15 @@ dependencies { // Jsoup implementation 'org.jsoup:jsoup:1.16.1' + // h2 + implementation 'com.h2database:h2' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + runtimeOnly 'com.h2database:h2' + implementation 'org.springframework.boot:spring-boot-devtools' + // Pusher java client implementation group: 'com.pusher', name: 'pusher-java-client', version: '2.4.4' - // WebFlux (WebClient) implementation 'org.springframework.boot:spring-boot-starter-webflux' diff --git a/src/main/java/kr/co/morandi/backend/common/config/H2Config.java b/src/main/java/kr/co/morandi/backend/common/config/H2Config.java new file mode 100644 index 00000000..b33d555f --- /dev/null +++ b/src/main/java/kr/co/morandi/backend/common/config/H2Config.java @@ -0,0 +1,35 @@ +package kr.co.morandi.backend.common.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.h2.tools.Server; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; + +@Component +@Profile("test") +@RequiredArgsConstructor +@Slf4j +public class H2Config { + + // '/h2-console' 시 webflux로 인해 localhost:9080으로 접속해서 DB 확인해야 함 + private final H2Properties properties; + private Server webServer; + + @EventListener + public void start(ContextRefreshedEvent event) throws SQLException { + log.info("h2 port : {}", properties.getPort()); + this.webServer = Server.createWebServer("-webPort", properties.getPort(), "-tcpAllowOthers").start(); + } + + @EventListener + public void stop(ContextClosedEvent event){ + this.webServer.stop(); + + } +} diff --git a/src/main/java/kr/co/morandi/backend/common/config/H2Properties.java b/src/main/java/kr/co/morandi/backend/common/config/H2Properties.java new file mode 100644 index 00000000..5fecc3a3 --- /dev/null +++ b/src/main/java/kr/co/morandi/backend/common/config/H2Properties.java @@ -0,0 +1,16 @@ +package kr.co.morandi.backend.common.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Getter +@Setter +@ConfigurationProperties("spring.h2.console") +@Configuration +@Profile("test") +public class H2Properties { + private String port; +} diff --git a/src/main/java/kr/co/morandi/backend/defense_information/domain/model/defense/ProblemTier.java b/src/main/java/kr/co/morandi/backend/defense_information/domain/model/defense/ProblemTier.java index 44d5db17..83213095 100644 --- a/src/main/java/kr/co/morandi/backend/defense_information/domain/model/defense/ProblemTier.java +++ b/src/main/java/kr/co/morandi/backend/defense_information/domain/model/defense/ProblemTier.java @@ -1,5 +1,7 @@ package kr.co.morandi.backend.defense_information.domain.model.defense; +import kr.co.morandi.backend.common.exception.MorandiException; +import kr.co.morandi.backend.problem_information.domain.model.error.ProblemErrorCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -23,7 +25,7 @@ public enum ProblemTier { public static List tierRangeOf(ProblemTier start, ProblemTier end) { if (start.tier > end.tier) - throw new IllegalArgumentException("시작 티어가 끝 티어보다 높을 수 없습니다."); + throw new MorandiException(ProblemErrorCode.PROBLEM_TIER_ERROR); return VALUES.stream() .filter(tier -> tier.tier >= start.tier && tier.tier <= end.tier) diff --git a/src/main/java/kr/co/morandi/backend/defense_information/domain/service/dailydefense/DailyDefenseGenerationService.java b/src/main/java/kr/co/morandi/backend/defense_information/domain/service/dailydefense/DailyDefenseGenerationService.java index da3ad8d5..c7309df7 100644 --- a/src/main/java/kr/co/morandi/backend/defense_information/domain/service/dailydefense/DailyDefenseGenerationService.java +++ b/src/main/java/kr/co/morandi/backend/defense_information/domain/service/dailydefense/DailyDefenseGenerationService.java @@ -23,11 +23,11 @@ public class DailyDefenseGenerationService { private final DailyDefenseProblemPort dailyDefenseProblemPort; private final DailyDefensePort dailyDefensePort; - private static final Map.Entry PROBLEM_1 = getRandomCriteria(1L, B5, B1, 1000L, 300000L); - private static final Map.Entry PROBLEM_2 = getRandomCriteria(2L, S5, S4, 1000L, 300000L); - private static final Map.Entry PROBLEM_3 = getRandomCriteria(3L, S3, S1, 1000L, 300000L); - private static final Map.Entry PROBLEM_4 = getRandomCriteria(4L, G5, G4, 1000L, 300000L); - private static final Map.Entry PROBLEM_5 = getRandomCriteria(5L, G3, G1, 1000L, 300000L); + private static final Map.Entry PROBLEM_1 = getRandomCriteria(1L, B5, B1, 1000L, 30000L); + private static final Map.Entry PROBLEM_2 = getRandomCriteria(2L, S5, S4, 1000L, 30000L); + private static final Map.Entry PROBLEM_3 = getRandomCriteria(3L, S3, S1, 1000L, 30000L); + private static final Map.Entry PROBLEM_4 = getRandomCriteria(4L, G5, G4, 1000L, 30000L); + private static final Map.Entry PROBLEM_5 = getRandomCriteria(5L, G3, G1, 1000L, 30000L); private static final String POSTFIX = "%d월 %d일 오늘의 문제"; @Transactional diff --git a/src/main/java/kr/co/morandi/backend/defense_information/infrastructure/adapter/dailydefense/DailyDefenseProblemAdapter.java b/src/main/java/kr/co/morandi/backend/defense_information/infrastructure/adapter/dailydefense/DailyDefenseProblemAdapter.java index 1e18aef5..b431bb15 100644 --- a/src/main/java/kr/co/morandi/backend/defense_information/infrastructure/adapter/dailydefense/DailyDefenseProblemAdapter.java +++ b/src/main/java/kr/co/morandi/backend/defense_information/infrastructure/adapter/dailydefense/DailyDefenseProblemAdapter.java @@ -12,8 +12,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; +import java.security.SecureRandom; import java.util.List; import java.util.Map; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @Component @@ -27,7 +30,7 @@ public class DailyDefenseProblemAdapter implements DailyDefenseProblemPort { @Override public Map getDailyDefenseProblem(Map criteria) { - Pageable pageable = PageRequest.of(0, 1); + Pageable pageable = PageRequest.of(0, 50); return criteria.entrySet().stream() .map(entry -> { @@ -37,12 +40,13 @@ public Map getDailyDefenseProblem(Map crite final ProblemTier endTier = difficultyRange.getEndDifficulty(); final List dailyDefenseProblems = - problemRepository.getDailyDefenseProblems(ProblemTier.tierRangeOf(startTier, endTier), - randomCriteria.getMinSolvedCount(), - randomCriteria.getMaxSolvedCount(), - pageable); + problemRepository.getDailyDefenseProblems + (ProblemTier.tierRangeOf(startTier, endTier), + randomCriteria.getMinSolvedCount(), + randomCriteria.getMaxSolvedCount(), pageable); - return Map.entry(entry.getKey(), dailyDefenseProblems.get(0)); + int randomNum = new SecureRandom().nextInt(dailyDefenseProblems.size()); + return Map.entry(entry.getKey(), dailyDefenseProblems.get(randomNum)); }) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } diff --git a/src/main/java/kr/co/morandi/backend/problem_information/domain/model/error/ProblemErrorCode.java b/src/main/java/kr/co/morandi/backend/problem_information/domain/model/error/ProblemErrorCode.java new file mode 100644 index 00000000..5da778a0 --- /dev/null +++ b/src/main/java/kr/co/morandi/backend/problem_information/domain/model/error/ProblemErrorCode.java @@ -0,0 +1,23 @@ +package kr.co.morandi.backend.problem_information.domain.model.error; + +import kr.co.morandi.backend.common.exception.errorcode.ErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@RequiredArgsConstructor +public enum ProblemErrorCode implements ErrorCode { + PROBLEM_TIER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "시작 티어가 끝 티어보다 높을 수 없습니다."); + + private final HttpStatus httpStatus; + private final String message; + + @Override + public HttpStatus getHttpStatus() { + return httpStatus; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/src/main/java/kr/co/morandi/backend/problem_information/domain/model/problem/Problem.java b/src/main/java/kr/co/morandi/backend/problem_information/domain/model/problem/Problem.java index 27c8d072..f0a97bea 100644 --- a/src/main/java/kr/co/morandi/backend/problem_information/domain/model/problem/Problem.java +++ b/src/main/java/kr/co/morandi/backend/problem_information/domain/model/problem/Problem.java @@ -8,6 +8,9 @@ import static kr.co.morandi.backend.problem_information.domain.model.problem.ProblemStatus.INIT; @Entity +@Table(name = "problem", indexes = { + @Index(name = "idx_problem_solvedCount_tier_status", columnList = "solvedCount, problemTier, problemStatus") +}) @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Problem extends BaseEntity { diff --git a/src/main/java/kr/co/morandi/backend/problem_information/infrastructure/persistence/problem/ProblemRepository.java b/src/main/java/kr/co/morandi/backend/problem_information/infrastructure/persistence/problem/ProblemRepository.java index 006b61e1..bb915f1f 100644 --- a/src/main/java/kr/co/morandi/backend/problem_information/infrastructure/persistence/problem/ProblemRepository.java +++ b/src/main/java/kr/co/morandi/backend/problem_information/infrastructure/persistence/problem/ProblemRepository.java @@ -14,13 +14,12 @@ public interface ProblemRepository extends JpaRepository { @Query(""" SELECT p FROM Problem p + LEFT JOIN DailyDefenseProblem ddp ON p.problemId = ddp.problem.problemId WHERE p.problemStatus = 'ACTIVE' - AND p.problemTier IN :problemTiers - AND p.solvedCount >= :startSolvedCount - AND p.solvedCount <= :endSolvedCount - AND p NOT IN (SELECT ddp.problem - FROM DailyDefenseProblem ddp) - ORDER BY FUNCTION('RAND') + AND p.problemTier IN :problemTiers + AND p.solvedCount >= :startSolvedCount + AND p.solvedCount <= :endSolvedCount + AND ddp.problem IS NULL """) List getDailyDefenseProblems(List problemTiers, Long startSolvedCount, Long endSolvedCount, Pageable pageable);