diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..4baf206ff Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/backend_dev_merge_workflow.yml b/.github/workflows/backend_dev_merge_workflow.yml index 867e515ac..c2ce9e5b5 100644 --- a/.github/workflows/backend_dev_merge_workflow.yml +++ b/.github/workflows/backend_dev_merge_workflow.yml @@ -5,22 +5,54 @@ on: - dev_backend jobs: - deploy: - runs-on: naaga + github_actions_setting: + runs-on: ubuntu-latest + steps: - - name: change permission - run: | - sudo chown -R ubuntu:ubuntu /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga - - name: checkout + - name: ๐Ÿ“‚ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘... ๐Ÿ“‚ uses: actions/checkout@v3 with: submodules: true ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} - - name: remove + + - name: โ˜•๏ธ ๊นƒํ—ˆ๋ธŒ ์•ก์…˜์— JDK 17 ์ ์šฉ์‹œํ‚ค๋Š” ์ค‘... โ˜•๏ธ + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'corretto' + + - name: ๐Ÿ˜ Gradle ์„ธํŒ… ์ค‘... ๐Ÿ˜ + uses: gradle/gradle-build-action@v2.6.0 + + - name: โš™๏ธ Gradle ๋กœ JAR ํŒŒ์ผ ํ…Œ์ŠคํŠธ ๋ฐ ๋นŒ๋“œ ์ค‘... โš™๏ธ + run: | + cd backend + ./gradlew clean bootJar -Dspring.profiles.active=test + + - name: ๐Ÿณ ๋„์ปค ์„ธํŒ… ์ค‘... ๐Ÿณ + uses: docker/setup-buildx-action@v2.9.1 + + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— ๋กœ๊ทธ์ธ ์ค‘... ๐Ÿณ + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESSTOKEN }} + + - name: ๐Ÿณ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์ค‘... ๐Ÿณ run: | - sudo rm -rf /home/ubuntu/2023-naaga - - name: deploy + cd backend + docker build --platform linux/arm64/v8 -t ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} -f Dockerfile-dev . + + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— Push ์ค‘... ๐Ÿณ + run: docker push ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} + + naaga_dev_ec2_deploy: + needs: github_actions_setting + runs-on: naaga + + steps: + - name: ๐Ÿ™ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์ค‘ ... ๐Ÿ™ run: | - sudo cp -r /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga /home/ubuntu cd /home/ubuntu - sudo ./deploy.sh + sudo ./deploy_new.sh + diff --git a/.github/workflows/backend_prod_merge_workflow.yml b/.github/workflows/backend_prod_merge_workflow.yml index 9615d5c08..93cc2c7bb 100644 --- a/.github/workflows/backend_prod_merge_workflow.yml +++ b/.github/workflows/backend_prod_merge_workflow.yml @@ -6,56 +6,55 @@ on: - main paths: - backend/** - + jobs: - deploy: - runs-on: naaga + github_actions_setting: + runs-on: ubuntu-latest + steps: - - name: change permission - run: | - sudo chown -R ubuntu:ubuntu /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga - - - name: checkout + - name: ๐Ÿ“‚ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘... ๐Ÿ“‚ uses: actions/checkout@v3 with: submodules: true ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} - - - name: project remove - run: | - sudo rm -rf /home/ubuntu/prod/2023-naaga - - - name: project copy - run: | - sudo cp -r /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga /home/ubuntu/prod - - name: build + - name: โ˜•๏ธ ๊นƒํ—ˆ๋ธŒ ์•ก์…˜์— JDK 17 ์ ์šฉ์‹œํ‚ค๋Š” ์ค‘... โ˜•๏ธ + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'corretto' + + - name: ๐Ÿ˜ Gradle ์„ธํŒ… ์ค‘... ๐Ÿ˜ + uses: gradle/gradle-build-action@v2.6.0 + + - name: โš™๏ธ Gradle ๋กœ JAR ํŒŒ์ผ ํ…Œ์ŠคํŠธ ๋ฐ ๋นŒ๋“œ ์ค‘... โš™๏ธ run: | - cd /home/ubuntu/prod/2023-naaga/backend - sudo chmod +x ./gradlew - sudo ./gradlew clean bootJar - - - name: transfer & run + cd backend + ./gradlew clean bootJar -Dspring.profiles.active=test + + - name: ๐Ÿณ ๋„์ปค ์„ธํŒ… ์ค‘... ๐Ÿณ + uses: docker/setup-buildx-action@v2.9.1 + + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— ๋กœ๊ทธ์ธ ์ค‘... ๐Ÿณ + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESSTOKEN }} + + - name: ๐Ÿณ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์ค‘... ๐Ÿณ run: | - cd /home/ubuntu/prod - chmod +x ./deploy_prod.sh - sudo ./deploy_prod.sh + cd backend + docker build --platform linux/arm64/v8 -t ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} -f Dockerfile-prod . - sync-dev_backend: - needs: deploy + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— Push ์ค‘... ๐Ÿณ + run: docker push ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} - runs-on: ubuntu-latest + naaga_prod_ec2_deploy: + needs: github_actions_setting + runs-on: naaga steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: main - - - name: merge main -> dev_backend - uses: devmasx/merge-branch@master - with: - type: now - from_branch: main - target_branch: dev_backend - github_token: ${{ secrets.GITHUB_TOKEN }} + - name: ๐Ÿ™ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์ค‘ ... ๐Ÿ™ + run: | + cd /home/ubuntu/prod + sudo ./deploy_prod.sh diff --git a/.github/workflows/backend_rel_merge_workflow.yml b/.github/workflows/backend_rel_merge_workflow.yml index 3ac3a3e3b..cb0cbf714 100644 --- a/.github/workflows/backend_rel_merge_workflow.yml +++ b/.github/workflows/backend_rel_merge_workflow.yml @@ -8,53 +8,53 @@ on: - backend/** jobs: - deploy: - runs-on: naaga + github_actions_setting: + runs-on: ubuntu-latest + steps: - - name: change permission - run: | - sudo chown -R ubuntu:ubuntu /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga - - - name: checkout + - name: ๐Ÿ“‚ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘... ๐Ÿ“‚ uses: actions/checkout@v3 with: submodules: true ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} - - - name: project remove - run: | - sudo rm -rf /home/ubuntu/prod/2023-naaga - - - name: project copy - run: | - sudo cp -r /home/ubuntu/actions-runner/naaga/2023-naaga/2023-naaga /home/ubuntu/prod - - name: build + - name: โ˜•๏ธ ๊นƒํ—ˆ๋ธŒ ์•ก์…˜์— JDK 17 ์ ์šฉ์‹œํ‚ค๋Š” ์ค‘... โ˜•๏ธ + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'corretto' + + - name: ๐Ÿ˜ Gradle ์„ธํŒ… ์ค‘... ๐Ÿ˜ + uses: gradle/gradle-build-action@v2.6.0 + + - name: โš™๏ธ Gradle ๋กœ JAR ํŒŒ์ผ ํ…Œ์ŠคํŠธ ๋ฐ ๋นŒ๋“œ ์ค‘... โš™๏ธ run: | - cd /home/ubuntu/prod/2023-naaga/backend - sudo chmod +x ./gradlew - sudo ./gradlew clean bootJar - - - name: transfer & run + cd backend + ./gradlew clean bootJar -Dspring.profiles.active=test + + - name: ๐Ÿณ ๋„์ปค ์„ธํŒ… ์ค‘... ๐Ÿณ + uses: docker/setup-buildx-action@v2.9.1 + + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— ๋กœ๊ทธ์ธ ์ค‘... ๐Ÿณ + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESSTOKEN }} + + - name: ๐Ÿณ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์ค‘... ๐Ÿณ run: | - cd /home/ubuntu/prod - chmod +x ./deploy_prod.sh - sudo ./deploy_prod.sh + cd backend + docker build --platform linux/arm64/v8 -t ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} -f Dockerfile-prod . - sync-dev_backend: - needs: deploy + - name: ๐Ÿณ ๋„์ปค ํ—ˆ๋ธŒ์— Push ์ค‘... ๐Ÿณ + run: docker push ${{ secrets.DOCKERHUB_REPOSITORY }}/${{ secrets.DOCKERHUB_APPNAME }} - runs-on: ubuntu-latest + naaga_prod_ec2_deploy: + needs: github_actions_setting + runs-on: naaga steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: main - - - name: merge release -> dev_backend - uses: devmasx/merge-branch@master - with: - type: now - target_branch: dev_backend - github_token: ${{ secrets.GITHUB_TOKEN }} + - name: ๐Ÿ™ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์ค‘ ... ๐Ÿ™ + run: | + cd /home/ubuntu/prod + sudo ./deploy_prod.sh diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..f7ca5d749 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 000000000..d40a26a45 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + 1690700433917 + + + 1691559894933 + + + 1691560703833 + + + 1691561197101 + + + 1691561397354 + + + 1691561859009 + + + 1691562765815 + + + 1691562780751 + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..f5c566453 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +![์ œ๋ชฉ](etc/images/header.png) + +## ๐Ÿ’Œ ๋‚˜์•„๊ฐ€๋กœ๋ถ€ํ„ฐ์˜ ์ดˆ๋Œ€์žฅ์ด ๋„์ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค + +๋ฐ˜๋ณต๋˜๋Š” ์ธ์Šคํƒ€ ํ”ผ๋“œ๋ฅผ ๋ณด๊ณ , ๊ฒŒ์ž„์„ ํ•˜๋ฉฐ ๋ณด๋‚ด๋Š” ์ผ์ƒ์ด ์ง€๋ฃจํ•˜์ง€ ์•Š์œผ์‹œ๋‚˜์š”? +๊ณต๋ถ€์™€ ์ผ์— ์น˜์—ฌ ์‹ค๋‚ด์—์„œ ๋ณด๋‚ด๋Š” ์‹œ๊ฐ„์ด ๋งŽ์„ ํ…๋ฐ์š”. ์ž‘์€ ํ™”๋ฉด ์†์„ ๋ฒ—์–ด๋‚˜ ํ˜„์‹ค ์„ธ๊ณ„์˜ ๊ฒฝํ—˜์„ ํ•ด๋ณด๊ณ  ์‹ถ์ง€ ์•Š์œผ์‹ ๊ฐ€์š”? + +๊ทธ๋Ÿฐ ๋‹น์‹ ์„ โ€˜๋‚˜์•„๊ฐ€โ€™๋กœ ์ดˆ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿšถ๐Ÿป ์ถ”๋ฆฌ์™€ ๋ฐœ๊ฑธ์Œ์˜ ๋งŒ๋‚จ +๋‚˜์•„๊ฐ€๋Š” ํ˜„์‹ค ์„ธ๊ณ„๋ฅผ ๋ˆ„๋น„๋ฉฐ ์ง„ํ–‰๋˜๋Š” ์ถ”๋ฆฌ ๊ฒŒ์ž„์ž…๋‹ˆ๋‹ค. ๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•˜๋ฉด, ๋‹น์‹  ์ฃผ๋ณ€ ์–ด๋”˜๊ฐ€์˜ ์‚ฌ์ง„์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์ง„์ด ์•Œ์ญ๋‹ฌ์ญํ•˜์—ฌ ๊ทธ๊ณณ์ด ์–ด๋”˜์ง€ ์•Œ์•„๋งžํžˆ๊ธฐ ์–ด๋ ต๊ฒ ์ง€๋งŒ, ์šฐ์„  ๋ฐœ๊ฑธ์Œ์„ ์˜ฎ๊ฒจ๋ณด์„ธ์š”. + + + + +![์ƒ์„ธ ํŽ˜์ด์ง€](etc/images/service%20intro.png) diff --git a/android/app/src/main/res/drawable/rect_radius_small.xml b/android/app/src/main/res/drawable/rect_radius_small.xml index dadcaab82..c3ca54021 100644 --- a/android/app/src/main/res/drawable/rect_radius_small.xml +++ b/android/app/src/main/res/drawable/rect_radius_small.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/android/app/src/test/java/com/now/naaga/UploadViewModelTest.kt b/android/app/src/test/java/com/now/naaga/UploadViewModelTest.kt index c2caee1c8..6e939569a 100644 --- a/android/app/src/test/java/com/now/naaga/UploadViewModelTest.kt +++ b/android/app/src/test/java/com/now/naaga/UploadViewModelTest.kt @@ -1,110 +1,110 @@ -package com.now.naaga - -import android.text.Editable -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.now.domain.model.Coordinate -import com.now.domain.model.Place -import com.now.domain.repository.PlaceRepository -import com.now.naaga.data.throwable.DataThrowable -import com.now.naaga.presentation.upload.UploadStatus -import com.now.naaga.presentation.upload.UploadViewModel -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.mockk -import junit.framework.TestCase.assertEquals -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -class UploadViewModelTest { - private lateinit var viewModel: UploadViewModel - private lateinit var placeRepository: PlaceRepository - - // ๋ผ์ด๋ธŒ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ํ•จ - @get:Rule - val instantExecutorRule = InstantTaskExecutorRule() - - @OptIn(ExperimentalCoroutinesApi::class) - @Before - fun setup() { - Dispatchers.setMain(UnconfinedTestDispatcher()) - placeRepository = mockk() - viewModel = UploadViewModel(placeRepository) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @After - fun tearDown() { - Dispatchers.resetMain() - } - - private fun String.toEditable(): Editable { - return Editable.Factory.getInstance().newEditable(this) - } - - @Test - fun `Coordinate์ด ์ž…๋ ฅ๋˜๋ฉด ๋ทฐ๋ชจ๋ธ์˜ Coordinate๋„ ๋ฐ”๋€๋‹ค`() { - // given - val coordinate = Coordinate(123.4567, 37.890) - - // when - viewModel.setCoordinate(coordinate) - - // then - assertEquals(Coordinate(123.4567, 37.890), viewModel.coordinate.value) - } - - @Test - fun `๋ฐ์ดํ„ฐ ์ „์†ก ์„ฑ๊ณต์‹œ successUpload๊ฐ€ UploadStatus SUCCESS๋‹ค`() { - // given - val coordinate = Coordinate(123.4567, 37.890) - viewModel.setCoordinate(coordinate) - - coEvery { - placeRepository.postPlace(any(), any(), any(), any()) - } returns ( - Place( - id = 1, - name = "krrong", - coordinate = Coordinate(37.1234, 127.1234), - image = "https://img.segye.com/content/image/2021/07/29/20210729513138.jpg", - description = "android", - ) - ) - - // when - viewModel.postPlace() - - // Assert: ๊ฒฐ๊ณผ ํ™•์ธ - assertEquals(UploadStatus.SUCCESS, viewModel.successUpload.getValue()) - } - - @Test - fun `๋ฐ์ดํ„ฐ ์ „์†ก ์‹คํŒจ์‹œ successUpload๊ฐ€ UploadStatus Fail์ด๊ณ  ๋ฐ˜ํ™˜๋œ throwable์ด ์ €์žฅ๋œ๋‹ค`() { - // given - val coordinate = Coordinate(123.4567, 37.890) - viewModel.setCoordinate(coordinate) - val placeThrowable = DataThrowable.PlaceThrowable(505, "Test Failure") - coEvery { placeRepository.postPlace(any(), any(), any(), any()) } throws placeThrowable - - // when - runBlocking { viewModel.postPlace() } - - // then - // placeRepository.postPlace๊ฐ€ ์‹คํ–‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธ - coVerify { placeRepository.postPlace(any(), any(), any(), any()) } - - // successUpload๊ฐ€ UploadStatus.Fail ์ธ์ง€ ํ™•์ธ - assertEquals(UploadStatus.FAIL, viewModel.successUpload.getValue()) - - // ์˜๋„ํ•œ throwable์ด ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ - assertEquals(placeThrowable, viewModel.throwable.value) - } -} +package com.now.naaga + +import android.text.Editable +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.now.domain.model.Coordinate +import com.now.domain.model.Place +import com.now.domain.repository.PlaceRepository +import com.now.naaga.data.throwable.DataThrowable +import com.now.naaga.presentation.upload.UploadStatus +import com.now.naaga.presentation.upload.UploadViewModel +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class UploadViewModelTest { + private lateinit var viewModel: UploadViewModel + private lateinit var placeRepository: PlaceRepository + + // ๋ผ์ด๋ธŒ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ํ•จ + @get:Rule + val instantExecutorRule = InstantTaskExecutorRule() + + @OptIn(ExperimentalCoroutinesApi::class) + @Before + fun setup() { + Dispatchers.setMain(UnconfinedTestDispatcher()) + placeRepository = mockk() + viewModel = UploadViewModel(placeRepository) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @After + fun tearDown() { + Dispatchers.resetMain() + } + + private fun String.toEditable(): Editable { + return Editable.Factory.getInstance().newEditable(this) + } + + @Test + fun `Coordinate์ด ์ž…๋ ฅ๋˜๋ฉด ๋ทฐ๋ชจ๋ธ์˜ Coordinate๋„ ๋ฐ”๋€๋‹ค`() { + // given + val coordinate = Coordinate(123.4567, 37.890) + + // when + viewModel.setCoordinate(coordinate) + + // then + assertEquals(Coordinate(123.4567, 37.890), viewModel.coordinate.value) + } + + @Test + fun `๋ฐ์ดํ„ฐ ์ „์†ก ์„ฑ๊ณต์‹œ successUpload๊ฐ€ UploadStatus SUCCESS๋‹ค`() { + // given + val coordinate = Coordinate(123.4567, 37.890) + viewModel.setCoordinate(coordinate) + + coEvery { + placeRepository.postPlace(any(), any(), any(), any()) + } returns ( + Place( + id = 1, + name = "krrong", + coordinate = Coordinate(37.1234, 127.1234), + image = "https://img.segye.com/content/image/2021/07/29/20210729513138.jpg", + description = "android", + ) + ) + + // when + viewModel.postPlace() + + // Assert: ๊ฒฐ๊ณผ ํ™•์ธ + assertEquals(UploadStatus.SUCCESS, viewModel.successUpload.getValue()) + } + + @Test + fun `๋ฐ์ดํ„ฐ ์ „์†ก ์‹คํŒจ์‹œ successUpload๊ฐ€ UploadStatus Fail์ด๊ณ  ๋ฐ˜ํ™˜๋œ throwable์ด ์ €์žฅ๋œ๋‹ค`() { + // given + val coordinate = Coordinate(123.4567, 37.890) + viewModel.setCoordinate(coordinate) + val placeThrowable = DataThrowable.PlaceThrowable(505, "Test Failure") + coEvery { placeRepository.postPlace(any(), any(), any(), any()) } throws placeThrowable + + // when + runBlocking { viewModel.postPlace() } + + // then + // placeRepository.postPlace๊ฐ€ ์‹คํ–‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธ + coVerify { placeRepository.postPlace(any(), any(), any(), any()) } + + // successUpload๊ฐ€ UploadStatus.Fail ์ธ์ง€ ํ™•์ธ + assertEquals(UploadStatus.FAIL, viewModel.successUpload.getValue()) + + // ์˜๋„ํ•œ throwable์ด ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ + assertEquals(placeThrowable, viewModel.throwable.value) + } +} diff --git a/android/gradlew.bat b/android/gradlew.bat index ac1b06f93..107acd32c 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/backend/Dockerfile b/backend/Dockerfile-dev similarity index 53% rename from backend/Dockerfile rename to backend/Dockerfile-dev index e390d39c0..e19acaa9a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile-dev @@ -4,4 +4,4 @@ WORKDIR /app COPY build/libs/*.jar /app/naaga-now.jar -CMD ["java", "-jar", "naaga-now.jar"] +CMD ["java", "-jar", "-Dspring.profiles.active=dev", "naaga-now.jar"] diff --git a/backend/Dockerfile-prod b/backend/Dockerfile-prod new file mode 100644 index 000000000..e0edca8c1 --- /dev/null +++ b/backend/Dockerfile-prod @@ -0,0 +1,7 @@ +FROM amazoncorretto:17 + +WORKDIR /app + +COPY build/libs/*.jar /app/naaga-now.jar + +CMD ["java", "-jar", "-Dspring.profiles.active=prod", "naaga-now.jar"] diff --git a/backend/build.gradle b/backend/build.gradle index 11045f858..0b98200bc 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -18,10 +18,6 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' - runtimeOnly 'com.h2database:h2' - runtimeOnly 'com.mysql:mysql-connector-j' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.rest-assured:rest-assured:5.3.1' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' @@ -30,6 +26,14 @@ dependencies { implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5' implementation 'ch.qos.logback.contrib:logback-json-classic:0.1.5' implementation 'net.logstash.logback:logstash-logback-encoder:6.1' + + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + runtimeOnly 'com.h2database:h2' + runtimeOnly 'com.mysql:mysql-connector-j' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.rest-assured:rest-assured:5.3.1' } tasks.named('test') { diff --git a/backend/gradlew.bat b/backend/gradlew.bat index 6689b85be..93e3f59f1 100644 --- a/backend/gradlew.bat +++ b/backend/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/backend/src/main/java/com/now/naaga/auth/application/AuthService.java b/backend/src/main/java/com/now/naaga/auth/application/AuthService.java index 4fd9c15f7..e32c7ccab 100644 --- a/backend/src/main/java/com/now/naaga/auth/application/AuthService.java +++ b/backend/src/main/java/com/now/naaga/auth/application/AuthService.java @@ -1,17 +1,19 @@ package com.now.naaga.auth.application; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN; + import com.now.naaga.auth.application.dto.AuthCommand; -import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.application.dto.RefreshTokenCommand; import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.exception.AuthException; import com.now.naaga.auth.infrastructure.AuthClient; +import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.infrastructure.dto.MemberAuth; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; import com.now.naaga.auth.persistence.AuthRepository; +import com.now.naaga.member.application.MemberService; import com.now.naaga.member.application.dto.CreateMemberCommand; import com.now.naaga.member.application.dto.DeleteMemberCommand; -import com.now.naaga.member.application.MemberService; import com.now.naaga.member.domain.Member; import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.application.dto.CreatePlayerCommand; @@ -19,8 +21,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN; - @Transactional @Service public class AuthService { diff --git a/backend/src/main/java/com/now/naaga/auth/domain/AuthToken.java b/backend/src/main/java/com/now/naaga/auth/domain/AuthToken.java index 90b4bf306..58d9c0cca 100644 --- a/backend/src/main/java/com/now/naaga/auth/domain/AuthToken.java +++ b/backend/src/main/java/com/now/naaga/auth/domain/AuthToken.java @@ -1,8 +1,12 @@ package com.now.naaga.auth.domain; import com.now.naaga.member.domain.Member; -import jakarta.persistence.*; - +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import java.util.Objects; @Entity diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/AuthClient.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/AuthClient.java index 593b2ecde..1395b99cd 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/AuthClient.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/AuthClient.java @@ -1,8 +1,8 @@ package com.now.naaga.auth.infrastructure; -import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.exception.AuthException; import com.now.naaga.auth.exception.AuthExceptionType; +import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.infrastructure.dto.LogoutInfo; import com.now.naaga.auth.infrastructure.dto.UnlinkInfo; import org.springframework.beans.factory.annotation.Value; diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/BearerAuthExtractor.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/BearerAuthExtractor.java index 84cee103f..4f067c548 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/BearerAuthExtractor.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/BearerAuthExtractor.java @@ -1,13 +1,15 @@ package com.now.naaga.auth.infrastructure; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_HEADER; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN; +import static com.now.naaga.auth.exception.AuthExceptionType.NOT_EXIST_HEADER; + +import com.now.naaga.auth.exception.AuthException; import com.now.naaga.auth.infrastructure.dto.MemberAuth; import com.now.naaga.auth.infrastructure.jwt.JwtProvider; -import com.now.naaga.auth.exception.AuthException; import com.now.naaga.common.exception.InternalException; import org.springframework.stereotype.Component; -import static com.now.naaga.auth.exception.AuthExceptionType.*; - @Component public class BearerAuthExtractor implements AuthenticationExtractor { diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/MemberAuthMapper.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/MemberAuthMapper.java index 20765365d..7430af987 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/MemberAuthMapper.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/MemberAuthMapper.java @@ -1,13 +1,13 @@ package com.now.naaga.auth.infrastructure; +import static com.now.naaga.common.exception.InternalExceptionType.FAIL_JSON_TO_OBJECT; +import static com.now.naaga.common.exception.InternalExceptionType.FAIL_OBJECT_TO_JSON; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.now.naaga.auth.infrastructure.dto.MemberAuth; import com.now.naaga.common.exception.InternalException; -import static com.now.naaga.common.exception.InternalExceptionType.FAIL_JSON_TO_OBJECT; -import static com.now.naaga.common.exception.InternalExceptionType.FAIL_OBJECT_TO_JSON; - public class MemberAuthMapper { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/AuthInfo.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/AuthInfo.java index 0e4d2bd1d..ecf815eb1 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/AuthInfo.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/AuthInfo.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class AuthInfo { diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/MemberAuth.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/MemberAuth.java index fdb4eb29c..74ccd1bf6 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/MemberAuth.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/dto/MemberAuth.java @@ -1,7 +1,6 @@ package com.now.naaga.auth.infrastructure.dto; import com.now.naaga.auth.infrastructure.AuthType; - import java.util.Objects; public class MemberAuth { diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/AuthTokenGenerator.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/AuthTokenGenerator.java index bdd8f1775..b5733b792 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/AuthTokenGenerator.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/AuthTokenGenerator.java @@ -1,27 +1,21 @@ package com.now.naaga.auth.infrastructure.jwt; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN_ACCESS; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.exception.AuthException; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.MemberAuthMapper; import com.now.naaga.auth.infrastructure.dto.MemberAuth; -import com.now.naaga.common.exception.InternalException; -import com.now.naaga.common.exception.InternalExceptionType; import com.now.naaga.member.domain.Member; -import org.springframework.stereotype.Component; -import org.springframework.validation.ObjectError; - import java.util.Date; - -import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN_ACCESS; +import org.springframework.stereotype.Component; @Component public class AuthTokenGenerator { - private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30๋ถ„ - private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 14; // 14์ผ + private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 365 * 100; // 100๋…„ + private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 365 * 100; // 100๋…„ private final JwtProvider jwtProvider; diff --git a/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/JwtProvider.java b/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/JwtProvider.java index b3d5c6966..175a38125 100644 --- a/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/JwtProvider.java +++ b/backend/src/main/java/com/now/naaga/auth/infrastructure/jwt/JwtProvider.java @@ -2,14 +2,16 @@ import com.now.naaga.auth.exception.AuthException; import com.now.naaga.auth.exception.AuthExceptionType; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - import java.security.Key; import java.util.Date; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; @Component public class JwtProvider { diff --git a/backend/src/main/java/com/now/naaga/auth/persistence/AuthRepository.java b/backend/src/main/java/com/now/naaga/auth/persistence/AuthRepository.java index f7c5d6c1b..7be0b62f1 100644 --- a/backend/src/main/java/com/now/naaga/auth/persistence/AuthRepository.java +++ b/backend/src/main/java/com/now/naaga/auth/persistence/AuthRepository.java @@ -1,10 +1,8 @@ package com.now.naaga.auth.persistence; import com.now.naaga.auth.domain.AuthToken; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; public interface AuthRepository extends JpaRepository { diff --git a/backend/src/main/java/com/now/naaga/auth/presentation/AuthController.java b/backend/src/main/java/com/now/naaga/auth/presentation/AuthController.java index 59dca8d07..b13e39511 100644 --- a/backend/src/main/java/com/now/naaga/auth/presentation/AuthController.java +++ b/backend/src/main/java/com/now/naaga/auth/presentation/AuthController.java @@ -1,17 +1,21 @@ package com.now.naaga.auth.presentation; -import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.auth.application.AuthService; import com.now.naaga.auth.application.dto.AuthCommand; import com.now.naaga.auth.application.dto.RefreshTokenCommand; import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.dto.MemberAuth; +import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.auth.presentation.dto.AuthRequest; import com.now.naaga.auth.presentation.dto.AuthResponse; import com.now.naaga.auth.presentation.dto.RefreshTokenRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +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; @RequestMapping("/auth") @RestController diff --git a/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/MemberAuthArgumentResolver.java b/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/MemberAuthArgumentResolver.java index f41a9abd1..068a8eca2 100644 --- a/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/MemberAuthArgumentResolver.java +++ b/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/MemberAuthArgumentResolver.java @@ -1,9 +1,8 @@ package com.now.naaga.auth.presentation.argumentresolver; -import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.auth.infrastructure.AuthenticationExtractor; import com.now.naaga.auth.infrastructure.dto.MemberAuth; -import com.now.naaga.auth.presentation.dto.MemberRequest; +import com.now.naaga.auth.presentation.annotation.Auth; import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; diff --git a/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/PlayerArgumentResolver.java b/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/PlayerArgumentResolver.java index 110492546..4353aefcf 100644 --- a/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/PlayerArgumentResolver.java +++ b/backend/src/main/java/com/now/naaga/auth/presentation/argumentresolver/PlayerArgumentResolver.java @@ -1,8 +1,8 @@ package com.now.naaga.auth.presentation.argumentresolver; -import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.auth.infrastructure.AuthenticationExtractor; import com.now.naaga.auth.infrastructure.dto.MemberAuth; +import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.domain.Player; import com.now.naaga.player.presentation.dto.PlayerRequest; diff --git a/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/AuthInterceptor.java b/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/AuthInterceptor.java index 54cc716dd..6966a5a7c 100644 --- a/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/AuthInterceptor.java +++ b/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/AuthInterceptor.java @@ -2,7 +2,6 @@ import com.now.naaga.auth.infrastructure.AuthenticationExtractor; import com.now.naaga.auth.infrastructure.dto.MemberAuth; -import com.now.naaga.member.domain.Member; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpHeaders; diff --git a/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptor.java b/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptor.java index 62ffb63c4..85e20f591 100644 --- a/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptor.java +++ b/backend/src/main/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptor.java @@ -1,19 +1,20 @@ package com.now.naaga.auth.presentation.interceptor; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_HEADER; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_MANAGER; +import static com.now.naaga.auth.exception.AuthExceptionType.NOT_EXIST_HEADER; + import com.now.naaga.auth.exception.AuthException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpHeaders; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Objects; - -import static com.now.naaga.auth.exception.AuthExceptionType.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; @Component public class ManagerAuthInterceptor implements HandlerInterceptor { diff --git a/backend/src/main/java/com/now/naaga/common/config/BeanConfig.java b/backend/src/main/java/com/now/naaga/common/config/BeanConfig.java index d6ad7c4a4..37d3d9583 100644 --- a/backend/src/main/java/com/now/naaga/common/config/BeanConfig.java +++ b/backend/src/main/java/com/now/naaga/common/config/BeanConfig.java @@ -1,10 +1,13 @@ package com.now.naaga.common.config; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.fasterxml.jackson.databind.ObjectMapper; import com.now.naaga.gameresult.domain.gamescore.FailResultScorePolicy; import com.now.naaga.gameresult.domain.gamescore.ResultScoreCalculator; import com.now.naaga.gameresult.domain.gamescore.ResultScorePolicy; import com.now.naaga.gameresult.domain.gamescore.SuccessResultScorePolicy; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @@ -14,6 +17,9 @@ @Configuration public class BeanConfig { + @Value("${cloud.aws.region.static}") + private String clientRegion; + @Bean public RestTemplate restTemplate() { return new RestTemplate(); @@ -42,4 +48,11 @@ public ResultScoreCalculator gameScoreCalculator() { ); return new ResultScoreCalculator(gameScorePolicies); } + + @Bean + public AmazonS3 amazonS3() { + return AmazonS3ClientBuilder.standard() + .withRegion(clientRegion) + .build(); + } } diff --git a/backend/src/main/java/com/now/naaga/common/config/HibernateConfig.java b/backend/src/main/java/com/now/naaga/common/config/HibernateConfig.java index f84ef50e0..26af49c82 100644 --- a/backend/src/main/java/com/now/naaga/common/config/HibernateConfig.java +++ b/backend/src/main/java/com/now/naaga/common/config/HibernateConfig.java @@ -1,12 +1,12 @@ package com.now.naaga.common.config; +import static org.hibernate.cfg.AvailableSettings.STATEMENT_INSPECTOR; + import com.now.naaga.common.presentation.QueryInspector; import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import static org.hibernate.cfg.AvailableSettings.STATEMENT_INSPECTOR; - @Configuration public class HibernateConfig { diff --git a/backend/src/main/java/com/now/naaga/common/config/WebConfig.java b/backend/src/main/java/com/now/naaga/common/config/WebConfig.java index 5014a2bcf..20f444f03 100644 --- a/backend/src/main/java/com/now/naaga/common/config/WebConfig.java +++ b/backend/src/main/java/com/now/naaga/common/config/WebConfig.java @@ -59,7 +59,8 @@ private HandlerInterceptor mapAuthInterceptor() { .excludeRequestPattern("/**", HttpMethod.OPTIONS) .excludeRequestPattern("/temporary-places", HttpMethod.GET) .excludeRequestPattern("/places", HttpMethod.POST) - .excludeRequestPattern("/temporary-places/**", HttpMethod.DELETE); + .excludeRequestPattern("/temporary-places/**", HttpMethod.DELETE) + .excludeRequestPattern("/places/{placeId}/likes/count"); } private HandlerInterceptor mapManagerAuthInterceptor() { diff --git a/backend/src/main/java/com/now/naaga/common/domain/BaseEntity.java b/backend/src/main/java/com/now/naaga/common/domain/BaseEntity.java index 4ee21d732..faa556bda 100644 --- a/backend/src/main/java/com/now/naaga/common/domain/BaseEntity.java +++ b/backend/src/main/java/com/now/naaga/common/domain/BaseEntity.java @@ -16,6 +16,7 @@ public abstract class BaseEntity { @Column(nullable = false, updatable = false) private LocalDateTime createdAt; + // TODO: 10/19/23 ์—…๋ฐ์ดํŠธ ์‹œ๊ฐ„ ์–ด๋””ํŒ… ์•ˆ๋˜๋Š” ๊ฒƒ ์ˆ˜์ • ํ•„์š” @LastModifiedBy private LocalDateTime updatedAt; diff --git a/backend/src/main/java/com/now/naaga/common/exception/CommonExceptionType.java b/backend/src/main/java/com/now/naaga/common/exception/CommonExceptionType.java index 6d6c8e341..863526174 100644 --- a/backend/src/main/java/com/now/naaga/common/exception/CommonExceptionType.java +++ b/backend/src/main/java/com/now/naaga/common/exception/CommonExceptionType.java @@ -21,7 +21,12 @@ public enum CommonExceptionType implements BaseExceptionType { HttpStatus.BAD_REQUEST, "ํŒŒ์ผ ์ €์žฅํ•˜๋‹ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." ), - ; + + MULTIPART_FILE_NOT_EXIST( + 224, + HttpStatus.BAD_REQUEST, + "์ „์†กํ•œ ํŒŒ์ผ์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค." + ); private final int errorCode; private final HttpStatus httpStatus; diff --git a/backend/src/main/java/com/now/naaga/common/infrastructure/AwsS3FileManager.java b/backend/src/main/java/com/now/naaga/common/infrastructure/AwsS3FileManager.java new file mode 100644 index 000000000..225c4f707 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/common/infrastructure/AwsS3FileManager.java @@ -0,0 +1,69 @@ +package com.now.naaga.common.infrastructure; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.now.naaga.common.exception.CommonException; +import com.now.naaga.common.exception.CommonExceptionType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +@Component +public class AwsS3FileManager { + + private static final String FILE_EXTENSION_SEPARATOR = "."; + + private final AmazonS3 amazonS3; + + private final String bucketName; + + public AwsS3FileManager(final AmazonS3 amazonS3, + @Value("${cloud.aws.s3.bucket}") final String bucketName) { + this.amazonS3 = amazonS3; + this.bucketName = bucketName; + } + + public String uploadFile(final MultipartFile multipartFile) { + validateFileExists(multipartFile); + final String fileName = buildFileName(multipartFile.getOriginalFilename()); + final ObjectMetadata objectMetadata = initializeObjectMetadata(multipartFile); + + try (final InputStream inputStream = multipartFile.getInputStream()) { + amazonS3.putObject(new PutObjectRequest(bucketName, fileName, inputStream, objectMetadata)); + } catch (IOException e) { + throw new IllegalArgumentException(); + } + + return amazonS3.getUrl(bucketName, fileName).toString(); + } + + private void validateFileExists(final MultipartFile multipartFile) { + if (multipartFile.isEmpty()) { + throw new CommonException(CommonExceptionType.MULTIPART_FILE_NOT_EXIST); + } + } + + private String buildFileName(final String originalFileName) { + final String extension = originalFileName.substring(originalFileName.lastIndexOf(FILE_EXTENSION_SEPARATOR)); + return UUID.randomUUID() + extension; + } + + private ObjectMetadata initializeObjectMetadata(final MultipartFile multipartFile) { + final ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(multipartFile.getSize()); + objectMetadata.setContentType(multipartFile.getContentType()); + return objectMetadata; + } + + //๋ฒ„์ „์ด ์ง€์ •๋œ ๋ฒ„ํ‚ท ๊ฐ์ฒด ์‚ญ์ œ + public void deleteFile(String imageUrl) { + String fileName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1); + amazonS3.deleteObject(new DeleteObjectRequest(bucketName, fileName)); + } +} diff --git a/backend/src/main/java/com/now/naaga/common/infrastructure/FileManager.java b/backend/src/main/java/com/now/naaga/common/infrastructure/FileManager.java deleted file mode 100644 index c7a05e14a..000000000 --- a/backend/src/main/java/com/now/naaga/common/infrastructure/FileManager.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.now.naaga.common.infrastructure; - -import java.io.File; - -public interface FileManager { - - File save(T t); - - String convertToUrlPath(File file); -} diff --git a/backend/src/main/java/com/now/naaga/common/infrastructure/MultipartFileManager.java b/backend/src/main/java/com/now/naaga/common/infrastructure/MultipartFileManager.java deleted file mode 100644 index 03dadefa9..000000000 --- a/backend/src/main/java/com/now/naaga/common/infrastructure/MultipartFileManager.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.now.naaga.common.infrastructure; - -import com.now.naaga.common.exception.CommonException; -import com.now.naaga.common.exception.CommonExceptionType; -import java.io.File; -import java.io.IOException; -import java.util.Objects; -import java.util.UUID; - -import com.now.naaga.common.exception.InternalException; -import com.now.naaga.common.exception.InternalExceptionType; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_BODY; -import static com.now.naaga.common.exception.InternalExceptionType.FAIL_MAKE_DIRECTORY; - -@Component -public class MultipartFileManager implements FileManager { - - private final String saveDirectory; - - private final String imagesUrlPrefix; - - public MultipartFileManager(@Value("${image.path.directory.prefix}") final String saveDirectory, - @Value("${image.path.url.prefix}") final String imagesUrlPrefix) { - this.saveDirectory = saveDirectory; - this.imagesUrlPrefix = imagesUrlPrefix; - } - - @Override - public File save(final MultipartFile multipartFile) { - final File directory = new File(saveDirectory); - if (!directory.exists()) { - if (directory.mkdirs()) { - throw new InternalException(FAIL_MAKE_DIRECTORY); - } - } - final String originalFilename = multipartFile.getOriginalFilename(); - if (Objects.isNull(originalFilename)) { - throw new CommonException(INVALID_REQUEST_BODY); - } - final String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); - final String savedFilename = UUID.randomUUID() + extension; - final File uploadPath = new File(saveDirectory, savedFilename); - try { - multipartFile.transferTo(uploadPath); - } catch (IOException e) { - throw new CommonException(CommonExceptionType.FILE_SAVE_ERROR); - } - return uploadPath; - } - - @Override - public String convertToUrlPath(final File file) { - final String filePath = file.toString(); - final String fileNameIncludeDirectorySeparator = filePath.replaceAll(saveDirectory, ""); - return imagesUrlPrefix + fileNameIncludeDirectorySeparator; - } -} diff --git a/backend/src/main/java/com/now/naaga/common/presentation/LogFilter.java b/backend/src/main/java/com/now/naaga/common/presentation/LogFilter.java index cad62bbd7..acc69107e 100644 --- a/backend/src/main/java/com/now/naaga/common/presentation/LogFilter.java +++ b/backend/src/main/java/com/now/naaga/common/presentation/LogFilter.java @@ -1,16 +1,19 @@ package com.now.naaga.common.presentation; -import jakarta.servlet.*; +import static com.now.naaga.common.presentation.MdcToken.REQUEST_ID; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import java.io.IOException; -import java.util.UUID; - -import static com.now.naaga.common.presentation.MdcToken.*; - public class LogFilter implements Filter { private static final String LOG_FORMAT = "uri: {}, method: {}, time: {}ms, queryCount: {}"; diff --git a/backend/src/main/java/com/now/naaga/common/presentation/QueryInspector.java b/backend/src/main/java/com/now/naaga/common/presentation/QueryInspector.java index 51e67bba3..216ea91c8 100644 --- a/backend/src/main/java/com/now/naaga/common/presentation/QueryInspector.java +++ b/backend/src/main/java/com/now/naaga/common/presentation/QueryInspector.java @@ -1,13 +1,10 @@ package com.now.naaga.common.presentation; +import java.util.Objects; import org.hibernate.resource.jdbc.spi.StatementInspector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; -import java.util.Objects; - @Component public class QueryInspector implements StatementInspector { diff --git a/backend/src/main/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptor.java b/backend/src/main/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptor.java index f13c7f3da..ade990797 100644 --- a/backend/src/main/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptor.java +++ b/backend/src/main/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptor.java @@ -2,11 +2,10 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.HttpMethod; -import org.springframework.web.servlet.HandlerInterceptor; - import java.util.ArrayList; import java.util.List; +import org.springframework.http.HttpMethod; +import org.springframework.web.servlet.HandlerInterceptor; public class RequestMatcherInterceptor implements HandlerInterceptor { diff --git a/backend/src/main/java/com/now/naaga/game/application/GameService.java b/backend/src/main/java/com/now/naaga/game/application/GameService.java index ccfde8e65..d2c4bc3ad 100644 --- a/backend/src/main/java/com/now/naaga/game/application/GameService.java +++ b/backend/src/main/java/com/now/naaga/game/application/GameService.java @@ -1,16 +1,29 @@ package com.now.naaga.game.application; -import com.now.naaga.game.application.dto.*; -import com.now.naaga.game.domain.*; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.ALREADY_IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.CAN_NOT_FIND_PLACE; +import static com.now.naaga.game.exception.GameExceptionType.NOT_EXIST; +import static com.now.naaga.game.exception.GameExceptionType.NOT_EXIST_IN_PROGRESS; +import static com.now.naaga.gameresult.exception.GameResultExceptionType.GAME_RESULT_NOT_EXIST; + +import com.now.naaga.game.application.dto.CreateGameCommand; +import com.now.naaga.game.application.dto.CreateGameResultCommand; +import com.now.naaga.game.application.dto.EndGameCommand; +import com.now.naaga.game.application.dto.FindAllGamesCommand; +import com.now.naaga.game.application.dto.FindGameByIdCommand; +import com.now.naaga.game.application.dto.FindGameByStatusCommand; +import com.now.naaga.game.application.dto.FindGameInProgressCommand; +import com.now.naaga.game.domain.EndType; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.domain.GameRecord; +import com.now.naaga.game.domain.Statistic; import com.now.naaga.game.exception.GameException; import com.now.naaga.game.exception.GameNotFinishedException; import com.now.naaga.game.repository.GameRepository; - -// TODO: 8/31/23 ์ œ๊ฑฐํ•  ๋Œ€์ƒ - ์ด์Šˆ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์ผ๋‹จ์€ ์ œ๊ฑฐํ•˜์ง€ ์•Š์Œ import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.gameresult.exception.GameResultException; import com.now.naaga.gameresult.repository.GameResultRepository; - import com.now.naaga.place.application.PlaceService; import com.now.naaga.place.application.dto.RecommendPlaceCommand; import com.now.naaga.place.domain.Place; @@ -19,32 +32,25 @@ import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.domain.Player; import com.now.naaga.player.presentation.dto.PlayerRequest; +import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - -import static com.now.naaga.game.exception.GameExceptionType.*; - -// TODO: 8/31/23 ์ œ๊ฑฐํ•  ๋Œ€์ƒ - ์ด์Šˆ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์ผ๋‹จ์€ ์ œ๊ฑฐํ•˜์ง€ ์•Š์Œ -import static com.now.naaga.gameresult.exception.GameResultExceptionType.GAME_RESULT_NOT_EXIST; - @Transactional @Service public class GameService { - + private final GameRepository gameRepository; - + // TODO: 8/31/23 ์ œ๊ฑฐํ•  ๋Œ€์ƒ - ์ด์Šˆ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์ผ๋‹จ์€ ์ œ๊ฑฐํ•˜์ง€ ์•Š์Œ private final GameResultRepository gameResultRepository; - + private final PlayerService playerService; - + private final PlaceService placeService; - + private final GameFinishService gameFinishService; - + public GameService(final GameRepository gameRepository, final GameResultRepository gameResultRepository, final PlayerService playerService, @@ -56,11 +62,11 @@ public GameService(final GameRepository gameRepository, this.placeService = placeService; this.gameFinishService = gameFinishService; } - + public Game createGame(final CreateGameCommand createGameCommand) { final Player player = playerService.findPlayerById(createGameCommand.playerId()); final List gamesByStatus = gameRepository.findByPlayerIdAndGameStatus(player.getId(), - GameStatus.IN_PROGRESS); + IN_PROGRESS); if (!gamesByStatus.isEmpty()) { throw new GameException(ALREADY_IN_PROGRESS); } @@ -74,79 +80,88 @@ public Game createGame(final CreateGameCommand createGameCommand) { throw new GameException(CAN_NOT_FIND_PLACE); } } - + @Transactional(noRollbackFor = {GameNotFinishedException.class}) public void endGame(final EndGameCommand endGameCommand) { final Game game = gameRepository.findById(endGameCommand.gameId()).orElseThrow(() -> new GameException(NOT_EXIST)); final Player player = playerService.findPlayerById(endGameCommand.playerId()); game.validateOwner(player); - + final EndType endType = endGameCommand.endType(); final Position position = endGameCommand.position(); - + game.endGame(position, endType); - + final CreateGameResultCommand createGameResultCommand = new CreateGameResultCommand(player, game, position, endType); gameFinishService.createGameResult(createGameResultCommand); } - + @Transactional(readOnly = true) public Game findGameById(final FindGameByIdCommand findGameByIdCommand) { final Player player = playerService.findPlayerById(findGameByIdCommand.playerId()); final Game game = gameRepository.findById(findGameByIdCommand.gameId()) - .orElseThrow(() -> new GameException(NOT_EXIST)); + .orElseThrow(() -> new GameException(NOT_EXIST)); game.validateOwner(player); return game; } - + @Transactional(readOnly = true) public List findGamesByStatus(final FindGameByStatusCommand findGameByStatusCommand) { Player player = playerService.findPlayerById(findGameByStatusCommand.playerId()); return gameRepository.findByPlayerIdAndGameStatus(player.getId(), findGameByStatusCommand.gameStatus()); } - + @Transactional(readOnly = true) public GameResult findGameResultByGameId(final Long gameId) { final List gameResultsByGameId = gameResultRepository.findByGameId(gameId); - + if (gameResultsByGameId.isEmpty()) { throw new GameResultException(GAME_RESULT_NOT_EXIST); } - + return gameResultsByGameId.get(0); } - + @Transactional(readOnly = true) public GameRecord findGameResult(final Long gameId) { final GameResult gameResult = findGameResultByGameId(gameId); return GameRecord.from(gameResult); } - + @Transactional(readOnly = true) public List findAllGameResult(final PlayerRequest playerRequest) { final Long playerId = playerRequest.playerId(); final List gameResults = gameResultRepository.findByPlayerId(playerId); - + final List sortedGameResults = gameResults.stream() - .sorted((gr1, gr2) -> gr2.getCreatedAt().compareTo(gr1.getCreatedAt())) - .toList(); - + .sorted((gr1, gr2) -> gr2.getCreatedAt().compareTo(gr1.getCreatedAt())) + .toList(); + return sortedGameResults.stream() - .map(GameRecord::from) - .toList(); + .map(GameRecord::from) + .toList(); } - + @Transactional(readOnly = true) public Statistic findStatistic(final PlayerRequest playerRequest) { final List gameResults = gameResultRepository.findByPlayerId(playerRequest.playerId()); final List gameRecords = gameResults.stream() - .map(GameRecord::from).toList(); - + .map(GameRecord::from).toList(); + return Statistic.of(gameRecords); } - + @Transactional(readOnly = true) public List findAllGames(FindAllGamesCommand findAllGamesCommand) { return gameRepository.findByPlayerId(findAllGamesCommand.playerId()); } + + @Transactional(readOnly = true) + public Game findGameInProgress(final FindGameInProgressCommand findGameByStatusCommand) { + List gameInProgress = gameRepository.findByPlayerIdAndGameStatus(findGameByStatusCommand.playerId(), IN_PROGRESS); + if (gameInProgress.isEmpty()) { + throw new GameException(NOT_EXIST_IN_PROGRESS); + } + return gameInProgress.get(0); + } } diff --git a/backend/src/main/java/com/now/naaga/game/application/HintService.java b/backend/src/main/java/com/now/naaga/game/application/HintService.java index cedb687c9..b4e74cc6d 100644 --- a/backend/src/main/java/com/now/naaga/game/application/HintService.java +++ b/backend/src/main/java/com/now/naaga/game/application/HintService.java @@ -1,5 +1,8 @@ package com.now.naaga.game.application; +import static com.now.naaga.game.exception.GameExceptionType.HINTS_EXHAUSTED; +import static com.now.naaga.game.exception.GameExceptionType.HINT_NOT_EXIST_IN_GAME; + import com.now.naaga.game.application.dto.CreateHintCommand; import com.now.naaga.game.application.dto.FindGameByIdCommand; import com.now.naaga.game.application.dto.FindHintByIdCommand; @@ -13,9 +16,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.now.naaga.game.exception.GameExceptionType.HINTS_EXHAUSTED; -import static com.now.naaga.game.exception.GameExceptionType.HINT_NOT_EXIST_IN_GAME; - @Transactional @Service public class HintService { diff --git a/backend/src/main/java/com/now/naaga/game/application/dto/FindGameInProgressCommand.java b/backend/src/main/java/com/now/naaga/game/application/dto/FindGameInProgressCommand.java new file mode 100644 index 000000000..584933e40 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/game/application/dto/FindGameInProgressCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.game.application.dto; + +public record FindGameInProgressCommand(Long playerId) { +} diff --git a/backend/src/main/java/com/now/naaga/game/domain/Game.java b/backend/src/main/java/com/now/naaga/game/domain/Game.java index 2ddb4b9ae..882b74145 100644 --- a/backend/src/main/java/com/now/naaga/game/domain/Game.java +++ b/backend/src/main/java/com/now/naaga/game/domain/Game.java @@ -1,5 +1,14 @@ package com.now.naaga.game.domain; +import static com.now.naaga.game.domain.EndType.ARRIVED; +import static com.now.naaga.game.domain.EndType.GIVE_UP; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.ALREADY_DONE; +import static com.now.naaga.game.exception.GameExceptionType.INACCESSIBLE_AUTHENTICATION; +import static com.now.naaga.game.exception.GameExceptionType.NOT_ARRIVED; +import static com.now.naaga.game.exception.GameExceptionType.NOT_DONE; + import com.now.naaga.common.domain.BaseEntity; import com.now.naaga.game.exception.GameException; import com.now.naaga.game.exception.GameExceptionType; @@ -7,19 +16,24 @@ import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; import com.now.naaga.player.domain.Player; -import jakarta.persistence.*; - +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import static com.now.naaga.game.domain.EndType.ARRIVED; -import static com.now.naaga.game.domain.EndType.GIVE_UP; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; -import static com.now.naaga.game.exception.GameExceptionType.*; - @Entity public class Game extends BaseEntity { diff --git a/backend/src/main/java/com/now/naaga/game/domain/GameResult.java b/backend/src/main/java/com/now/naaga/game/domain/GameResult.java deleted file mode 100644 index 3ed81d94e..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/GameResult.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.now.naaga.game.domain; - -import com.now.naaga.common.domain.BaseEntity; -import com.now.naaga.score.domain.Score; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; -import java.util.Objects; - -@Entity -public class GameResult extends BaseEntity { - - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Id - private Long id; - - @Enumerated(EnumType.STRING) - private ResultType resultType; - - @Embedded - private Score score; - - @OneToOne - @JoinColumn(name = "game_id") - private Game game; - - protected GameResult() { - } - - public GameResult(final ResultType resultType, - final Score score, - final Game game) { - this(null, resultType, score, game); - } - - public GameResult(final Long id, - final ResultType resultType, - final Score score, - final Game game) { - this.id = id; - this.resultType = resultType; - this.score = score; - this.game = game; - } - - public Long getId() { - return id; - } - - public ResultType getResultType() { - return resultType; - } - - public Score getScore() { - return score; - } - - public Game getGame() { - return game; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final GameResult that = (GameResult) o; - return Objects.equals(id, that.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } - - @Override - public String toString() { - return "GameResult{" + - "id=" + id + - ", resultType=" + resultType + - ", score=" + score + - ", gameId=" + game.getId() + - '}'; - } -} diff --git a/backend/src/main/java/com/now/naaga/game/domain/ResultType.java b/backend/src/main/java/com/now/naaga/game/domain/ResultType.java deleted file mode 100644 index 83ca772f1..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/ResultType.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.now.naaga.game.domain; - -public enum ResultType { - - SUCCESS, - FAIL, -} diff --git a/backend/src/main/java/com/now/naaga/game/domain/Statistic.java b/backend/src/main/java/com/now/naaga/game/domain/Statistic.java index 214bba3b9..f627145ff 100644 --- a/backend/src/main/java/com/now/naaga/game/domain/Statistic.java +++ b/backend/src/main/java/com/now/naaga/game/domain/Statistic.java @@ -1,11 +1,11 @@ package com.now.naaga.game.domain; -import java.time.Duration; -import java.util.List; - import static com.now.naaga.gameresult.domain.ResultType.FAIL; import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import java.time.Duration; +import java.util.List; + public class Statistic { private int gameCount; diff --git a/backend/src/main/java/com/now/naaga/game/domain/gamescore/FailGameScorePolicy.java b/backend/src/main/java/com/now/naaga/game/domain/gamescore/FailGameScorePolicy.java deleted file mode 100644 index 66c3a6049..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/gamescore/FailGameScorePolicy.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.now.naaga.game.domain.gamescore; - -import com.now.naaga.game.domain.Game; -import com.now.naaga.game.domain.ResultType; -import com.now.naaga.score.domain.Score; - -import static com.now.naaga.game.domain.ResultType.FAIL; - -public class FailGameScorePolicy implements GameScorePolicy { - - @Override - public Score calculate(final Game game) { - return new Score(0); - } - - @Override - public boolean hasSameResultType(final ResultType resultType) { - return resultType == FAIL; - } -} diff --git a/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScoreCalculator.java b/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScoreCalculator.java deleted file mode 100644 index 6e909e05a..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScoreCalculator.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.now.naaga.game.domain.gamescore; - -import com.now.naaga.common.exception.InternalException; -import com.now.naaga.game.domain.Game; -import com.now.naaga.game.domain.ResultType; -import com.now.naaga.score.domain.Score; - -import java.util.List; - -import static com.now.naaga.common.exception.InternalExceptionType.FAIL_ESTABLISH_GAME_SCORE_POLICY; - -public class GameScoreCalculator { - - private final List scorePolicies; - - public GameScoreCalculator(final List scorePolicies) { - this.scorePolicies = scorePolicies; - } - - public Score calculate(final Game game, - final ResultType resultType) { - final GameScorePolicy gameScorePolicy = scorePolicies.stream() - .filter(policy -> policy.hasSameResultType(resultType)) - .findAny() - .orElseThrow(() -> new InternalException(FAIL_ESTABLISH_GAME_SCORE_POLICY)); - return gameScorePolicy.calculate(game); - } -} diff --git a/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScorePolicy.java b/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScorePolicy.java deleted file mode 100644 index fd669845a..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/gamescore/GameScorePolicy.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.now.naaga.game.domain.gamescore; - -import com.now.naaga.game.domain.Game; -import com.now.naaga.game.domain.ResultType; -import com.now.naaga.score.domain.Score; - -public interface GameScorePolicy { - - Score calculate(final Game game); - - boolean hasSameResultType(final ResultType resultType); -} diff --git a/backend/src/main/java/com/now/naaga/game/domain/gamescore/SuccessGameScorePolicy.java b/backend/src/main/java/com/now/naaga/game/domain/gamescore/SuccessGameScorePolicy.java deleted file mode 100644 index 2eb1e2d54..000000000 --- a/backend/src/main/java/com/now/naaga/game/domain/gamescore/SuccessGameScorePolicy.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.now.naaga.game.domain.gamescore; - -import com.now.naaga.game.domain.Game; -import com.now.naaga.game.domain.ResultType; -import com.now.naaga.score.domain.Score; - -import java.time.Duration; -import java.time.LocalDateTime; - -import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; -import static com.now.naaga.game.domain.Game.MAX_HINT_COUNT; -import static com.now.naaga.game.domain.ResultType.SUCCESS; - -public class SuccessGameScorePolicy implements GameScorePolicy { - - private static final Score BASE_SCORE = new Score(50); - private static final double HINT_SCORE_RATIO = 0.3; - private static final double ATTEMPT_SCORE_RATIO = 0.3; - private static final double AVERAGE_SPEED = (double) 10 / 9; - - @Override - public Score calculate(final Game game) { - return BASE_SCORE - .plus(calculateHintScore(game)) - .plus(calculateAttemptScore(game)) - .plus(calculateTimeScore(game)); - } - - @Override - public boolean hasSameResultType(final ResultType resultType) { - return resultType == SUCCESS; - } - - private Score calculateHintScore(final Game game) { - final int usedHintCount = game.getHints().size(); - final double maxHintScore = BASE_SCORE.getValue() * HINT_SCORE_RATIO; - final double hintScoreValue = maxHintScore - ((maxHintScore / MAX_HINT_COUNT) * usedHintCount); - return new Score((int) hintScoreValue); - } - - private Score calculateAttemptScore(final Game game) { - final int remainingAttempt = game.getRemainingAttempts(); - final int maxPossibleAttempts = MAX_ATTEMPT_COUNT - 1; - final double maxAttemptScore = BASE_SCORE.getValue() * ATTEMPT_SCORE_RATIO; - final double attemptScoreValue = maxAttemptScore / maxPossibleAttempts * remainingAttempt; - return new Score((int) attemptScoreValue); - } - - private Score calculateTimeScore(final Game game) { - final double distance = game.findDistance(); - final long playTimeInSecond = findDurationInSecond(game); - final double slope = AVERAGE_SPEED / (distance * BASE_SCORE.getValue()); - final double shiftX = distance / AVERAGE_SPEED; - final double timeScoreValue = 1 / (slope * (playTimeInSecond + shiftX)); - return new Score((int) timeScoreValue); - } - - private long findDurationInSecond(final Game game) { - final LocalDateTime startTime = game.getStartTime(); - final LocalDateTime endTime = game.getEndTime(); - final Duration duration = Duration.between(startTime, endTime); - return duration.toSeconds(); - } -} diff --git a/backend/src/main/java/com/now/naaga/game/exception/GameExceptionType.java b/backend/src/main/java/com/now/naaga/game/exception/GameExceptionType.java index 87df21fa2..27bbb3b75 100644 --- a/backend/src/main/java/com/now/naaga/game/exception/GameExceptionType.java +++ b/backend/src/main/java/com/now/naaga/game/exception/GameExceptionType.java @@ -28,6 +28,12 @@ public enum GameExceptionType implements BaseExceptionType { HttpStatus.BAD_REQUEST, "๋ฐฐ์ • ํ•  ๋ชฉ์ ์ง€๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค." ), + + NOT_EXIST_IN_PROGRESS( + 414, + HttpStatus.NOT_FOUND, + "์ง„ํ–‰ ์ค‘์ธ ๊ฒŒ์ž„์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค" + ), NOT_ARRIVED( 415, @@ -47,6 +53,12 @@ public enum GameExceptionType implements BaseExceptionType { "์•„์ง ์ข…๋ฃŒ๋˜์ง€ ์•Š์€ ๊ฒŒ์ž„์ž…๋‹ˆ๋‹ค." ), + NOT_REMAIN_ATTEMPTS( + 418, + HttpStatus.BAD_REQUEST, + "์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ์ด๋ฏธ ๋‹ค ์‚ฌ์šฉํ•œ ๊ฒŒ์ž„์ž…๋‹ˆ๋‹ค" + ), + HINT_NOT_EXIST_IN_GAME( 454, HttpStatus.NOT_FOUND, @@ -58,12 +70,6 @@ public enum GameExceptionType implements BaseExceptionType { HttpStatus.BAD_REQUEST, "์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํžŒํŠธ๋ฅผ ๋ชจ๋‘ ์†Œ์ง„ํ–ˆ์Šต๋‹ˆ๋‹ค." ), - - NOT_REMAIN_ATTEMPTS( - 418, - HttpStatus.BAD_REQUEST, - "์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ์ด๋ฏธ ๋‹ค ์‚ฌ์šฉํ•œ ๊ฒŒ์ž„์ž…๋‹ˆ๋‹ค" - ), ; private final int errorCode; diff --git a/backend/src/main/java/com/now/naaga/game/exception/GameNotArrivalException.java b/backend/src/main/java/com/now/naaga/game/exception/GameNotArrivalException.java deleted file mode 100644 index 7ce3e9208..000000000 --- a/backend/src/main/java/com/now/naaga/game/exception/GameNotArrivalException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.now.naaga.game.exception; - -public class GameNotArrivalException extends GameException{ - - public GameNotArrivalException(GameExceptionType gameExceptionType) { - super(gameExceptionType); - } -} diff --git a/backend/src/main/java/com/now/naaga/game/presentation/GameController.java b/backend/src/main/java/com/now/naaga/game/presentation/GameController.java index d7f55eb7a..9b5b4bfb7 100644 --- a/backend/src/main/java/com/now/naaga/game/presentation/GameController.java +++ b/backend/src/main/java/com/now/naaga/game/presentation/GameController.java @@ -1,24 +1,41 @@ package com.now.naaga.game.presentation; +import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; + import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.common.exception.CommonException; import com.now.naaga.game.application.GameService; import com.now.naaga.game.application.HintService; -import com.now.naaga.game.application.dto.*; +import com.now.naaga.game.application.dto.CreateGameCommand; +import com.now.naaga.game.application.dto.CreateHintCommand; +import com.now.naaga.game.application.dto.EndGameCommand; +import com.now.naaga.game.application.dto.FindAllGamesCommand; +import com.now.naaga.game.application.dto.FindGameByIdCommand; +import com.now.naaga.game.application.dto.FindGameByStatusCommand; +import com.now.naaga.game.application.dto.FindHintByIdCommand; import com.now.naaga.game.domain.Game; import com.now.naaga.game.domain.GameRecord; import com.now.naaga.game.domain.Hint; -import com.now.naaga.game.presentation.dto.*; +import com.now.naaga.game.presentation.dto.CoordinateRequest; +import com.now.naaga.game.presentation.dto.EndGameRequest; +import com.now.naaga.game.presentation.dto.GameResponse; +import com.now.naaga.game.presentation.dto.GameResultResponse; +import com.now.naaga.game.presentation.dto.GameStatusResponse; +import com.now.naaga.game.presentation.dto.HintResponse; import com.now.naaga.player.presentation.dto.PlayerRequest; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - import java.net.URI; import java.util.List; import java.util.stream.Collectors; - -import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; @RequestMapping("/games") @RestController diff --git a/backend/src/main/java/com/now/naaga/game/repository/GameRepository.java b/backend/src/main/java/com/now/naaga/game/repository/GameRepository.java index 15423a643..d7f9b1a93 100644 --- a/backend/src/main/java/com/now/naaga/game/repository/GameRepository.java +++ b/backend/src/main/java/com/now/naaga/game/repository/GameRepository.java @@ -6,9 +6,9 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface GameRepository extends JpaRepository { - + List findByPlayerIdAndGameStatus(final Long playerId, final GameStatus gameStatus); - + List findByPlayerId(Long playerId); } diff --git a/backend/src/main/java/com/now/naaga/game/repository/GameResultRepository.java b/backend/src/main/java/com/now/naaga/game/repository/GameResultRepository.java deleted file mode 100644 index 03dc25d44..000000000 --- a/backend/src/main/java/com/now/naaga/game/repository/GameResultRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.now.naaga.game.repository; - -import com.now.naaga.game.domain.GameResult; -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface GameResultRepository extends JpaRepository { - - List findByGameId(final Long gameId); -} diff --git a/backend/src/main/java/com/now/naaga/gameresult/application/GameResultService.java b/backend/src/main/java/com/now/naaga/gameresult/application/GameResultService.java index 010b295ad..25d739646 100644 --- a/backend/src/main/java/com/now/naaga/gameresult/application/GameResultService.java +++ b/backend/src/main/java/com/now/naaga/gameresult/application/GameResultService.java @@ -1,9 +1,9 @@ package com.now.naaga.gameresult.application; +import com.now.naaga.game.application.GameFinishService; import com.now.naaga.game.application.dto.CreateGameResultCommand; import com.now.naaga.game.domain.EndType; import com.now.naaga.game.domain.Game; -import com.now.naaga.game.application.GameFinishService; import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.gameresult.domain.ResultType; import com.now.naaga.gameresult.domain.gamescore.ResultScoreCalculator; diff --git a/backend/src/main/java/com/now/naaga/gameresult/domain/GameResult.java b/backend/src/main/java/com/now/naaga/gameresult/domain/GameResult.java index 12b8a9c01..ce6acc9e2 100644 --- a/backend/src/main/java/com/now/naaga/gameresult/domain/GameResult.java +++ b/backend/src/main/java/com/now/naaga/gameresult/domain/GameResult.java @@ -3,8 +3,16 @@ import com.now.naaga.common.domain.BaseEntity; import com.now.naaga.game.domain.Game; import com.now.naaga.score.domain.Score; -import jakarta.persistence.*; - +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; import java.util.Objects; @Entity diff --git a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/FailResultScorePolicy.java b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/FailResultScorePolicy.java index 90a0457ca..5368e1217 100644 --- a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/FailResultScorePolicy.java +++ b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/FailResultScorePolicy.java @@ -1,11 +1,11 @@ package com.now.naaga.gameresult.domain.gamescore; +import static com.now.naaga.gameresult.domain.ResultType.FAIL; + import com.now.naaga.game.domain.Game; import com.now.naaga.gameresult.domain.ResultType; import com.now.naaga.score.domain.Score; -import static com.now.naaga.gameresult.domain.ResultType.FAIL; - public class FailResultScorePolicy implements ResultScorePolicy { @Override diff --git a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/ResultScoreCalculator.java b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/ResultScoreCalculator.java index d1db814ef..c8536e948 100644 --- a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/ResultScoreCalculator.java +++ b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/ResultScoreCalculator.java @@ -1,14 +1,13 @@ package com.now.naaga.gameresult.domain.gamescore; +import static com.now.naaga.common.exception.InternalExceptionType.FAIL_ESTABLISH_GAME_SCORE_POLICY; + import com.now.naaga.common.exception.InternalException; import com.now.naaga.game.domain.Game; import com.now.naaga.gameresult.domain.ResultType; import com.now.naaga.score.domain.Score; - import java.util.List; -import static com.now.naaga.common.exception.InternalExceptionType.FAIL_ESTABLISH_GAME_SCORE_POLICY; - public class ResultScoreCalculator { private final List scorePolicies; diff --git a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/SuccessResultScorePolicy.java b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/SuccessResultScorePolicy.java index 3827c3d1e..7d5775083 100644 --- a/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/SuccessResultScorePolicy.java +++ b/backend/src/main/java/com/now/naaga/gameresult/domain/gamescore/SuccessResultScorePolicy.java @@ -1,16 +1,15 @@ package com.now.naaga.gameresult.domain.gamescore; +import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; +import static com.now.naaga.game.domain.Game.MAX_HINT_COUNT; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; + import com.now.naaga.game.domain.Game; import com.now.naaga.gameresult.domain.ResultType; import com.now.naaga.score.domain.Score; - import java.time.Duration; import java.time.LocalDateTime; -import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; -import static com.now.naaga.game.domain.Game.MAX_HINT_COUNT; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; - public class SuccessResultScorePolicy implements ResultScorePolicy { private static final Score BASE_SCORE = new Score(50); diff --git a/backend/src/main/java/com/now/naaga/letter/application/LetterFindService.java b/backend/src/main/java/com/now/naaga/letter/application/LetterFindService.java new file mode 100644 index 000000000..64e4e0b8b --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/LetterFindService.java @@ -0,0 +1,26 @@ +package com.now.naaga.letter.application; + +import com.now.naaga.letter.application.dto.FindLetterByIdCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.exception.LetterException; +import com.now.naaga.letter.exception.LetterExceptionType; +import com.now.naaga.letter.repository.LetterRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class LetterFindService { + + private final LetterRepository letterRepository; + + public LetterFindService(final LetterRepository letterRepository) { + this.letterRepository = letterRepository; + } + + public Letter findById(FindLetterByIdCommand findLetterByIdCommand) { + Long letterId = findLetterByIdCommand.letterId(); + return letterRepository.findById(letterId) + .orElseThrow(() -> new LetterException(LetterExceptionType.NO_EXIST)); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/LetterService.java b/backend/src/main/java/com/now/naaga/letter/application/LetterService.java new file mode 100644 index 000000000..2e930bbe9 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/LetterService.java @@ -0,0 +1,68 @@ +package com.now.naaga.letter.application; + +import static com.now.naaga.letter.exception.LetterExceptionType.NO_EXIST; + +import com.now.naaga.letter.application.dto.CreateLetterCommand; +import com.now.naaga.letter.application.letterlog.ReadLetterLogService; +import com.now.naaga.letter.application.letterlog.WriteLetterLogService; +import com.now.naaga.letter.application.letterlog.dto.LetterLogCreateCommand; +import com.now.naaga.letter.application.letterlog.dto.WriteLetterLogCreateCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.exception.LetterException; +import com.now.naaga.letter.presentation.dto.FindNearByLetterCommand; +import com.now.naaga.letter.presentation.dto.LetterReadCommand; +import com.now.naaga.letter.repository.LetterRepository; +import com.now.naaga.player.application.PlayerService; +import com.now.naaga.player.domain.Player; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class LetterService { + + private static final double LETTER_RADIUS = 0.1; + + private final LetterRepository letterRepository; + + private final ReadLetterLogService readLetterLogService; + + private final WriteLetterLogService writeLetterLogService; + + private final PlayerService playerService; + + public LetterService(final LetterRepository letterRepository, + final ReadLetterLogService readLetterLogService, + final WriteLetterLogService writeLetterLogService, + final PlayerService playerService) { + this.letterRepository = letterRepository; + this.readLetterLogService = readLetterLogService; + this.writeLetterLogService = writeLetterLogService; + this.playerService = playerService; + } + + public Letter findLetter(final LetterReadCommand letterReadCommand) { + final Player player = playerService.findPlayerById(letterReadCommand.playerId()); + final Letter foundLetter = letterRepository.findById(letterReadCommand.letterId()) + .orElseThrow(() -> new LetterException(NO_EXIST)); + + readLetterLogService.log(new LetterLogCreateCommand(player.getId(), foundLetter)); + return foundLetter; + } + + @Transactional(readOnly = true) + public List findNearByLetters(final FindNearByLetterCommand findNearByLetterCommand) { + return letterRepository.findLetterByPositionAndDistance(findNearByLetterCommand.position(), LETTER_RADIUS); + } + + public Letter writeLetter(final CreateLetterCommand createLetterCommand) { + final Player player = playerService.findPlayerById(createLetterCommand.playerId()); + final Letter letter = new Letter(player, createLetterCommand.position(), createLetterCommand.message()); + letterRepository.save(letter); + + final WriteLetterLogCreateCommand writeLetterLogCreateCommand = new WriteLetterLogCreateCommand(letter.getId()); + writeLetterLogService.log(writeLetterLogCreateCommand); + return letter; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/WriteLetterLogCreateService.java b/backend/src/main/java/com/now/naaga/letter/application/WriteLetterLogCreateService.java new file mode 100644 index 000000000..1d999e92a --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/WriteLetterLogCreateService.java @@ -0,0 +1,8 @@ +package com.now.naaga.letter.application; + +import com.now.naaga.letter.application.letterlog.dto.WriteLetterLogCreateCommand; + +public interface WriteLetterLogCreateService { + + void log(WriteLetterLogCreateCommand writeLetterLogCreateCommand); +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/dto/CreateLetterCommand.java b/backend/src/main/java/com/now/naaga/letter/application/dto/CreateLetterCommand.java new file mode 100644 index 000000000..f06815caf --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/dto/CreateLetterCommand.java @@ -0,0 +1,19 @@ +package com.now.naaga.letter.application.dto; + +import com.now.naaga.letter.presentation.dto.LetterRequest; +import com.now.naaga.place.domain.Position; +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record CreateLetterCommand(Long playerId, + String message, + Position position) { + + public static CreateLetterCommand of(final PlayerRequest playerRequest, + final LetterRequest letterRequest) { + final Long playerId = playerRequest.playerId(); + final Position position = Position.of(letterRequest.latitude(), letterRequest.longitude()); + return new CreateLetterCommand(playerId, + letterRequest.message(), + position); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/dto/FindLetterByIdCommand.java b/backend/src/main/java/com/now/naaga/letter/application/dto/FindLetterByIdCommand.java new file mode 100644 index 000000000..fa52b41a1 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/dto/FindLetterByIdCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.letter.application.dto; + +public record FindLetterByIdCommand(Long letterId) { +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/letterlog/ReadLetterLogService.java b/backend/src/main/java/com/now/naaga/letter/application/letterlog/ReadLetterLogService.java new file mode 100644 index 000000000..bd780b0d2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/letterlog/ReadLetterLogService.java @@ -0,0 +1,52 @@ +package com.now.naaga.letter.application.letterlog; + +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; + +import com.now.naaga.game.application.GameService; +import com.now.naaga.game.application.dto.FindGameByIdCommand; +import com.now.naaga.game.application.dto.FindGameByStatusCommand; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.exception.GameException; +import com.now.naaga.game.exception.GameExceptionType; +import com.now.naaga.letter.application.letterlog.dto.LetterByGameCommand; +import com.now.naaga.letter.application.letterlog.dto.LetterLogCreateCommand; +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import com.now.naaga.letter.repository.letterlog.ReadLetterLogRepository; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class ReadLetterLogService { + + private final ReadLetterLogRepository readLetterLogRepository; + + private final GameService gameService; + + public ReadLetterLogService(final ReadLetterLogRepository readLetterLogRepository, final GameService gameService) { + this.readLetterLogRepository = readLetterLogRepository; + this.gameService = gameService; + } + + public List findReadLettersByGameId(final LetterByGameCommand letterByGameCommand) { + final FindGameByIdCommand findGameByIdCommand = new FindGameByIdCommand(letterByGameCommand.gameId(), letterByGameCommand.playerId()); + final Game game = gameService.findGameById(findGameByIdCommand); + return readLetterLogRepository.findByGameId(game.getId()); + } + + public void log(final LetterLogCreateCommand letterLogCreateCommand) { + final Game gameInProgress = getGameInProgress(letterLogCreateCommand.playerId()); + final ReadLetterLog readLetterLog = new ReadLetterLog(gameInProgress, letterLogCreateCommand.letter()); + readLetterLogRepository.save(readLetterLog); + } + + private Game getGameInProgress(final Long playerId) { + final FindGameByStatusCommand findGameByStatusCommand = new FindGameByStatusCommand(playerId, IN_PROGRESS); + final List gamesInProgress = gameService.findGamesByStatus(findGameByStatusCommand); + if (gamesInProgress.isEmpty()) { + throw new GameException(GameExceptionType.NOT_EXIST_IN_PROGRESS); + } + return gamesInProgress.get(0); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/letterlog/WriteLetterLogService.java b/backend/src/main/java/com/now/naaga/letter/application/letterlog/WriteLetterLogService.java new file mode 100644 index 000000000..c63a69839 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/letterlog/WriteLetterLogService.java @@ -0,0 +1,54 @@ +package com.now.naaga.letter.application.letterlog; + +import com.now.naaga.game.application.GameService; +import com.now.naaga.game.application.dto.FindGameByIdCommand; +import com.now.naaga.game.application.dto.FindGameInProgressCommand; +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.application.LetterFindService; +import com.now.naaga.letter.application.dto.FindLetterByIdCommand; +import com.now.naaga.letter.application.letterlog.dto.LetterByGameCommand; +import com.now.naaga.letter.application.letterlog.dto.WriteLetterLogCreateCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; +import com.now.naaga.letter.repository.letterlog.WriteLetterLogRepository; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class WriteLetterLogService { + + private final WriteLetterLogRepository writeLetterLogRepository; + + private final LetterFindService letterFindService; + + private final GameService gameService; + + public WriteLetterLogService(final WriteLetterLogRepository writeLetterLogRepository, + final LetterFindService letterFindService, + final GameService gameService) { + this.writeLetterLogRepository = writeLetterLogRepository; + this.letterFindService = letterFindService; + this.gameService = gameService; + } + + public void log(final WriteLetterLogCreateCommand writeLetterLogCreateCommand) { + FindLetterByIdCommand findLetterByIdCommand = new FindLetterByIdCommand(writeLetterLogCreateCommand.letterId()); + Letter letter = letterFindService.findById(findLetterByIdCommand); + Game gameInProgress = getGameInProgress(letter.getRegisteredPlayer().getId()); + final WriteLetterLog writeLetterLog = new WriteLetterLog(gameInProgress, letter); + writeLetterLogRepository.save(writeLetterLog); + } + + private Game getGameInProgress(final Long playerId) { + final FindGameInProgressCommand findGameByStatusCommand = new FindGameInProgressCommand(playerId); + return gameService.findGameInProgress(findGameByStatusCommand); + } + + public List findWriteLetterByGameId(final LetterByGameCommand letterByGameCommand) { + final FindGameByIdCommand findGameByIdCommand = new FindGameByIdCommand(letterByGameCommand.gameId(), letterByGameCommand.playerId()); + final Game game = gameService.findGameById(findGameByIdCommand); + return writeLetterLogRepository.findByGameId(game.getId()); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterByGameCommand.java b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterByGameCommand.java new file mode 100644 index 000000000..0a2cf04fa --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterByGameCommand.java @@ -0,0 +1,13 @@ +package com.now.naaga.letter.application.letterlog.dto; + + +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record LetterByGameCommand(Long playerId, + Long gameId) { + + public static LetterByGameCommand of(final PlayerRequest playerRequest, + final Long gameId) { + return new LetterByGameCommand(playerRequest.playerId(), gameId); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterLogCreateCommand.java b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterLogCreateCommand.java new file mode 100644 index 000000000..e0b4f54d3 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/LetterLogCreateCommand.java @@ -0,0 +1,7 @@ +package com.now.naaga.letter.application.letterlog.dto; + +import com.now.naaga.letter.domain.Letter; + +public record LetterLogCreateCommand(Long playerId, + Letter letter) { +} diff --git a/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/WriteLetterLogCreateCommand.java b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/WriteLetterLogCreateCommand.java new file mode 100644 index 000000000..83071b91c --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/application/letterlog/dto/WriteLetterLogCreateCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.letter.application.letterlog.dto; + +public record WriteLetterLogCreateCommand(Long letterId) { +} diff --git a/backend/src/main/java/com/now/naaga/letter/domain/Letter.java b/backend/src/main/java/com/now/naaga/letter/domain/Letter.java new file mode 100644 index 000000000..2d644a5d8 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/domain/Letter.java @@ -0,0 +1,98 @@ +package com.now.naaga.letter.domain; + +import com.now.naaga.common.domain.BaseEntity; +import com.now.naaga.place.domain.Position; +import com.now.naaga.player.domain.Player; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; +import java.util.Objects; + +@Entity +public class Letter extends BaseEntity { + + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "player_id") + private Player registeredPlayer; + + @Embedded + private Position position; + + private String message; + + protected Letter() { + } + + public Letter(final Player registeredPlayer, + final Position position, + final String message) { + this(null, registeredPlayer, position, message); + } + + public Letter(final Long id, + final Player registeredPlayer, + final Position position, + final String message) { + this.id = id; + this.registeredPlayer = registeredPlayer; + this.position = position; + this.message = message; + } + + public Long getId() { + return id; + } + + public Player getRegisteredPlayer() { + return registeredPlayer; + } + + public Position getPosition() { + return position; + } + + public String getMessage() { + return message; + } + + public LocalDateTime getCreatedTime() { + return super.getCreatedAt(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Letter letter = (Letter) o; + return Objects.equals(id, letter.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "Letter{" + + "id=" + id + + ", playerId=" + registeredPlayer.getId() + + ", position=" + position + + ", message='" + message + + '}'; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/domain/letterlog/ReadLetterLog.java b/backend/src/main/java/com/now/naaga/letter/domain/letterlog/ReadLetterLog.java new file mode 100644 index 000000000..cc73198cc --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/domain/letterlog/ReadLetterLog.java @@ -0,0 +1,84 @@ +package com.now.naaga.letter.domain.letterlog; + +import com.now.naaga.common.domain.BaseEntity; +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.domain.Letter; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.util.Objects; + + +@Entity +public class ReadLetterLog extends BaseEntity { + + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "game_id") + private Game game; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "letter_id") + private Letter letter; + + protected ReadLetterLog() { + } + + public ReadLetterLog(final Game game, + final Letter letter) { + this(null, game, letter); + } + + public ReadLetterLog(final Long id, + final Game game, + final Letter letter) { + this.id = id; + this.game = game; + this.letter = letter; + } + + public Long getId() { + return id; + } + + public Game getGame() { + return game; + } + + public Letter getLetter() { + return letter; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ReadLetterLog letterLog = (ReadLetterLog) o; + return Objects.equals(id, letterLog.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "LetterLog{" + + "id=" + id + + ", gameId=" + game.getId() + + ", letterId=" + letter.getId() + + '}'; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/domain/letterlog/WriteLetterLog.java b/backend/src/main/java/com/now/naaga/letter/domain/letterlog/WriteLetterLog.java new file mode 100644 index 000000000..b95899dfb --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/domain/letterlog/WriteLetterLog.java @@ -0,0 +1,80 @@ +package com.now.naaga.letter.domain.letterlog; + +import com.now.naaga.common.domain.BaseEntity; +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.domain.Letter; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import java.util.Objects; + +@Entity +public class WriteLetterLog extends BaseEntity { + + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "game_id") + private Game game; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "letter_id") + private Letter letter; + + protected WriteLetterLog() { + } + + public WriteLetterLog(final Game game, + final Letter letter) { + this(null, game, letter); + } + + public WriteLetterLog(final Long id, + final Game game, + final Letter letter) { + this.id = id; + this.game = game; + this.letter = letter; + } + + public Long getId() { + return id; + } + + public Game getGame() { + return game; + } + + public Letter getLetter() { + return letter; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WriteLetterLog that = (WriteLetterLog) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "WriteLetterLog{" + + "id=" + id + + ", gameId=" + game.getId() + + ", letterId=" + letter.getId() + + '}'; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/exception/LetterException.java b/backend/src/main/java/com/now/naaga/letter/exception/LetterException.java new file mode 100644 index 000000000..9ee166500 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/exception/LetterException.java @@ -0,0 +1,18 @@ +package com.now.naaga.letter.exception; + +import com.now.naaga.common.exception.BaseException; +import com.now.naaga.common.exception.BaseExceptionType; + +public class LetterException extends BaseException { + + private final LetterExceptionType letterExceptionType; + + public LetterException(final LetterExceptionType letterExceptionType) { + this.letterExceptionType = letterExceptionType; + } + + @Override + public BaseExceptionType exceptionType() { + return letterExceptionType; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/exception/LetterExceptionType.java b/backend/src/main/java/com/now/naaga/letter/exception/LetterExceptionType.java new file mode 100644 index 000000000..ce1039f04 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/exception/LetterExceptionType.java @@ -0,0 +1,41 @@ +package com.now.naaga.letter.exception; + +import com.now.naaga.common.exception.BaseExceptionType; +import org.springframework.http.HttpStatus; + +public enum LetterExceptionType implements BaseExceptionType { + + NO_EXIST( + 604, + HttpStatus.NOT_FOUND, + "ํ•ด๋‹น ์ชฝ์ง€๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค." + ), + ; + + private final int errorCode; + private final HttpStatus httpStatus; + private final String errorMessage; + + LetterExceptionType(final int errorCode, + final HttpStatus httpStatus, + final String errorMessage) { + this.errorCode = errorCode; + this.httpStatus = httpStatus; + this.errorMessage = errorMessage; + } + + @Override + public int errorCode() { + return errorCode; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String errorMessage() { + return errorMessage; + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/LetterController.java b/backend/src/main/java/com/now/naaga/letter/presentation/LetterController.java new file mode 100644 index 000000000..af9b288f5 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/LetterController.java @@ -0,0 +1,68 @@ +package com.now.naaga.letter.presentation; + +import com.now.naaga.auth.presentation.annotation.Auth; +import com.now.naaga.letter.application.LetterService; +import com.now.naaga.letter.application.dto.CreateLetterCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.presentation.dto.FindNearByLetterCommand; +import com.now.naaga.letter.presentation.dto.LetterReadCommand; +import com.now.naaga.letter.presentation.dto.LetterRequest; +import com.now.naaga.letter.presentation.dto.LetterResponse; +import com.now.naaga.letter.presentation.dto.NearByLetterResponse; +import com.now.naaga.player.presentation.dto.PlayerRequest; +import java.net.URI; +import java.util.List; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/letters") +@RestController +public class LetterController { + + private final LetterService letterService; + + public LetterController(final LetterService letterService) { + this.letterService = letterService; + } + + @PostMapping + public ResponseEntity createLetter(@Auth final PlayerRequest playerRequest, + @RequestBody final LetterRequest letterRequest) { + final CreateLetterCommand createLetterCommand = CreateLetterCommand.of(playerRequest, letterRequest); + final Letter letter = letterService.writeLetter(createLetterCommand); + return ResponseEntity + .status(HttpStatus.CREATED) + .location(URI.create("/letters/" + letter.getId())) + .body(LetterResponse.from(letter)); + } + + @GetMapping("/{letterId}") + public ResponseEntity findLetterById(@Auth final PlayerRequest playerRequest, + @PathVariable final Long letterId) { + final LetterReadCommand letterReadCommand = LetterReadCommand.of(playerRequest, letterId); + final Letter letter = letterService.findLetter(letterReadCommand); + return ResponseEntity + .status(HttpStatus.OK) + .body(LetterResponse.from(letter)); + } + + @GetMapping("/nearby") + public ResponseEntity> findLetterNearBy(@Auth final PlayerRequest playerRequest, + @RequestParam final Double latitude, + @RequestParam final Double longitude) { + final FindNearByLetterCommand findNearByLetterCommand = FindNearByLetterCommand.from(latitude, longitude); + + final List letters = letterService.findNearByLetters(findNearByLetterCommand); + final List nearByLetterResponses = NearByLetterResponse.convertToLetterResponses(letters); + return ResponseEntity + .status(HttpStatus.OK) + .body(nearByLetterResponses); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/LetterLogController.java b/backend/src/main/java/com/now/naaga/letter/presentation/LetterLogController.java new file mode 100644 index 000000000..dd07115b2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/LetterLogController.java @@ -0,0 +1,72 @@ +package com.now.naaga.letter.presentation; + +import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; +import static com.now.naaga.letter.presentation.LogType.READ; +import static com.now.naaga.letter.presentation.LogType.WRITE; + +import com.now.naaga.auth.presentation.annotation.Auth; +import com.now.naaga.common.exception.CommonException; +import com.now.naaga.letter.application.letterlog.ReadLetterLogService; +import com.now.naaga.letter.application.letterlog.WriteLetterLogService; +import com.now.naaga.letter.application.letterlog.dto.LetterByGameCommand; +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; +import com.now.naaga.letter.presentation.dto.LetterResponse; +import com.now.naaga.player.presentation.dto.PlayerRequest; +import java.util.List; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/letterlogs") +@RestController +public class LetterLogController { + + private final ReadLetterLogService readLetterLogService; + + private final WriteLetterLogService writeLetterLogService; + + public LetterLogController(final ReadLetterLogService readLetterLogService, + final WriteLetterLogService writeLetterLogService) { + this.readLetterLogService = readLetterLogService; + this.writeLetterLogService = writeLetterLogService; + } + + @GetMapping + public ResponseEntity> findLetterInGame(@Auth final PlayerRequest playerRequest, + @RequestParam final Long gameId, + @RequestParam final String logType) { + if (!(READ.name().equalsIgnoreCase(logType) || WRITE.name().equalsIgnoreCase(logType))) { + throw new CommonException(INVALID_REQUEST_PARAMETERS); + } + final LetterByGameCommand letterByGameCommand = LetterByGameCommand.of(playerRequest, gameId); + + if (READ.name().equalsIgnoreCase(logType)) { + return findReadLetterByGameId(letterByGameCommand); + } + return findWriteLetterByGameId(letterByGameCommand); + } + + private ResponseEntity> findReadLetterByGameId(final LetterByGameCommand letterByGameCommand) { + final List readLetterLogs = readLetterLogService.findReadLettersByGameId(letterByGameCommand); + final List readLetterResponses = readLetterLogs.stream() + .map(readLetterLog -> LetterResponse.from(readLetterLog.getLetter())) + .toList(); + return ResponseEntity + .status(HttpStatus.OK) + .body(readLetterResponses); + } + + private ResponseEntity> findWriteLetterByGameId(final LetterByGameCommand letterByGameCommand) { + final List writeLetterLogs = writeLetterLogService.findWriteLetterByGameId(letterByGameCommand); + final List writeLetterResponses = writeLetterLogs.stream() + .map(readLetterLog -> LetterResponse.from(readLetterLog.getLetter())) + .toList(); + return ResponseEntity + .status(HttpStatus.OK) + .body(writeLetterResponses); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/LogType.java b/backend/src/main/java/com/now/naaga/letter/presentation/LogType.java new file mode 100644 index 000000000..d584575ea --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/LogType.java @@ -0,0 +1,8 @@ +package com.now.naaga.letter.presentation; + +public enum LogType { + + READ, + WRITE, + ; +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/dto/FindNearByLetterCommand.java b/backend/src/main/java/com/now/naaga/letter/presentation/dto/FindNearByLetterCommand.java new file mode 100644 index 000000000..32d527413 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/dto/FindNearByLetterCommand.java @@ -0,0 +1,12 @@ +package com.now.naaga.letter.presentation.dto; + +import com.now.naaga.place.domain.Position; +import java.math.BigDecimal; + +public record FindNearByLetterCommand(Position position) { + + public static FindNearByLetterCommand from(final Double latitude, + final Double longitude) { + return new FindNearByLetterCommand(new Position(BigDecimal.valueOf(latitude), BigDecimal.valueOf(longitude))); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterReadCommand.java b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterReadCommand.java new file mode 100644 index 000000000..4e0616eb0 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterReadCommand.java @@ -0,0 +1,13 @@ +package com.now.naaga.letter.presentation.dto; + +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record LetterReadCommand(Long playerId, + Long letterId) { + + public static LetterReadCommand of(final PlayerRequest playerRequest, + final Long letterId) { + final Long playerId = playerRequest.playerId(); + return new LetterReadCommand(playerId, letterId); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterRequest.java b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterRequest.java new file mode 100644 index 000000000..03af3dc15 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterRequest.java @@ -0,0 +1,6 @@ +package com.now.naaga.letter.presentation.dto; + +public record LetterRequest(String message, + Double latitude, + Double longitude) { +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterResponse.java b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterResponse.java new file mode 100644 index 000000000..9c6a771d2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/dto/LetterResponse.java @@ -0,0 +1,20 @@ +package com.now.naaga.letter.presentation.dto; + +import com.now.naaga.game.presentation.dto.CoordinateResponse; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.player.presentation.dto.PlayerResponse; + +public record LetterResponse(Long id, + PlayerResponse player, + CoordinateResponse coordinate, + String message, + String registerDate) { + + public static LetterResponse from(final Letter letter) { + final Long id = letter.getId(); + final PlayerResponse playerResponse = PlayerResponse.from(letter.getRegisteredPlayer()); + final CoordinateResponse coordinateResponse = CoordinateResponse.of(letter.getPosition()); + final String registerDate = letter.getCreatedTime().toString(); + return new LetterResponse(id, playerResponse, coordinateResponse, letter.getMessage(), registerDate); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/presentation/dto/NearByLetterResponse.java b/backend/src/main/java/com/now/naaga/letter/presentation/dto/NearByLetterResponse.java new file mode 100644 index 000000000..fa0f1ce2c --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/presentation/dto/NearByLetterResponse.java @@ -0,0 +1,19 @@ +package com.now.naaga.letter.presentation.dto; + +import com.now.naaga.game.presentation.dto.CoordinateResponse; +import com.now.naaga.letter.domain.Letter; +import java.util.List; + +public record NearByLetterResponse(Long id, + CoordinateResponse coordinate) { + + public static NearByLetterResponse from(final Letter letter) { + return new NearByLetterResponse(letter.getId(), CoordinateResponse.of(letter.getPosition())); + } + + public static List convertToLetterResponses(final List letters) { + return letters.stream() + .map(NearByLetterResponse::from) + .toList(); + } +} diff --git a/backend/src/main/java/com/now/naaga/letter/repository/LetterRepository.java b/backend/src/main/java/com/now/naaga/letter/repository/LetterRepository.java new file mode 100644 index 000000000..24ab1c07d --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/repository/LetterRepository.java @@ -0,0 +1,19 @@ +package com.now.naaga.letter.repository; + +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.place.domain.Position; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface LetterRepository extends JpaRepository { + + @Query(value = "SELECT letter FROM Letter letter " + + "WHERE ACOS(" + + "SIN(RADIANS(:#{#user_position.latitude})) * SIN(RADIANS(letter.position.latitude)) " + + "+ (COS(RADIANS(:#{#user_position.latitude})) * COS(RADIANS(letter.position.latitude)) * COS(RADIANS(:#{#user_position.longitude} - letter.position.longitude)))" + + ") * 6371.0 <= :distance") + List findLetterByPositionAndDistance(@Param(value = "user_position") final Position position, + @Param(value = "distance") final double distance); +} diff --git a/backend/src/main/java/com/now/naaga/letter/repository/letterlog/ReadLetterLogRepository.java b/backend/src/main/java/com/now/naaga/letter/repository/letterlog/ReadLetterLogRepository.java new file mode 100644 index 000000000..7a2d47c79 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/repository/letterlog/ReadLetterLogRepository.java @@ -0,0 +1,10 @@ +package com.now.naaga.letter.repository.letterlog; + +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReadLetterLogRepository extends JpaRepository { + + List findByGameId(Long gamedId); +} diff --git a/backend/src/main/java/com/now/naaga/letter/repository/letterlog/WriteLetterLogRepository.java b/backend/src/main/java/com/now/naaga/letter/repository/letterlog/WriteLetterLogRepository.java new file mode 100644 index 000000000..1af8058e3 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/letter/repository/letterlog/WriteLetterLogRepository.java @@ -0,0 +1,10 @@ +package com.now.naaga.letter.repository.letterlog; + +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface WriteLetterLogRepository extends JpaRepository { + + List findByGameId(Long gamedId); +} diff --git a/backend/src/main/java/com/now/naaga/like/application/PlaceLikeService.java b/backend/src/main/java/com/now/naaga/like/application/PlaceLikeService.java new file mode 100644 index 000000000..1ab9531af --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/application/PlaceLikeService.java @@ -0,0 +1,128 @@ +package com.now.naaga.like.application; + +import com.now.naaga.like.application.dto.ApplyLikeCommand; +import com.now.naaga.like.application.dto.CancelLikeCommand; +import com.now.naaga.like.application.dto.CheckMyPlaceLikeCommand; +import com.now.naaga.like.application.dto.CountPlaceLikeCommand; +import com.now.naaga.like.domain.MyPlaceLikeType; +import com.now.naaga.like.domain.PlaceLike; +import com.now.naaga.like.domain.PlaceLikeType; +import com.now.naaga.like.exception.PlaceLikeException; +import com.now.naaga.like.exception.PlaceLikeExceptionType; +import com.now.naaga.like.repository.PlaceLikeRepository; +import com.now.naaga.place.application.PlaceService; +import com.now.naaga.place.application.PlaceStatisticsService; +import com.now.naaga.place.application.dto.FindPlaceByIdCommand; +import com.now.naaga.place.application.dto.FindPlaceStatisticsByPlaceIdCommand; +import com.now.naaga.place.application.dto.PlusLikeCommand; +import com.now.naaga.place.application.dto.SubtractLikeCommand; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.player.application.PlayerService; +import com.now.naaga.player.domain.Player; +import java.util.Optional; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class PlaceLikeService { + + private final PlaceLikeRepository placeLikeRepository; + + private final PlaceStatisticsService placeStatisticsService; + + private final PlayerService playerService; + + private final PlaceService placeService; + + public PlaceLikeService(final PlaceLikeRepository placeLikeRepository, + final PlaceStatisticsService placeStatisticsService, + final PlayerService playerService, + final PlaceService placeService) { + this.placeLikeRepository = placeLikeRepository; + this.placeStatisticsService = placeStatisticsService; + this.playerService = playerService; + this.placeService = placeService; + } + + public PlaceLike applyLike(final ApplyLikeCommand applyLikeCommand) { + final Long playerId = applyLikeCommand.playerId(); + final Long placeId = applyLikeCommand.placeId(); + final PlaceLikeType placeLikeType = applyLikeCommand.placeLikeType(); + + if (placeLikeType == PlaceLikeType.LIKE) { + placeStatisticsService.plusLike(new PlusLikeCommand(placeId)); + } + + final Optional maybePlaceLike = placeLikeRepository.findByPlaceIdAndPlayerId(placeId, playerId); + + if (maybePlaceLike.isPresent()) { + final PlaceLike exsistingPlaceLike = maybePlaceLike.get(); + return updatePlaceLike(exsistingPlaceLike, placeLikeType); + } + + return createPlaceLike(playerId, placeId, placeLikeType); + } + + private PlaceLike updatePlaceLike(final PlaceLike target, + final PlaceLikeType toBeChanged) { + if (target.getType() == toBeChanged) { + throw new PlaceLikeException(PlaceLikeExceptionType.ALREADY_APPLIED_TYPE); + } + if (toBeChanged == PlaceLikeType.DISLIKE) { + placeStatisticsService.subtractLike(new SubtractLikeCommand(target.getPlace().getId())); + } + target.switchType(); + return target; + } + + private PlaceLike createPlaceLike(final Long playerId, + final Long placeId, + final PlaceLikeType placeLikeType) { + final Player player = playerService.findPlayerById(playerId); + final Place place = placeService.findPlaceById(new FindPlaceByIdCommand(placeId)); + return placeLikeRepository.save(new PlaceLike(place, player, placeLikeType)); + } + + public void cancelLike(final CancelLikeCommand cancelLikeCommand) { + final Long playerId = cancelLikeCommand.playerId(); + final Long placeId = cancelLikeCommand.placeId(); + + final Optional maybePlaceLike = placeLikeRepository.findByPlaceIdAndPlayerId(placeId, playerId); + if (maybePlaceLike.isEmpty()) { + return; + } + + final PlaceLike placeLike = maybePlaceLike.get(); + placeLikeRepository.delete(placeLike); + + subtractPlaceLikeCount(placeId, placeLike); + } + + private void subtractPlaceLikeCount(final Long placeId, final PlaceLike placeLike) { + if (placeLike.getType() == PlaceLikeType.LIKE) { + final SubtractLikeCommand subtractLikeCommand = new SubtractLikeCommand(placeId); + placeStatisticsService.subtractLike(subtractLikeCommand); + } + } + + @Transactional(readOnly = true) + public MyPlaceLikeType checkMyLike(final CheckMyPlaceLikeCommand checkMyPlaceLikeCommand) { + final Long playerId = checkMyPlaceLikeCommand.playerId(); + final Long placeId = checkMyPlaceLikeCommand.placeId(); + + return placeLikeRepository.findByPlaceIdAndPlayerId(placeId, playerId) + .map(PlaceLike::getType) + .map(MyPlaceLikeType::from) + .orElse(MyPlaceLikeType.NONE); + } + + @Transactional(readOnly = true) + public Long countPlaceLike(final CountPlaceLikeCommand countPlaceLikeCommand) { + final FindPlaceStatisticsByPlaceIdCommand findPlaceStatisticsByPlaceIdCommand = new FindPlaceStatisticsByPlaceIdCommand(countPlaceLikeCommand.placeId()); + final PlaceStatistics placeStatistics = placeStatisticsService.findPlaceStatisticsByPlaceId(findPlaceStatisticsByPlaceIdCommand); + + return placeStatistics.getLikeCount(); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/application/dto/ApplyLikeCommand.java b/backend/src/main/java/com/now/naaga/like/application/dto/ApplyLikeCommand.java new file mode 100644 index 000000000..812181ea0 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/application/dto/ApplyLikeCommand.java @@ -0,0 +1,16 @@ +package com.now.naaga.like.application.dto; + +import com.now.naaga.like.domain.PlaceLikeType; +import com.now.naaga.like.presentation.dto.ApplyPlaceLikeRequest; +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record ApplyLikeCommand(Long playerId, + Long placeId, + PlaceLikeType placeLikeType) { + + public static ApplyLikeCommand of(final PlayerRequest playerRequest, + final Long placeId, + final ApplyPlaceLikeRequest applyPlaceLikeRequest) { + return new ApplyLikeCommand(playerRequest.playerId(), placeId, applyPlaceLikeRequest.type()); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/application/dto/CancelLikeCommand.java b/backend/src/main/java/com/now/naaga/like/application/dto/CancelLikeCommand.java new file mode 100644 index 000000000..df25ec1c0 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/application/dto/CancelLikeCommand.java @@ -0,0 +1,11 @@ +package com.now.naaga.like.application.dto; + +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record CancelLikeCommand(Long playerId, + Long placeId) { + + public static CancelLikeCommand of(final PlayerRequest playerRequest, final Long placeId) { + return new CancelLikeCommand(playerRequest.playerId(), placeId); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/application/dto/CheckMyPlaceLikeCommand.java b/backend/src/main/java/com/now/naaga/like/application/dto/CheckMyPlaceLikeCommand.java new file mode 100644 index 000000000..75d79c847 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/application/dto/CheckMyPlaceLikeCommand.java @@ -0,0 +1,13 @@ +package com.now.naaga.like.application.dto; + +import com.now.naaga.player.presentation.dto.PlayerRequest; + +public record CheckMyPlaceLikeCommand(Long playerId, + Long placeId) { + + public static CheckMyPlaceLikeCommand of(final PlayerRequest playerRequest, + final Long placeId) { + return new CheckMyPlaceLikeCommand(playerRequest.playerId(), + placeId); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/application/dto/CountPlaceLikeCommand.java b/backend/src/main/java/com/now/naaga/like/application/dto/CountPlaceLikeCommand.java new file mode 100644 index 000000000..932a0ead2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/application/dto/CountPlaceLikeCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.like.application.dto; + +public record CountPlaceLikeCommand(Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/like/domain/MyPlaceLikeType.java b/backend/src/main/java/com/now/naaga/like/domain/MyPlaceLikeType.java new file mode 100644 index 000000000..4afdaf4ac --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/domain/MyPlaceLikeType.java @@ -0,0 +1,13 @@ +package com.now.naaga.like.domain; + +public enum MyPlaceLikeType { + + LIKE, + DISLIKE, + NONE, + ; + + public static MyPlaceLikeType from(final PlaceLikeType placeLikeType) { + return MyPlaceLikeType.valueOf(placeLikeType.name()); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/domain/PlaceLike.java b/backend/src/main/java/com/now/naaga/like/domain/PlaceLike.java index 744ed121b..73bf1ac91 100644 --- a/backend/src/main/java/com/now/naaga/like/domain/PlaceLike.java +++ b/backend/src/main/java/com/now/naaga/like/domain/PlaceLike.java @@ -1,11 +1,14 @@ package com.now.naaga.like.domain; import com.now.naaga.common.domain.BaseEntity; +import com.now.naaga.like.exception.PlaceLikeException; +import com.now.naaga.like.exception.PlaceLikeExceptionType; import com.now.naaga.place.domain.Place; import com.now.naaga.player.domain.Player; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -13,7 +16,6 @@ import jakarta.persistence.ManyToOne; import java.util.Objects; -// ์•„์ง ๋ฏธ๊ตฌ์—ญ ์˜์—ญ์ž…๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๋ฐฑ์—”๋“œ ๋””๋ ‰ํ† ๋ฆฌ ๋ณ€๊ฒฝ์„ ์œ„ํ•œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. @Entity public class PlaceLike extends BaseEntity { @@ -21,11 +23,11 @@ public class PlaceLike extends BaseEntity { @Id private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "place_id") private Place place; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "player_id") private Player player; @@ -51,6 +53,16 @@ public PlaceLike(final Long id, this.placeLikeType = placeLikeType; } + public void validateOwner(final Player player) { + if (!this.player.equals(player)) { + throw new PlaceLikeException(PlaceLikeExceptionType.INACCESSIBLE_AUTHENTICATION); + } + } + + public void switchType() { + this.placeLikeType = this.placeLikeType.switchType(); + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/now/naaga/like/domain/PlaceLikeType.java b/backend/src/main/java/com/now/naaga/like/domain/PlaceLikeType.java index e18438bbc..77940326a 100644 --- a/backend/src/main/java/com/now/naaga/like/domain/PlaceLikeType.java +++ b/backend/src/main/java/com/now/naaga/like/domain/PlaceLikeType.java @@ -5,4 +5,11 @@ public enum PlaceLikeType { LIKE, DISLIKE, ; + + public PlaceLikeType switchType() { + if (this == LIKE) { + return DISLIKE; + } + return LIKE; + } } diff --git a/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeException.java b/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeException.java new file mode 100644 index 000000000..c2ad8bc82 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeException.java @@ -0,0 +1,18 @@ +package com.now.naaga.like.exception; + +import com.now.naaga.common.exception.BaseException; +import com.now.naaga.common.exception.BaseExceptionType; + +public class PlaceLikeException extends BaseException { + + private final PlaceLikeExceptionType placeLikeExceptionType; + + public PlaceLikeException(final PlaceLikeExceptionType placeLikeExceptionType) { + this.placeLikeExceptionType = placeLikeExceptionType; + } + + @Override + public BaseExceptionType exceptionType() { + return placeLikeExceptionType; + } +} diff --git a/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeExceptionType.java b/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeExceptionType.java new file mode 100644 index 000000000..88cf5b5b2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/exception/PlaceLikeExceptionType.java @@ -0,0 +1,53 @@ +package com.now.naaga.like.exception; + +import com.now.naaga.common.exception.BaseExceptionType; +import org.springframework.http.HttpStatus; + +public enum PlaceLikeExceptionType implements BaseExceptionType { + + INACCESSIBLE_AUTHENTICATION( + 703, + HttpStatus.FORBIDDEN, + "์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†๋Š” ์ข‹์•„์š”/์‹ซ์–ด์š”์ž…๋‹ˆ๋‹ค." + ), + + NOT_EXIST( + 704, + HttpStatus.NOT_FOUND, + "์ข‹์•„์š”/์‹ซ์–ด์š”๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค." + ), + + ALREADY_APPLIED_TYPE( + 705, + HttpStatus.BAD_REQUEST, + "์ด๋ฏธ ๋“ฑ๋ก๋œ ์ข‹์•„์š”/์‹ซ์–ด์š” ์ž…๋‹ˆ๋‹ค." + ), + ; + + private final int errorCode; + private final HttpStatus httpStatus; + private final String errorMessage; + + PlaceLikeExceptionType(final int errorCode, + final HttpStatus httpStatus, + final String errorMessage) { + this.errorCode = errorCode; + this.httpStatus = httpStatus; + this.errorMessage = errorMessage; + } + + @Override + public int errorCode() { + return errorCode; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String errorMessage() { + return errorMessage; + } +} diff --git a/backend/src/main/java/com/now/naaga/like/presentation/PlaceLikeController.java b/backend/src/main/java/com/now/naaga/like/presentation/PlaceLikeController.java new file mode 100644 index 000000000..d7481783f --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/presentation/PlaceLikeController.java @@ -0,0 +1,83 @@ +package com.now.naaga.like.presentation; + +import com.now.naaga.auth.presentation.annotation.Auth; +import com.now.naaga.like.application.PlaceLikeService; +import com.now.naaga.like.application.dto.ApplyLikeCommand; +import com.now.naaga.like.application.dto.CancelLikeCommand; +import com.now.naaga.like.application.dto.CheckMyPlaceLikeCommand; +import com.now.naaga.like.application.dto.CountPlaceLikeCommand; +import com.now.naaga.like.domain.MyPlaceLikeType; +import com.now.naaga.like.domain.PlaceLike; +import com.now.naaga.like.presentation.dto.ApplyPlaceLikeRequest; +import com.now.naaga.like.presentation.dto.CheckMyPlaceLikeResponse; +import com.now.naaga.like.presentation.dto.PlaceLikeCountResponse; +import com.now.naaga.like.presentation.dto.PlaceLikeResponse; +import com.now.naaga.player.presentation.dto.PlayerRequest; +import java.net.URI; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/places/{placeId}/likes") +@RestController +public class PlaceLikeController { + + private final PlaceLikeService placeLikeService; + + public PlaceLikeController(final PlaceLikeService placeLikeService) { + this.placeLikeService = placeLikeService; + } + + @PostMapping + public ResponseEntity applyPlaceLike(@Auth PlayerRequest playerRequest, + @PathVariable Long placeId, + @RequestBody ApplyPlaceLikeRequest applyPlaceLikeRequest) { + final ApplyLikeCommand command = ApplyLikeCommand.of(playerRequest, + placeId, + applyPlaceLikeRequest); + final PlaceLike placeLike = placeLikeService.applyLike(command); + final PlaceLikeResponse response = PlaceLikeResponse.from(placeLike); + return ResponseEntity + .status(HttpStatus.CREATED) + .location(URI.create("/places/" + placeId + "/likes/my")) + .body(response); + } + + @DeleteMapping("/my") + public ResponseEntity cancelPlaceLike(@Auth PlayerRequest playerRequest, + @PathVariable Long placeId) { + final CancelLikeCommand cancelLikeCommand = CancelLikeCommand.of(playerRequest, placeId); + placeLikeService.cancelLike(cancelLikeCommand); + return ResponseEntity + .status(HttpStatus.NO_CONTENT) + .build(); + } + + @GetMapping("/my") + public ResponseEntity checkMyPlaceLike(@Auth final PlayerRequest playerRequest, + @PathVariable final Long placeId) { + final CheckMyPlaceLikeCommand command = CheckMyPlaceLikeCommand.of(playerRequest, placeId); + final MyPlaceLikeType myPlaceLikeType = placeLikeService.checkMyLike(command); + final CheckMyPlaceLikeResponse response = CheckMyPlaceLikeResponse.from(myPlaceLikeType); + return ResponseEntity + .status(HttpStatus.OK) + .body(response); + } + + @GetMapping("/count") + public ResponseEntity countPlaceLike(@PathVariable Long placeId) { + final CountPlaceLikeCommand countPlaceLikeCommand = new CountPlaceLikeCommand(placeId); + final Long placeLikeCount = placeLikeService.countPlaceLike(countPlaceLikeCommand); + + final PlaceLikeCountResponse placeLikeCountResponse = new PlaceLikeCountResponse(placeLikeCount); + return ResponseEntity + .status(HttpStatus.OK) + .body(placeLikeCountResponse); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/presentation/dto/ApplyPlaceLikeRequest.java b/backend/src/main/java/com/now/naaga/like/presentation/dto/ApplyPlaceLikeRequest.java new file mode 100644 index 000000000..c4270dfed --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/presentation/dto/ApplyPlaceLikeRequest.java @@ -0,0 +1,6 @@ +package com.now.naaga.like.presentation.dto; + +import com.now.naaga.like.domain.PlaceLikeType; + +public record ApplyPlaceLikeRequest(PlaceLikeType type) { +} diff --git a/backend/src/main/java/com/now/naaga/like/presentation/dto/CheckMyPlaceLikeResponse.java b/backend/src/main/java/com/now/naaga/like/presentation/dto/CheckMyPlaceLikeResponse.java new file mode 100644 index 000000000..d71a8b39d --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/presentation/dto/CheckMyPlaceLikeResponse.java @@ -0,0 +1,10 @@ +package com.now.naaga.like.presentation.dto; + +import com.now.naaga.like.domain.MyPlaceLikeType; + +public record CheckMyPlaceLikeResponse(MyPlaceLikeType type) { + + public static CheckMyPlaceLikeResponse from(final MyPlaceLikeType myPlaceLikeType) { + return new CheckMyPlaceLikeResponse(myPlaceLikeType); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeCountResponse.java b/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeCountResponse.java new file mode 100644 index 000000000..2487f2261 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeCountResponse.java @@ -0,0 +1,4 @@ +package com.now.naaga.like.presentation.dto; + +public record PlaceLikeCountResponse(Long placeLikeCount) { +} diff --git a/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeResponse.java b/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeResponse.java new file mode 100644 index 000000000..bb918402a --- /dev/null +++ b/backend/src/main/java/com/now/naaga/like/presentation/dto/PlaceLikeResponse.java @@ -0,0 +1,17 @@ +package com.now.naaga.like.presentation.dto; + +import com.now.naaga.like.domain.PlaceLike; +import com.now.naaga.like.domain.PlaceLikeType; + +public record PlaceLikeResponse(Long id, + Long playerId, + Long placeId, + PlaceLikeType type) { + + public static PlaceLikeResponse from(final PlaceLike placeLike) { + return new PlaceLikeResponse(placeLike.getId(), + placeLike.getPlayer().getId(), + placeLike.getPlace().getId(), + placeLike.getType()); + } +} diff --git a/backend/src/main/java/com/now/naaga/like/repository/PlaceLikeRepository.java b/backend/src/main/java/com/now/naaga/like/repository/PlaceLikeRepository.java index 920c754dd..b1376f9d7 100644 --- a/backend/src/main/java/com/now/naaga/like/repository/PlaceLikeRepository.java +++ b/backend/src/main/java/com/now/naaga/like/repository/PlaceLikeRepository.java @@ -1,8 +1,11 @@ package com.now.naaga.like.repository; import com.now.naaga.like.domain.PlaceLike; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface PlaceLikeRepository extends JpaRepository { + Optional findByPlaceIdAndPlayerId(final Long placeId, + final Long playerId); } diff --git a/backend/src/main/java/com/now/naaga/member/application/CreateMemberCommand.java b/backend/src/main/java/com/now/naaga/member/application/CreateMemberCommand.java deleted file mode 100644 index a9b305085..000000000 --- a/backend/src/main/java/com/now/naaga/member/application/CreateMemberCommand.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.now.naaga.member.application; - -public record CreateMemberCommand(String email) { -} diff --git a/backend/src/main/java/com/now/naaga/member/application/DeleteMemberCommand.java b/backend/src/main/java/com/now/naaga/member/application/DeleteMemberCommand.java deleted file mode 100644 index 347ede966..000000000 --- a/backend/src/main/java/com/now/naaga/member/application/DeleteMemberCommand.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.now.naaga.member.application; - -public record DeleteMemberCommand(Long memberId) { -} diff --git a/backend/src/main/java/com/now/naaga/member/domain/Member.java b/backend/src/main/java/com/now/naaga/member/domain/Member.java index f57ffe742..a8327b525 100644 --- a/backend/src/main/java/com/now/naaga/member/domain/Member.java +++ b/backend/src/main/java/com/now/naaga/member/domain/Member.java @@ -1,12 +1,14 @@ package com.now.naaga.member.domain; import com.now.naaga.common.domain.BaseEntity; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import java.util.Objects; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; -import java.util.Objects; - @SQLDelete(sql = "UPDATE member SET deleted = true WHERE id = ?") @Where(clause = "deleted = false") @Entity diff --git a/backend/src/main/java/com/now/naaga/place/application/CreatePlaceStatisticsWithPlaceCreateEventHandler.java b/backend/src/main/java/com/now/naaga/place/application/CreatePlaceStatisticsWithPlaceCreateEventHandler.java new file mode 100644 index 000000000..6726da7b9 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/CreatePlaceStatisticsWithPlaceCreateEventHandler.java @@ -0,0 +1,25 @@ +package com.now.naaga.place.application; + +import com.now.naaga.place.application.dto.CreatePlaceStatisticsCommand; +import com.now.naaga.place.domain.PlaceCreateEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class CreatePlaceStatisticsWithPlaceCreateEventHandler { + + private final PlaceStatisticsService placeStatisticsService; + + public CreatePlaceStatisticsWithPlaceCreateEventHandler(final PlaceStatisticsService placeStatisticsService) { + this.placeStatisticsService = placeStatisticsService; + } + + @Transactional + @EventListener + public void handle(final PlaceCreateEvent placeCreateEvent) { + final Long placeId = placeCreateEvent.placeId(); + final CreatePlaceStatisticsCommand command = new CreatePlaceStatisticsCommand(placeId); + placeStatisticsService.createPlaceStatistics(command); + } +} diff --git a/backend/src/main/java/com/now/naaga/place/application/PlaceService.java b/backend/src/main/java/com/now/naaga/place/application/PlaceService.java index d27464e1a..0b3ec83de 100644 --- a/backend/src/main/java/com/now/naaga/place/application/PlaceService.java +++ b/backend/src/main/java/com/now/naaga/place/application/PlaceService.java @@ -8,16 +8,16 @@ import com.now.naaga.place.application.dto.FindPlaceByIdCommand; import com.now.naaga.place.application.dto.RecommendPlaceCommand; import com.now.naaga.place.domain.Place; -import com.now.naaga.place.domain.PlaceCheckService; +import com.now.naaga.place.domain.PlaceCreateEvent; import com.now.naaga.place.domain.PlaceRecommendService; import com.now.naaga.place.domain.Position; import com.now.naaga.place.domain.SortType; import com.now.naaga.place.exception.PlaceException; -import com.now.naaga.place.persistence.repository.PlaceRepository; +import com.now.naaga.place.repository.PlaceRepository; import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.domain.Player; -import com.now.naaga.temporaryplace.application.TemporaryPlaceService; import java.util.List; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,22 +29,18 @@ public class PlaceService { private final PlayerService playerService; - private final TemporaryPlaceService temporaryPlaceService; - - private final PlaceCheckService placeCheckService; - private final PlaceRecommendService placeRecommendService; + private final ApplicationEventPublisher applicationEventPublisher; + public PlaceService(final PlaceRepository placeRepository, final PlayerService playerService, - final TemporaryPlaceService temporaryPlaceService, - final PlaceCheckService placeCheckService, - final PlaceRecommendService placeRecommendService) { + final PlaceRecommendService placeRecommendService, + final ApplicationEventPublisher applicationEventPublisher) { this.placeRepository = placeRepository; this.playerService = playerService; - this.temporaryPlaceService = temporaryPlaceService; - this.placeCheckService = placeCheckService; this.placeRecommendService = placeRecommendService; + this.applicationEventPublisher = applicationEventPublisher; } @Transactional(readOnly = true) @@ -69,8 +65,6 @@ public Place recommendPlaceByPosition(final RecommendPlaceCommand recommendPlace } public Place createPlace(final CreatePlaceCommand createPlaceCommand) { - placeCheckService.checkOtherPlaceNearby(createPlaceCommand.position()); - final Long registeredPlayerId = createPlaceCommand.registeredPlayerId(); final Player registeredPlayer = playerService.findPlayerById(registeredPlayerId); final Place place = new Place(createPlaceCommand.name(), @@ -80,7 +74,7 @@ public Place createPlace(final CreatePlaceCommand createPlaceCommand) { registeredPlayer); placeRepository.save(place); - temporaryPlaceService.deleteById(createPlaceCommand.temporaryPlaceId()); + applicationEventPublisher.publishEvent(new PlaceCreateEvent(createPlaceCommand.temporaryPlaceId(), place.getId())); return place; } } diff --git a/backend/src/main/java/com/now/naaga/place/application/PlaceStatisticsService.java b/backend/src/main/java/com/now/naaga/place/application/PlaceStatisticsService.java new file mode 100644 index 000000000..b81e4ab49 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/PlaceStatisticsService.java @@ -0,0 +1,64 @@ +package com.now.naaga.place.application; + +import static com.now.naaga.place.domain.PlaceStatistics.LIKE_COUNT_DEFAULT_VALUE; + +import com.now.naaga.place.application.dto.CreatePlaceStatisticsCommand; +import com.now.naaga.place.application.dto.FindPlaceByIdCommand; +import com.now.naaga.place.application.dto.FindPlaceStatisticsByPlaceIdCommand; +import com.now.naaga.place.application.dto.PlusLikeCommand; +import com.now.naaga.place.application.dto.SubtractLikeCommand; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.place.exception.PlaceException; +import com.now.naaga.place.exception.PlaceExceptionType; +import com.now.naaga.place.exception.PlaceStatisticsException; +import com.now.naaga.place.exception.PlaceStatisticsExceptionType; +import com.now.naaga.place.repository.PlaceStatisticsRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class PlaceStatisticsService { + + private final PlaceStatisticsRepository placeStatisticsRepository; + + private final PlaceService placeService; + + public PlaceStatisticsService(final PlaceStatisticsRepository placeStatisticsRepository, + final PlaceService placeService) { + this.placeStatisticsRepository = placeStatisticsRepository; + this.placeService = placeService; + } + + public PlaceStatistics createPlaceStatistics(final CreatePlaceStatisticsCommand createPlaceStatisticsCommand) { + final Long placeId = createPlaceStatisticsCommand.placeId(); + final Place place = placeService.findPlaceById(new FindPlaceByIdCommand(placeId)); + + final PlaceStatistics placeStatistics = new PlaceStatistics(place, LIKE_COUNT_DEFAULT_VALUE); + return placeStatisticsRepository.save(placeStatistics); + } + + public void plusLike(final PlusLikeCommand plusLikeCommand) { + final Long placeId = plusLikeCommand.placeId(); + final PlaceStatistics placeStatistics = placeStatisticsRepository.findByPlaceId(placeId) + .orElseThrow(() -> new PlaceException(PlaceExceptionType.NO_EXIST)); + placeStatistics.plusLike(); + } + + public void subtractLike(final SubtractLikeCommand subtractLikeCommand) { + final Long placeId = subtractLikeCommand.placeId(); + + final PlaceStatistics placeStatistics = placeStatisticsRepository.findByPlaceId(placeId) + .orElseThrow(() -> new PlaceStatisticsException(PlaceStatisticsExceptionType.NOT_FOUND)); + + placeStatistics.subtractLike(); + } + + public PlaceStatistics findPlaceStatisticsByPlaceId(final FindPlaceStatisticsByPlaceIdCommand findPlaceStatisticsByPlaceIdCommand) { + final Long placeId = findPlaceStatisticsByPlaceIdCommand.placeId(); + + return placeStatisticsRepository.findByPlaceId(placeId) + .orElseThrow(() -> new PlaceStatisticsException(PlaceStatisticsExceptionType.NOT_FOUND)); + } +} diff --git a/backend/src/main/java/com/now/naaga/place/application/dto/CreatePlaceStatisticsCommand.java b/backend/src/main/java/com/now/naaga/place/application/dto/CreatePlaceStatisticsCommand.java new file mode 100644 index 000000000..5366c66e5 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/dto/CreatePlaceStatisticsCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.place.application.dto; + +public record CreatePlaceStatisticsCommand(Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/place/application/dto/FindPlaceStatisticsByPlaceIdCommand.java b/backend/src/main/java/com/now/naaga/place/application/dto/FindPlaceStatisticsByPlaceIdCommand.java new file mode 100644 index 000000000..be6546cd8 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/dto/FindPlaceStatisticsByPlaceIdCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.place.application.dto; + +public record FindPlaceStatisticsByPlaceIdCommand(Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/place/application/dto/PlusLikeCommand.java b/backend/src/main/java/com/now/naaga/place/application/dto/PlusLikeCommand.java new file mode 100644 index 000000000..cfd4b8bc7 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/dto/PlusLikeCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.place.application.dto; + +public record PlusLikeCommand(Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/place/application/dto/SubtractLikeCommand.java b/backend/src/main/java/com/now/naaga/place/application/dto/SubtractLikeCommand.java new file mode 100644 index 000000000..149043c94 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/application/dto/SubtractLikeCommand.java @@ -0,0 +1,4 @@ +package com.now.naaga.place.application.dto; + +public record SubtractLikeCommand(Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/place/domain/Place.java b/backend/src/main/java/com/now/naaga/place/domain/Place.java index 6b2682faa..2d8b9882c 100644 --- a/backend/src/main/java/com/now/naaga/place/domain/Place.java +++ b/backend/src/main/java/com/now/naaga/place/domain/Place.java @@ -6,8 +6,14 @@ import com.now.naaga.place.exception.PlaceException; import com.now.naaga.place.exception.PlaceExceptionType; import com.now.naaga.player.domain.Player; -import jakarta.persistence.*; - +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import java.util.Objects; @Entity diff --git a/backend/src/main/java/com/now/naaga/place/domain/PlaceCheckService.java b/backend/src/main/java/com/now/naaga/place/domain/PlaceCheckService.java deleted file mode 100644 index b44b9ac6e..000000000 --- a/backend/src/main/java/com/now/naaga/place/domain/PlaceCheckService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.now.naaga.place.domain; - -import com.now.naaga.place.exception.PlaceException; -import com.now.naaga.place.exception.PlaceExceptionType; -import com.now.naaga.place.persistence.repository.PlaceRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Transactional -@Service -public class PlaceCheckService { - - private final PlaceRepository placeRepository; - - public PlaceCheckService(final PlaceRepository placeRepository) { - this.placeRepository = placeRepository; - } - - @Transactional(readOnly = true) - public void checkOtherPlaceNearby(final Position position) { - List places = placeRepository.findPlaceByPositionAndDistance(position, 0.02); - if (!places.isEmpty()) { - throw new PlaceException(PlaceExceptionType.ALREADY_EXIST_NEARBY); - } - } -} diff --git a/backend/src/main/java/com/now/naaga/place/domain/PlaceCreateEvent.java b/backend/src/main/java/com/now/naaga/place/domain/PlaceCreateEvent.java new file mode 100644 index 000000000..93f2548a2 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/domain/PlaceCreateEvent.java @@ -0,0 +1,5 @@ +package com.now.naaga.place.domain; + +public record PlaceCreateEvent(Long temporaryPlaceId, + Long placeId) { +} diff --git a/backend/src/main/java/com/now/naaga/place/domain/PlaceRecommendService.java b/backend/src/main/java/com/now/naaga/place/domain/PlaceRecommendService.java index 93060f064..63adfa6a8 100644 --- a/backend/src/main/java/com/now/naaga/place/domain/PlaceRecommendService.java +++ b/backend/src/main/java/com/now/naaga/place/domain/PlaceRecommendService.java @@ -1,14 +1,12 @@ package com.now.naaga.place.domain; -import com.now.naaga.place.exception.PlaceException; -import com.now.naaga.place.persistence.repository.PlaceRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import static com.now.naaga.place.exception.PlaceExceptionType.NO_EXIST; +import com.now.naaga.place.exception.PlaceException; +import com.now.naaga.place.repository.PlaceRepository; import java.util.List; import java.util.Random; - -import static com.now.naaga.place.exception.PlaceExceptionType.NO_EXIST; +import org.springframework.stereotype.Service; @Service public class PlaceRecommendService { diff --git a/backend/src/main/java/com/now/naaga/placestatistics/PlaceStatistics.java b/backend/src/main/java/com/now/naaga/place/domain/PlaceStatistics.java similarity index 79% rename from backend/src/main/java/com/now/naaga/placestatistics/PlaceStatistics.java rename to backend/src/main/java/com/now/naaga/place/domain/PlaceStatistics.java index 61ed29625..c3adeba52 100644 --- a/backend/src/main/java/com/now/naaga/placestatistics/PlaceStatistics.java +++ b/backend/src/main/java/com/now/naaga/place/domain/PlaceStatistics.java @@ -1,8 +1,8 @@ -package com.now.naaga.placestatistics; +package com.now.naaga.place.domain; import com.now.naaga.common.domain.BaseEntity; -import com.now.naaga.place.domain.Place; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -13,11 +13,13 @@ @Entity public class PlaceStatistics extends BaseEntity { + public static final long LIKE_COUNT_DEFAULT_VALUE = 0L; + @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private Long id; - @OneToOne + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "place_id") private Place place; @@ -41,6 +43,21 @@ public PlaceStatistics(final Long id, this.likeCount = likeCount; } + public void plusLike() { + likeCount++; + } + + public void subtractLike() { + if(isDefaultValue()) { + return; + } + likeCount--; + } + + private boolean isDefaultValue() { + return likeCount == LIKE_COUNT_DEFAULT_VALUE; + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsException.java b/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsException.java new file mode 100644 index 000000000..11991da1a --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsException.java @@ -0,0 +1,18 @@ +package com.now.naaga.place.exception; + +import com.now.naaga.common.exception.BaseException; +import com.now.naaga.common.exception.BaseExceptionType; + +public class PlaceStatisticsException extends BaseException { + + private final PlaceStatisticsExceptionType placeStatisticsExceptionType; + + public PlaceStatisticsException(final PlaceStatisticsExceptionType placeStatisticsExceptionType) { + this.placeStatisticsExceptionType = placeStatisticsExceptionType; + } + + @Override + public BaseExceptionType exceptionType() { + return placeStatisticsExceptionType; + } +} diff --git a/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsExceptionType.java b/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsExceptionType.java new file mode 100644 index 000000000..421f03a32 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/exception/PlaceStatisticsExceptionType.java @@ -0,0 +1,41 @@ +package com.now.naaga.place.exception; + +import com.now.naaga.common.exception.BaseExceptionType; +import org.springframework.http.HttpStatus; + +public enum PlaceStatisticsExceptionType implements BaseExceptionType { + + NOT_FOUND( + 804, + HttpStatus.NOT_FOUND, + "ํ•ด๋‹น ์žฅ์†Œ ํ†ต๊ณ„๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค." + ), + ; + + private final int errorCode; + private final HttpStatus httpStatus; + private final String errorMessage; + + PlaceStatisticsExceptionType(final int errorCode, + final HttpStatus httpStatus, + final String errorMessage) { + this.errorCode = errorCode; + this.httpStatus = httpStatus; + this.errorMessage = errorMessage; + } + + @Override + public int errorCode() { + return errorCode; + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String errorMessage() { + return errorMessage; + } +} diff --git a/backend/src/main/java/com/now/naaga/place/persistence/repository/PlaceRepository.java b/backend/src/main/java/com/now/naaga/place/repository/PlaceRepository.java similarity index 95% rename from backend/src/main/java/com/now/naaga/place/persistence/repository/PlaceRepository.java rename to backend/src/main/java/com/now/naaga/place/repository/PlaceRepository.java index 60198adf9..acc420dcd 100644 --- a/backend/src/main/java/com/now/naaga/place/persistence/repository/PlaceRepository.java +++ b/backend/src/main/java/com/now/naaga/place/repository/PlaceRepository.java @@ -1,4 +1,4 @@ -package com.now.naaga.place.persistence.repository; +package com.now.naaga.place.repository; import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; diff --git a/backend/src/main/java/com/now/naaga/place/repository/PlaceStatisticsRepository.java b/backend/src/main/java/com/now/naaga/place/repository/PlaceStatisticsRepository.java new file mode 100644 index 000000000..01c07d853 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/place/repository/PlaceStatisticsRepository.java @@ -0,0 +1,10 @@ +package com.now.naaga.place.repository; + +import com.now.naaga.place.domain.PlaceStatistics; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PlaceStatisticsRepository extends JpaRepository { + + Optional findByPlaceId(Long placeId); +} diff --git a/backend/src/main/java/com/now/naaga/placestatistics/repository/PlaceStatisticsRepository.java b/backend/src/main/java/com/now/naaga/placestatistics/repository/PlaceStatisticsRepository.java deleted file mode 100644 index 267c40a17..000000000 --- a/backend/src/main/java/com/now/naaga/placestatistics/repository/PlaceStatisticsRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.now.naaga.placestatistics.repository; - -import com.now.naaga.placestatistics.PlaceStatistics; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface PlaceStatisticsRepository extends JpaRepository { -} diff --git a/backend/src/main/java/com/now/naaga/player/application/PlayerService.java b/backend/src/main/java/com/now/naaga/player/application/PlayerService.java index b1c96dd24..b3ea891ad 100644 --- a/backend/src/main/java/com/now/naaga/player/application/PlayerService.java +++ b/backend/src/main/java/com/now/naaga/player/application/PlayerService.java @@ -1,5 +1,7 @@ package com.now.naaga.player.application; +import static com.now.naaga.player.exception.PlayerExceptionType.PLAYER_NOT_FOUND; + import com.now.naaga.player.application.dto.AddScoreCommand; import com.now.naaga.player.application.dto.CreatePlayerCommand; import com.now.naaga.player.application.dto.DeletePlayerCommand; @@ -9,13 +11,10 @@ import com.now.naaga.player.persistence.repository.PlayerRepository; import com.now.naaga.player.presentation.dto.PlayerRequest; import com.now.naaga.score.domain.Score; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.util.ArrayList; import java.util.List; - -import static com.now.naaga.player.exception.PlayerExceptionType.PLAYER_NOT_FOUND; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Transactional @Service diff --git a/backend/src/main/java/com/now/naaga/player/domain/Player.java b/backend/src/main/java/com/now/naaga/player/domain/Player.java index c892c1a62..aaea7a4c3 100644 --- a/backend/src/main/java/com/now/naaga/player/domain/Player.java +++ b/backend/src/main/java/com/now/naaga/player/domain/Player.java @@ -1,16 +1,24 @@ package com.now.naaga.player.domain; +import static java.lang.Boolean.FALSE; + import com.now.naaga.common.domain.BaseEntity; import com.now.naaga.member.domain.Member; import com.now.naaga.score.domain.Score; -import jakarta.persistence.*; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import java.util.Objects; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; -import java.util.Objects; - -import static java.lang.Boolean.FALSE; - @SQLDelete(sql = "UPDATE player SET deleted = true WHERE id = ?") @Where(clause = "deleted = false") @Entity diff --git a/backend/src/main/java/com/now/naaga/player/presentation/PlayerController.java b/backend/src/main/java/com/now/naaga/player/presentation/PlayerController.java index 318957cac..0638e5ac7 100644 --- a/backend/src/main/java/com/now/naaga/player/presentation/PlayerController.java +++ b/backend/src/main/java/com/now/naaga/player/presentation/PlayerController.java @@ -1,22 +1,21 @@ package com.now.naaga.player.presentation; +import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; + import com.now.naaga.auth.presentation.annotation.Auth; import com.now.naaga.common.exception.CommonException; import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.domain.Rank; import com.now.naaga.player.presentation.dto.PlayerRequest; import com.now.naaga.player.presentation.dto.RankResponse; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; -import java.util.stream.Collectors; - -import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; - @RequestMapping("/ranks") @RestController public class PlayerController { diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/application/DeleteTemporaryPlaceWithPlaceCreateEventHandler.java b/backend/src/main/java/com/now/naaga/temporaryplace/application/DeleteTemporaryPlaceWithPlaceCreateEventHandler.java new file mode 100644 index 000000000..89d08f46f --- /dev/null +++ b/backend/src/main/java/com/now/naaga/temporaryplace/application/DeleteTemporaryPlaceWithPlaceCreateEventHandler.java @@ -0,0 +1,23 @@ +package com.now.naaga.temporaryplace.application; + +import com.now.naaga.place.domain.PlaceCreateEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class DeleteTemporaryPlaceWithPlaceCreateEventHandler { + + private final TemporaryPlaceService temporaryPlaceService; + + public DeleteTemporaryPlaceWithPlaceCreateEventHandler(final TemporaryPlaceService temporaryPlaceService) { + this.temporaryPlaceService = temporaryPlaceService; + } + + @Transactional + @EventListener + public void handle(final PlaceCreateEvent placeCreateEvent) { + final Long temporaryPlaceId = placeCreateEvent.temporaryPlaceId(); + temporaryPlaceService.deleteByIdWhenPlaceCreated(temporaryPlaceId); + } +} diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/application/TemporaryPlaceService.java b/backend/src/main/java/com/now/naaga/temporaryplace/application/TemporaryPlaceService.java index 5165a2694..b2c1ca530 100644 --- a/backend/src/main/java/com/now/naaga/temporaryplace/application/TemporaryPlaceService.java +++ b/backend/src/main/java/com/now/naaga/temporaryplace/application/TemporaryPlaceService.java @@ -1,41 +1,49 @@ package com.now.naaga.temporaryplace.application; -import com.now.naaga.common.infrastructure.FileManager; +import com.now.naaga.common.infrastructure.AwsS3FileManager; import com.now.naaga.place.domain.Position; import com.now.naaga.player.application.PlayerService; import com.now.naaga.player.domain.Player; import com.now.naaga.temporaryplace.application.dto.CreateTemporaryPlaceCommand; import com.now.naaga.temporaryplace.domain.TemporaryPlace; +import com.now.naaga.temporaryplace.exception.TemporaryPlaceException; import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; -import java.io.File; import java.util.Comparator; import java.util.List; +import static com.now.naaga.temporaryplace.exception.TemporaryPlaceExceptionType.NOT_EXIST; + @Transactional @Service public class TemporaryPlaceService { - + private final TemporaryPlaceRepository temporaryPlaceRepository; - + private final PlayerService playerService; - - private final FileManager fileManager; - + + private final AwsS3FileManager awsS3FileManager; + + private final String imageUrlPrefix; + public TemporaryPlaceService(final TemporaryPlaceRepository temporaryPlaceRepository, final PlayerService playerService, - final FileManager fileManager) { + final AwsS3FileManager awsS3FileManager, + @Value("${image.path.url.prefix}") final String imageUrlPrefix) { this.temporaryPlaceRepository = temporaryPlaceRepository; this.playerService = playerService; - this.fileManager = fileManager; + this.awsS3FileManager = awsS3FileManager; + this.imageUrlPrefix = imageUrlPrefix; } - + public TemporaryPlace createTemporaryPlace(final CreateTemporaryPlaceCommand createTemporaryPlaceCommand) { final Position position = createTemporaryPlaceCommand.position(); - final File uploadPath = fileManager.save(createTemporaryPlaceCommand.imageFile()); + final String s3Path = awsS3FileManager.uploadFile(createTemporaryPlaceCommand.imageFile()); + final String fileName = s3Path.substring(s3Path.lastIndexOf("/") + 1); + final String imageUrl = imageUrlPrefix + fileName; try { final Long playerId = createTemporaryPlaceCommand.playerId(); final Player registeredPlayer = playerService.findPlayerById(playerId); @@ -43,19 +51,28 @@ public TemporaryPlace createTemporaryPlace(final CreateTemporaryPlaceCommand cre createTemporaryPlaceCommand.name(), createTemporaryPlaceCommand.description(), position, - fileManager.convertToUrlPath(uploadPath), + imageUrl, registeredPlayer); return temporaryPlaceRepository.save(temporaryPlace); } catch (final RuntimeException exception) { - uploadPath.delete(); + awsS3FileManager.deleteFile(s3Path); throw exception; } } - - public void deleteById(final Long id) { + + public void deleteByIdWhenPlaceCreated(final Long id) { + TemporaryPlace temporaryPlace = temporaryPlaceRepository.findById(id) + .orElseThrow(() -> new TemporaryPlaceException(NOT_EXIST)); temporaryPlaceRepository.deleteById(id); } - + + public void deleteByIdWhenTemporaryPlaceDenied(final Long id) { + TemporaryPlace temporaryPlace = temporaryPlaceRepository.findById(id) + .orElseThrow(() -> new TemporaryPlaceException(NOT_EXIST)); + temporaryPlaceRepository.deleteById(id); + awsS3FileManager.deleteFile(temporaryPlace.getImageUrl()); + } + @Transactional(readOnly = true) public List findAllTemporaryPlace() { final List temporaryPlaces = temporaryPlaceRepository.findAll(); diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceException.java b/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceException.java new file mode 100644 index 000000000..b5aaf85eb --- /dev/null +++ b/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceException.java @@ -0,0 +1,18 @@ +package com.now.naaga.temporaryplace.exception; + +import com.now.naaga.common.exception.BaseException; +import com.now.naaga.common.exception.BaseExceptionType; + +public class TemporaryPlaceException extends BaseException { + + private final TemporaryPlaceExceptionType temporaryPlaceExceptionType; + + public TemporaryPlaceException(final TemporaryPlaceExceptionType temporaryPlaceExceptionType) { + this.temporaryPlaceExceptionType = temporaryPlaceExceptionType; + } + + @Override + public BaseExceptionType exceptionType() { + return temporaryPlaceExceptionType; + } +} diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceExceptionType.java b/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceExceptionType.java new file mode 100644 index 000000000..7592731c6 --- /dev/null +++ b/backend/src/main/java/com/now/naaga/temporaryplace/exception/TemporaryPlaceExceptionType.java @@ -0,0 +1,43 @@ +package com.now.naaga.temporaryplace.exception; + +import com.now.naaga.common.exception.BaseExceptionType; +import org.springframework.http.HttpStatus; + +public enum TemporaryPlaceExceptionType implements BaseExceptionType { + + NOT_EXIST( + 604, + HttpStatus.NOT_FOUND, + "๊ฒ€์ˆ˜ ํ•  ์žฅ์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค" + ), + ; + + private final int errorCode; + + private final HttpStatus httpStatus; + + private final String errorMessage; + + TemporaryPlaceExceptionType(final int errorCode, + final HttpStatus httpStatus, + final String errorMessage) { + this.errorCode = errorCode; + this.httpStatus = httpStatus; + this.errorMessage = errorMessage; + } + + @Override + public int errorCode() { + return errorCode(); + } + + @Override + public HttpStatus httpStatus() { + return httpStatus; + } + + @Override + public String errorMessage() { + return errorMessage; + } +} diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceController.java b/backend/src/main/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceController.java index c6832a902..76f6073c6 100644 --- a/backend/src/main/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceController.java +++ b/backend/src/main/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceController.java @@ -7,12 +7,17 @@ import com.now.naaga.temporaryplace.domain.TemporaryPlace; import com.now.naaga.temporaryplace.presentation.dto.CreateTemporaryPlaceRequest; import com.now.naaga.temporaryplace.presentation.dto.TemporaryPlaceResponse; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - import java.net.URI; import java.util.List; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RequestMapping("/temporary-places") @RestController @@ -47,9 +52,11 @@ public ResponseEntity createTemporaryPlace(@Auth final P @DeleteMapping("/{temporaryPlaceId}") public ResponseEntity deleteTemporaryPlace(@PathVariable final Long temporaryPlaceId) { - temporaryPlaceService.deleteById(temporaryPlaceId); + temporaryPlaceService.deleteByIdWhenTemporaryPlaceDenied(temporaryPlaceId); return ResponseEntity .status(HttpStatus.NO_CONTENT) .build(); } + /* S3์‚ฌ์šฉ + */ } diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/presentation/dto/TemporaryPlaceResponse.java b/backend/src/main/java/com/now/naaga/temporaryplace/presentation/dto/TemporaryPlaceResponse.java index 2730c3832..f8da3b645 100644 --- a/backend/src/main/java/com/now/naaga/temporaryplace/presentation/dto/TemporaryPlaceResponse.java +++ b/backend/src/main/java/com/now/naaga/temporaryplace/presentation/dto/TemporaryPlaceResponse.java @@ -2,7 +2,6 @@ import com.now.naaga.game.presentation.dto.CoordinateResponse; import com.now.naaga.temporaryplace.domain.TemporaryPlace; - import java.util.List; import java.util.stream.Collectors; diff --git a/backend/src/main/java/com/now/naaga/temporaryplace/repository/TemporaryPlaceRepository.java b/backend/src/main/java/com/now/naaga/temporaryplace/repository/TemporaryPlaceRepository.java index d9a4a1faf..6c3ac7a0e 100644 --- a/backend/src/main/java/com/now/naaga/temporaryplace/repository/TemporaryPlaceRepository.java +++ b/backend/src/main/java/com/now/naaga/temporaryplace/repository/TemporaryPlaceRepository.java @@ -2,11 +2,10 @@ import com.now.naaga.temporaryplace.domain.TemporaryPlace; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import java.util.List; - public interface TemporaryPlaceRepository extends JpaRepository { @Override diff --git a/backend/src/main/resources/console-appender.xml b/backend/src/main/resources/console-appender.xml deleted file mode 100644 index b56628e05..000000000 --- a/backend/src/main/resources/console-appender.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - ${CONSOLE_LOG_PATTERN} - - - diff --git a/backend/src/main/resources/db-file-appender.xml b/backend/src/main/resources/db-file-appender.xml deleted file mode 100644 index 13bfd0d23..000000000 --- a/backend/src/main/resources/db-file-appender.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - ${LOGS_ABSOLUTE_PATH}/db.log - - INFO - ACCEPT - DENY - - - ${FILE_LOG_PATTERN} - - - ${LOGS_ABSOLUTE_PATH}/old-logs/db.%d{yyyy-MM-dd}.%i.log.gz - - 50MB - - 30 - 1GB - - - diff --git a/backend/src/main/resources/error-file-appender.xml b/backend/src/main/resources/error-file-appender.xml deleted file mode 100644 index f564b5387..000000000 --- a/backend/src/main/resources/error-file-appender.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - ${LOGS_ABSOLUTE_PATH}/error.log - - ERROR - ACCEPT - DENY - - - ${FILE_LOG_PATTERN} - - - ${LOGS_ABSOLUTE_PATH}/old-logs/error.%d{yyyy-MM-dd}.%i.log.gz - - 50MB - - 30 - 3GB - - - diff --git a/backend/src/main/resources/info-file-appender.xml b/backend/src/main/resources/info-file-appender.xml deleted file mode 100644 index ba4fa9373..000000000 --- a/backend/src/main/resources/info-file-appender.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - ${LOGS_ABSOLUTE_PATH}/info.log - - INFO - ACCEPT - DENY - - - ${FILE_LOG_PATTERN} - - - ${LOGS_ABSOLUTE_PATH}/old-logs/info.%d{yyyy-MM-dd}.%i.log.gz - - 50MB - - 30 - 1GB - - - diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml deleted file mode 100644 index 06a184653..000000000 --- a/backend/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/main/resources/security b/backend/src/main/resources/security index f95252b8f..6aea9836a 160000 --- a/backend/src/main/resources/security +++ b/backend/src/main/resources/security @@ -1 +1 @@ -Subproject commit f95252b8fb6b0766ec8a964f3737e70073863fee +Subproject commit 6aea9836ab385f1eee8d5075f6dd98e800ff158e diff --git a/backend/src/main/resources/warn-file-appender.xml b/backend/src/main/resources/warn-file-appender.xml deleted file mode 100644 index f512584da..000000000 --- a/backend/src/main/resources/warn-file-appender.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - ${LOGS_ABSOLUTE_PATH}/warn.log - - WARN - ACCEPT - DENY - - - ${FILE_LOG_PATTERN} - - - ${LOGS_ABSOLUTE_PATH}/old-logs/warn.%d{yyyy-MM-dd}.%i.log.gz - - 50MB - - 30 - 2GB - - - diff --git a/backend/src/test/java/com/now/naaga/auth/application/AuthServiceTest.java b/backend/src/test/java/com/now/naaga/auth/application/AuthServiceTest.java index 4ca8e2bff..106c8170e 100644 --- a/backend/src/test/java/com/now/naaga/auth/application/AuthServiceTest.java +++ b/backend/src/test/java/com/now/naaga/auth/application/AuthServiceTest.java @@ -1,19 +1,24 @@ package com.now.naaga.auth.application; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + import com.now.naaga.auth.application.dto.AuthCommand; -import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthClient; import com.now.naaga.auth.infrastructure.AuthType; +import com.now.naaga.auth.infrastructure.dto.AuthInfo; import com.now.naaga.auth.infrastructure.dto.MemberAuth; import com.now.naaga.common.builder.PlayerBuilder; import com.now.naaga.member.domain.Member; -import com.now.naaga.member.exception.MemberException; import com.now.naaga.member.persistence.repository.MemberRepository; import com.now.naaga.player.domain.Player; -import com.now.naaga.player.exception.PlayerException; import com.now.naaga.player.persistence.repository.PlayerRepository; import com.now.naaga.score.domain.Score; +import java.util.Optional; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -22,16 +27,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.jdbc.Sql; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.MimeType; - -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/auth/presentation/AuthControllerTest.java b/backend/src/test/java/com/now/naaga/auth/presentation/AuthControllerTest.java index 544f04d7a..eae1b861f 100644 --- a/backend/src/test/java/com/now/naaga/auth/presentation/AuthControllerTest.java +++ b/backend/src/test/java/com/now/naaga/auth/presentation/AuthControllerTest.java @@ -1,5 +1,14 @@ package com.now.naaga.auth.presentation; +import static com.now.naaga.auth.exception.AuthExceptionType.EXPIRED_TOKEN; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN_ACCESS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthClient; import com.now.naaga.auth.infrastructure.AuthType; @@ -17,11 +26,11 @@ import com.now.naaga.common.exception.ExceptionResponse; import com.now.naaga.member.domain.Member; import com.now.naaga.player.domain.Player; -import com.now.naaga.player.persistence.repository.PlayerRepository; import com.now.naaga.score.domain.Score; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.util.Date; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; @@ -31,16 +40,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import java.util.Date; - -import static com.now.naaga.auth.exception.AuthExceptionType.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.InstanceOfAssertFactories.spliterator; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) class AuthControllerTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/auth/presentation/AuthInterceptorTest.java b/backend/src/test/java/com/now/naaga/auth/presentation/AuthInterceptorTest.java index 604b55c32..ae30a54fe 100644 --- a/backend/src/test/java/com/now/naaga/auth/presentation/AuthInterceptorTest.java +++ b/backend/src/test/java/com/now/naaga/auth/presentation/AuthInterceptorTest.java @@ -1,5 +1,11 @@ package com.now.naaga.auth.presentation; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_HEADER; +import static com.now.naaga.auth.exception.AuthExceptionType.INVALID_TOKEN; +import static com.now.naaga.auth.exception.AuthExceptionType.NOT_EXIST_HEADER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; @@ -21,10 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import static com.now.naaga.auth.exception.AuthExceptionType.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) public class AuthInterceptorTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/auth/presentation/PlayerArgumentResolverTest.java b/backend/src/test/java/com/now/naaga/auth/presentation/PlayerArgumentResolverTest.java index 4db66a1a4..702334f78 100644 --- a/backend/src/test/java/com/now/naaga/auth/presentation/PlayerArgumentResolverTest.java +++ b/backend/src/test/java/com/now/naaga/auth/presentation/PlayerArgumentResolverTest.java @@ -1,5 +1,9 @@ package com.now.naaga.auth.presentation; +import static com.now.naaga.player.exception.PlayerExceptionType.PLAYER_NOT_FOUND; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; @@ -18,10 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import static com.now.naaga.player.exception.PlayerExceptionType.PLAYER_NOT_FOUND; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) public class PlayerArgumentResolverTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptorTest.java b/backend/src/test/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptorTest.java index 0ba3de499..6eb262c4d 100644 --- a/backend/src/test/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptorTest.java +++ b/backend/src/test/java/com/now/naaga/auth/presentation/interceptor/ManagerAuthInterceptorTest.java @@ -1,6 +1,10 @@ package com.now.naaga.auth.presentation.interceptor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.now.naaga.auth.exception.AuthException; +import java.util.Base64; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -13,11 +17,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.web.servlet.mvc.Controller; -import java.util.Base64; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/common/CommonControllerTest.java b/backend/src/test/java/com/now/naaga/common/CommonControllerTest.java index 542ca0bb4..fb1103278 100644 --- a/backend/src/test/java/com/now/naaga/common/CommonControllerTest.java +++ b/backend/src/test/java/com/now/naaga/common/CommonControllerTest.java @@ -6,8 +6,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; +@ActiveProfiles("test") @Sql("/truncate.sql") @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public abstract class CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/common/builder/LetterBuilder.java b/backend/src/test/java/com/now/naaga/common/builder/LetterBuilder.java new file mode 100644 index 000000000..de7e2b51b --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/builder/LetterBuilder.java @@ -0,0 +1,61 @@ +package com.now.naaga.common.builder; + +import static com.now.naaga.common.fixture.LetterFixture.MESSAGE; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; + +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.repository.LetterRepository; +import com.now.naaga.place.domain.Position; +import com.now.naaga.player.domain.Player; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class LetterBuilder { + + @Autowired + private LetterRepository letterRepository; + + @Autowired + private PlayerBuilder playerBuilder; + + private Optional registeredPlayer; + + private Position position; + + private String message; + + public LetterBuilder init() { + this.registeredPlayer = Optional.empty(); + this.position = ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; + this.message = MESSAGE; + return this; + } + + public LetterBuilder registeredPlayer(final Player persistedPlayer) { + this.registeredPlayer = Optional.ofNullable(persistedPlayer); + return this; + } + + public LetterBuilder position(final Position position) { + this.position = position; + return this; + } + + public LetterBuilder message(final String message) { + this.message = message; + return this; + } + + public Letter build() { + final Player persistedPlayer = registeredPlayer.orElseGet(this::getPersistedPlayer); + final Letter letter = new Letter(persistedPlayer, position, message); + return letterRepository.save(letter); + } + + private Player getPersistedPlayer() { + return playerBuilder.init() + .build(); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/builder/PlaceBuilder.java b/backend/src/test/java/com/now/naaga/common/builder/PlaceBuilder.java index bfca3974d..d716e2b98 100644 --- a/backend/src/test/java/com/now/naaga/common/builder/PlaceBuilder.java +++ b/backend/src/test/java/com/now/naaga/common/builder/PlaceBuilder.java @@ -7,7 +7,7 @@ import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; -import com.now.naaga.place.persistence.repository.PlaceRepository; +import com.now.naaga.place.repository.PlaceRepository; import com.now.naaga.player.domain.Player; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; diff --git a/backend/src/test/java/com/now/naaga/common/builder/PlaceStatisticsBuilder.java b/backend/src/test/java/com/now/naaga/common/builder/PlaceStatisticsBuilder.java index e49f7a80d..e0211024b 100644 --- a/backend/src/test/java/com/now/naaga/common/builder/PlaceStatisticsBuilder.java +++ b/backend/src/test/java/com/now/naaga/common/builder/PlaceStatisticsBuilder.java @@ -1,8 +1,8 @@ package com.now.naaga.common.builder; import com.now.naaga.place.domain.Place; -import com.now.naaga.placestatistics.PlaceStatistics; -import com.now.naaga.placestatistics.repository.PlaceStatisticsRepository; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.place.repository.PlaceStatisticsRepository; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/backend/src/test/java/com/now/naaga/common/builder/ReadLetterLogBuilder.java b/backend/src/test/java/com/now/naaga/common/builder/ReadLetterLogBuilder.java new file mode 100644 index 000000000..5e9b30fcb --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/builder/ReadLetterLogBuilder.java @@ -0,0 +1,59 @@ +package com.now.naaga.common.builder; + +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import com.now.naaga.letter.repository.letterlog.ReadLetterLogRepository; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ReadLetterLogBuilder { + + @Autowired + private ReadLetterLogRepository readLetterLogRepository; + + @Autowired + private GameBuilder gameBuilder; + + @Autowired + private LetterBuilder letterBuilder; + + private Optional registeredGame; + + private Optional registeredLetter; + + public ReadLetterLogBuilder init() { + this.registeredGame = Optional.empty(); + this.registeredLetter = Optional.empty(); + return this; + } + + public ReadLetterLogBuilder game(final Game registeredGame) { + this.registeredGame = Optional.ofNullable(registeredGame); + return this; + } + + public ReadLetterLogBuilder letter(final Letter registerdLetter) { + this.registeredLetter = Optional.ofNullable(registerdLetter); + return this; + } + + public ReadLetterLog build() { + final Game persistedgame = registeredGame.orElseGet(this::getPersitedGame); + final Letter persistedLetter = registeredLetter.orElseGet(this::getPersistedLetter); + final ReadLetterLog letterLog = new ReadLetterLog(persistedgame, persistedLetter); + return readLetterLogRepository.save(letterLog); + } + + public Game getPersitedGame() { + return gameBuilder.init() + .build(); + } + + public Letter getPersistedLetter() { + return letterBuilder.init() + .build(); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/builder/WriteLetterLogBuilder.java b/backend/src/test/java/com/now/naaga/common/builder/WriteLetterLogBuilder.java new file mode 100644 index 000000000..27ed2d335 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/builder/WriteLetterLogBuilder.java @@ -0,0 +1,59 @@ +package com.now.naaga.common.builder; + +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; +import com.now.naaga.letter.repository.letterlog.WriteLetterLogRepository; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class WriteLetterLogBuilder { + + @Autowired + GameBuilder gameBuilder; + + @Autowired + LetterBuilder letterBuilder; + + @Autowired + WriteLetterLogRepository writeLetterLogRepository; + + private Optional game; + + private Optional letter; + + public WriteLetterLogBuilder init() { + this.game = Optional.empty(); + this.letter = Optional.empty(); + return this; + } + + public WriteLetterLogBuilder game(Game registerdGame) { + this.game = Optional.ofNullable(registerdGame); + return this; + } + + public WriteLetterLogBuilder letter(Letter registerdLetter) { + this.letter = Optional.ofNullable(registerdLetter); + return this; + } + + public WriteLetterLog build() { + final Game persistedGame = game.orElseGet(this::getPersitedGame); + final Letter persistedLetter = letter.orElseGet(this::getPersistedLetter); + final WriteLetterLog writeLetterLog = new WriteLetterLog(persistedGame, persistedLetter); + return writeLetterLogRepository.save(writeLetterLog); + } + + public Game getPersitedGame() { + return gameBuilder.init() + .build(); + } + + public Letter getPersistedLetter() { + return letterBuilder.init() + .build(); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/fixture/GameFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/GameFixture.java new file mode 100644 index 000000000..f9f5d79ca --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/fixture/GameFixture.java @@ -0,0 +1,14 @@ +package com.now.naaga.common.fixture; + +import static com.now.naaga.common.fixture.PlaceFixture.PLACE; +import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; +import static com.now.naaga.common.fixture.PositionFixture.์„œ์šธ_์ขŒํ‘œ; + +import com.now.naaga.game.domain.Game; + +public class GameFixture { + + public static Game GAME() { + return new Game(PLAYER(), PLACE(), ์„œ์šธ_์ขŒํ‘œ); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/fixture/LetterFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/LetterFixture.java new file mode 100644 index 000000000..aff247be8 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/fixture/LetterFixture.java @@ -0,0 +1,15 @@ +package com.now.naaga.common.fixture; + +import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; + +import com.now.naaga.letter.domain.Letter; + +public class LetterFixture { + + public static final String MESSAGE = "์•ˆ๋…•ํ•˜์„ธ์š”. ๋‚˜์•„๊ฐ€ ๊ฐœ๋ฐœ์ž๋“ค ์ž…๋‹ˆ๋‹ค."; + + public static Letter LETTER() { + return new Letter(PLAYER(), ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, MESSAGE); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/fixture/PlaceFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/PlaceFixture.java index f4d5363d0..4fb54e1bb 100644 --- a/backend/src/test/java/com/now/naaga/common/fixture/PlaceFixture.java +++ b/backend/src/test/java/com/now/naaga/common/fixture/PlaceFixture.java @@ -1,7 +1,6 @@ package com.now.naaga.common.fixture; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.*; +import static com.now.naaga.common.fixture.PositionFixture.์„œ์šธ_์ขŒํ‘œ; import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; diff --git a/backend/src/test/java/com/now/naaga/common/fixture/PlaceStatisticsFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/PlaceStatisticsFixture.java index 3ae0ea5db..0f35c6bd2 100644 --- a/backend/src/test/java/com/now/naaga/common/fixture/PlaceStatisticsFixture.java +++ b/backend/src/test/java/com/now/naaga/common/fixture/PlaceStatisticsFixture.java @@ -1,6 +1,6 @@ package com.now.naaga.common.fixture; -import com.now.naaga.placestatistics.PlaceStatistics; +import com.now.naaga.place.domain.PlaceStatistics; public class PlaceStatisticsFixture { diff --git a/backend/src/test/java/com/now/naaga/common/fixture/PlayerFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/PlayerFixture.java index 067007b2b..6645b3387 100644 --- a/backend/src/test/java/com/now/naaga/common/fixture/PlayerFixture.java +++ b/backend/src/test/java/com/now/naaga/common/fixture/PlayerFixture.java @@ -1,6 +1,6 @@ package com.now.naaga.common.fixture; -import static com.now.naaga.common.fixture.MemberFixture.*; +import static com.now.naaga.common.fixture.MemberFixture.MEMBER; import com.now.naaga.player.domain.Player; import com.now.naaga.score.domain.Score; diff --git a/backend/src/test/java/com/now/naaga/common/fixture/PositionFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/PositionFixture.java index faee1475c..075cb2aed 100644 --- a/backend/src/test/java/com/now/naaga/common/fixture/PositionFixture.java +++ b/backend/src/test/java/com/now/naaga/common/fixture/PositionFixture.java @@ -14,6 +14,9 @@ public class PositionFixture { public static final Position ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ = new Position(BigDecimal.valueOf(37.514258), BigDecimal.valueOf(127.100883)); + public static final Position ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ = new Position(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ.getLatitude().add(BigDecimal.valueOf(0.000991)), + ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ.getLongitude()); + public static final Position GS25_๋ฐฉ์ด๋„๊ณก์ _์ขŒํ‘œ = new Position(BigDecimal.valueOf(37.512184), BigDecimal.valueOf(127.112789)); diff --git a/backend/src/test/java/com/now/naaga/common/fixture/ReadLetterLogFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/ReadLetterLogFixture.java new file mode 100644 index 000000000..a33678b03 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/fixture/ReadLetterLogFixture.java @@ -0,0 +1,13 @@ +package com.now.naaga.common.fixture; + +import static com.now.naaga.common.fixture.GameFixture.GAME; +import static com.now.naaga.common.fixture.LetterFixture.LETTER; + +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; + +public class ReadLetterLogFixture { + + public static ReadLetterLog READ_LETTER_LOG() { + return new ReadLetterLog(GAME(), LETTER()); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/fixture/TemporaryPlaceFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/TemporaryPlaceFixture.java index 906f53597..d00235bc3 100644 --- a/backend/src/test/java/com/now/naaga/common/fixture/TemporaryPlaceFixture.java +++ b/backend/src/test/java/com/now/naaga/common/fixture/TemporaryPlaceFixture.java @@ -1,10 +1,10 @@ package com.now.naaga.common.fixture; -import com.now.naaga.temporaryplace.domain.TemporaryPlace; - import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import com.now.naaga.temporaryplace.domain.TemporaryPlace; + public class TemporaryPlaceFixture { public static final String NAME = "temp_place_name"; diff --git a/backend/src/test/java/com/now/naaga/common/fixture/WriteLetterLogFixture.java b/backend/src/test/java/com/now/naaga/common/fixture/WriteLetterLogFixture.java new file mode 100644 index 000000000..cb1901c62 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/common/fixture/WriteLetterLogFixture.java @@ -0,0 +1,13 @@ +package com.now.naaga.common.fixture; + +import static com.now.naaga.common.fixture.GameFixture.GAME; +import static com.now.naaga.common.fixture.LetterFixture.LETTER; + +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; + +public class WriteLetterLogFixture { + + public static WriteLetterLog WRITE_LETTER_LOG() { + return new WriteLetterLog(GAME(), LETTER()); + } +} diff --git a/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptorTest.java b/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptorTest.java index 7693dd2cc..9c88dbb0e 100644 --- a/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptorTest.java +++ b/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestMatcherInterceptorTest.java @@ -1,5 +1,11 @@ package com.now.naaga.common.presentation.interceptor; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; + +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.params.ParameterizedTest; @@ -10,18 +16,9 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ActiveProfiles; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.mvc.Controller; -import java.util.stream.Stream; - -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; - @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestPatternTest.java b/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestPatternTest.java index ec66c21a0..5debcc19d 100644 --- a/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestPatternTest.java +++ b/backend/src/test/java/com/now/naaga/common/presentation/interceptor/RequestPatternTest.java @@ -1,21 +1,17 @@ package com.now.naaga.common.presentation.interceptor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; + +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.http.HttpMethod; import org.springframework.test.context.ActiveProfiles; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; - -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") diff --git a/backend/src/test/java/com/now/naaga/game/application/GameServiceTest.java b/backend/src/test/java/com/now/naaga/game/application/GameServiceTest.java index 7112ba7f2..9281e908c 100644 --- a/backend/src/test/java/com/now/naaga/game/application/GameServiceTest.java +++ b/backend/src/test/java/com/now/naaga/game/application/GameServiceTest.java @@ -1,5 +1,23 @@ package com.now.naaga.game.application; +import static com.now.naaga.common.fixture.PositionFixture.์—ญ์‚ผ์—ญ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_๊ทผ์ฒ˜_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.game.domain.EndType.ARRIVED; +import static com.now.naaga.game.domain.EndType.GIVE_UP; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.ALREADY_IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.CAN_NOT_FIND_PLACE; +import static com.now.naaga.game.exception.GameExceptionType.INACCESSIBLE_AUTHENTICATION; +import static com.now.naaga.gameresult.domain.ResultType.FAIL; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import static com.now.naaga.gameresult.exception.GameResultExceptionType.GAME_RESULT_NOT_EXIST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertThrows; + import com.now.naaga.common.builder.GameBuilder; import com.now.naaga.common.builder.GameResultBuilder; import com.now.naaga.common.builder.PlaceBuilder; @@ -10,14 +28,17 @@ import com.now.naaga.game.application.dto.FindGameByStatusCommand; import com.now.naaga.game.domain.Game; import com.now.naaga.game.domain.GameRecord; -import com.now.naaga.game.exception.GameNotFinishedException; -import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.game.domain.Statistic; import com.now.naaga.game.exception.GameException; +import com.now.naaga.game.exception.GameNotFinishedException; +import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.gameresult.exception.GameResultException; import com.now.naaga.place.domain.Place; import com.now.naaga.player.domain.Player; import com.now.naaga.player.presentation.dto.PlayerRequest; +import java.time.LocalDateTime; +import java.time.Month; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -25,23 +46,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; -import java.time.LocalDateTime; -import java.time.Month; -import java.util.List; - -import static com.now.naaga.common.fixture.PositionFixture.*; -import static com.now.naaga.game.domain.EndType.ARRIVED; -import static com.now.naaga.game.domain.EndType.GIVE_UP; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; -import static com.now.naaga.gameresult.domain.ResultType.FAIL; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; -import static com.now.naaga.game.exception.GameExceptionType.*; -import static com.now.naaga.gameresult.exception.GameResultExceptionType.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.junit.jupiter.api.Assertions.assertThrows; - @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @Sql("/truncate.sql") @SpringBootTest diff --git a/backend/src/test/java/com/now/naaga/game/application/HintServiceTest.java b/backend/src/test/java/com/now/naaga/game/application/HintServiceTest.java index f511504c8..351844d2e 100644 --- a/backend/src/test/java/com/now/naaga/game/application/HintServiceTest.java +++ b/backend/src/test/java/com/now/naaga/game/application/HintServiceTest.java @@ -17,7 +17,6 @@ import com.now.naaga.game.domain.Game; import com.now.naaga.game.domain.Hint; import com.now.naaga.game.exception.GameException; -import com.now.naaga.member.domain.Member; import com.now.naaga.place.domain.Place; import java.time.LocalDateTime; import org.junit.jupiter.api.DisplayNameGeneration; diff --git a/backend/src/test/java/com/now/naaga/game/domain/GameScorePolicyTest.java b/backend/src/test/java/com/now/naaga/game/domain/GameScorePolicyTest.java deleted file mode 100644 index b1d80943f..000000000 --- a/backend/src/test/java/com/now/naaga/game/domain/GameScorePolicyTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.now.naaga.game.domain; - -import com.now.naaga.game.domain.gamescore.GameScorePolicy; -import com.now.naaga.game.domain.gamescore.SuccessGameScorePolicy; -import com.now.naaga.player.domain.Player; -import com.now.naaga.score.domain.Score; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; - -import java.time.LocalDateTime; -import java.util.List; - -import static com.now.naaga.common.fixture.PlaceFixture.PLACE; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.*; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static org.assertj.core.api.Assertions.assertThat; - -@ActiveProfiles("test") -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class GameScorePolicyTest { - - private final Player player = PLAYER(); - private final GameScorePolicy gameScorer = new SuccessGameScorePolicy(); - - @Test - void ๋‹ค๋ฅธ_์กฐ๊ฑด์ด_๊ฐ™๊ณ _๊ฑฐ๋ฆฌ๊ฐ€_๋ฉ€์ˆ˜๋ก_์ ์ˆ˜๊ฐ€_๋†’๋‹ค() { - //given - List hints = List.of(new Hint(), new Hint()); - LocalDateTime startTime = LocalDateTime.of(2023, 7, 31, 12, 00, 30); - LocalDateTime endTime = LocalDateTime.of(2023, 7, 31, 14, 00, 30); - Game gameHasDestinationInFurtherArea = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, 3, hints, startTime, endTime); - Game gameHasDestinationInNearArea = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ, 3, hints, startTime, endTime); - - //when - Score furtherAreaScore = gameScorer.calculate(gameHasDestinationInFurtherArea); - Score nearerAreaScore = gameScorer.calculate(gameHasDestinationInNearArea); - - //then - assertThat(furtherAreaScore.isHigherThan(nearerAreaScore)) - .isTrue(); - } - - @Test - void ๋‹ค๋ฅธ_์กฐ๊ฑด์ด_๊ฐ™๊ณ _์†Œ์š”_์‹œ๊ฐ„์ด_์งง์„์ˆ˜๋ก_์ ์ˆ˜๊ฐ€_๋†’๋‹ค() { - //given - List hints = List.of(new Hint(), new Hint()); - LocalDateTime startTime = LocalDateTime.of(2023, 7, 31, 12, 00, 30); - LocalDateTime slowerEndTime = LocalDateTime.of(2023, 7, 31, 15, 00, 30); - LocalDateTime fasterEndTime = LocalDateTime.of(2023, 7, 31, 13, 00, 30); - Game slowerGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, 3, hints, startTime, slowerEndTime); - Game fasterGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, 3, hints, startTime, fasterEndTime); - - //when - Score slowerGameScore = gameScorer.calculate(slowerGame); - Score fasterGameScore = gameScorer.calculate(fasterGame); - //then - assertThat(fasterGameScore.isHigherThan(slowerGameScore)) - .isTrue(); - } - - @Test - void ๋‹ค๋ฅธ_์กฐ๊ฑด์ด_๊ฐ™๊ณ _ํžŒํŠธ_์‚ฌ์šฉ_๊ฐœ์ˆ˜๊ฐ€_์ ์„์ˆ˜๋ก_์ ์ˆ˜๊ฐ€_๋†’๋‹ค() { - //given - List threeHints = List.of(new Hint(), new Hint(), new Hint()); - List oneHints = List.of(new Hint()); - LocalDateTime startTime = LocalDateTime.of(2023, 7, 31, 12, 00, 30); - LocalDateTime endTime = LocalDateTime.of(2023, 7, 31, 15, 00, 30); - Game threeHintsGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, 3, threeHints, startTime, endTime); - Game oneHintsGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, 3, oneHints, startTime, endTime); - - //when - Score threeHintsGameScore = gameScorer.calculate(threeHintsGame); - Score oneHintsGameScore = gameScorer.calculate(oneHintsGame); - //then - assertThat(oneHintsGameScore.isHigherThan(threeHintsGameScore)) - .isTrue(); - } - - @Test - void ๋‹ค๋ฅธ_์กฐ๊ฑด์ด_๊ฐ™๊ณ _์ž”์—ฌ_์‹œ๋„_ํšŸ์ˆ˜๊ฐ€_๋งŽ์„์ˆ˜๋ก_์ ์ˆ˜๊ฐ€_๋†’๋‹ค() { - //given - int threeRemainingAttempts = 3; - int oneRemainingAttempts = 1; - List hints = List.of(new Hint(), new Hint()); - LocalDateTime startTime = LocalDateTime.of(2023, 7, 31, 12, 00, 30); - LocalDateTime endTime = LocalDateTime.of(2023, 7, 31, 15, 00, 30); - Game threeRemainingAttemptsGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, threeRemainingAttempts, hints, startTime, endTime); - Game oneRemainingAttemptsGame = new Game(DONE, player, PLACE(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ, player), ๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ, oneRemainingAttempts, hints, startTime, endTime); - - //when - Score threeRemainingAttemptsGameScore = gameScorer.calculate(threeRemainingAttemptsGame); - Score oneRemainingAttemptsGameScore = gameScorer.calculate(oneRemainingAttemptsGame); - //then - assertThat(threeRemainingAttemptsGameScore.isHigherThan(oneRemainingAttemptsGameScore)) - .isTrue(); - } -} diff --git a/backend/src/test/java/com/now/naaga/game/domain/GameTest.java b/backend/src/test/java/com/now/naaga/game/domain/GameTest.java index e89146dc5..38d15db49 100644 --- a/backend/src/test/java/com/now/naaga/game/domain/GameTest.java +++ b/backend/src/test/java/com/now/naaga/game/domain/GameTest.java @@ -1,10 +1,25 @@ package com.now.naaga.game.domain; +import static com.now.naaga.common.fixture.PlaceFixture.PLACE; +import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; +import static com.now.naaga.common.fixture.PositionFixture.GS25_๋ฐฉ์ด๋„๊ณก์ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_๊ทผ์ฒ˜_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.game.domain.EndType.ARRIVED; +import static com.now.naaga.game.domain.EndType.GIVE_UP; +import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.now.naaga.game.exception.GameException; import com.now.naaga.game.exception.GameNotFinishedException; import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; import com.now.naaga.player.domain.Player; +import java.time.LocalDateTime; +import java.util.ArrayList; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -13,20 +28,6 @@ import org.junit.jupiter.params.provider.ValueSource; import org.springframework.test.context.ActiveProfiles; -import java.time.LocalDateTime; -import java.util.ArrayList; - -import static com.now.naaga.common.fixture.PlaceFixture.PLACE; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.*; -import static com.now.naaga.game.domain.EndType.ARRIVED; -import static com.now.naaga.game.domain.EndType.GIVE_UP; -import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; -import static com.now.naaga.game.domain.Game.MAX_HINT_COUNT; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/game/domain/ResultScorePolicyTest.java b/backend/src/test/java/com/now/naaga/game/domain/ResultScorePolicyTest.java index beacce10f..5291cad0c 100644 --- a/backend/src/test/java/com/now/naaga/game/domain/ResultScorePolicyTest.java +++ b/backend/src/test/java/com/now/naaga/game/domain/ResultScorePolicyTest.java @@ -1,23 +1,24 @@ package com.now.naaga.game.domain; +import static com.now.naaga.common.fixture.PlaceFixture.PLACE; +import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; +import static com.now.naaga.common.fixture.PositionFixture.๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static org.assertj.core.api.Assertions.assertThat; + import com.now.naaga.gameresult.domain.gamescore.ResultScorePolicy; import com.now.naaga.gameresult.domain.gamescore.SuccessResultScorePolicy; import com.now.naaga.player.domain.Player; import com.now.naaga.score.domain.Score; +import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; -import java.time.LocalDateTime; -import java.util.List; - -import static com.now.naaga.common.fixture.PlaceFixture.PLACE; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.*; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static org.assertj.core.api.Assertions.assertThat; - @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/game/domain/StatisticTest.java b/backend/src/test/java/com/now/naaga/game/domain/StatisticTest.java index 3865c809c..1f1ab2848 100644 --- a/backend/src/test/java/com/now/naaga/game/domain/StatisticTest.java +++ b/backend/src/test/java/com/now/naaga/game/domain/StatisticTest.java @@ -1,28 +1,27 @@ package com.now.naaga.game.domain; +import static com.now.naaga.common.fixture.PlaceFixture.PLACE; +import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; +import static com.now.naaga.common.fixture.PositionFixture.๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.place.domain.Place; import com.now.naaga.place.domain.Position; import com.now.naaga.player.domain.Player; import com.now.naaga.score.domain.Score; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; - import java.time.Duration; import java.time.LocalDateTime; import java.time.Month; import java.util.ArrayList; import java.util.List; - -import static com.now.naaga.common.fixture.PlaceFixture.PLACE; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.๋˜ํ‚จ๋„๋„ˆ์ธ _์˜ฌ๋ฆผํ”ฝ๊ณต์›์ _์ขŒํ‘œ; -import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; -import static org.assertj.core.api.SoftAssertions.assertSoftly; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; @ActiveProfiles("test") @SuppressWarnings("NonAsciiCharacters") diff --git a/backend/src/test/java/com/now/naaga/game/domain/gamescore/GameScoreCalculatorTest.java b/backend/src/test/java/com/now/naaga/game/domain/gamescore/GameScoreCalculatorTest.java deleted file mode 100644 index 1c634c54f..000000000 --- a/backend/src/test/java/com/now/naaga/game/domain/gamescore/GameScoreCalculatorTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.now.naaga.game.domain.gamescore; - -import static com.now.naaga.common.fixture.PlaceFixture.PLACE; -import static com.now.naaga.common.fixture.PlayerFixture.PLAYER; -import static com.now.naaga.common.fixture.PositionFixture.์„œ์šธ_์ขŒํ‘œ; -import static com.now.naaga.game.domain.ResultType.FAIL; -import static com.now.naaga.game.domain.ResultType.SUCCESS; -import static org.assertj.core.api.Assertions.assertThat; - -import com.now.naaga.game.domain.Game; -import com.now.naaga.game.domain.GameStatus; -import com.now.naaga.game.domain.ResultType; -import com.now.naaga.score.domain.Score; -import java.time.LocalDateTime; -import java.util.Collections; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SpringBootTest -class GameScoreCalculatorTest { - - @Autowired - GameScoreCalculator gameScoreCalculator; - - @Test - void ๊ฒŒ์ž„๊ฒฐ๊ณผ๊ฐ€_์„ฑ๊ณต์ผ_๋•Œ_์ ์ˆ˜๋ฅผ_์–ป๋Š”๋‹ค() { - //given - ResultType resultType = SUCCESS; - Game game = new Game(GameStatus.DONE, PLAYER(), PLACE(), ์„œ์šธ_์ขŒํ‘œ, 3, - Collections.emptyList(), - LocalDateTime.now().minusHours(1), LocalDateTime.now()); - - //when - final Score actual = gameScoreCalculator.calculate(game, resultType); - - //then - assertThat(actual.getValue()).isGreaterThan(0); - } - - @Test - void ๊ฒŒ์ž„๊ฒฐ๊ณผ๊ฐ€_์‹คํŒจ์ผ_๋•Œ_0์ ์„_์–ป๋Š”๋‹ค() { - //given - ResultType resultType = FAIL; - Game game = new Game(GameStatus.DONE, PLAYER(), PLACE(), ์„œ์šธ_์ขŒํ‘œ, 3, - Collections.emptyList(), - LocalDateTime.now().minusHours(1), LocalDateTime.now()); - - //when - final Score actual = gameScoreCalculator.calculate(game, resultType); - - //then - assertThat(actual.getValue()).isZero(); - } -} diff --git a/backend/src/test/java/com/now/naaga/game/presentation/GameControllerTest.java b/backend/src/test/java/com/now/naaga/game/presentation/GameControllerTest.java index 69744849a..5a1dcd656 100644 --- a/backend/src/test/java/com/now/naaga/game/presentation/GameControllerTest.java +++ b/backend/src/test/java/com/now/naaga/game/presentation/GameControllerTest.java @@ -1,13 +1,46 @@ package com.now.naaga.game.presentation; +import static com.now.naaga.auth.exception.AuthExceptionType.NOT_EXIST_HEADER; +import static com.now.naaga.common.fixture.PositionFixture.์„œ์šธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์—ญ์‚ผ์—ญ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_๊ทผ์ฒ˜_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ œ์ฃผ_์ขŒํ‘œ; +import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.ALREADY_DONE; +import static com.now.naaga.game.exception.GameExceptionType.ALREADY_IN_PROGRESS; +import static com.now.naaga.game.exception.GameExceptionType.CAN_NOT_FIND_PLACE; +import static com.now.naaga.game.exception.GameExceptionType.HINT_NOT_EXIST_IN_GAME; +import static com.now.naaga.game.exception.GameExceptionType.INACCESSIBLE_AUTHENTICATION; +import static com.now.naaga.game.exception.GameExceptionType.NOT_ARRIVED; +import static com.now.naaga.game.exception.GameExceptionType.NOT_EXIST; +import static com.now.naaga.gameresult.domain.ResultType.FAIL; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; import com.now.naaga.common.CommonControllerTest; -import com.now.naaga.common.builder.*; +import com.now.naaga.common.builder.GameBuilder; +import com.now.naaga.common.builder.GameResultBuilder; +import com.now.naaga.common.builder.MemberBuilder; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlayerBuilder; import com.now.naaga.common.exception.ExceptionResponse; -import com.now.naaga.game.domain.*; -import com.now.naaga.game.presentation.dto.*; +import com.now.naaga.game.domain.Direction; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.domain.GameRecord; +import com.now.naaga.game.domain.Hint; +import com.now.naaga.game.presentation.dto.CoordinateRequest; +import com.now.naaga.game.presentation.dto.EndGameRequest; +import com.now.naaga.game.presentation.dto.GameResponse; +import com.now.naaga.game.presentation.dto.GameResultResponse; +import com.now.naaga.game.presentation.dto.GameStatusResponse; +import com.now.naaga.game.presentation.dto.HintResponse; import com.now.naaga.game.repository.GameRepository; import com.now.naaga.game.repository.HintRepository; import com.now.naaga.gameresult.domain.GameResult; @@ -23,6 +56,9 @@ import io.restassured.http.ContentType; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; @@ -31,20 +67,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -import static com.now.naaga.auth.exception.AuthExceptionType.NOT_EXIST_HEADER; -import static com.now.naaga.common.fixture.PositionFixture.*; -import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; -import static com.now.naaga.gameresult.domain.ResultType.FAIL; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; -import static com.now.naaga.game.exception.GameExceptionType.*; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) class GameControllerTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/game/presentation/StatisticControllerTest.java b/backend/src/test/java/com/now/naaga/game/presentation/StatisticControllerTest.java index 16d798e76..53808d3ea 100644 --- a/backend/src/test/java/com/now/naaga/game/presentation/StatisticControllerTest.java +++ b/backend/src/test/java/com/now/naaga/game/presentation/StatisticControllerTest.java @@ -1,5 +1,9 @@ package com.now.naaga.game.presentation; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; @@ -27,10 +31,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; -import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class StatisticControllerTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/gameresult/application/GameResultServiceTest.java b/backend/src/test/java/com/now/naaga/gameresult/application/GameResultServiceTest.java index 5842116a9..cc7aeb026 100644 --- a/backend/src/test/java/com/now/naaga/gameresult/application/GameResultServiceTest.java +++ b/backend/src/test/java/com/now/naaga/gameresult/application/GameResultServiceTest.java @@ -1,5 +1,11 @@ package com.now.naaga.gameresult.application; +import static com.now.naaga.game.domain.EndType.ARRIVED; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.now.naaga.common.builder.GameBuilder; import com.now.naaga.common.builder.GameResultBuilder; import com.now.naaga.common.builder.PlaceBuilder; @@ -8,11 +14,11 @@ import com.now.naaga.game.domain.Game; import com.now.naaga.game.exception.GameException; import com.now.naaga.gameresult.domain.GameResult; -import com.now.naaga.gameresult.exception.GameResultException; import com.now.naaga.gameresult.repository.GameResultRepository; import com.now.naaga.place.domain.Position; import com.now.naaga.player.domain.Player; import com.now.naaga.score.domain.Score; +import java.time.LocalDateTime; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -23,14 +29,6 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; - -import static com.now.naaga.game.domain.EndType.ARRIVED; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @Sql("/truncate.sql") @ActiveProfiles("test") diff --git a/backend/src/test/java/com/now/naaga/gameresult/repository/GameResultRepositoryTest.java b/backend/src/test/java/com/now/naaga/gameresult/repository/GameResultRepositoryTest.java index a0297933a..c4b42d429 100644 --- a/backend/src/test/java/com/now/naaga/gameresult/repository/GameResultRepositoryTest.java +++ b/backend/src/test/java/com/now/naaga/gameresult/repository/GameResultRepositoryTest.java @@ -1,5 +1,11 @@ package com.now.naaga.gameresult.repository; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + import com.now.naaga.common.builder.GameBuilder; import com.now.naaga.common.builder.GameResultBuilder; import com.now.naaga.common.builder.PlaceBuilder; @@ -8,6 +14,7 @@ import com.now.naaga.gameresult.domain.GameResult; import com.now.naaga.place.domain.Place; import com.now.naaga.player.domain.Player; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -15,14 +22,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; -import java.util.List; - -import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; -import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; -import static com.now.naaga.game.domain.GameStatus.DONE; -import static com.now.naaga.gameresult.domain.ResultType.SUCCESS; -import static org.assertj.core.api.Assertions.assertThat; - @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @Sql("/truncate.sql") @SpringBootTest diff --git a/backend/src/test/java/com/now/naaga/letter/application/LetterServiceTest.java b/backend/src/test/java/com/now/naaga/letter/application/LetterServiceTest.java new file mode 100644 index 000000000..27bf88d7f --- /dev/null +++ b/backend/src/test/java/com/now/naaga/letter/application/LetterServiceTest.java @@ -0,0 +1,216 @@ +package com.now.naaga.letter.application; + +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.letter.exception.LetterExceptionType.NO_EXIST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.now.naaga.common.builder.GameBuilder; +import com.now.naaga.common.builder.LetterBuilder; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.common.exception.BaseExceptionType; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.exception.GameException; +import com.now.naaga.game.exception.GameExceptionType; +import com.now.naaga.letter.application.dto.CreateLetterCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.exception.LetterException; +import com.now.naaga.letter.presentation.dto.FindNearByLetterCommand; +import com.now.naaga.letter.presentation.dto.LetterReadCommand; +import com.now.naaga.letter.repository.letterlog.ReadLetterLogRepository; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.Position; +import com.now.naaga.player.domain.Player; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@ActiveProfiles("test") +@Sql("/truncate.sql") +@SpringBootTest +class LetterServiceTest { + + @Autowired + private LetterService letterService; + + @Autowired + private PlayerBuilder playerBuilder; + + @Autowired + private GameBuilder gameBuilder; + + @Autowired + private ReadLetterLogRepository readLetterLogRepository; + + @Autowired + private PlaceBuilder placeBuilder; + + @Autowired + private LetterBuilder letterBuilder; + + @Transactional + @Test + void ์ชฝ์ง€๋ฅผ_์ •์ƒ์ ์œผ๋กœ_์ƒ์„ฑํ•˜๊ณ _๊ฒŒ์ž„์ค‘_๋“ฑ๋กํ•œ_์ชฝ์ง€๋ฅผ_๊ธฐ๋ก์œผ๋กœ_๋‚จ๊ธด๋‹ค() { + //given + final Player savedPlayer = playerBuilder.init() + .build(); + final String message = "๋‚ ์”จ๊ฐ€ ์„ ์„ ํ•ด์š”."; + final Position position = ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; + gameBuilder.init() + .player(savedPlayer) + .build(); + + //when + final CreateLetterCommand createLetterCommand = new CreateLetterCommand( + savedPlayer.getId(), + message, + position); + final Letter expected = new Letter(savedPlayer, position, message); + final Letter actual = letterService.writeLetter(createLetterCommand); + + //then + assertSoftly(softAssertions -> { + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expected); + }); + } + + @Test + void ์ชฝ์ง€๋ฅผ_๋“ฑ๋กํ• _๋•Œ_ํ˜„์žฌ_์ง„ํ–‰_์ค‘์ธ_๊ฒŒ์ž„์ด_์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + //given + final Player savedPlayer = playerBuilder.init() + .build(); + final String message = "๋‚ ์”จ๊ฐ€ ์„ ์„ ํ•ด์š”."; + final Position position = ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; + final CreateLetterCommand createLetterCommand = new CreateLetterCommand( + savedPlayer.getId(), + message, + position); + + //when&then + assertThatThrownBy(() -> letterService.writeLetter(createLetterCommand)) + .isInstanceOf(GameException.class); + Assertions.assertAll(() -> { + final BaseExceptionType baseExceptionType = assertThrows(GameException.class, () -> letterService.writeLetter(createLetterCommand)) + .exceptionType(); + assertThat(baseExceptionType).isEqualTo(GameExceptionType.NOT_EXIST_IN_PROGRESS); + }); + } + + @Test + void ํ”Œ๋ ˆ์ด์–ด์ฃผ๋ณ€_100m_๋‚ด๋กœ์˜_์ชฝ์ง€๋งŒ_๋ชจ๋‘_์กฐํšŒํ•œ๋‹ค() { + // given + final Player registerPlayer = playerBuilder.init() + .build(); + + final Letter letter1 = letterBuilder.init() + .registeredPlayer(registerPlayer) + .build(); + + final Letter letter2 = letterBuilder.init() + .registeredPlayer(registerPlayer) + .position(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ) + .build(); + + // when + final List actual = letterService.findNearByLetters(new FindNearByLetterCommand(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ)); + + // then + assertSoftly(softAssertions -> { + softAssertions.assertThat(actual.size()).isEqualTo(1); + softAssertions.assertThat(actual.get(0).getId()).isEqualTo(letter1.getId()); + }); + } + + @Test + void ํ”Œ๋ ˆ์ด์–ด์ฃผ๋ณ€_100m_๋‚ด๋กœ์˜_์ชฝ์ง€๊ฐ€_์—†์œผ๋ฉด_๋นˆ๋ฆฌ์ŠคํŠธ๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player registerPlayer = playerBuilder.init() + .build(); + + final Letter letter = letterBuilder.init() + .registeredPlayer(registerPlayer) + .position(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ) + .build(); + + // when + final List actual = letterService.findNearByLetters(new FindNearByLetterCommand(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ)); + + // then + assertThat(actual).isEmpty(); + } + + @Test + void ์ชฝ์ง€๋ฅผ_๋‹จ๊ฑด์กฐํšŒ_ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ) + .build(); + + final Player letterRegister = playerBuilder.init() + .build(); + + final Letter letter = letterBuilder.init() + .registeredPlayer(letterRegister) + .build(); + + // when + final Letter actual = letterService.findLetter(new LetterReadCommand(player.getId(), letter.getId())); + + // then + assertThat(actual.getId()).isEqualTo(letter.getId()); + } + + @Test + void ์ชฝ์ง€๊ฐ€_์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ) + .build(); + + final Player letterRegister = playerBuilder.init() + .build(); + + final Letter letter = letterBuilder.init() + .registeredPlayer(letterRegister) + .build(); + + // then + final LetterException letterException = assertThrows( + LetterException.class, () -> letterService.findLetter(new LetterReadCommand(player.getId(), letter.getId() + 1))); + assertThat(letterException.exceptionType()).isEqualTo(NO_EXIST); + } +} diff --git a/backend/src/test/java/com/now/naaga/letter/application/letterlog/ReadLetterLogServiceTest.java b/backend/src/test/java/com/now/naaga/letter/application/letterlog/ReadLetterLogServiceTest.java new file mode 100644 index 000000000..4ddc644ef --- /dev/null +++ b/backend/src/test/java/com/now/naaga/letter/application/letterlog/ReadLetterLogServiceTest.java @@ -0,0 +1,119 @@ +package com.now.naaga.letter.application.letterlog; + +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.game.exception.GameExceptionType.NOT_EXIST_IN_PROGRESS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.now.naaga.common.builder.GameBuilder; +import com.now.naaga.common.builder.LetterBuilder; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.domain.GameStatus; +import com.now.naaga.game.exception.GameException; +import com.now.naaga.letter.application.letterlog.dto.LetterLogCreateCommand; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import com.now.naaga.letter.repository.letterlog.ReadLetterLogRepository; +import com.now.naaga.place.domain.Place; +import com.now.naaga.player.domain.Player; +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@Sql("/truncate.sql") +@SpringBootTest +class ReadLetterLogServiceTest { + + @Autowired + private ReadLetterLogService readLetterLogService; + + @Autowired + private ReadLetterLogRepository readLetterLogRepository; + + @Autowired + private PlayerBuilder playerBuilder; + + @Autowired + private PlaceBuilder placeBuilder; + + @Autowired + private GameBuilder gameBuilder; + + @Autowired + private LetterBuilder letterBuilder; + + @Test + void ์ฝ์€์ชฝ์ง€๋กœ๊ทธ์—_๋ฐ์ดํ„ฐ๋ฅผ_์ €์žฅํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ) + .build(); + + final Player letterRegister = playerBuilder.init() + .build(); + + final Letter letter = letterBuilder.init() + .registeredPlayer(letterRegister) + .build(); + + // when + readLetterLogService.log(new LetterLogCreateCommand(player.getId(), letter)); + + // then + final List actual = readLetterLogRepository.findAll(); + final long expected = actual.get(0).getLetter().getId(); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(1); + softAssertions.assertThat(expected).isEqualTo(letter.getId()); + }); + } + + @Test + void ์ฝ์€์ชฝ์ง€๋กœ๊ทธ์—_๋ฐ์ดํ„ฐ์ €์žฅ์‹œ_์ง„ํ–‰์ค‘์ธ_๊ฒŒ์ž„์ด์—†์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given && when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ) + .gameStatus(GameStatus.DONE) + .build(); + + final Player letterRegister = playerBuilder.init() + .build(); + + final Letter letter = letterBuilder.init() + .registeredPlayer(letterRegister) + .build(); + + //then + final GameException gameException = assertThrows( + GameException.class, () -> readLetterLogService.log(new LetterLogCreateCommand(player.getId(), letter))); + assertThat(gameException.exceptionType()).isEqualTo(NOT_EXIST_IN_PROGRESS); + } +} diff --git a/backend/src/test/java/com/now/naaga/letter/presentation/LetterControllerTest.java b/backend/src/test/java/com/now/naaga/letter/presentation/LetterControllerTest.java new file mode 100644 index 000000000..10326043a --- /dev/null +++ b/backend/src/test/java/com/now/naaga/letter/presentation/LetterControllerTest.java @@ -0,0 +1,353 @@ +package com.now.naaga.letter.presentation; + +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ; +import static com.now.naaga.game.domain.GameStatus.DONE; +import static com.now.naaga.game.exception.GameExceptionType.NOT_EXIST_IN_PROGRESS; +import static com.now.naaga.player.exception.PlayerExceptionType.PLAYER_NOT_FOUND; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import com.now.naaga.auth.domain.AuthToken; +import com.now.naaga.auth.infrastructure.AuthType; +import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; +import com.now.naaga.common.CommonControllerTest; +import com.now.naaga.common.builder.GameBuilder; +import com.now.naaga.common.builder.LetterBuilder; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.common.exception.ExceptionResponse; +import com.now.naaga.game.domain.Game; +import com.now.naaga.game.presentation.dto.CoordinateResponse; +import com.now.naaga.game.repository.GameRepository; +import com.now.naaga.letter.application.LetterService; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.presentation.dto.LetterRequest; +import com.now.naaga.letter.presentation.dto.LetterResponse; +import com.now.naaga.letter.presentation.dto.NearByLetterResponse; +import com.now.naaga.letter.repository.LetterRepository; +import com.now.naaga.member.domain.Member; +import com.now.naaga.place.domain.Place; +import com.now.naaga.player.domain.Player; +import com.now.naaga.player.presentation.dto.PlayerResponse; +import io.restassured.RestAssured; +import io.restassured.common.mapper.TypeRef; +import io.restassured.http.ContentType; +import io.restassured.parsing.Parser; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LetterControllerTest extends CommonControllerTest { + + @Autowired + private AuthTokenGenerator authTokenGenerator; + + @Autowired + private GameRepository gameRepository; + + @Autowired + private LetterService letterService; + + @Autowired + private LetterRepository letterRepository; + + @Autowired + private GameBuilder gameBuilder; + + @Autowired + private LetterBuilder letterBuilder; + + @Autowired + private PlaceBuilder placeBuilder; + + @Autowired + private PlayerBuilder playerBuilder; + + @BeforeEach + protected void setUp() { + super.setUp(); + } + + @Test + void ์ฃผ๋ณ€_์ชฝ์ง€๋ฅผ_๋ชจ๋‘_์กฐํšŒํ•œ๋‹ค() { + // given + final Place destination = placeBuilder.init() + .build(); + final Player player = playerBuilder.init() + .build(); + final Letter letter1 = letterBuilder.init() + .build(); + final Letter letter2 = letterBuilder.init() + .position(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _110๋ฏธํ„ฐ_์•ž_์ขŒํ‘œ) + .build(); + + final AuthToken generate = authTokenGenerator.generate(destination.getRegisteredPlayer().getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + // when + final ExtractableResponse extract = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + accessToken) + .param("latitude", ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ.getLatitude()) + .param("longitude", ์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ.getLongitude()) + .when() + .get("/letters/nearby") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + + // then + final List actual = extract.as(new TypeRef<>() { + }); + final int statusCode = extract.statusCode(); + final List expected = List.of(NearByLetterResponse.from(letter1)); + + assertSoftly(softly -> { + softly.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softly.assertThat(actual) + .hasSize(1) + .usingRecursiveComparison() + .isEqualTo(expected); + }); + } + + @Test + void ์ชฝ์ง€_์‹๋ณ„์ž๋กœ_์ชฝ์ง€๋ฅผ_์กฐํšŒํ•œ๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค์—ญ_๊ต๋ณด๋ฌธ๊ณ _์ขŒํ‘œ) + .build(); + + final Letter letter = letterBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .when() + .get("/letters/{letterId}", letter.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final LetterResponse actual = extract.as(LetterResponse.class); + final LetterResponse expected = LetterResponse.from(letter); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .ignoringFields("registerDate") + .isEqualTo(expected); + }); + } + + @Test + void ์ชฝ์ง€_์‹๋ณ„์ž๋กœ_์ชฝ์ง€๋ฅผ_์กฐํšŒ์‹œ_์ž”ํ–‰์ค‘_๊ฒŒ์ž„์ด_์—†์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Letter letter = letterBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .when() + .get("/letters/{letterId}", letter.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + + final ExceptionResponse expected = new ExceptionResponse(NOT_EXIST_IN_PROGRESS.errorCode(), NOT_EXIST_IN_PROGRESS.errorMessage()); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.NOT_FOUND.value()); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .ignoringFieldsOfTypes(LocalDateTime.class) + .isEqualTo(expected); + }); + } + + @Test + void ์ชฝ์ง€_๋“ฑ๋ก_์š”์ฒญ์‹œ_์ชฝ์ง€๋ฅผ_๋“ฑ๋กํ•œ_ํ”Œ๋ ˆ์ด์–ด๊ฐ€_์กด์žฌํ•˜๊ณ _๊ทธ_ํ”Œ๋ ˆ์ด์–ด๊ฐ€_์ง„ํ–‰์ค‘์ธ_๊ฒŒ์ž„์ด_์žˆ์œผ๋ฉด_์ •์ƒ์ ์œผ๋กœ_์ชฝ์ง€๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { + //given + final Player player = playerBuilder.init() + .build(); + final Game game = gameBuilder.init() + .player(player) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + String message = "๋‚ ์”จ๊ฐ€ ์„ ์„ ํ•˜๋„ค์š”"; + final LetterRequest letterRequest = new LetterRequest(message, + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLatitude().doubleValue(), + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLongitude().doubleValue()); + + //when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(ContentType.JSON) + .body(letterRequest) + .when() + .post("/letters") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + final String location = extract.header("Location"); + final Long letterId = getIdFromLocationHeader(extract); + final LetterResponse actual = extract.as(LetterResponse.class); + final LetterResponse expected = new LetterResponse( + letterId, + PlayerResponse.from(player), + CoordinateResponse.of(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ), + message, + null + ); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); + softAssertions.assertThat(location).isNotEqualTo("/letters" + letterId); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expected); + } + ); + } + + @Test + void ์ชฝ์ง€_๋“ฑ๋ก_์š”์ฒญ์‹œ_์ชฝ์ง€๋ฅผ_๋“ฑ๋กํ•œ_ํ”Œ๋ ˆ์ด์–ด๊ฐ€_์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + //given + final AuthToken generate = authTokenGenerator.generate(new Member(1L, "email", false), + 1L, + AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + String message = "๋‚ ์”จ๊ฐ€ ์„ ์„ ํ•˜๋„ค์š”"; + final LetterRequest letterRequest = new LetterRequest(message, + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLatitude().doubleValue(), + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLongitude().doubleValue()); + + //when + RestAssured.defaultParser = Parser.JSON; + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(ContentType.JSON) + .body(letterRequest) + .when() + .post("/letters") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + final ExceptionResponse expected = new ExceptionResponse( + PLAYER_NOT_FOUND.errorCode(), + PLAYER_NOT_FOUND.errorMessage() + ); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.NOT_FOUND.value()); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + ); + } + + @Test + void ์ชฝ์ง€_๋“ฑ๋ก_์š”์ฒญ์‹œ_์ชฝ์ง€๋ฅผ_๋“ฑ๋กํ•œ_์ง„ํ–‰์ค‘์ธ_๊ฒŒ์ž„์ด_์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + //given + final Player player = playerBuilder.init() + .build(); + final Game finishedGame = gameBuilder.init() + .player(player) + .gameStatus(DONE) + .build(); + final AuthToken generate = authTokenGenerator.generate(player.getMember(), + 1L, + AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + String message = "๋‚ ์”จ๊ฐ€ ์„ ์„ ํ•˜๋„ค์š”"; + final LetterRequest letterRequest = new LetterRequest(message, + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLatitude().doubleValue(), + ์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ.getLongitude().doubleValue()); + + //when + RestAssured.defaultParser = Parser.JSON; + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(ContentType.JSON) + .body(letterRequest) + .when() + .post("/letters") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + final ExceptionResponse expected = new ExceptionResponse( + NOT_EXIST_IN_PROGRESS.errorCode(), + NOT_EXIST_IN_PROGRESS.errorMessage() + ); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.NOT_FOUND.value()); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + ); + } +} diff --git a/backend/src/test/java/com/now/naaga/letter/presentation/LetterLogControllerTest.java b/backend/src/test/java/com/now/naaga/letter/presentation/LetterLogControllerTest.java new file mode 100644 index 000000000..350dfa22e --- /dev/null +++ b/backend/src/test/java/com/now/naaga/letter/presentation/LetterLogControllerTest.java @@ -0,0 +1,266 @@ +package com.now.naaga.letter.presentation; + +import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; +import static com.now.naaga.common.fixture.PositionFixture.GS25_๋ฐฉ์ด๋„๊ณก์ _์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์—ญ์‚ผ์—ญ_์ขŒํ‘œ; +import static com.now.naaga.common.fixture.PositionFixture.์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import com.now.naaga.auth.domain.AuthToken; +import com.now.naaga.auth.infrastructure.AuthType; +import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; +import com.now.naaga.common.CommonControllerTest; +import com.now.naaga.common.builder.GameBuilder; +import com.now.naaga.common.builder.LetterBuilder; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.common.builder.ReadLetterLogBuilder; +import com.now.naaga.common.builder.WriteLetterLogBuilder; +import com.now.naaga.common.exception.ExceptionResponse; +import com.now.naaga.game.domain.Game; +import com.now.naaga.letter.domain.Letter; +import com.now.naaga.letter.domain.letterlog.ReadLetterLog; +import com.now.naaga.letter.domain.letterlog.WriteLetterLog; +import com.now.naaga.letter.presentation.dto.LetterResponse; +import com.now.naaga.letter.repository.letterlog.WriteLetterLogRepository; +import com.now.naaga.place.domain.Place; +import com.now.naaga.player.domain.Player; +import io.restassured.RestAssured; +import io.restassured.common.mapper.TypeRef; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LetterLogControllerTest extends CommonControllerTest { + + @Autowired + private AuthTokenGenerator authTokenGenerator; + + @Autowired + private WriteLetterLogRepository writeLetterLogRepository; + + @Autowired + private GameBuilder gameBuilder; + + @Autowired + private PlaceBuilder placeBuilder; + + @Autowired + private PlayerBuilder playerBuilder; + + @Autowired + private LetterBuilder letterBuilder; + + @Autowired + private ReadLetterLogBuilder readLetterLogBuilder; + + @Autowired + private WriteLetterLogBuilder writeLetterLogBuilder; + + @BeforeEach + protected void setUp() { + super.setUp(); + } + + // ์ž˜๋ชป๋œ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค. + @Test + void ๊ฒŒ์ž„์•„์ด๋””์™€_ํšŒ์›์œผ๋กœ_์ฝ์€์ชฝ์ง€๋กœ๊ทธ๋ฅผ_๋ชจ๋‘_์กฐํšŒํ•œ๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game1 = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game2 = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์—ญ์‚ผ์—ญ_์ขŒํ‘œ) + .build(); + + final Letter letter = letterBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final ReadLetterLog readLetterLog1 = readLetterLogBuilder.init() + .game(game1) + .letter(letter) + .build(); + + final ReadLetterLog readLetterLog2 = readLetterLogBuilder.init() + .game(game2) + .letter(letter) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .param("gameId", game1.getId()) + .param("logType", "READ") + .when() + .get("/letterlogs") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + + // then + final List actual = extract.as(new TypeRef<>() { + }); + final int statusCode = extract.statusCode(); + final List expected = List.of(LetterResponse.from(letter)); + + assertSoftly(softly -> { + softly.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softly.assertThat(actual) + .hasSize(1) + .usingRecursiveComparison() + .ignoringFields("registerDate") + .isEqualTo(expected); + }); + } + + @Test + void + ๊ฒŒ์ž„์•„์ด๋””์™€_ํšŒ์›์œผ๋กœ_์ž‘์„ฑํ•œ_์ชฝ์ง€๋กœ๊ทธ๋ฅผ_๋ชจ๋‘_์กฐํšŒํ•œ๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Letter letter = letterBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final WriteLetterLog writeLetterLog1 = writeLetterLogBuilder.init() + .game(game) + .letter(letter) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .param("gameId", game.getId()) + .param("logType", "WRITE") + .when() + .get("/letterlogs") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + + // then + final List actual = extract.as(new TypeRef<>() { + }); + final int statusCode = extract.statusCode(); + final List expected = List.of(LetterResponse.from(letter)); + + assertSoftly(softly -> { + softly.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softly.assertThat(actual) + .hasSize(1) + .usingRecursiveComparison() + .ignoringFields("registerDate") + .isEqualTo(expected); + }); + } + + @Test + void ์ชฝ์ง€๋กœ๊ทธ_์กฐํšŒ์‹œ_์ž˜๋ชป๋œ_ํŒŒ๋ผ๋ฏธํ„ฐ๋Š”_์˜ˆ์™ธ๋ฅผ_๋˜์ง„๋‹ค() { + // given & when + final Player player = playerBuilder.init() + .build(); + + final Place destination = placeBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game1 = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Game game2 = gameBuilder.init() + .place(destination) + .player(player) + .startPosition(์—ญ์‚ผ์—ญ_์ขŒํ‘œ) + .build(); + + final Letter letter = letterBuilder.init() + .position(์ž ์‹ค_๋ฃจํ„ฐํšŒ๊ด€_์ •๋ฌธ_์ขŒํ‘œ) + .build(); + + final Letter letter2 = letterBuilder.init() + .position(GS25_๋ฐฉ์ด๋„๊ณก์ _์ขŒํ‘œ) + .build(); + + final WriteLetterLog writeLetterLog1 = writeLetterLogBuilder.init() + .game(game1) + .letter(letter) + .build(); + + final WriteLetterLog writeLetterLog2 = writeLetterLogBuilder.init() + .game(game2) + .letter(letter2) + .build(); + + final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .param("gameId", game1.getId()) + .param("logType", "์ž˜๋ชป๋œenum") + .when() + .get("/letterlogs") + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + final ExceptionResponse expected = new ExceptionResponse(INVALID_REQUEST_PARAMETERS.errorCode(), INVALID_REQUEST_PARAMETERS.errorMessage()); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.BAD_REQUEST.value()); + softAssertions.assertThat(actual) + .usingRecursiveComparison() + .ignoringExpectedNullFields() + .ignoringFieldsOfTypes(LocalDateTime.class) + .ignoringFields("registerDate") + .isEqualTo(expected); + } + ); + } +} diff --git a/backend/src/test/java/com/now/naaga/like/application/PlaceLikeServiceTest.java b/backend/src/test/java/com/now/naaga/like/application/PlaceLikeServiceTest.java new file mode 100644 index 000000000..f7266a748 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/like/application/PlaceLikeServiceTest.java @@ -0,0 +1,425 @@ +package com.now.naaga.like.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlaceLikeBuilder; +import com.now.naaga.common.builder.PlaceStatisticsBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.common.exception.BaseExceptionType; +import com.now.naaga.like.application.dto.ApplyLikeCommand; +import com.now.naaga.like.application.dto.CancelLikeCommand; +import com.now.naaga.like.application.dto.CheckMyPlaceLikeCommand; +import com.now.naaga.like.application.dto.CountPlaceLikeCommand; +import com.now.naaga.like.domain.MyPlaceLikeType; +import com.now.naaga.like.domain.PlaceLike; +import com.now.naaga.like.domain.PlaceLikeType; +import com.now.naaga.like.exception.PlaceLikeException; +import com.now.naaga.like.exception.PlaceLikeExceptionType; +import com.now.naaga.like.repository.PlaceLikeRepository; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.place.repository.PlaceStatisticsRepository; +import com.now.naaga.player.domain.Player; +import java.util.Optional; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +@Sql("/truncate.sql") +@ActiveProfiles("test") +@SpringBootTest +class PlaceLikeServiceTest { + + private final PlaceLikeService placeLikeService; + + private final PlaceBuilder placeBuilder; + + private final PlayerBuilder playerBuilder; + + private final PlaceLikeBuilder placeLikeBuilder; + + private final PlaceStatisticsBuilder placeStatisticsBuilder; + + private final PlaceLikeRepository placeLikeRepository; + + private final PlaceStatisticsRepository placeStatisticsRepository; + + @Autowired + public PlaceLikeServiceTest(final PlaceLikeService placeLikeService, + final PlaceBuilder placeBuilder, + final PlayerBuilder playerBuilder, + final PlaceLikeBuilder placeLikeBuilder, + final PlaceStatisticsBuilder placeStatisticsBuilder, + final PlaceLikeRepository placeLikeRepository, + final PlaceStatisticsRepository placeStatisticsRepository) { + this.placeLikeService = placeLikeService; + this.placeBuilder = placeBuilder; + this.playerBuilder = playerBuilder; + this.placeLikeBuilder = placeLikeBuilder; + this.placeStatisticsBuilder = placeStatisticsBuilder; + this.placeLikeRepository = placeLikeRepository; + this.placeStatisticsRepository = placeStatisticsRepository; + } + + @Transactional + @Test + void ๊ธฐ์กด์˜_์ข‹์•„์š”_ํƒ€์ž…๊ณผ_๋ฐ˜๋Œ€๋˜๋Š”_ํƒ€์ž…์„_๋“ฑ๋กํ•˜๋ฉด_๊ธฐ์กด์˜_์—”ํ‹ฐํ‹ฐ์—์„œ_์ข‹์•„์š”_ํƒ€์ž…๋งŒ_์ˆ˜์ •๋œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeStatisticsBuilder.init() + .place(place) + .build(); + + final PlaceLike placeLike = placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + // when + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.DISLIKE); + final PlaceLike actual = placeLikeService.applyLike(command); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).usingRecursiveComparison() + .ignoringFields("placeLikeType") + .isEqualTo(placeLike); + softAssertions.assertThat(actual.getType()).isEqualTo(PlaceLikeType.DISLIKE); + }); + } + + @Test + void ๊ธฐ์กด์˜_์ข‹์•„์š”๊ฐ€_์—†์„_๊ฒฝ์šฐ์—_ํƒ€์ž…์„_๋“ฑ๋กํ•˜๋ฉด_์ƒˆ๋กœ์šด_์ข‹์•„์š”_์—”ํ‹ฐํ‹ฐ๊ฐ€_์ƒ์„ฑ๋œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeStatisticsBuilder.init() + .place(place) + .build(); + + // when + final int beforeSize = placeLikeRepository.findAll().size(); + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.LIKE); + placeLikeService.applyLike(command); + final int afterSize = placeLikeRepository.findAll().size(); + + // then + assertThat(afterSize).isEqualTo(beforeSize + 1); + } + + @Test + void ์ข‹์•„์š”๋ฅผ_๋“ฑ๋กํ•˜๋ฉด_์ข‹์•„์š”_์ง‘๊ณ„_๊ฐ’์—_1_๋”ํ•ด์ง„๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + final long beforeLikeCount = 10L; + + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + + // when + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.LIKE); + placeLikeService.applyLike(command); + final PlaceStatistics placeStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + + // then + assertThat(placeStatistics.getLikeCount()).isEqualTo(beforeLikeCount + 1); + } + + @Test + void ๊ธฐ์กด์˜_์ข‹์•„์š”์—์„œ_์‹ซ์–ด์š”๋กœ_๋ณ€๊ฒฝํ•˜์—ฌ_๋“ฑ๋กํ•˜๋ฉด_์ข‹์•„์š”_์ง‘๊ณ„_๊ฐ’์„_1_๋บ€๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + final long beforeLikeCount = 10L; + + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + + placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + // when + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.DISLIKE); + placeLikeService.applyLike(command); + final PlaceStatistics placeStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + + // then + assertThat(placeStatistics.getLikeCount()).isEqualTo(beforeLikeCount - 1); + } + + @Test + void ์ƒˆ๋กญ๊ฒŒ_์‹ซ์–ด์š”๋ฅผ_๋“ฑ๋กํ–ˆ์„_๋•Œ_์ข‹์•„์š”_์ง‘๊ณ„_์ˆ˜๋Š”_๋ณ€ํ•˜์ง€_์•Š๋Š”๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + final long beforeLikeCount = 10L; + + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + + // when + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.DISLIKE); + placeLikeService.applyLike(command); + + // then + final PlaceStatistics placeStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + assertThat(placeStatistics.getLikeCount()).isEqualTo(beforeLikeCount); + } + + @Test + void ๊ธฐ์กด์˜_์ข‹์•„์š”_ํƒ€์ž…๊ณผ_๊ฐ™์€_ํƒ€์ž…์œผ๋กœ_์ข‹์•„์š”_๋“ฑ๋ก์„_์š”์ฒญํ•˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeStatisticsBuilder.init() + .place(place) + .build(); + + placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + // when + final ApplyLikeCommand command = new ApplyLikeCommand(player.getId(), + place.getId(), + PlaceLikeType.LIKE); + final BaseExceptionType baseExceptionType = assertThrows(PlaceLikeException.class, () -> + placeLikeService.applyLike(command) + ).exceptionType(); + + // then + assertThat(baseExceptionType).isEqualTo(PlaceLikeExceptionType.ALREADY_APPLIED_TYPE); + } + + @Test + void ์ข‹์•„์š”๋ฅผ_์‚ญ์ œํ•˜๊ณ _ํ†ต๊ณ„์—์„œ_์ข‹์•„์š”๋ฅผ_1๊ฐœ_๋บธ๋‹ค() { + // given + final Place place = placeBuilder.init() + .build(); + final Player player = playerBuilder.init() + .build(); + final PlaceLike placeLike = placeLikeBuilder.init() + .place(place) + .player(player) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + final long beforeLikeCount = 10L; + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + final CancelLikeCommand cancelLikeCommand = new CancelLikeCommand(player.getId(), place.getId()); + + // when + placeLikeService.cancelLike(cancelLikeCommand); + + // then + final Optional findPlaceLike = placeLikeRepository.findById(placeLike.getId()); + final PlaceStatistics findPlaceStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + assertSoftly(softAssertions -> { + softAssertions.assertThat(findPlaceStatistics.getLikeCount()).isEqualTo(beforeLikeCount - 1); + softAssertions.assertThat(findPlaceLike).isEmpty(); + }); + } + + @Test + void ์‹ซ์–ด์š”๋ฅผ_์‚ญ์ œํ•˜๋ฉด_ํ†ต๊ณ„๊ฐ€_์ค„์–ด๋“ค์ง€_์•Š๋Š”๋‹ค() { + // given + final Place place = placeBuilder.init() + .build(); + final Player player = playerBuilder.init() + .build(); + final PlaceLike placeLike = placeLikeBuilder.init() + .place(place) + .player(player) + .placeLikeType(PlaceLikeType.DISLIKE) + .build(); + final long beforeLikeCount = 10L; + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + final CancelLikeCommand cancelLikeCommand = new CancelLikeCommand(player.getId(), place.getId()); + + // when + placeLikeService.cancelLike(cancelLikeCommand); + + // then + final Optional findPlaceLike = placeLikeRepository.findById(placeLike.getId()); + final PlaceStatistics findPlaceStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + assertSoftly(softAssertions -> { + assertThat(findPlaceStatistics.getLikeCount()).isEqualTo(beforeLikeCount); + assertThat(findPlaceLike).isEmpty(); + }); + } + + @Test + void ์ข‹์•„์š”๊ฐ€_0๊ฐœ์ผ_๋•Œ_์ข‹์•„์š”๋ฅผ_์‚ญ์ œํ•˜๋ฉด_ํ†ต๊ณ„์—์„œ_์ข‹์•„์š”๋ฅผ_0๊ฐœ๋กœ_์œ ์ง€ํ•œ๋‹ค() { + // given + final Place place = placeBuilder.init() + .build(); + final Player player = playerBuilder.init() + .build(); + final PlaceLike placeLike = placeLikeBuilder.init() + .place(place) + .player(player) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + final long beforeLikeCount = 0L; + placeStatisticsBuilder.init() + .place(place) + .likeCount(beforeLikeCount) + .build(); + final CancelLikeCommand cancelLikeCommand = new CancelLikeCommand(player.getId(), place.getId()); + + // when + placeLikeService.cancelLike(cancelLikeCommand); + + // then + final Optional findPlaceLike = placeLikeRepository.findById(placeLike.getId()); + final PlaceStatistics findPlaceStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + assertSoftly(softAssertions -> { + softAssertions.assertThat(findPlaceStatistics.getLikeCount()).isEqualTo(beforeLikeCount); + softAssertions.assertThat(findPlaceLike).isEmpty(); + }); + } + + @Test + void ์ข‹์•„์š”๋ฅผ_์‚ญ์ œํ• _๋•Œ_์ข‹์•„์š”๊ฐ€_์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์•„๋ฌด_์ผ๋„_์ผ์–ด๋‚˜์ง€_์•Š๋Š”๋‹ค() { + // given + final Place place = placeBuilder.init() + .build(); + final Player player = playerBuilder.init() + .build(); + final CancelLikeCommand cancelLikeCommand = new CancelLikeCommand(player.getId(), place.getId()); + + // when & then + assertDoesNotThrow(() -> placeLikeService.cancelLike(cancelLikeCommand)); + } + + @Test + void ์žฅ์†Œ์—_๋Œ€ํ•œ_์ข‹์•„์š”_์ˆ˜๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + //given + final Long expected = 123L; + final PlaceStatistics placeStatistics = placeStatisticsBuilder.init() + .likeCount(expected) + .build(); + final Long placeId = placeStatistics.getPlace().getId(); + + // when + final CountPlaceLikeCommand countPlaceLikeCommand = new CountPlaceLikeCommand(placeId); + final Long actual = placeLikeService.countPlaceLike(countPlaceLikeCommand); + + // then + assertThat(actual).isEqualTo(expected); + } + + @Test + void ํŠน์ •_์žฅ์†Œ์—_๋Œ€ํ•œ_๋‚˜์˜_์ข‹์•„์š”_์—ฌ๋ถ€_์กฐํšŒ์‹œ_๋‚ด๊ฐ€_๋ˆ„๋ฅธ_์ข‹์•„์š”๊ฐ€_์กด์žฌํ•œ๋‹ค๋ฉด_ํ•ด๋‹น_์ข‹์•„์š”_ํƒ€์ž…์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + // when + final CheckMyPlaceLikeCommand command = new CheckMyPlaceLikeCommand(player.getId(), place.getId()); + final MyPlaceLikeType actual = placeLikeService.checkMyLike(command); + + // then + assertThat(actual).isEqualTo(MyPlaceLikeType.LIKE); + } + + @Test + void ํŠน์ •_์žฅ์†Œ์—_๋Œ€ํ•œ_๋‚˜์˜_์ข‹์•„์š”_์—ฌ๋ถ€_์กฐํšŒ์‹œ_๋‚ด๊ฐ€_๋ˆ„๋ฅธ_์ข‹์•„์š”๊ฐ€_์—†๋‹ค๋ฉด_NONE_ํƒ€์ž…์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place applied = placeBuilder.init() + .build(); + + final Place notApplied = placeBuilder.init() + .build(); + + placeLikeBuilder.init() + .player(player) + .place(applied) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + // when + final CheckMyPlaceLikeCommand command = new CheckMyPlaceLikeCommand(player.getId(), notApplied.getId()); + final MyPlaceLikeType actual = placeLikeService.checkMyLike(command); + + // then + assertThat(actual).isEqualTo(MyPlaceLikeType.NONE); + } +} diff --git a/backend/src/test/java/com/now/naaga/like/domain/MyPlaceLikeTypeTest.java b/backend/src/test/java/com/now/naaga/like/domain/MyPlaceLikeTypeTest.java new file mode 100644 index 000000000..3c4b37e8a --- /dev/null +++ b/backend/src/test/java/com/now/naaga/like/domain/MyPlaceLikeTypeTest.java @@ -0,0 +1,26 @@ +package com.now.naaga.like.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class MyPlaceLikeTypeTest { + + @ParameterizedTest(name = "PlaceLikeType ์ด {0} ์ผ๋•Œ, MyPlaceLikeType ์˜ {1} ์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค.") + @CsvSource(value = {"LIKE,LIKE", "DISLIKE,DISLIKE"}) + void PlaceLikeType_์„_MyPlaceLikeType_์œผ๋กœ_๋ณ€ํ™˜ํ•œ๋‹ค(final String placeLikeTypeValue, final String myPlaceLikeTypeValue) { + // given + final PlaceLikeType placeLikeType = PlaceLikeType.valueOf(placeLikeTypeValue); + + // when + final MyPlaceLikeType actual = MyPlaceLikeType.from(placeLikeType); + + // then + assertThat(actual.name()).isEqualTo(myPlaceLikeTypeValue); + } +} diff --git a/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTest.java b/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTest.java new file mode 100644 index 000000000..d92a62d89 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTest.java @@ -0,0 +1,37 @@ +package com.now.naaga.like.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.now.naaga.common.exception.BaseExceptionType; +import com.now.naaga.common.fixture.PlaceLikeFixture; +import com.now.naaga.like.exception.PlaceLikeException; +import com.now.naaga.like.exception.PlaceLikeExceptionType; +import com.now.naaga.member.domain.Member; +import com.now.naaga.player.domain.Player; +import com.now.naaga.score.domain.Score; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; + +@ActiveProfiles("test") +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class PlaceLikeTest { + + @Test + void ์ข‹์•„์š”๋ฅผ_๋ˆ„๋ฅธ_๋‹น์‚ฌ์ž๊ฐ€_์•„๋‹ˆ๋ฉด_์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒํ•œ๋‹ค() { + // given + final PlaceLike placeLike = PlaceLikeFixture.PLACE_LIKE(); + final Player newPlayer = new Player(2L, "unknown", new Score(123), new Member("123@naver.com"), false); + + // when & then + assertSoftly(softAssertions -> { + final BaseExceptionType baseExceptionType = assertThrows(PlaceLikeException.class, () -> placeLike.validateOwner(newPlayer)) + .exceptionType(); + assertThat(baseExceptionType).isEqualTo(PlaceLikeExceptionType.INACCESSIBLE_AUTHENTICATION); + }); + } +} diff --git a/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTypeTest.java b/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTypeTest.java new file mode 100644 index 000000000..d5321207c --- /dev/null +++ b/backend/src/test/java/com/now/naaga/like/domain/PlaceLikeTypeTest.java @@ -0,0 +1,29 @@ +package com.now.naaga.like.domain; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class PlaceLikeTypeTest { + + @Test + void ์ข‹์•„์š”_ํ˜น์€_์‹ซ์–ด์š”_ํƒ€์ž…์„_๋ฐ˜๋Œ€๋กœ_๋ฐ”๊พผ๋‹ค() { + // given + final PlaceLikeType like = PlaceLikeType.LIKE; + final PlaceLikeType dislike = PlaceLikeType.DISLIKE; + + // when + final PlaceLikeType actual1 = like.switchType(); + final PlaceLikeType actual2 = dislike.switchType(); + + // then + assertSoftly(softAssertions -> { + softAssertions.assertThat(actual1).isEqualTo(PlaceLikeType.DISLIKE); + softAssertions.assertThat(actual2).isEqualTo(PlaceLikeType.LIKE); + }); + } +} \ No newline at end of file diff --git a/backend/src/test/java/com/now/naaga/like/presentation/PlaceLikeControllerTest.java b/backend/src/test/java/com/now/naaga/like/presentation/PlaceLikeControllerTest.java new file mode 100644 index 000000000..88fd6c672 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/like/presentation/PlaceLikeControllerTest.java @@ -0,0 +1,315 @@ +package com.now.naaga.like.presentation; + +import static com.now.naaga.like.exception.PlaceLikeExceptionType.ALREADY_APPLIED_TYPE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.NO_CONTENT; +import static org.springframework.http.HttpStatus.OK; + +import com.now.naaga.auth.domain.AuthToken; +import com.now.naaga.auth.infrastructure.AuthType; +import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; +import com.now.naaga.common.CommonControllerTest; +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlaceLikeBuilder; +import com.now.naaga.common.builder.PlaceStatisticsBuilder; +import com.now.naaga.common.builder.PlayerBuilder; +import com.now.naaga.common.exception.ExceptionResponse; +import com.now.naaga.like.domain.MyPlaceLikeType; +import com.now.naaga.like.domain.PlaceLike; +import com.now.naaga.like.domain.PlaceLikeType; +import com.now.naaga.like.presentation.dto.ApplyPlaceLikeRequest; +import com.now.naaga.like.presentation.dto.CheckMyPlaceLikeResponse; +import com.now.naaga.like.presentation.dto.PlaceLikeCountResponse; +import com.now.naaga.like.presentation.dto.PlaceLikeResponse; +import com.now.naaga.member.domain.Member; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.place.exception.PlaceStatisticsExceptionType; +import com.now.naaga.player.domain.Player; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class PlaceLikeControllerTest extends CommonControllerTest { + + @Autowired + private PlayerBuilder playerBuilder; + + @Autowired + private PlaceBuilder placeBuilder; + + @Autowired + private PlaceLikeBuilder placeLikeBuilder; + + @Autowired + private PlaceStatisticsBuilder placeStatisticsBuilder; + + @Autowired + private AuthTokenGenerator authTokenGenerator; + + @BeforeEach + void setup() { + super.setUp(); + } + + @Test + void ์ข‹์•„์š”_๋“ฑ๋ก์ด_์„ฑ๊ณตํ•˜๋ฉด_201_์‘๋‹ต์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeStatisticsBuilder.init() + .place(place) + .build(); + + final ApplyPlaceLikeRequest applyPlaceLikeRequest = new ApplyPlaceLikeRequest(PlaceLikeType.LIKE); + + final Member member = player.getMember(); + final AuthToken generate = authTokenGenerator.generate(member, member.getId(), AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + // when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(applyPlaceLikeRequest) + .when() + .post("/places/{placeId}/likes", place.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final PlaceLikeResponse actual = extract.as(PlaceLikeResponse.class); + final PlaceLikeResponse expected = new PlaceLikeResponse(null, + player.getId(), + place.getId(), + PlaceLikeType.LIKE); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); + softAssertions.assertThat(actual).usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expected); + }); + } + + @Test + void ์ข‹์•„์š”_๋“ฑ๋ก_์š”์ฒญ์‹œ_์ค‘๋ณต๋˜๋Š”_์ข‹์•„์š”_ํƒ€์ž…์—_๋Œ€ํ•œ_์ž…๋ ฅ์ด_๋“ค์–ด์˜ค๋ฉด_์˜ˆ์™ธ_์‘๋‹ต๊ณผ_400_์‘๋‹ต์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + placeStatisticsBuilder.init() + .place(place) + .build(); + + placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(PlaceLikeType.LIKE) + .build(); + + final ApplyPlaceLikeRequest applyPlaceLikeRequest = new ApplyPlaceLikeRequest(PlaceLikeType.LIKE); + + final Member member = player.getMember(); + final AuthToken generate = authTokenGenerator.generate(member, member.getId(), AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + // when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(applyPlaceLikeRequest) + .when() + .post("/places/{placeId}/likes", place.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + final ExceptionResponse expected = new ExceptionResponse(ALREADY_APPLIED_TYPE.errorCode(), + ALREADY_APPLIED_TYPE.errorMessage()); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.BAD_REQUEST.value()); + softAssertions.assertThat(actual).usingRecursiveComparison() + .isEqualTo(expected); + }); + } + + @Test + void ์ข‹์•„์š”_์‚ญ์ œ๋ฅผ_์„ฑ๊ณตํ•˜๋ฉด_204์‘๋‹ต์„_ํ•œ๋‹ค() { + //given + final Place place = placeBuilder.init() + .build(); + final PlaceLike placeLike = placeLikeBuilder.init() + .place(place) + .build(); + placeStatisticsBuilder.init() + .place(place) + .build(); + final Member member = placeLike.getPlayer().getMember(); + final AuthToken generate = authTokenGenerator.generate(member, member.getId(), AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + //when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(ContentType.JSON) + .pathParam("placeId", place.getId()) + .when() + .delete("/places/{placeId}/likes/my") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + assertThat(statusCode).isEqualTo(NO_CONTENT.value()); + } + + @Test + void ์ข‹์•„์š”๊ฐ€_๋ˆŒ๋ ค์žˆ์„_๋•Œ_๋‚˜์˜_์ข‹์•„์š”_์—ฌ๋ถ€๋ฅผ_์กฐํšŒํ•˜๋ฉด_ํ•ด๋‹น_์ข‹์•„์š”_ํƒ€์ž…_์‘๋‹ต๊ณผ_200_์ƒํƒœ์ฝ”๋“œ๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + final PlaceLikeType myType = PlaceLikeType.LIKE; + + placeLikeBuilder.init() + .player(player) + .place(place) + .placeLikeType(myType) + .build(); + + final Member member = player.getMember(); + final AuthToken generate = authTokenGenerator.generate(member, member.getId(), AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + // when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when() + .get("/places/{placeId}/likes/my", place.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final CheckMyPlaceLikeResponse actual = extract.as(CheckMyPlaceLikeResponse.class); + final CheckMyPlaceLikeResponse expected = new CheckMyPlaceLikeResponse(MyPlaceLikeType.from(myType)); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softAssertions.assertThat(actual).usingRecursiveComparison() + .isEqualTo(expected); + }); + } + + @Test + void ์•„๋ฌด๊ฒƒ๋„_๋ˆŒ๋ ค์žˆ์ง€_์•Š์„_๋•Œ_๋‚˜์˜_์ข‹์•„์š”_์—ฌ๋ถ€๋ฅผ_์กฐํšŒํ•˜๋ฉด_NONE_ํƒ€์ž…_์‘๋‹ต๊ณผ_200_์ƒํƒœ์ฝ”๋“œ๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + // given + final Player player = playerBuilder.init() + .build(); + + final Place place = placeBuilder.init() + .build(); + + final Member member = player.getMember(); + final AuthToken generate = authTokenGenerator.generate(member, member.getId(), AuthType.KAKAO); + final String accessToken = generate.getAccessToken(); + + // when + final ExtractableResponse extract = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when() + .get("/places/{placeId}/likes/my", place.getId()) + .then().log().all() + .extract(); + + // then + final int statusCode = extract.statusCode(); + final CheckMyPlaceLikeResponse actual = extract.as(CheckMyPlaceLikeResponse.class); + final CheckMyPlaceLikeResponse expected = new CheckMyPlaceLikeResponse(MyPlaceLikeType.NONE); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.OK.value()); + softAssertions.assertThat(actual).usingRecursiveComparison() + .isEqualTo(expected); + }); + } + + @Test + void ํŠน์ •_์žฅ์†Œ์˜_์ข‹์•„์š”_์ˆ˜๋ฅผ_์‘๋‹ตํ•˜๊ณ _์‘๋‹ต์˜_์ƒํƒœ์ฝ”๋“œ๋Š”_200์ด๋‹ค() { + //given + final Long expected = 123L; + final PlaceStatistics placeStatistics = placeStatisticsBuilder.init() + .likeCount(expected) + .build(); + final Long placeId = placeStatistics.getPlace().getId(); + + //when + final ExtractableResponse extract = RestAssured + .given().log().all() + .pathParam("placeId", placeId) + .when() + .get("/places/{placeId}/likes/count") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + final PlaceLikeCountResponse actual = extract.as(PlaceLikeCountResponse.class); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(OK.value()); + softAssertions.assertThat(actual.placeLikeCount()).isEqualTo(expected); + }); + } + + @Test + void ์ข‹์•„์š”_์ˆ˜๋ฅผ_์กฐํšŒํ• _๋•Œ_์žฅ์†Œ_ํ†ต๊ณ„๊ฐ€_์—†์œผ๋ฉด_400_์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒํ•œ๋‹ค() { + //given & when + final ExtractableResponse extract = RestAssured + .given().log().all() + .pathParam("placeId", 100) + .when() + .get("/places/{placeId}/likes/count") + .then().log().all() + .extract(); + + //then + final int statusCode = extract.statusCode(); + final ExceptionResponse actual = extract.as(ExceptionResponse.class); + assertSoftly(softAssertions -> { + softAssertions.assertThat(statusCode).isEqualTo(NOT_FOUND.value()); + softAssertions.assertThat(actual.getCode()).isEqualTo(PlaceStatisticsExceptionType.NOT_FOUND.errorCode()); + softAssertions.assertThat(actual.getMessage()).isEqualTo(PlaceStatisticsExceptionType.NOT_FOUND.errorMessage()); + }); + } +} diff --git a/backend/src/test/java/com/now/naaga/place/application/PlaceServiceTest.java b/backend/src/test/java/com/now/naaga/place/application/PlaceServiceTest.java index c1543b507..87a799721 100644 --- a/backend/src/test/java/com/now/naaga/place/application/PlaceServiceTest.java +++ b/backend/src/test/java/com/now/naaga/place/application/PlaceServiceTest.java @@ -1,18 +1,13 @@ package com.now.naaga.place.application; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.now.naaga.common.builder.PlaceBuilder; import com.now.naaga.common.builder.PlayerBuilder; import com.now.naaga.common.builder.TemporaryPlaceBuilder; -import com.now.naaga.common.exception.BaseExceptionType; +import com.now.naaga.common.infrastructure.AwsS3FileManager; import com.now.naaga.place.application.dto.CreatePlaceCommand; import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; import com.now.naaga.place.domain.Position; -import com.now.naaga.place.exception.PlaceException; -import com.now.naaga.place.exception.PlaceExceptionType; +import com.now.naaga.place.repository.PlaceStatisticsRepository; import com.now.naaga.player.domain.Player; import com.now.naaga.temporaryplace.domain.TemporaryPlace; import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository; @@ -21,95 +16,104 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.*; + @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) @Sql("/truncate.sql") @ActiveProfiles("test") @SpringBootTest class PlaceServiceTest { - + @Autowired private TemporaryPlaceRepository temporaryPlaceRepository; - + + @Autowired + private PlaceStatisticsRepository placeStatisticsRepository; + @Autowired private PlaceService placeService; - + @Autowired private TemporaryPlaceBuilder temporaryPlaceBuilder; - - @Autowired - private PlaceBuilder placeBuilder; - + @Autowired private PlayerBuilder playerBuilder; - + @Transactional @Test void ์žฅ์†Œ๋ฅผ_๋“ฑ๋กํ•œ_๋’ค_๊ธฐ์กด์˜_๊ฒ€์ˆ˜_์žฅ์†Œ_๋ฐ์ดํ„ฐ๋Š”_์‚ญ์ œํ•œ๋‹ค() { // given final Player player = playerBuilder.init() .build(); - + final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init() .build(); - + final Long temporaryPlaceId = temporaryPlace.getId(); - + final CreatePlaceCommand createPlaceCommand = new CreatePlaceCommand("๋ฃจํ„ฐํšŒ๊ด€", - "์ด๊ณณ์€ ๋ฃจํ„ฐํšŒ๊ด€์ด๋‹ค ์•Œ๊ฒ ๋ƒ", - Position.of(1.23, 4.56), - "image/url", - player.getId(), - temporaryPlaceId); - + "์ด๊ณณ์€ ๋ฃจํ„ฐํšŒ๊ด€์ด๋‹ค ์•Œ๊ฒ ๋ƒ", + Position.of(1.23, 4.56), + "image/url", + player.getId(), + temporaryPlaceId); + // when final Place actual = placeService.createPlace(createPlaceCommand); - + // then final Place expected = new Place(createPlaceCommand.name(), - createPlaceCommand.description(), - createPlaceCommand.position(), - createPlaceCommand.imageUrl(), - player); - + createPlaceCommand.description(), + createPlaceCommand.position(), + createPlaceCommand.imageUrl(), + player); + final TemporaryPlace found = temporaryPlaceRepository.findById(temporaryPlaceId) .orElse(null); - - assertSoftly(softAssertions -> { - assertThat(actual).usingRecursiveComparison() - .ignoringExpectedNullFields() - .isEqualTo(expected); - assertThat(found).isNull(); - }); + + assertAll( + () -> assertThat(actual).usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expected), + () -> assertThat(found).isNull() + ); } - + @Test - void ์žฅ์†Œ_๋“ฑ๋ก_์‹œ_์ฃผ๋ณ€_๋ฐ˜๊ฒฝ_20M_๋‚ด์—_๋“ฑ๋ก๋œ_์žฅ์†Œ๊ฐ€_์กด์žฌํ•œ๋‹ค๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + void ์žฅ์†Œ_๋“ฑ๋ก_์‹œ_์žฅ์†Œ_ํ†ต๊ณ„๋„_ํ•จ๊ป˜_๋“ฑ๋ก๋œ๋‹ค() { // given final Player player = playerBuilder.init() .build(); - - placeBuilder.init() - .position(Position.of(1.234567, 1.234567)) - .build(); - + + final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init() + .build(); + + final Long temporaryPlaceId = temporaryPlace.getId(); + final CreatePlaceCommand createPlaceCommand = new CreatePlaceCommand("๋ฃจํ„ฐํšŒ๊ด€", - "์ด๊ณณ์€ ๋ฃจํ„ฐํšŒ๊ด€์ด๋‹ค ์•Œ๊ฒ ๋ƒ", - Position.of(1.23456, 1.23456), - "image/url", - player.getId(), - 1L); - + "์ด๊ณณ์€ ๋ฃจํ„ฐํšŒ๊ด€์ด๋‹ค ์•Œ๊ฒ ๋ƒ", + Position.of(1.23, 4.56), + "image/url", + player.getId(), + temporaryPlaceId); + // when - final BaseExceptionType baseExceptionType = assertThrows(PlaceException.class, - () -> placeService.createPlace(createPlaceCommand) - ).exceptionType(); - + final Place place = placeService.createPlace(createPlaceCommand); + // then - assertThat(baseExceptionType).isEqualTo(PlaceExceptionType.ALREADY_EXIST_NEARBY); + final Optional placeStatistics = placeStatisticsRepository.findByPlaceId(place.getId()); + assertAll( + () -> assertThat(placeStatistics).isNotEmpty() + ); } -} \ No newline at end of file +} diff --git a/backend/src/test/java/com/now/naaga/place/application/PlaceStatisticsServiceTest.java b/backend/src/test/java/com/now/naaga/place/application/PlaceStatisticsServiceTest.java new file mode 100644 index 000000000..c9e8b5692 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/place/application/PlaceStatisticsServiceTest.java @@ -0,0 +1,146 @@ +package com.now.naaga.place.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.now.naaga.common.builder.PlaceBuilder; +import com.now.naaga.common.builder.PlaceStatisticsBuilder; +import com.now.naaga.common.exception.BaseExceptionType; +import com.now.naaga.place.application.dto.CreatePlaceStatisticsCommand; +import com.now.naaga.place.application.dto.FindPlaceStatisticsByPlaceIdCommand; +import com.now.naaga.place.application.dto.PlusLikeCommand; +import com.now.naaga.place.application.dto.SubtractLikeCommand; +import com.now.naaga.place.domain.Place; +import com.now.naaga.place.domain.PlaceStatistics; +import com.now.naaga.place.exception.PlaceException; +import com.now.naaga.place.exception.PlaceExceptionType; +import com.now.naaga.place.exception.PlaceStatisticsException; +import com.now.naaga.place.exception.PlaceStatisticsExceptionType; +import com.now.naaga.place.repository.PlaceStatisticsRepository; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +@Sql("/truncate.sql") +@ActiveProfiles("test") +@SpringBootTest +class PlaceStatisticsServiceTest { + + @Autowired + private PlaceStatisticsRepository placeStatisticsRepository; + + @Autowired + private PlaceStatisticsService placeStatisticsService; + + @Autowired + private PlaceStatisticsBuilder placeStatisticsBuilder; + + @Autowired + private PlaceBuilder placeBuilder; + + @Transactional + @Test + void ์žฅ์†Œํ†ต๊ณ„๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { + // given + final Place place = placeBuilder.init() + .build(); + + // when + final CreatePlaceStatisticsCommand command = new CreatePlaceStatisticsCommand(place.getId()); + final PlaceStatistics actual = placeStatisticsService.createPlaceStatistics(command); + + // then + final PlaceStatistics expected = new PlaceStatistics(place, PlaceStatistics.LIKE_COUNT_DEFAULT_VALUE); + assertThat(actual).usingRecursiveComparison() + .ignoringExpectedNullFields() + .isEqualTo(expected); + } + + @Test + void ์žฅ์†Œํ†ต๊ณ„์—์„œ_์ข‹์•„์š”_์ˆ˜_1๊ฐœ๋ฅผ_์˜ฌ๋ฆฐ๋‹ค() { + // given + final PlaceStatistics placeStatistics = placeStatisticsBuilder.init() + .likeCount(5L) + .build(); + + // when + placeStatisticsService.plusLike(new PlusLikeCommand(placeStatistics.getPlace().getId())); + + // then + final PlaceStatistics actual = placeStatisticsRepository.findByPlaceId(placeStatistics.getPlace().getId()).get(); + assertThat(actual.getLikeCount()).isEqualTo(6L); + } + + @Test + void ์žฅ์†Œํ†ต๊ณ„์˜_์ข‹์•„์š”_์ˆ˜๋ฅผ_์˜ฌ๋ฆด๋•Œ_์žฅ์†Œํ†ต๊ณ„๋ฅผ_์ฐพ์„_์ˆ˜_์—†๋‹ค๋ฉด_์žฅ์†Œ_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given + final PlaceStatistics placeStatistics = placeStatisticsBuilder.init() + .build(); + + // when + final PlusLikeCommand plusLikeCommand = new PlusLikeCommand(placeStatistics.getPlace().getId() + 1); + final BaseExceptionType baseExceptionType = assertThrows(PlaceException.class, + () -> placeStatisticsService.plusLike(plusLikeCommand) + ).exceptionType(); + + // then + assertThat(baseExceptionType).isEqualTo(PlaceExceptionType.NO_EXIST); + } + + @Test + void ์žฅ์†Œํ†ต๊ณ„_1๊ฐœ๋ฅผ_์ค„์ธ๋‹ค() { + //given + final long beforeLikeCount = 10L; + final PlaceStatistics placeStatistics = placeStatisticsBuilder.init() + .likeCount(beforeLikeCount) + .build(); + final Place place = placeStatistics.getPlace(); + + //when + final SubtractLikeCommand subtractLikeCommand = new SubtractLikeCommand(place.getId()); + placeStatisticsService.subtractLike(subtractLikeCommand); + + //then + final PlaceStatistics findPlaceStatistics = placeStatisticsRepository.findByPlaceId(place.getId()).get(); + assertThat(findPlaceStatistics.getLikeCount()).isEqualTo(beforeLikeCount - 1); + } + + @Transactional + @Test + void ์žฅ์†Œํ†ต๊ณ„๋ฅผ_์žฅ์†Œ์•„์ด๋””๋กœ_์กฐํšŒํ•œ๋‹ค() { + // given + final PlaceStatistics expected = placeStatisticsBuilder.init() + .likeCount(123L) + .build(); + + // when + final Long placeId = expected.getPlace().getId(); + final FindPlaceStatisticsByPlaceIdCommand findPlaceStatisticsByPlaceIdCommand = new FindPlaceStatisticsByPlaceIdCommand(placeId); + final PlaceStatistics actual = placeStatisticsService.findPlaceStatisticsByPlaceId(findPlaceStatisticsByPlaceIdCommand); + + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } + + @Test + void ์žฅ์†Œํ†ต๊ณ„๋ฅผ_์žฅ์†Œ์•„์ด๋””๋กœ_์กฐํšŒ_์‹œ_์—†์œผ๋ฉด_์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒํ•œ๋‹ค() { + // given & when + final FindPlaceStatisticsByPlaceIdCommand findPlaceStatisticsByPlaceIdCommand = new FindPlaceStatisticsByPlaceIdCommand(1L); + + // then + assertAll(() -> { + final BaseExceptionType baseExceptionType = assertThrows(PlaceStatisticsException.class, () -> placeStatisticsService.findPlaceStatisticsByPlaceId(findPlaceStatisticsByPlaceIdCommand)) + .exceptionType(); + assertThat(baseExceptionType).isEqualTo(PlaceStatisticsExceptionType.NOT_FOUND); + }); + } +} diff --git a/backend/src/test/java/com/now/naaga/place/domain/PlaceStatisticsTest.java b/backend/src/test/java/com/now/naaga/place/domain/PlaceStatisticsTest.java new file mode 100644 index 000000000..59dd74f95 --- /dev/null +++ b/backend/src/test/java/com/now/naaga/place/domain/PlaceStatisticsTest.java @@ -0,0 +1,43 @@ +package com.now.naaga.place.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.now.naaga.common.fixture.PlaceFixture; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ActiveProfiles; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@ActiveProfiles("test") +class PlaceStatisticsTest { + + @Test + void ์žฅ์†Œ_ํ†ต๊ณ„์—์„œ_์ข‹์•„์š”๋ฅผ_1๊ฐœ_๋บ€๋‹ค() { + // given + final Place place = PlaceFixture.PLACE(); + final long beforeLikeCount = 10L; + final PlaceStatistics placeStatistics = new PlaceStatistics(place, beforeLikeCount); + + // when + placeStatistics.subtractLike(); + + // then + assertThat(placeStatistics.getLikeCount()).isEqualTo(beforeLikeCount - 1); + } + + @Test + void ์žฅ์†Œ_ํ†ต๊ณ„_์ข‹์•„์š”๋Š”_0๊ฐœ_์ดํ•˜๋กœ_์ค„์–ด๋“ค์ง€_์•Š๋Š”๋‹ค() { + // given + final Place place = PlaceFixture.PLACE(); + final long beforeLikeCount = 0L; + final PlaceStatistics placeStatistics = new PlaceStatistics(place, beforeLikeCount); + + // when + placeStatistics.subtractLike(); + + // then + assertThat(placeStatistics.getLikeCount()).isEqualTo(0L); + } +} diff --git "a/backend/src/test/java/com/now/naaga/place/fixture/\353\243\250\355\204\260\355\232\214\352\264\200.png" "b/backend/src/test/java/com/now/naaga/place/fixture/\353\243\250\355\204\260\355\232\214\352\264\200.png" deleted file mode 100644 index 34126c328..000000000 Binary files "a/backend/src/test/java/com/now/naaga/place/fixture/\353\243\250\355\204\260\355\232\214\352\264\200.png" and /dev/null differ diff --git a/backend/src/test/java/com/now/naaga/place/presentation/PlaceControllerTest.java b/backend/src/test/java/com/now/naaga/place/presentation/PlaceControllerTest.java index 074487753..6643490bc 100644 --- a/backend/src/test/java/com/now/naaga/place/presentation/PlaceControllerTest.java +++ b/backend/src/test/java/com/now/naaga/place/presentation/PlaceControllerTest.java @@ -56,49 +56,49 @@ protected void setUp() { super.setUp(); } - @Test - void ์žฅ์†Œ_๋“ฑ๋ก_์š”์ฒญ์ด_์„ฑ๊ณตํ•˜๋ฉด_201_์ƒํƒœ์ฝ”๋“œ์™€_์ƒ์„ฑ๋œ_์žฅ์†Œ๋ฅผ_์‘๋‹ตํ•œ๋‹ค() { - // given - final Player player = playerBuilder.init() - .build(); - - final CreatePlaceRequest createPlaceRequest = new CreatePlaceRequest("๋ฃจํ„ฐํšŒ๊ด€", - "์ด๊ณณ์€ ์—ญ์‚ฌ์™€ ์ „ํ†ต์ด ๊นŠ์€ ๋ฃจํ„ฐํšŒ๊ด€์ž…๋‹ˆ๋‹ค.", - 1.234, - 5.6789, - "image/url", - player.getId(), - 1L); - - // when - final ExtractableResponse extract = given() - .log().all() - .auth().preemptive().basic(id, password) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(createPlaceRequest) - .when() - .post("/places") - .then() - .log().all() - .extract(); - - // then - final int statusCode = extract.statusCode(); - final PlaceResponse actual = extract.as(PlaceResponse.class); - final Position position = Position.of(createPlaceRequest.latitude(), - createPlaceRequest.longitude()); - final PlaceResponse expected = new PlaceResponse(getIdFromLocationHeader(extract), - createPlaceRequest.name(), - CoordinateResponse.from(position), - createPlaceRequest.imageUrl(), - createPlaceRequest.description()); - assertSoftly(softAssertions -> { - softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); - softAssertions.assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(expected); - }); - } +// @Test +// void ์žฅ์†Œ_๋“ฑ๋ก_์š”์ฒญ์ด_์„ฑ๊ณตํ•˜๋ฉด_201_์ƒํƒœ์ฝ”๋“œ์™€_์ƒ์„ฑ๋œ_์žฅ์†Œ๋ฅผ_์‘๋‹ตํ•œ๋‹ค() { +// // given +// final Player player = playerBuilder.init() +// .build(); +// +// final CreatePlaceRequest createPlaceRequest = new CreatePlaceRequest("๋ฃจํ„ฐํšŒ๊ด€", +// "์ด๊ณณ์€ ์—ญ์‚ฌ์™€ ์ „ํ†ต์ด ๊นŠ์€ ๋ฃจํ„ฐํšŒ๊ด€์ž…๋‹ˆ๋‹ค.", +// 1.234, +// 5.6789, +// "image/url", +// player.getId(), +// 1L); +// +// // when +// final ExtractableResponse extract = given() +// .log().all() +// .auth().preemptive().basic(id, password) +// .contentType(MediaType.APPLICATION_JSON_VALUE) +// .body(createPlaceRequest) +// .when() +// .post("/places") +// .then() +// .log().all() +// .extract(); +// +// // then +// final int statusCode = extract.statusCode(); +// final PlaceResponse actual = extract.as(PlaceResponse.class); +// final Position position = Position.of(createPlaceRequest.latitude(), +// createPlaceRequest.longitude()); +// final PlaceResponse expected = new PlaceResponse(getIdFromLocationHeader(extract), +// createPlaceRequest.name(), +// CoordinateResponse.from(position), +// createPlaceRequest.imageUrl(), +// createPlaceRequest.description()); +// assertSoftly(softAssertions -> { +// softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); +// softAssertions.assertThat(actual) +// .usingRecursiveComparison() +// .isEqualTo(expected); +// }); +// } @Test void ์žฅ์†Œ๋ฅผ_์•„์ด๋””๋กœ_์กฐํšŒํ•œ๋‹ค() { diff --git a/backend/src/test/java/com/now/naaga/place/service/PlaceServiceTest.java b/backend/src/test/java/com/now/naaga/place/service/PlaceServiceTest.java index 75fa82586..c2e3f225d 100644 --- a/backend/src/test/java/com/now/naaga/place/service/PlaceServiceTest.java +++ b/backend/src/test/java/com/now/naaga/place/service/PlaceServiceTest.java @@ -1,5 +1,9 @@ package com.now.naaga.place.service; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.junit.jupiter.api.Assertions.assertThrows; + import com.now.naaga.common.builder.PlaceBuilder; import com.now.naaga.common.exception.BaseExceptionType; import com.now.naaga.common.fixture.PositionFixture; @@ -9,8 +13,6 @@ import com.now.naaga.place.domain.Position; import com.now.naaga.place.exception.PlaceException; import com.now.naaga.place.exception.PlaceExceptionType; -import io.restassured.internal.common.assertion.Assertion; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -19,11 +21,6 @@ import org.springframework.test.context.jdbc.Sql; import org.springframework.transaction.annotation.Transactional; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.junit.jupiter.api.Assertions.assertThrows; - @ActiveProfiles("test") @Sql("/truncate.sql") @SpringBootTest diff --git a/backend/src/test/java/com/now/naaga/player/application/PlayerServiceTest.java b/backend/src/test/java/com/now/naaga/player/application/PlayerServiceTest.java index 593aa8b2b..c5a24b2e8 100644 --- a/backend/src/test/java/com/now/naaga/player/application/PlayerServiceTest.java +++ b/backend/src/test/java/com/now/naaga/player/application/PlayerServiceTest.java @@ -14,8 +14,6 @@ import com.now.naaga.score.domain.Score; import java.util.ArrayList; import java.util.List; - -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; diff --git a/backend/src/test/java/com/now/naaga/player/persistence/repository/PlayerRepositoryTest.java b/backend/src/test/java/com/now/naaga/player/persistence/repository/PlayerRepositoryTest.java index 6f554ce97..e977ebffa 100644 --- a/backend/src/test/java/com/now/naaga/player/persistence/repository/PlayerRepositoryTest.java +++ b/backend/src/test/java/com/now/naaga/player/persistence/repository/PlayerRepositoryTest.java @@ -5,13 +5,11 @@ import com.now.naaga.common.builder.PlayerBuilder; import com.now.naaga.player.domain.Player; import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; -import org.springframework.transaction.annotation.Transactional; @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) diff --git a/backend/src/test/java/com/now/naaga/player/presentation/PlayerControllerTest.java b/backend/src/test/java/com/now/naaga/player/presentation/PlayerControllerTest.java index 26a0ea32e..327c177a5 100644 --- a/backend/src/test/java/com/now/naaga/player/presentation/PlayerControllerTest.java +++ b/backend/src/test/java/com/now/naaga/player/presentation/PlayerControllerTest.java @@ -1,5 +1,9 @@ package com.now.naaga.player.presentation; +import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import com.now.naaga.auth.domain.AuthToken; import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; @@ -13,6 +17,8 @@ import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import java.time.LocalDateTime; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; @@ -21,13 +27,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import java.time.LocalDateTime; -import java.util.List; - -import static com.now.naaga.common.exception.CommonExceptionType.INVALID_REQUEST_PARAMETERS; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) public class PlayerControllerTest extends CommonControllerTest { diff --git a/backend/src/test/java/com/now/naaga/temporaryplace/application/TemporaryPlaceServiceTest.java b/backend/src/test/java/com/now/naaga/temporaryplace/application/TemporaryPlaceServiceTest.java index 6261eeb8a..c94edf149 100644 --- a/backend/src/test/java/com/now/naaga/temporaryplace/application/TemporaryPlaceServiceTest.java +++ b/backend/src/test/java/com/now/naaga/temporaryplace/application/TemporaryPlaceServiceTest.java @@ -1,15 +1,21 @@ package com.now.naaga.temporaryplace.application; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; import com.now.naaga.common.builder.TemporaryPlaceBuilder; +import com.now.naaga.common.infrastructure.AwsS3FileManager; import com.now.naaga.temporaryplace.domain.TemporaryPlace; import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; @@ -19,31 +25,58 @@ @ActiveProfiles("test") @SpringBootTest class TemporaryPlaceServiceTest { - + @Autowired private TemporaryPlaceRepository temporaryPlaceRepository; - + @Autowired private TemporaryPlaceService temporaryPlaceService; - + @Autowired private TemporaryPlaceBuilder temporaryPlaceBuilder; - + + @MockBean + private AwsS3FileManager awsS3FileManager; + @Test - void ID๋กœ_๊ฒ€์ˆ˜_์žฅ์†Œ_๋ฐ์ดํ„ฐ๋ฅผ_์‚ญ์ œํ•œ๋‹ค() { + void ์žฅ์†Œ๊ฐ€_์ƒ์„ฑ๋ _๋•Œ_ID๋กœ_๊ฒ€์ˆ˜_์žฅ์†Œ_๋ฐ์ดํ„ฐ๋งŒ_์‚ญ์ œ๋œ๋‹ค() { // given final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init() .build(); - + final Long id = temporaryPlace.getId(); - + // when - temporaryPlaceService.deleteById(id); - + temporaryPlaceService.deleteByIdWhenPlaceCreated(id); + // then final TemporaryPlace actual = temporaryPlaceRepository.findById(id) .orElse(null); - - assertThat(actual).isNull(); + + assertAll( + () -> assertThat(actual).isNull(), + () -> verify(awsS3FileManager, times(0)).deleteFile(anyString()) + ); + } + + @Test + void ๊ฒ€์ˆ˜_์žฅ์†Œ_๋“ฑ๋ก์ด_๊ฑฐ์ ˆ๋ _๋•Œ_ID๋กœ_๊ฒ€์ˆ˜_์žฅ์†Œ_๋ฐ์ดํ„ฐ_์‚ญ์ œ_๋ฐ_S3_์ด๋ฏธ์ง€_ํŒŒ์ผ์ด_ํ•จ๊ป˜_์‚ญ์ œ๋œ๋‹ค() { + // given + final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init() + .build(); + + final Long id = temporaryPlace.getId(); + + // when + temporaryPlaceService.deleteByIdWhenTemporaryPlaceDenied(id); + + // then + final TemporaryPlace actual = temporaryPlaceRepository.findById(id) + .orElse(null); + + assertAll( + () -> assertThat(actual).isNull(), + () -> verify(awsS3FileManager, times(1)).deleteFile(anyString()) + ); } } diff --git a/backend/src/test/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceControllerTest.java b/backend/src/test/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceControllerTest.java index cfb51f20b..a20681bf9 100644 --- a/backend/src/test/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceControllerTest.java +++ b/backend/src/test/java/com/now/naaga/temporaryplace/presentation/TemporaryPlaceControllerTest.java @@ -1,46 +1,34 @@ package com.now.naaga.temporaryplace.presentation; +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; + import com.fasterxml.jackson.core.JsonProcessingException; -import com.now.naaga.auth.domain.AuthToken; -import com.now.naaga.auth.infrastructure.AuthType; import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator; import com.now.naaga.common.CommonControllerTest; import com.now.naaga.common.builder.PlaceBuilder; import com.now.naaga.common.builder.PlayerBuilder; import com.now.naaga.common.builder.TemporaryPlaceBuilder; -import com.now.naaga.common.infrastructure.FileManager; -import com.now.naaga.player.domain.Player; import com.now.naaga.temporaryplace.domain.TemporaryPlace; import com.now.naaga.temporaryplace.presentation.dto.TemporaryPlaceResponse; import io.restassured.RestAssured; -import io.restassured.builder.MultiPartSpecBuilder; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; + +import java.io.FileNotFoundException; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.math.RoundingMode; -import java.nio.charset.StandardCharsets; -import java.util.List; - -import static com.now.naaga.common.fixture.TemporaryPlaceFixture.TEMPORARY_PLACE; -import static io.restassured.RestAssured.given; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) @@ -59,8 +47,8 @@ class TemporaryPlaceControllerTest extends CommonControllerTest { @Autowired private AuthTokenGenerator authTokenGenerator; - @SpyBean - private FileManager fileManager; +// @SpyBean +// private FileManager fileManager; @Value("${manager.id}") private String id; @@ -73,56 +61,56 @@ void setup() { super.setUp(); } - @Test - void ๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_๋“ฑ๋ก_์š”์ฒญ์„_๋ฐ›์œผ๋ฉด_201_์ƒํƒœ์ฝ”๋“œ์™€_ํ•จ๊ป˜_์ถ”๊ฐ€๋œ_๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_์ •๋ณด๋ฅผ_์‘๋‹ตํ•œ๋‹ค() throws FileNotFoundException { - //given - final Player player = playerBuilder.init() - .build(); - final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); - final String accessToken = generate.getAccessToken(); - final TemporaryPlace temporaryPlace = TEMPORARY_PLACE(); - - doReturn(new File("/์ž„์‹œ๊ฒฝ๋กœ", "์ด๋ฏธ์ง€.png")).when(fileManager) - .save(any()); - //when - final ExtractableResponse extract = given() - .log().all() - .multiPart(new MultiPartSpecBuilder(temporaryPlace.getName()).controlName("name").charset(StandardCharsets.UTF_8).build()) - .multiPart(new MultiPartSpecBuilder(temporaryPlace.getDescription()).controlName("description").charset(StandardCharsets.UTF_8).build()) - .multiPart( - new MultiPartSpecBuilder(temporaryPlace.getPosition().getLatitude().setScale(6, RoundingMode.HALF_DOWN).doubleValue()).controlName( - "latitude").charset(StandardCharsets.UTF_8).build()) - .multiPart( - new MultiPartSpecBuilder(temporaryPlace.getPosition().getLongitude().setScale(6, RoundingMode.HALF_DOWN).doubleValue()).controlName( - "longitude").charset(StandardCharsets.UTF_8).build()) - .multiPart(new MultiPartSpecBuilder(new FileInputStream(new File("src/test/java/com/now/naaga/temporaryplace/fixture/๋ฃจํ„ฐํšŒ๊ด€.png"))).controlName( - "imageFile").charset(StandardCharsets.UTF_8) - .fileName( - "src/test/java/com/now/naaga/temporaryplace/fixture/๋ฃจํ„ฐํšŒ๊ด€.png") - .mimeType( - "image/png") - .build()) - .header("Authorization", "Bearer " + accessToken) - .when() - .post("/temporary-places") - .then() - .log().all() - .extract(); - final int statusCode = extract.statusCode(); - final String location = extract.header("Location"); - final TemporaryPlaceResponse actual = extract.as(TemporaryPlaceResponse.class); - final TemporaryPlaceResponse expected = TemporaryPlaceResponse.from(temporaryPlace); - - //then - assertSoftly(softAssertions -> { - softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); - softAssertions.assertThat(location).isEqualTo("/temporary-places/" + actual.id()); - softAssertions.assertThat(actual) - .usingRecursiveComparison() - .ignoringFields("id", "imageUrl", "registeredPlayerId") - .isEqualTo(expected); - }); - } +// @Test +// void ๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_๋“ฑ๋ก_์š”์ฒญ์„_๋ฐ›์œผ๋ฉด_201_์ƒํƒœ์ฝ”๋“œ์™€_ํ•จ๊ป˜_์ถ”๊ฐ€๋œ_๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_์ •๋ณด๋ฅผ_์‘๋‹ตํ•œ๋‹ค() throws FileNotFoundException { +// //given +// final Player player = playerBuilder.init() +// .build(); +// final AuthToken generate = authTokenGenerator.generate(player.getMember(), 1L, AuthType.KAKAO); +// final String accessToken = generate.getAccessToken(); +// final TemporaryPlace temporaryPlace = TEMPORARY_PLACE(); +// +// doReturn(new File("/์ž„์‹œ๊ฒฝ๋กœ", "์ด๋ฏธ์ง€.png")).when(fileManager) +// .save(any()); +// //when +// final ExtractableResponse extract = given() +// .log().all() +// .multiPart(new MultiPartSpecBuilder(temporaryPlace.getName()).controlName("name").charset(StandardCharsets.UTF_8).build()) +// .multiPart(new MultiPartSpecBuilder(temporaryPlace.getDescription()).controlName("description").charset(StandardCharsets.UTF_8).build()) +// .multiPart( +// new MultiPartSpecBuilder(temporaryPlace.getPosition().getLatitude().setScale(6, RoundingMode.HALF_DOWN).doubleValue()).controlName( +// "latitude").charset(StandardCharsets.UTF_8).build()) +// .multiPart( +// new MultiPartSpecBuilder(temporaryPlace.getPosition().getLongitude().setScale(6, RoundingMode.HALF_DOWN).doubleValue()).controlName( +// "longitude").charset(StandardCharsets.UTF_8).build()) +// .multiPart(new MultiPartSpecBuilder(new FileInputStream(new File("src/test/java/com/now/naaga/temporaryplace/fixture/๋ฃจํ„ฐํšŒ๊ด€.png"))).controlName( +// "imageFile").charset(StandardCharsets.UTF_8) +// .fileName( +// "src/test/java/com/now/naaga/temporaryplace/fixture/๋ฃจํ„ฐํšŒ๊ด€.png") +// .mimeType( +// "image/png") +// .build()) +// .header("Authorization", "Bearer " + accessToken) +// .when() +// .post("/temporary-places") +// .then() +// .log().all() +// .extract(); +// final int statusCode = extract.statusCode(); +// final String location = extract.header("Location"); +// final TemporaryPlaceResponse actual = extract.as(TemporaryPlaceResponse.class); +// final TemporaryPlaceResponse expected = TemporaryPlaceResponse.from(temporaryPlace); +// +// //then +// assertSoftly(softAssertions -> { +// softAssertions.assertThat(statusCode).isEqualTo(HttpStatus.CREATED.value()); +// softAssertions.assertThat(location).isEqualTo("/temporary-places/" + actual.id()); +// softAssertions.assertThat(actual) +// .usingRecursiveComparison() +// .ignoringFields("id", "imageUrl", "registeredPlayerId") +// .isEqualTo(expected); +// }); +// } @Test void ๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_๋ชฉ๋ก_์กฐํšŒ_์š”์ฒญ์„_๋ฐ›์œผ๋ฉด_200_์ƒํƒœ์ฝ”๋“œ์™€_ํ•จ๊ป˜_๊ฒ€์ˆ˜ํ• _์žฅ์†Œ_๋ชฉ๋ก์„_์‘๋‹ตํ•œ๋‹ค() throws FileNotFoundException, JsonProcessingException { diff --git a/backend/src/test/resources/truncate.sql b/backend/src/test/resources/truncate.sql index 59397b49b..b05fa514a 100644 --- a/backend/src/test/resources/truncate.sql +++ b/backend/src/test/resources/truncate.sql @@ -1,6 +1,7 @@ -SET -FOREIGN_KEY_CHECKS = 0; +SET FOREIGN_KEY_CHECKS = 0; +TRUNCATE TABLE place_statistics; +TRUNCATE TABLE place_like; TRUNCATE TABLE place; TRUNCATE TABLE member; TRUNCATE TABLE player; @@ -8,6 +9,8 @@ TRUNCATE TABLE game_result; TRUNCATE TABLE game; TRUNCATE TABLE hint; TRUNCATE TABLE auth_token; +TRUNCATE TABLE letter; +TRUNCATE TABLE read_letter_log; +TRUNCATE TABLE write_letter_log; -SET -FOREIGN_KEY_CHECKS = 1; +SET FOREIGN_KEY_CHECKS = 1; diff --git a/etc/images/google play store.png b/etc/images/google play store.png new file mode 100644 index 000000000..6313ad11d Binary files /dev/null and b/etc/images/google play store.png differ diff --git a/etc/images/header.png b/etc/images/header.png new file mode 100644 index 000000000..d637cedaa Binary files /dev/null and b/etc/images/header.png differ diff --git a/etc/images/service intro.png b/etc/images/service intro.png new file mode 100644 index 000000000..edd044876 Binary files /dev/null and b/etc/images/service intro.png differ