From 8e0df12e6a2717b099b2a26b70b5c6366f385604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=9A=B0?= Date: Sun, 8 Oct 2023 17:49:49 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: mypage total 조회 * refactor: jacoco 기준 변경 * feat : embedded redis를 test-container 대체 (#68) --------- Co-authored-by: Hyeonjun Park <55674648+phjppo0918@users.noreply.github.com> --- .github/workflows/build-docker-image-dev.yml | 4 ++ .github/workflows/build-docker-image-prd.yml | 4 ++ .github/workflows/build.yml | 4 ++ build.gradle | 7 +-- .../battle/repository/BattleRepository.java | 3 ++ .../mypage/controller/MyPageController.java | 27 +++++++++++ .../mypage/dto/MyPageTotalResponse.java | 43 ++++++++++++++++++ .../domain/mypage/service/MyPageService.java | 29 ++++++++++++ .../domain/single/entity/RunningTime.java | 4 ++ .../single/repository/SingleRepository.java | 4 ++ .../global/config/RedissonConfig.java | 19 -------- src/main/resources/application.yml | 2 +- .../repository/BattleRepositoryTest.java | 18 ++++++++ .../repository/SingleRepositoryTest.java | 45 +++++++++++++++++++ 14 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java delete mode 100644 src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java create mode 100644 src/test/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepositoryTest.java diff --git a/.github/workflows/build-docker-image-dev.yml b/.github/workflows/build-docker-image-dev.yml index eca19130..61ad4df6 100644 --- a/.github/workflows/build-docker-image-dev.yml +++ b/.github/workflows/build-docker-image-dev.yml @@ -23,6 +23,10 @@ jobs: with: java-version: '17' distribution: 'temurin' + - name: Setup Testcontainers Cloud Client + uses: atomicjar/testcontainers-cloud-setup-action@v1 + with: + token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2.1.0 diff --git a/.github/workflows/build-docker-image-prd.yml b/.github/workflows/build-docker-image-prd.yml index ba38f0be..90b28f25 100644 --- a/.github/workflows/build-docker-image-prd.yml +++ b/.github/workflows/build-docker-image-prd.yml @@ -23,6 +23,10 @@ jobs: with: java-version: '17' distribution: 'temurin' + - name: Setup Testcontainers Cloud Client + uses: atomicjar/testcontainers-cloud-setup-action@v1 + with: + token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2.1.0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0d315e2..758f7c69 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,10 @@ jobs: with: java-version: "17" distribution: "temurin" + - name: Setup Testcontainers Cloud Client + uses: atomicjar/testcontainers-cloud-setup-action@v1 + with: + token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Grant execute permission for gradlew run: chmod +x ./gradlew - name: Build with Gradle diff --git a/build.gradle b/build.gradle index 9e0867ec..7532ce39 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs' implementation 'com.github.maricn:logback-slack-appender:1.6.1' - testImplementation 'com.github.SWM-KAWAI-MANS:test-manager:1.0.2' + testImplementation 'com.github.SWM-KAWAI-MANS:test-manager:1.1.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' @@ -155,7 +155,8 @@ jacocoTestCoverageVerification { '**.*Parser*', '**.*Listener*', '**.*Message*', - '**.*logging*.**' + '**.*logging*.**', + '**.*mypage*.**' ] } @@ -166,7 +167,7 @@ jacocoTestCoverageVerification { limit { counter = 'LINE' value = 'TOTALCOUNT' - maximum = 20 + maximum = 40 } } } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepository.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepository.java index 4dc9b455..5f56dc77 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepository.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepository.java @@ -40,4 +40,7 @@ void addRunnerRecordsAndUpdateRunnerStatus( Optional findBattleByRunnerStatus(String runnerId, List preRunnerStatus); Optional findByIdAndRunnersId(String battleId, String runnerId); + + @Query(value = "{ 'runners.id': ?0 }", fields = "{'runners.runnerRecords': 0}") + List findAllByRunnersIdExceptRunnerRecords(String runnerId); } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java new file mode 100644 index 00000000..02da8aef --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java @@ -0,0 +1,27 @@ +package online.partyrun.partyrunbattleservice.domain.mypage.controller; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageTotalResponse; +import online.partyrun.partyrunbattleservice.domain.mypage.service.MyPageService; +import online.partyrun.partyrunbattleservice.global.logging.Logging; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Logging +@RestController +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +@RequestMapping("mypage") +public class MyPageController { + + MyPageService myPageService; + + @GetMapping("/total") + public MyPageTotalResponse getMyPageTotalResponse(Authentication auth) { + return myPageService.getMyPageTotal(auth.getName()); + } +} diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java new file mode 100644 index 00000000..88525c51 --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java @@ -0,0 +1,43 @@ +package online.partyrun.partyrunbattleservice.domain.mypage.dto; + +import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; +import online.partyrun.partyrunbattleservice.domain.runner.entity.record.RunnerRecord; +import online.partyrun.partyrunbattleservice.domain.single.dto.RunningTimeResponse; +import online.partyrun.partyrunbattleservice.domain.single.entity.Single; + +import java.time.Duration; +import java.util.List; + +public record MyPageTotalResponse(double totalDistance, double averagePace, RunningTimeResponse totalRunningTime) { + + public static MyPageTotalResponse of(String memberId, List battles, List singles) { + final double totalBattleDistance = battles.stream() + .mapToDouble(battle -> battle.getRunnerRecentDistance(memberId)) + .sum(); + + final long totalBattleRunningTime = battles.stream() + .mapToLong(battle -> Duration.between(battle.getCreatedAt(), battle.getRunnerRecentRecord(memberId).getTime()).toSeconds()) + .sum(); + + final double totalSingleDistance = singles.stream() + .mapToDouble(single -> { + final List runnerRecords = single.getRunnerRecords(); + return runnerRecords.get(runnerRecords.size() - 1).getDistance(); + }) + .sum(); + + final int totalSingleRunningTime = singles.stream() + .mapToInt(single -> single.getRunningTime().getTotalSeconds()) + .sum(); + + + final double totalDistance = totalBattleDistance + totalSingleDistance; + final long totalRunningTime = totalBattleRunningTime + totalSingleRunningTime; + + int totalHour = (int) totalRunningTime / 3600; + int totalMinute = (int) (totalRunningTime % 3600) / 60; + int totalSecond = (int) totalRunningTime % 60; + + return new MyPageTotalResponse(totalDistance, totalDistance / totalRunningTime, new RunningTimeResponse(totalHour, totalMinute, totalSecond)); + } +} diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java new file mode 100644 index 00000000..d20105c2 --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java @@ -0,0 +1,29 @@ +package online.partyrun.partyrunbattleservice.domain.mypage.service; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; +import online.partyrun.partyrunbattleservice.domain.battle.repository.BattleRepository; +import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageTotalResponse; +import online.partyrun.partyrunbattleservice.domain.single.entity.Single; +import online.partyrun.partyrunbattleservice.domain.single.repository.SingleRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class MyPageService { + + BattleRepository battleRepository; + SingleRepository singleRepository; + + public MyPageTotalResponse getMyPageTotal(String memberId) { + final List battles = battleRepository.findAllByRunnersIdExceptRunnerRecords(memberId); + final List singles = singleRepository.findAllByRunnerId(memberId); + + return MyPageTotalResponse.of(memberId, battles, singles); + } +} diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/single/entity/RunningTime.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/single/entity/RunningTime.java index e8c1cbfb..728a50d6 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/single/entity/RunningTime.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/single/entity/RunningTime.java @@ -51,4 +51,8 @@ private boolean isNotCorrectMinutes(int minutes) { private boolean isNotCorrectSeconds(int seconds) { return seconds < MIN_TIME || seconds > MAX_TIME; } + + public int getTotalSeconds() { + return hours * 60 * 60 + minutes * 60 + seconds; + } } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepository.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepository.java index dff1676c..8dca4d1c 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepository.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepository.java @@ -3,5 +3,9 @@ import online.partyrun.partyrunbattleservice.domain.single.entity.Single; import org.springframework.data.mongodb.repository.MongoRepository; +import java.util.List; + public interface SingleRepository extends MongoRepository { + + List findAllByRunnerId(String memberId); } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java b/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java deleted file mode 100644 index 7b7d0492..00000000 --- a/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package online.partyrun.partyrunbattleservice.global.config; - -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class RedissonConfig { - - @Bean - public RedissonClient singleRedissonClient(@Value("${spring.data.redis.url}") String redisUrl) { - Config config = new Config(); - config.useSingleServer().setAddress(redisUrl); - return Redisson.create(config); - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a943d980..d6a99bb6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,7 +14,7 @@ spring: data: redis: - url: redis://localhost:16379 + url: redis://localhost:6379/0 cloud: aws: diff --git a/src/test/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepositoryTest.java b/src/test/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepositoryTest.java index 781a8a12..afbe890d 100644 --- a/src/test/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepositoryTest.java +++ b/src/test/java/online/partyrun/partyrunbattleservice/domain/battle/repository/BattleRepositoryTest.java @@ -303,4 +303,22 @@ void returnNull() { ); } } + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class findAllByRunnersIdExceptRunnerRecords는 { + + @BeforeEach + void setUp() { + battleRepository.save(new Battle(1000, List.of(박성우, 박현준), now)); + battleRepository.save(new Battle(1000, List.of(박성우, 노준혁), now)); + } + @Test + @DisplayName("러너가 속한 모든 배틀을 반환한다.") + void returnResult() { + final List battles = battleRepository.findAllByRunnersIdExceptRunnerRecords(박성우.getId()); + + assertThat(battles).hasSize(2); + } + } } diff --git a/src/test/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepositoryTest.java b/src/test/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepositoryTest.java new file mode 100644 index 00000000..f70f17af --- /dev/null +++ b/src/test/java/online/partyrun/partyrunbattleservice/domain/single/repository/SingleRepositoryTest.java @@ -0,0 +1,45 @@ +package online.partyrun.partyrunbattleservice.domain.single.repository; + +import online.partyrun.partyrunbattleservice.domain.runner.entity.record.GpsData; +import online.partyrun.partyrunbattleservice.domain.runner.entity.record.RunnerRecord; +import online.partyrun.partyrunbattleservice.domain.single.entity.RunningTime; +import online.partyrun.partyrunbattleservice.domain.single.entity.Single; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataMongoTest +@DisplayName("SingleRepository") +class SingleRepositoryTest { + + @Autowired + SingleRepository singleRepository; + + String runnerId = "박성우"; + LocalDateTime now = LocalDateTime.now(); + + @Nested + @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) + class findAllByRunnerId는 { + + @Test + @DisplayName("모든 싱글 데이터를 조회한다.") + void returnSingles() { + final List singleData = List.of( + new Single(runnerId, new RunningTime(0, 0, 1), List.of(new RunnerRecord(GpsData.of(1, 1, 1, now), 0))), + new Single(runnerId, new RunningTime(0, 0, 1), List.of(new RunnerRecord(GpsData.of(1, 1, 1, now), 0))) + ); + + singleRepository.saveAll(singleData); + + final List singleResult = singleRepository.findAllByRunnerId(runnerId); + + assertThat(singleResult).hasSize(2); + } + } +} From 86b27c1bebd4d700817eaefdd2e8d727ffdcc4a4 Mon Sep 17 00:00:00 2001 From: Hyeonjun Park <55674648+phjppo0918@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:10:30 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix=20:=20deploy=20=EC=8B=9C=EC=97=90=20tes?= =?UTF-8?q?t-container=20cloud=20=EC=9D=B8=EC=A6=9D=20=EC=95=88=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docker-image-dev.yml | 2 ++ .github/workflows/build-docker-image-prd.yml | 2 ++ .github/workflows/cd-dev.yml | 1 + .github/workflows/cd-prd.yml | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker-image-dev.yml b/.github/workflows/build-docker-image-dev.yml index 61ad4df6..e27c4305 100644 --- a/.github/workflows/build-docker-image-dev.yml +++ b/.github/workflows/build-docker-image-dev.yml @@ -9,6 +9,8 @@ on: required: true DOCKER_BATTLE_IMAGE_NAME: required: true + TC_CLOUD_TOKEN: + required: true jobs: build: diff --git a/.github/workflows/build-docker-image-prd.yml b/.github/workflows/build-docker-image-prd.yml index 90b28f25..26bc9d76 100644 --- a/.github/workflows/build-docker-image-prd.yml +++ b/.github/workflows/build-docker-image-prd.yml @@ -9,6 +9,8 @@ on: required: true PRD_DOCKER_BATTLE_IMAGE_NAME: required: true + TC_CLOUD_TOKEN: + required: true jobs: build: diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml index 41143993..96dfe5cd 100644 --- a/.github/workflows/cd-dev.yml +++ b/.github/workflows/cd-dev.yml @@ -12,6 +12,7 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} DOCKER_BATTLE_IMAGE_NAME: ${{ secrets.DOCKER_BATTLE_IMAGE_NAME }} + TC_CLOUD_TOKEN: ${{ secrets.TC_CLOUD_TOKEN }} deploy: needs: build-back-docker-img diff --git a/.github/workflows/cd-prd.yml b/.github/workflows/cd-prd.yml index 46ba6fd1..1d1be7fd 100644 --- a/.github/workflows/cd-prd.yml +++ b/.github/workflows/cd-prd.yml @@ -12,7 +12,7 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} PRD_DOCKER_BATTLE_IMAGE_NAME: ${{ secrets.PRD_DOCKER_BATTLE_IMAGE_NAME }} - + TC_CLOUD_TOKEN: ${{ secrets.TC_CLOUD_TOKEN }} deploy: needs: build-back-docker-img runs-on: ubuntu-latest From 2ba3319474482d51911effab2125e093ab864822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=9A=B0?= Date: Mon, 9 Oct 2023 19:08:49 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hyeonjun Park --- .github/workflows/build-docker-image-dev.yml | 6 --- .github/workflows/build-docker-image-prd.yml | 7 --- .github/workflows/build.yml | 4 -- .github/workflows/cd-dev.yml | 1 - .github/workflows/cd-prd.yml | 2 +- build.gradle | 2 +- .../mypage/controller/MyPageController.java | 13 +++++- .../mypage/dto/MyPageHistoryResponse.java | 25 +++++++++++ .../mypage/dto/MyPageRunningResponse.java | 45 +++++++++++++++++++ .../domain/mypage/service/MyPageService.java | 13 ++++++ .../global/config/RedissonConfig.java | 19 ++++++++ src/main/resources/application.yml | 2 +- 12 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageRunningResponse.java create mode 100644 src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java diff --git a/.github/workflows/build-docker-image-dev.yml b/.github/workflows/build-docker-image-dev.yml index e27c4305..eca19130 100644 --- a/.github/workflows/build-docker-image-dev.yml +++ b/.github/workflows/build-docker-image-dev.yml @@ -9,8 +9,6 @@ on: required: true DOCKER_BATTLE_IMAGE_NAME: required: true - TC_CLOUD_TOKEN: - required: true jobs: build: @@ -25,10 +23,6 @@ jobs: with: java-version: '17' distribution: 'temurin' - - name: Setup Testcontainers Cloud Client - uses: atomicjar/testcontainers-cloud-setup-action@v1 - with: - token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@v2.1.0 diff --git a/.github/workflows/build-docker-image-prd.yml b/.github/workflows/build-docker-image-prd.yml index 26bc9d76..a05e4d99 100644 --- a/.github/workflows/build-docker-image-prd.yml +++ b/.github/workflows/build-docker-image-prd.yml @@ -9,8 +9,6 @@ on: required: true PRD_DOCKER_BATTLE_IMAGE_NAME: required: true - TC_CLOUD_TOKEN: - required: true jobs: build: @@ -25,11 +23,6 @@ jobs: with: java-version: '17' distribution: 'temurin' - - name: Setup Testcontainers Cloud Client - uses: atomicjar/testcontainers-cloud-setup-action@v1 - with: - token: ${{ secrets.TC_CLOUD_TOKEN }} - - name: Login to Docker Hub uses: docker/login-action@v2.1.0 with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 758f7c69..f0d315e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,6 @@ jobs: with: java-version: "17" distribution: "temurin" - - name: Setup Testcontainers Cloud Client - uses: atomicjar/testcontainers-cloud-setup-action@v1 - with: - token: ${{ secrets.TC_CLOUD_TOKEN }} - name: Grant execute permission for gradlew run: chmod +x ./gradlew - name: Build with Gradle diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml index 96dfe5cd..41143993 100644 --- a/.github/workflows/cd-dev.yml +++ b/.github/workflows/cd-dev.yml @@ -12,7 +12,6 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} DOCKER_BATTLE_IMAGE_NAME: ${{ secrets.DOCKER_BATTLE_IMAGE_NAME }} - TC_CLOUD_TOKEN: ${{ secrets.TC_CLOUD_TOKEN }} deploy: needs: build-back-docker-img diff --git a/.github/workflows/cd-prd.yml b/.github/workflows/cd-prd.yml index 1d1be7fd..46ba6fd1 100644 --- a/.github/workflows/cd-prd.yml +++ b/.github/workflows/cd-prd.yml @@ -12,7 +12,7 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} PRD_DOCKER_BATTLE_IMAGE_NAME: ${{ secrets.PRD_DOCKER_BATTLE_IMAGE_NAME }} - TC_CLOUD_TOKEN: ${{ secrets.TC_CLOUD_TOKEN }} + deploy: needs: build-back-docker-img runs-on: ubuntu-latest diff --git a/build.gradle b/build.gradle index 7532ce39..54220ba2 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,7 @@ dependencies { implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs' implementation 'com.github.maricn:logback-slack-appender:1.6.1' - testImplementation 'com.github.SWM-KAWAI-MANS:test-manager:1.1.0' + testImplementation 'com.github.SWM-KAWAI-MANS:test-manager:1.0.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java index 02da8aef..f76a30dc 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/controller/MyPageController.java @@ -3,6 +3,7 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageHistoryResponse; import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageTotalResponse; import online.partyrun.partyrunbattleservice.domain.mypage.service.MyPageService; import online.partyrun.partyrunbattleservice.global.logging.Logging; @@ -20,8 +21,18 @@ public class MyPageController { MyPageService myPageService; - @GetMapping("/total") + @GetMapping("total") public MyPageTotalResponse getMyPageTotalResponse(Authentication auth) { return myPageService.getMyPageTotal(auth.getName()); } + + @GetMapping("battles") + public MyPageHistoryResponse getMyPageBattleResponse(Authentication auth) { + return myPageService.getMyPageBattle(auth.getName()); + } + + @GetMapping("singles") + public MyPageHistoryResponse getMyPageSingleResponse(Authentication auth) { + return myPageService.getMyPageSingle(auth.getName()); + } } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java new file mode 100644 index 00000000..ba33c402 --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java @@ -0,0 +1,25 @@ +package online.partyrun.partyrunbattleservice.domain.mypage.dto; + +import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; +import online.partyrun.partyrunbattleservice.domain.single.entity.Single; + +import java.util.List; + +public record MyPageHistoryResponse(List history) { + + public static MyPageHistoryResponse ofBattles(List battles, String memberId) { + return new MyPageHistoryResponse( + battles.stream() + .map(battle -> MyPageRunningResponse.of(battle, memberId)) + .toList() + ); + } + + public static MyPageHistoryResponse ofSingle(List singles) { + return new MyPageHistoryResponse( + singles.stream() + .map(MyPageRunningResponse::from) + .toList() + ); + } +} diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageRunningResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageRunningResponse.java new file mode 100644 index 00000000..741d2ab8 --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageRunningResponse.java @@ -0,0 +1,45 @@ +package online.partyrun.partyrunbattleservice.domain.mypage.dto; + +import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; +import online.partyrun.partyrunbattleservice.domain.runner.entity.record.RunnerRecord; +import online.partyrun.partyrunbattleservice.domain.single.dto.RunningTimeResponse; +import online.partyrun.partyrunbattleservice.domain.single.entity.RunningTime; +import online.partyrun.partyrunbattleservice.domain.single.entity.Single; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +public record MyPageRunningResponse(String id, + LocalDateTime startTime, + RunningTimeResponse runningTime, + double distance) { + + public static MyPageRunningResponse of(Battle battle, String memberId) { + final RunnerRecord runnerRecentRecord = battle.getRunnerRecentRecord(memberId); + + final LocalDateTime startTime = battle.getStartTime(); + final LocalDateTime endTime = runnerRecentRecord.getTime(); + final Duration duration = Duration.between(startTime, endTime); + + return new MyPageRunningResponse( + battle.getId(), + startTime, + new RunningTimeResponse(duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart()), + runnerRecentRecord.getDistance() + ); + } + + public static MyPageRunningResponse from(Single single) { + final RunningTime runningTime = single.getRunningTime(); + + final List runnerRecords = single.getRunnerRecords(); + + return new MyPageRunningResponse( + single.getId(), + runnerRecords.get(0).getTime(), + new RunningTimeResponse(runningTime.getHours(), runningTime.getMinutes(), runningTime.getSeconds()), + runnerRecords.get(runnerRecords.size() - 1).getDistance() + ); + } +} diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java index d20105c2..9fb87273 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/service/MyPageService.java @@ -5,6 +5,7 @@ import lombok.experimental.FieldDefaults; import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; import online.partyrun.partyrunbattleservice.domain.battle.repository.BattleRepository; +import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageHistoryResponse; import online.partyrun.partyrunbattleservice.domain.mypage.dto.MyPageTotalResponse; import online.partyrun.partyrunbattleservice.domain.single.entity.Single; import online.partyrun.partyrunbattleservice.domain.single.repository.SingleRepository; @@ -26,4 +27,16 @@ public MyPageTotalResponse getMyPageTotal(String memberId) { return MyPageTotalResponse.of(memberId, battles, singles); } + + public MyPageHistoryResponse getMyPageBattle(String memberId) { + final List battles = battleRepository.findAllByRunnersIdExceptRunnerRecords(memberId); + + return MyPageHistoryResponse.ofBattles(battles, memberId); + } + + public MyPageHistoryResponse getMyPageSingle(String memberId) { + final List singles = singleRepository.findAllByRunnerId(memberId); + + return MyPageHistoryResponse.ofSingle(singles); + } } diff --git a/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java b/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java new file mode 100644 index 00000000..9b87a829 --- /dev/null +++ b/src/main/java/online/partyrun/partyrunbattleservice/global/config/RedissonConfig.java @@ -0,0 +1,19 @@ +package online.partyrun.partyrunbattleservice.global.config; + + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + @Bean + public RedissonClient singleRedissonClient(@Value("${spring.data.redis.url}") String redisUrl) { + Config config = new Config(); + config.useSingleServer().setAddress(redisUrl); + return Redisson.create(config); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d6a99bb6..a943d980 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,7 +14,7 @@ spring: data: redis: - url: redis://localhost:6379/0 + url: redis://localhost:16379 cloud: aws: From 0bffcb96a070889d9e0f6d219a20044114e798ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=9A=B0?= Date: Tue, 10 Oct 2023 15:52:39 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EA=B8=B0=EB=A1=9D=EC=9D=B4=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20=EC=9D=91=EB=8B=B5=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/mypage/dto/MyPageTotalResponse.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java index 88525c51..7c1b33db 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java @@ -11,6 +11,10 @@ public record MyPageTotalResponse(double totalDistance, double averagePace, RunningTimeResponse totalRunningTime) { public static MyPageTotalResponse of(String memberId, List battles, List singles) { + if (battles.isEmpty() && singles.isEmpty()) { + return new MyPageTotalResponse(0, 0, new RunningTimeResponse(0, 0, 0)); + } + final double totalBattleDistance = battles.stream() .mapToDouble(battle -> battle.getRunnerRecentDistance(memberId)) .sum(); From cecd258b8f99e05f7af82b332276e5d344d7590b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=9A=B0?= Date: Wed, 11 Oct 2023 16:12:18 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=EA=B8=B0=EB=A1=9D=EC=9D=B4=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EB=B9=88=20=EA=B8=B0?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=98=ED=99=98=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/mypage/dto/MyPageHistoryResponse.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java index ba33c402..d3f76899 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageHistoryResponse.java @@ -3,11 +3,17 @@ import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; import online.partyrun.partyrunbattleservice.domain.single.entity.Single; +import java.util.Collections; import java.util.List; public record MyPageHistoryResponse(List history) { + private static final MyPageHistoryResponse EMPTY_HISTORY = new MyPageHistoryResponse(Collections.emptyList()); public static MyPageHistoryResponse ofBattles(List battles, String memberId) { + if (battles.isEmpty()) { + return EMPTY_HISTORY; + } + return new MyPageHistoryResponse( battles.stream() .map(battle -> MyPageRunningResponse.of(battle, memberId)) @@ -16,6 +22,9 @@ public static MyPageHistoryResponse ofBattles(List battles, String membe } public static MyPageHistoryResponse ofSingle(List singles) { + if (singles.isEmpty()) { + return EMPTY_HISTORY; + } return new MyPageHistoryResponse( singles.stream() .map(MyPageRunningResponse::from) From c2990494974d4786ec12726431992b5877558f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=9A=B0?= Date: Wed, 11 Oct 2023 16:24:25 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8B=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EB=A7=8C=20=ED=95=98=EA=B3=A0=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/dto/MyPageTotalResponse.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java index 7c1b33db..20dd9def 100644 --- a/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java +++ b/src/main/java/online/partyrun/partyrunbattleservice/domain/mypage/dto/MyPageTotalResponse.java @@ -2,6 +2,7 @@ import online.partyrun.partyrunbattleservice.domain.battle.entity.Battle; import online.partyrun.partyrunbattleservice.domain.runner.entity.record.RunnerRecord; +import online.partyrun.partyrunbattleservice.domain.runner.exception.InvalidRecentRunnerRecordException; import online.partyrun.partyrunbattleservice.domain.single.dto.RunningTimeResponse; import online.partyrun.partyrunbattleservice.domain.single.entity.Single; @@ -16,11 +17,27 @@ public static MyPageTotalResponse of(String memberId, List battles, List } final double totalBattleDistance = battles.stream() - .mapToDouble(battle -> battle.getRunnerRecentDistance(memberId)) + .mapToDouble( + battle -> { + try { + return battle.getRunnerRecentDistance(memberId); + } catch (InvalidRecentRunnerRecordException exception) { + return 0; + } + } + ) .sum(); final long totalBattleRunningTime = battles.stream() - .mapToLong(battle -> Duration.between(battle.getCreatedAt(), battle.getRunnerRecentRecord(memberId).getTime()).toSeconds()) + .mapToLong( + battle -> { + try { + return Duration.between(battle.getCreatedAt(), battle.getRunnerRecentRecord(memberId).getTime()).toSeconds(); + } catch (InvalidRecentRunnerRecordException exception) { + return 0; + } + } + ) .sum(); final double totalSingleDistance = singles.stream() @@ -40,7 +57,7 @@ public static MyPageTotalResponse of(String memberId, List battles, List int totalHour = (int) totalRunningTime / 3600; int totalMinute = (int) (totalRunningTime % 3600) / 60; - int totalSecond = (int) totalRunningTime % 60; + int totalSecond = (int) totalRunningTime % 60; return new MyPageTotalResponse(totalDistance, totalDistance / totalRunningTime, new RunningTimeResponse(totalHour, totalMinute, totalSecond)); }