Skip to content

Commit

Permalink
Merge pull request #108 from playkuround/refactor/adventure
Browse files Browse the repository at this point in the history
Refactor/adventure
  • Loading branch information
redcarrot1 authored Aug 13, 2024
2 parents 698da7a + 7c5745a commit b5a2c58
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import com.playkuround.playkuroundserver.domain.user.domain.User;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;
import com.playkuround.playkuroundserver.global.util.Location;
import com.playkuround.playkuroundserver.global.util.LocationDistanceUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -49,16 +48,15 @@ public NewlyRegisteredBadge saveAdventure(AdventureSaveDto adventureSaveDto) {
}

private void validateLocation(Landmark landmark, Location location) {
Location locationOfLandmark = new Location(landmark.getLatitude(), landmark.getLongitude());
double distance = LocationDistanceUtils.distance(locationOfLandmark, location);
if (distance > landmark.getRecognitionRadius()) {
if (!landmark.isInRecognitionRadius(location)) {
throw new InvalidLandmarkLocationException();
}
}

private void updateUserScore(User user, ScoreType scoreType, long score) {
totalScoreService.incrementTotalScore(user, score);
user.getHighestScore().updateGameHighestScore(scoreType, score);
user.getHighestScore()
.updateGameHighestScore(scoreType, score);
userRepository.save(user);
}

Expand All @@ -69,7 +67,7 @@ private void saveAdventure(User user, Landmark landmark, ScoreType scoreType, lo

private void updateLandmarkHighestScore(User user, Landmark landmark) {
LocalDateTime monthStartDateTime = DateTimeUtils.getMonthStartDateTime(dateTimeService.getLocalDateNow());
long sumScore = adventureRepository.getSumScoreByUserAndLandmark(user, landmark, monthStartDateTime);
long sumScore = adventureRepository.getSumScoreByUserAndLandmarkAfter(user, landmark, monthStartDateTime);
landmark.updateFirstUser(user, sumScore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,30 @@

public interface AdventureRepository extends JpaRepository<Adventure, Long> {

@Query(value =
"SELECT new com.playkuround.playkuroundserver.domain.score.dto.NicknameAndScore(a.user.nickname, cast(SUM(a.score) as integer)) " +
"FROM Adventure a " +
"where a.landmark.id=:landmark AND a.createdAt >= :from " +
"GROUP BY a.user.id " +
"ORDER BY SUM(a.score) DESC, a.user.nickname DESC " +
"LIMIT 100")
@Query("SELECT new com.playkuround.playkuroundserver.domain.score.dto.NicknameAndScore(a.user.nickname, cast(SUM(a.score) as integer)) " +
"FROM Adventure a " +
"where a.landmark.id=:landmark AND a.createdAt >= :from " +
"GROUP BY a.user.id " +
"ORDER BY SUM(a.score) DESC, a.user.nickname DESC " +
"LIMIT 100")
List<NicknameAndScore> findRankTop100DescByLandmarkId(@Param(value = "landmark") Long landmarkId, @Param(value = "from") LocalDateTime from);

@Query(value =
"SELECT new com.playkuround.playkuroundserver.domain.score.dto.RankAndScore(cast(user_rank as integer), cast(score as integer)) FROM " +
"(SELECT a.user.id as user_id, (RANK() over (order by SUM(a.score) desc)) as user_rank, SUM(a.score) as score " +
"FROM Adventure a " +
"where a.landmark.id=:landmark AND a.createdAt >= :from " +
"GROUP BY a.user.id) " +
"where user_id=:#{#user.id}")
@Query("SELECT new com.playkuround.playkuroundserver.domain.score.dto.RankAndScore(cast(user_rank as integer), cast(score as integer)) FROM " +
"(SELECT a.user.id as user_id, (RANK() over (order by SUM(a.score) desc)) as user_rank, SUM(a.score) as score " +
"FROM Adventure a " +
"where a.landmark.id=:landmark AND a.createdAt >= :from " +
"GROUP BY a.user.id) " +
"where user_id=:#{#user.id}")
Optional<RankAndScore> findMyRankByLandmarkId(@Param(value = "user") User user,
@Param(value = "landmark") Long landmarkId,
@Param(value = "from") LocalDateTime from);

@Query("SELECT SUM(a.score) " +
"FROM Adventure a " +
"WHERE a.user.id=:#{#user.id} AND a.landmark.id=:#{#landmark.id} AND a.createdAt >= :from")
long getSumScoreByUserAndLandmark(@Param(value = "user") User user,
@Param(value = "landmark") Landmark landmark,
@Param(value = "from") LocalDateTime from);
long getSumScoreByUserAndLandmarkAfter(@Param(value = "user") User user,
@Param(value = "landmark") Landmark landmark,
@Param(value = "from") LocalDateTime from);

long countByUserAndLandmark(User user, Landmark requestSaveLandmark);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.playkuround.playkuroundserver.domain.landmark.domain;

import com.playkuround.playkuroundserver.domain.user.domain.User;
import com.playkuround.playkuroundserver.global.util.Location;
import com.playkuround.playkuroundserver.global.util.LocationDistanceUtils;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -46,4 +48,11 @@ public void deleteRank() {
this.firstUser = null;
this.highestScore = 0;
}

public boolean isInRecognitionRadius(Location location) {
Location locationOfLandmark = new Location(latitude, longitude);
double distance = LocationDistanceUtils.distance(locationOfLandmark, location);

return distance <= recognitionRadius;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.playkuround.playkuroundserver;

import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(properties = "spring.profiles.active=test")
@EntityScan(basePackages = {"com.playkuround.playkuroundserver.domain"})
public @interface IntegrationServiceTest {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.playkuround.playkuroundserver.domain.adventure.application;

import com.playkuround.playkuroundserver.IntegrationServiceTest;
import com.playkuround.playkuroundserver.TestUtil;
import com.playkuround.playkuroundserver.domain.adventure.dao.AdventureRepository;
import com.playkuround.playkuroundserver.domain.adventure.domain.Adventure;
import com.playkuround.playkuroundserver.domain.adventure.dto.AdventureSaveDto;
import com.playkuround.playkuroundserver.domain.adventure.exception.InvalidLandmarkLocationException;
import com.playkuround.playkuroundserver.domain.badge.dao.BadgeRepository;
import com.playkuround.playkuroundserver.domain.badge.dto.NewlyRegisteredBadge;
import com.playkuround.playkuroundserver.domain.landmark.dao.LandmarkRepository;
import com.playkuround.playkuroundserver.domain.landmark.domain.Landmark;
import com.playkuround.playkuroundserver.domain.landmark.exception.LandmarkNotFoundException;
import com.playkuround.playkuroundserver.domain.score.domain.ScoreType;
import com.playkuround.playkuroundserver.domain.user.dao.UserRepository;
import com.playkuround.playkuroundserver.domain.user.domain.Major;
import com.playkuround.playkuroundserver.domain.user.domain.User;
import com.playkuround.playkuroundserver.global.util.Location;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.*;

@IntegrationServiceTest
@Sql(scripts = {"/data-mysql.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
class AdventureServiceTest {

private final String redisSetKey = "ranking";
@Autowired
private BadgeRepository badgeRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private AdventureRepository adventureRepository;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private LandmarkRepository landmarkRepository;
@Autowired
private AdventureService adventureService;

@AfterEach
void clean() {
badgeRepository.deleteAllInBatch();
adventureRepository.deleteAllInBatch();
landmarkRepository.deleteAllInBatch();
userRepository.deleteAllInBatch();
redisTemplate.delete(redisSetKey);
}

@Test
@DisplayName("탐험을 하게 되면 total score 증가, adventure 저장, 랜드마크별 최고기록과 유저별 게임 최고기록이 업데이트 된다.")
void saveAdventure_1() {
// given
User user = TestUtil.createUser();

Landmark landmark = landmarkRepository.findById(3L).get();
Location location = new Location(landmark.getLatitude(), landmark.getLongitude());
AdventureSaveDto adventureSaveDto = new AdventureSaveDto(user, landmark.getId(), location, 100, ScoreType.BOOK);

// when
NewlyRegisteredBadge newlyRegisteredBadge = adventureService.saveAdventure(adventureSaveDto);

// then
// Total Score 저장 및 최고 점수 갱신
List<User> users = userRepository.findAll();
assertThat(users).hasSize(1)
.extracting("highestScore.highestCardScore")
.containsExactly(adventureSaveDto.score());

// adventure 저장
List<Adventure> adventures = adventureRepository.findAll();
assertThat(adventures).hasSize(1)
.extracting("score", "scoreType", "user.id", "landmark.id")
.containsOnly(tuple(adventureSaveDto.score(), adventureSaveDto.scoreType(), users.get(0).getId(), landmark.getId()));

// 랜드마크 최고 점수 갱신
Optional<Landmark> optionalLandmark = landmarkRepository.findById(landmark.getId());
assertThat(optionalLandmark).isPresent()
.get()
.extracting("highestScore", "firstUser.id")
.contains(adventureSaveDto.score(), users.get(0).getId());
}

@Test
@DisplayName("랜드마크가 존재하지 않으면 에러가 발생한다.")
void saveAdventure_2() {
// given
User user = TestUtil.createUser();

Landmark landmark = landmarkRepository.findById(3L).get();
Location location = new Location(landmark.getLatitude(), landmark.getLongitude());
AdventureSaveDto adventureSaveDto = new AdventureSaveDto(user, -1L, location, 100, ScoreType.BOOK);

// expected
assertThatThrownBy(() -> adventureService.saveAdventure(adventureSaveDto))
.isInstanceOf(LandmarkNotFoundException.class)
.hasMessage("-1의 랜드마크 조회에 실패하였습니다.");

List<Adventure> adventures = adventureRepository.findAll();
assertThat(adventures).isEmpty();
}

@Test
@DisplayName("인식 거리 밖에 있으면 에러가 발생한다.")
void saveAdventure_3() {
// given
User user = TestUtil.createUser();

Landmark landmark = landmarkRepository.findById(3L).get();
Location location = new Location(0, 0);
AdventureSaveDto adventureSaveDto = new AdventureSaveDto(user, landmark.getId(), location, 100, ScoreType.BOOK);

// when
assertThatThrownBy(() -> adventureService.saveAdventure(adventureSaveDto))
.isInstanceOf(InvalidLandmarkLocationException.class)
.hasMessage("현재 위치와 랜드마크 위치가 너무 멉니다.");

List<Adventure> adventures = adventureRepository.findAll();
assertThat(adventures).isEmpty();
}

@Test
@DisplayName("랜드마크 최고 기록자가 아니라면, 랜드마크 랭킹 1위는 업데이트 되지 않는다.")
void saveAdventure_4() {
// given
User user = TestUtil.createUser();
User otherUser = TestUtil.createUser("other@test.com", "other", Major.건축학부);
userRepository.saveAll(List.of(user, otherUser));

long highestScore = 1000;
Landmark landmark = landmarkRepository.findById(3L).get();
landmark.updateFirstUser(otherUser, highestScore);
landmarkRepository.save(landmark); // update

Location location = new Location(landmark.getLatitude(), landmark.getLongitude());
AdventureSaveDto adventureSaveDto = new AdventureSaveDto(user, landmark.getId(), location, 100, ScoreType.BOOK);

// when
NewlyRegisteredBadge newlyRegisteredBadge = adventureService.saveAdventure(adventureSaveDto);

// then
Optional<Landmark> optionalLandmark = landmarkRepository.findById(landmark.getId());
assertThat(optionalLandmark).isPresent()
.get()
.extracting("highestScore", "firstUser.id")
.contains(highestScore, otherUser.getId());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,12 @@ void authEmailInvalidate() {
@DisplayName("authEmail이 만료되면 AuthCodeExpiredException 발생")
void authEmailExpired() {
// given
AuthEmail authEmail = AuthEmail.createAuthEmail("target", "code", LocalDateTime.now().minusDays(1));
LocalDateTime now = LocalDateTime.of(2024, 7, 1, 0, 0);
AuthEmail authEmail = AuthEmail.createAuthEmail("target", "code", now.minusDays(1));
when(authEmailRepository.findFirstByTargetOrderByCreatedAtDesc(any(String.class)))
.thenReturn(Optional.of(authEmail));
when(dateTimeService.getLocalDateTimeNow())
.thenReturn(LocalDateTime.of(2024, 7, 1, 0, 0));
.thenReturn(now);

// expected
assertThatThrownBy(() -> authEmailVerifyService.verifyAuthEmail("code", "target"))
Expand Down

0 comments on commit b5a2c58

Please sign in to comment.