From 6b0bfb1cdd430d2f10075a514eb9bf682e481d88 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 12:36:31 +0900 Subject: [PATCH 001/134] =?UTF-8?q?build(gradle):=20dependency=EC=97=90=20?= =?UTF-8?q?JPA=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 3e55fd9..6690aa3 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' From 2c21a416f14c8d5d1beeb26af1daa4d572b0f71f Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:05:16 +0900 Subject: [PATCH 002/134] =?UTF-8?q?ci(gradle):=20ci=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..ed93a81 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,60 @@ +name: Java CI with Gradle + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + + - name: Build with Gradle Wrapper + run: ./gradlew build + + # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). + # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. + # + # - name: Setup Gradle + # uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + # with: + # gradle-version: '8.5' + # + # - name: Build with Gradle 8.5 + # run: gradle build + + dependency-submission: + + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. + # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 From 0cc469844262646ae165519942c9c3dcd9836444 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:18:35 +0900 Subject: [PATCH 003/134] =?UTF-8?q?build(gradle):=20dependency=EC=97=90=20?= =?UTF-8?q?mariadb=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 6690aa3..c0b2557 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' + runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' } tasks.named('test') { From 82c9ad7c27ef68b59426996c46425c0282514079 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:30:43 +0900 Subject: [PATCH 004/134] =?UTF-8?q?build(gradle):=20dependency=EC=97=90=20?= =?UTF-8?q?jar=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index c0b2557..6a3817d 100644 --- a/build.gradle +++ b/build.gradle @@ -36,3 +36,7 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +jar { + enabled = false +} From a40e40872e0db91d0c682fc6fd78919ee345314c Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:31:30 +0900 Subject: [PATCH 005/134] =?UTF-8?q?ci(gradle):=20java=20distribution=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ed93a81..cbefcc2 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -19,7 +19,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: '17' - distribution: 'temurin' + distribution: 'corretto' # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md @@ -52,7 +52,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: '17' - distribution: 'temurin' + distribution: 'corretto' # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md From 8ebe9d68df2c50a3f3521b95eb9534f95984466e Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:36:29 +0900 Subject: [PATCH 006/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index cbefcc2..82f1a80 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -23,22 +23,22 @@ jobs: # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md - - name: Setup Gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - - - name: Build with Gradle Wrapper - run: ./gradlew build +# - name: Setup Gradle +# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 +# +# - name: Build with Gradle Wrapper +# run: ./gradlew build # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. # - # - name: Setup Gradle - # uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - # with: - # gradle-version: '8.5' - # - # - name: Build with Gradle 8.5 - # run: gradle build + - name: Setup Gradle + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + with: + gradle-version: '8.5' + + - name: Build with Gradle 8.5 + run: gradle build dependency-submission: From 6d374237039838849595d105fae493606fab4e8d Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:40:19 +0900 Subject: [PATCH 007/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 82f1a80..af3bd58 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,9 +35,9 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 with: - gradle-version: '8.5' + gradle-version: '8.7' - - name: Build with Gradle 8.5 + - name: Build with Gradle 8.7 run: gradle build dependency-submission: From d88cd019c441bc8581324c601509978046faee70 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 15:52:52 +0900 Subject: [PATCH 008/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index af3bd58..43b628d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -10,35 +10,37 @@ jobs: build: runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'corretto' + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew clean build + - # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. + # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md -# - name: Setup Gradle -# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 +# - name: Setup Gradle +# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 # -# - name: Build with Gradle Wrapper -# run: ./gradlew build +# - name: Build with Gradle Wrapper +# run: ./gradlew build # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. # - - name: Setup Gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - with: - gradle-version: '8.7' - - - name: Build with Gradle 8.7 - run: gradle build +# - name: Setup Gradle +# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 +# with: +# gradle-version: '8.7' +# +# - name: Build with Gradle 8.7 +# run: gradle build dependency-submission: From 592c11f08ec48d51cc20515f93e6e986e036e327 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 16:19:41 +0900 Subject: [PATCH 009/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 43b628d..739ea3a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -34,13 +34,13 @@ jobs: # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. # -# - name: Setup Gradle -# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 -# with: -# gradle-version: '8.7' -# -# - name: Build with Gradle 8.7 -# run: gradle build + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 # v3.1.0 + with: + gradle-version: '8.7' + + - name: Build with Gradle 8.7 + run: gradle build dependency-submission: From ac650c6659f45b0ae4703410f322e6705154ad03 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 16:23:36 +0900 Subject: [PATCH 010/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 739ea3a..1634cc7 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -16,11 +16,11 @@ jobs: with: distribution: 'corretto' java-version: '17' - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Build with Gradle - run: ./gradlew clean build +# - name: Grant execute permission for gradlew +# run: chmod +x gradlew +# +# - name: Build with Gradle +# run: ./gradlew clean build # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. From e3c26c85776ef3a369c1d5c5533a48faea253d9c Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 16:34:41 +0900 Subject: [PATCH 011/134] =?UTF-8?q?test(github=20action):=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/NewsnippetBackApplicationTests.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index 917816e..89b7f3a 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -2,12 +2,17 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.util.Assert; + +import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest class NewsnippetBackApplicationTests { + @Test void contextLoads() { + assertTrue(true); } } From 09851fe206aeec0659f4f9e210b7c61b41da2fe5 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 16:40:47 +0900 Subject: [PATCH 012/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 37 ++++++------------------------------ 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1634cc7..ca42301 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -8,39 +8,14 @@ on: jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: '17' -# - name: Grant execute permission for gradlew -# run: chmod +x gradlew -# -# - name: Build with Gradle -# run: ./gradlew clean build - - - # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. - # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md -# - name: Setup Gradle -# uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 -# -# - name: Build with Gradle Wrapper -# run: ./gradlew build - - # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). - # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. - # - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 # v3.1.0 - with: - gradle-version: '8.7' - - - name: Build with Gradle 8.7 - run: gradle build + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup Gradle + uses: gradle/gradle-build-action@v3 + - name: Build with Gradle + run: ./gradlew build dependency-submission: From 7ee8846bd477bd8732097b565946db2f97844865 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 16:45:49 +0900 Subject: [PATCH 013/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 19 +++++++++++++------ build.gradle | 6 +++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ca42301..fa95e03 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -10,12 +10,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v4 - - name: Setup Gradle - uses: gradle/gradle-build-action@v3 - - name: Build with Gradle - run: ./gradlew build + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' +# + - name: Setup Gradle + uses: gradle/gradle-build-action@v3 # v3.1.0 + with: + gradle-version: '8.7' + + - name: Build with Gradle + run: ./gradlew build dependency-submission: diff --git a/build.gradle b/build.gradle index 6a3817d..8804ae4 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,6 @@ tasks.named('test') { useJUnitPlatform() } -jar { - enabled = false -} +//jar { +// enabled = false +//} From ccc072f9eb863407fa50f45beac4a4aa19430c7d Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:03:28 +0900 Subject: [PATCH 014/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index fa95e03..dbdb2a1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,15 +15,35 @@ jobs: with: distribution: 'corretto' java-version: '17' -# - - name: Setup Gradle - uses: gradle/gradle-build-action@v3 # v3.1.0 - with: - gradle-version: '8.7' + - name: Gradle 명령 실행을 위한 권한을 부여합니다 + run: chmod +x gradlew - - name: Build with Gradle + - name: Gradle build를 수행합니다 run: ./gradlew build + - name: 테스트 결과를 PR에 코멘트로 등록합니다 + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + + - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록합니다 + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + token: ${{ github.token }} + + - name: build 실패 시 Slack으로 알립니다 + uses: Triumers/action-slack@v3 + with: + status: ${{ job.status }} + author_name: 백엔드 빌드 실패 알림 + fields: repo, message, commit, author, action, eventName, ref, workflow, job, took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + if: failure() + dependency-submission: runs-on: ubuntu-latest From f94e00bbb53a9e0060affa09e646ecd3e0ed64ca Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:11:30 +0900 Subject: [PATCH 015/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 43 +++++++++--------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index dbdb2a1..3e49a2b 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -10,39 +10,17 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: '17' - - name: Gradle 명령 실행을 위한 권한을 부여합니다 - run: chmod +x gradlew - - - name: Gradle build를 수행합니다 - run: ./gradlew build - - - name: 테스트 결과를 PR에 코멘트로 등록합니다 - uses: EnricoMi/publish-unit-test-result-action@v1 - if: always() - with: - files: '**/build/test-results/test/TEST-*.xml' + - name: JDK 17을 설치합니다. + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'corretto' - - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록합니다 - uses: mikepenz/action-junit-report@v3 - if: always() - with: - report_paths: '**/build/test-results/test/TEST-*.xml' - token: ${{ github.token }} + - name: Gradle 명령 실행을 위한 권한을 부여합니다. + run: chmod +x gradlew - - name: build 실패 시 Slack으로 알립니다 - uses: Triumers/action-slack@v3 - with: - status: ${{ job.status }} - author_name: 백엔드 빌드 실패 알림 - fields: repo, message, commit, author, action, eventName, ref, workflow, job, took - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - if: failure() + - name: Gradle Build를 수행합니다. + run: ./gradlew build dependency-submission: @@ -52,13 +30,12 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'corretto' - # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. - # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 From ed12582605871a1377aca29c355689c916d4ecbd Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:14:33 +0900 Subject: [PATCH 016/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3e49a2b..e494065 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -17,10 +17,10 @@ jobs: distribution: 'corretto' - name: Gradle 명령 실행을 위한 권한을 부여합니다. - run: chmod +x gradlew + run: chmod +x build.gradle - name: Gradle Build를 수행합니다. - run: ./gradlew build + run: ./build.gradle build dependency-submission: From 8bb6a6d0b759ca2ae6836b92914df77cd9142759 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:22:43 +0900 Subject: [PATCH 017/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e494065..3141c25 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,10 +2,11 @@ name: Java CI with Gradle on: push: - branches: [ "develop" ] + branches: + - develop pull_request: - branches: [ "develop" ] - + branches: + - develop jobs: build: runs-on: ubuntu-latest @@ -16,11 +17,11 @@ jobs: java-version: '17' distribution: 'corretto' - - name: Gradle 명령 실행을 위한 권한을 부여합니다. - run: chmod +x build.gradle + - name: Gradle 명령 실행을 위한 설정을 합니다. + uses: gradle/actions/setup-gradle@v3 - name: Gradle Build를 수행합니다. - run: ./build.gradle build + run: ./gradlew build dependency-submission: From 1d21aa16066e5170a43f1f1f07d9a693a799113e Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:27:16 +0900 Subject: [PATCH 018/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3141c25..70e380a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -20,6 +20,9 @@ jobs: - name: Gradle 명령 실행을 위한 설정을 합니다. uses: gradle/actions/setup-gradle@v3 + - name: Gradle 실행을 위한 권한을 설정합니다. + run: chmod +x gradlew + - name: Gradle Build를 수행합니다. run: ./gradlew build From 3d38ab4411334253fdaf8fca15c2cf6c9374a83a Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:33:59 +0900 Subject: [PATCH 019/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 70e380a..bb985c0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -11,6 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: JDK 17을 설치합니다. uses: actions/setup-java@v4 with: @@ -20,9 +23,6 @@ jobs: - name: Gradle 명령 실행을 위한 설정을 합니다. uses: gradle/actions/setup-gradle@v3 - - name: Gradle 실행을 위한 권한을 설정합니다. - run: chmod +x gradlew - - name: Gradle Build를 수행합니다. run: ./gradlew build From c2e7142a71f066a1239d6430ba56c8db7876109c Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:45:47 +0900 Subject: [PATCH 020/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bb985c0..40bac43 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: JDK 17을 설치합니다. uses: actions/setup-java@v4 @@ -20,11 +20,18 @@ jobs: java-version: '17' distribution: 'corretto' - - name: Gradle 명령 실행을 위한 설정을 합니다. - uses: gradle/actions/setup-gradle@v3 +# - name: Gradle 명령 실행을 위한 설정을 합니다. +# uses: gradle/actions/setup-gradle@v3 +# +# - name: Gradle Build를 수행합니다. +# run: ./gradlew build - - name: Gradle Build를 수행합니다. - run: ./gradlew build + - name: Setup Gradle 8.7 + uses: gradle/actions/setup-gradle@v3 + with: + gradle-version: 8.7 + - name: Build with Gradle 8.7 + run: gradle build dependency-submission: From 91114cdaa31634ffe8050a8fe69093de94f71797 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:50:08 +0900 Subject: [PATCH 021/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +++--- .../newsnippetback/NewsnippetBackApplicationTests.java | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 8804ae4..6a3817d 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,6 @@ tasks.named('test') { useJUnitPlatform() } -//jar { -// enabled = false -//} +jar { + enabled = false +} diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index 89b7f3a..42a0463 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -2,14 +2,12 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.util.Assert; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest class NewsnippetBackApplicationTests { - @Test void contextLoads() { assertTrue(true); From 86002a7f7172656fa16bf055c6cd19490a5c9bdf Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 17:59:23 +0900 Subject: [PATCH 022/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 40bac43..84d2850 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,37 +1,32 @@ + name: Java CI with Gradle on: push: - branches: - - develop + branches: [ "develop" ] pull_request: - branches: - - develop + branches: [ "develop" ] + jobs: build: + runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 + permissions: + contents: read - - name: JDK 17을 설치합니다. + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'corretto' -# - name: Gradle 명령 실행을 위한 설정을 합니다. -# uses: gradle/actions/setup-gradle@v3 -# -# - name: Gradle Build를 수행합니다. -# run: ./gradlew build - - - name: Setup Gradle 8.7 + - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - with: - gradle-version: 8.7 - - name: Build with Gradle 8.7 - run: gradle build + + - name: Build with Gradle Wrapper + run: ./gradlew build dependency-submission: @@ -41,7 +36,6 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 uses: actions/setup-java@v4 with: From 3d9ccc0b3806bc577c06867c5483d064b43d2695 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Wed, 3 Apr 2024 18:01:51 +0900 Subject: [PATCH 023/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/NewsnippetBackApplicationTests.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index 42a0463..b2e5356 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -8,9 +8,4 @@ @SpringBootTest class NewsnippetBackApplicationTests { - @Test - void contextLoads() { - assertTrue(true); - } - } From d9cdeb8a7fd52fff4220d5d45dca2520b8a0c9c6 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Wed, 3 Apr 2024 20:44:14 +0900 Subject: [PATCH 024/134] =?UTF-8?q?build(gradle):=20build.gradle=20depende?= =?UTF-8?q?ncy=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JWT, OAuth, ModelMapper 라이브러리를 추가하고 각 라이브러리를 구분하기 쉽도록 주석이 추가되었습니다. --- build.gradle | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 6a3817d..ea7a43f 100644 --- a/build.gradle +++ b/build.gradle @@ -22,15 +22,32 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - compileOnly 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // 편의성 라이브러리 developmentOnly 'org.springframework.boot:spring-boot-devtools' + + // Lombok + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // SpringSecurity 라이브러리 + implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // DB 관련 라이브러리 runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // ModelMapper + implementation 'org.modelmapper:modelmapper:3.2.0' } tasks.named('test') { From cce0c1b91d5b11517867c2504405ca959fa99566 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Wed, 3 Apr 2024 20:45:52 +0900 Subject: [PATCH 025/134] =?UTF-8?q?feat(Entity):=20=EA=B0=81=20Entity=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DB에 맞는 엔티티를 생성했습니다. 엔티티는 DB와 같은 구조이므로 추후 개발의 편의성을 위해 미리 추가하게 되었습니다. --- .../domain/aggregate/entity/Category.java | 16 +++++ .../domain/aggregate/entity/CrawlingQuiz.java | 46 +++++++++++++++ .../domain/aggregate/entity/Quiz.java | 59 +++++++++++++++++++ .../domain/aggregate/entity/Solved.java | 27 +++++++++ .../domain/aggregate/entity/User.java | 49 +++++++++++++++ .../domain/aggregate/enums/Provider.java | 9 +++ .../domain/aggregate/enums/UserRole.java | 7 +++ .../domain/aggregate/enums/UserStatus.java | 8 +++ 8 files changed, 221 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/Provider.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserStatus.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java new file mode 100644 index 0000000..0c1bd1e --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -0,0 +1,16 @@ +package org.triumers.newsnippetback.domain.aggregate.entity; + +import jakarta.persistence.*; + +@Entity +@Table(name = "tbl_category") +public class Category { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "CATEGORY_NAME", nullable = false, unique = true) + private String categoryName; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java new file mode 100644 index 0000000..01e6560 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -0,0 +1,46 @@ +package org.triumers.newsnippetback.domain.aggregate.entity; + +import jakarta.persistence.*; + +import java.time.LocalDate; + +@Entity +@Table(name = "tbl_crawling_quiz") +public class CrawlingQuiz { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "CONTENT", nullable = false) + private String content; + + @Column(name = "OPTION_A", nullable = false) + private String optionA; + + @Column(name = "OPTION_B", nullable = false) + private String optionB; + + @Column(name = "OPTION_C", nullable = false) + private String optionC; + + @Column(name = "OPTION_D", nullable = false) + private String optionD; + + @Column(name = "ANSWER", nullable = false) + private String answer; + + @Column(name = "EXPLANATION") + private String explanation; + + @Column(name = "NEWS_LINK") + private String newsLink; + + @Column(name = "NEWS_DATE", nullable = false) + private LocalDate newsDate; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "CATEGORY_ID") + private Category category; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java new file mode 100644 index 0000000..3049cd3 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -0,0 +1,59 @@ +package org.triumers.newsnippetback.domain.aggregate.entity; + +import jakarta.persistence.*; + +import java.time.LocalDate; + +@Entity +@Table(name = "tbl_quiz") +public class Quiz { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "DATE") + private LocalDate date; + + @Column(name = "NO") + private int no; + + @Column(name = "CONTENT", nullable = false) + private String content; + + @Column(name = "OPTION_A", nullable = false) + private String optionA; + + @Column(name = "OPTION_B", nullable = false) + private String optionB; + + @Column(name = "OPTION_C", nullable = false) + private String optionC; + + @Column(name = "OPTION_D", nullable = false) + private String optionD; + + @Column(name = "ANSWER", nullable = false) + private String answer; + + @Column(name = "EXPLANATION") + private String explanation; + + @Column(name = "NEWS_LINK", nullable = false) + private String newsLink; + + @Column(name = "SOLVED_CNT", nullable = false) + private int solvedCnt; + + @Column(name = "CORRECT_CNT", nullable = false) + private int correctCnt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "CATEGORY_ID") + private Category category; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "ORIGIN_QUIZ_ID") + private CrawlingQuiz originQuiz; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java new file mode 100644 index 0000000..99cfbc1 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -0,0 +1,27 @@ +package org.triumers.newsnippetback.domain.aggregate.entity; + +import jakarta.persistence.*; + +@Entity +@Table(name = "tbl_solved") +public class Solved { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "IS_CORRECT", nullable = false) + private boolean isCorrect; + + @Column(name = "SELECTED_OPTION") + private String selectedOption; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "USER_ID") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "QUIZ_ID") + private Quiz quiz; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java new file mode 100644 index 0000000..1fa57d1 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java @@ -0,0 +1,49 @@ +package org.triumers.newsnippetback.domain.aggregate.entity; + +import jakarta.persistence.*; +import org.triumers.newsnippetback.domain.aggregate.enums.Provider; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; + +@Entity +@Table(name = "tbl_user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "NAME") + private String name; + + @Column(name = "NICKNAME", nullable = false, unique = true) + private String nickname; + + @Column(name = "EMAIL", unique = true) + private String email; + + @Column(name = "PASSWORD") + private String password; + + @Column(name = "SOLVED_CNT", nullable = false) + private int solvedCnt; + + @Column(name = "CORRECT_CNT", nullable = false) + private int correctCnt; + + @Column(name = "USER_ROLE", nullable = false) + private UserRole userRole; + + @Column(name = "PROVIDER", nullable = false) + private Provider provider; + + @Column(name = "SNS_ID", unique = true) + private String snsId; + + @Column(name = "USER_STATUS", nullable = false) + private UserStatus userStatus; + + @Column(name = "LOGIN_FAILED_CNT", nullable = false) + private int loginFailedCnt; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/Provider.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/Provider.java new file mode 100644 index 0000000..c757e7e --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/Provider.java @@ -0,0 +1,9 @@ +package org.triumers.newsnippetback.domain.aggregate.enums; + +public enum Provider { + LOCAL, + KAKAO, + GOOGLE, + NAVER, + GITHUB +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java new file mode 100644 index 0000000..d433924 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.domain.aggregate.enums; + +public enum UserRole { + ROLE_USER, + ROLE_MANAGER, + ROLE_ADMIN +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserStatus.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserStatus.java new file mode 100644 index 0000000..c3137ab --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserStatus.java @@ -0,0 +1,8 @@ +package org.triumers.newsnippetback.domain.aggregate.enums; + +public enum UserStatus { + ACTIVE, // 활성화된 계정 + DELETED, // 탈퇴한 계정 + DORMANT, // 휴면 계정 + BLOCKED // 차단된 계정 +} From 673d645bc04b74d13a1e13e28f0aa204d49349e4 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 4 Apr 2024 11:11:31 +0900 Subject: [PATCH 026/134] =?UTF-8?q?refactor(Entity):=20=EA=B0=81=20Entity?= =?UTF-8?q?=20FK=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JPA의 N+1 문제 예방을 위해 각 관계를 끊었습니다. 실제 DB에서는 관계가 맺어져 있으므로 부모 테이블을 먼저 조회한 뒤 id를 추출하여 다시 자식 테이블에 요청하는 방식으로 구현할 예정입니다. 추후 변경 시 다시 기재하겠습니다. --- .../domain/aggregate/entity/CrawlingQuiz.java | 5 ++--- .../newsnippetback/domain/aggregate/entity/Quiz.java | 10 ++++------ .../newsnippetback/domain/aggregate/entity/Solved.java | 10 ++++------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java index 01e6560..98450fd 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -40,7 +40,6 @@ public class CrawlingQuiz { @Column(name = "NEWS_DATE", nullable = false) private LocalDate newsDate; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "CATEGORY_ID") - private Category category; + @Column(name = "CATEGORY_ID") + private int categoryId; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index 3049cd3..c87cd93 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -49,11 +49,9 @@ public class Quiz { @Column(name = "CORRECT_CNT", nullable = false) private int correctCnt; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "CATEGORY_ID") - private Category category; + @Column(name = "CATEGORY_ID") + private int categoryId; - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "ORIGIN_QUIZ_ID") - private CrawlingQuiz originQuiz; + @Column(name = "ORIGIN_QUIZ_ID") + private int originQuizId; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index 99cfbc1..d6875f1 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -17,11 +17,9 @@ public class Solved { @Column(name = "SELECTED_OPTION") private String selectedOption; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "USER_ID") - private User user; + @Column(name = "USER_ID") + private int userId; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "QUIZ_ID") - private Quiz quiz; + @Column(name = "QUIZ_ID") + private int quizId; } From 19602d17529d29bb68894b9f2d3890bb5261eeb7 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 11:15:01 +0900 Subject: [PATCH 027/134] =?UTF-8?q?feat(DTO)=20:=20QuizDTO=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/domain/dto/QuizDTO.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java new file mode 100644 index 0000000..664fa1f --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -0,0 +1,39 @@ +package org.triumers.newsnippetback.domain.dto; + +import jakarta.persistence.*; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; + +import java.time.LocalDate; + +public class QuizDTO { + private int id; + + private LocalDate date; + + private int no; + + private String content; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + + private int solvedCnt; + + private int correctCnt; + + private Category category; + + private int originQuizId; +} From 669c59cf99c4a04c80de9af4823c096e531324f3 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 11:16:45 +0900 Subject: [PATCH 028/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 12 +++++++++++- .../NewsnippetBackApplicationTests.java | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 84d2850..e8d98f8 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -25,8 +25,18 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Build with Gradle Wrapper + - name: Build with Gradle run: ./gradlew build + env: + DB_URL: ${{ secrets.DB_URL }} + DB_USER_NAME: ${{ secrets.DB_USER_NAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} +# MAIL_PORT: ${{ secrets.MAIL_PORT }} +# MAIL_USER_NAME: ${{ secrets.MAIL_USER_NAME }} +# MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} +# JWT_KEY: ${{ secrets.JWT_KEY }} +# REDIS_HOST: ${{ secrets.REDIS_HOST }} +# REDIS_PORT: ${{ secrets.REDIS_PORT }} dependency-submission: diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index b2e5356..af0eae8 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -8,4 +8,8 @@ @SpringBootTest class NewsnippetBackApplicationTests { + @Test + void testCode(){ + assertTrue(true); + } } From 11e7cec1752f00a04393036a66207b04e18be850 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 11:50:45 +0900 Subject: [PATCH 029/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=9E=84=EC=8B=9C?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 10 ---------- .../newsnippetback/NewsnippetBackApplicationTests.java | 9 +++++++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e8d98f8..b5bc085 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -27,16 +27,6 @@ jobs: - name: Build with Gradle run: ./gradlew build - env: - DB_URL: ${{ secrets.DB_URL }} - DB_USER_NAME: ${{ secrets.DB_USER_NAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} -# MAIL_PORT: ${{ secrets.MAIL_PORT }} -# MAIL_USER_NAME: ${{ secrets.MAIL_USER_NAME }} -# MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }} -# JWT_KEY: ${{ secrets.JWT_KEY }} -# REDIS_HOST: ${{ secrets.REDIS_HOST }} -# REDIS_PORT: ${{ secrets.REDIS_PORT }} dependency-submission: diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index af0eae8..b1a0918 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -1,5 +1,4 @@ package org.triumers.newsnippetback; - import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -9,7 +8,13 @@ class NewsnippetBackApplicationTests { @Test - void testCode(){ + void githubActionTestSuccess(){ assertTrue(true); } + + @Test + void githubActionTestFail(){ + assertTrue(false); + } + } From 2fbc43e6a6be6db917483a21b04ca02b22dfb5d1 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 11:56:13 +0900 Subject: [PATCH 030/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b5bc085..ddeb927 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -36,11 +36,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'corretto' + java-version: '22' +# distribution: 'corretto' + cache: gradle - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 From ac8bc943e303b7b20fb3bc6f787cec887f02407a Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 11:57:46 +0900 Subject: [PATCH 031/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ddeb927..ea45a3a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -16,11 +16,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'corretto' + java-version: '22' +# distribution: 'corretto' + cache: gradle - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 From fc11ed09092132eee78df91d85b9cdc9861ec2e0 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 11:59:05 +0900 Subject: [PATCH 032/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ea45a3a..e5ffbf3 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -20,7 +20,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: '22' -# distribution: 'corretto' + distribution: 'corretto' cache: gradle - name: Setup Gradle @@ -41,7 +41,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: '22' -# distribution: 'corretto' + distribution: 'corretto' cache: gradle - name: Generate and submit dependency graph From bbaab5caa7bcee0cc9d4f66652ecb2120ce010b9 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 12:01:08 +0900 Subject: [PATCH 033/134] =?UTF-8?q?ci(gradle):=20java=20=EB=B2=84=EC=A0=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e5ffbf3..ea7dd5d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -19,7 +19,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: '22' + java-version: '17' distribution: 'corretto' cache: gradle @@ -40,7 +40,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: '22' + java-version: '17' distribution: 'corretto' cache: gradle From 976ce6eaaad78d9210fd9c8cd01976c6a2365c12 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 12:38:07 +0900 Subject: [PATCH 034/134] =?UTF-8?q?feat(manager)=20:=20=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=EB=A7=81=EB=AC=B8=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EB=82=A0=EC=A7=9C=EB=A1=9C=20=EC=9D=BC=EC=B9=98?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8(4/2=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EB=A1=9C=20=EC=A1=B0=ED=9A=8C,=20=EC=B6=94=ED=9B=84=20Paramete?= =?UTF-8?q?rizedTest=EB=A1=9C=20=EB=B3=80=EA=B2=BD)=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EB=90=9C=20=EB=AC=B8=EC=A0=9C=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8(=EC=B6=94=ED=9B=84=20ParameterizedTest=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 61 +++++++++++++++++++ .../NewsnippetBackApplication.java | 6 ++ .../domain/aggregate/entity/Category.java | 2 + .../domain/aggregate/entity/CrawlingQuiz.java | 2 + .../domain/dto/CrawlingQuizDTO.java | 32 ++++++++++ .../newsnippetback/domain/dto/QuizDTO.java | 1 - .../domain/repository/CategoryRepository.java | 7 +++ .../repository/CrawlingQuizRepository.java | 12 ++++ .../domain/repository/QuizRepository.java | 7 +++ .../service/ManageServiceTest.java | 34 +++++++++++ 10 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java new file mode 100644 index 0000000..f31775e --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -0,0 +1,61 @@ +package org.triumers.newsnippetback.Application.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class ManageService { + + QuizRepository quizRepository; + CrawlingQuizRepository crawlingQuizRepository; + CategoryRepository categoryRepository; + ModelMapper mapper; + + @Autowired + public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, + CategoryRepository categoryRepository, ModelMapper mapper) { + this.quizRepository = quizRepository; + this.crawlingQuizRepository = crawlingQuizRepository; + this.categoryRepository = categoryRepository; + this.mapper = mapper; + } + + public List selectCrawlingQuizListByDate(LocalDate date){ + + List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); + + List crawlingQuizDTOList = crawlingQuizList.stream() + .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < crawlingQuizList.size(); i++) { + Category category = categoryRepository.findById(crawlingQuizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTOList.get(i).setCategory(category); + } + + return crawlingQuizDTOList; + } + + public CrawlingQuizDTO selectCrawlingQuizByID(int id) { + CrawlingQuiz crawlingQuiz = crawlingQuizRepository.findById(id).orElseThrow(IllegalAccessError::new); + CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); + + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTO.setCategory(category); + + return crawlingQuizDTO; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java index d6e1ac3..5f9d4f7 100644 --- a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java +++ b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback; +import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class NewsnippetBackApplication { @@ -10,4 +12,8 @@ public static void main(String[] args) { SpringApplication.run(NewsnippetBackApplication.class, args); } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 0c1bd1e..fedfad7 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -1,9 +1,11 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; @Entity @Table(name = "tbl_category") +@Data public class Category { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java index 98450fd..a6bf8d2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -1,11 +1,13 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; import java.time.LocalDate; @Entity @Table(name = "tbl_crawling_quiz") +@Data public class CrawlingQuiz { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java new file mode 100644 index 0000000..b931882 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java @@ -0,0 +1,32 @@ +package org.triumers.newsnippetback.domain.dto; + +import jakarta.persistence.Column; +import lombok.Data; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.time.LocalDate; + +@Data +public class CrawlingQuizDTO { + private int id; + + private String content; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + + private LocalDate newsDate; + + private Category category; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 664fa1f..dbdfb75 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -2,7 +2,6 @@ import jakarta.persistence.*; import org.triumers.newsnippetback.domain.aggregate.entity.Category; -import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import java.time.LocalDate; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java new file mode 100644 index 0000000..81d1c1a --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +public interface CategoryRepository extends JpaRepository { +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java new file mode 100644 index 0000000..6316308 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java @@ -0,0 +1,12 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; + +import java.time.LocalDate; +import java.util.List; + +public interface CrawlingQuizRepository extends JpaRepository { + List findByNewsDate(LocalDate date); +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java new file mode 100644 index 0000000..2678f1f --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; + +public interface QuizRepository extends JpaRepository { +} diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java new file mode 100644 index 0000000..f16c1db --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -0,0 +1,34 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; + +import java.time.LocalDate; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class ManageServiceTest { + + ManageService manageService; + + @Autowired + public ManageServiceTest(ManageService manageService) { + this.manageService = manageService; + } + + @Test + void selectCrawlingQuizListByDate(){ + List quizDTOList = manageService.selectCrawlingQuizListByDate(LocalDate.of(2024,4,2)); + assertNotNull(quizDTOList); + } + + @Test + void selectCrawlingQuizById(){ + CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(1); + } + +} \ No newline at end of file From 694f5eb3a9bdb90ac968331a1ed7b87a1fba383a Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 13:28:07 +0900 Subject: [PATCH 035/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 5 +++++ .../newsnippetback/NewsnippetBackApplicationTests.java | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ea7dd5d..bb84d64 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,6 +28,11 @@ jobs: - name: Build with Gradle run: ./gradlew build + env: + DB_URL: ${{ secrets.DB_URL }} + DB_USER_NAME: ${{ secrets.DB_USER_NAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + dependency-submission: diff --git a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java index b1a0918..182e58c 100644 --- a/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java +++ b/src/test/java/org/triumers/newsnippetback/NewsnippetBackApplicationTests.java @@ -12,9 +12,5 @@ void githubActionTestSuccess(){ assertTrue(true); } - @Test - void githubActionTestFail(){ - assertTrue(false); - } } From 0e01f1beed76ccd278be22595cbfbfa12e596a68 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 13:31:11 +0900 Subject: [PATCH 036/134] =?UTF-8?q?ci(gradle):=20gradle=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bb84d64..cdd6bd0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -21,7 +21,6 @@ jobs: with: java-version: '17' distribution: 'corretto' - cache: gradle - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 @@ -29,9 +28,9 @@ jobs: - name: Build with Gradle run: ./gradlew build env: - DB_URL: ${{ secrets.DB_URL }} - DB_USER_NAME: ${{ secrets.DB_USER_NAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + DB_URL: DB URL = ${{ secrets.DB_URL }} + DB_USER_NAME: USER = ${{ secrets.DB_USER_NAME }} + DB_PASSWORD: PSW = ${{ secrets.DB_PASSWORD }} dependency-submission: From 9f5a966d7184ce3683f072f3ed04e7f97d5b45a1 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 14:10:47 +0900 Subject: [PATCH 037/134] =?UTF-8?q?build(gradle):=20gradle=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle b/build.gradle index ea7a43f..30bb43c 100644 --- a/build.gradle +++ b/build.gradle @@ -50,10 +50,6 @@ dependencies { implementation 'org.modelmapper:modelmapper:3.2.0' } -tasks.named('test') { - useJUnitPlatform() -} - jar { enabled = false } From 20653ee7f7cf72cf259f6aa8f7e81731c0315e45 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 14:17:45 +0900 Subject: [PATCH 038/134] =?UTF-8?q?chore(gitignore):=20yml=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c2065bc..e284392 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + +### YMl ### +.yml/ From 7b2c33abdd50cf642d3eee7a4f671ab2bf0356bc Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 14:32:34 +0900 Subject: [PATCH 039/134] =?UTF-8?q?feat(Quiz)=20:=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=A0=80=EC=9E=A5=20=EC=B6=9C=EC=A0=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=EB=90=9C=20=EB=AC=B8=EC=A0=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20Quiz=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20=ED=9B=84=20DB=EC=97=90=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=8A=94=20=EC=B6=94=ED=9B=84=20ParameterizedTest?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 24 +++++++++++++++++++ .../domain/aggregate/entity/Quiz.java | 2 ++ .../newsnippetback/domain/dto/QuizDTO.java | 2 ++ .../service/ManageServiceTest.java | 12 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index f31775e..739e6c2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -1,11 +1,15 @@ package org.triumers.newsnippetback.Application.service; +import jakarta.transaction.Transactional; import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.domain.aggregate.entity.Category; import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; @@ -58,4 +62,24 @@ public CrawlingQuizDTO selectCrawlingQuizByID(int id) { return crawlingQuizDTO; } + + @Transactional + public List insertSelectedQuiz(List crawlingQuizDTOList) { + + mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + List quizList = crawlingQuizDTOList.stream() + .map(crawlingQuizDTO -> mapper.map(crawlingQuizDTO, Quiz.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < crawlingQuizDTOList.size(); i++) { + + CrawlingQuizDTO seletedQuiz = crawlingQuizDTOList.get(i); + + quizList.get(i).setNo(i+1); + quizList.get(i).setDate(LocalDate.now()); + quizList.get(i).setCategoryId(seletedQuiz.getCategory().getId()); + quizList.get(i).setOriginQuizId(seletedQuiz.getId()); + } + return quizRepository.saveAll(quizList); + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index c87cd93..58fceac 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,11 +1,13 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; import java.time.LocalDate; @Entity @Table(name = "tbl_quiz") +@Data public class Quiz { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index dbdfb75..71367df 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -1,10 +1,12 @@ package org.triumers.newsnippetback.domain.dto; import jakarta.persistence.*; +import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; import java.time.LocalDate; +@Data public class QuizDTO { private int id; diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index f16c1db..d6bfd99 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; import java.time.LocalDate; @@ -23,12 +24,23 @@ public ManageServiceTest(ManageService manageService) { @Test void selectCrawlingQuizListByDate(){ List quizDTOList = manageService.selectCrawlingQuizListByDate(LocalDate.of(2024,4,2)); + assertNotNull(quizDTOList); } @Test void selectCrawlingQuizById(){ CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(1); + + assertNotNull(quizDTO); + } + + @Test + void addSelectedQuiz(){ + List selectedCrawlingQuizList = manageService.selectCrawlingQuizListByDate(LocalDate.of(2024,4,2)); + List savedQuizList = manageService.insertSelectedQuiz(selectedCrawlingQuizList); + + assertNotNull(savedQuizList); } } \ No newline at end of file From 80b8e5d1f57187a62b1099721dfd90ad77acaec9 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 14:39:54 +0900 Subject: [PATCH 040/134] =?UTF-8?q?fix(Quiz)=20:=20=EC=B6=9C=EC=A0=9C=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=88=98=EC=A0=95=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=EB=90=A0=20=EB=82=A0=EC=A7=9C=20=EB=8B=B9=EC=9D=BC->=EB=8B=A4?= =?UTF-8?q?=EC=9D=8C=EB=82=A0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/ManageService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 739e6c2..a6e3dd5 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -57,7 +57,7 @@ public CrawlingQuizDTO selectCrawlingQuizByID(int id) { CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) - .orElseThrow(IllegalAccessError::new); + .orElseThrow(IllegalAccessError::new); crawlingQuizDTO.setCategory(category); return crawlingQuizDTO; @@ -76,7 +76,7 @@ public List insertSelectedQuiz(List crawlingQuizDTOList) CrawlingQuizDTO seletedQuiz = crawlingQuizDTOList.get(i); quizList.get(i).setNo(i+1); - quizList.get(i).setDate(LocalDate.now()); + quizList.get(i).setDate(LocalDate.now().plusDays(1)); quizList.get(i).setCategoryId(seletedQuiz.getCategory().getId()); quizList.get(i).setOriginQuizId(seletedQuiz.getId()); } From 80658170b2a687c8de056edb619dde06498c45e3 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 14:52:20 +0900 Subject: [PATCH 041/134] =?UTF-8?q?feat(Quiz)=20:=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=ED=95=A0=20=EB=AC=B8=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=ED=95=A0=20=EB=AC=B8=EC=A0=9C=20Quiz=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=97=90=EC=84=9C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 16 ++++++++++++++++ .../domain/repository/QuizRepository.java | 4 ++++ .../Application/service/ManageServiceTest.java | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index a6e3dd5..bffc75d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -82,4 +82,20 @@ public List insertSelectedQuiz(List crawlingQuizDTOList) } return quizRepository.saveAll(quizList); } + + public List selectQuizListByDate(LocalDate date) { + List quizList = quizRepository.findByDateOrderByNoAsc(date); + + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + quizDTOList.get(i).setCategory(category); + } + + return quizDTOList; + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 2678f1f..b5e4574 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -3,5 +3,9 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import java.time.LocalDate; +import java.util.List; + public interface QuizRepository extends JpaRepository { + List findByDateOrderByNoAsc(LocalDate date); } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index d6bfd99..af70fdd 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -5,6 +5,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; import java.util.List; @@ -43,4 +44,10 @@ void addSelectedQuiz(){ assertNotNull(savedQuizList); } + @Test + void selectQuizListByDate(){ + List savedQuizList = manageService.selectQuizListByDate(LocalDate.of(2024,4,5)); + + } + } \ No newline at end of file From 204569df438bf036166bf3eff3fa8e55fe7016d5 Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 15:32:51 +0900 Subject: [PATCH 042/134] =?UTF-8?q?feat(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 68 +++++++++++++++++++ .../Application/service/QuizService.java | 7 ++ .../Application/service/QuizServiceImpl.java | 59 ++++++++++++++++ .../NewsnippetBackApplication.java | 7 ++ .../domain/aggregate/vo/QuizRequest.java | 15 ++++ .../domain/aggregate/vo/QuizResponse.java | 21 ++++++ .../newsnippetback/domain/dto/QuizDTO.java | 3 + .../domain/repository/CategoryRepository.java | 12 ++++ .../domain/repository/QuizRepository.java | 13 ++++ 9 files changed, 205 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java new file mode 100644 index 0000000..7512ad4 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -0,0 +1,68 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.QuizService; + +@RestController +@RequestMapping("/quiz") +public class QuizController { + + private final QuizService quizService; + + private final ModelMapper modelMapper; + + @Autowired + public QuizController(QuizService quizService, ModelMapper modelMapper) { + this.quizService = quizService; + this.modelMapper = modelMapper; + } + + /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 + ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + +// @PostMapping("/test") +// public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); +// System.out.println("quizDTO = " + quizDTO); +// QuizResponse quizResponse = modelMapper.map(quizDTO, QuizResponse.class); +// System.out.println("quizResponse = " + quizResponse); +// +// // 정답률 계산 및 설정 +// double correctRate = calculateCorrectRate(quizDTO.getCorrectCnt(), quizDTO.getSolvedCnt()); +// System.out.println("correctRate = " + correctRate); +// quizResponse.setCorrectRate(correctRate); +// System.out.println("correctRate = " + correctRate); +// +// return ResponseEntity.ok().body(quizResponse); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } + + // 정답률 계산 메서드 + private double calculateCorrectRate(int correctCnt, int solvedCnt) { + if (solvedCnt == 0) { + return 0.0; + } + return (double) correctCnt / solvedCnt * 100; + } + + + + + + /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 + ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + + + /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ + + /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 + ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java new file mode 100644 index 0000000..3913fbf --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.Application.service; + +public interface QuizService { + +// QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java new file mode 100644 index 0000000..d877e2d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -0,0 +1,59 @@ +package org.triumers.newsnippetback.Application.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; + +@Service +public class QuizServiceImpl implements QuizService { + + private final QuizRepository quizRepository; + + private final CategoryRepository categoryRepository; + private final ModelMapper modelMapper; + + @Autowired + public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + this.quizRepository = quizRepository; + this.categoryRepository = categoryRepository; + this.modelMapper = modelMapper; + } + + // 1 +// @Override +// public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// +// return quizRepository.findByDateAndNo(date, no) +// .map(quiz -> { +// QuizDTO quizDTO = modelMapper.map(quiz, QuizDTO.class); +// +// // 카테고리 ID를 사용하여 카테고리 이름 조회 +// int categoryId = quiz.getCategoryId(); +// System.out.println("categoryId = " + categoryId); +// +// Optional optionalCategory = categoryRepository.findById(categoryId); +// System.out.println("optionalCategory = " + optionalCategory); +// +// Category category = optionalCategory.orElseThrow(() -> new NoSuchElementException("Category not found for id: " + categoryId)); +// System.out.println("category = " + category); +// +// // 정답률 계산 +// double correctRate = (double) quiz.getCorrectCnt() / quiz.getSolvedCnt() * 100; +// System.out.println("correctRate = " + correctRate); +// +// // QuizDTO에 카테고리 이름과 정답률 설정 +// quizDTO.setCategoryName(category.getCategoryName()); +// quizDTO.setCorrectRate(correctRate); +// +// System.out.println("correctRate = " + correctRate); +// +// return quizDTO; +// }) +// .orElseThrow(() -> new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no)); +// } + +} diff --git a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java index d6e1ac3..6317e25 100644 --- a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java +++ b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback; +import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class NewsnippetBackApplication { @@ -10,4 +12,9 @@ public static void main(String[] args) { SpringApplication.run(NewsnippetBackApplication.class, args); } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java new file mode 100644 index 0000000..4f17eaf --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -0,0 +1,15 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class QuizRequest { + private LocalDate date; + private int no; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java new file mode 100644 index 0000000..32488d6 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java @@ -0,0 +1,21 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class QuizResponse { + + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private int solvedCnt; + private int correctCnt; + private String categoryName; + private double correctRate; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 664fa1f..6379871 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -36,4 +36,7 @@ public class QuizDTO { private Category category; private int originQuizId; + + private String categoryName; + private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java new file mode 100644 index 0000000..ac5e2fc --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -0,0 +1,12 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.util.Optional; + +@Repository +public interface CategoryRepository extends JpaRepository { + Optional findById(int categoryId); +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java new file mode 100644 index 0000000..e249d7b --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; + +import java.time.LocalDate; +import java.util.Optional; + +@Repository +public interface QuizRepository extends JpaRepository { + Optional findByDateAndNo(LocalDate date, int no); +} From e839a1b66b8cf313553e94b059bf60d6c8f57ac4 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 15:42:24 +0900 Subject: [PATCH 043/134] =?UTF-8?q?feat(Quiz)=20:=20=EC=B6=9C=EC=A0=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=82=AD=EC=A0=9C=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=EB=90=A0=20=EB=AC=B8=EC=A0=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=B6=9C=EC=A0=9C=EB=90=A0=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 19 +++++++++++++++++++ .../domain/repository/QuizRepository.java | 4 ++++ .../service/ManageServiceTest.java | 9 ++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index bffc75d..2689996 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -98,4 +98,23 @@ public List selectQuizListByDate(LocalDate date) { return quizDTOList; } + + @Transactional + public QuizDTO deleteQuizInListById(int id) { + Quiz deleteQuiz = quizRepository.findById(id).orElseThrow(IllegalAccessError::new); + + if (deleteQuiz != null) { + + quizRepository.deleteById(id); + List modifyQuizList = quizRepository + .findByDateAndNoGreaterThanOrderByNoAsc(LocalDate.now().plusDays(1), deleteQuiz.getNo()); + + for (Quiz modifyQuiz : modifyQuizList) { + modifyQuiz.setNo(modifyQuiz.getNo() - 1); + } + + return mapper.map(deleteQuiz, QuizDTO.class); + } + return null; + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index b5e4574..f73ed4e 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -8,4 +8,8 @@ public interface QuizRepository extends JpaRepository { List findByDateOrderByNoAsc(LocalDate date); + + List findByDateAndNoGreaterThan(LocalDate localDate, int no); + + List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index af70fdd..ed221bf 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -46,8 +46,15 @@ void addSelectedQuiz(){ @Test void selectQuizListByDate(){ - List savedQuizList = manageService.selectQuizListByDate(LocalDate.of(2024,4,5)); + List savedQuizList = manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); + assertNotNull(savedQuizList); + } + @Test + void deleteQuizInList(){ + QuizDTO quizDTO = manageService.deleteQuizInListById(1); + assertNotNull(quizDTO); } + } \ No newline at end of file From ecb2c61c32ac9c812ba7ba2bc99f0de4b9de6641 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 16:09:43 +0900 Subject: [PATCH 044/134] =?UTF-8?q?style(Quiz)=20:=20max=EC=9D=B8=EB=8D=B1?= =?UTF-8?q?=EC=8A=A4=EB=A1=9C=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=B6=9C=EC=A0=9C=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20max?= =?UTF-8?q?=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EA=B0=92=20=EC=B0=BE=EC=95=84?= =?UTF-8?q?=EC=84=9C=20=EC=88=98=EC=A0=95=EB=8F=84=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 20 +++++++++++-------- .../domain/repository/QuizRepository.java | 4 ++++ .../service/ManageServiceTest.java | 5 +++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 2689996..a8b3e9f 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -35,7 +35,7 @@ public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawl this.mapper = mapper; } - public List selectCrawlingQuizListByDate(LocalDate date){ + public List selectCrawlingQuizListByDate(LocalDate date) { List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); @@ -45,7 +45,7 @@ public List selectCrawlingQuizListByDate(LocalDate date){ for (int i = 0; i < crawlingQuizList.size(); i++) { Category category = categoryRepository.findById(crawlingQuizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); + .orElseThrow(IllegalAccessError::new); crawlingQuizDTOList.get(i).setCategory(category); } @@ -57,7 +57,7 @@ public CrawlingQuizDTO selectCrawlingQuizByID(int id) { CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) - .orElseThrow(IllegalAccessError::new); + .orElseThrow(IllegalAccessError::new); crawlingQuizDTO.setCategory(category); return crawlingQuizDTO; @@ -71,18 +71,24 @@ public List insertSelectedQuiz(List crawlingQuizDTOList) .map(crawlingQuizDTO -> mapper.map(crawlingQuizDTO, Quiz.class)) .collect(Collectors.toList()); + int index = getMaxIndex() + 1; for (int i = 0; i < crawlingQuizDTOList.size(); i++) { - + CrawlingQuizDTO seletedQuiz = crawlingQuizDTOList.get(i); - - quizList.get(i).setNo(i+1); + + quizList.get(i).setNo(index++); quizList.get(i).setDate(LocalDate.now().plusDays(1)); quizList.get(i).setCategoryId(seletedQuiz.getCategory().getId()); quizList.get(i).setOriginQuizId(seletedQuiz.getId()); } + return quizRepository.saveAll(quizList); } + public int getMaxIndex() { + return quizRepository.countByDate(LocalDate.now().plusDays(1)); + } + public List selectQuizListByDate(LocalDate date) { List quizList = quizRepository.findByDateOrderByNoAsc(date); @@ -95,7 +101,6 @@ public List selectQuizListByDate(LocalDate date) { .orElseThrow(IllegalAccessError::new); quizDTOList.get(i).setCategory(category); } - return quizDTOList; } @@ -112,7 +117,6 @@ public QuizDTO deleteQuizInListById(int id) { for (Quiz modifyQuiz : modifyQuizList) { modifyQuiz.setNo(modifyQuiz.getNo() - 1); } - return mapper.map(deleteQuiz, QuizDTO.class); } return null; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index f73ed4e..506a0d9 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -1,6 +1,8 @@ package org.triumers.newsnippetback.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; @@ -12,4 +14,6 @@ public interface QuizRepository extends JpaRepository { List findByDateAndNoGreaterThan(LocalDate localDate, int no); List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); + + Integer countByDate(LocalDate localDate); } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index ed221bf..123dca2 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -56,5 +56,10 @@ void deleteQuizInList(){ assertNotNull(quizDTO); } + @Test + void getMaxIndex(){ + int result = manageService.getMaxIndex(); + } + } \ No newline at end of file From 05fad53a5b1d18b6bb12d8310e50aabc43044166 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 17:04:26 +0900 Subject: [PATCH 045/134] =?UTF-8?q?feat(controller)=20:=20manage=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80=20manage=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B4=80=EB=A0=A8=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ManageController.java | 60 +++++++++++++++++++ .../Application/service/ManageService.java | 8 +-- .../service/ManageServiceTest.java | 6 -- 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java new file mode 100644 index 0000000..9de5c9a --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -0,0 +1,60 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.triumers.newsnippetback.Application.service.ManageService; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/manage") +public class ManageController { + + private final ManageService manageService; + + @Autowired + public ManageController(ManageService manageService) { + this.manageService = manageService; + } + + @PostMapping("/findCrawlingQuiz") + public List findCrawlingQuizList(@RequestBody Map params) { + return manageService.selectCrawlingQuizListByDate(LocalDate.parse(params.get("date"), + DateTimeFormatter.ISO_DATE)); + } + + @GetMapping("/findCrawlingQuiz/{id}") + public CrawlingQuizDTO findCrawlingQuizById(@PathVariable int id){ + return manageService.selectCrawlingQuizByID(id); + } + + @PostMapping("/addQuiz") + public ResponseEntity> addQuizInList(@RequestBody List crawlingQuizDTOList){ + + List savedQuizList = manageService.insertSelectedQuiz(crawlingQuizDTOList); + + return ResponseEntity.status(HttpStatus.OK).body(savedQuizList); + } + + @GetMapping("findSelectedQuiz") + public List findSelectedQuizList(){ + return manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); + } + + @DeleteMapping("deleteQuiz/{id}") + public ResponseEntity deleteQuizInList(@PathVariable int id){ + + QuizDTO deletedQuiz = manageService.deleteQuizInListById(id); + + return ResponseEntity.status(HttpStatus.OK).body(deletedQuiz); + } + +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index a8b3e9f..a01f25a 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -21,10 +21,10 @@ @Service public class ManageService { - QuizRepository quizRepository; - CrawlingQuizRepository crawlingQuizRepository; - CategoryRepository categoryRepository; - ModelMapper mapper; + private final QuizRepository quizRepository; + private final CrawlingQuizRepository crawlingQuizRepository; + private final CategoryRepository categoryRepository; + private final ModelMapper mapper; @Autowired public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index 123dca2..3e722f0 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -56,10 +56,4 @@ void deleteQuizInList(){ assertNotNull(quizDTO); } - @Test - void getMaxIndex(){ - int result = manageService.getMaxIndex(); - } - - } \ No newline at end of file From 6f269910d13cc82f919b3f8c70e663c66be3b4b3 Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 17:31:55 +0900 Subject: [PATCH 046/134] =?UTF-8?q?refactor(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +- .../controller/QuizController.java | 64 +++++++------- .../Application/service/QuizService.java | 8 +- .../Application/service/QuizServiceImpl.java | 84 +++++++++++-------- .../domain/aggregate/entity/Category.java | 2 + .../domain/aggregate/entity/Quiz.java | 2 + .../domain/aggregate/vo/QuizRequest.java | 31 +++++++ .../domain/aggregate/vo/QuizResponse.java | 26 ++++++ .../newsnippetback/domain/dto/QuizDTO.java | 4 +- .../domain/repository/CategoryRepository.java | 2 +- .../domain/repository/QuizRepository.java | 5 +- 11 files changed, 160 insertions(+), 74 deletions(-) diff --git a/build.gradle b/build.gradle index ea7a43f..2bc2abb 100644 --- a/build.gradle +++ b/build.gradle @@ -33,9 +33,9 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' // SpringSecurity 라이브러리 - implementation 'org.springframework.boot:spring-boot-starter-security' - testImplementation 'org.springframework.security:spring-security-test' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' +// implementation 'org.springframework.boot:spring-boot-starter-security' +// testImplementation 'org.springframework.security:spring-security-test' +// implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' // JWT implementation 'io.jsonwebtoken:jjwt-api:0.11.5' diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 7512ad4..cdb4f83 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -2,9 +2,18 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; import org.triumers.newsnippetback.Application.service.QuizService; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -22,47 +31,36 @@ public QuizController(QuizService quizService, ModelMapper modelMapper) { /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ - -// @PostMapping("/test") -// public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { -// try { -// QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); -// System.out.println("quizDTO = " + quizDTO); -// QuizResponse quizResponse = modelMapper.map(quizDTO, QuizResponse.class); -// System.out.println("quizResponse = " + quizResponse); -// -// // 정답률 계산 및 설정 -// double correctRate = calculateCorrectRate(quizDTO.getCorrectCnt(), quizDTO.getSolvedCnt()); -// System.out.println("correctRate = " + correctRate); -// quizResponse.setCorrectRate(correctRate); -// System.out.println("correctRate = " + correctRate); -// -// return ResponseEntity.ok().body(quizResponse); -// } catch (NoSuchElementException e) { -// return ResponseEntity.notFound().build(); -// } catch (Exception e) { -// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); -// } -// } - - // 정답률 계산 메서드 - private double calculateCorrectRate(int correctCnt, int solvedCnt) { - if (solvedCnt == 0) { - return 0.0; + @PostMapping("/test") + public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { + try { + QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); + + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + + return ResponseEntity.ok().body(quizResponse); + } catch (NoSuchElementException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } - return (double) correctCnt / solvedCnt * 100; } - - - /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ + /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 3913fbf..d4459b4 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,7 +1,13 @@ package org.triumers.newsnippetback.Application.service; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.List; + public interface QuizService { -// QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index d877e2d..a53714b 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -3,9 +3,16 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; +import java.time.LocalDate; +import java.util.NoSuchElementException; + @Service public class QuizServiceImpl implements QuizService { @@ -22,38 +29,49 @@ public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categor } // 1 -// @Override -// public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { -// LocalDate date = quizRequest.getDate(); -// int no = quizRequest.getNo(); -// -// return quizRepository.findByDateAndNo(date, no) -// .map(quiz -> { -// QuizDTO quizDTO = modelMapper.map(quiz, QuizDTO.class); -// -// // 카테고리 ID를 사용하여 카테고리 이름 조회 -// int categoryId = quiz.getCategoryId(); -// System.out.println("categoryId = " + categoryId); -// -// Optional optionalCategory = categoryRepository.findById(categoryId); -// System.out.println("optionalCategory = " + optionalCategory); -// -// Category category = optionalCategory.orElseThrow(() -> new NoSuchElementException("Category not found for id: " + categoryId)); -// System.out.println("category = " + category); -// -// // 정답률 계산 -// double correctRate = (double) quiz.getCorrectCnt() / quiz.getSolvedCnt() * 100; -// System.out.println("correctRate = " + correctRate); -// -// // QuizDTO에 카테고리 이름과 정답률 설정 -// quizDTO.setCategoryName(category.getCategoryName()); -// quizDTO.setCorrectRate(correctRate); -// -// System.out.println("correctRate = " + correctRate); -// -// return quizDTO; -// }) -// .orElseThrow(() -> new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no)); -// } + @Override + public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + + Quiz quiz = quizRepository.findByDateAndNo(date, no); + + if (quiz == null) { + throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + } + + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + + // 카테고리 ID를 사용하여 카테고리 이름 조회 + int categoryId = quiz.getCategoryId(); + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); + } + quizDTO.setCategoryName(category.getCategoryName()); + + // 정답률 계산 + double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); + quizDTO.setCorrectRate(correctRate); + + return quizDTO; + } + + // 정답률 계산 메서드 + private double calculateCorrectRate(int correctCnt, int solvedCnt) { + if (solvedCnt == 0) { + return 0.0; + } + return (double) correctCnt / solvedCnt * 100; + } + + + + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 0c1bd1e..6153844 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; +@Data @Entity @Table(name = "tbl_category") public class Category { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index c87cd93..d20bb8b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,9 +1,11 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; import java.time.LocalDate; +@Data @Entity @Table(name = "tbl_quiz") public class Quiz { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java index 4f17eaf..e0e6063 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.domain.aggregate.vo; import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; import java.time.LocalDate; @@ -10,6 +11,36 @@ @Setter @ToString public class QuizRequest { + private int id; + private LocalDate date; + private int no; + + private String content; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + + private int solvedCnt; + + private int correctCnt; + + private Category category; + + private int originQuizId; + + private String categoryName; + private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java index 32488d6..b24070b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java @@ -1,6 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.vo; import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.time.LocalDate; @NoArgsConstructor @AllArgsConstructor @@ -9,13 +12,36 @@ @ToString public class QuizResponse { + private int id; + + private LocalDate date; + + private int no; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + private int solvedCnt; + private int correctCnt; + + private Category category; + + private int originQuizId; + private String categoryName; private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 6379871..a2bdc67 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -1,11 +1,11 @@ package org.triumers.newsnippetback.domain.dto; -import jakarta.persistence.*; +import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; -import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import java.time.LocalDate; +@Data public class QuizDTO { private int id; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java index ac5e2fc..bb8efb7 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -8,5 +8,5 @@ @Repository public interface CategoryRepository extends JpaRepository { - Optional findById(int categoryId); + Category findById(int categoryId); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index e249d7b..f095b1b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -5,9 +5,12 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; +import java.util.List; import java.util.Optional; @Repository public interface QuizRepository extends JpaRepository { - Optional findByDateAndNo(LocalDate date, int no); + Quiz findByDateAndNo(LocalDate date, int no); + + List findQuizById(int id); } From e525fefa334166b065bef28ca127e2972c38c47b Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 17:54:55 +0900 Subject: [PATCH 047/134] =?UTF-8?q?feat(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=A0=95=EB=8B=B5,=20=ED=95=B4?= =?UTF-8?q?=EC=84=A4,=20=EC=9B=90=EB=B3=B8=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 19 ++++++++++++++++++ .../Application/service/QuizService.java | 2 ++ .../Application/service/QuizServiceImpl.java | 20 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index cdb4f83..8bcf3c6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -11,6 +11,7 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; @@ -56,6 +57,24 @@ public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + @PostMapping("/answer") + public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { + try { + QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); + + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setAnswer(quizDTO.getAnswer()); + quizResponse.setExplanation(quizDTO.getExplanation()); + quizResponse.setNewsLink(quizDTO.getNewsLink()); + + return ResponseEntity.ok().body(quizResponse); + } catch (NoSuchElementException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index d4459b4..a4b2f27 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; @@ -10,4 +11,5 @@ public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index a53714b..9755ae8 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -71,6 +71,26 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { } + // 2 + @Override + public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + + Quiz quiz = quizRepository.findByDateAndNo(date, no); + + if (quiz == null) { + throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + } + + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setAnswer(quiz.getAnswer()); + quizDTO.setExplanation(quiz.getExplanation()); + quizDTO.setNewsLink(quiz.getNewsLink()); + + return quizDTO; + } + From 50561a9a0cb49c4fecc784f246a75a3dfb8cdc52 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Thu, 4 Apr 2024 17:57:08 +0900 Subject: [PATCH 048/134] =?UTF-8?q?ci(jira):=20jira=20CI=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/jira.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/jira.yml diff --git a/.github/workflows/jira.yml b/.github/workflows/jira.yml new file mode 100644 index 0000000..f77c632 --- /dev/null +++ b/.github/workflows/jira.yml @@ -0,0 +1,24 @@ +name: Create Jira issue +on: + issues: + types: [opened, closed, edited] + +jobs: + create-issue: + name: Create Jira issue + runs-on: ubuntu-latest + steps: + - name: Login + uses: atlassian/gajira-login@v3 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + + - name: Create Issue + uses: atlassian/gajira-create@v3 + with: + project: NEWS # 프로젝트 key + issuetype: Task # 이슈 타입 + summary: '${{ github.event.issue.title }}' + description: '${{ github.event.issue.html_url }}' \ No newline at end of file From aea0afeb0d8864e4116643fc2b79af23c56db22c Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 4 Apr 2024 18:01:34 +0900 Subject: [PATCH 049/134] =?UTF-8?q?test(managerService)=20:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20param=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EC=97=90=20ParameterizedTest=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 58 +++++++++++-------- .../domain/repository/QuizRepository.java | 2 - .../service/ManageServiceTest.java | 38 ++++++++---- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index a01f25a..71ec15f 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -16,6 +16,7 @@ import java.time.LocalDate; import java.util.List; +import java.util.NoSuchElementException; import java.util.stream.Collectors; @Service @@ -39,28 +40,36 @@ public List selectCrawlingQuizListByDate(LocalDate date) { List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); - List crawlingQuizDTOList = crawlingQuizList.stream() - .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) - .collect(Collectors.toList()); + if (crawlingQuizList != null) { + List crawlingQuizDTOList = crawlingQuizList.stream() + .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) + .collect(Collectors.toList()); - for (int i = 0; i < crawlingQuizList.size(); i++) { - Category category = categoryRepository.findById(crawlingQuizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); - crawlingQuizDTOList.get(i).setCategory(category); + for (int i = 0; i < crawlingQuizList.size(); i++) { + Category category = categoryRepository.findById(crawlingQuizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTOList.get(i).setCategory(category); + } + return crawlingQuizDTOList; + } else { + // 이후에 크롤링 서버에 문제 생성 요청하기 + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); } - - return crawlingQuizDTOList; } public CrawlingQuizDTO selectCrawlingQuizByID(int id) { CrawlingQuiz crawlingQuiz = crawlingQuizRepository.findById(id).orElseThrow(IllegalAccessError::new); - CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); - Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) - .orElseThrow(IllegalAccessError::new); - crawlingQuizDTO.setCategory(category); + if (crawlingQuiz != null) { + CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); + + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTO.setCategory(category); - return crawlingQuizDTO; + return crawlingQuizDTO; + } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); } @Transactional @@ -92,16 +101,19 @@ public int getMaxIndex() { public List selectQuizListByDate(LocalDate date) { List quizList = quizRepository.findByDateOrderByNoAsc(date); - List quizDTOList = quizList.stream() - .map(quiz -> mapper.map(quiz, QuizDTO.class)) - .collect(Collectors.toList()); + if (quizList != null) { + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); - for (int i = 0; i < quizList.size(); i++) { - Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); - quizDTOList.get(i).setCategory(category); + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + quizDTOList.get(i).setCategory(category); + } + return quizDTOList; } - return quizDTOList; + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); } @Transactional @@ -119,6 +131,6 @@ public QuizDTO deleteQuizInListById(int id) { } return mapper.map(deleteQuiz, QuizDTO.class); } - return null; + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 506a0d9..b98001d 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -11,8 +11,6 @@ public interface QuizRepository extends JpaRepository { List findByDateOrderByNoAsc(LocalDate date); - List findByDateAndNoGreaterThan(LocalDate localDate, int no); - List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); Integer countByDate(LocalDate localDate); diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index 3e722f0..7b8fa65 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -1,6 +1,9 @@ package org.triumers.newsnippetback.Application.service; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; @@ -8,7 +11,10 @@ import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; +import java.util.Date; import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; @@ -22,16 +28,28 @@ public ManageServiceTest(ManageService manageService) { this.manageService = manageService; } - @Test - void selectCrawlingQuizListByDate(){ - List quizDTOList = manageService.selectCrawlingQuizListByDate(LocalDate.of(2024,4,2)); + static Stream getDate() { + return Stream.of( + LocalDate.of(2024, 4, 1), + LocalDate.of(2024, 4, 2), + LocalDate.of(2024, 4, 3), + LocalDate.of(2024, 4, 4), + LocalDate.of(2024, 4, 5) + ); + } + + @ParameterizedTest + @MethodSource("getDate") + void selectCrawlingQuizListByDate(LocalDate date){ + List quizDTOList = manageService.selectCrawlingQuizListByDate(date); assertNotNull(quizDTOList); } - @Test - void selectCrawlingQuizById(){ - CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(1); + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5}) + void selectCrawlingQuizById(int id){ + CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(id); assertNotNull(quizDTO); } @@ -50,10 +68,10 @@ void selectQuizListByDate(){ assertNotNull(savedQuizList); } - @Test - void deleteQuizInList(){ - QuizDTO quizDTO = manageService.deleteQuizInListById(1); + @ParameterizedTest + @ValueSource(ints = {6, 7, 8}) + void deleteQuizInListSuccess(int id){ + QuizDTO quizDTO = manageService.deleteQuizInListById(id); assertNotNull(quizDTO); } - } \ No newline at end of file From b7542a2310e4fab8f02fdb7f1a3efc5afa90c45d Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 18:21:19 +0900 Subject: [PATCH 050/134] =?UTF-8?q?feat(Quiz):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 - 2. 해당 문제의 정답, 해설, 원본 링크 조회 --- .../domain/aggregate/vo/QuizRequest.java | 5 + .../service/QuizServiceImplTests.java | 154 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java index e0e6063..279e8e0 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -43,4 +43,9 @@ public class QuizRequest { private String categoryName; private double correctRate; + + public QuizRequest(LocalDate date, int no) { + this.date = date; + this.no = no; + } } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java new file mode 100644 index 0000000..8d0fa1d --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java @@ -0,0 +1,154 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.NoSuchElementException; + +@SpringBootTest +public class QuizServiceImplTests { + + @Autowired + private QuizServiceImpl quizServiceImpl; + + @Autowired + private PlatformTransactionManager transactionManager; + + @AfterEach + void rollback() { + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + transactionManager.rollback(status); + } + + @DisplayName("퀴즈 조회 - 첫 번째 문제") + @Test + void findQuizByDateAndNo_FirstQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 1; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", quizDTO.getContent()); + Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", quizDTO.getOptionA()); + Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", quizDTO.getOptionB()); + Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", quizDTO.getOptionC()); + Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", quizDTO.getOptionD()); + Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); + Assertions.assertEquals(70.0, quizDTO.getCorrectRate()); + } + + @DisplayName("퀴즈 조회 - 두 번째 문제") + @Test + void findQuizByDateAndNo_SecondQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 2; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("중국 전자상거래 기업 알리바바가 1시간 이내에 전 세계로 상품을 배송하는 시도에 나선다고 하는데, 이를 위해 협업하는 로켓 개발 스타트업은?", quizDTO.getContent()); + Assertions.assertEquals("스페이스 엑스 (Space X)", quizDTO.getOptionA()); + Assertions.assertEquals("스페이스 에포크 (Space Epoch)", quizDTO.getOptionB()); + Assertions.assertEquals("블루 오리진 (Blue Origin)", quizDTO.getOptionC()); + Assertions.assertEquals("로켓랩 (Rocket Lab)", quizDTO.getOptionD()); + Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); + Assertions.assertEquals(80.0, quizDTO.getCorrectRate()); + } + + @DisplayName("존재하지 않는 퀴즈 조회") + @Test + void findNonExistingQuiz() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 999; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizByDateAndNo(quizRequest); + }); + } + + @DisplayName("존재하지 않는 카테고리의 퀴즈 조회") + @Test + void findQuizWithNonExistingCategory() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 3; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizByDateAndNo(quizRequest); + }); + } + + @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") + @Test + void findQuizAnswerByDateAndNo_FirstQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 1; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("C", quizDTO.getAnswer()); + Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); + } + + @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") + @Test + void findQuizAnswerByDateAndNo_SecondQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 2; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("B", quizDTO.getAnswer()); + Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); + } + + @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") + @Test + void findAnswerOfNonExistingQuiz() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 999; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + }); + } +} From 36d07756fa9bb9fa4971954dc0e5c5789a69e862 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 5 Apr 2024 09:05:31 +0900 Subject: [PATCH 051/134] =?UTF-8?q?test(Manage)=20:=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 8 ++-- .../service/ManageServiceTest.java | 41 ++++++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 71ec15f..4a76d75 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -40,7 +40,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); - if (crawlingQuizList != null) { + if (!crawlingQuizList.isEmpty()) { List crawlingQuizDTOList = crawlingQuizList.stream() .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) .collect(Collectors.toList()); @@ -53,7 +53,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { return crawlingQuizDTOList; } else { // 이후에 크롤링 서버에 문제 생성 요청하기 - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); } } @@ -101,7 +101,7 @@ public int getMaxIndex() { public List selectQuizListByDate(LocalDate date) { List quizList = quizRepository.findByDateOrderByNoAsc(date); - if (quizList != null) { + if (!quizList.isEmpty()) { List quizDTOList = quizList.stream() .map(quiz -> mapper.map(quiz, QuizDTO.class)) .collect(Collectors.toList()); @@ -113,7 +113,7 @@ public List selectQuizListByDate(LocalDate date) { } return quizDTOList; } - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); } @Transactional diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index 7b8fa65..593aa06 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -47,7 +47,7 @@ void selectCrawlingQuizListByDate(LocalDate date){ } @ParameterizedTest - @ValueSource(ints = {1, 2, 3, 4, 5}) + @ValueSource(ints = {11, 12, 13}) void selectCrawlingQuizById(int id){ CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(id); @@ -69,9 +69,46 @@ void selectQuizListByDate(){ } @ParameterizedTest - @ValueSource(ints = {6, 7, 8}) + @ValueSource(ints = {13, 14, 15}) void deleteQuizInListSuccess(int id){ QuizDTO quizDTO = manageService.deleteQuizInListById(id); assertNotNull(quizDTO); } + + @Test + void deleteQuizInListException(){ + int id = -1; + + assertThrows(IllegalAccessError.class, () -> { + manageService.deleteQuizInListById(id); + } ); + } + + @Test + void selectCrawlingQuizByIdException(){ + int id = -1; + + assertThrows(IllegalAccessError.class, () -> { + manageService.selectCrawlingQuizByID(id); + } ); + } + + @Test + void selectCrawlingQuizListByDateException(){ + LocalDate date = LocalDate.of(2020, 1, 1); + + assertThrows(NoSuchElementException.class, () -> { + manageService.selectCrawlingQuizListByDate(date); + } ); + } + + @Test + void selectQuizListByDateException(){ + LocalDate date = LocalDate.of(2020, 1, 1); + + assertThrows(NoSuchElementException.class, () -> { + manageService.selectQuizListByDate(date); + } ); + } + } \ No newline at end of file From b9f921e821706dd705ac207c05c3fd81ec1460a7 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 5 Apr 2024 10:41:57 +0900 Subject: [PATCH 052/134] =?UTF-8?q?feat(CrawlingQuiz)=20:=20=EC=B6=9C?= =?UTF-8?q?=EC=A0=9C,=20=EB=AF=B8=EC=B6=9C=EC=A0=9C=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EC=8B=9C,=20=ED=99=94=EB=A9=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=99=95=EC=9D=B8=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EC=88=98=20=EB=B0=8F=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/ManageService.java | 7 ++++++- .../newsnippetback/domain/dto/CrawlingQuizDTO.java | 2 ++ .../newsnippetback/domain/repository/QuizRepository.java | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 4a76d75..6cb9099 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -46,9 +46,14 @@ public List selectCrawlingQuizListByDate(LocalDate date) { .collect(Collectors.toList()); for (int i = 0; i < crawlingQuizList.size(); i++) { - Category category = categoryRepository.findById(crawlingQuizList.get(i).getCategoryId()) + CrawlingQuiz crawlingQuiz = crawlingQuizList.get(i); + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) .orElseThrow(IllegalAccessError::new); crawlingQuizDTOList.get(i).setCategory(category); + + boolean isSelected = quizRepository.countByDateAndOriginQuizId + (LocalDate.now().plusDays(1), crawlingQuiz.getId()) != 0; + crawlingQuizDTOList.get(i).setSelected(isSelected); } return crawlingQuizDTOList; } else { diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java index b931882..d4d8e77 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java @@ -29,4 +29,6 @@ public class CrawlingQuizDTO { private LocalDate newsDate; private Category category; + + private boolean isSelected; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index b98001d..e95f525 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -14,4 +14,6 @@ public interface QuizRepository extends JpaRepository { List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); Integer countByDate(LocalDate localDate); + + Integer countByDateAndOriginQuizId(LocalDate localDate, int id); } From 9921dbe4abe88b02a5521dcc641610bcf8714e9e Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 5 Apr 2024 12:25:36 +0900 Subject: [PATCH 053/134] =?UTF-8?q?style(Quiz)=20:=20isSelected=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/ManageService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 6cb9099..3f6350c 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -52,7 +52,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { crawlingQuizDTOList.get(i).setCategory(category); boolean isSelected = quizRepository.countByDateAndOriginQuizId - (LocalDate.now().plusDays(1), crawlingQuiz.getId()) != 0; + (LocalDate.now().plusDays(1), crawlingQuiz.getId()) > 0; crawlingQuizDTOList.get(i).setSelected(isSelected); } return crawlingQuizDTOList; From 2a0be9392e95c43be1eb50bf2b0af40f9842c5b8 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 13:56:56 +0900 Subject: [PATCH 054/134] =?UTF-8?q?feat(Solved):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 정답 여부, 회원이 입력한 답 추가 - 과거에 풀었던 문제 조회 --- .../controller/SolvedController.java | 72 ++++++++++++++ .../Application/service/SolvedService.java | 10 ++ .../service/SolvedServiceImpl.java | 99 +++++++++++++++++++ .../domain/aggregate/entity/Solved.java | 2 + .../aggregate/vo/SolvedIsCorrectResponse.java | 14 +++ .../aggregate/vo/SolvedQuizResponse.java | 25 +++++ .../domain/aggregate/vo/SolvedRequest.java | 13 +++ .../newsnippetback/domain/dto/SolvedDTO.java | 25 +++++ .../domain/repository/QuizRepository.java | 7 +- .../domain/repository/SolvedRepository.java | 10 ++ .../service/SolvedServiceImplTest.java | 75 ++++++++++++++ 11 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java new file mode 100644 index 0000000..471f99d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -0,0 +1,72 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.SolvedService; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedQuizResponse; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedIsCorrectResponse; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +import java.util.NoSuchElementException; + +@RestController +@RequestMapping("/solved") +public class SolvedController { + private final SolvedService solvedService; + + @Autowired + public SolvedController(SolvedService solvedService) { + this.solvedService = solvedService; + } + + /* 설명. 1. 사용자가 입력한 답과 문제의 정답 여부 판단 */ + @PostMapping("/check") + public ResponseEntity findSelectedOptionAndCompareAnswer(@RequestBody SolvedRequest solvedRequest) { + try { + SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); + SolvedIsCorrectResponse solvedIsCorrectResponse = new SolvedIsCorrectResponse(); + solvedIsCorrectResponse.setUserId(solvedDTO.getUserId()); + solvedIsCorrectResponse.setQuizId(solvedDTO.getQuizId()); + solvedIsCorrectResponse.setCorrect(solvedDTO.isCorrect()); + + return ResponseEntity.ok().body(solvedIsCorrectResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /* 설명. 2. 사용자가 과거에 풀었던 문제 조회 */ + @PostMapping("/find") + public ResponseEntity findSolvedQuizByUserID(@RequestBody SolvedRequest solvedRequest){ + try { + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + SolvedQuizResponse solvedQuizResponse = new SolvedQuizResponse(); + solvedQuizResponse.setUserId(solvedDTO.getUserId()); + solvedQuizResponse.setQuizId(solvedDTO.getQuizId()); + solvedQuizResponse.setCategoryId(solvedDTO.getCategoryId()); + solvedQuizResponse.setContent(solvedDTO.getContent()); + solvedQuizResponse.setOptionA(solvedDTO.getOptionA()); + solvedQuizResponse.setOptionB(solvedDTO.getOptionB()); + solvedQuizResponse.setOptionC(solvedDTO.getOptionC()); + solvedQuizResponse.setOptionD(solvedDTO.getOptionD()); + solvedQuizResponse.setAnswer(solvedDTO.getAnswer()); + solvedQuizResponse.setExplanation(solvedDTO.getExplanation()); + solvedQuizResponse.setNewsLink(solvedDTO.getNewsLink()); + solvedQuizResponse.setDate(solvedDTO.getDate()); + + return ResponseEntity.ok().body(solvedQuizResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java new file mode 100644 index 0000000..a795c5a --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -0,0 +1,10 @@ +package org.triumers.newsnippetback.Application.service; + +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +public interface SolvedService { + SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest); + + SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest); +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java new file mode 100644 index 0000000..c70976f --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -0,0 +1,99 @@ +package org.triumers.newsnippetback.Application.service; + +import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; +import org.triumers.newsnippetback.domain.repository.SolvedRepository; + +import java.util.NoSuchElementException; +import java.util.Objects; + +@Service +public class SolvedServiceImpl implements SolvedService{ + + private final SolvedRepository solvedRepository; + private final QuizRepository quizRepository; + private final CategoryRepository categoryRepository; + private final ModelMapper modelMapper; + + @Autowired + public SolvedServiceImpl(SolvedRepository solvedRepository, QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + this.solvedRepository = solvedRepository; + this.quizRepository = quizRepository; + this.categoryRepository = categoryRepository; + this.modelMapper = modelMapper; + } + + @Override + public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) { + + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + int userId = solvedRequest.getUserId(); + int quizId = solvedRequest.getQuizId(); + + Solved solved = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); + Quiz answer = quizRepository.findAnswerById(quizId); + + if (answer == null || solved == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); + } + + SolvedDTO solvedDTO = new SolvedDTO(); + solvedDTO.setUserId(userId); + solvedDTO.setQuizId(quizId); + + if (Objects.equals(answer.getAnswer(), solved.getSelectedOption())) { + solvedDTO.setCorrect(true); + solved.setCorrect(true); + solvedRepository.save(solved); + + return solvedDTO; + } + + solvedDTO.setCorrect(false); + solved.setCorrect(false); + solvedRepository.save(solved); + + return solvedDTO; + } + + @Override + public SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest) { + int userId = solvedRequest.getUserId(); + int quizId = solvedRequest.getQuizId(); + + Solved solvedQuiz = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); + Quiz answer = quizRepository.findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(quizId); + Category category = categoryRepository.findById(answer.getCategoryId()); + + if (answer == null || solvedQuiz == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); + } + + SolvedDTO solvedDTO = new SolvedDTO(); + solvedDTO.setUserId(solvedQuiz.getUserId()); + solvedDTO.setQuizId(solvedQuiz.getQuizId()); + solvedDTO.setCategoryId(category.getId()); + solvedDTO.setContent(answer.getContent()); + solvedDTO.setOptionA(answer.getOptionA()); + solvedDTO.setOptionB(answer.getOptionB()); + solvedDTO.setOptionC(answer.getOptionC()); + solvedDTO.setOptionD(answer.getOptionD()); + solvedDTO.setAnswer(answer.getAnswer()); + solvedDTO.setSelectedOption(solvedQuiz.getSelectedOption()); + solvedDTO.setExplanation(answer.getExplanation()); + solvedDTO.setNewsLink(answer.getNewsLink()); + solvedDTO.setDate(answer.getDate()); + + return solvedDTO; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index d6875f1..95f520c 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; +@Data @Entity @Table(name = "tbl_solved") public class Solved { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java new file mode 100644 index 0000000..c9ad656 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java @@ -0,0 +1,14 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedIsCorrectResponse { + private boolean isCorrect; + private int userId; + private int quizId; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java new file mode 100644 index 0000000..8bffcdc --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java @@ -0,0 +1,25 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedQuizResponse { + private int userId; + private int quizId; + private int categoryId; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private String answer; + private String explanation; + private String newsLink; + private LocalDate date; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java new file mode 100644 index 0000000..e2ba736 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedRequest { + private int userId; + private int quizId; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java new file mode 100644 index 0000000..29027da --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java @@ -0,0 +1,25 @@ +package org.triumers.newsnippetback.domain.dto; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class SolvedDTO { + private int id; + private boolean isCorrect; + private String selectedOption; + private int userId; + private int quizId; + private int categoryId; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private String answer; + private String explanation; + private String newsLink; + private LocalDate date; + +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index f095b1b..8266fc2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -5,12 +5,11 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - @Repository public interface QuizRepository extends JpaRepository { Quiz findByDateAndNo(LocalDate date, int no); - List findQuizById(int id); + Quiz findAnswerById(int quizId); + + Quiz findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(int quizId); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java new file mode 100644 index 0000000..0811f64 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -0,0 +1,10 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; + +@Repository +public interface SolvedRepository extends JpaRepository { + Solved findSelectedOptionByUserIdAndQuizId(int userId, int quizId); +} diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java new file mode 100644 index 0000000..e47cd8b --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -0,0 +1,75 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +import java.time.LocalDate; + +@SpringBootTest +class SolvedServiceImplTest { + @Autowired + private SolvedService solvedService; + + @Autowired + private PlatformTransactionManager transactionManager; + + @AfterEach + void rollback() { + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + transactionManager.rollback(status); + } + + @DisplayName("사용자가 입력한 답과 정답 비교하여 판정") + @Test + void findSelectedOptionAndCompareAnswer() { + // Given + int userId = 1; + int quizId = 1; + SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + + // When + SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); + + // Then + Assertions.assertNotNull(solvedDTO); + Assertions.assertEquals(1,solvedDTO.getUserId()); + Assertions.assertEquals(1,solvedDTO.getQuizId()); + Assertions.assertEquals(true, solvedDTO.isCorrect()); + } + + @DisplayName("사용자가 풀었던 문제 조회") + @Test + void findSolvedQuizByUserID() { + // Given + int userId = 1; + int quizId = 1; + SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + + // When + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + + // Then + Assertions.assertNotNull(solvedDTO); + Assertions.assertEquals(1, solvedDTO.getUserId()); + Assertions.assertEquals(1, solvedDTO.getQuizId()); + Assertions.assertEquals(9, solvedDTO.getCategoryId()); + Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", solvedDTO.getContent()); + Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", solvedDTO.getOptionA()); + Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", solvedDTO.getOptionB()); + Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", solvedDTO.getOptionC()); + Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", solvedDTO.getOptionD()); + Assertions.assertEquals("C",solvedDTO.getAnswer()); + Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", solvedDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", solvedDTO.getNewsLink()); + Assertions.assertEquals(LocalDate.of(2024, 4, 2), solvedDTO.getDate()); + } +} \ No newline at end of file From 70c66302a993ecf36b10f85da13ec60a65845ec5 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 13:58:40 +0900 Subject: [PATCH 055/134] =?UTF-8?q?style(Quiz):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/controller/QuizController.java | 18 +----------------- .../Application/service/QuizService.java | 4 ---- .../Application/service/QuizServiceImpl.java | 4 +--- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 8bcf3c6..6e13f71 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -1,20 +1,15 @@ package org.triumers.newsnippetback.Application.controller; -import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.triumers.newsnippetback.Application.service.QuizService; -import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; -import java.time.LocalDate; -import java.util.List; import java.util.NoSuchElementException; -import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -22,12 +17,9 @@ public class QuizController { private final QuizService quizService; - private final ModelMapper modelMapper; - @Autowired - public QuizController(QuizService quizService, ModelMapper modelMapper) { + public QuizController(QuizService quizService) { this.quizService = quizService; - this.modelMapper = modelMapper; } /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 @@ -74,12 +66,4 @@ public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizR return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } - - - - /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ - - - /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 - ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index a4b2f27..83f1369 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,12 +1,8 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; -import java.time.LocalDate; -import java.util.List; - public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 9755ae8..cba1c36 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -19,13 +19,11 @@ public class QuizServiceImpl implements QuizService { private final QuizRepository quizRepository; private final CategoryRepository categoryRepository; - private final ModelMapper modelMapper; @Autowired - public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository) { this.quizRepository = quizRepository; this.categoryRepository = categoryRepository; - this.modelMapper = modelMapper; } // 1 From 35807237f3b3e6b2ea5ae87c911c5ea4729e63ad Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 14:11:53 +0900 Subject: [PATCH 056/134] ci(gradle,jira): workflows -> workflow-templates --- .github/{workflows => workflow-templates}/gradle.yml | 2 ++ .github/{workflows => workflow-templates}/jira.yml | 0 2 files changed, 2 insertions(+) rename .github/{workflows => workflow-templates}/gradle.yml (99%) rename .github/{workflows => workflow-templates}/jira.yml (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflow-templates/gradle.yml similarity index 99% rename from .github/workflows/gradle.yml rename to .github/workflow-templates/gradle.yml index cdd6bd0..5dd9fcc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflow-templates/gradle.yml @@ -50,3 +50,5 @@ jobs: - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + + diff --git a/.github/workflows/jira.yml b/.github/workflow-templates/jira.yml similarity index 100% rename from .github/workflows/jira.yml rename to .github/workflow-templates/jira.yml From 8fa7e22734c9a564b635afd73fcb121f447529cd Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 14:18:20 +0900 Subject: [PATCH 057/134] ci(gradle,jira): workflow-templates -> workflows --- .github/{workflow-templates => workflows}/gradle.yml | 0 .github/{workflow-templates => workflows}/jira.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{workflow-templates => workflows}/gradle.yml (100%) rename .github/{workflow-templates => workflows}/jira.yml (100%) diff --git a/.github/workflow-templates/gradle.yml b/.github/workflows/gradle.yml similarity index 100% rename from .github/workflow-templates/gradle.yml rename to .github/workflows/gradle.yml diff --git a/.github/workflow-templates/jira.yml b/.github/workflows/jira.yml similarity index 100% rename from .github/workflow-templates/jira.yml rename to .github/workflows/jira.yml From 17bee71514353afa8e5d2989f5fd7f94dc2d72ae Mon Sep 17 00:00:00 2001 From: hso Date: Sun, 7 Apr 2024 21:07:12 +0900 Subject: [PATCH 058/134] =?UTF-8?q?feat(Quiz)=20:=20param=20List->int=20id=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=ED=94=84?= =?UTF-8?q?=EB=A1=A0=ED=8A=B8=EC=97=90=EC=84=9C=20=ED=9A=A8=EC=9C=A8?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EB=AC=B8=EC=A0=9C=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=EB=A5=BC=20=EB=B3=B4=EB=82=B4=EB=8A=94=20=EA=B2=83?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=88=EB=9D=BC=20id=20=ED=95=98?= =?UTF-8?q?=EB=82=98=EC=94=A9=20=EC=B6=94=EA=B0=80=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ManageController.java | 8 +- .../Application/service/ManageService.java | 85 +++++++++---------- .../service/ManageServiceTest.java | 12 +-- 3 files changed, 48 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java index 9de5c9a..9ede9ba 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -36,12 +36,12 @@ public CrawlingQuizDTO findCrawlingQuizById(@PathVariable int id){ return manageService.selectCrawlingQuizByID(id); } - @PostMapping("/addQuiz") - public ResponseEntity> addQuizInList(@RequestBody List crawlingQuizDTOList){ + @PostMapping("/addQuiz/{id}") + public ResponseEntity addQuizInList(@PathVariable int id){ - List savedQuizList = manageService.insertSelectedQuiz(crawlingQuizDTOList); + Quiz savedQuiz = manageService.insertSelectedQuizById(id); - return ResponseEntity.status(HttpStatus.OK).body(savedQuizList); + return ResponseEntity.status(HttpStatus.OK).body(savedQuiz); } @GetMapping("findSelectedQuiz") diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 3f6350c..b920fda 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -2,7 +2,6 @@ import jakarta.transaction.Transactional; import org.modelmapper.ModelMapper; -import org.modelmapper.convention.MatchingStrategies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.domain.aggregate.entity.Category; @@ -52,7 +51,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { crawlingQuizDTOList.get(i).setCategory(category); boolean isSelected = quizRepository.countByDateAndOriginQuizId - (LocalDate.now().plusDays(1), crawlingQuiz.getId()) > 0; + (LocalDate.now().plusDays(1), crawlingQuiz.getId()) > 0; crawlingQuizDTOList.get(i).setSelected(isSelected); } return crawlingQuizDTOList; @@ -78,64 +77,56 @@ public CrawlingQuizDTO selectCrawlingQuizByID(int id) { } @Transactional - public List insertSelectedQuiz(List crawlingQuizDTOList) { + public Quiz insertSelectedQuizById(int id) { - mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); - List quizList = crawlingQuizDTOList.stream() - .map(crawlingQuizDTO -> mapper.map(crawlingQuizDTO, Quiz.class)) - .collect(Collectors.toList()); - - int index = getMaxIndex() + 1; - for (int i = 0; i < crawlingQuizDTOList.size(); i++) { + CrawlingQuizDTO selectedQuiz = selectCrawlingQuizByID(id); + Quiz insertQuiz = mapper.map(selectedQuiz, Quiz.class); - CrawlingQuizDTO seletedQuiz = crawlingQuizDTOList.get(i); + insertQuiz.setNo(getMaxNo() + 1); + insertQuiz.setDate(LocalDate.now().plusDays(1)); + insertQuiz.setCategoryId(selectedQuiz.getCategory().getId()); + insertQuiz.setOriginQuizId(selectedQuiz.getId()); - quizList.get(i).setNo(index++); - quizList.get(i).setDate(LocalDate.now().plusDays(1)); - quizList.get(i).setCategoryId(seletedQuiz.getCategory().getId()); - quizList.get(i).setOriginQuizId(seletedQuiz.getId()); - } - - return quizRepository.saveAll(quizList); - } + return quizRepository.save(insertQuiz); +} - public int getMaxIndex() { - return quizRepository.countByDate(LocalDate.now().plusDays(1)); - } +public int getMaxNo() { + return quizRepository.countByDate(LocalDate.now().plusDays(1)); +} - public List selectQuizListByDate(LocalDate date) { - List quizList = quizRepository.findByDateOrderByNoAsc(date); +public List selectQuizListByDate(LocalDate date) { + List quizList = quizRepository.findByDateOrderByNoAsc(date); - if (!quizList.isEmpty()) { - List quizDTOList = quizList.stream() - .map(quiz -> mapper.map(quiz, QuizDTO.class)) - .collect(Collectors.toList()); + if (!quizList.isEmpty()) { + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); - for (int i = 0; i < quizList.size(); i++) { - Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); - quizDTOList.get(i).setCategory(category); - } - return quizDTOList; + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + quizDTOList.get(i).setCategory(category); } - throw new NoSuchElementException("문제 정보를 불러올 수 없음"); + return quizDTOList; } + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); +} - @Transactional - public QuizDTO deleteQuizInListById(int id) { - Quiz deleteQuiz = quizRepository.findById(id).orElseThrow(IllegalAccessError::new); +@Transactional +public QuizDTO deleteQuizInListById(int id) { + Quiz deleteQuiz = quizRepository.findById(id).orElseThrow(IllegalAccessError::new); - if (deleteQuiz != null) { + if (deleteQuiz != null) { - quizRepository.deleteById(id); - List modifyQuizList = quizRepository - .findByDateAndNoGreaterThanOrderByNoAsc(LocalDate.now().plusDays(1), deleteQuiz.getNo()); + quizRepository.deleteById(id); + List modifyQuizList = quizRepository + .findByDateAndNoGreaterThanOrderByNoAsc(LocalDate.now().plusDays(1), deleteQuiz.getNo()); - for (Quiz modifyQuiz : modifyQuizList) { - modifyQuiz.setNo(modifyQuiz.getNo() - 1); - } - return mapper.map(deleteQuiz, QuizDTO.class); + for (Quiz modifyQuiz : modifyQuizList) { + modifyQuiz.setNo(modifyQuiz.getNo() - 1); } - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + return mapper.map(deleteQuiz, QuizDTO.class); } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); +} } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index 593aa06..cc5d2dc 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -11,7 +11,6 @@ import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; -import java.util.Date; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Stream; @@ -54,17 +53,18 @@ void selectCrawlingQuizById(int id){ assertNotNull(quizDTO); } - @Test - void addSelectedQuiz(){ - List selectedCrawlingQuizList = manageService.selectCrawlingQuizListByDate(LocalDate.of(2024,4,2)); - List savedQuizList = manageService.insertSelectedQuiz(selectedCrawlingQuizList); + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + void addSelectedQuiz(int id){ + Quiz savedQuiz = manageService.insertSelectedQuizById(id); - assertNotNull(savedQuizList); + assertNotNull(savedQuiz); } @Test void selectQuizListByDate(){ List savedQuizList = manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); + assertNotNull(savedQuizList); } From 24e05e8893fa87bdaf9903c917bd8ddb53d7c41d Mon Sep 17 00:00:00 2001 From: moomint8 Date: Mon, 8 Apr 2024 10:34:11 +0900 Subject: [PATCH 059/134] =?UTF-8?q?refactor(Entity):=20Lombok=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20Solved=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 개발 편의를 위해 Lombok @ 들이 추가되었습니다. 문제 해결 날짜를 저장할 컬럼이 추가되었습니다. --- .../domain/aggregate/entity/Category.java | 6 ++++++ .../domain/aggregate/entity/CrawlingQuiz.java | 6 ++++++ .../newsnippetback/domain/aggregate/entity/Quiz.java | 6 ++++++ .../domain/aggregate/entity/Solved.java | 11 +++++++++++ .../newsnippetback/domain/aggregate/entity/User.java | 6 ++++++ 5 files changed, 35 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 0c1bd1e..6ba0f4d 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -1,9 +1,15 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.*; @Entity @Table(name = "tbl_category") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString public class Category { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java index 98450fd..c5e525e 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -1,11 +1,17 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.*; import java.time.LocalDate; @Entity @Table(name = "tbl_crawling_quiz") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString public class CrawlingQuiz { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index c87cd93..da0efd7 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,11 +1,17 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.*; import java.time.LocalDate; @Entity @Table(name = "tbl_quiz") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString public class Quiz { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index d6875f1..0ca5f6c 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -1,9 +1,17 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDate; @Entity @Table(name = "tbl_solved") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString public class Solved { @Id @@ -17,6 +25,9 @@ public class Solved { @Column(name = "SELECTED_OPTION") private String selectedOption; + @Column(name = "SOLVED_DATE") + private LocalDate solvedDate; + @Column(name = "USER_ID") private int userId; diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java index 1fa57d1..4f23c74 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/User.java @@ -1,12 +1,18 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.*; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; @Entity @Table(name = "tbl_user") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString public class User { @Id From 75a01d89f1c19ad3318f0b45f5f86bbd71dca76a Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 15:32:51 +0900 Subject: [PATCH 060/134] =?UTF-8?q?feat(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 68 +++++++++++++++++++ .../Application/service/QuizService.java | 7 ++ .../Application/service/QuizServiceImpl.java | 59 ++++++++++++++++ .../NewsnippetBackApplication.java | 7 ++ .../domain/aggregate/vo/QuizRequest.java | 15 ++++ .../domain/aggregate/vo/QuizResponse.java | 21 ++++++ .../newsnippetback/domain/dto/QuizDTO.java | 3 + .../domain/repository/CategoryRepository.java | 12 ++++ .../domain/repository/QuizRepository.java | 13 ++++ 9 files changed, 205 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java new file mode 100644 index 0000000..7512ad4 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -0,0 +1,68 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.QuizService; + +@RestController +@RequestMapping("/quiz") +public class QuizController { + + private final QuizService quizService; + + private final ModelMapper modelMapper; + + @Autowired + public QuizController(QuizService quizService, ModelMapper modelMapper) { + this.quizService = quizService; + this.modelMapper = modelMapper; + } + + /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 + ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + +// @PostMapping("/test") +// public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); +// System.out.println("quizDTO = " + quizDTO); +// QuizResponse quizResponse = modelMapper.map(quizDTO, QuizResponse.class); +// System.out.println("quizResponse = " + quizResponse); +// +// // 정답률 계산 및 설정 +// double correctRate = calculateCorrectRate(quizDTO.getCorrectCnt(), quizDTO.getSolvedCnt()); +// System.out.println("correctRate = " + correctRate); +// quizResponse.setCorrectRate(correctRate); +// System.out.println("correctRate = " + correctRate); +// +// return ResponseEntity.ok().body(quizResponse); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } + + // 정답률 계산 메서드 + private double calculateCorrectRate(int correctCnt, int solvedCnt) { + if (solvedCnt == 0) { + return 0.0; + } + return (double) correctCnt / solvedCnt * 100; + } + + + + + + /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 + ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + + + /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ + + /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 + ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java new file mode 100644 index 0000000..3913fbf --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.Application.service; + +public interface QuizService { + +// QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java new file mode 100644 index 0000000..d877e2d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -0,0 +1,59 @@ +package org.triumers.newsnippetback.Application.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; + +@Service +public class QuizServiceImpl implements QuizService { + + private final QuizRepository quizRepository; + + private final CategoryRepository categoryRepository; + private final ModelMapper modelMapper; + + @Autowired + public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + this.quizRepository = quizRepository; + this.categoryRepository = categoryRepository; + this.modelMapper = modelMapper; + } + + // 1 +// @Override +// public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// +// return quizRepository.findByDateAndNo(date, no) +// .map(quiz -> { +// QuizDTO quizDTO = modelMapper.map(quiz, QuizDTO.class); +// +// // 카테고리 ID를 사용하여 카테고리 이름 조회 +// int categoryId = quiz.getCategoryId(); +// System.out.println("categoryId = " + categoryId); +// +// Optional optionalCategory = categoryRepository.findById(categoryId); +// System.out.println("optionalCategory = " + optionalCategory); +// +// Category category = optionalCategory.orElseThrow(() -> new NoSuchElementException("Category not found for id: " + categoryId)); +// System.out.println("category = " + category); +// +// // 정답률 계산 +// double correctRate = (double) quiz.getCorrectCnt() / quiz.getSolvedCnt() * 100; +// System.out.println("correctRate = " + correctRate); +// +// // QuizDTO에 카테고리 이름과 정답률 설정 +// quizDTO.setCategoryName(category.getCategoryName()); +// quizDTO.setCorrectRate(correctRate); +// +// System.out.println("correctRate = " + correctRate); +// +// return quizDTO; +// }) +// .orElseThrow(() -> new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no)); +// } + +} diff --git a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java index d6e1ac3..6317e25 100644 --- a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java +++ b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback; +import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class NewsnippetBackApplication { @@ -10,4 +12,9 @@ public static void main(String[] args) { SpringApplication.run(NewsnippetBackApplication.class, args); } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java new file mode 100644 index 0000000..4f17eaf --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -0,0 +1,15 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class QuizRequest { + private LocalDate date; + private int no; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java new file mode 100644 index 0000000..32488d6 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java @@ -0,0 +1,21 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class QuizResponse { + + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private int solvedCnt; + private int correctCnt; + private String categoryName; + private double correctRate; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 664fa1f..6379871 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -36,4 +36,7 @@ public class QuizDTO { private Category category; private int originQuizId; + + private String categoryName; + private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java new file mode 100644 index 0000000..ac5e2fc --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -0,0 +1,12 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.util.Optional; + +@Repository +public interface CategoryRepository extends JpaRepository { + Optional findById(int categoryId); +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java new file mode 100644 index 0000000..e249d7b --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; + +import java.time.LocalDate; +import java.util.Optional; + +@Repository +public interface QuizRepository extends JpaRepository { + Optional findByDateAndNo(LocalDate date, int no); +} From 3f421b6494df07534f6116519a8d5bce3c1f9df7 Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 17:31:55 +0900 Subject: [PATCH 061/134] =?UTF-8?q?refactor(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +- .../controller/QuizController.java | 64 +++++++------- .../Application/service/QuizService.java | 8 +- .../Application/service/QuizServiceImpl.java | 84 +++++++++++-------- .../domain/aggregate/entity/Category.java | 2 + .../domain/aggregate/entity/Quiz.java | 2 + .../domain/aggregate/vo/QuizRequest.java | 31 +++++++ .../domain/aggregate/vo/QuizResponse.java | 26 ++++++ .../newsnippetback/domain/dto/QuizDTO.java | 4 +- .../domain/repository/CategoryRepository.java | 2 +- .../domain/repository/QuizRepository.java | 5 +- 11 files changed, 160 insertions(+), 74 deletions(-) diff --git a/build.gradle b/build.gradle index 30bb43c..5b45d0e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,9 +33,9 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' // SpringSecurity 라이브러리 - implementation 'org.springframework.boot:spring-boot-starter-security' - testImplementation 'org.springframework.security:spring-security-test' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' +// implementation 'org.springframework.boot:spring-boot-starter-security' +// testImplementation 'org.springframework.security:spring-security-test' +// implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' // JWT implementation 'io.jsonwebtoken:jjwt-api:0.11.5' diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 7512ad4..cdb4f83 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -2,9 +2,18 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; import org.triumers.newsnippetback.Application.service.QuizService; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -22,47 +31,36 @@ public QuizController(QuizService quizService, ModelMapper modelMapper) { /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ - -// @PostMapping("/test") -// public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { -// try { -// QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); -// System.out.println("quizDTO = " + quizDTO); -// QuizResponse quizResponse = modelMapper.map(quizDTO, QuizResponse.class); -// System.out.println("quizResponse = " + quizResponse); -// -// // 정답률 계산 및 설정 -// double correctRate = calculateCorrectRate(quizDTO.getCorrectCnt(), quizDTO.getSolvedCnt()); -// System.out.println("correctRate = " + correctRate); -// quizResponse.setCorrectRate(correctRate); -// System.out.println("correctRate = " + correctRate); -// -// return ResponseEntity.ok().body(quizResponse); -// } catch (NoSuchElementException e) { -// return ResponseEntity.notFound().build(); -// } catch (Exception e) { -// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); -// } -// } - - // 정답률 계산 메서드 - private double calculateCorrectRate(int correctCnt, int solvedCnt) { - if (solvedCnt == 0) { - return 0.0; + @PostMapping("/test") + public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { + try { + QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); + + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + + return ResponseEntity.ok().body(quizResponse); + } catch (NoSuchElementException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } - return (double) correctCnt / solvedCnt * 100; } - - - /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ + /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 3913fbf..d4459b4 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,7 +1,13 @@ package org.triumers.newsnippetback.Application.service; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.List; + public interface QuizService { -// QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index d877e2d..a53714b 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -3,9 +3,16 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; +import java.time.LocalDate; +import java.util.NoSuchElementException; + @Service public class QuizServiceImpl implements QuizService { @@ -22,38 +29,49 @@ public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categor } // 1 -// @Override -// public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { -// LocalDate date = quizRequest.getDate(); -// int no = quizRequest.getNo(); -// -// return quizRepository.findByDateAndNo(date, no) -// .map(quiz -> { -// QuizDTO quizDTO = modelMapper.map(quiz, QuizDTO.class); -// -// // 카테고리 ID를 사용하여 카테고리 이름 조회 -// int categoryId = quiz.getCategoryId(); -// System.out.println("categoryId = " + categoryId); -// -// Optional optionalCategory = categoryRepository.findById(categoryId); -// System.out.println("optionalCategory = " + optionalCategory); -// -// Category category = optionalCategory.orElseThrow(() -> new NoSuchElementException("Category not found for id: " + categoryId)); -// System.out.println("category = " + category); -// -// // 정답률 계산 -// double correctRate = (double) quiz.getCorrectCnt() / quiz.getSolvedCnt() * 100; -// System.out.println("correctRate = " + correctRate); -// -// // QuizDTO에 카테고리 이름과 정답률 설정 -// quizDTO.setCategoryName(category.getCategoryName()); -// quizDTO.setCorrectRate(correctRate); -// -// System.out.println("correctRate = " + correctRate); -// -// return quizDTO; -// }) -// .orElseThrow(() -> new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no)); -// } + @Override + public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + + Quiz quiz = quizRepository.findByDateAndNo(date, no); + + if (quiz == null) { + throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + } + + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + + // 카테고리 ID를 사용하여 카테고리 이름 조회 + int categoryId = quiz.getCategoryId(); + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); + } + quizDTO.setCategoryName(category.getCategoryName()); + + // 정답률 계산 + double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); + quizDTO.setCorrectRate(correctRate); + + return quizDTO; + } + + // 정답률 계산 메서드 + private double calculateCorrectRate(int correctCnt, int solvedCnt) { + if (solvedCnt == 0) { + return 0.0; + } + return (double) correctCnt / solvedCnt * 100; + } + + + + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 0c1bd1e..6153844 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; +@Data @Entity @Table(name = "tbl_category") public class Category { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index c87cd93..d20bb8b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,9 +1,11 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; import java.time.LocalDate; +@Data @Entity @Table(name = "tbl_quiz") public class Quiz { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java index 4f17eaf..e0e6063 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.domain.aggregate.vo; import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; import java.time.LocalDate; @@ -10,6 +11,36 @@ @Setter @ToString public class QuizRequest { + private int id; + private LocalDate date; + private int no; + + private String content; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + + private int solvedCnt; + + private int correctCnt; + + private Category category; + + private int originQuizId; + + private String categoryName; + private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java index 32488d6..b24070b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizResponse.java @@ -1,6 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.vo; import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.time.LocalDate; @NoArgsConstructor @AllArgsConstructor @@ -9,13 +12,36 @@ @ToString public class QuizResponse { + private int id; + + private LocalDate date; + + private int no; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + private int solvedCnt; + private int correctCnt; + + private Category category; + + private int originQuizId; + private String categoryName; private double correctRate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 6379871..a2bdc67 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -1,11 +1,11 @@ package org.triumers.newsnippetback.domain.dto; -import jakarta.persistence.*; +import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; -import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import java.time.LocalDate; +@Data public class QuizDTO { private int id; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java index ac5e2fc..bb8efb7 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -8,5 +8,5 @@ @Repository public interface CategoryRepository extends JpaRepository { - Optional findById(int categoryId); + Category findById(int categoryId); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index e249d7b..f095b1b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -5,9 +5,12 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; +import java.util.List; import java.util.Optional; @Repository public interface QuizRepository extends JpaRepository { - Optional findByDateAndNo(LocalDate date, int no); + Quiz findByDateAndNo(LocalDate date, int no); + + List findQuizById(int id); } From 24971db52f767e0fba78bb9dd920fe4c1e1cf1cf Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 17:54:55 +0900 Subject: [PATCH 062/134] =?UTF-8?q?feat(Quiz):=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=A0=95=EB=8B=B5,=20=ED=95=B4?= =?UTF-8?q?=EC=84=A4,=20=EC=9B=90=EB=B3=B8=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 19 ++++++++++++++++++ .../Application/service/QuizService.java | 2 ++ .../Application/service/QuizServiceImpl.java | 20 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index cdb4f83..8bcf3c6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -11,6 +11,7 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; @@ -56,6 +57,24 @@ public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ + @PostMapping("/answer") + public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { + try { + QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); + + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setAnswer(quizDTO.getAnswer()); + quizResponse.setExplanation(quizDTO.getExplanation()); + quizResponse.setNewsLink(quizDTO.getNewsLink()); + + return ResponseEntity.ok().body(quizResponse); + } catch (NoSuchElementException e) { + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index d4459b4..a4b2f27 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; @@ -10,4 +11,5 @@ public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); + QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index a53714b..9755ae8 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -71,6 +71,26 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { } + // 2 + @Override + public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + + Quiz quiz = quizRepository.findByDateAndNo(date, no); + + if (quiz == null) { + throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + } + + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setAnswer(quiz.getAnswer()); + quizDTO.setExplanation(quiz.getExplanation()); + quizDTO.setNewsLink(quiz.getNewsLink()); + + return quizDTO; + } + From c756f11b4dde56d923ce03497089f798bbef5ab0 Mon Sep 17 00:00:00 2001 From: B Date: Thu, 4 Apr 2024 18:21:19 +0900 Subject: [PATCH 063/134] =?UTF-8?q?feat(Quiz):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 - 2. 해당 문제의 정답, 해설, 원본 링크 조회 --- .../domain/aggregate/vo/QuizRequest.java | 5 + .../service/QuizServiceImplTests.java | 154 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java index e0e6063..279e8e0 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/QuizRequest.java @@ -43,4 +43,9 @@ public class QuizRequest { private String categoryName; private double correctRate; + + public QuizRequest(LocalDate date, int no) { + this.date = date; + this.no = no; + } } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java new file mode 100644 index 0000000..8d0fa1d --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java @@ -0,0 +1,154 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.NoSuchElementException; + +@SpringBootTest +public class QuizServiceImplTests { + + @Autowired + private QuizServiceImpl quizServiceImpl; + + @Autowired + private PlatformTransactionManager transactionManager; + + @AfterEach + void rollback() { + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + transactionManager.rollback(status); + } + + @DisplayName("퀴즈 조회 - 첫 번째 문제") + @Test + void findQuizByDateAndNo_FirstQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 1; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", quizDTO.getContent()); + Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", quizDTO.getOptionA()); + Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", quizDTO.getOptionB()); + Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", quizDTO.getOptionC()); + Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", quizDTO.getOptionD()); + Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); + Assertions.assertEquals(70.0, quizDTO.getCorrectRate()); + } + + @DisplayName("퀴즈 조회 - 두 번째 문제") + @Test + void findQuizByDateAndNo_SecondQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 2; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("중국 전자상거래 기업 알리바바가 1시간 이내에 전 세계로 상품을 배송하는 시도에 나선다고 하는데, 이를 위해 협업하는 로켓 개발 스타트업은?", quizDTO.getContent()); + Assertions.assertEquals("스페이스 엑스 (Space X)", quizDTO.getOptionA()); + Assertions.assertEquals("스페이스 에포크 (Space Epoch)", quizDTO.getOptionB()); + Assertions.assertEquals("블루 오리진 (Blue Origin)", quizDTO.getOptionC()); + Assertions.assertEquals("로켓랩 (Rocket Lab)", quizDTO.getOptionD()); + Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); + Assertions.assertEquals(80.0, quizDTO.getCorrectRate()); + } + + @DisplayName("존재하지 않는 퀴즈 조회") + @Test + void findNonExistingQuiz() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 999; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizByDateAndNo(quizRequest); + }); + } + + @DisplayName("존재하지 않는 카테고리의 퀴즈 조회") + @Test + void findQuizWithNonExistingCategory() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 3; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizByDateAndNo(quizRequest); + }); + } + + @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") + @Test + void findQuizAnswerByDateAndNo_FirstQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 1; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("C", quizDTO.getAnswer()); + Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); + } + + @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") + @Test + void findQuizAnswerByDateAndNo_SecondQuestion() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 2; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When + QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + + // Then + Assertions.assertNotNull(quizDTO); + Assertions.assertEquals("B", quizDTO.getAnswer()); + Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); + } + + @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") + @Test + void findAnswerOfNonExistingQuiz() { + // Given + LocalDate date = LocalDate.of(2024, 4, 2); + int no = 999; + QuizRequest quizRequest = new QuizRequest(date, no); + + // When & Then + Assertions.assertThrows(NoSuchElementException.class, () -> { + quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); + }); + } +} From 6a88a561bbcb7b64cab5405ea9a2deb9e94bf0d2 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 13:56:56 +0900 Subject: [PATCH 064/134] =?UTF-8?q?feat(Solved):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 정답 여부, 회원이 입력한 답 추가 - 과거에 풀었던 문제 조회 --- .../controller/SolvedController.java | 72 ++++++++++++++ .../Application/service/SolvedService.java | 10 ++ .../service/SolvedServiceImpl.java | 99 +++++++++++++++++++ .../domain/aggregate/entity/Solved.java | 2 + .../aggregate/vo/SolvedIsCorrectResponse.java | 14 +++ .../aggregate/vo/SolvedQuizResponse.java | 25 +++++ .../domain/aggregate/vo/SolvedRequest.java | 13 +++ .../newsnippetback/domain/dto/SolvedDTO.java | 25 +++++ .../domain/repository/QuizRepository.java | 7 +- .../domain/repository/SolvedRepository.java | 10 ++ .../service/SolvedServiceImplTest.java | 75 ++++++++++++++ 11 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java new file mode 100644 index 0000000..471f99d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -0,0 +1,72 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.SolvedService; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedQuizResponse; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedIsCorrectResponse; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +import java.util.NoSuchElementException; + +@RestController +@RequestMapping("/solved") +public class SolvedController { + private final SolvedService solvedService; + + @Autowired + public SolvedController(SolvedService solvedService) { + this.solvedService = solvedService; + } + + /* 설명. 1. 사용자가 입력한 답과 문제의 정답 여부 판단 */ + @PostMapping("/check") + public ResponseEntity findSelectedOptionAndCompareAnswer(@RequestBody SolvedRequest solvedRequest) { + try { + SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); + SolvedIsCorrectResponse solvedIsCorrectResponse = new SolvedIsCorrectResponse(); + solvedIsCorrectResponse.setUserId(solvedDTO.getUserId()); + solvedIsCorrectResponse.setQuizId(solvedDTO.getQuizId()); + solvedIsCorrectResponse.setCorrect(solvedDTO.isCorrect()); + + return ResponseEntity.ok().body(solvedIsCorrectResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /* 설명. 2. 사용자가 과거에 풀었던 문제 조회 */ + @PostMapping("/find") + public ResponseEntity findSolvedQuizByUserID(@RequestBody SolvedRequest solvedRequest){ + try { + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + SolvedQuizResponse solvedQuizResponse = new SolvedQuizResponse(); + solvedQuizResponse.setUserId(solvedDTO.getUserId()); + solvedQuizResponse.setQuizId(solvedDTO.getQuizId()); + solvedQuizResponse.setCategoryId(solvedDTO.getCategoryId()); + solvedQuizResponse.setContent(solvedDTO.getContent()); + solvedQuizResponse.setOptionA(solvedDTO.getOptionA()); + solvedQuizResponse.setOptionB(solvedDTO.getOptionB()); + solvedQuizResponse.setOptionC(solvedDTO.getOptionC()); + solvedQuizResponse.setOptionD(solvedDTO.getOptionD()); + solvedQuizResponse.setAnswer(solvedDTO.getAnswer()); + solvedQuizResponse.setExplanation(solvedDTO.getExplanation()); + solvedQuizResponse.setNewsLink(solvedDTO.getNewsLink()); + solvedQuizResponse.setDate(solvedDTO.getDate()); + + return ResponseEntity.ok().body(solvedQuizResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java new file mode 100644 index 0000000..a795c5a --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -0,0 +1,10 @@ +package org.triumers.newsnippetback.Application.service; + +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +public interface SolvedService { + SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest); + + SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest); +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java new file mode 100644 index 0000000..c70976f --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -0,0 +1,99 @@ +package org.triumers.newsnippetback.Application.service; + +import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; +import org.triumers.newsnippetback.domain.repository.SolvedRepository; + +import java.util.NoSuchElementException; +import java.util.Objects; + +@Service +public class SolvedServiceImpl implements SolvedService{ + + private final SolvedRepository solvedRepository; + private final QuizRepository quizRepository; + private final CategoryRepository categoryRepository; + private final ModelMapper modelMapper; + + @Autowired + public SolvedServiceImpl(SolvedRepository solvedRepository, QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + this.solvedRepository = solvedRepository; + this.quizRepository = quizRepository; + this.categoryRepository = categoryRepository; + this.modelMapper = modelMapper; + } + + @Override + public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) { + + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + int userId = solvedRequest.getUserId(); + int quizId = solvedRequest.getQuizId(); + + Solved solved = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); + Quiz answer = quizRepository.findAnswerById(quizId); + + if (answer == null || solved == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); + } + + SolvedDTO solvedDTO = new SolvedDTO(); + solvedDTO.setUserId(userId); + solvedDTO.setQuizId(quizId); + + if (Objects.equals(answer.getAnswer(), solved.getSelectedOption())) { + solvedDTO.setCorrect(true); + solved.setCorrect(true); + solvedRepository.save(solved); + + return solvedDTO; + } + + solvedDTO.setCorrect(false); + solved.setCorrect(false); + solvedRepository.save(solved); + + return solvedDTO; + } + + @Override + public SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest) { + int userId = solvedRequest.getUserId(); + int quizId = solvedRequest.getQuizId(); + + Solved solvedQuiz = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); + Quiz answer = quizRepository.findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(quizId); + Category category = categoryRepository.findById(answer.getCategoryId()); + + if (answer == null || solvedQuiz == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); + } + + SolvedDTO solvedDTO = new SolvedDTO(); + solvedDTO.setUserId(solvedQuiz.getUserId()); + solvedDTO.setQuizId(solvedQuiz.getQuizId()); + solvedDTO.setCategoryId(category.getId()); + solvedDTO.setContent(answer.getContent()); + solvedDTO.setOptionA(answer.getOptionA()); + solvedDTO.setOptionB(answer.getOptionB()); + solvedDTO.setOptionC(answer.getOptionC()); + solvedDTO.setOptionD(answer.getOptionD()); + solvedDTO.setAnswer(answer.getAnswer()); + solvedDTO.setSelectedOption(solvedQuiz.getSelectedOption()); + solvedDTO.setExplanation(answer.getExplanation()); + solvedDTO.setNewsLink(answer.getNewsLink()); + solvedDTO.setDate(answer.getDate()); + + return solvedDTO; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index d6875f1..95f520c 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; +import lombok.Data; +@Data @Entity @Table(name = "tbl_solved") public class Solved { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java new file mode 100644 index 0000000..c9ad656 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedIsCorrectResponse.java @@ -0,0 +1,14 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedIsCorrectResponse { + private boolean isCorrect; + private int userId; + private int quizId; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java new file mode 100644 index 0000000..8bffcdc --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java @@ -0,0 +1,25 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedQuizResponse { + private int userId; + private int quizId; + private int categoryId; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private String answer; + private String explanation; + private String newsLink; + private LocalDate date; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java new file mode 100644 index 0000000..e2ba736 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedRequest { + private int userId; + private int quizId; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java new file mode 100644 index 0000000..29027da --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java @@ -0,0 +1,25 @@ +package org.triumers.newsnippetback.domain.dto; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class SolvedDTO { + private int id; + private boolean isCorrect; + private String selectedOption; + private int userId; + private int quizId; + private int categoryId; + private String content; + private String optionA; + private String optionB; + private String optionC; + private String optionD; + private String answer; + private String explanation; + private String newsLink; + private LocalDate date; + +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index f095b1b..8266fc2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -5,12 +5,11 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - @Repository public interface QuizRepository extends JpaRepository { Quiz findByDateAndNo(LocalDate date, int no); - List findQuizById(int id); + Quiz findAnswerById(int quizId); + + Quiz findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(int quizId); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java new file mode 100644 index 0000000..0811f64 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -0,0 +1,10 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; + +@Repository +public interface SolvedRepository extends JpaRepository { + Solved findSelectedOptionByUserIdAndQuizId(int userId, int quizId); +} diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java new file mode 100644 index 0000000..e47cd8b --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -0,0 +1,75 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.dto.SolvedDTO; + +import java.time.LocalDate; + +@SpringBootTest +class SolvedServiceImplTest { + @Autowired + private SolvedService solvedService; + + @Autowired + private PlatformTransactionManager transactionManager; + + @AfterEach + void rollback() { + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + transactionManager.rollback(status); + } + + @DisplayName("사용자가 입력한 답과 정답 비교하여 판정") + @Test + void findSelectedOptionAndCompareAnswer() { + // Given + int userId = 1; + int quizId = 1; + SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + + // When + SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); + + // Then + Assertions.assertNotNull(solvedDTO); + Assertions.assertEquals(1,solvedDTO.getUserId()); + Assertions.assertEquals(1,solvedDTO.getQuizId()); + Assertions.assertEquals(true, solvedDTO.isCorrect()); + } + + @DisplayName("사용자가 풀었던 문제 조회") + @Test + void findSolvedQuizByUserID() { + // Given + int userId = 1; + int quizId = 1; + SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + + // When + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + + // Then + Assertions.assertNotNull(solvedDTO); + Assertions.assertEquals(1, solvedDTO.getUserId()); + Assertions.assertEquals(1, solvedDTO.getQuizId()); + Assertions.assertEquals(9, solvedDTO.getCategoryId()); + Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", solvedDTO.getContent()); + Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", solvedDTO.getOptionA()); + Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", solvedDTO.getOptionB()); + Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", solvedDTO.getOptionC()); + Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", solvedDTO.getOptionD()); + Assertions.assertEquals("C",solvedDTO.getAnswer()); + Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", solvedDTO.getExplanation()); + Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", solvedDTO.getNewsLink()); + Assertions.assertEquals(LocalDate.of(2024, 4, 2), solvedDTO.getDate()); + } +} \ No newline at end of file From a9dad9f21fc1dc54b1f351073bd57b6d305cd500 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 5 Apr 2024 13:58:40 +0900 Subject: [PATCH 065/134] =?UTF-8?q?style(Quiz):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/controller/QuizController.java | 18 +----------------- .../Application/service/QuizService.java | 4 ---- .../Application/service/QuizServiceImpl.java | 4 +--- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 8bcf3c6..6e13f71 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -1,20 +1,15 @@ package org.triumers.newsnippetback.Application.controller; -import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.triumers.newsnippetback.Application.service.QuizService; -import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; -import java.time.LocalDate; -import java.util.List; import java.util.NoSuchElementException; -import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -22,12 +17,9 @@ public class QuizController { private final QuizService quizService; - private final ModelMapper modelMapper; - @Autowired - public QuizController(QuizService quizService, ModelMapper modelMapper) { + public QuizController(QuizService quizService) { this.quizService = quizService; - this.modelMapper = modelMapper; } /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 @@ -74,12 +66,4 @@ public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizR return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } - - - - /* 3. 회원이 입력한 답과 문제의 정답이 같은지 조회한 후 정답 여부와 회원이 입력한 답 저장 */ - - - /* 4. 문제 id 값 이용해 과거에 풀었던 문제 조회 - ㄴ 문제의 카테고리, 지문, 선택지, 정답, 선택했던 답, 해설, 원본 링크, 날짜, 정답률 조회 */ } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index a4b2f27..83f1369 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,12 +1,8 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; -import java.time.LocalDate; -import java.util.List; - public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 9755ae8..cba1c36 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -19,13 +19,11 @@ public class QuizServiceImpl implements QuizService { private final QuizRepository quizRepository; private final CategoryRepository categoryRepository; - private final ModelMapper modelMapper; @Autowired - public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categoryRepository) { this.quizRepository = quizRepository; this.categoryRepository = categoryRepository; - this.modelMapper = modelMapper; } // 1 From dbf7a3f8d997b6776736fffb5ede84f8cfbca20f Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Mon, 8 Apr 2024 10:46:44 +0900 Subject: [PATCH 066/134] =?UTF-8?q?style(entity):=20Data=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/domain/aggregate/entity/Category.java | 8 ++++++-- .../newsnippetback/domain/aggregate/entity/Quiz.java | 8 ++++++-- .../newsnippetback/domain/aggregate/entity/Solved.java | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 6153844..68ac939 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -1,9 +1,13 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; -import lombok.Data; +import lombok.*; -@Data +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString @Entity @Table(name = "tbl_category") public class Category { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index d20bb8b..d4e6144 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,11 +1,15 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; -import lombok.Data; +import lombok.*; import java.time.LocalDate; -@Data +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString @Entity @Table(name = "tbl_quiz") public class Quiz { diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index 95f520c..cae8a01 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -1,9 +1,13 @@ package org.triumers.newsnippetback.domain.aggregate.entity; import jakarta.persistence.*; -import lombok.Data; +import lombok.*; -@Data +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString @Entity @Table(name = "tbl_solved") public class Solved { From 8e9abe2eae3b4eca844c189158c1fd8c58349c04 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Mon, 8 Apr 2024 17:44:26 +0900 Subject: [PATCH 067/134] =?UTF-8?q?feat(Solved):=20=EC=A7=80=EC=A0=95=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EC=9D=B4=20=EB=A7=9E=EC=B6=98=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20close=20#16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추후 Count 반환 추가예정 --- .../controller/SolvedController.java | 32 ++++++++++++++++--- .../Application/service/SolvedService.java | 6 ++++ .../service/SolvedServiceImpl.java | 16 ++++++++++ .../domain/aggregate/entity/Solved.java | 5 +++ .../aggregate/vo/SolvedResultRequest.java | 16 ++++++++++ .../aggregate/vo/SolvedResultResponse.java | 13 ++++++++ .../newsnippetback/domain/dto/SolvedDTO.java | 10 ++++-- .../domain/repository/SolvedRepository.java | 5 +++ 8 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultRequest.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultResponse.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index 471f99d..c6b24a2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -1,5 +1,7 @@ package org.triumers.newsnippetback.Application.controller; +import org.modelmapper.ModelMapper; +import org.modelmapper.convention.MatchingStrategies; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -8,21 +10,24 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.triumers.newsnippetback.Application.service.SolvedService; -import org.triumers.newsnippetback.domain.aggregate.vo.SolvedQuizResponse; -import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; -import org.triumers.newsnippetback.domain.aggregate.vo.SolvedIsCorrectResponse; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; +import org.triumers.newsnippetback.domain.aggregate.vo.*; import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; @RestController @RequestMapping("/solved") public class SolvedController { private final SolvedService solvedService; + private final ModelMapper modelMapper; @Autowired - public SolvedController(SolvedService solvedService) { + public SolvedController(SolvedService solvedService, ModelMapper modelMapper) { this.solvedService = solvedService; + this.modelMapper = modelMapper; } /* 설명. 1. 사용자가 입력한 답과 문제의 정답 여부 판단 */ @@ -69,4 +74,23 @@ public ResponseEntity findSolvedQuizByUserID(@RequestBody So return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } + + /* 설명. 3. 회원이 지정한 날짜에 맞춘 문제 갯수 조회 */ + @PostMapping("/result") + public ResponseEntity> findCorrectQuizByUserIdAndSolvedDate(@RequestBody SolvedResultRequest solvedResultRequest){ + try { + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + List solvedList = solvedService.findCorrectQuizByUserIdAndSolvedDate(solvedResultRequest); + List solvedResultResponse = solvedList.stream() + .map(dot -> modelMapper.map(dot, SolvedResultResponse.class)) + .collect(Collectors.toList()); + + return ResponseEntity.ok().body(solvedResultResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index a795c5a..e6351de 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -1,10 +1,16 @@ package org.triumers.newsnippetback.Application.service; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import java.util.List; + public interface SolvedService { SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest); SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest); + + List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index c70976f..a6fa459 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -8,11 +8,15 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; import org.triumers.newsnippetback.domain.dto.SolvedDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; import org.triumers.newsnippetback.domain.repository.SolvedRepository; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -96,4 +100,16 @@ public SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest) { return solvedDTO; } + + @Override + public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest) { + int userId = solvedResultRequest.getUserId(); + boolean isCorrect = solvedResultRequest.isCorrect(); + LocalDate solvedDate = solvedResultRequest.getSolvedDate(); + + List solvedList = solvedRepository.findIdByUserIdAndIsCorrectAndSolvedDate(userId, isCorrect, solvedDate); + + return solvedList; + } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index cae8a01..f267a43 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -3,6 +3,8 @@ import jakarta.persistence.*; import lombok.*; +import java.time.LocalDate; + @NoArgsConstructor @AllArgsConstructor @Getter @@ -28,4 +30,7 @@ public class Solved { @Column(name = "QUIZ_ID") private int quizId; + + @Column(name = "SOLVED_DATE") + private LocalDate solvedDate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultRequest.java new file mode 100644 index 0000000..b81bc24 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultRequest.java @@ -0,0 +1,16 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedResultRequest { + private int userId; + private boolean isCorrect; + private LocalDate solvedDate; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultResponse.java new file mode 100644 index 0000000..0d353ac --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedResultResponse.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedResultResponse { + private int id; + private boolean isCorrect; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java index 29027da..359948e 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java @@ -1,10 +1,14 @@ package org.triumers.newsnippetback.domain.dto; -import lombok.Data; +import lombok.*; import java.time.LocalDate; -@Data +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString public class SolvedDTO { private int id; private boolean isCorrect; @@ -21,5 +25,5 @@ public class SolvedDTO { private String explanation; private String newsLink; private LocalDate date; - + private LocalDate solvedDate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java index 0811f64..66e51f3 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -4,7 +4,12 @@ import org.springframework.stereotype.Repository; import org.triumers.newsnippetback.domain.aggregate.entity.Solved; +import java.time.LocalDate; +import java.util.List; + @Repository public interface SolvedRepository extends JpaRepository { Solved findSelectedOptionByUserIdAndQuizId(int userId, int quizId); + + List findIdByUserIdAndIsCorrectAndSolvedDate(int userId, boolean isCorrect, LocalDate solvedDate); } From 26a8efe06f4543e6ec825f4dc256d2188ba01ec6 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 9 Apr 2024 10:09:16 +0900 Subject: [PATCH 068/134] =?UTF-8?q?feat(Auth):=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원가입 기능이 구현되었습니다. --- .../controller/AuthController.java | 54 ++++++++++++++++++ .../Application/service/SignupService.java | 57 +++++++++++++++++++ .../common/config/SecurityConfig.java | 46 +++++++++++++++ .../common/exception/CustomException.java | 7 +++ .../UserEmailDuplicateException.java | 7 +++ .../UserNicknameDuplicateException.java | 7 +++ .../domain/aggregate/vo/RequestSignupVO.java | 15 +++++ .../aggregate/vo/ResponseMessageVO.java | 12 ++++ .../newsnippetback/domain/dto/SignupDTO.java | 24 ++++++++ .../domain/repository/UserRepository.java | 11 ++++ 10 files changed, 240 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/CustomException.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/UserEmailDuplicateException.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/UserNicknameDuplicateException.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseMessageVO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java new file mode 100644 index 0000000..421c549 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -0,0 +1,54 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.SignupService; +import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; +import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.domain.aggregate.enums.Provider; +import org.triumers.newsnippetback.domain.aggregate.vo.RequestSignupVO; +import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; +import org.triumers.newsnippetback.domain.dto.SignupDTO; + +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final SignupService signupService; + + @Autowired + public AuthController(SignupService signupService) { + this.signupService = signupService; + } + + @PostMapping("/signup") + public ResponseEntity signup(@RequestBody RequestSignupVO request) { + + ResponseMessageVO response = new ResponseMessageVO(); + + SignupDTO user = new SignupDTO(); + + user.setName(request.getName()); + user.setNickname(request.getNickname()); + user.setEmail(request.getEmail()); + user.setPassword(request.getPassword()); + user.setProvider(Provider.LOCAL); + + try { + signupService.signup(user); + + response.setMessage(user.getNickname() + "님 회원가입에 성공했습니다."); + + } catch (UserNicknameDuplicateException | UserEmailDuplicateException e) { + response.setMessage(e.getMessage()); + ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + return ResponseEntity.status(HttpStatus.OK).body(response); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java new file mode 100644 index 0000000..7cd7aa0 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java @@ -0,0 +1,57 @@ +package org.triumers.newsnippetback.Application.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; +import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; +import org.triumers.newsnippetback.domain.dto.SignupDTO; +import org.triumers.newsnippetback.domain.repository.UserRepository; + +@Service +public class SignupService { + + private final UserRepository userRepository; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + + @Autowired + public SignupService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { + this.userRepository = userRepository; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; + } + + public void signup(SignupDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { + + // 닉네임 중복 예외 처리 + if (userRepository.existsByNickname(request.getNickname())) { + throw new UserNicknameDuplicateException(); + } + + // 이메일 중복 예외 처리 + if (userRepository.existsByEmail(request.getEmail())) { + throw new UserEmailDuplicateException(); + } + + User user = userMapper(request); + + userRepository.save(user); + } + + private User userMapper(SignupDTO request) { + User user = new User(); + + user.setName(request.getName()); + user.setNickname(request.getNickname()); + user.setEmail(request.getEmail()); + user.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); + user.setUserRole(UserRole.ROLE_USER); + user.setProvider(request.getProvider()); + user.setSnsId(request.getSnsId()); + user.setUserStatus(UserStatus.ACTIVE); + + return user; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java new file mode 100644 index 0000000..0cd1fc8 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -0,0 +1,46 @@ +package org.triumers.newsnippetback.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + // csrf disable + http.csrf(AbstractHttpConfigurer::disable); + + //From 로그인 방식 disable + http.formLogin(AbstractHttpConfigurer::disable); + + //http basic 인증 방식 disable + http.httpBasic(AbstractHttpConfigurer::disable); + + //경로별 인가 작업 + http.authorizeHttpRequests((auth) -> auth + .requestMatchers("/auth/login", "/auth/signup").permitAll() + .requestMatchers("/**").permitAll() + .requestMatchers("/admin").hasRole("ADMIN") + .anyRequest().authenticated()); + + //세션 설정 + http.sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + + return http.build(); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/CustomException.java b/src/main/java/org/triumers/newsnippetback/common/exception/CustomException.java new file mode 100644 index 0000000..1793bd3 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/CustomException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class CustomException extends Exception { + public CustomException(String message) { + super("[ERROR] " + message); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/UserEmailDuplicateException.java b/src/main/java/org/triumers/newsnippetback/common/exception/UserEmailDuplicateException.java new file mode 100644 index 0000000..2daceaa --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/UserEmailDuplicateException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class UserEmailDuplicateException extends CustomException { + public UserEmailDuplicateException() { + super("이메일 중복"); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/UserNicknameDuplicateException.java b/src/main/java/org/triumers/newsnippetback/common/exception/UserNicknameDuplicateException.java new file mode 100644 index 0000000..fc6b77d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/UserNicknameDuplicateException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class UserNicknameDuplicateException extends CustomException { + public UserNicknameDuplicateException() { + super("닉네임 중복"); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java new file mode 100644 index 0000000..56eb667 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java @@ -0,0 +1,15 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class RequestSignupVO { + private String name; + private String nickname; + private String email; + private String password; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseMessageVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseMessageVO.java new file mode 100644 index 0000000..44987a4 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseMessageVO.java @@ -0,0 +1,12 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class ResponseMessageVO { + private String message; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java new file mode 100644 index 0000000..f1bcda7 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java @@ -0,0 +1,24 @@ +package org.triumers.newsnippetback.domain.dto; + +import jakarta.persistence.Column; +import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.enums.Provider; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class SignupDTO { + + private String name; + private String nickname; + private String email; + private String password; + private UserRole userRole; + private Provider provider; + private String snsId; + private UserStatus userStatus; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java new file mode 100644 index 0000000..ee867e3 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java @@ -0,0 +1,11 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.triumers.newsnippetback.domain.aggregate.entity.User; + +public interface UserRepository extends JpaRepository { + + Boolean existsByNickname(String nickname); + + Boolean existsByEmail(String email); +} From f5947c9dcf13fc35a237a21cbb8ad04ff32dc9f0 Mon Sep 17 00:00:00 2001 From: hso Date: Tue, 9 Apr 2024 10:29:23 +0900 Subject: [PATCH 069/134] =?UTF-8?q?style(CrawlingQuiz)=20:=20post=20->=20g?= =?UTF-8?q?et=20=EB=B3=80=EA=B2=BD=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=9D=BC=20postMapping?= =?UTF-8?q?=EC=9D=84=20getMapping=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/controller/ManageController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java index 9ede9ba..88872b8 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -36,7 +36,7 @@ public CrawlingQuizDTO findCrawlingQuizById(@PathVariable int id){ return manageService.selectCrawlingQuizByID(id); } - @PostMapping("/addQuiz/{id}") + @GetMapping("/addQuiz/{id}") public ResponseEntity addQuizInList(@PathVariable int id){ Quiz savedQuiz = manageService.insertSelectedQuizById(id); From 504dcb7a1ca479eaa116ea7658eec7acad676f2c Mon Sep 17 00:00:00 2001 From: hso Date: Tue, 9 Apr 2024 10:33:09 +0900 Subject: [PATCH 070/134] =?UTF-8?q?style(Controller)=20:=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/controller/ManageController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java index 88872b8..e5cd378 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -44,12 +44,12 @@ public ResponseEntity addQuizInList(@PathVariable int id){ return ResponseEntity.status(HttpStatus.OK).body(savedQuiz); } - @GetMapping("findSelectedQuiz") + @GetMapping("/findSelectedQuiz") public List findSelectedQuizList(){ return manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); } - @DeleteMapping("deleteQuiz/{id}") + @DeleteMapping("/deleteQuiz/{id}") public ResponseEntity deleteQuizInList(@PathVariable int id){ QuizDTO deletedQuiz = manageService.deleteQuizInListById(id); From 3a88478c9eb389415fc72f7640731c69b6358582 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 9 Apr 2024 11:14:21 +0900 Subject: [PATCH 071/134] =?UTF-8?q?feat(Auth):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 기능이 구현되었습니다. 현재는 이메일과 비밀번호를 이용해 검증만이 구현되었습니다. 추후 JWT 발급 등이 구현될 예정입니다. --- .../service/CustomUserDetailsService.java | 34 ++++++++++ .../common/config/SecurityConfig.java | 22 +++++++ .../common/jwt/LoginFilter.java | 46 +++++++++++++ .../domain/dto/CustomUserDetails.java | 65 +++++++++++++++++++ .../domain/repository/UserRepository.java | 2 + 5 files changed, 169 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java new file mode 100644 index 0000000..bcd9707 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java @@ -0,0 +1,34 @@ +package org.triumers.newsnippetback.Application.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.dto.CustomUserDetails; +import org.triumers.newsnippetback.domain.repository.UserRepository; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + @Autowired + public CustomUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + + User userData = userRepository.findByEmail(email); + + if (userData != null) { + + return new CustomUserDetails(userData); + } + + return null; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index 0cd1fc8..0ab26db 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -2,17 +2,35 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.triumers.newsnippetback.common.jwt.LoginFilter; @Configuration @EnableWebSecurity public class SecurityConfig { + //AuthenticationManager가 인자로 받을 AuthenticationConfiguraion 객체 생성자 주입 + private final AuthenticationConfiguration authenticationConfiguration; + + public SecurityConfig(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = authenticationConfiguration; + } + + //AuthenticationManager Bean 등록 + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + + return configuration.getAuthenticationManager(); + } + @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); @@ -37,6 +55,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers("/admin").hasRole("ADMIN") .anyRequest().authenticated()); + // 로그인 필터 + http.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration)), + UsernamePasswordAuthenticationFilter.class); + //세션 설정 http.sessionManagement((session) -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java new file mode 100644 index 0000000..dd28f7b --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java @@ -0,0 +1,46 @@ +package org.triumers.newsnippetback.common.jwt; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.triumers.newsnippetback.domain.aggregate.vo.RequestLoginVO; + +import java.io.IOException; + +public class LoginFilter extends UsernamePasswordAuthenticationFilter { + + private final AuthenticationManager authenticationManager; + + public LoginFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + + String email = obtainUsername(request); + String password = obtainPassword(request); + + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(email, password, null); + + return authenticationManager.authenticate(authToken); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { + + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { + + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java new file mode 100644 index 0000000..cc7e860 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java @@ -0,0 +1,65 @@ +package org.triumers.newsnippetback.domain.dto; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; + +import java.util.ArrayList; +import java.util.Collection; + +public class CustomUserDetails implements UserDetails { + + private final User user; + + public CustomUserDetails(User user) { + this.user = user; + } + + @Override + public Collection getAuthorities() { + + Collection collection = new ArrayList<>(); + + collection.add(new GrantedAuthority() { + @Override + public String getAuthority() { + return user.getUserRole().name(); + } + }); + + return collection; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getEmail(); + } + + @Override + public boolean isAccountNonExpired() { + + return user.getUserStatus() != UserStatus.DELETED; + } + + @Override + public boolean isAccountNonLocked() { + return user.getUserStatus() != UserStatus.BLOCKED; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return user.getUserStatus() != UserStatus.DELETED && + user.getUserStatus() != UserStatus.BLOCKED; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java index ee867e3..481bc5e 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java @@ -8,4 +8,6 @@ public interface UserRepository extends JpaRepository { Boolean existsByNickname(String nickname); Boolean existsByEmail(String email); + + User findByEmail(String email); } From 58c8235268c1488662db39ff0f69efc552f9e405 Mon Sep 17 00:00:00 2001 From: hso Date: Tue, 9 Apr 2024 12:18:09 +0900 Subject: [PATCH 072/134] =?UTF-8?q?fix(Quiz)=20:=20delete=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=88=98=EC=A0=95=20originId=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=84=9C=20=EC=82=AD=EC=A0=9C=ED=95=98=EB=8A=94=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20deleteQuiz=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ManageController.java | 2 - .../Application/service/ManageService.java | 65 ++++++++++--------- .../domain/repository/QuizRepository.java | 3 + 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java index e5cd378..272c7d0 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -38,7 +38,6 @@ public CrawlingQuizDTO findCrawlingQuizById(@PathVariable int id){ @GetMapping("/addQuiz/{id}") public ResponseEntity addQuizInList(@PathVariable int id){ - Quiz savedQuiz = manageService.insertSelectedQuizById(id); return ResponseEntity.status(HttpStatus.OK).body(savedQuiz); @@ -51,7 +50,6 @@ public List findSelectedQuizList(){ @DeleteMapping("/deleteQuiz/{id}") public ResponseEntity deleteQuizInList(@PathVariable int id){ - QuizDTO deletedQuiz = manageService.deleteQuizInListById(id); return ResponseEntity.status(HttpStatus.OK).body(deletedQuiz); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index b920fda..a84bddb 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -26,6 +26,8 @@ public class ManageService { private final CategoryRepository categoryRepository; private final ModelMapper mapper; + final static LocalDate nextDate = LocalDate.now().plusDays(1); + @Autowired public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, CategoryRepository categoryRepository, ModelMapper mapper) { @@ -51,7 +53,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { crawlingQuizDTOList.get(i).setCategory(category); boolean isSelected = quizRepository.countByDateAndOriginQuizId - (LocalDate.now().plusDays(1), crawlingQuiz.getId()) > 0; + (nextDate, crawlingQuiz.getId()) > 0; crawlingQuizDTOList.get(i).setSelected(isSelected); } return crawlingQuizDTOList; @@ -83,50 +85,51 @@ public Quiz insertSelectedQuizById(int id) { Quiz insertQuiz = mapper.map(selectedQuiz, Quiz.class); insertQuiz.setNo(getMaxNo() + 1); - insertQuiz.setDate(LocalDate.now().plusDays(1)); + insertQuiz.setDate(nextDate); insertQuiz.setCategoryId(selectedQuiz.getCategory().getId()); insertQuiz.setOriginQuizId(selectedQuiz.getId()); return quizRepository.save(insertQuiz); -} + } -public int getMaxNo() { - return quizRepository.countByDate(LocalDate.now().plusDays(1)); -} + public int getMaxNo() { + return quizRepository.countByDate(nextDate); + } -public List selectQuizListByDate(LocalDate date) { - List quizList = quizRepository.findByDateOrderByNoAsc(date); + public List selectQuizListByDate(LocalDate date) { + List quizList = quizRepository.findByDateOrderByNoAsc(date); - if (!quizList.isEmpty()) { - List quizDTOList = quizList.stream() - .map(quiz -> mapper.map(quiz, QuizDTO.class)) - .collect(Collectors.toList()); + if (!quizList.isEmpty()) { + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); - for (int i = 0; i < quizList.size(); i++) { - Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); - quizDTOList.get(i).setCategory(category); + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + quizDTOList.get(i).setCategory(category); + } + return quizDTOList; } - return quizDTOList; + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); } - throw new NoSuchElementException("문제 정보를 불러올 수 없음"); -} -@Transactional -public QuizDTO deleteQuizInListById(int id) { - Quiz deleteQuiz = quizRepository.findById(id).orElseThrow(IllegalAccessError::new); + @Transactional + public QuizDTO deleteQuizInListById(int id) { + + Quiz deleteQuiz = quizRepository.findByOriginQuizIdAndDate(id, nextDate); - if (deleteQuiz != null) { + if (deleteQuiz != null) { - quizRepository.deleteById(id); - List modifyQuizList = quizRepository - .findByDateAndNoGreaterThanOrderByNoAsc(LocalDate.now().plusDays(1), deleteQuiz.getNo()); + quizRepository.deleteById(deleteQuiz.getId()); + List modifyQuizList = quizRepository + .findByDateAndNoGreaterThanOrderByNoAsc(nextDate, deleteQuiz.getNo()); - for (Quiz modifyQuiz : modifyQuizList) { - modifyQuiz.setNo(modifyQuiz.getNo() - 1); + for (Quiz modifyQuiz : modifyQuizList) { + modifyQuiz.setNo(modifyQuiz.getNo() - 1); + } + return mapper.map(deleteQuiz, QuizDTO.class); } - return mapper.map(deleteQuiz, QuizDTO.class); + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); } - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); -} } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index e95f525..6a69868 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -7,6 +7,7 @@ import java.time.LocalDate; import java.util.List; +import java.util.Optional; public interface QuizRepository extends JpaRepository { List findByDateOrderByNoAsc(LocalDate date); @@ -16,4 +17,6 @@ public interface QuizRepository extends JpaRepository { Integer countByDate(LocalDate localDate); Integer countByDateAndOriginQuizId(LocalDate localDate, int id); + + Quiz findByOriginQuizIdAndDate(int id, LocalDate date); } From 2319773e6dd97e0a9158b475b0c84b7abb98545c Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Tue, 9 Apr 2024 12:55:34 +0900 Subject: [PATCH 073/134] =?UTF-8?q?feat(Solved):=20=EA=B3=BC=EA=B1=B0?= =?UTF-8?q?=EC=97=90=20=ED=92=80=EC=97=88=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #10 - 사용자 ID로 과거에 풀었던 문제들의 ID와 날짜 조회기능 구현 --- .../controller/SolvedController.java | 23 +++++++++++++++-- .../Application/service/SolvedService.java | 2 ++ .../service/SolvedServiceImpl.java | 25 ++++++++++++++++--- .../aggregate/vo/SolvedQuizListResponse.java | 16 ++++++++++++ .../domain/repository/SolvedRepository.java | 2 ++ 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizListResponse.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index c6b24a2..f4bf132 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -48,7 +48,26 @@ public ResponseEntity findSelectedOptionAndCompareAnswe } } - /* 설명. 2. 사용자가 과거에 풀었던 문제 조회 */ + /* 설명. 2. 사용자가 과거에 풀었던 문제들 조회 */ + @PostMapping("/find/all") + public ResponseEntity> findSolvedQuizByUserId(@RequestBody SolvedRequest solvedRequest){ + try { + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + List solvedList = solvedService.findSolvedQuizListByUserId(solvedRequest); + List SolvedQuizListResponse = solvedList.stream() + .map(dot -> modelMapper.map(dot, SolvedQuizListResponse.class)) + .collect(Collectors.toList()); + + return ResponseEntity.ok().body(SolvedQuizListResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /* 설명. 3. 사용자가 과거에 풀었던 문제 하나 조회 */ @PostMapping("/find") public ResponseEntity findSolvedQuizByUserID(@RequestBody SolvedRequest solvedRequest){ try { @@ -75,7 +94,7 @@ public ResponseEntity findSolvedQuizByUserID(@RequestBody So } } - /* 설명. 3. 회원이 지정한 날짜에 맞춘 문제 갯수 조회 */ + /* 설명. 4. 회원이 지정한 날짜에 맞춘 문제 갯수 조회 */ @PostMapping("/result") public ResponseEntity> findCorrectQuizByUserIdAndSolvedDate(@RequestBody SolvedResultRequest solvedResultRequest){ try { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index e6351de..cc3dc34 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -12,5 +12,7 @@ public interface SolvedService { SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest); + List findSolvedQuizListByUserId(SolvedRequest solvedRequest); + List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index a6fa459..9d8d578 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -15,10 +15,7 @@ import org.triumers.newsnippetback.domain.repository.SolvedRepository; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; +import java.util.*; @Service public class SolvedServiceImpl implements SolvedService{ @@ -36,6 +33,7 @@ public SolvedServiceImpl(SolvedRepository solvedRepository, QuizRepository quizR this.modelMapper = modelMapper; } + /* 설명. 사용자가 입력한 답과 정답 비교 후 여부 추가 */ @Override public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) { @@ -70,6 +68,25 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) return solvedDTO; } + /* 설명. 사용자의 ID로 풀었던 문제 ID들 불러오기 */ + @Override + public List findSolvedQuizListByUserId(SolvedRequest solvedRequest) { + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + int userId = solvedRequest.getUserId(); + List solvedList = solvedRepository.findSolvedQuizByUserId(userId); + List quizList = new ArrayList<>(); + + for (Solved solved: solvedList) { + int id = solved.getQuizId(); + quizList.add(quizRepository.findById(id).orElseThrow()); + } + System.out.println("quizList = " + quizList); + + return solvedList; + } + + /* 설명. 사용자의 ID와 문제 ID로 문제 내용 불러오기 */ @Override public SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest) { int userId = solvedRequest.getUserId(); diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizListResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizListResponse.java new file mode 100644 index 0000000..d6e9d9b --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizListResponse.java @@ -0,0 +1,16 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class SolvedQuizListResponse { + private int userId; + private int quizId; + private LocalDate solvedDate; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java index 66e51f3..a14c43f 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -12,4 +12,6 @@ public interface SolvedRepository extends JpaRepository { Solved findSelectedOptionByUserIdAndQuizId(int userId, int quizId); List findIdByUserIdAndIsCorrectAndSolvedDate(int userId, boolean isCorrect, LocalDate solvedDate); + + List findSolvedQuizByUserId(int userId); } From 9c462545fc046decf553895b81bda704298310b4 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 9 Apr 2024 14:08:53 +0900 Subject: [PATCH 074/134] =?UTF-8?q?feat(Auth):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20JWT=20=EB=B0=9C=EA=B8=89=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 토큰 발급 기능이 구현되었습니다. 로그인 시 해당 유저의 email과 role, 만료시간을 담은 JWT가 발급됩니다. --- .../common/config/SecurityConfig.java | 8 +-- .../newsnippetback/common/jwt/JWTUtil.java | 54 +++++++++++++++++++ .../common/jwt/LoginFilter.java | 28 +++++++--- 3 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index 0ab26db..3f5f883 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -11,17 +11,19 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.triumers.newsnippetback.common.jwt.JWTUtil; import org.triumers.newsnippetback.common.jwt.LoginFilter; @Configuration @EnableWebSecurity public class SecurityConfig { - //AuthenticationManager가 인자로 받을 AuthenticationConfiguraion 객체 생성자 주입 private final AuthenticationConfiguration authenticationConfiguration; + private final JWTUtil jwtUtil; - public SecurityConfig(AuthenticationConfiguration authenticationConfiguration) { + public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) { this.authenticationConfiguration = authenticationConfiguration; + this.jwtUtil = jwtUtil; } //AuthenticationManager Bean 등록 @@ -56,7 +58,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .anyRequest().authenticated()); // 로그인 필터 - http.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration)), + http.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class); //세션 설정 diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java new file mode 100644 index 0000000..6db8143 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java @@ -0,0 +1,54 @@ +package org.triumers.newsnippetback.common.jwt; + +import io.jsonwebtoken.Jwts; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.Objects; + +@Component +public class JWTUtil { + + private final SecretKey secretKey; + private final Long expiredMs; + + @Autowired + public JWTUtil(Environment env) { + this.secretKey = new SecretKeySpec( + Objects.requireNonNull(env.getProperty("token.secret")).getBytes(StandardCharsets.UTF_8), + Jwts.SIG.HS256.key().build().getAlgorithm()); + this.expiredMs = Long.valueOf(Objects.requireNonNull(env.getProperty("token.expiration_time"))); + } + + public String getEmail(String token) { + + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class); + } + + public UserRole getRole(String token) { + + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", UserRole.class); + } + + public Boolean isExpired(String token) { + + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + } + + public String createJwt(String username, UserRole role) { + + return Jwts.builder() + .claim("username", username) + .claim("role", role) + .issuedAt(new Date(System.currentTimeMillis())) + .expiration(new Date(System.currentTimeMillis() + expiredMs)) + .signWith(secretKey) + .compact(); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java index dd28f7b..6757a01 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java @@ -1,26 +1,30 @@ package org.triumers.newsnippetback.common.jwt; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.triumers.newsnippetback.domain.aggregate.vo.RequestLoginVO; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.dto.CustomUserDetails; import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; + private final JWTUtil jwtUtil; - public LoginFilter(AuthenticationManager authenticationManager) { + public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) { this.authenticationManager = authenticationManager; + this.jwtUtil = jwtUtil; } @Override @@ -35,12 +39,24 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ } @Override - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { + CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); + String username = customUserDetails.getUsername(); + + Collection authorities = authentication.getAuthorities(); + Iterator iterator = authorities.iterator(); + GrantedAuthority auth = iterator.next(); + + UserRole role = UserRole.valueOf(auth.getAuthority()); + + String token = jwtUtil.createJwt(username, role); + + response.addHeader("Authorization", "Bearer " + token); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { - + response.setStatus(401); } } From f349d75b570e4b13823cb6585fcc55cedb09673d Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Tue, 9 Apr 2024 14:41:01 +0900 Subject: [PATCH 075/134] =?UTF-8?q?test(Solved):=20=EA=B3=BC=EA=B1=B0?= =?UTF-8?q?=EC=97=90=20=ED=92=80=EC=97=88=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #10 - 사용자 ID로 과거에 풀었던 문제들의 ID와 날짜 조회기능 테스트 코드 구현 - 회원이 지정한 날짜에 맞춘 문제 ID와 날짜 조회기능 테스트 코드 구현 --- .../service/SolvedServiceImplTest.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java index e47cd8b..89a7716 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -9,10 +9,13 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; +import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; import org.triumers.newsnippetback.domain.dto.SolvedDTO; import java.time.LocalDate; +import java.util.List; @SpringBootTest class SolvedServiceImplTest { @@ -46,7 +49,22 @@ void findSelectedOptionAndCompareAnswer() { Assertions.assertEquals(true, solvedDTO.isCorrect()); } - @DisplayName("사용자가 풀었던 문제 조회") + @DisplayName("사용자가 풀었던 문제 리스트 조회") + @Test + void findSolvedQuizListByUserId() { + // Given + int userId = 1; + int quizId = 0; + + // When + SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + List solvedList = solvedService.findSolvedQuizListByUserId(solvedRequest); + + // Then + Assertions.assertNotNull(solvedList); + } + + @DisplayName("사용자가 풀었던 문제 한 개 조회") @Test void findSolvedQuizByUserID() { // Given @@ -55,7 +73,7 @@ void findSolvedQuizByUserID() { SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); // When - SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserIdAnfQuizId(solvedRequest); // Then Assertions.assertNotNull(solvedDTO); @@ -72,4 +90,20 @@ void findSolvedQuizByUserID() { Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", solvedDTO.getNewsLink()); Assertions.assertEquals(LocalDate.of(2024, 4, 2), solvedDTO.getDate()); } + + @DisplayName("회원이 지정한 날짜에 맞춘 문제 조회") + @Test + void findCorrectQuizByUserIdAndSolvedDate() { + // Given + int userId = 1; + boolean isCorrect = true; + LocalDate solvedDate = LocalDate.parse("2024-04-08"); + SolvedResultRequest solvedResultRequest = new SolvedResultRequest(userId, isCorrect, solvedDate); + + // When + List solvedList = solvedService.findCorrectQuizByUserIdAndSolvedDate(solvedResultRequest); + + // Then + Assertions.assertNotNull(solvedList); + } } \ No newline at end of file From bdbda7d5dba6bf3dae7e14014a5ced4bfd391f54 Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Tue, 9 Apr 2024 14:45:05 +0900 Subject: [PATCH 076/134] =?UTF-8?q?refactor(Solved):=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/controller/SolvedController.java | 4 ++-- .../newsnippetback/Application/service/SolvedService.java | 2 +- .../Application/service/SolvedServiceImpl.java | 5 ++--- .../Application/service/SolvedServiceImplTest.java | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index f4bf132..e62170c 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -69,9 +69,9 @@ public ResponseEntity> findSolvedQuizByUserId(@Requ /* 설명. 3. 사용자가 과거에 풀었던 문제 하나 조회 */ @PostMapping("/find") - public ResponseEntity findSolvedQuizByUserID(@RequestBody SolvedRequest solvedRequest){ + public ResponseEntity findSolvedQuizByUserIdAndQuizId(@RequestBody SolvedRequest solvedRequest){ try { - SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserID(solvedRequest); + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserIdAndQuizId(solvedRequest); SolvedQuizResponse solvedQuizResponse = new SolvedQuizResponse(); solvedQuizResponse.setUserId(solvedDTO.getUserId()); solvedQuizResponse.setQuizId(solvedDTO.getQuizId()); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index cc3dc34..c0dece6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -10,7 +10,7 @@ public interface SolvedService { SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest); - SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest); + SolvedDTO findSolvedQuizByUserIdAndQuizId(SolvedRequest solvedRequest); List findSolvedQuizListByUserId(SolvedRequest solvedRequest); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 9d8d578..e46628d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -79,16 +79,15 @@ public List findSolvedQuizListByUserId(SolvedRequest solvedRequest) { for (Solved solved: solvedList) { int id = solved.getQuizId(); - quizList.add(quizRepository.findById(id).orElseThrow()); + quizList.add(quizRepository.findById(id).orElseThrow()); // 추후 response 추가 예정 } - System.out.println("quizList = " + quizList); return solvedList; } /* 설명. 사용자의 ID와 문제 ID로 문제 내용 불러오기 */ @Override - public SolvedDTO findSolvedQuizByUserID(SolvedRequest solvedRequest) { + public SolvedDTO findSolvedQuizByUserIdAndQuizId(SolvedRequest solvedRequest) { int userId = solvedRequest.getUserId(); int quizId = solvedRequest.getQuizId(); diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java index 89a7716..aaceea5 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -73,7 +73,7 @@ void findSolvedQuizByUserID() { SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); // When - SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserIdAnfQuizId(solvedRequest); + SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserIdAndQuizId(solvedRequest); // Then Assertions.assertNotNull(solvedDTO); From b98b33490c33980c668274abc85e9adb0cc17bdf Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 9 Apr 2024 16:10:13 +0900 Subject: [PATCH 077/134] =?UTF-8?q?build(build.gradle):=20JWT=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JWT 버전이 0.11.5에서 0.12.3 으로 버전업 되었습니다. --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 30bb43c..c8f5c49 100644 --- a/build.gradle +++ b/build.gradle @@ -38,9 +38,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'io.jsonwebtoken:jjwt-api:0.12.3' + implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' + implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' // DB 관련 라이브러리 runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' From ab359bca49fe0ea3b5bc16d317c50d29ec2ed98f Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 9 Apr 2024 16:12:12 +0900 Subject: [PATCH 078/134] chore(gitignore): .yml ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .yml 이 뒤에 / 가 붙어 적용되지 않던 것을 수정했습니다. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e284392..eeeb5cd 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ out/ .vscode/ ### YMl ### -.yml/ +.yml From 9c95261fe7b13c9a372eb94b1c7acdd5c2454b1b Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Tue, 9 Apr 2024 16:26:17 +0900 Subject: [PATCH 079/134] =?UTF-8?q?fix(quiz):=20=EB=8F=99=EC=9D=BC=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=AA=85=20=EB=82=B4=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Category, Solved: 중복 코드 제거 - QuizRepository: manage 기능 추가 - CrawlingQuiz: @Entity, @Table 어노테이션 추가 --- .../Application/service/ManageService.java | 9 +++------ .../domain/aggregate/entity/Category.java | 7 +------ .../domain/aggregate/entity/CrawlingQuiz.java | 4 +++- .../domain/aggregate/entity/Solved.java | 3 --- .../newsnippetback/domain/dto/CrawlingQuizDTO.java | 1 - .../domain/repository/QuizRepository.java | 12 ++++++++++++ 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index a84bddb..c12a383 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -48,8 +48,7 @@ public List selectCrawlingQuizListByDate(LocalDate date) { for (int i = 0; i < crawlingQuizList.size(); i++) { CrawlingQuiz crawlingQuiz = crawlingQuizList.get(i); - Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) - .orElseThrow(IllegalAccessError::new); + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); crawlingQuizDTOList.get(i).setCategory(category); boolean isSelected = quizRepository.countByDateAndOriginQuizId @@ -69,8 +68,7 @@ public CrawlingQuizDTO selectCrawlingQuizByID(int id) { if (crawlingQuiz != null) { CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); - Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) - .orElseThrow(IllegalAccessError::new); + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); crawlingQuizDTO.setCategory(category); return crawlingQuizDTO; @@ -105,8 +103,7 @@ public List selectQuizListByDate(LocalDate date) { .collect(Collectors.toList()); for (int i = 0; i < quizList.size(); i++) { - Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) - .orElseThrow(IllegalAccessError::new); + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()); quizDTOList.get(i).setCategory(category); } return quizDTOList; diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 6974fa9..e3b92df 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -3,11 +3,6 @@ import jakarta.persistence.*; import lombok.*; -@NoArgsConstructor -@AllArgsConstructor -@Getter -@Setter -@ToString @Entity @Table(name = "tbl_category") @Getter @@ -25,4 +20,4 @@ public class Category { @Column(name = "CATEGORY_NAME", nullable = false, unique = true) private String categoryName; -} +} \ No newline at end of file diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java index 2eb5d41..8a3b79c 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -9,6 +9,8 @@ @NoArgsConstructor @AllArgsConstructor @ToString +@Entity +@Table(name = "tbl_crawling_quiz") public class CrawlingQuiz { @Id @@ -45,4 +47,4 @@ public class CrawlingQuiz { @Column(name = "CATEGORY_ID") private int categoryId; -} +} \ No newline at end of file diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java index 3aadfc1..d502962 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Solved.java @@ -33,7 +33,4 @@ public class Solved { @Column(name = "QUIZ_ID") private int quizId; - - @Column(name = "SOLVED_DATE") - private LocalDate solvedDate; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java index d4d8e77..797efa5 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java @@ -1,6 +1,5 @@ package org.triumers.newsnippetback.domain.dto; -import jakarta.persistence.Column; import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 8266fc2..12cc39a 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -5,6 +5,8 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import java.time.LocalDate; +import java.util.List; + @Repository public interface QuizRepository extends JpaRepository { Quiz findByDateAndNo(LocalDate date, int no); @@ -12,4 +14,14 @@ public interface QuizRepository extends JpaRepository { Quiz findAnswerById(int quizId); Quiz findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(int quizId); + + List findByDateOrderByNoAsc(LocalDate date); + + List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); + + Integer countByDate(LocalDate localDate); + + Integer countByDateAndOriginQuizId(LocalDate localDate, int id); + + Quiz findByOriginQuizIdAndDate(int id, LocalDate date); } From edb0631190582cf81ec5a04035fa8d6598ec9da6 Mon Sep 17 00:00:00 2001 From: hso Date: Tue, 9 Apr 2024 17:07:52 +0900 Subject: [PATCH 080/134] =?UTF-8?q?style(Manage)=20:=20service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B6=84=EB=A6=AC=20ManageService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84,=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/service/ManageService.java | 124 +---------------- .../service/ManageServiceImpl.java | 131 ++++++++++++++++++ .../service/ManageServiceTest.java | 7 +- 3 files changed, 139 insertions(+), 123 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index c12a383..9bf4cde 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -1,132 +1,20 @@ package org.triumers.newsnippetback.Application.service; -import jakarta.transaction.Transactional; -import org.modelmapper.ModelMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.triumers.newsnippetback.domain.aggregate.entity.Category; -import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; import org.triumers.newsnippetback.domain.dto.QuizDTO; -import org.triumers.newsnippetback.domain.repository.CategoryRepository; -import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; -import org.triumers.newsnippetback.domain.repository.QuizRepository; import java.time.LocalDate; import java.util.List; -import java.util.NoSuchElementException; -import java.util.stream.Collectors; -@Service -public class ManageService { +public interface ManageService { + List selectCrawlingQuizListByDate(LocalDate date); - private final QuizRepository quizRepository; - private final CrawlingQuizRepository crawlingQuizRepository; - private final CategoryRepository categoryRepository; - private final ModelMapper mapper; + CrawlingQuizDTO selectCrawlingQuizByID(int id); - final static LocalDate nextDate = LocalDate.now().plusDays(1); + List selectQuizListByDate(LocalDate localDate); - @Autowired - public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, - CategoryRepository categoryRepository, ModelMapper mapper) { - this.quizRepository = quizRepository; - this.crawlingQuizRepository = crawlingQuizRepository; - this.categoryRepository = categoryRepository; - this.mapper = mapper; - } + Quiz insertSelectedQuizById(int id); - public List selectCrawlingQuizListByDate(LocalDate date) { - - List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); - - if (!crawlingQuizList.isEmpty()) { - List crawlingQuizDTOList = crawlingQuizList.stream() - .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) - .collect(Collectors.toList()); - - for (int i = 0; i < crawlingQuizList.size(); i++) { - CrawlingQuiz crawlingQuiz = crawlingQuizList.get(i); - Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); - crawlingQuizDTOList.get(i).setCategory(category); - - boolean isSelected = quizRepository.countByDateAndOriginQuizId - (nextDate, crawlingQuiz.getId()) > 0; - crawlingQuizDTOList.get(i).setSelected(isSelected); - } - return crawlingQuizDTOList; - } else { - // 이후에 크롤링 서버에 문제 생성 요청하기 - throw new NoSuchElementException("문제 정보를 불러올 수 없음"); - } - } - - public CrawlingQuizDTO selectCrawlingQuizByID(int id) { - CrawlingQuiz crawlingQuiz = crawlingQuizRepository.findById(id).orElseThrow(IllegalAccessError::new); - - if (crawlingQuiz != null) { - CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); - - Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); - crawlingQuizDTO.setCategory(category); - - return crawlingQuizDTO; - } - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); - } - - @Transactional - public Quiz insertSelectedQuizById(int id) { - - CrawlingQuizDTO selectedQuiz = selectCrawlingQuizByID(id); - Quiz insertQuiz = mapper.map(selectedQuiz, Quiz.class); - - insertQuiz.setNo(getMaxNo() + 1); - insertQuiz.setDate(nextDate); - insertQuiz.setCategoryId(selectedQuiz.getCategory().getId()); - insertQuiz.setOriginQuizId(selectedQuiz.getId()); - - return quizRepository.save(insertQuiz); - } - - public int getMaxNo() { - return quizRepository.countByDate(nextDate); - } - - public List selectQuizListByDate(LocalDate date) { - List quizList = quizRepository.findByDateOrderByNoAsc(date); - - if (!quizList.isEmpty()) { - List quizDTOList = quizList.stream() - .map(quiz -> mapper.map(quiz, QuizDTO.class)) - .collect(Collectors.toList()); - - for (int i = 0; i < quizList.size(); i++) { - Category category = categoryRepository.findById(quizList.get(i).getCategoryId()); - quizDTOList.get(i).setCategory(category); - } - return quizDTOList; - } - throw new NoSuchElementException("문제 정보를 불러올 수 없음"); - } - - @Transactional - public QuizDTO deleteQuizInListById(int id) { - - Quiz deleteQuiz = quizRepository.findByOriginQuizIdAndDate(id, nextDate); - - if (deleteQuiz != null) { - - quizRepository.deleteById(deleteQuiz.getId()); - List modifyQuizList = quizRepository - .findByDateAndNoGreaterThanOrderByNoAsc(nextDate, deleteQuiz.getNo()); - - for (Quiz modifyQuiz : modifyQuizList) { - modifyQuiz.setNo(modifyQuiz.getNo() - 1); - } - return mapper.map(deleteQuiz, QuizDTO.class); - } - throw new IllegalAccessError("문제 정보를 불러올 수 없음"); - } + QuizDTO deleteQuizInListById(int id); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java new file mode 100644 index 0000000..322f728 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java @@ -0,0 +1,131 @@ +package org.triumers.newsnippetback.Application.service; + +import jakarta.transaction.Transactional; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; + +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +@Service +public class ManageServiceImpl implements ManageService { + + private final QuizRepository quizRepository; + private final CrawlingQuizRepository crawlingQuizRepository; + private final CategoryRepository categoryRepository; + private final ModelMapper mapper; + + final static LocalDate nextDate = LocalDate.now().plusDays(1); + + @Autowired + public ManageServiceImpl(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, CategoryRepository categoryRepository, ModelMapper mapper) { + this.quizRepository = quizRepository; + this.crawlingQuizRepository = crawlingQuizRepository; + this.categoryRepository = categoryRepository; + this.mapper = mapper; + } + + public List selectCrawlingQuizListByDate(LocalDate date) { + + List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); + + if (!crawlingQuizList.isEmpty()) { + List crawlingQuizDTOList = crawlingQuizList.stream() + .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < crawlingQuizList.size(); i++) { + CrawlingQuiz crawlingQuiz = crawlingQuizList.get(i); + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); + crawlingQuizDTOList.get(i).setCategory(category); + + boolean isSelected = quizRepository.countByDateAndOriginQuizId + (nextDate, crawlingQuiz.getId()) > 0; + crawlingQuizDTOList.get(i).setSelected(isSelected); + } + return crawlingQuizDTOList; + } else { + // 이후에 크롤링 서버에 문제 생성 요청하기 + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); + } + } + + public CrawlingQuizDTO selectCrawlingQuizByID(int id) { + CrawlingQuiz crawlingQuiz = crawlingQuizRepository.findById(id).orElseThrow(IllegalAccessError::new); + + if (crawlingQuiz != null) { + CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); + + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()); + crawlingQuizDTO.setCategory(category); + + return crawlingQuizDTO; + } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + } + + @Transactional + public Quiz insertSelectedQuizById(int id) { + + CrawlingQuizDTO selectedQuiz = selectCrawlingQuizByID(id); + Quiz insertQuiz = mapper.map(selectedQuiz, Quiz.class); + + insertQuiz.setNo(getMaxNo() + 1); + insertQuiz.setDate(nextDate); + insertQuiz.setCategoryId(selectedQuiz.getCategory().getId()); + insertQuiz.setOriginQuizId(selectedQuiz.getId()); + + return quizRepository.save(insertQuiz); + } + + public int getMaxNo() { + return quizRepository.countByDate(nextDate); + } + + public List selectQuizListByDate(LocalDate date) { + List quizList = quizRepository.findByDateOrderByNoAsc(date); + + if (!quizList.isEmpty()) { + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()); + quizDTOList.get(i).setCategory(category); + } + return quizDTOList; + } + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); + } + + @Transactional + public QuizDTO deleteQuizInListById(int id) { + + Quiz deleteQuiz = quizRepository.findByOriginQuizIdAndDate(id, nextDate); + + if (deleteQuiz != null) { + + quizRepository.deleteById(deleteQuiz.getId()); + List modifyQuizList = quizRepository + .findByDateAndNoGreaterThanOrderByNoAsc(nextDate, deleteQuiz.getNo()); + + for (Quiz modifyQuiz : modifyQuizList) { + modifyQuiz.setNo(modifyQuiz.getNo() - 1); + } + return mapper.map(deleteQuiz, QuizDTO.class); + } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + } +} diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index cc5d2dc..fcd5798 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -20,7 +20,7 @@ @SpringBootTest class ManageServiceTest { - ManageService manageService; + private final ManageService manageService; @Autowired public ManageServiceTest(ManageService manageService) { @@ -29,11 +29,8 @@ public ManageServiceTest(ManageService manageService) { static Stream getDate() { return Stream.of( - LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 2), - LocalDate.of(2024, 4, 3), - LocalDate.of(2024, 4, 4), - LocalDate.of(2024, 4, 5) + LocalDate.of(2024, 4, 9) ); } From 626f54fd8d1e6ed0c32bab5c10ed5848e948b81d Mon Sep 17 00:00:00 2001 From: moomint8 Date: Wed, 10 Apr 2024 19:53:39 +0900 Subject: [PATCH 081/134] =?UTF-8?q?feat(gitignore):=20JWT=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=ED=95=84=ED=84=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 발급한 토큰에 대해서 검증하는 필터를 구현했습니다. --- .../controller/AdminController.java | 47 +++++++++++ .../service/CustomUserDetailsService.java | 2 +- .../Application/service/SignupService.java | 2 +- .../common/config/SecurityConfig.java | 6 +- .../InsufficientAuthorityException.java | 7 ++ .../newsnippetback/common/jwt/JWTFilter.java | 77 +++++++++++++++++++ .../newsnippetback/common/jwt/JWTUtil.java | 14 +++- .../common/jwt/LoginFilter.java | 18 +++-- .../domain/aggregate/enums/UserRole.java | 16 +++- .../domain/aggregate/vo/RequestLoginVO.java | 10 +++ .../domain/dto/CustomUserDetails.java | 4 + 11 files changed, 188 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/AdminController.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/InsufficientAuthorityException.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestLoginVO.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AdminController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AdminController.java new file mode 100644 index 0000000..0653e12 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AdminController.java @@ -0,0 +1,47 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.common.exception.InsufficientAuthorityException; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; + +import java.util.Collection; +import java.util.Iterator; + +@RestController +@RequestMapping("/admin") +public class AdminController { + + @GetMapping("/health-check") + public ResponseEntity healthCheck() { + try { + isAdmin(); + } catch (InsufficientAuthorityException e) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ResponseMessageVO(e.getMessage())); + } + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("Server is online...")); + } + + private void isAdmin() throws InsufficientAuthorityException { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Collection authorities = authentication.getAuthorities(); + Iterator iter = authorities.iterator(); + GrantedAuthority auth = iter.next(); + String role = auth.getAuthority(); + +// System.out.println("사용자 아이디: " + SecurityContextHolder.getContext().getAuthentication().getName()); +// System.out.println("사용자 ROLE: " + role); + + if (!role.equals(UserRole.ADMIN.name())) { + throw new InsufficientAuthorityException(); + } + } +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java index bcd9707..920bb68 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java @@ -29,6 +29,6 @@ public UserDetails loadUserByUsername(String email) throws UsernameNotFoundExcep return new CustomUserDetails(userData); } - return null; + throw new UsernameNotFoundException("존재하지 않는 유저입니다."); } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java index 7cd7aa0..55af67d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java @@ -47,7 +47,7 @@ private User userMapper(SignupDTO request) { user.setNickname(request.getNickname()); user.setEmail(request.getEmail()); user.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); - user.setUserRole(UserRole.ROLE_USER); + user.setUserRole(UserRole.USER); user.setProvider(request.getProvider()); user.setSnsId(request.getSnsId()); user.setUserStatus(UserStatus.ACTIVE); diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index 3f5f883..b0f4c3a 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -11,6 +11,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.triumers.newsnippetback.common.jwt.JWTFilter; import org.triumers.newsnippetback.common.jwt.JWTUtil; import org.triumers.newsnippetback.common.jwt.LoginFilter; @@ -54,9 +55,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti http.authorizeHttpRequests((auth) -> auth .requestMatchers("/auth/login", "/auth/signup").permitAll() .requestMatchers("/**").permitAll() - .requestMatchers("/admin").hasRole("ADMIN") +// .requestMatchers("/admin").hasRole("ADMIN") .anyRequest().authenticated()); + // JWT 필터 + http.addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class); + // 로그인 필터 http.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class); diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/InsufficientAuthorityException.java b/src/main/java/org/triumers/newsnippetback/common/exception/InsufficientAuthorityException.java new file mode 100644 index 0000000..dede17c --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/InsufficientAuthorityException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class InsufficientAuthorityException extends CustomException { + public InsufficientAuthorityException() { + super("접근 권한이 없습니다."); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java new file mode 100644 index 0000000..11ce021 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java @@ -0,0 +1,77 @@ +package org.triumers.newsnippetback.common.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.dto.CustomUserDetails; + +import java.io.IOException; + +public class JWTFilter extends OncePerRequestFilter { + + private final JWTUtil jwtUtil; + + public JWTFilter(JWTUtil jwtUtil) { + + this.jwtUtil = jwtUtil; + } + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException { + + //request에서 Authorization 헤더를 찾음 + String authorization= request.getHeader("Authorization"); + + //Authorization 헤더 검증 + if (authorization == null || !authorization.startsWith("Bearer ")) { + + System.out.println("token null"); + filterChain.doFilter(request, response); + + //조건이 해당되면 메소드 종료 (필수) + return; + } + + //Bearer 부분 제거 후 순수 토큰만 획득 + String token = authorization.split(" ")[1]; + + //토큰 소멸 시간 검증 + if (jwtUtil.isExpired(token)) { + + System.out.println("token expired"); + filterChain.doFilter(request, response); + + //조건이 해당되면 메소드 종료 (필수) + return; + } + + //토큰에서 username과 role 획득 + String email = jwtUtil.getEmail(token); + UserRole role = UserRole.USER.getUserRole(jwtUtil.getRole(token)); + + //userEntity를 생성하여 값 set + User userEntity = new User(); + userEntity.setEmail(email); + userEntity.setPassword("temppassword"); + userEntity.setUserRole(role); + + //UserDetails에 회원 정보 객체 담기 + CustomUserDetails customUserDetails = new CustomUserDetails(userEntity); + + //스프링 시큐리티 인증 토큰 생성 + Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities()); + + //세션에 사용자 등록 + SecurityContextHolder.getContext().setAuthentication(authToken); + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java index 6db8143..401932d 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java @@ -31,9 +31,14 @@ public String getEmail(String token) { return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("email", String.class); } - public UserRole getRole(String token) { + public String getRole(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", UserRole.class); + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); + } + + public String getNickname(String token) { + + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("nickname", String.class); } public Boolean isExpired(String token) { @@ -41,11 +46,12 @@ public Boolean isExpired(String token) { return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); } - public String createJwt(String username, UserRole role) { + public String createJwt(String email, UserRole role, String nickname) { return Jwts.builder() - .claim("username", username) + .claim("email", email) .claim("role", role) + .claim("nickname", nickname) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + expiredMs)) .signWith(secretKey) diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java index 6757a01..5c0fcfd 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java @@ -1,5 +1,6 @@ package org.triumers.newsnippetback.common.jwt; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -11,6 +12,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.vo.RequestLoginVO; import org.triumers.newsnippetback.domain.dto.CustomUserDetails; import java.io.IOException; @@ -30,12 +32,17 @@ public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { - String email = obtainUsername(request); - String password = obtainPassword(request); + try { + RequestLoginVO loginVO = new ObjectMapper().readValue(request.getInputStream(), RequestLoginVO.class); - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(email, password, null); + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(loginVO.getEmail(), loginVO.getPassword(), null); - return authenticationManager.authenticate(authToken); + return authenticationManager.authenticate(authToken); + + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -43,6 +50,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); String username = customUserDetails.getUsername(); + String nickname = customUserDetails.getNickname(); Collection authorities = authentication.getAuthorities(); Iterator iterator = authorities.iterator(); @@ -50,7 +58,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR UserRole role = UserRole.valueOf(auth.getAuthority()); - String token = jwtUtil.createJwt(username, role); + String token = jwtUtil.createJwt(username, role, nickname); response.addHeader("Authorization", "Bearer " + token); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java index d433924..9fa4e14 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/enums/UserRole.java @@ -1,7 +1,17 @@ package org.triumers.newsnippetback.domain.aggregate.enums; public enum UserRole { - ROLE_USER, - ROLE_MANAGER, - ROLE_ADMIN + USER, + MANAGER, + ADMIN; + + public UserRole getUserRole(String roleName) { + if (roleName.equals("USER")) { + return USER; + } + if (roleName.equals("MANAGER")) { + return MANAGER; + } + return ADMIN; + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestLoginVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestLoginVO.java new file mode 100644 index 0000000..9ca504b --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestLoginVO.java @@ -0,0 +1,10 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.Getter; +import lombok.ToString; + +@Getter +public class RequestLoginVO { + private String email; + private String password; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java index cc7e860..225e972 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java @@ -41,6 +41,10 @@ public String getUsername() { return user.getEmail(); } + public String getNickname() { + return user.getNickname(); + } + @Override public boolean isAccountNonExpired() { From 456adc3a95348ad6165e9c88f95363257376c64b Mon Sep 17 00:00:00 2001 From: moomint8 Date: Wed, 10 Apr 2024 23:54:30 +0900 Subject: [PATCH 082/134] =?UTF-8?q?feat(Auth):=20SpringSecurity=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=84=A4=EC=A0=95=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Controller 경로에 따른 권한 설정이 구현되었습니다. --- .../common/config/SecurityConfig.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index b0f4c3a..e577e94 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -2,6 +2,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -39,6 +41,17 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } + @Bean + public RoleHierarchy roleHierarchy() { + + RoleHierarchyImpl hierarchy = new RoleHierarchyImpl(); + + hierarchy.setHierarchy("ROLE_ADMIN > ROLE_MANAGER\n" + + "ROLE_MANAGER > ROLE_USER"); + + return hierarchy; + } + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -53,9 +66,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti //경로별 인가 작업 http.authorizeHttpRequests((auth) -> auth - .requestMatchers("/auth/login", "/auth/signup").permitAll() + .requestMatchers("/auth/signup").permitAll() .requestMatchers("/**").permitAll() -// .requestMatchers("/admin").hasRole("ADMIN") + .requestMatchers(("/manager")).hasAnyRole("MANAGER") + .requestMatchers("/admin").hasAnyRole("ADMIN") .anyRequest().authenticated()); // JWT 필터 From de1d568a525b3028c15ebff6a2a1194a87eec2f7 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 01:05:15 +0900 Subject: [PATCH 083/134] =?UTF-8?q?feat(Auth):=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=ED=99=95=EC=9D=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원가입, 회원 정보 변경 등에서 사용할 닉네임 중복 확인 기능이 구현되었습니다. --- build.gradle | 6 +-- .../controller/AuthController.java | 38 ++++++++++++------- .../{SignupService.java => AuthService.java} | 10 ++++- ...equestSignupVO.java => RequestUserVO.java} | 2 +- 4 files changed, 36 insertions(+), 20 deletions(-) rename src/main/java/org/triumers/newsnippetback/Application/service/{SignupService.java => AuthService.java} (89%) rename src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/{RequestSignupVO.java => RequestUserVO.java} (89%) diff --git a/build.gradle b/build.gradle index 9658f8a..c8f5c49 100644 --- a/build.gradle +++ b/build.gradle @@ -33,9 +33,9 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' // SpringSecurity 라이브러리 -// implementation 'org.springframework.boot:spring-boot-starter-security' -// testImplementation 'org.springframework.security:spring-security-test' -// implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' // JWT implementation 'io.jsonwebtoken:jjwt-api:0.12.3' diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index 421c549..6c0ce9f 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -7,11 +7,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.triumers.newsnippetback.Application.service.SignupService; +import org.triumers.newsnippetback.Application.service.AuthService; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; -import org.triumers.newsnippetback.domain.aggregate.vo.RequestSignupVO; +import org.triumers.newsnippetback.domain.aggregate.vo.RequestUserVO; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; import org.triumers.newsnippetback.domain.dto.SignupDTO; @@ -19,17 +19,15 @@ @RequestMapping("/auth") public class AuthController { - private final SignupService signupService; + private final AuthService authService; @Autowired - public AuthController(SignupService signupService) { - this.signupService = signupService; + public AuthController(AuthService authService) { + this.authService = authService; } @PostMapping("/signup") - public ResponseEntity signup(@RequestBody RequestSignupVO request) { - - ResponseMessageVO response = new ResponseMessageVO(); + public ResponseEntity signup(@RequestBody RequestUserVO request) { SignupDTO user = new SignupDTO(); @@ -40,15 +38,27 @@ public ResponseEntity signup(@RequestBody RequestSignupVO req user.setProvider(Provider.LOCAL); try { - signupService.signup(user); - - response.setMessage(user.getNickname() + "님 회원가입에 성공했습니다."); + authService.signup(user); } catch (UserNicknameDuplicateException | UserEmailDuplicateException e) { - response.setMessage(e.getMessage()); - ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO(e.getMessage())); } - return ResponseEntity.status(HttpStatus.OK).body(response); + return ResponseEntity.status(HttpStatus.OK).body( + new ResponseMessageVO(user.getNickname() + "님 회원가입에 성공했습니다.")); } + + @PostMapping("/exist/nickname") + public ResponseEntity existNickname(@RequestBody RequestUserVO request) { + + if (authService.existNickname(request.getNickname())) { + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("이미 존재하는 닉네임입니다.")); + } + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 닉네임입니다.")); + } + + } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java similarity index 89% rename from src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java rename to src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 55af67d..63de8c3 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SignupService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -12,13 +12,13 @@ import org.triumers.newsnippetback.domain.repository.UserRepository; @Service -public class SignupService { +public class AuthService { private final UserRepository userRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired - public SignupService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { + public AuthService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userRepository = userRepository; this.bCryptPasswordEncoder = bCryptPasswordEncoder; } @@ -40,6 +40,12 @@ public void signup(SignupDTO request) throws UserNicknameDuplicateException, Use userRepository.save(user); } + public boolean existNickname(String nickname) { + return userRepository.existsByNickname(nickname); + } + + + private User userMapper(SignupDTO request) { User user = new User(); diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestUserVO.java similarity index 89% rename from src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java rename to src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestUserVO.java index 56eb667..c2264c2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestSignupVO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestUserVO.java @@ -7,7 +7,7 @@ @NoArgsConstructor @AllArgsConstructor @ToString -public class RequestSignupVO { +public class RequestUserVO { private String name; private String nickname; private String email; From 76295f256317d4a1aea5ffe747972f105783aba8 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 01:05:46 +0900 Subject: [PATCH 084/134] =?UTF-8?q?feat(Auth):=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EC=A4=91=EB=B3=B5=20=ED=99=95=EC=9D=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원가입, 회원 정보 변경 등에서 사용할 이메일 중복 확인 기능이 구현되었습니다. --- .../Application/controller/AuthController.java | 9 +++++++++ .../newsnippetback/Application/service/AuthService.java | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index 6c0ce9f..1357985 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -60,5 +60,14 @@ public ResponseEntity existNickname(@RequestBody RequestUserV return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 닉네임입니다.")); } + @PostMapping("/exist/email") + public ResponseEntity existEmail(@RequestBody RequestUserVO request) { + if (authService.existEmail(request.getEmail())) { + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("이미 존재하는 이메일입니다.")); + } + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 이메일입니다.")); + } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 63de8c3..1838cd3 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -44,7 +44,9 @@ public boolean existNickname(String nickname) { return userRepository.existsByNickname(nickname); } - + public boolean existEmail(String email) { + return userRepository.existsByEmail(email); + } private User userMapper(SignupDTO request) { User user = new User(); From 73282102b6442f79bf87a4f9aa34231376f9ca35 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 14:45:28 +0900 Subject: [PATCH 085/134] =?UTF-8?q?rename(SignupDTO):=20AuthDTO=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기능에 맞는 더 적합한 이름으로 클래스명을 수정했습니다. --- .../newsnippetback/domain/dto/{SignupDTO.java => AuthDTO.java} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename src/main/java/org/triumers/newsnippetback/domain/dto/{SignupDTO.java => AuthDTO.java} (90%) diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java similarity index 90% rename from src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java rename to src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java index f1bcda7..f10bca2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/SignupDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java @@ -1,6 +1,5 @@ package org.triumers.newsnippetback.domain.dto; -import jakarta.persistence.Column; import lombok.*; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; @@ -11,7 +10,7 @@ @NoArgsConstructor @AllArgsConstructor @ToString -public class SignupDTO { +public class AuthDTO { private String name; private String nickname; From e45135a92a494ba91ffa460ddb691754fc8cb63a Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 14:46:53 +0900 Subject: [PATCH 086/134] =?UTF-8?q?refactor(SignupDTO):=20AuthDTO=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=B0=B8=EC=A1=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/controller/AuthController.java | 4 ++-- .../newsnippetback/Application/service/AuthService.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index 1357985..639a2da 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -13,7 +13,7 @@ import org.triumers.newsnippetback.domain.aggregate.enums.Provider; import org.triumers.newsnippetback.domain.aggregate.vo.RequestUserVO; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; -import org.triumers.newsnippetback.domain.dto.SignupDTO; +import org.triumers.newsnippetback.domain.dto.AuthDTO; @RestController @RequestMapping("/auth") @@ -29,7 +29,7 @@ public AuthController(AuthService authService) { @PostMapping("/signup") public ResponseEntity signup(@RequestBody RequestUserVO request) { - SignupDTO user = new SignupDTO(); + AuthDTO user = new AuthDTO(); user.setName(request.getName()); user.setNickname(request.getNickname()); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 1838cd3..22ca0d0 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -8,7 +8,7 @@ import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; -import org.triumers.newsnippetback.domain.dto.SignupDTO; +import org.triumers.newsnippetback.domain.dto.AuthDTO; import org.triumers.newsnippetback.domain.repository.UserRepository; @Service @@ -23,7 +23,7 @@ public AuthService(UserRepository userRepository, BCryptPasswordEncoder bCryptPa this.bCryptPasswordEncoder = bCryptPasswordEncoder; } - public void signup(SignupDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { + public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { // 닉네임 중복 예외 처리 if (userRepository.existsByNickname(request.getNickname())) { @@ -48,7 +48,7 @@ public boolean existEmail(String email) { return userRepository.existsByEmail(email); } - private User userMapper(SignupDTO request) { + private User userMapper(AuthDTO request) { User user = new User(); user.setName(request.getName()); From b46b563409b375f72759c55eacb70070e10bc3a0 Mon Sep 17 00:00:00 2001 From: B Date: Thu, 11 Apr 2024 15:31:42 +0900 Subject: [PATCH 087/134] =?UTF-8?q?feat(Quiz):=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EB=A7=9E=EC=B6=B0=EC=84=9C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 56 ++++++++--- .../Application/service/QuizService.java | 9 +- .../Application/service/QuizServiceImpl.java | 57 +++++++++-- .../domain/repository/QuizRepository.java | 4 + .../service/QuizServiceImplTests.java | 98 +++++++++---------- 5 files changed, 153 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 6e13f71..7bcc052 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -9,7 +9,11 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -25,20 +29,23 @@ public QuizController(QuizService quizService) { /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ @PostMapping("/test") - public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { + public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { try { - QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); - - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setContent(quizDTO.getContent()); - quizResponse.setOptionA(quizDTO.getOptionA()); - quizResponse.setOptionB(quizDTO.getOptionB()); - quizResponse.setOptionC(quizDTO.getOptionC()); - quizResponse.setOptionD(quizDTO.getOptionD()); - quizResponse.setCategoryName(quizDTO.getCategoryName()); - quizResponse.setCorrectRate(quizDTO.getCorrectRate()); - - return ResponseEntity.ok().body(quizResponse); + LocalDate date = quizRequest.getDate(); + List quizDTOs = quizService.findAllQuizzesByDate(date); + List quizResponses = new ArrayList<>(); + for (QuizDTO quizDTO : quizDTOs) { + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + quizResponses.add(quizResponse); + } + return ResponseEntity.ok().body(quizResponses); } catch (NoSuchElementException e) { return ResponseEntity.notFound().build(); } catch (Exception e) { @@ -49,12 +56,33 @@ public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// @PostMapping("/answer") +// public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); +// +// QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setAnswer(quizDTO.getAnswer()); +// quizResponse.setExplanation(quizDTO.getExplanation()); +// quizResponse.setNewsLink(quizDTO.getNewsLink()); +// +// return ResponseEntity.ok().body(quizResponse); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } + @PostMapping("/answer") public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { try { - QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); QuizResponse quizResponse = new QuizResponse(); + quizResponse.setNo(quizDTO.getNo()); quizResponse.setAnswer(quizDTO.getAnswer()); quizResponse.setExplanation(quizDTO.getExplanation()); quizResponse.setNewsLink(quizDTO.getNewsLink()); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 83f1369..5df5525 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -3,9 +3,16 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; +import java.util.List; + public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); - QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); +// QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); + + List findAllQuizzesByDate(LocalDate date); + + QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index cba1c36..79ea60d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -11,7 +11,10 @@ import org.triumers.newsnippetback.domain.repository.QuizRepository; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; @Service public class QuizServiceImpl implements QuizService { @@ -60,6 +63,32 @@ public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { return quizDTO; } + public List findAllQuizzesByDate(LocalDate date) { + List quizzes = quizRepository.findAllByDate(date); + List quizDTOs = new ArrayList<>(); + for (Quiz quiz : quizzes) { + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + + int categoryId = quiz.getCategoryId(); + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); + } + quizDTO.setCategoryName(category.getCategoryName()); + + double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); + quizDTO.setCorrectRate(correctRate); + + quizDTOs.add(quizDTO); + } + return quizDTOs; + } + // 정답률 계산 메서드 private double calculateCorrectRate(int correctCnt, int solvedCnt) { if (solvedCnt == 0) { @@ -70,22 +99,36 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { // 2 - @Override - public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { - LocalDate date = quizRequest.getDate(); - int no = quizRequest.getNo(); +// @Override +// public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// +// Quiz quiz = quizRepository.findByDateAndNo(date, no); +// +// if (quiz == null) { +// throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); +// } +// +// QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setAnswer(quiz.getAnswer()); +// quizDTO.setExplanation(quiz.getExplanation()); +// quizDTO.setNewsLink(quiz.getNewsLink()); +// +// return quizDTO; +// } + @Override + public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { Quiz quiz = quizRepository.findByDateAndNo(date, no); - if (quiz == null) { throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); } - QuizDTO quizDTO = new QuizDTO(); + quizDTO.setNo(quiz.getNo()); quizDTO.setAnswer(quiz.getAnswer()); quizDTO.setExplanation(quiz.getExplanation()); quizDTO.setNewsLink(quiz.getNewsLink()); - return quizDTO; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 12cc39a..f3607fa 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -24,4 +24,8 @@ public interface QuizRepository extends JpaRepository { Integer countByDateAndOriginQuizId(LocalDate localDate, int id); Quiz findByOriginQuizIdAndDate(int id, LocalDate date); + + List findAllByDate(LocalDate date); + + List findByDateAndNoIn(LocalDate date, List nos); } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java index 8d0fa1d..ec2fc32 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java @@ -102,53 +102,53 @@ void findQuizWithNonExistingCategory() { }); } - @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") - @Test - void findQuizAnswerByDateAndNo_FirstQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 1; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("C", quizDTO.getAnswer()); - Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); - Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); - } - - @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") - @Test - void findQuizAnswerByDateAndNo_SecondQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 2; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("B", quizDTO.getAnswer()); - Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); - Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); - } - - @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") - @Test - void findAnswerOfNonExistingQuiz() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 999; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When & Then - Assertions.assertThrows(NoSuchElementException.class, () -> { - quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - }); - } +// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") +// @Test +// void findQuizAnswerByDateAndNo_FirstQuestion() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 1; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When +// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// +// // Then +// Assertions.assertNotNull(quizDTO); +// Assertions.assertEquals("C", quizDTO.getAnswer()); +// Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); +// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); +// } + +// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") +// @Test +// void findQuizAnswerByDateAndNo_SecondQuestion() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 2; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When +// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// +// // Then +// Assertions.assertNotNull(quizDTO); +// Assertions.assertEquals("B", quizDTO.getAnswer()); +// Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); +// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); +// } + +// @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") +// @Test +// void findAnswerOfNonExistingQuiz() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 999; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When & Then +// Assertions.assertThrows(NoSuchElementException.class, () -> { +// quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// }); +// } } From 0ef3c66ee8b348cb88b1a18899461eab8f94b148 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 15:42:58 +0900 Subject: [PATCH 088/134] =?UTF-8?q?feat(User):=20Email=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=9C=A0=EC=A0=80=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit email을 이용해 유저를 조회하는 기능이 구현되었습니다. --- .../controller/UserController.java | 69 +++++++++++++++++++ .../Application/service/UserService.java | 14 ++++ .../Application/service/UserServiceImpl.java | 47 +++++++++++++ .../exception/UserNotFoundException.java | 7 ++ .../aggregate/vo/ResponseUserInfoVO.java | 20 ++++++ .../newsnippetback/domain/dto/UserDTO.java | 23 +++++++ .../domain/repository/UserRepository.java | 5 ++ 7 files changed, 185 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/UserService.java create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/UserNotFoundException.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseUserInfoVO.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java new file mode 100644 index 0000000..3609da9 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -0,0 +1,69 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.triumers.newsnippetback.Application.service.UserService; +import org.triumers.newsnippetback.common.exception.UserNotFoundException; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; +import org.triumers.newsnippetback.domain.aggregate.vo.ResponseUserInfoVO; +import org.triumers.newsnippetback.domain.dto.UserDTO; + +@RestController +@RequestMapping("/user") +public class UserController { + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @GetMapping("/email/{email}") + public ResponseEntity findUserByEmail(@PathVariable("email") String email) { + + try { + UserDTO userDTO = userService.findUserByEmail(email); + return ResponseEntity.status(HttpStatus.OK).body(userDTOToUserInfoVO(userDTO)); + + } catch (UserNotFoundException e) { + ResponseUserInfoVO response = new ResponseUserInfoVO(); + response.setMessage(e.getMessage()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + + private ResponseUserInfoVO userDTOToUserInfoVO(UserDTO user) { + return new ResponseUserInfoVO("조회 성공", user.getName(), user.getNickname(), user.getEmail(), + userRoleToString(user.getUserRole()), userStatusToString(user.getUserStatus()), user.getSolvedCnt(), + user.getCorrectCnt(), user.getRank()); + } + + private String userRoleToString(UserRole userRole) { + if (userRole.equals(UserRole.USER)) { + return "일반 회원"; + } + if (userRole.equals(UserRole.MANAGER)) { + return "운영자"; + } + return "관리자"; + } + + private String userStatusToString(UserStatus userStatus) { + if (userStatus.equals(UserStatus.ACTIVE)) { + return "활성 회원"; + } + if (userStatus.equals(UserStatus.BLOCKED)) { + return "차단 회원"; + } + if (userStatus.equals(UserStatus.DORMANT)) { + return "휴면 회원"; + } + return "탈퇴 회원"; + } +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java new file mode 100644 index 0000000..00bf638 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java @@ -0,0 +1,14 @@ +package org.triumers.newsnippetback.Application.service; + +import org.triumers.newsnippetback.common.exception.UserNotFoundException; +import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.dto.UserDTO; + +public interface UserService { + + UserDTO findUserByEmail(String email) throws UserNotFoundException; + + UserDTO findUserById(int id); + + UserDTO findUserByNickname(String nickname); +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java new file mode 100644 index 0000000..54625d0 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java @@ -0,0 +1,47 @@ +package org.triumers.newsnippetback.Application.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.common.exception.UserNotFoundException; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.domain.repository.UserRepository; + +@Service +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + + @Autowired + public UserServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDTO findUserByEmail(String email) throws UserNotFoundException { + User user = userRepository.findByEmail(email); + + return userToUserDTO(user); + } + + @Override + public UserDTO findUserById(int id) { + return null; + } + + @Override + public UserDTO findUserByNickname(String nickname) { + return null; + } + + private UserDTO userToUserDTO(User user) throws UserNotFoundException { + if (user == null) { + throw new UserNotFoundException(); + } + + int rank = userRepository.findRankByCorrectCnt(user.getCorrectCnt()); + + return new UserDTO(user.getName(), user.getNickname(), user.getEmail(), user.getUserRole(), + user.getUserStatus(), user.getSolvedCnt(), user.getCorrectCnt(), rank); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/UserNotFoundException.java b/src/main/java/org/triumers/newsnippetback/common/exception/UserNotFoundException.java new file mode 100644 index 0000000..ccdf270 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/UserNotFoundException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class UserNotFoundException extends CustomException { + public UserNotFoundException() { + super("존재하지 않는 유저입니다."); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseUserInfoVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseUserInfoVO.java new file mode 100644 index 0000000..a479bd5 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/ResponseUserInfoVO.java @@ -0,0 +1,20 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class ResponseUserInfoVO { + private String message; + private String name; + private String nickname; + private String email; + private String userRole; + private String userStatus; + private int solvedCnt; + private int correctCnt; + private int rank; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java new file mode 100644 index 0000000..893198d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java @@ -0,0 +1,23 @@ +package org.triumers.newsnippetback.domain.dto; + +import lombok.*; +import org.triumers.newsnippetback.domain.aggregate.enums.Provider; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class UserDTO { + + private String name; + private String nickname; + private String email; + private UserRole userRole; + private UserStatus userStatus; + private int solvedCnt; + private int correctCnt; + private int rank; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java index 481bc5e..82f8cc8 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java @@ -1,6 +1,8 @@ package org.triumers.newsnippetback.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.triumers.newsnippetback.domain.aggregate.entity.User; public interface UserRepository extends JpaRepository { @@ -10,4 +12,7 @@ public interface UserRepository extends JpaRepository { Boolean existsByEmail(String email); User findByEmail(String email); + + @Query("SELECT COUNT(u) + 1 FROM User u WHERE u.correctCnt > :correctCnt") + int findRankByCorrectCnt(@Param("correctCnt") int correctCnt); } From 380723a39ae59a045f8bcfbfcd8fee949d7418be Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 16:04:27 +0900 Subject: [PATCH 089/134] =?UTF-8?q?feat(User):=20id=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit id를 이용해 유저를 조회하는 기능이 구현되었습니다. --- .../Application/controller/UserController.java | 15 +++++++++++++++ .../Application/service/UserService.java | 2 +- .../Application/service/UserServiceImpl.java | 6 ++++-- .../domain/repository/UserRepository.java | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java index 3609da9..d3d7770 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -38,6 +38,21 @@ public ResponseEntity findUserByEmail(@PathVariable("email") } } + @GetMapping("/id/{id}") + public ResponseEntity findUserById(@PathVariable("id") int id) { + + try { + UserDTO userDTO = userService.findUserById(id); + return ResponseEntity.status(HttpStatus.OK).body(userDTOToUserInfoVO(userDTO)); + + } catch (UserNotFoundException e) { + ResponseUserInfoVO response = new ResponseUserInfoVO(); + response.setMessage(e.getMessage()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + private ResponseUserInfoVO userDTOToUserInfoVO(UserDTO user) { return new ResponseUserInfoVO("조회 성공", user.getName(), user.getNickname(), user.getEmail(), userRoleToString(user.getUserRole()), userStatusToString(user.getUserStatus()), user.getSolvedCnt(), diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java index 00bf638..4f99df4 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java @@ -8,7 +8,7 @@ public interface UserService { UserDTO findUserByEmail(String email) throws UserNotFoundException; - UserDTO findUserById(int id); + UserDTO findUserById(int id) throws UserNotFoundException; UserDTO findUserByNickname(String nickname); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java index 54625d0..b7ec827 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java @@ -25,8 +25,10 @@ public UserDTO findUserByEmail(String email) throws UserNotFoundException { } @Override - public UserDTO findUserById(int id) { - return null; + public UserDTO findUserById(int id) throws UserNotFoundException { + User user = userRepository.findById(id); + + return userToUserDTO(user); } @Override diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java index 82f8cc8..0995aa8 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java @@ -11,6 +11,8 @@ public interface UserRepository extends JpaRepository { Boolean existsByEmail(String email); + User findById(int id); + User findByEmail(String email); @Query("SELECT COUNT(u) + 1 FROM User u WHERE u.correctCnt > :correctCnt") From 51ba99de76a28d62f7301429c199ed3fdbbc61c3 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 16:11:04 +0900 Subject: [PATCH 090/134] =?UTF-8?q?feat(User):=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=9D=B4=EC=9A=A9=20=EC=9C=A0=EC=A0=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 닉네임을 이용해 유저를 조회하는 기능이 구현되었습니다. --- .../Application/controller/UserController.java | 15 +++++++++++++++ .../Application/service/UserService.java | 2 +- .../Application/service/UserServiceImpl.java | 6 ++++-- .../domain/repository/UserRepository.java | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java index d3d7770..123dbe2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -53,6 +53,21 @@ public ResponseEntity findUserById(@PathVariable("id") int i } } + @GetMapping("/nickname/{nickname}") + public ResponseEntity findUserByNickname(@PathVariable("nickname") String nickname) { + System.out.println("nickname = " + nickname); + try { + UserDTO userDTO = userService.findUserByNickname(nickname); + return ResponseEntity.status(HttpStatus.OK).body(userDTOToUserInfoVO(userDTO)); + + } catch (UserNotFoundException e) { + ResponseUserInfoVO response = new ResponseUserInfoVO(); + response.setMessage(e.getMessage()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + private ResponseUserInfoVO userDTOToUserInfoVO(UserDTO user) { return new ResponseUserInfoVO("조회 성공", user.getName(), user.getNickname(), user.getEmail(), userRoleToString(user.getUserRole()), userStatusToString(user.getUserStatus()), user.getSolvedCnt(), diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java index 4f99df4..0f4fd9c 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java @@ -10,5 +10,5 @@ public interface UserService { UserDTO findUserById(int id) throws UserNotFoundException; - UserDTO findUserByNickname(String nickname); + UserDTO findUserByNickname(String nickname) throws UserNotFoundException; } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java index b7ec827..2bbeb5f 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java @@ -32,8 +32,10 @@ public UserDTO findUserById(int id) throws UserNotFoundException { } @Override - public UserDTO findUserByNickname(String nickname) { - return null; + public UserDTO findUserByNickname(String nickname) throws UserNotFoundException { + User user = userRepository.findByNickname(nickname); + + return userToUserDTO(user); } private UserDTO userToUserDTO(User user) throws UserNotFoundException { diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java index 0995aa8..4927cb1 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/UserRepository.java @@ -15,6 +15,8 @@ public interface UserRepository extends JpaRepository { User findByEmail(String email); + User findByNickname(String nickname); + @Query("SELECT COUNT(u) + 1 FROM User u WHERE u.correctCnt > :correctCnt") int findRankByCorrectCnt(@Param("correctCnt") int correctCnt); } From 41b3264b442abeabe56d33c2b9ea8e69c360fcc4 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 11 Apr 2024 16:41:44 +0900 Subject: [PATCH 091/134] =?UTF-8?q?feat(SolvedQuiz)=20:=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EB=B3=84=20=ED=92=80=EC=97=88=EB=8D=98=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EC=B6=94=EA=B0=80=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EC=99=80=20=EC=9C=A0=EC=A0=80=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=EC=97=90=20=EB=94=B0=EB=9D=BC=20=ED=92=80=EC=97=88?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SolvedController.java | 18 ++++++++++ .../Application/service/SolvedService.java | 2 ++ .../service/SolvedServiceImpl.java | 35 +++++++++++++++++++ .../aggregate/vo/SolvedQuizResponse.java | 1 + .../domain/aggregate/vo/SolvedRequest.java | 3 ++ .../domain/repository/SolvedRepository.java | 2 ++ 6 files changed, 61 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index e62170c..d26bc69 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -112,4 +112,22 @@ public ResponseEntity> findCorrectQuizByUserIdAndSolv return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } + + @PostMapping("/find/allByDate") + public ResponseEntity> findSolvedQuizByUserIdAndDate(@RequestBody SolvedRequest solvedRequest){ + try { + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + List solvedList = solvedService.findSolvedQuizListByUserIdAndDate(solvedRequest); + List SolvedQuizListResponse = solvedList.stream() + .map(dot -> modelMapper.map(dot, SolvedQuizResponse.class)) + .collect(Collectors.toList()); + + return ResponseEntity.ok().body(SolvedQuizListResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index c0dece6..143f703 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -15,4 +15,6 @@ public interface SolvedService { List findSolvedQuizListByUserId(SolvedRequest solvedRequest); List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest); + + List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index e46628d..4beb54b 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -128,4 +128,39 @@ public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest sol return solvedList; } + @Override + public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest) { + System.out.println(solvedRequest); + int userId = solvedRequest.getUserId(); + LocalDate solvedDate = solvedRequest.getDate(); + + List solvedList = solvedRepository.findSolvedQuizByUserIdAndSolvedDate(userId, solvedDate); + List solvedDTOList = new ArrayList<>(); + + for (Solved solved: solvedList) { + int id = solved.getQuizId(); + Quiz quiz = quizRepository.findById(id).orElseThrow(); + + SolvedDTO solvedDTO = new SolvedDTO(); + + solvedDTO.setUserId(solved.getUserId()); + solvedDTO.setQuizId(quiz.getId()); + solvedDTO.setCategoryId(quiz.getCategoryId()); + solvedDTO.setContent(quiz.getContent()); + solvedDTO.setOptionA(quiz.getOptionA()); + solvedDTO.setOptionB(quiz.getOptionB()); + solvedDTO.setOptionC(quiz.getOptionC()); + solvedDTO.setOptionD(quiz.getOptionD()); + solvedDTO.setAnswer(quiz.getAnswer()); + solvedDTO.setSelectedOption(solved.getSelectedOption()); + solvedDTO.setExplanation(quiz.getExplanation()); + solvedDTO.setNewsLink(quiz.getNewsLink()); + solvedDTO.setDate(quiz.getDate()); + + solvedDTOList.add(solvedDTO); + } + + return solvedDTOList; + } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java index 8bffcdc..d52dce2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java @@ -12,6 +12,7 @@ public class SolvedQuizResponse { private int userId; private int quizId; + private String selectedOption; private int categoryId; private String content; private String optionA; diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java index e2ba736..4047bda 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -2,6 +2,8 @@ import lombok.*; +import java.time.LocalDate; + @NoArgsConstructor @AllArgsConstructor @Getter @@ -10,4 +12,5 @@ public class SolvedRequest { private int userId; private int quizId; + private LocalDate date; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java index a14c43f..4b65d75 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -14,4 +14,6 @@ public interface SolvedRepository extends JpaRepository { List findIdByUserIdAndIsCorrectAndSolvedDate(int userId, boolean isCorrect, LocalDate solvedDate); List findSolvedQuizByUserId(int userId); + + List findSolvedQuizByUserIdAndSolvedDate(int userId, LocalDate solvedDate); } From dca20c43c50854293d077a7d46dad54c7dc37abb Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 17:12:28 +0900 Subject: [PATCH 092/134] =?UTF-8?q?feat(User):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=20=EC=9C=A0=EC=A0=80=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 토큰을 이용해 유저를 조회하는 기능이 구현되었습니다. --- .../Application/controller/UserController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java index 123dbe2..e782c55 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback.Application.controller; +import io.jsonwebtoken.ExpiredJwtException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -68,6 +70,20 @@ public ResponseEntity findUserByNickname(@PathVariable("nick } } + @GetMapping("/my-page") + public ResponseEntity myPage() { + try { + String email = SecurityContextHolder.getContext().getAuthentication().getName(); + + return findUserByEmail(email); + } catch (ExpiredJwtException e) { + ResponseUserInfoVO response = new ResponseUserInfoVO(); + response.setMessage("[ERROR] 로그인 이후 이용해주십시오."); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + private ResponseUserInfoVO userDTOToUserInfoVO(UserDTO user) { return new ResponseUserInfoVO("조회 성공", user.getName(), user.getNickname(), user.getEmail(), userRoleToString(user.getUserRole()), userStatusToString(user.getUserStatus()), user.getSolvedCnt(), From db2a6a67703074f3a3dc91a07d1c190892599922 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 11 Apr 2024 17:27:01 +0900 Subject: [PATCH 093/134] =?UTF-8?q?fix(solvedQuiz)=20:=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EB=90=9C=20=EC=98=B5=EC=85=98=EA=B0=92=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/controller/SolvedController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index d26bc69..04e2929 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -82,6 +82,7 @@ public ResponseEntity findSolvedQuizByUserIdAndQuizId(@Reque solvedQuizResponse.setOptionC(solvedDTO.getOptionC()); solvedQuizResponse.setOptionD(solvedDTO.getOptionD()); solvedQuizResponse.setAnswer(solvedDTO.getAnswer()); + solvedQuizResponse.setSelectedOption(solvedDTO.getSelectedOption()); solvedQuizResponse.setExplanation(solvedDTO.getExplanation()); solvedQuizResponse.setNewsLink(solvedDTO.getNewsLink()); solvedQuizResponse.setDate(solvedDTO.getDate()); From 14c81fb6ecb4601796a7614dcfe0a138ee4ee9bb Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 19:06:52 +0900 Subject: [PATCH 094/134] =?UTF-8?q?refactor(JWT):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=20=EC=8B=9C=20500=20Error=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 현재는 try/catch를 이용해 임의로 처리되어 토큰 없이 진행되는 방식으로 구현되어있습니다. 추후 Filter와 Handler를 이용해 제대로 된 처리가 이루어질 예정입니다. --- .../org/triumers/newsnippetback/common/jwt/JWTFilter.java | 6 +++++- .../org/triumers/newsnippetback/common/jwt/JWTUtil.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java index 11ce021..cfddd63 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java @@ -1,12 +1,16 @@ package org.triumers.newsnippetback.common.jwt; +import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.ErrorResponse; +import org.springframework.web.ErrorResponseException; import org.springframework.web.filter.OncePerRequestFilter; import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; @@ -25,7 +29,7 @@ public JWTFilter(JWTUtil jwtUtil) { @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //request에서 Authorization 헤더를 찾음 String authorization= request.getHeader("Authorization"); diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java index 401932d..7a3ec97 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java @@ -1,5 +1,6 @@ package org.triumers.newsnippetback.common.jwt; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; @@ -43,7 +44,11 @@ public String getNickname(String token) { public Boolean isExpired(String token) { - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + try { + return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date()); + } catch (ExpiredJwtException e) { + return true; + } } public String createJwt(String email, UserRole role, String nickname) { From 533ccd0ba9e5fb78fff20de23968af0d2947944e Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 19:19:02 +0900 Subject: [PATCH 095/134] =?UTF-8?q?refactor(User):=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=20=ED=9A=8C=EC=9B=90=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 토큰을 이용해 회원을 조회할 때 처리를 Controller이 아닌 Service가 하도록 개선했습니다. --- .../Application/controller/UserController.java | 10 +++++++--- .../Application/service/UserService.java | 3 ++- .../Application/service/UserServiceImpl.java | 9 +++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java index e782c55..c678032 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -3,7 +3,6 @@ import io.jsonwebtoken.ExpiredJwtException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -73,13 +72,18 @@ public ResponseEntity findUserByNickname(@PathVariable("nick @GetMapping("/my-page") public ResponseEntity myPage() { try { - String email = SecurityContextHolder.getContext().getAuthentication().getName(); + UserDTO userDTO = userService.findByToken(); + return ResponseEntity.status(HttpStatus.OK).body(userDTOToUserInfoVO(userDTO)); - return findUserByEmail(email); } catch (ExpiredJwtException e) { ResponseUserInfoVO response = new ResponseUserInfoVO(); response.setMessage("[ERROR] 로그인 이후 이용해주십시오."); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } catch (UserNotFoundException e) { + ResponseUserInfoVO response = new ResponseUserInfoVO(); + response.setMessage(e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java index 0f4fd9c..57c8f53 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java @@ -1,7 +1,6 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.common.exception.UserNotFoundException; -import org.triumers.newsnippetback.domain.dto.AuthDTO; import org.triumers.newsnippetback.domain.dto.UserDTO; public interface UserService { @@ -11,4 +10,6 @@ public interface UserService { UserDTO findUserById(int id) throws UserNotFoundException; UserDTO findUserByNickname(String nickname) throws UserNotFoundException; + + UserDTO findByToken() throws UserNotFoundException; } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java index 2bbeb5f..88be50e 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.Application.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserNotFoundException; import org.triumers.newsnippetback.domain.aggregate.entity.User; @@ -38,6 +39,14 @@ public UserDTO findUserByNickname(String nickname) throws UserNotFoundException return userToUserDTO(user); } + @Override + public UserDTO findByToken() throws UserNotFoundException { + + String email = SecurityContextHolder.getContext().getAuthentication().getName(); + + return findUserByEmail(email); + } + private UserDTO userToUserDTO(User user) throws UserNotFoundException { if (user == null) { throw new UserNotFoundException(); From 0eb8238461ce1b7acf34342848882014253b3148 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 19:25:21 +0900 Subject: [PATCH 096/134] =?UTF-8?q?refactor(Auth):=20Auth=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20Interface=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 객체지향하도록 Interface로 Controller가 접근하게 개선되었습니다. --- .../Application/service/AuthService.java | 59 ++--------------- .../Application/service/AuthServiceImpl.java | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+), 55 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 22ca0d0..36ac161 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -1,65 +1,14 @@ package org.triumers.newsnippetback.Application.service; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; -import org.triumers.newsnippetback.domain.aggregate.entity.User; -import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; -import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; import org.triumers.newsnippetback.domain.dto.AuthDTO; -import org.triumers.newsnippetback.domain.repository.UserRepository; -@Service -public class AuthService { +public interface AuthService { - private final UserRepository userRepository; - private final BCryptPasswordEncoder bCryptPasswordEncoder; + public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException; - @Autowired - public AuthService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { - this.userRepository = userRepository; - this.bCryptPasswordEncoder = bCryptPasswordEncoder; - } + public boolean existNickname(String nickname); - public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { - - // 닉네임 중복 예외 처리 - if (userRepository.existsByNickname(request.getNickname())) { - throw new UserNicknameDuplicateException(); - } - - // 이메일 중복 예외 처리 - if (userRepository.existsByEmail(request.getEmail())) { - throw new UserEmailDuplicateException(); - } - - User user = userMapper(request); - - userRepository.save(user); - } - - public boolean existNickname(String nickname) { - return userRepository.existsByNickname(nickname); - } - - public boolean existEmail(String email) { - return userRepository.existsByEmail(email); - } - - private User userMapper(AuthDTO request) { - User user = new User(); - - user.setName(request.getName()); - user.setNickname(request.getNickname()); - user.setEmail(request.getEmail()); - user.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); - user.setUserRole(UserRole.USER); - user.setProvider(request.getProvider()); - user.setSnsId(request.getSnsId()); - user.setUserStatus(UserStatus.ACTIVE); - - return user; - } + public boolean existEmail(String email); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java new file mode 100644 index 0000000..e5f6609 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -0,0 +1,66 @@ +package org.triumers.newsnippetback.Application.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; +import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.domain.aggregate.entity.User; +import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; +import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; +import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.repository.UserRepository; + +@Service +public class AuthServiceImpl implements AuthService { + + private final UserRepository userRepository; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + + @Autowired + public AuthServiceImpl(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { + this.userRepository = userRepository; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; + } + + @Override + public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { + + // 닉네임 중복 예외 처리 + if (userRepository.existsByNickname(request.getNickname())) { + throw new UserNicknameDuplicateException(); + } + + // 이메일 중복 예외 처리 + if (userRepository.existsByEmail(request.getEmail())) { + throw new UserEmailDuplicateException(); + } + + User user = userMapper(request); + + userRepository.save(user); + } + + public boolean existNickname(String nickname) { + return userRepository.existsByNickname(nickname); + } + + public boolean existEmail(String email) { + return userRepository.existsByEmail(email); + } + + private User userMapper(AuthDTO request) { + User user = new User(); + + user.setName(request.getName()); + user.setNickname(request.getNickname()); + user.setEmail(request.getEmail()); + user.setPassword(bCryptPasswordEncoder.encode(request.getPassword())); + user.setUserRole(UserRole.USER); + user.setProvider(request.getProvider()); + user.setSnsId(request.getSnsId()); + user.setUserStatus(UserStatus.ACTIVE); + + return user; + } +} From f52b9c0452cf44f92abe0602f620122836a16cc0 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 19:26:14 +0900 Subject: [PATCH 097/134] =?UTF-8?q?refactor(Auth):=20Auth=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20Interface=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 인터페이스에 맞도록 메소드가 수정되었습니다. --- .../newsnippetback/Application/service/AuthService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 36ac161..f3595f6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -6,9 +6,9 @@ public interface AuthService { - public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException; + void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException; - public boolean existNickname(String nickname); + boolean existNickname(String nickname); - public boolean existEmail(String email); + boolean existEmail(String email); } From 57c874c51f87c00e97aa6bbcf38c0881b9ada986 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 20:11:45 +0900 Subject: [PATCH 098/134] =?UTF-8?q?feat(Auth):=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 닉네임과 이름을 변경하는 기능이 구현되었습니다. --- .../controller/AuthController.java | 26 ++++++++++++++++--- .../Application/service/AuthService.java | 6 +++++ .../Application/service/AuthServiceImpl.java | 26 +++++++++++++++++++ .../newsnippetback/common/jwt/JWTUtil.java | 6 ----- .../domain/dto/PasswordDTO.java | 14 ++++++++++ 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index 639a2da..d393484 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -1,5 +1,6 @@ package org.triumers.newsnippetback.Application.controller; +import io.jsonwebtoken.ExpiredJwtException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -14,6 +15,7 @@ import org.triumers.newsnippetback.domain.aggregate.vo.RequestUserVO; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.dto.UserDTO; @RestController @RequestMapping("/auth") @@ -54,7 +56,7 @@ public ResponseEntity existNickname(@RequestBody RequestUserV if (authService.existNickname(request.getNickname())) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("이미 존재하는 닉네임입니다.")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 닉네임입니다.")); } return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 닉네임입니다.")); @@ -65,9 +67,27 @@ public ResponseEntity existEmail(@RequestBody RequestUserVO r if (authService.existEmail(request.getEmail())) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("이미 존재하는 이메일입니다.")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 이메일입니다.")); } - return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 이메일입니다.")); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("[사용 가능한 이메일입니다.")); + } + + @PostMapping("/modify/info") + public ResponseEntity modifyUserInfo(@RequestBody RequestUserVO request) { + + UserDTO userDTO = new UserDTO(); + userDTO.setName(request.getName()); + userDTO.setNickname(request.getNickname()); + + try { + authService.modifyUserInfo(userDTO); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("변경 성공")); + } catch (ExpiredJwtException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 로그인 이후 이용해주십시오.")); + } catch (UserNicknameDuplicateException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 닉네임입니다.")); + } } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index f3595f6..4afdac3 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -3,6 +3,8 @@ import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.dto.PasswordDTO; +import org.triumers.newsnippetback.domain.dto.UserDTO; public interface AuthService { @@ -11,4 +13,8 @@ public interface AuthService { boolean existNickname(String nickname); boolean existEmail(String email); + + void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException; + + void modifyPassword(PasswordDTO passwordDTO); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index e5f6609..460bd7c 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -1,6 +1,7 @@ package org.triumers.newsnippetback.Application.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; @@ -9,6 +10,8 @@ import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.dto.PasswordDTO; +import org.triumers.newsnippetback.domain.dto.UserDTO; import org.triumers.newsnippetback.domain.repository.UserRepository; @Service @@ -49,6 +52,29 @@ public boolean existEmail(String email) { return userRepository.existsByEmail(email); } + @Override + public void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException { + User user = userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()); + + if (userDTO.getNickname() != null) { + if (existNickname(userDTO.getNickname())) { + throw new UserNicknameDuplicateException(); + } + user.setNickname(userDTO.getNickname()); + } + + if (userDTO.getName() != null) { + user.setName(userDTO.getName()); + } + + userRepository.save(user); + } + + @Override + public void modifyPassword(PasswordDTO passwordDTO) { + + } + private User userMapper(AuthDTO request) { User user = new User(); diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java index 7a3ec97..a47738e 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTUtil.java @@ -37,11 +37,6 @@ public String getRole(String token) { return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class); } - public String getNickname(String token) { - - return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("nickname", String.class); - } - public Boolean isExpired(String token) { try { @@ -56,7 +51,6 @@ public String createJwt(String email, UserRole role, String nickname) { return Jwts.builder() .claim("email", email) .claim("role", role) - .claim("nickname", nickname) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + expiredMs)) .signWith(secretKey) diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java new file mode 100644 index 0000000..63aaa44 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java @@ -0,0 +1,14 @@ +package org.triumers.newsnippetback.domain.dto; + + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class PasswordDTO { + private String oldPassword; + private String newPassword; +} From 7c3ca0aff513e73e60f3bc9e13b3588b832ae28a Mon Sep 17 00:00:00 2001 From: moomint8 Date: Thu, 11 Apr 2024 20:37:06 +0900 Subject: [PATCH 099/134] =?UTF-8?q?feat(Auth):=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 비밀번호를 변경하는 기능이 구현되었습니다. --- .../controller/AuthController.java | 19 +++++++++++++++++++ .../Application/service/AuthService.java | 3 ++- .../Application/service/AuthServiceImpl.java | 11 ++++++++++- .../exception/WrongPasswordException.java | 7 +++++++ .../aggregate/vo/RequestModifyPasswordVO.java | 13 +++++++++++++ 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/WrongPasswordException.java create mode 100644 src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestModifyPasswordVO.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index d393484..12dd336 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -11,10 +11,13 @@ import org.triumers.newsnippetback.Application.service.AuthService; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; +import org.triumers.newsnippetback.domain.aggregate.vo.RequestModifyPasswordVO; import org.triumers.newsnippetback.domain.aggregate.vo.RequestUserVO; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; import org.triumers.newsnippetback.domain.dto.AuthDTO; +import org.triumers.newsnippetback.domain.dto.PasswordDTO; import org.triumers.newsnippetback.domain.dto.UserDTO; @RestController @@ -90,4 +93,20 @@ public ResponseEntity modifyUserInfo(@RequestBody RequestUser return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 닉네임입니다.")); } } + + @PostMapping("/modify/password") + public ResponseEntity modifyPassword(@RequestBody RequestModifyPasswordVO request) { + + PasswordDTO passwordDTO = new PasswordDTO(request.getOldPassword(), request.getNewPassword()); + + try { + authService.modifyPassword(passwordDTO); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("변경 성공")); + + } catch (ExpiredJwtException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 로그인 이후 이용해주십시오.")); + } catch (WrongPasswordException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO(e.getMessage())); + } + } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 4afdac3..3eb8980 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -2,6 +2,7 @@ import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.dto.AuthDTO; import org.triumers.newsnippetback.domain.dto.PasswordDTO; import org.triumers.newsnippetback.domain.dto.UserDTO; @@ -16,5 +17,5 @@ public interface AuthService { void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException; - void modifyPassword(PasswordDTO passwordDTO); + void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException; } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index 460bd7c..d26c640 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; @@ -71,8 +72,16 @@ public void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateExceptio } @Override - public void modifyPassword(PasswordDTO passwordDTO) { + public void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException { + User user = userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()); + + if (bCryptPasswordEncoder.matches(passwordDTO.getOldPassword(), user.getPassword())) { + user.setPassword(bCryptPasswordEncoder.encode(passwordDTO.getNewPassword())); + userRepository.save(user); + return; + } + throw new WrongPasswordException(); } private User userMapper(AuthDTO request) { diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/WrongPasswordException.java b/src/main/java/org/triumers/newsnippetback/common/exception/WrongPasswordException.java new file mode 100644 index 0000000..a728c62 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/WrongPasswordException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class WrongPasswordException extends CustomException { + public WrongPasswordException() { + super("잘못된 비밀번호입니다."); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestModifyPasswordVO.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestModifyPasswordVO.java new file mode 100644 index 0000000..112b012 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/RequestModifyPasswordVO.java @@ -0,0 +1,13 @@ +package org.triumers.newsnippetback.domain.aggregate.vo; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class RequestModifyPasswordVO { + private String oldPassword; + private String newPassword; +} From 2ceaa4f727346a808d9a1322b6e38ac5b15a172e Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 10:50:34 +0900 Subject: [PATCH 100/134] =?UTF-8?q?refactor(Solved):=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/SolvedServiceImpl.java | 3 ++- .../newsnippetback/domain/aggregate/vo/SolvedRequest.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 4beb54b..548bbfd 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -132,7 +132,7 @@ public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest sol public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest) { System.out.println(solvedRequest); int userId = solvedRequest.getUserId(); - LocalDate solvedDate = solvedRequest.getDate(); + LocalDate solvedDate = solvedRequest.getSolvedDate(); List solvedList = solvedRepository.findSolvedQuizByUserIdAndSolvedDate(userId, solvedDate); List solvedDTOList = new ArrayList<>(); @@ -163,4 +163,5 @@ public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedReq return solvedDTOList; } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java index 4047bda..91b3a89 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -12,5 +12,7 @@ public class SolvedRequest { private int userId; private int quizId; - private LocalDate date; + private LocalDate solvedDate; + private String selectedOption; + private boolean isCorrect; } From 3d167009382504ecb98ad64d157079778be27920 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 10:55:02 +0900 Subject: [PATCH 101/134] =?UTF-8?q?refactor(Quiz):=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=ED=95=98=EB=82=98=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 99 +++++++++++-------- .../Application/service/QuizService.java | 4 +- .../Application/service/QuizServiceImpl.java | 97 ++++++++++-------- .../domain/repository/QuizRepository.java | 1 - 4 files changed, 111 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 7bcc052..f1ed834 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -26,42 +26,45 @@ public QuizController(QuizService quizService) { this.quizService = quizService; } - /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 - ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ - @PostMapping("/test") - public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { - try { - LocalDate date = quizRequest.getDate(); - List quizDTOs = quizService.findAllQuizzesByDate(date); - List quizResponses = new ArrayList<>(); - for (QuizDTO quizDTO : quizDTOs) { - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setContent(quizDTO.getContent()); - quizResponse.setOptionA(quizDTO.getOptionA()); - quizResponse.setOptionB(quizDTO.getOptionB()); - quizResponse.setOptionC(quizDTO.getOptionC()); - quizResponse.setOptionD(quizDTO.getOptionD()); - quizResponse.setCategoryName(quizDTO.getCategoryName()); - quizResponse.setCorrectRate(quizDTO.getCorrectRate()); - quizResponses.add(quizResponse); - } - return ResponseEntity.ok().body(quizResponses); - } catch (NoSuchElementException e) { - return ResponseEntity.notFound().build(); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - } - - - /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 - ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 +// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// @PostMapping("/test") +// public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// LocalDate date = quizRequest.getDate(); +// List quizDTOs = quizService.findAllQuizzesByDate(date); +// List quizResponses = new ArrayList<>(); +// for (QuizDTO quizDTO : quizDTOs) { +// QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setContent(quizDTO.getContent()); +// quizResponse.setOptionA(quizDTO.getOptionA()); +// quizResponse.setOptionB(quizDTO.getOptionB()); +// quizResponse.setOptionC(quizDTO.getOptionC()); +// quizResponse.setOptionD(quizDTO.getOptionD()); +// quizResponse.setCategoryName(quizDTO.getCategoryName()); +// quizResponse.setCorrectRate(quizDTO.getCorrectRate()); +// quizResponses.add(quizResponse); +// } +// return ResponseEntity.ok().body(quizResponses); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } +// +// +// /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 +// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ // @PostMapping("/answer") // public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { // try { -// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); // // QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setNo(quizDTO.getNo()); // quizResponse.setAnswer(quizDTO.getAnswer()); // quizResponse.setExplanation(quizDTO.getExplanation()); // quizResponse.setNewsLink(quizDTO.getNewsLink()); @@ -74,20 +77,30 @@ public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizR // } // } - @PostMapping("/answer") - public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { + + @PostMapping("/test") + public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { try { LocalDate date = quizRequest.getDate(); - int no = quizRequest.getNo(); - QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); - - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setNo(quizDTO.getNo()); - quizResponse.setAnswer(quizDTO.getAnswer()); - quizResponse.setExplanation(quizDTO.getExplanation()); - quizResponse.setNewsLink(quizDTO.getNewsLink()); - - return ResponseEntity.ok().body(quizResponse); + List quizDTOs = quizService.findAllQuizzesByDate(date); + List quizResponses = quizDTOs.stream() + .map(quizDTO -> { + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setId(quizDTO.getId()); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + quizResponse.setAnswer(quizDTO.getAnswer()); + quizResponse.setExplanation(quizDTO.getExplanation()); + quizResponse.setNewsLink(quizDTO.getNewsLink()); + return quizResponse; + }) + .collect(Collectors.toList()); + return ResponseEntity.ok().body(quizResponses); } catch (NoSuchElementException e) { return ResponseEntity.notFound().build(); } catch (Exception e) { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 5df5525..5a640c6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -10,9 +10,7 @@ public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); -// QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); - List findAllQuizzesByDate(LocalDate date); - QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no); + } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 79ea60d..fb48d5d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -63,31 +63,31 @@ public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { return quizDTO; } - public List findAllQuizzesByDate(LocalDate date) { - List quizzes = quizRepository.findAllByDate(date); - List quizDTOs = new ArrayList<>(); - for (Quiz quiz : quizzes) { - QuizDTO quizDTO = new QuizDTO(); - quizDTO.setContent(quiz.getContent()); - quizDTO.setOptionA(quiz.getOptionA()); - quizDTO.setOptionB(quiz.getOptionB()); - quizDTO.setOptionC(quiz.getOptionC()); - quizDTO.setOptionD(quiz.getOptionD()); - - int categoryId = quiz.getCategoryId(); - Category category = categoryRepository.findById(categoryId); - if (category == null) { - throw new NoSuchElementException("Category not found for id: " + categoryId); - } - quizDTO.setCategoryName(category.getCategoryName()); - - double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); - quizDTO.setCorrectRate(correctRate); - - quizDTOs.add(quizDTO); - } - return quizDTOs; - } +// public List findAllQuizzesByDate(LocalDate date) { +// List quizzes = quizRepository.findAllByDate(date); +// List quizDTOs = new ArrayList<>(); +// for (Quiz quiz : quizzes) { +// QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setContent(quiz.getContent()); +// quizDTO.setOptionA(quiz.getOptionA()); +// quizDTO.setOptionB(quiz.getOptionB()); +// quizDTO.setOptionC(quiz.getOptionC()); +// quizDTO.setOptionD(quiz.getOptionD()); +// +// int categoryId = quiz.getCategoryId(); +// Category category = categoryRepository.findById(categoryId); +// if (category == null) { +// throw new NoSuchElementException("Category not found for id: " + categoryId); +// } +// quizDTO.setCategoryName(category.getCategoryName()); +// +// double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); +// quizDTO.setCorrectRate(correctRate); +// +// quizDTOs.add(quizDTO); +// } +// return quizDTOs; +// } // 정답률 계산 메서드 private double calculateCorrectRate(int correctCnt, int solvedCnt) { @@ -100,36 +100,47 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { // 2 // @Override -// public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { -// LocalDate date = quizRequest.getDate(); -// int no = quizRequest.getNo(); -// +// public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { // Quiz quiz = quizRepository.findByDateAndNo(date, no); -// // if (quiz == null) { // throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); // } -// // QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setNo(quiz.getNo()); // quizDTO.setAnswer(quiz.getAnswer()); // quizDTO.setExplanation(quiz.getExplanation()); // quizDTO.setNewsLink(quiz.getNewsLink()); -// // return quizDTO; // } - @Override - public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { - Quiz quiz = quizRepository.findByDateAndNo(date, no); - if (quiz == null) { - throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + + public List findAllQuizzesByDate(LocalDate date) { + List quizzes = quizRepository.findAllByDate(date); + List quizDTOs = quizzes.stream() + .map(quiz -> { + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + quizDTO.setCategoryName(getCategoryName(quiz.getCategoryId())); + quizDTO.setCorrectRate(calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt())); + quizDTO.setAnswer(quiz.getAnswer()); + quizDTO.setExplanation(quiz.getExplanation()); + quizDTO.setNewsLink(quiz.getNewsLink()); + return quizDTO; + }) + .collect(Collectors.toList()); + return quizDTOs; + } + + private String getCategoryName(int categoryId) { + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); } - QuizDTO quizDTO = new QuizDTO(); - quizDTO.setNo(quiz.getNo()); - quizDTO.setAnswer(quiz.getAnswer()); - quizDTO.setExplanation(quiz.getExplanation()); - quizDTO.setNewsLink(quiz.getNewsLink()); - return quizDTO; + return category.getCategoryName(); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index f3607fa..801be11 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -27,5 +27,4 @@ public interface QuizRepository extends JpaRepository { List findAllByDate(LocalDate date); - List findByDateAndNoIn(LocalDate date, List nos); } From 64537ea9310eb3a5e3b2490a23304b059bd2e059 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 11:52:31 +0900 Subject: [PATCH 102/134] =?UTF-8?q?refactor(Quiz):=20=ED=80=B4=EC=A6=88=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EB=94=94=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/QuizServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index fb48d5d..3072833 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -119,6 +119,7 @@ public List findAllQuizzesByDate(LocalDate date) { List quizDTOs = quizzes.stream() .map(quiz -> { QuizDTO quizDTO = new QuizDTO(); + quizDTO.setId(quiz.getId()); quizDTO.setContent(quiz.getContent()); quizDTO.setOptionA(quiz.getOptionA()); quizDTO.setOptionB(quiz.getOptionB()); From 692ea884226591907be1f4ec49e251963627f277 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 12:02:42 +0900 Subject: [PATCH 103/134] =?UTF-8?q?test(Auth):=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 닉네임 검증 테스트가 구현되었습니다. - 2-10자 길이 테스트 - 한글, 알파벳, 숫자만 가능 테스트 --- .../exception/WrongInputTypeException.java | 7 ++++ .../service/ValidationAuthServiceTest.java | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/common/exception/WrongInputTypeException.java create mode 100644 src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java diff --git a/src/main/java/org/triumers/newsnippetback/common/exception/WrongInputTypeException.java b/src/main/java/org/triumers/newsnippetback/common/exception/WrongInputTypeException.java new file mode 100644 index 0000000..28fcc07 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/common/exception/WrongInputTypeException.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.common.exception; + +public class WrongInputTypeException extends CustomException { + public WrongInputTypeException() { + super("유효하지 않은 입력입니다."); + } +} diff --git a/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java b/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java new file mode 100644 index 0000000..9221d30 --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java @@ -0,0 +1,39 @@ +package org.triumers.newsnippetback.domain.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class ValidationAuthServiceTest { + + @Autowired + private ValidationAuthService validation; + + @DisplayName("닉네임 정상값 테스트") + @ParameterizedTest + @ValueSource(strings = {"테스트닉네임", "짧다", "일이삼사오육칠팔구십", "alpha123한글", "ㅁㄴㅇㅁㄴㅇ"}) + void rightNickname(String nickname) throws WrongInputTypeException { + + assertDoesNotThrow(() -> validation.nickname(nickname)); + } + + @DisplayName("닉네임 길이 예외 테스트") + @ParameterizedTest + @ValueSource(strings = {"", "짧", "일이삼사오육칠팔구십넘었다"}) + void wrongLengthNickname(String nickname) { + assertThrows(WrongInputTypeException.class, () -> validation.nickname(nickname)); + } + + @DisplayName("닉네임 타입 예외 테스트") + @ParameterizedTest + @ValueSource(strings = {"!@#", "테스트$%^", "&*()/\\"}) + void wrongTypeNickname(String nickname) { + assertThrows(WrongInputTypeException.class, () -> validation.nickname(nickname)); + } +} \ No newline at end of file From 0b975db2df437c13b4b20bd30b50cc3e5ecfae60 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 12:16:16 +0900 Subject: [PATCH 104/134] =?UTF-8?q?feat(Auth):=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회원가입 및 회원정보 수정 기능에 닉네임 값의 유효성 검사(2~10자, 특수문자 불가)가 추가되었습니다. --- .../controller/AuthController.java | 7 ++++--- .../Application/service/AuthService.java | 5 +++-- .../Application/service/AuthServiceImpl.java | 17 +++++++++++++--- .../domain/service/ValidationAuthService.java | 20 +++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index 12dd336..e913f71 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -11,6 +11,7 @@ import org.triumers.newsnippetback.Application.service.AuthService; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; import org.triumers.newsnippetback.domain.aggregate.vo.RequestModifyPasswordVO; @@ -45,7 +46,7 @@ public ResponseEntity signup(@RequestBody RequestUserVO reque try { authService.signup(user); - } catch (UserNicknameDuplicateException | UserEmailDuplicateException e) { + } catch (UserNicknameDuplicateException | UserEmailDuplicateException | WrongInputTypeException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO(e.getMessage())); } @@ -89,8 +90,8 @@ public ResponseEntity modifyUserInfo(@RequestBody RequestUser return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("변경 성공")); } catch (ExpiredJwtException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 로그인 이후 이용해주십시오.")); - } catch (UserNicknameDuplicateException e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 닉네임입니다.")); + } catch (UserNicknameDuplicateException | WrongInputTypeException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO(e.getMessage())); } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 3eb8980..ec71dc2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -2,6 +2,7 @@ import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.dto.AuthDTO; import org.triumers.newsnippetback.domain.dto.PasswordDTO; @@ -9,13 +10,13 @@ public interface AuthService { - void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException; + void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException, WrongInputTypeException; boolean existNickname(String nickname); boolean existEmail(String email); - void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException; + void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException, WrongInputTypeException; void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException; } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index d26c640..5318412 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserEmailDuplicateException; import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; import org.triumers.newsnippetback.common.exception.WrongPasswordException; import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; @@ -14,27 +15,33 @@ import org.triumers.newsnippetback.domain.dto.PasswordDTO; import org.triumers.newsnippetback.domain.dto.UserDTO; import org.triumers.newsnippetback.domain.repository.UserRepository; +import org.triumers.newsnippetback.domain.service.ValidationAuthService; @Service public class AuthServiceImpl implements AuthService { private final UserRepository userRepository; + private final ValidationAuthService validation; private final BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired - public AuthServiceImpl(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { + public AuthServiceImpl(UserRepository userRepository, ValidationAuthService validation, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userRepository = userRepository; + this.validation = validation; this.bCryptPasswordEncoder = bCryptPasswordEncoder; } @Override - public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException { + public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserEmailDuplicateException, WrongInputTypeException { // 닉네임 중복 예외 처리 if (userRepository.existsByNickname(request.getNickname())) { throw new UserNicknameDuplicateException(); } + // 닉네임 자릿수, 타입 검증 + validation.nickname(request.getNickname()); + // 이메일 중복 예외 처리 if (userRepository.existsByEmail(request.getEmail())) { throw new UserEmailDuplicateException(); @@ -54,13 +61,17 @@ public boolean existEmail(String email) { } @Override - public void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException { + public void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException, WrongInputTypeException { User user = userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()); if (userDTO.getNickname() != null) { if (existNickname(userDTO.getNickname())) { throw new UserNicknameDuplicateException(); } + + // 닉네임 자릿수, 타입 검증 + validation.nickname(userDTO.getNickname()); + user.setNickname(userDTO.getNickname()); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java new file mode 100644 index 0000000..bb370be --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java @@ -0,0 +1,20 @@ +package org.triumers.newsnippetback.domain.service; + +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class ValidationAuthService { + + public void nickname(String nickname) throws WrongInputTypeException { + Pattern pattern = Pattern.compile("^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9]{2,10}$"); + Matcher matcher = pattern.matcher(nickname); + + if (!matcher.matches()) { + throw new WrongInputTypeException(); + } + } +} From 508d10d806fdca9bcabe70ee222cd7d8f3f5dd62 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 12:16:46 +0900 Subject: [PATCH 105/134] =?UTF-8?q?fix(SolvedQuiz)=20:=20solved=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=ED=9B=84=20=EC=A1=B0=ED=9A=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20solved=20=EC=A0=80=EC=9E=A5=20=ED=9B=84=20=EA=B0=92?= =?UTF-8?q?=20=EB=A6=AC=ED=84=B4=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SolvedServiceImpl.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 548bbfd..ddcf203 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -41,30 +41,23 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) int userId = solvedRequest.getUserId(); int quizId = solvedRequest.getQuizId(); + String seletedOption = solvedRequest.getSelectedOption(); - Solved solved = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); Quiz answer = quizRepository.findAnswerById(quizId); - if (answer == null || solved == null) { - throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); - } - SolvedDTO solvedDTO = new SolvedDTO(); solvedDTO.setUserId(userId); solvedDTO.setQuizId(quizId); + solvedDTO.setCorrect(Objects.equals(answer.getAnswer(), seletedOption)); + solvedDTO.setSelectedOption(seletedOption); - if (Objects.equals(answer.getAnswer(), solved.getSelectedOption())) { - solvedDTO.setCorrect(true); - solved.setCorrect(true); - solvedRepository.save(solved); + Solved solved = modelMapper.map(solvedDTO, Solved.class); + solvedRepository.save(solved); - return solvedDTO; + if (answer == null || solved == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); } - solvedDTO.setCorrect(false); - solved.setCorrect(false); - solvedRepository.save(solved); - return solvedDTO; } From 375a71fb1eda93f5cd05044d613e74ec07638209 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 12:30:28 +0900 Subject: [PATCH 106/134] =?UTF-8?q?test(Auth):=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 비밀번호의 유효성을 테스트하는 기능이 구현되었습니다. - 정상적인 경우 테스트 - 8~12자가 아닐 경우 예외 테스트 - 알파벳, 숫자 이외의 값 예외 테스트 - 소문자, 대문자, 숫자 각 한 개 이상 미포함 예외 테스트 --- .../service/ValidationAuthServiceTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java b/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java index 9221d30..e2e71e9 100644 --- a/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceTest.java @@ -36,4 +36,33 @@ void wrongLengthNickname(String nickname) { void wrongTypeNickname(String nickname) { assertThrows(WrongInputTypeException.class, () -> validation.nickname(nickname)); } + + @DisplayName("비밀번호 정상값 테스트") + @ParameterizedTest + @ValueSource(strings = {"aA123456", "1234567890Zz", "aAbBcCdD123"}) + void rightPassword(String password) throws WrongInputTypeException { + + assertDoesNotThrow(() -> validation.password(password)); + } + + @DisplayName("비밀번호 길이 예외 테스트") + @ParameterizedTest + @ValueSource(strings = {"", "aAbBcC1", "aAb1234567890"}) + void wrongLengthPassword(String password) { + assertThrows(WrongInputTypeException.class, () -> validation.password(password)); + } + + @DisplayName("비밀번호 타입 예외 테스트") + @ParameterizedTest + @ValueSource(strings = {"aA123456!@#", "테스트123456", "&123aaAAwWd"}) + void wrongTypePassword(String password) { + assertThrows(WrongInputTypeException.class, () -> validation.password(password)); + } + + @DisplayName("비밀번호 소문자,대문자,숫자 한 개 이상 미포함 예외 테스트") + @ParameterizedTest + @ValueSource(strings = {"aa123456", "AA123456", "aAbBcCdDeE"}) + void wrongIncludePassword(String password) { + assertThrows(WrongInputTypeException.class, () -> validation.password(password)); + } } \ No newline at end of file From 8aa059fc2a36679c8b37db5a84538cfc4f4edd33 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 12:39:29 +0900 Subject: [PATCH 107/134] =?UTF-8?q?feat(Auth):=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 비밀번호의 유효성을 검사하는 기능이 구현되었습니다. - 정상적인 경우 테스트 - 8~12자가 아닐 경우 예외 테스트 - 알파벳, 숫자 이외의 값 예외 테스트 - 소문자, 대문자, 숫자 각 한 개 이상 미포함 예외 테스트 --- .../Application/controller/AuthController.java | 2 +- .../Application/service/AuthService.java | 2 +- .../Application/service/AuthServiceImpl.java | 10 ++++++++-- .../domain/service/ValidationAuthService.java | 9 +++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index e913f71..a9395a7 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -106,7 +106,7 @@ public ResponseEntity modifyPassword(@RequestBody RequestModi } catch (ExpiredJwtException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 로그인 이후 이용해주십시오.")); - } catch (WrongPasswordException e) { + } catch (WrongPasswordException | WrongInputTypeException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO(e.getMessage())); } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index ec71dc2..9f744bc 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -18,5 +18,5 @@ public interface AuthService { void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException, WrongInputTypeException; - void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException; + void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException, WrongInputTypeException; } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index 5318412..24b629d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -39,9 +39,12 @@ public void signup(AuthDTO request) throws UserNicknameDuplicateException, UserE throw new UserNicknameDuplicateException(); } - // 닉네임 자릿수, 타입 검증 + // 닉네임 유효성 검사 validation.nickname(request.getNickname()); + // 비밀번호 유효성 검사 + validation.password(request.getPassword()); + // 이메일 중복 예외 처리 if (userRepository.existsByEmail(request.getEmail())) { throw new UserEmailDuplicateException(); @@ -83,9 +86,12 @@ public void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateExceptio } @Override - public void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException { + public void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException, WrongInputTypeException { User user = userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()); + // 비밀번호 유효성 검사 + validation.password(passwordDTO.getNewPassword()); + if (bCryptPasswordEncoder.matches(passwordDTO.getOldPassword(), user.getPassword())) { user.setPassword(bCryptPasswordEncoder.encode(passwordDTO.getNewPassword())); userRepository.save(user); diff --git a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java index bb370be..ebf8fdb 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java +++ b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java @@ -17,4 +17,13 @@ public void nickname(String nickname) throws WrongInputTypeException { throw new WrongInputTypeException(); } } + + public void password(String password) throws WrongInputTypeException { + Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,12}$"); + Matcher matcher = pattern.matcher(password); + + if (!matcher.matches()) { + throw new WrongInputTypeException(); + } + } } From ce8b2da70f5e74070640411d30da8f9bfba5bc28 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 12:44:55 +0900 Subject: [PATCH 108/134] =?UTF-8?q?fix(Solved)=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?insert=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/SolvedServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index ddcf203..230a5e5 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -48,6 +48,7 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) SolvedDTO solvedDTO = new SolvedDTO(); solvedDTO.setUserId(userId); solvedDTO.setQuizId(quizId); + solvedDTO.setSolvedDate(LocalDate.now()); solvedDTO.setCorrect(Objects.equals(answer.getAnswer(), seletedOption)); solvedDTO.setSelectedOption(seletedOption); From 335546036880a2d7cd4145a9a0c0fb1517327106 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 12:45:16 +0900 Subject: [PATCH 109/134] =?UTF-8?q?refactor(ValidationAuthService):=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 객체지향하도록 하기 위해 인터페이스를 만들고 상속받도록 개선했습니다. --- .../domain/service/ValidationAuthService.java | 25 ++------------- .../service/ValidationAuthServiceImpl.java | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceImpl.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java index ebf8fdb..4562d6f 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java +++ b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthService.java @@ -1,29 +1,10 @@ package org.triumers.newsnippetback.domain.service; -import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.WrongInputTypeException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +public interface ValidationAuthService { -@Service -public class ValidationAuthService { + void nickname(String nickname) throws WrongInputTypeException; - public void nickname(String nickname) throws WrongInputTypeException { - Pattern pattern = Pattern.compile("^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9]{2,10}$"); - Matcher matcher = pattern.matcher(nickname); - - if (!matcher.matches()) { - throw new WrongInputTypeException(); - } - } - - public void password(String password) throws WrongInputTypeException { - Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,12}$"); - Matcher matcher = pattern.matcher(password); - - if (!matcher.matches()) { - throw new WrongInputTypeException(); - } - } + void password(String password) throws WrongInputTypeException; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceImpl.java new file mode 100644 index 0000000..a8df081 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/service/ValidationAuthServiceImpl.java @@ -0,0 +1,31 @@ +package org.triumers.newsnippetback.domain.service; + +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.common.exception.WrongInputTypeException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class ValidationAuthServiceImpl implements ValidationAuthService { + + @Override + public void nickname(String nickname) throws WrongInputTypeException { + Pattern pattern = Pattern.compile("^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9]{2,10}$"); + Matcher matcher = pattern.matcher(nickname); + + if (!matcher.matches()) { + throw new WrongInputTypeException(); + } + } + + @Override + public void password(String password) throws WrongInputTypeException { + Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,12}$"); + Matcher matcher = pattern.matcher(password); + + if (!matcher.matches()) { + throw new WrongInputTypeException(); + } + } +} From dbad7eb52b58ec8103495c271ef2d2c9283b45da Mon Sep 17 00:00:00 2001 From: B Date: Thu, 11 Apr 2024 15:31:42 +0900 Subject: [PATCH 110/134] =?UTF-8?q?feat(Quiz):=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EB=A7=9E=EC=B6=B0=EC=84=9C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EB=82=A0=EC=A7=9C=EC=97=90=20=EC=B6=9C=EC=A0=9C?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=EC=9D=98=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC,=20=EC=A7=80=EB=AC=B8,=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=A7=80,=20=EC=A0=95=EB=8B=B5=EB=A5=A0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 56 ++++++++--- .../Application/service/QuizService.java | 9 +- .../Application/service/QuizServiceImpl.java | 57 +++++++++-- .../domain/repository/QuizRepository.java | 4 + .../service/QuizServiceImplTests.java | 98 +++++++++---------- 5 files changed, 153 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 6e13f71..7bcc052 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -9,7 +9,11 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; @RestController @RequestMapping("/quiz") @@ -25,20 +29,23 @@ public QuizController(QuizService quizService) { /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ @PostMapping("/test") - public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { + public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { try { - QuizDTO quizDTO = quizService.findQuizByDateAndNo(quizRequest); - - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setContent(quizDTO.getContent()); - quizResponse.setOptionA(quizDTO.getOptionA()); - quizResponse.setOptionB(quizDTO.getOptionB()); - quizResponse.setOptionC(quizDTO.getOptionC()); - quizResponse.setOptionD(quizDTO.getOptionD()); - quizResponse.setCategoryName(quizDTO.getCategoryName()); - quizResponse.setCorrectRate(quizDTO.getCorrectRate()); - - return ResponseEntity.ok().body(quizResponse); + LocalDate date = quizRequest.getDate(); + List quizDTOs = quizService.findAllQuizzesByDate(date); + List quizResponses = new ArrayList<>(); + for (QuizDTO quizDTO : quizDTOs) { + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + quizResponses.add(quizResponse); + } + return ResponseEntity.ok().body(quizResponses); } catch (NoSuchElementException e) { return ResponseEntity.notFound().build(); } catch (Exception e) { @@ -49,12 +56,33 @@ public ResponseEntity findQuizByDateAndNo(@RequestBody QuizRequest /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// @PostMapping("/answer") +// public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); +// +// QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setAnswer(quizDTO.getAnswer()); +// quizResponse.setExplanation(quizDTO.getExplanation()); +// quizResponse.setNewsLink(quizDTO.getNewsLink()); +// +// return ResponseEntity.ok().body(quizResponse); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } + @PostMapping("/answer") public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { try { - QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); + LocalDate date = quizRequest.getDate(); + int no = quizRequest.getNo(); + QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); QuizResponse quizResponse = new QuizResponse(); + quizResponse.setNo(quizDTO.getNo()); quizResponse.setAnswer(quizDTO.getAnswer()); quizResponse.setExplanation(quizDTO.getExplanation()); quizResponse.setNewsLink(quizDTO.getNewsLink()); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 83f1369..5df5525 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -3,9 +3,16 @@ import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.dto.QuizDTO; +import java.time.LocalDate; +import java.util.List; + public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); - QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); +// QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); + + List findAllQuizzesByDate(LocalDate date); + + QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index cba1c36..79ea60d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -11,7 +11,10 @@ import org.triumers.newsnippetback.domain.repository.QuizRepository; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.NoSuchElementException; +import java.util.stream.Collectors; @Service public class QuizServiceImpl implements QuizService { @@ -60,6 +63,32 @@ public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { return quizDTO; } + public List findAllQuizzesByDate(LocalDate date) { + List quizzes = quizRepository.findAllByDate(date); + List quizDTOs = new ArrayList<>(); + for (Quiz quiz : quizzes) { + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + + int categoryId = quiz.getCategoryId(); + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); + } + quizDTO.setCategoryName(category.getCategoryName()); + + double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); + quizDTO.setCorrectRate(correctRate); + + quizDTOs.add(quizDTO); + } + return quizDTOs; + } + // 정답률 계산 메서드 private double calculateCorrectRate(int correctCnt, int solvedCnt) { if (solvedCnt == 0) { @@ -70,22 +99,36 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { // 2 - @Override - public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { - LocalDate date = quizRequest.getDate(); - int no = quizRequest.getNo(); +// @Override +// public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// +// Quiz quiz = quizRepository.findByDateAndNo(date, no); +// +// if (quiz == null) { +// throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); +// } +// +// QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setAnswer(quiz.getAnswer()); +// quizDTO.setExplanation(quiz.getExplanation()); +// quizDTO.setNewsLink(quiz.getNewsLink()); +// +// return quizDTO; +// } + @Override + public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { Quiz quiz = quizRepository.findByDateAndNo(date, no); - if (quiz == null) { throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); } - QuizDTO quizDTO = new QuizDTO(); + quizDTO.setNo(quiz.getNo()); quizDTO.setAnswer(quiz.getAnswer()); quizDTO.setExplanation(quiz.getExplanation()); quizDTO.setNewsLink(quiz.getNewsLink()); - return quizDTO; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 12cc39a..f3607fa 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -24,4 +24,8 @@ public interface QuizRepository extends JpaRepository { Integer countByDateAndOriginQuizId(LocalDate localDate, int id); Quiz findByOriginQuizIdAndDate(int id, LocalDate date); + + List findAllByDate(LocalDate date); + + List findByDateAndNoIn(LocalDate date, List nos); } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java index 8d0fa1d..ec2fc32 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java @@ -102,53 +102,53 @@ void findQuizWithNonExistingCategory() { }); } - @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") - @Test - void findQuizAnswerByDateAndNo_FirstQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 1; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("C", quizDTO.getAnswer()); - Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); - Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); - } - - @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") - @Test - void findQuizAnswerByDateAndNo_SecondQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 2; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("B", quizDTO.getAnswer()); - Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); - Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); - } - - @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") - @Test - void findAnswerOfNonExistingQuiz() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 999; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When & Then - Assertions.assertThrows(NoSuchElementException.class, () -> { - quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); - }); - } +// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") +// @Test +// void findQuizAnswerByDateAndNo_FirstQuestion() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 1; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When +// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// +// // Then +// Assertions.assertNotNull(quizDTO); +// Assertions.assertEquals("C", quizDTO.getAnswer()); +// Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); +// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); +// } + +// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") +// @Test +// void findQuizAnswerByDateAndNo_SecondQuestion() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 2; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When +// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// +// // Then +// Assertions.assertNotNull(quizDTO); +// Assertions.assertEquals("B", quizDTO.getAnswer()); +// Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); +// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); +// } + +// @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") +// @Test +// void findAnswerOfNonExistingQuiz() { +// // Given +// LocalDate date = LocalDate.of(2024, 4, 2); +// int no = 999; +// QuizRequest quizRequest = new QuizRequest(date, no); +// +// // When & Then +// Assertions.assertThrows(NoSuchElementException.class, () -> { +// quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); +// }); +// } } From ee36775c22bcfb7411ebef0df6ab0968d59605c4 Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 11 Apr 2024 16:41:44 +0900 Subject: [PATCH 111/134] =?UTF-8?q?feat(SolvedQuiz)=20:=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EB=B3=84=20=ED=92=80=EC=97=88=EB=8D=98=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20=EC=B6=94=EA=B0=80=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EC=99=80=20=EC=9C=A0=EC=A0=80=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=EC=97=90=20=EB=94=B0=EB=9D=BC=20=ED=92=80=EC=97=88?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SolvedController.java | 18 ++++++++++ .../Application/service/SolvedService.java | 2 ++ .../service/SolvedServiceImpl.java | 35 +++++++++++++++++++ .../aggregate/vo/SolvedQuizResponse.java | 1 + .../domain/aggregate/vo/SolvedRequest.java | 3 ++ .../domain/repository/SolvedRepository.java | 2 ++ 6 files changed, 61 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index e62170c..d26bc69 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -112,4 +112,22 @@ public ResponseEntity> findCorrectQuizByUserIdAndSolv return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } + + @PostMapping("/find/allByDate") + public ResponseEntity> findSolvedQuizByUserIdAndDate(@RequestBody SolvedRequest solvedRequest){ + try { + modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); + + List solvedList = solvedService.findSolvedQuizListByUserIdAndDate(solvedRequest); + List SolvedQuizListResponse = solvedList.stream() + .map(dot -> modelMapper.map(dot, SolvedQuizResponse.class)) + .collect(Collectors.toList()); + + return ResponseEntity.ok().body(SolvedQuizListResponse); + } catch (NoSuchElementException e){ + return ResponseEntity.notFound().build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index c0dece6..143f703 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -15,4 +15,6 @@ public interface SolvedService { List findSolvedQuizListByUserId(SolvedRequest solvedRequest); List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest); + + List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index e46628d..4beb54b 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -128,4 +128,39 @@ public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest sol return solvedList; } + @Override + public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest) { + System.out.println(solvedRequest); + int userId = solvedRequest.getUserId(); + LocalDate solvedDate = solvedRequest.getDate(); + + List solvedList = solvedRepository.findSolvedQuizByUserIdAndSolvedDate(userId, solvedDate); + List solvedDTOList = new ArrayList<>(); + + for (Solved solved: solvedList) { + int id = solved.getQuizId(); + Quiz quiz = quizRepository.findById(id).orElseThrow(); + + SolvedDTO solvedDTO = new SolvedDTO(); + + solvedDTO.setUserId(solved.getUserId()); + solvedDTO.setQuizId(quiz.getId()); + solvedDTO.setCategoryId(quiz.getCategoryId()); + solvedDTO.setContent(quiz.getContent()); + solvedDTO.setOptionA(quiz.getOptionA()); + solvedDTO.setOptionB(quiz.getOptionB()); + solvedDTO.setOptionC(quiz.getOptionC()); + solvedDTO.setOptionD(quiz.getOptionD()); + solvedDTO.setAnswer(quiz.getAnswer()); + solvedDTO.setSelectedOption(solved.getSelectedOption()); + solvedDTO.setExplanation(quiz.getExplanation()); + solvedDTO.setNewsLink(quiz.getNewsLink()); + solvedDTO.setDate(quiz.getDate()); + + solvedDTOList.add(solvedDTO); + } + + return solvedDTOList; + } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java index 8bffcdc..d52dce2 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedQuizResponse.java @@ -12,6 +12,7 @@ public class SolvedQuizResponse { private int userId; private int quizId; + private String selectedOption; private int categoryId; private String content; private String optionA; diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java index e2ba736..4047bda 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -2,6 +2,8 @@ import lombok.*; +import java.time.LocalDate; + @NoArgsConstructor @AllArgsConstructor @Getter @@ -10,4 +12,5 @@ public class SolvedRequest { private int userId; private int quizId; + private LocalDate date; } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java index a14c43f..4b65d75 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/SolvedRepository.java @@ -14,4 +14,6 @@ public interface SolvedRepository extends JpaRepository { List findIdByUserIdAndIsCorrectAndSolvedDate(int userId, boolean isCorrect, LocalDate solvedDate); List findSolvedQuizByUserId(int userId); + + List findSolvedQuizByUserIdAndSolvedDate(int userId, LocalDate solvedDate); } From 71e51f9526683ab6dd406c1491f1af9ea65267e2 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 10:50:34 +0900 Subject: [PATCH 112/134] =?UTF-8?q?refactor(Solved):=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/SolvedServiceImpl.java | 3 ++- .../newsnippetback/domain/aggregate/vo/SolvedRequest.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 4beb54b..548bbfd 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -132,7 +132,7 @@ public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest sol public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest) { System.out.println(solvedRequest); int userId = solvedRequest.getUserId(); - LocalDate solvedDate = solvedRequest.getDate(); + LocalDate solvedDate = solvedRequest.getSolvedDate(); List solvedList = solvedRepository.findSolvedQuizByUserIdAndSolvedDate(userId, solvedDate); List solvedDTOList = new ArrayList<>(); @@ -163,4 +163,5 @@ public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedReq return solvedDTOList; } + } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java index 4047bda..91b3a89 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -12,5 +12,7 @@ public class SolvedRequest { private int userId; private int quizId; - private LocalDate date; + private LocalDate solvedDate; + private String selectedOption; + private boolean isCorrect; } From 232b7d7b571d8e8db70e7cdb94e13774e4af118f Mon Sep 17 00:00:00 2001 From: hso Date: Thu, 11 Apr 2024 17:27:01 +0900 Subject: [PATCH 113/134] =?UTF-8?q?fix(solvedQuiz)=20:=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EB=90=9C=20=EC=98=B5=EC=85=98=EA=B0=92=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/controller/SolvedController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index d26bc69..04e2929 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -82,6 +82,7 @@ public ResponseEntity findSolvedQuizByUserIdAndQuizId(@Reque solvedQuizResponse.setOptionC(solvedDTO.getOptionC()); solvedQuizResponse.setOptionD(solvedDTO.getOptionD()); solvedQuizResponse.setAnswer(solvedDTO.getAnswer()); + solvedQuizResponse.setSelectedOption(solvedDTO.getSelectedOption()); solvedQuizResponse.setExplanation(solvedDTO.getExplanation()); solvedQuizResponse.setNewsLink(solvedDTO.getNewsLink()); solvedQuizResponse.setDate(solvedDTO.getDate()); From d50c916ba2201b25d2f2e2960eab5d46d4762115 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 10:55:02 +0900 Subject: [PATCH 114/134] =?UTF-8?q?refactor(Quiz):=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=ED=95=98=EB=82=98=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 99 +++++++++++-------- .../Application/service/QuizService.java | 4 +- .../Application/service/QuizServiceImpl.java | 97 ++++++++++-------- .../domain/repository/QuizRepository.java | 1 - 4 files changed, 111 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 7bcc052..f1ed834 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -26,42 +26,45 @@ public QuizController(QuizService quizService) { this.quizService = quizService; } - /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 - ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ - @PostMapping("/test") - public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { - try { - LocalDate date = quizRequest.getDate(); - List quizDTOs = quizService.findAllQuizzesByDate(date); - List quizResponses = new ArrayList<>(); - for (QuizDTO quizDTO : quizDTOs) { - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setContent(quizDTO.getContent()); - quizResponse.setOptionA(quizDTO.getOptionA()); - quizResponse.setOptionB(quizDTO.getOptionB()); - quizResponse.setOptionC(quizDTO.getOptionC()); - quizResponse.setOptionD(quizDTO.getOptionD()); - quizResponse.setCategoryName(quizDTO.getCategoryName()); - quizResponse.setCorrectRate(quizDTO.getCorrectRate()); - quizResponses.add(quizResponse); - } - return ResponseEntity.ok().body(quizResponses); - } catch (NoSuchElementException e) { - return ResponseEntity.notFound().build(); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - } - - - /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 - ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 +// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ +// @PostMapping("/test") +// public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { +// try { +// LocalDate date = quizRequest.getDate(); +// List quizDTOs = quizService.findAllQuizzesByDate(date); +// List quizResponses = new ArrayList<>(); +// for (QuizDTO quizDTO : quizDTOs) { +// QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setContent(quizDTO.getContent()); +// quizResponse.setOptionA(quizDTO.getOptionA()); +// quizResponse.setOptionB(quizDTO.getOptionB()); +// quizResponse.setOptionC(quizDTO.getOptionC()); +// quizResponse.setOptionD(quizDTO.getOptionD()); +// quizResponse.setCategoryName(quizDTO.getCategoryName()); +// quizResponse.setCorrectRate(quizDTO.getCorrectRate()); +// quizResponses.add(quizResponse); +// } +// return ResponseEntity.ok().body(quizResponses); +// } catch (NoSuchElementException e) { +// return ResponseEntity.notFound().build(); +// } catch (Exception e) { +// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); +// } +// } +// +// +// /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 +// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ // @PostMapping("/answer") // public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { // try { -// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(quizRequest); +// LocalDate date = quizRequest.getDate(); +// int no = quizRequest.getNo(); +// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); // // QuizResponse quizResponse = new QuizResponse(); +// quizResponse.setNo(quizDTO.getNo()); // quizResponse.setAnswer(quizDTO.getAnswer()); // quizResponse.setExplanation(quizDTO.getExplanation()); // quizResponse.setNewsLink(quizDTO.getNewsLink()); @@ -74,20 +77,30 @@ public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizR // } // } - @PostMapping("/answer") - public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { + + @PostMapping("/test") + public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { try { LocalDate date = quizRequest.getDate(); - int no = quizRequest.getNo(); - QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); - - QuizResponse quizResponse = new QuizResponse(); - quizResponse.setNo(quizDTO.getNo()); - quizResponse.setAnswer(quizDTO.getAnswer()); - quizResponse.setExplanation(quizDTO.getExplanation()); - quizResponse.setNewsLink(quizDTO.getNewsLink()); - - return ResponseEntity.ok().body(quizResponse); + List quizDTOs = quizService.findAllQuizzesByDate(date); + List quizResponses = quizDTOs.stream() + .map(quizDTO -> { + QuizResponse quizResponse = new QuizResponse(); + quizResponse.setId(quizDTO.getId()); + quizResponse.setContent(quizDTO.getContent()); + quizResponse.setOptionA(quizDTO.getOptionA()); + quizResponse.setOptionB(quizDTO.getOptionB()); + quizResponse.setOptionC(quizDTO.getOptionC()); + quizResponse.setOptionD(quizDTO.getOptionD()); + quizResponse.setCategoryName(quizDTO.getCategoryName()); + quizResponse.setCorrectRate(quizDTO.getCorrectRate()); + quizResponse.setAnswer(quizDTO.getAnswer()); + quizResponse.setExplanation(quizDTO.getExplanation()); + quizResponse.setNewsLink(quizDTO.getNewsLink()); + return quizResponse; + }) + .collect(Collectors.toList()); + return ResponseEntity.ok().body(quizResponses); } catch (NoSuchElementException e) { return ResponseEntity.notFound().build(); } catch (Exception e) { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 5df5525..5a640c6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -10,9 +10,7 @@ public interface QuizService { QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); -// QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest); - List findAllQuizzesByDate(LocalDate date); - QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no); + } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 79ea60d..fb48d5d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -63,31 +63,31 @@ public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { return quizDTO; } - public List findAllQuizzesByDate(LocalDate date) { - List quizzes = quizRepository.findAllByDate(date); - List quizDTOs = new ArrayList<>(); - for (Quiz quiz : quizzes) { - QuizDTO quizDTO = new QuizDTO(); - quizDTO.setContent(quiz.getContent()); - quizDTO.setOptionA(quiz.getOptionA()); - quizDTO.setOptionB(quiz.getOptionB()); - quizDTO.setOptionC(quiz.getOptionC()); - quizDTO.setOptionD(quiz.getOptionD()); - - int categoryId = quiz.getCategoryId(); - Category category = categoryRepository.findById(categoryId); - if (category == null) { - throw new NoSuchElementException("Category not found for id: " + categoryId); - } - quizDTO.setCategoryName(category.getCategoryName()); - - double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); - quizDTO.setCorrectRate(correctRate); - - quizDTOs.add(quizDTO); - } - return quizDTOs; - } +// public List findAllQuizzesByDate(LocalDate date) { +// List quizzes = quizRepository.findAllByDate(date); +// List quizDTOs = new ArrayList<>(); +// for (Quiz quiz : quizzes) { +// QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setContent(quiz.getContent()); +// quizDTO.setOptionA(quiz.getOptionA()); +// quizDTO.setOptionB(quiz.getOptionB()); +// quizDTO.setOptionC(quiz.getOptionC()); +// quizDTO.setOptionD(quiz.getOptionD()); +// +// int categoryId = quiz.getCategoryId(); +// Category category = categoryRepository.findById(categoryId); +// if (category == null) { +// throw new NoSuchElementException("Category not found for id: " + categoryId); +// } +// quizDTO.setCategoryName(category.getCategoryName()); +// +// double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); +// quizDTO.setCorrectRate(correctRate); +// +// quizDTOs.add(quizDTO); +// } +// return quizDTOs; +// } // 정답률 계산 메서드 private double calculateCorrectRate(int correctCnt, int solvedCnt) { @@ -100,36 +100,47 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { // 2 // @Override -// public QuizDTO findQuizAnswerByDateAndNo(QuizRequest quizRequest) { -// LocalDate date = quizRequest.getDate(); -// int no = quizRequest.getNo(); -// +// public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { // Quiz quiz = quizRepository.findByDateAndNo(date, no); -// // if (quiz == null) { // throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); // } -// // QuizDTO quizDTO = new QuizDTO(); +// quizDTO.setNo(quiz.getNo()); // quizDTO.setAnswer(quiz.getAnswer()); // quizDTO.setExplanation(quiz.getExplanation()); // quizDTO.setNewsLink(quiz.getNewsLink()); -// // return quizDTO; // } - @Override - public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { - Quiz quiz = quizRepository.findByDateAndNo(date, no); - if (quiz == null) { - throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); + + public List findAllQuizzesByDate(LocalDate date) { + List quizzes = quizRepository.findAllByDate(date); + List quizDTOs = quizzes.stream() + .map(quiz -> { + QuizDTO quizDTO = new QuizDTO(); + quizDTO.setContent(quiz.getContent()); + quizDTO.setOptionA(quiz.getOptionA()); + quizDTO.setOptionB(quiz.getOptionB()); + quizDTO.setOptionC(quiz.getOptionC()); + quizDTO.setOptionD(quiz.getOptionD()); + quizDTO.setCategoryName(getCategoryName(quiz.getCategoryId())); + quizDTO.setCorrectRate(calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt())); + quizDTO.setAnswer(quiz.getAnswer()); + quizDTO.setExplanation(quiz.getExplanation()); + quizDTO.setNewsLink(quiz.getNewsLink()); + return quizDTO; + }) + .collect(Collectors.toList()); + return quizDTOs; + } + + private String getCategoryName(int categoryId) { + Category category = categoryRepository.findById(categoryId); + if (category == null) { + throw new NoSuchElementException("Category not found for id: " + categoryId); } - QuizDTO quizDTO = new QuizDTO(); - quizDTO.setNo(quiz.getNo()); - quizDTO.setAnswer(quiz.getAnswer()); - quizDTO.setExplanation(quiz.getExplanation()); - quizDTO.setNewsLink(quiz.getNewsLink()); - return quizDTO; + return category.getCategoryName(); } diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index f3607fa..801be11 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -27,5 +27,4 @@ public interface QuizRepository extends JpaRepository { List findAllByDate(LocalDate date); - List findByDateAndNoIn(LocalDate date, List nos); } From 36cbe7e1b1efe8a79c5fe9ba204f92f1c8e80a57 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 11:52:31 +0900 Subject: [PATCH 115/134] =?UTF-8?q?refactor(Quiz):=20=ED=80=B4=EC=A6=88=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EB=94=94=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/QuizServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index fb48d5d..3072833 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -119,6 +119,7 @@ public List findAllQuizzesByDate(LocalDate date) { List quizDTOs = quizzes.stream() .map(quiz -> { QuizDTO quizDTO = new QuizDTO(); + quizDTO.setId(quiz.getId()); quizDTO.setContent(quiz.getContent()); quizDTO.setOptionA(quiz.getOptionA()); quizDTO.setOptionB(quiz.getOptionB()); From 5197e691bb2e88c58d07bd24d6dbc1a28dfc81e9 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 12:16:46 +0900 Subject: [PATCH 116/134] =?UTF-8?q?fix(SolvedQuiz)=20:=20solved=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=ED=9B=84=20=EC=A1=B0=ED=9A=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20solved=20=EC=A0=80=EC=9E=A5=20=ED=9B=84=20=EA=B0=92?= =?UTF-8?q?=20=EB=A6=AC=ED=84=B4=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SolvedServiceImpl.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 548bbfd..ddcf203 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -41,30 +41,23 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) int userId = solvedRequest.getUserId(); int quizId = solvedRequest.getQuizId(); + String seletedOption = solvedRequest.getSelectedOption(); - Solved solved = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); Quiz answer = quizRepository.findAnswerById(quizId); - if (answer == null || solved == null) { - throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); - } - SolvedDTO solvedDTO = new SolvedDTO(); solvedDTO.setUserId(userId); solvedDTO.setQuizId(quizId); + solvedDTO.setCorrect(Objects.equals(answer.getAnswer(), seletedOption)); + solvedDTO.setSelectedOption(seletedOption); - if (Objects.equals(answer.getAnswer(), solved.getSelectedOption())) { - solvedDTO.setCorrect(true); - solved.setCorrect(true); - solvedRepository.save(solved); + Solved solved = modelMapper.map(solvedDTO, Solved.class); + solvedRepository.save(solved); - return solvedDTO; + if (answer == null || solved == null) { + throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); } - solvedDTO.setCorrect(false); - solved.setCorrect(false); - solvedRepository.save(solved); - return solvedDTO; } From 6c431dd80869674203e608bc1eb06bde56faf415 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 12:44:55 +0900 Subject: [PATCH 117/134] =?UTF-8?q?fix(Solved)=20:=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?insert=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/SolvedServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index ddcf203..230a5e5 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -48,6 +48,7 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) SolvedDTO solvedDTO = new SolvedDTO(); solvedDTO.setUserId(userId); solvedDTO.setQuizId(quizId); + solvedDTO.setSolvedDate(LocalDate.now()); solvedDTO.setCorrect(Objects.equals(answer.getAnswer(), seletedOption)); solvedDTO.setSelectedOption(seletedOption); From 1b5ae3b4fdb8a8e4788f5aa3c4b1e18205b020b5 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 14:20:42 +0900 Subject: [PATCH 118/134] =?UTF-8?q?style(Quiz):=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EB=B0=8F=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuizController.java | 54 +------------ .../controller/SolvedController.java | 2 +- .../Application/service/QuizService.java | 3 - .../Application/service/QuizServiceImpl.java | 78 ------------------- 4 files changed, 3 insertions(+), 134 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index f1ed834..470e25e 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -26,58 +26,8 @@ public QuizController(QuizService quizService) { this.quizService = quizService; } -// /* 1. 해당 날짜에 출제되는 문제의 카테고리, 지문, 선택지, 정답률 조회 -// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ -// @PostMapping("/test") -// public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { -// try { -// LocalDate date = quizRequest.getDate(); -// List quizDTOs = quizService.findAllQuizzesByDate(date); -// List quizResponses = new ArrayList<>(); -// for (QuizDTO quizDTO : quizDTOs) { -// QuizResponse quizResponse = new QuizResponse(); -// quizResponse.setContent(quizDTO.getContent()); -// quizResponse.setOptionA(quizDTO.getOptionA()); -// quizResponse.setOptionB(quizDTO.getOptionB()); -// quizResponse.setOptionC(quizDTO.getOptionC()); -// quizResponse.setOptionD(quizDTO.getOptionD()); -// quizResponse.setCategoryName(quizDTO.getCategoryName()); -// quizResponse.setCorrectRate(quizDTO.getCorrectRate()); -// quizResponses.add(quizResponse); -// } -// return ResponseEntity.ok().body(quizResponses); -// } catch (NoSuchElementException e) { -// return ResponseEntity.notFound().build(); -// } catch (Exception e) { -// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); -// } -// } -// -// -// /* 2. 해당 문제의 정답, 해설, 원본 링크 조회 -// ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ -// @PostMapping("/answer") -// public ResponseEntity findQuizAnswerByDateAndNo(@RequestBody QuizRequest quizRequest) { -// try { -// LocalDate date = quizRequest.getDate(); -// int no = quizRequest.getNo(); -// QuizDTO quizDTO = quizService.findQuizAnswerByDateAndNo(date, no); -// -// QuizResponse quizResponse = new QuizResponse(); -// quizResponse.setNo(quizDTO.getNo()); -// quizResponse.setAnswer(quizDTO.getAnswer()); -// quizResponse.setExplanation(quizDTO.getExplanation()); -// quizResponse.setNewsLink(quizDTO.getNewsLink()); -// -// return ResponseEntity.ok().body(quizResponse); -// } catch (NoSuchElementException e) { -// return ResponseEntity.notFound().build(); -// } catch (Exception e) { -// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); -// } -// } - - + /* 해당 날짜의 출제되는 문제의 카테고리, 지문, 선택지, 정답률, 정답, 해설, 원본 링크 조회 + * ㄴ 문제는 10개씩, 각 날짜마다 1번-10번까지 번호 부여 */ @PostMapping("/test") public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizRequest quizRequest) { try { diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index 04e2929..9400af6 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -30,7 +30,7 @@ public SolvedController(SolvedService solvedService, ModelMapper modelMapper) { this.modelMapper = modelMapper; } - /* 설명. 1. 사용자가 입력한 답과 문제의 정답 여부 판단 */ + /* 설명. 1. 사용자가 입력한 답과 문제의 정답 여부 판단 및 사용자가 입력한 답 db에 저장 */ @PostMapping("/check") public ResponseEntity findSelectedOptionAndCompareAnswer(@RequestBody SolvedRequest solvedRequest) { try { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index 5a640c6..edaa16d 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,6 +1,5 @@ package org.triumers.newsnippetback.Application.service; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.dto.QuizDTO; import java.time.LocalDate; @@ -8,8 +7,6 @@ public interface QuizService { - QuizDTO findQuizByDateAndNo(QuizRequest quizRequest); - List findAllQuizzesByDate(LocalDate date); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 3072833..65abe95 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -1,17 +1,14 @@ package org.triumers.newsnippetback.Application.service; -import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.domain.aggregate.entity.Category; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; @@ -29,65 +26,6 @@ public QuizServiceImpl(QuizRepository quizRepository, CategoryRepository categor this.categoryRepository = categoryRepository; } - // 1 - @Override - public QuizDTO findQuizByDateAndNo(QuizRequest quizRequest) { - LocalDate date = quizRequest.getDate(); - int no = quizRequest.getNo(); - - Quiz quiz = quizRepository.findByDateAndNo(date, no); - - if (quiz == null) { - throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); - } - - QuizDTO quizDTO = new QuizDTO(); - quizDTO.setContent(quiz.getContent()); - quizDTO.setOptionA(quiz.getOptionA()); - quizDTO.setOptionB(quiz.getOptionB()); - quizDTO.setOptionC(quiz.getOptionC()); - quizDTO.setOptionD(quiz.getOptionD()); - - // 카테고리 ID를 사용하여 카테고리 이름 조회 - int categoryId = quiz.getCategoryId(); - Category category = categoryRepository.findById(categoryId); - if (category == null) { - throw new NoSuchElementException("Category not found for id: " + categoryId); - } - quizDTO.setCategoryName(category.getCategoryName()); - - // 정답률 계산 - double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); - quizDTO.setCorrectRate(correctRate); - - return quizDTO; - } - -// public List findAllQuizzesByDate(LocalDate date) { -// List quizzes = quizRepository.findAllByDate(date); -// List quizDTOs = new ArrayList<>(); -// for (Quiz quiz : quizzes) { -// QuizDTO quizDTO = new QuizDTO(); -// quizDTO.setContent(quiz.getContent()); -// quizDTO.setOptionA(quiz.getOptionA()); -// quizDTO.setOptionB(quiz.getOptionB()); -// quizDTO.setOptionC(quiz.getOptionC()); -// quizDTO.setOptionD(quiz.getOptionD()); -// -// int categoryId = quiz.getCategoryId(); -// Category category = categoryRepository.findById(categoryId); -// if (category == null) { -// throw new NoSuchElementException("Category not found for id: " + categoryId); -// } -// quizDTO.setCategoryName(category.getCategoryName()); -// -// double correctRate = calculateCorrectRate(quiz.getCorrectCnt(), quiz.getSolvedCnt()); -// quizDTO.setCorrectRate(correctRate); -// -// quizDTOs.add(quizDTO); -// } -// return quizDTOs; -// } // 정답률 계산 메서드 private double calculateCorrectRate(int correctCnt, int solvedCnt) { @@ -98,22 +36,6 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { } - // 2 -// @Override -// public QuizDTO findQuizAnswerByDateAndNo(LocalDate date, int no) { -// Quiz quiz = quizRepository.findByDateAndNo(date, no); -// if (quiz == null) { -// throw new NoSuchElementException("Quiz not found for date: " + date + " and no: " + no); -// } -// QuizDTO quizDTO = new QuizDTO(); -// quizDTO.setNo(quiz.getNo()); -// quizDTO.setAnswer(quiz.getAnswer()); -// quizDTO.setExplanation(quiz.getExplanation()); -// quizDTO.setNewsLink(quiz.getNewsLink()); -// return quizDTO; -// } - - public List findAllQuizzesByDate(LocalDate date) { List quizzes = quizRepository.findAllByDate(date); List quizDTOs = quizzes.stream() From c69e03531c1ab2e76724484470f061077d4c84e4 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 14:23:15 +0900 Subject: [PATCH 119/134] =?UTF-8?q?refactor(Quiz):=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/controller/QuizController.java | 1 + .../newsnippetback/Application/service/QuizServiceImpl.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 470e25e..08d63ca 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -37,6 +37,7 @@ public ResponseEntity> findQuizByDateAndNo(@RequestBody QuizR .map(quizDTO -> { QuizResponse quizResponse = new QuizResponse(); quizResponse.setId(quizDTO.getId()); + quizResponse.setNo(quizDTO.getNo()); quizResponse.setContent(quizDTO.getContent()); quizResponse.setOptionA(quizDTO.getOptionA()); quizResponse.setOptionB(quizDTO.getOptionB()); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 65abe95..741e0a2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -42,6 +42,7 @@ public List findAllQuizzesByDate(LocalDate date) { .map(quiz -> { QuizDTO quizDTO = new QuizDTO(); quizDTO.setId(quiz.getId()); + quizDTO.setNo(quiz.getNo()); quizDTO.setContent(quiz.getContent()); quizDTO.setOptionA(quiz.getOptionA()); quizDTO.setOptionB(quiz.getOptionB()); From eda27642212a34ec7c1180cb15c43bc8cee2f1d7 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 15:09:28 +0900 Subject: [PATCH 120/134] =?UTF-8?q?refactor(Quiz):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/vo/SolvedRequest.java | 5 + .../service/QuizServiceImplTest.java | 54 ++++++ .../service/QuizServiceImplTests.java | 154 ------------------ 3 files changed, 59 insertions(+), 154 deletions(-) create mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java delete mode 100644 src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java index 91b3a89..a86e3e0 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/vo/SolvedRequest.java @@ -15,4 +15,9 @@ public class SolvedRequest { private LocalDate solvedDate; private String selectedOption; private boolean isCorrect; + + public SolvedRequest(int userId, int quizId) { + this.userId = userId; + this.quizId = quizId; + } } diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java new file mode 100644 index 0000000..b0abf7a --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java @@ -0,0 +1,54 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class QuizServiceImplTest { + + private final QuizServiceImpl quizService; + + @Autowired + public QuizServiceImplTest(QuizServiceImpl quizService) { + this.quizService = quizService; + } + + static Stream getDate() { + return Stream.of( + LocalDate.of(2024, 4, 2), + LocalDate.of(2024, 4, 9) + ); + } + + @ParameterizedTest + @MethodSource("getDate") + void findAllQuizzesByDate(LocalDate date) { + List quizDTOList = quizService.findAllQuizzesByDate(date); + + assertNotNull(quizDTOList); + assertFalse(quizDTOList.isEmpty()); + } + + @Test + void findAllQuizzesByDateException() { + LocalDate date = LocalDate.of(2020, 1, 1); + + assertThrows(NoSuchElementException.class, () -> { + quizService.findAllQuizzesByDate(date); + }); + } + +} \ No newline at end of file diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java deleted file mode 100644 index ec2fc32..0000000 --- a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTests.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.triumers.newsnippetback.Application.service; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; -import org.triumers.newsnippetback.domain.dto.QuizDTO; - -import java.time.LocalDate; -import java.util.NoSuchElementException; - -@SpringBootTest -public class QuizServiceImplTests { - - @Autowired - private QuizServiceImpl quizServiceImpl; - - @Autowired - private PlatformTransactionManager transactionManager; - - @AfterEach - void rollback() { - TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); - transactionManager.rollback(status); - } - - @DisplayName("퀴즈 조회 - 첫 번째 문제") - @Test - void findQuizByDateAndNo_FirstQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 1; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", quizDTO.getContent()); - Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", quizDTO.getOptionA()); - Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", quizDTO.getOptionB()); - Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", quizDTO.getOptionC()); - Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", quizDTO.getOptionD()); - Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); - Assertions.assertEquals(70.0, quizDTO.getCorrectRate()); - } - - @DisplayName("퀴즈 조회 - 두 번째 문제") - @Test - void findQuizByDateAndNo_SecondQuestion() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 2; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When - QuizDTO quizDTO = quizServiceImpl.findQuizByDateAndNo(quizRequest); - - // Then - Assertions.assertNotNull(quizDTO); - Assertions.assertEquals("중국 전자상거래 기업 알리바바가 1시간 이내에 전 세계로 상품을 배송하는 시도에 나선다고 하는데, 이를 위해 협업하는 로켓 개발 스타트업은?", quizDTO.getContent()); - Assertions.assertEquals("스페이스 엑스 (Space X)", quizDTO.getOptionA()); - Assertions.assertEquals("스페이스 에포크 (Space Epoch)", quizDTO.getOptionB()); - Assertions.assertEquals("블루 오리진 (Blue Origin)", quizDTO.getOptionC()); - Assertions.assertEquals("로켓랩 (Rocket Lab)", quizDTO.getOptionD()); - Assertions.assertEquals("IT/과학", quizDTO.getCategoryName()); - Assertions.assertEquals(80.0, quizDTO.getCorrectRate()); - } - - @DisplayName("존재하지 않는 퀴즈 조회") - @Test - void findNonExistingQuiz() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 999; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When & Then - Assertions.assertThrows(NoSuchElementException.class, () -> { - quizServiceImpl.findQuizByDateAndNo(quizRequest); - }); - } - - @DisplayName("존재하지 않는 카테고리의 퀴즈 조회") - @Test - void findQuizWithNonExistingCategory() { - // Given - LocalDate date = LocalDate.of(2024, 4, 2); - int no = 3; - QuizRequest quizRequest = new QuizRequest(date, no); - - // When & Then - Assertions.assertThrows(NoSuchElementException.class, () -> { - quizServiceImpl.findQuizByDateAndNo(quizRequest); - }); - } - -// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 첫 번째 문제") -// @Test -// void findQuizAnswerByDateAndNo_FirstQuestion() { -// // Given -// LocalDate date = LocalDate.of(2024, 4, 2); -// int no = 1; -// QuizRequest quizRequest = new QuizRequest(date, no); -// -// // When -// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); -// -// // Then -// Assertions.assertNotNull(quizDTO); -// Assertions.assertEquals("C", quizDTO.getAnswer()); -// Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", quizDTO.getExplanation()); -// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", quizDTO.getNewsLink()); -// } - -// @DisplayName("퀴즈 정답, 해설, 원본 링크 조회 - 두 번째 문제") -// @Test -// void findQuizAnswerByDateAndNo_SecondQuestion() { -// // Given -// LocalDate date = LocalDate.of(2024, 4, 2); -// int no = 2; -// QuizRequest quizRequest = new QuizRequest(date, no); -// -// // When -// QuizDTO quizDTO = quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); -// -// // Then -// Assertions.assertNotNull(quizDTO); -// Assertions.assertEquals("B", quizDTO.getAnswer()); -// Assertions.assertEquals("알리바바가 전 세계 1시간 이내 배송을 추진하기 위해 협업하는 로켓 개발 스타트업은 스페이스 에포크입니다. 이 소식은 2024년 4월 2일에 보도되었습니다. 이는 알리바바의 전 세계적인 물류 서비스를 더욱 확장하기 위한 시도 중 하나로, 스페이스 에포크의 재사용 로켓 XZY-1을 활용하여 1시간 이내에 상품을 운송할 계획입니다.", quizDTO.getExplanation()); -// Assertions.assertEquals("https://www.ytn.co.kr/_ln/0104_202404021429256706", quizDTO.getNewsLink()); -// } - -// @DisplayName("존재하지 않는 퀴즈의 정답, 해설, 원본 링크 조회") -// @Test -// void findAnswerOfNonExistingQuiz() { -// // Given -// LocalDate date = LocalDate.of(2024, 4, 2); -// int no = 999; -// QuizRequest quizRequest = new QuizRequest(date, no); -// -// // When & Then -// Assertions.assertThrows(NoSuchElementException.class, () -> { -// quizServiceImpl.findQuizAnswerByDateAndNo(quizRequest); -// }); -// } -} From 4d8ad5fed686aacb3a3de5923b137be4e6ad1b27 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Fri, 12 Apr 2024 16:03:12 +0900 Subject: [PATCH 121/134] =?UTF-8?q?refactor(Auth):=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JSON 반환 값의 오타가 수정되었습니다. --- .../newsnippetback/Application/controller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index a9395a7..e820e91 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -74,7 +74,7 @@ public ResponseEntity existEmail(@RequestBody RequestUserVO r return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessageVO("[ERROR] 이미 존재하는 이메일입니다.")); } - return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("[사용 가능한 이메일입니다.")); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessageVO("사용 가능한 이메일입니다.")); } @PostMapping("/modify/info") From 1d5daad3b9ebbfc2746c5c62ba4aaa582d006436 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 16:36:05 +0900 Subject: [PATCH 122/134] =?UTF-8?q?feat(Config)=20:=20cors=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20config=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/config/WebConfig.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/org/triumers/newsnippetback/config/WebConfig.java diff --git a/src/main/java/org/triumers/newsnippetback/config/WebConfig.java b/src/main/java/org/triumers/newsnippetback/config/WebConfig.java new file mode 100644 index 0000000..79c125d --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/config/WebConfig.java @@ -0,0 +1,20 @@ +package org.triumers.newsnippetback.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@EnableWebMvc +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins( + "*" + ) + .allowedMethods("GET", "POST", "PUT", "DELETE"); + } +} From 84f114b86f577fad62697482d356f5a17e1532b3 Mon Sep 17 00:00:00 2001 From: hso Date: Fri, 12 Apr 2024 16:39:24 +0900 Subject: [PATCH 123/134] =?UTF-8?q?style(Config)=20:=20config=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../triumers/newsnippetback/{ => common}/config/WebConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/org/triumers/newsnippetback/{ => common}/config/WebConfig.java (92%) diff --git a/src/main/java/org/triumers/newsnippetback/config/WebConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java similarity index 92% rename from src/main/java/org/triumers/newsnippetback/config/WebConfig.java rename to src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java index 79c125d..d2134df 100644 --- a/src/main/java/org/triumers/newsnippetback/config/WebConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.config; +package org.triumers.newsnippetback.common.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; From 530ad91aae37533822d76027645f2db3e3a0947e Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 17:45:07 +0900 Subject: [PATCH 124/134] =?UTF-8?q?refactor(Quiz):=20=EC=A0=95=EB=8B=B5?= =?UTF-8?q?=EB=A5=A0=20=EA=B3=84=EC=82=B0=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/Application/service/QuizServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 741e0a2..ad25e28 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -32,7 +32,7 @@ private double calculateCorrectRate(int correctCnt, int solvedCnt) { if (solvedCnt == 0) { return 0.0; } - return (double) correctCnt / solvedCnt * 100; + return (double) correctCnt / solvedCnt; } From cb6ce0116bbb21a46c8a2f65ccc7d5067a2cc231 Mon Sep 17 00:00:00 2001 From: B Date: Fri, 12 Apr 2024 17:45:39 +0900 Subject: [PATCH 125/134] =?UTF-8?q?style(Quiz):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsnippetback/domain/repository/QuizRepository.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java index 801be11..62c187b 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -9,8 +9,6 @@ @Repository public interface QuizRepository extends JpaRepository { - Quiz findByDateAndNo(LocalDate date, int no); - Quiz findAnswerById(int quizId); Quiz findCategoryIdAndContentAndOptionAAndOptionBAndOptionCAndOptionDAndAnswerById(int quizId); From c92a19b9bef78b33db3fc470e3b4ca47e2b1d2ef Mon Sep 17 00:00:00 2001 From: Leegiyeon Date: Fri, 12 Apr 2024 17:55:14 +0900 Subject: [PATCH 126/134] =?UTF-8?q?docs(Template):=20PR=20template=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8a25ee1..f3d21b6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,8 @@ ### PR 타입(하나 이상의 PR 타입을 선택해주세요) --[ ] 기능 추가 --[ ] 기능 삭제 --[ ] 버그 수정 --[ ] 의존성, 환경 변수, 빌드 관련 코드 업데이트 +- [ ] 기능 추가 +- [ ] 기능 삭제 +- [ ] 버그 수정 +- [ ] 의존성, 환경 변수, 빌드 관련 코드 업데이트 ### 반영 브랜치 ex) feat/login -> dev From 25943a70221407fa3a974f009701c49c89351e63 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Mon, 15 Apr 2024 15:02:40 +0900 Subject: [PATCH 127/134] =?UTF-8?q?refactor(CORS):=20Spring=20Security?= =?UTF-8?q?=EC=97=90=EC=84=9C=20CORS=20=EC=B2=98=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SpringSecurity에서 CORS를 처리하도록 구현했습니다. --- .../common/config/SecurityConfig.java | 28 +++++++++++++++++++ .../common/config/WebConfig.java | 20 ------------- 2 files changed, 28 insertions(+), 20 deletions(-) delete mode 100644 src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index e577e94..2479320 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -1,5 +1,6 @@ package org.triumers.newsnippetback.common.config; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; @@ -13,10 +14,17 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.triumers.newsnippetback.common.jwt.JWTFilter; import org.triumers.newsnippetback.common.jwt.JWTUtil; import org.triumers.newsnippetback.common.jwt.LoginFilter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + @Configuration @EnableWebSecurity public class SecurityConfig { @@ -55,6 +63,26 @@ public RoleHierarchy roleHierarchy() { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // CORS 설정 + http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + + List allowStringList = Collections.singletonList("*"); + List exposedHeaders = List.of("Authorization"); + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.setAllowedOriginPatterns(allowStringList); + configuration.setAllowedMethods(allowStringList); + configuration.setAllowedHeaders(allowStringList); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(exposedHeaders); + + return configuration; + } + })); + // csrf disable http.csrf(AbstractHttpConfigurer::disable); diff --git a/src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java deleted file mode 100644 index d2134df..0000000 --- a/src/main/java/org/triumers/newsnippetback/common/config/WebConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.triumers.newsnippetback.common.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -@EnableWebMvc -public class WebConfig implements WebMvcConfigurer { - - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins( - "*" - ) - .allowedMethods("GET", "POST", "PUT", "DELETE"); - } -} From b4da92f90d49df097f6319a651a54879b34c6d7e Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 14:23:42 +0900 Subject: [PATCH 128/134] =?UTF-8?q?refactor(DTO):=20DTO=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DTO의 계층을 더 적합하도록 수정했습니다. (Domain -> Application) --- .../Application/controller/AuthController.java | 6 +++--- .../Application/controller/ManageController.java | 4 ++-- .../Application/controller/QuizController.java | 3 +-- .../Application/controller/SolvedController.java | 2 +- .../Application/controller/UserController.java | 2 +- .../newsnippetback/{domain => Application}/dto/AuthDTO.java | 2 +- .../{domain => Application}/dto/CrawlingQuizDTO.java | 2 +- .../{domain => Application}/dto/CustomUserDetails.java | 2 +- .../{domain => Application}/dto/PasswordDTO.java | 2 +- .../newsnippetback/{domain => Application}/dto/QuizDTO.java | 2 +- .../{domain => Application}/dto/SolvedDTO.java | 2 +- .../newsnippetback/{domain => Application}/dto/UserDTO.java | 2 +- .../newsnippetback/Application/service/AuthService.java | 6 +++--- .../newsnippetback/Application/service/AuthServiceImpl.java | 6 +++--- .../Application/service/CustomUserDetailsService.java | 2 +- .../newsnippetback/Application/service/ManageService.java | 4 ++-- .../Application/service/ManageServiceImpl.java | 4 ++-- .../newsnippetback/Application/service/QuizService.java | 2 +- .../newsnippetback/Application/service/QuizServiceImpl.java | 2 +- .../newsnippetback/Application/service/SolvedService.java | 2 +- .../Application/service/SolvedServiceImpl.java | 2 +- .../newsnippetback/Application/service/UserService.java | 2 +- .../newsnippetback/Application/service/UserServiceImpl.java | 2 +- .../org/triumers/newsnippetback/common/jwt/JWTFilter.java | 6 +----- .../org/triumers/newsnippetback/common/jwt/LoginFilter.java | 2 +- .../Application/service/ManageServiceTest.java | 4 ++-- .../Application/service/QuizServiceImplTest.java | 4 +--- .../Application/service/SolvedServiceImplTest.java | 2 +- 28 files changed, 38 insertions(+), 45 deletions(-) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/AuthDTO.java (91%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/CrawlingQuizDTO.java (90%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/CustomUserDetails.java (96%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/PasswordDTO.java (77%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/QuizDTO.java (92%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/SolvedDTO.java (91%) rename src/main/java/org/triumers/newsnippetback/{domain => Application}/dto/UserDTO.java (91%) diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java index e820e91..de3c04e 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/AuthController.java @@ -17,9 +17,9 @@ import org.triumers.newsnippetback.domain.aggregate.vo.RequestModifyPasswordVO; import org.triumers.newsnippetback.domain.aggregate.vo.RequestUserVO; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseMessageVO; -import org.triumers.newsnippetback.domain.dto.AuthDTO; -import org.triumers.newsnippetback.domain.dto.PasswordDTO; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.AuthDTO; +import org.triumers.newsnippetback.Application.dto.PasswordDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; @RestController @RequestMapping("/auth") diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java index 272c7d0..53343d2 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -6,8 +6,8 @@ import org.springframework.web.bind.annotation.*; import org.triumers.newsnippetback.Application.service.ManageService; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java index 08d63ca..c99d4bf 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/QuizController.java @@ -7,10 +7,9 @@ import org.triumers.newsnippetback.Application.service.QuizService; import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; import org.triumers.newsnippetback.domain.aggregate.vo.QuizResponse; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java index 9400af6..bde01e1 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/SolvedController.java @@ -12,7 +12,7 @@ import org.triumers.newsnippetback.Application.service.SolvedService; import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.*; -import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.Application.dto.SolvedDTO; import java.util.List; import java.util.NoSuchElementException; diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java index c678032..da5a620 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/UserController.java @@ -12,7 +12,7 @@ import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; import org.triumers.newsnippetback.domain.aggregate.vo.ResponseUserInfoVO; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; @RestController @RequestMapping("/user") diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/AuthDTO.java similarity index 91% rename from src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/AuthDTO.java index f10bca2..c008531 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/AuthDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/AuthDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.*; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/CrawlingQuizDTO.java similarity index 90% rename from src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/CrawlingQuizDTO.java index 797efa5..ac8ff95 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/CrawlingQuizDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java b/src/main/java/org/triumers/newsnippetback/Application/dto/CustomUserDetails.java similarity index 96% rename from src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/CustomUserDetails.java index 225e972..d456325 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/CustomUserDetails.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/CustomUserDetails.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/PasswordDTO.java similarity index 77% rename from src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/PasswordDTO.java index 63aaa44..7f9e124 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/PasswordDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/PasswordDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.*; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/QuizDTO.java similarity index 92% rename from src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/QuizDTO.java index a2bdc67..00fe911 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/QuizDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/SolvedDTO.java similarity index 91% rename from src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/SolvedDTO.java index 359948e..f5ef5b1 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/SolvedDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/SolvedDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.*; diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java b/src/main/java/org/triumers/newsnippetback/Application/dto/UserDTO.java similarity index 91% rename from src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java rename to src/main/java/org/triumers/newsnippetback/Application/dto/UserDTO.java index 893198d..72b4beb 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/UserDTO.java +++ b/src/main/java/org/triumers/newsnippetback/Application/dto/UserDTO.java @@ -1,4 +1,4 @@ -package org.triumers.newsnippetback.domain.dto; +package org.triumers.newsnippetback.Application.dto; import lombok.*; import org.triumers.newsnippetback.domain.aggregate.enums.Provider; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 9f744bc..e61608b 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -4,9 +4,9 @@ import org.triumers.newsnippetback.common.exception.UserNicknameDuplicateException; import org.triumers.newsnippetback.common.exception.WrongInputTypeException; import org.triumers.newsnippetback.common.exception.WrongPasswordException; -import org.triumers.newsnippetback.domain.dto.AuthDTO; -import org.triumers.newsnippetback.domain.dto.PasswordDTO; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.AuthDTO; +import org.triumers.newsnippetback.Application.dto.PasswordDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; public interface AuthService { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index 24b629d..e910bb3 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -11,9 +11,9 @@ import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.enums.UserStatus; -import org.triumers.newsnippetback.domain.dto.AuthDTO; -import org.triumers.newsnippetback.domain.dto.PasswordDTO; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.AuthDTO; +import org.triumers.newsnippetback.Application.dto.PasswordDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; import org.triumers.newsnippetback.domain.repository.UserRepository; import org.triumers.newsnippetback.domain.service.ValidationAuthService; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java index 920bb68..1369f42 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/CustomUserDetailsService.java @@ -6,7 +6,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.triumers.newsnippetback.domain.aggregate.entity.User; -import org.triumers.newsnippetback.domain.dto.CustomUserDetails; +import org.triumers.newsnippetback.Application.dto.CustomUserDetails; import org.triumers.newsnippetback.domain.repository.UserRepository; @Service diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java index 9bf4cde..ecb017a 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -1,8 +1,8 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; import java.util.List; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java index 322f728..1708adf 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageServiceImpl.java @@ -7,8 +7,8 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Category; import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java index edaa16d..d9857ae 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizService.java @@ -1,6 +1,6 @@ package org.triumers.newsnippetback.Application.service; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; import java.util.List; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java index 741e0a2..7371adc 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/QuizServiceImpl.java @@ -4,7 +4,7 @@ import org.springframework.stereotype.Service; import org.triumers.newsnippetback.domain.aggregate.entity.Category; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java index 143f703..df08379 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedService.java @@ -3,7 +3,7 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; -import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.Application.dto.SolvedDTO; import java.util.List; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 230a5e5..49c0731 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -9,7 +9,7 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; -import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.Application.dto.SolvedDTO; import org.triumers.newsnippetback.domain.repository.CategoryRepository; import org.triumers.newsnippetback.domain.repository.QuizRepository; import org.triumers.newsnippetback.domain.repository.SolvedRepository; diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java index 57c8f53..823f1bb 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserService.java @@ -1,7 +1,7 @@ package org.triumers.newsnippetback.Application.service; import org.triumers.newsnippetback.common.exception.UserNotFoundException; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; public interface UserService { diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java index 88be50e..f921d9e 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/UserServiceImpl.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Service; import org.triumers.newsnippetback.common.exception.UserNotFoundException; import org.triumers.newsnippetback.domain.aggregate.entity.User; -import org.triumers.newsnippetback.domain.dto.UserDTO; +import org.triumers.newsnippetback.Application.dto.UserDTO; import org.triumers.newsnippetback.domain.repository.UserRepository; @Service diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java index cfddd63..d728a5b 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/JWTFilter.java @@ -1,20 +1,16 @@ package org.triumers.newsnippetback.common.jwt; -import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.HttpStatus; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.ErrorResponse; -import org.springframework.web.ErrorResponseException; import org.springframework.web.filter.OncePerRequestFilter; import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; -import org.triumers.newsnippetback.domain.dto.CustomUserDetails; +import org.triumers.newsnippetback.Application.dto.CustomUserDetails; import java.io.IOException; diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java index 5c0fcfd..4e0f435 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java @@ -13,7 +13,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.triumers.newsnippetback.domain.aggregate.enums.UserRole; import org.triumers.newsnippetback.domain.aggregate.vo.RequestLoginVO; -import org.triumers.newsnippetback.domain.dto.CustomUserDetails; +import org.triumers.newsnippetback.Application.dto.CustomUserDetails; import java.io.IOException; import java.util.Collection; diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java index fcd5798..8d55440 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -7,8 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; -import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; import java.util.List; diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java index b0abf7a..c8b82ab 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/QuizServiceImplTest.java @@ -3,11 +3,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.triumers.newsnippetback.domain.aggregate.vo.QuizRequest; -import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.Application.dto.QuizDTO; import java.time.LocalDate; import java.util.List; diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java index aaceea5..60a17ac 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -12,7 +12,7 @@ import org.triumers.newsnippetback.domain.aggregate.entity.Solved; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; -import org.triumers.newsnippetback.domain.dto.SolvedDTO; +import org.triumers.newsnippetback.Application.dto.SolvedDTO; import java.time.LocalDate; import java.util.List; From 92887303d5a8a4492644e50ee2bbdfac1d7d4d8a Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 16:07:14 +0900 Subject: [PATCH 129/134] =?UTF-8?q?test(Solved):=20=ED=80=B4=EC=A6=88=20?= =?UTF-8?q?=EB=8B=B5=EC=95=88=20=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 퀴즈 답안 제출 시 정답, 오답 여부에 따라 유저 정보 업데이트를 테스트하는 메소드가 구현되었습니다. --- .../service/SolvedServiceImplTest.java | 81 ++++++++++++++++++- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java index 60a17ac..fdb3963 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -1,30 +1,54 @@ package org.triumers.newsnippetback.Application.service; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.triumers.newsnippetback.common.exception.UserNotFoundException; import org.triumers.newsnippetback.domain.aggregate.entity.Solved; +import org.triumers.newsnippetback.domain.aggregate.entity.User; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedRequest; import org.triumers.newsnippetback.domain.aggregate.vo.SolvedResultRequest; import org.triumers.newsnippetback.Application.dto.SolvedDTO; +import org.triumers.newsnippetback.domain.repository.UserRepository; import java.time.LocalDate; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + @SpringBootTest class SolvedServiceImplTest { + + private final int TEST_QUIZ_ID = 1; + private final String TEST_RIGHT_OPTION = "C"; + private final String TEST_WRONG_OPTION = "A"; + @Autowired private SolvedService solvedService; + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + @Autowired private PlatformTransactionManager transactionManager; + @BeforeEach + void setUserToken() { + setUserToContextByUsername(); + } + @AfterEach void rollback() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); @@ -106,4 +130,53 @@ void findCorrectQuizByUserIdAndSolvedDate() { // Then Assertions.assertNotNull(solvedList); } + + @DisplayName("퀴즈 정답 제출시 유저 정보 업데이트 테스트") + @Test + @Transactional + void userInfoUpdateWhenCorrect() throws UserNotFoundException { + // given + User user = userRepository.findByEmail(userService.findByToken().getEmail()); + int solvedCnt = user.getSolvedCnt(); + int correctCnt = user.getCorrectCnt(); + SolvedRequest request = new SolvedRequest(user.getId(), TEST_QUIZ_ID); + request.setSolvedDate(LocalDate.now()); + request.setSelectedOption(TEST_RIGHT_OPTION); + + // when + solvedService.findSelectedOptionAndCompareAnswer(request); + user = userRepository.findByEmail(userService.findByToken().getEmail()); + + // then + assertEquals(solvedCnt + 1, user.getSolvedCnt()); + assertEquals(correctCnt + 1, user.getCorrectCnt()); + } + + @DisplayName("퀴즈 오답 제출시 유저 정보 업데이트 테스트") + @Test + @Transactional + void userInfoUpdateWhenWrong() throws UserNotFoundException { + // given + User user = userRepository.findByEmail(userService.findByToken().getEmail()); + int solvedCnt = user.getSolvedCnt(); + int correctCnt = user.getCorrectCnt(); + SolvedRequest request = new SolvedRequest(user.getId(), TEST_QUIZ_ID); + request.setSolvedDate(LocalDate.now()); + request.setSelectedOption(TEST_WRONG_OPTION); + + // when + solvedService.findSelectedOptionAndCompareAnswer(request); + user = userRepository.findByEmail(userService.findByToken().getEmail()); + + // then + assertEquals(solvedCnt + 1, user.getSolvedCnt()); + assertEquals(correctCnt, user.getCorrectCnt()); + } + + private void setUserToContextByUsername() { + CustomUserDetailsService customUserDetailsService = new CustomUserDetailsService(userRepository); + UserDetails userDetails = customUserDetailsService.loadUserByUsername("test@gmail.com"); + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities())); + } } \ No newline at end of file From 2509241df20a4014e58f3ea8de72e211b22a1049 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 16:07:48 +0900 Subject: [PATCH 130/134] =?UTF-8?q?feat(Solved):=20=ED=80=B4=EC=A6=88=20?= =?UTF-8?q?=EB=8B=B5=EC=95=88=20=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 퀴즈 답안 제출 시 정답, 오답 여부에 따라 유저 정보 업데이트하는 기능이 구현되었습니다. --- .../Application/service/AuthService.java | 2 ++ .../Application/service/AuthServiceImpl.java | 12 ++++++++++++ .../Application/service/SolvedServiceImpl.java | 7 ++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index e61608b..0be6ab4 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -19,4 +19,6 @@ public interface AuthService { void modifyUserInfo(UserDTO userDTO) throws UserNicknameDuplicateException, WrongInputTypeException; void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException, WrongInputTypeException; + + void updateSolvedQuiz(boolean isCorrect); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index e910bb3..dca8966 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -101,6 +101,18 @@ public void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordExceptio throw new WrongPasswordException(); } + @Override + public void updateSolvedQuiz(boolean isCorrect) { + User user = userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()); + + user.setSolvedCnt(user.getSolvedCnt() + 1); + if (isCorrect) { + user.setCorrectCnt(user.getCorrectCnt() + 1); + } + + userRepository.save(user); + } + private User userMapper(AuthDTO request) { User user = new User(); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 49c0731..578dccb 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -20,13 +20,15 @@ @Service public class SolvedServiceImpl implements SolvedService{ + private final AuthService authService; private final SolvedRepository solvedRepository; private final QuizRepository quizRepository; private final CategoryRepository categoryRepository; private final ModelMapper modelMapper; @Autowired - public SolvedServiceImpl(SolvedRepository solvedRepository, QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + public SolvedServiceImpl(AuthService authService, SolvedRepository solvedRepository, QuizRepository quizRepository, CategoryRepository categoryRepository, ModelMapper modelMapper) { + this.authService = authService; this.solvedRepository = solvedRepository; this.quizRepository = quizRepository; this.categoryRepository = categoryRepository; @@ -55,6 +57,9 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) Solved solved = modelMapper.map(solvedDTO, Solved.class); solvedRepository.save(solved); + // 사용자 정보 업데이트 + authService.updateSolvedQuiz(solvedDTO.isCorrect()); + if (answer == null || solved == null) { throw new NoSuchElementException("Quiz or Selected Option not found for quizId: " + quizId); } From 0a58ad4dff21942fd0b51645618250cf78521933 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 16:34:02 +0900 Subject: [PATCH 131/134] =?UTF-8?q?refactor(Solved=20Test):=20Solved=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 반복해 사용되는 값의 경우 상수 처리를 하고, DB DDL 변경 등의 조건에 따라 적합하게 테스트 코드를 개선했습니다. --- .../service/SolvedServiceImplTest.java | 77 +++++++------------ 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java index fdb3963..f18b5f1 100644 --- a/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java +++ b/src/test/java/org/triumers/newsnippetback/Application/service/SolvedServiceImplTest.java @@ -22,9 +22,9 @@ import java.time.LocalDate; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; +@Transactional @SpringBootTest class SolvedServiceImplTest { @@ -41,85 +41,64 @@ class SolvedServiceImplTest { @Autowired private UserRepository userRepository; - @Autowired - private PlatformTransactionManager transactionManager; - @BeforeEach void setUserToken() { setUserToContextByUsername(); } - @AfterEach - void rollback() { - TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); - transactionManager.rollback(status); - } - - @DisplayName("사용자가 입력한 답과 정답 비교하여 판정") + @DisplayName("사용자가 정답을 제출한 경우 정답 판정 내리는지 테스트") @Test - void findSelectedOptionAndCompareAnswer() { + void isCorrectIsTrueWhenRightOption() throws UserNotFoundException { // Given - int userId = 1; - int quizId = 1; - SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + int userId = userRepository.findByEmail(userService.findByToken().getEmail()).getId(); + SolvedRequest solvedRequest = new SolvedRequest(userId, TEST_QUIZ_ID); + solvedRequest.setSolvedDate(LocalDate.now()); + solvedRequest.setSelectedOption(TEST_RIGHT_OPTION); // When SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); // Then - Assertions.assertNotNull(solvedDTO); - Assertions.assertEquals(1,solvedDTO.getUserId()); - Assertions.assertEquals(1,solvedDTO.getQuizId()); - Assertions.assertEquals(true, solvedDTO.isCorrect()); + assertNotNull(solvedDTO); + assertTrue(solvedDTO.isCorrect()); } - @DisplayName("사용자가 풀었던 문제 리스트 조회") + @DisplayName("사용자가 오답을 제출한 경우 정답 판정 내리는지 테스트") @Test - void findSolvedQuizListByUserId() { + void isCorrectIsFalseWhenWrongOption() throws UserNotFoundException { // Given - int userId = 1; - int quizId = 0; + int userId = userRepository.findByEmail(userService.findByToken().getEmail()).getId(); + SolvedRequest solvedRequest = new SolvedRequest(userId, TEST_QUIZ_ID); + solvedRequest.setSolvedDate(LocalDate.now()); + solvedRequest.setSelectedOption(TEST_WRONG_OPTION); // When - SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); - List solvedList = solvedService.findSolvedQuizListByUserId(solvedRequest); + SolvedDTO solvedDTO = solvedService.findSelectedOptionAndCompareAnswer(solvedRequest); // Then - Assertions.assertNotNull(solvedList); + assertNotNull(solvedDTO); + assertFalse(solvedDTO.isCorrect()); } - @DisplayName("사용자가 풀었던 문제 한 개 조회") + @DisplayName("사용자가 풀었던 문제 리스트 조회") @Test - void findSolvedQuizByUserID() { + void findSolvedQuizListByUserId() throws UserNotFoundException { // Given - int userId = 1; - int quizId = 1; - SolvedRequest solvedRequest = new SolvedRequest(userId, quizId); + int userId = userRepository.findByEmail(userService.findByToken().getEmail()).getId(); // When - SolvedDTO solvedDTO = solvedService.findSolvedQuizByUserIdAndQuizId(solvedRequest); + SolvedRequest solvedRequest = new SolvedRequest(userId, TEST_QUIZ_ID); + List solvedList = solvedService.findSolvedQuizListByUserId(solvedRequest); // Then - Assertions.assertNotNull(solvedDTO); - Assertions.assertEquals(1, solvedDTO.getUserId()); - Assertions.assertEquals(1, solvedDTO.getQuizId()); - Assertions.assertEquals(9, solvedDTO.getCategoryId()); - Assertions.assertEquals("EELS 로봇에 관한 다음 설명 중 옳은 것은 무엇입니까?", solvedDTO.getContent()); - Assertions.assertEquals("EELS 로봇은 지구의 앨버타주에 있는 애서배스카 빙하에서 개발되었습니다.", solvedDTO.getOptionA()); - Assertions.assertEquals("이 로봇은 머리 쪽에 자율주행용 라이다와 카메라를 장착하고 있어서 스스로 움직일 수 있습니다.", solvedDTO.getOptionB()); - Assertions.assertEquals("EELS 로봇의 목표는 타이탄 위성에서의 탐사를 위한 것입니다.", solvedDTO.getOptionC()); - Assertions.assertEquals("이 로봇은 무게가 50kg이며, 액추에이터는 총 24개 달려 있습니다.", solvedDTO.getOptionD()); - Assertions.assertEquals("C",solvedDTO.getAnswer()); - Assertions.assertEquals("캐나다 앨버타주의 애서배스카 빙하에서 출발한 NASA의 로봇 탐사 임무에 대한 내용을 담고 있습니다. 이 임무는 미 항공우주국이 개발 중인 외계 생명체 탐사로봇인 EELS(일스)를 사용하여 토성의 위성 엔셀라두스에 보내는 것이 목표입니다. 이 로봇은 지구의 극한 환경에서도 작동할 수 있는 고성능을 갖추고 있으며, 엔셀라두스의 얼음 아래에 있는 바다에서 생명체를 찾는 임무를 수행할 예정입니다.", solvedDTO.getExplanation()); - Assertions.assertEquals("https://www.ytn.co.kr/_ln/0105_202404012353120871", solvedDTO.getNewsLink()); - Assertions.assertEquals(LocalDate.of(2024, 4, 2), solvedDTO.getDate()); + assertNotNull(solvedList); } @DisplayName("회원이 지정한 날짜에 맞춘 문제 조회") @Test - void findCorrectQuizByUserIdAndSolvedDate() { + void findCorrectQuizByUserIdAndSolvedDate() throws UserNotFoundException { // Given - int userId = 1; + int userId = userRepository.findByEmail(userService.findByToken().getEmail()).getId(); boolean isCorrect = true; LocalDate solvedDate = LocalDate.parse("2024-04-08"); SolvedResultRequest solvedResultRequest = new SolvedResultRequest(userId, isCorrect, solvedDate); @@ -128,7 +107,7 @@ void findCorrectQuizByUserIdAndSolvedDate() { List solvedList = solvedService.findCorrectQuizByUserIdAndSolvedDate(solvedResultRequest); // Then - Assertions.assertNotNull(solvedList); + assertNotNull(solvedList); } @DisplayName("퀴즈 정답 제출시 유저 정보 업데이트 테스트") From 1b08c9f7b664e823f1017bdf92222a3ff3747501 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 17:32:43 +0900 Subject: [PATCH 132/134] =?UTF-8?q?refactor(Solved):=20Solved=20userId=20t?= =?UTF-8?q?oken=20=EC=9D=B4=EC=9A=A9=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit userId를 프론트로부터 제공받는 것이 아닌, 로그인한 사용자의 토큰을 이용해 값을 반환하도록 개선했습니다. --- .../Application/service/AuthService.java | 2 ++ .../Application/service/AuthServiceImpl.java | 5 +++++ .../Application/service/SolvedServiceImpl.java | 11 ++++------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java index 0be6ab4..2db1a61 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthService.java @@ -21,4 +21,6 @@ public interface AuthService { void modifyPassword(PasswordDTO passwordDTO) throws WrongPasswordException, WrongInputTypeException; void updateSolvedQuiz(boolean isCorrect); + + public int getUserId(); } diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java index dca8966..022d693 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/AuthServiceImpl.java @@ -113,6 +113,11 @@ public void updateSolvedQuiz(boolean isCorrect) { userRepository.save(user); } + @Override + public int getUserId() { + return userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()).getId(); + } + private User userMapper(AuthDTO request) { User user = new User(); diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java index 578dccb..410de1f 100644 --- a/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java +++ b/src/main/java/org/triumers/newsnippetback/Application/service/SolvedServiceImpl.java @@ -41,7 +41,7 @@ public SolvedDTO findSelectedOptionAndCompareAnswer(SolvedRequest solvedRequest) modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); - int userId = solvedRequest.getUserId(); + int userId = authService.getUserId(); int quizId = solvedRequest.getQuizId(); String seletedOption = solvedRequest.getSelectedOption(); @@ -87,7 +87,7 @@ public List findSolvedQuizListByUserId(SolvedRequest solvedRequest) { /* 설명. 사용자의 ID와 문제 ID로 문제 내용 불러오기 */ @Override public SolvedDTO findSolvedQuizByUserIdAndQuizId(SolvedRequest solvedRequest) { - int userId = solvedRequest.getUserId(); + int userId = authService.getUserId(); int quizId = solvedRequest.getQuizId(); Solved solvedQuiz = solvedRepository.findSelectedOptionByUserIdAndQuizId(userId, quizId); @@ -118,7 +118,7 @@ public SolvedDTO findSolvedQuizByUserIdAndQuizId(SolvedRequest solvedRequest) { @Override public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest solvedResultRequest) { - int userId = solvedResultRequest.getUserId(); + int userId = authService.getUserId(); boolean isCorrect = solvedResultRequest.isCorrect(); LocalDate solvedDate = solvedResultRequest.getSolvedDate(); @@ -129,8 +129,7 @@ public List findCorrectQuizByUserIdAndSolvedDate(SolvedResultRequest sol @Override public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedRequest) { - System.out.println(solvedRequest); - int userId = solvedRequest.getUserId(); + int userId = authService.getUserId(); LocalDate solvedDate = solvedRequest.getSolvedDate(); List solvedList = solvedRepository.findSolvedQuizByUserIdAndSolvedDate(userId, solvedDate); @@ -161,6 +160,4 @@ public List findSolvedQuizListByUserIdAndDate(SolvedRequest solvedReq return solvedDTOList; } - - } From d49ac459b2f8662588918d9ed27628de80632db6 Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 18:05:21 +0900 Subject: [PATCH 133/134] =?UTF-8?q?feat(Security):=20Header=EC=97=90=20Use?= =?UTF-8?q?rRole=20=EC=B6=94=EA=B0=80,=20=EA=B6=8C=ED=95=9C=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UserRole을 헤더에 담아 전달하여 프론트에서 처리할 수 있도록 하였습니다. 비정상적인 접근을 차단하기 위해 SpringSecurity에서도 처리를 구현했습니다. --- .../triumers/newsnippetback/common/config/SecurityConfig.java | 3 +-- .../org/triumers/newsnippetback/common/jwt/LoginFilter.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index 2479320..cb45a73 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -95,8 +95,7 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { //경로별 인가 작업 http.authorizeHttpRequests((auth) -> auth .requestMatchers("/auth/signup").permitAll() - .requestMatchers("/**").permitAll() - .requestMatchers(("/manager")).hasAnyRole("MANAGER") + .requestMatchers(("/manage")).hasAnyRole("MANAGER") .requestMatchers("/admin").hasAnyRole("ADMIN") .anyRequest().authenticated()); diff --git a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java index 4e0f435..2280821 100644 --- a/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java +++ b/src/main/java/org/triumers/newsnippetback/common/jwt/LoginFilter.java @@ -61,6 +61,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR String token = jwtUtil.createJwt(username, role, nickname); response.addHeader("Authorization", "Bearer " + token); + response.addHeader("UserRole", role.name()); } @Override From 5a99e31d0da0396d75dcab923f6dd65e51c54e4b Mon Sep 17 00:00:00 2001 From: moomint8 Date: Tue, 16 Apr 2024 18:19:34 +0900 Subject: [PATCH 134/134] =?UTF-8?q?fix(Security):=20UserRole=20CORS=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UserRole이 CORS 처리가 되지 않아 받아지지 않던 문제를 해결했습니다. --- .../triumers/newsnippetback/common/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java index cb45a73..a30477e 100644 --- a/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/newsnippetback/common/config/SecurityConfig.java @@ -69,7 +69,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { List allowStringList = Collections.singletonList("*"); - List exposedHeaders = List.of("Authorization"); + List exposedHeaders = List.of("Authorization", "UserRole"); CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOriginPatterns(allowStringList);