Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
redcarrot1 committed Apr 1, 2024
2 parents 9c6b72c + 5fc0ed3 commit 0e149a9
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
import com.playkuround.playkuroundserver.domain.score.domain.ScoreType;
import com.playkuround.playkuroundserver.domain.user.dao.UserRepository;
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;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class AdventureService {
Expand Down Expand Up @@ -63,7 +66,8 @@ private void saveAdventure(User user, Landmark landmark, ScoreType scoreType, lo
}

private void updateLandmarkHighestScore(User user, Landmark landmark) {
long sumScore = adventureRepository.getSumScoreByUserAndLandmark(user, landmark);
LocalDateTime monthStartDateTime = DateTimeUtils.getMonthStartDateTime();
long sumScore = adventureRepository.getSumScoreByUserAndLandmark(user, landmark, monthStartDateTime);
landmark.updateFirstUser(user, sumScore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand All @@ -17,23 +18,29 @@ 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 " +
"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);
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 " +
"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);
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}")
long getSumScoreByUserAndLandmark(@Param(value = "user") User user, @Param(value = "landmark") Landmark landmark);
@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 countByUserAndLandmark(User user, Landmark requestSaveLandmark);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.playkuround.playkuroundserver.domain.badge.application.specialday_badge;

import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.global.util.DateUtils;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;

import java.util.Set;

Expand All @@ -13,7 +13,7 @@ public class ArborDayBadge implements SpecialDayBadge {
@Override
public boolean supports(Set<BadgeType> userBadgeSet) {
BadgeType badgeType = getBadgeType();
return DateUtils.isTodayArborDay() && !userBadgeSet.contains(badgeType);
return DateTimeUtils.isTodayArborDay() && !userBadgeSet.contains(badgeType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.playkuround.playkuroundserver.domain.badge.application.specialday_badge;

import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.global.util.DateUtils;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;

import java.util.Set;

Expand All @@ -13,7 +13,7 @@ public class ChildrenDayBadge implements SpecialDayBadge {
@Override
public boolean supports(Set<BadgeType> userBadgeSet) {
BadgeType badgeType = getBadgeType();
return DateUtils.isTodayChildrenDay() && !userBadgeSet.contains(badgeType);
return DateTimeUtils.isTodayChildrenDay() && !userBadgeSet.contains(badgeType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.playkuround.playkuroundserver.domain.badge.application.specialday_badge;

import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.global.util.DateUtils;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;

import java.util.Set;

Expand All @@ -13,7 +13,7 @@ public class DuckDayBadge implements SpecialDayBadge {
@Override
public boolean supports(Set<BadgeType> userBadgeSet) {
BadgeType badgeType = getBadgeType();
return DateUtils.isTodayDuckDay() && !userBadgeSet.contains(badgeType);
return DateTimeUtils.isTodayDuckDay() && !userBadgeSet.contains(badgeType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.playkuround.playkuroundserver.domain.badge.application.specialday_badge;

import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.global.util.DateUtils;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;

import java.util.Set;

Expand All @@ -13,7 +13,7 @@ public class FoundationDayBadge implements SpecialDayBadge {
@Override
public boolean supports(Set<BadgeType> userBadgeSet) {
BadgeType badgeType = getBadgeType();
return DateUtils.isTodayFoundationDay() && !userBadgeSet.contains(badgeType);
return DateTimeUtils.isTodayFoundationDay() && !userBadgeSet.contains(badgeType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.playkuround.playkuroundserver.domain.badge.application.specialday_badge;

import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.global.util.DateUtils;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;

import java.util.Set;

Expand All @@ -13,7 +13,7 @@ public class WhiteDayBadge implements SpecialDayBadge {
@Override
public boolean supports(Set<BadgeType> userBadgeSet) {
BadgeType badgeType = getBadgeType();
return DateUtils.isTodayWhiteDay() && !userBadgeSet.contains(badgeType);
return DateTimeUtils.isTodayWhiteDay() && !userBadgeSet.contains(badgeType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
public interface BadgeRepository extends JpaRepository<Badge, Long> {
List<Badge> findByUser(User user);

boolean existsByUserAndBadgeType(User user, BadgeType conqueror);
boolean existsByUserAndBadgeType(User user, BadgeType badgeType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ public void updateFirstUser(User user, long score) {
this.highestScore = score;
}
}

public void deleteRank() {
this.firstUser = null;
this.highestScore = 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import com.playkuround.playkuroundserver.domain.score.dto.RankAndScore;
import com.playkuround.playkuroundserver.domain.score.dto.response.ScoreRankingResponse;
import com.playkuround.playkuroundserver.domain.user.domain.User;
import com.playkuround.playkuroundserver.global.util.DateTimeUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Service
Expand All @@ -20,12 +22,13 @@ public class LandmarkRankService {
@Transactional(readOnly = true)
public ScoreRankingResponse getRankTop100ByLandmark(User user, Long landmarkId) {
ScoreRankingResponse response = ScoreRankingResponse.createEmptyResponse();
LocalDateTime monthStartDateTime = DateTimeUtils.getMonthStartDateTime();

List<NicknameAndScore> nicknameAndScores = adventureRepository.findRankTop100DescByLandmarkId(landmarkId);
List<NicknameAndScore> nicknameAndScores = adventureRepository.findRankTop100DescByLandmarkId(landmarkId, monthStartDateTime);
nicknameAndScores.forEach(nicknameAndScore ->
response.addRank(nicknameAndScore.nickname(), nicknameAndScore.score()));

RankAndScore rankAndScore = adventureRepository.findMyRankByLandmarkId(user, landmarkId)
RankAndScore rankAndScore = adventureRepository.findMyRankByLandmarkId(user, landmarkId, monthStartDateTime)
.orElseGet(() -> new RankAndScore(0, 0));
response.setMyRank(rankAndScore.ranking(), rankAndScore.score());
return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.playkuround.playkuroundserver.domain.common.AppVersion;
import com.playkuround.playkuroundserver.domain.common.SystemCheck;
import com.playkuround.playkuroundserver.domain.user.api.request.ManualBadgeSaveRequest;
import com.playkuround.playkuroundserver.domain.user.application.NewMonthUpdateService;
import com.playkuround.playkuroundserver.global.common.response.ApiResponse;
import com.playkuround.playkuroundserver.global.util.ApiUtils;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -22,9 +23,10 @@
public class AdminApi {

private final BadgeService badgeService;
private final NewMonthUpdateService newMonthUpdateService;

@PostMapping("/app-version")
@Operation(summary = "앱 버전 올리기(관리자모드)", description = "앱 버전을 올립니다. 이전버전 사용자에게 공지 메시지를 보냅니다.",
@Operation(summary = "앱 버전 올리기", description = "앱 버전을 올립니다. 이전버전 사용자에게 공지 메시지를 보냅니다.",
parameters = {
@Parameter(name = "version", description = "최신 앱 버전", example = "2.0.2", required = true),
@Parameter(name = "os", description = "모바일 운영체제(android 또는 ios)", example = "android", required = true)
Expand All @@ -37,20 +39,32 @@ public ApiResponse<Void> updateAppVersion(@RequestParam("version") String appVer
}

@PostMapping("/system-available")
@Operation(summary = "시스템 점검 유무 변경하기(관리자모드)", description = "시스템 점검 유무를 변경합니다.")
@Operation(summary = "시스템 점검 유무 변경하기", description = "시스템 점검 유무를 변경합니다.")
public ApiResponse<Void> changeSystemAvailable(@RequestParam("available") boolean appVersion) {
SystemCheck.changeSystemAvailable(appVersion);
return ApiUtils.success(null);
}

@PostMapping("badges/manual")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "수동 뱃지 등록(관리자권한)", description = "수동으로 뱃지를 등록합니다. 이미 획득한 뱃지였다면 false를 반환합니다. " +
@Operation(summary = "수동 뱃지 등록", description = "수동으로 뱃지를 등록합니다. 이미 획득한 뱃지였다면 false를 반환합니다. " +
"설정에 따라 개인 메시지로 등록할 수 있습니다.")
public ApiResponse<Boolean> saveManualBadge(@RequestBody @Valid ManualBadgeSaveRequest request) {
BadgeType badgeType = BadgeType.valueOf(request.getBadge());
boolean response = badgeService.saveManualBadge(request.getUserEmail(), badgeType, request.isRegisterMessage());
return ApiUtils.success(response);
}

@PostMapping("new-month-update")
@Operation(summary = "월(month) 업데이트",
description = "== 해당 API는 아래의 작업을 포함합니다. ==<br>" +
"1. MONTHLY_RANKING_1, MONTHLY_RANKING_2, MONTHLY_RANKING_3 부여(+메시지 부여)<br>" +
"2. 랜드마크별 랭킹 초기화<br>" +
"3. 유저별 최고 랭킹, 스코어 기록<br>" +
"4. 종합 랭킹 초기화")
public ApiResponse<Void> updateNewMonth() {
newMonthUpdateService.updateMonth();
return ApiUtils.success(null);
}

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

import com.playkuround.playkuroundserver.domain.badge.dao.BadgeRepository;
import com.playkuround.playkuroundserver.domain.badge.domain.Badge;
import com.playkuround.playkuroundserver.domain.badge.domain.BadgeType;
import com.playkuround.playkuroundserver.domain.landmark.dao.LandmarkRepository;
import com.playkuround.playkuroundserver.domain.landmark.domain.Landmark;
import com.playkuround.playkuroundserver.domain.user.dao.UserRepository;
import com.playkuround.playkuroundserver.domain.user.domain.User;
import com.playkuround.playkuroundserver.domain.user.exception.UserNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static java.lang.Math.min;

@Service
@RequiredArgsConstructor
public class NewMonthUpdateService {

private final String redisSetKey = "ranking";
private final RedisTemplate<String, String> redisTemplate;
private final UserRepository userRepository;
private final BadgeRepository badgeRepository;
private final LandmarkRepository landmarkRepository;

@Transactional
public void updateMonth() {
List<RankData> totalRankDateList = getTotalRankDateList();
saveBadge(totalRankDateList);
updateUserHighestRank(totalRankDateList);
updateLandmarkRank();
deleteAllTotalScoreRepository();
}

private List<RankData> getTotalRankDateList() {
Set<ZSetOperations.TypedTuple<String>> totalScoreTypedTuples = getTotalScoreTypedTuples();

List<RankData> rankDataList = new ArrayList<>();
long rank = 1;
for (ZSetOperations.TypedTuple<String> typedTuple : totalScoreTypedTuples) {
if (typedTuple.getValue() == null || typedTuple.getScore() == null) {
throw new RuntimeException("typedTuple value or score is null");
}
rankDataList.add(new RankData(typedTuple.getValue(), rank, typedTuple.getScore().longValue()));
rank++;
}
return rankDataList;
}

private Set<ZSetOperations.TypedTuple<String>> getTotalScoreTypedTuples() {
ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
Long zSetSize = zSetOperations.size(redisSetKey);
if (zSetSize == null) {
throw new RuntimeException("zSetSize is null");
}

Set<ZSetOperations.TypedTuple<String>> typedTuples
= zSetOperations.reverseRangeWithScores(redisSetKey, 0, zSetSize - 1);
if (typedTuples == null) {
throw new RuntimeException("typedTuples is null");
}
return typedTuples;
}

private void saveBadge(List<RankData> rankDataList) {
BadgeType[] monthlyRankingBadges = {BadgeType.MONTHLY_RANKING_1, BadgeType.MONTHLY_RANKING_2, BadgeType.MONTHLY_RANKING_3};
int maxIter = min(monthlyRankingBadges.length, rankDataList.size());

for (int i = 0; i < maxIter; i++) {
BadgeType badgeType = monthlyRankingBadges[i];
RankData rankData = rankDataList.get(i);

User user = userRepository.findByEmail(rankData.email)
.orElseThrow(UserNotFoundException::new);
if (!badgeRepository.existsByUserAndBadgeType(user, badgeType)) {
Badge badge = Badge.createBadge(user, badgeType);
badgeRepository.save(badge);
user.addNewBadgeNotification(badgeType);
}
}
}

private void updateUserHighestRank(List<RankData> rankDataList) {
List<String> emailList = rankDataList.stream()
.map(RankData::email)
.toList();
List<User> users = userRepository.findByEmailIn(emailList);
Map<String, User> mapEmailToUser = users.stream()
.collect(Collectors.toMap(User::getEmail, user -> user));

for (RankData rankData : rankDataList) {
User user = mapEmailToUser.get(rankData.email);
if (user == null) {
throw new UserNotFoundException();
}
user.updateHighestRank(rankData.rank, rankData.score);
}
}

private void updateLandmarkRank() {
landmarkRepository.findAll()
.forEach(Landmark::deleteRank);
}

private void deleteAllTotalScoreRepository() {
redisTemplate.delete(redisSetKey);
}

private record RankData(String email, long rank, long score) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
"from User u " +
"where u.email in :emails")
List<EmailAndNickname> findNicknameByEmailIn(List<String> emails);

List<User> findByEmailIn(List<String> emailList);
}
Loading

0 comments on commit 0e149a9

Please sign in to comment.