diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/service/LevelService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/service/LevelService.java index de885ac9..15df3e2d 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/service/LevelService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/service/LevelService.java @@ -1,6 +1,6 @@ package com.depromeet.domains.level.service; -import com.depromeet.domains.level.data.LevelPolicy; +import com.depromeet.level.data.LevelPolicy; import com.depromeet.domains.level.dto.LevelPolicyDataDto; import com.depromeet.domains.level.dto.LevelPolicyResponseDto; import lombok.RequiredArgsConstructor; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java index d2775afe..331d5401 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java @@ -3,10 +3,10 @@ import com.depromeet.common.error.dto.CommonErrorCode; import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.item.repository.ItemRepository; -import com.depromeet.domains.level.data.LevelPolicy; import com.depromeet.domains.user.dto.response.UserLevelProgressDto; import com.depromeet.domains.user.dto.response.UserLevelResponseDto; import com.depromeet.domains.user.repository.UserLevelRepository; +import com.depromeet.level.data.LevelPolicy; import com.depromeet.user.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,7 +14,8 @@ import java.util.Arrays; -import static com.depromeet.domains.level.data.LevelPolicy.getNextLevel; +import static com.depromeet.level.data.LevelPolicy.getNextLevel; + @RequiredArgsConstructor @Service diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/dto/UserItemCount.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/dto/UserItemCount.java new file mode 100644 index 00000000..68ca3592 --- /dev/null +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/dto/UserItemCount.java @@ -0,0 +1,11 @@ +package com.depromeet.domains.item.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class UserItemCount { + private Long userId; + private Long itemCount; +} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/repository/ItemRepository.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/repository/ItemRepository.java index b015780c..c996d927 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/repository/ItemRepository.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/item/repository/ItemRepository.java @@ -1,17 +1,23 @@ package com.depromeet.domains.item.repository; -import com.depromeet.domains.user.UserItemDto; +import com.depromeet.domains.item.dto.UserItemCount; import com.depromeet.item.Item; +import com.depromeet.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; import java.util.List; @Repository public interface ItemRepository extends JpaRepository { - @Query("SELECT new com.depromeet.domains.user.UserItemDto(i.user.id, CAST(COUNT(i.id) AS int)) FROM Item i WHERE i.user.id IN :userIds GROUP BY i.user.id") - List countItemsByUserIdIn(@Param("userIds") List userIds); + @Query("SELECT new com.depromeet.domains.item.dto.UserItemCount(i.user.id, CAST(COUNT(i.id) AS long)) FROM Item i WHERE i.user.id IN :userIds GROUP BY i.user.id") + List countItemsByUserIdIn(@Param("userIds") List userIds); + + @Query("SELECT i.user FROM Item AS i WHERE i.createdAt > :createdAt") + List findAllItemUpdateUser(@Param("createdAt") LocalDateTime createdAt); + } diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicy.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicy.java deleted file mode 100644 index 07f39afb..00000000 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicy.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.depromeet.domains.level; - -import com.depromeet.user.UserLevel; - -public interface LevelUpdatePolicy { - boolean isAcceptable(int itemCount); - UserLevel getNewLevel(); -} \ No newline at end of file diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicyImpl.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicyImpl.java deleted file mode 100644 index 09260bae..00000000 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdatePolicyImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.depromeet.domains.level; - -import com.depromeet.user.UserLevel; -import lombok.AllArgsConstructor; - -/** - * 기능 요구 사항 - * 등급 레벨명 조건 - * 1 스트릿드랍퍼 드랍횟수 0~4 - * 2 열정드랍퍼 드랍횟수 5~24 - * 3 스페셜 DJ 드랍횟수 25~55 - */ -@AllArgsConstructor -public class LevelUpdatePolicyImpl implements LevelUpdatePolicy { - private final int minItemDropCount; - private final int maxItemDropCount; - private final UserLevel newLevel; - - @Override - public boolean isAcceptable(int itemCount) { - return itemCount >= minItemDropCount && itemCount <= maxItemDropCount; - } - - @Override - public UserLevel getNewLevel() { - return newLevel; - } -} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdater.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdater.java new file mode 100644 index 00000000..d614ee70 --- /dev/null +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/level/LevelUpdater.java @@ -0,0 +1,19 @@ +package com.depromeet.domains.level; + +import com.depromeet.common.error.code.GlobalErrorCode; +import com.depromeet.common.error.exceptions.BusinessException; +import com.depromeet.level.data.LevelPolicy; +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public class LevelUpdater { + public static LevelPolicy getUpdateLevel(Long dropCount) { + return Arrays.stream(LevelPolicy.values()) + .filter(policy -> dropCount >= policy.getDropCountStart() && dropCount < policy.getDropCountEnd()) + .findFirst() + .orElseThrow(() -> new BusinessException(GlobalErrorCode.INTERNAL_SERVER_ERROR)); + + } +} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/repository/PopupRepository.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/repository/PopupRepository.java index b1bb0d73..27f51b37 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/repository/PopupRepository.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/repository/PopupRepository.java @@ -2,6 +2,8 @@ import com.depromeet.popup.Popup; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface PopupRepository extends JpaRepository { } diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/service/PopupService.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/service/PopupService.java index a4074a6f..1f0ae3bb 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/service/PopupService.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/popup/service/PopupService.java @@ -16,4 +16,9 @@ public void createLevelUpPopup(User user, long newLevel) { Popup popup = new Popup(user.getId(), "LEVEL_" + newLevel); popupRepository.save(popup); } + + public void createGuidePopup(User user) { + Popup popup = new Popup(user.getId(), "GUIDE_1"); + popupRepository.save(popup); + } } diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/UserItemDto.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/UserItemDto.java deleted file mode 100644 index d9140d2e..00000000 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/UserItemDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.depromeet.domains.user; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -public class UserItemDto { - private Long userId; - private Integer itemCount; - - public UserItemDto(Long userId, Integer itemCount) { - this.userId = userId; - this.itemCount = itemCount; - } -} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/jobs/UserGuideJob.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/jobs/UserGuideJob.java new file mode 100644 index 00000000..ee25259d --- /dev/null +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/jobs/UserGuideJob.java @@ -0,0 +1,16 @@ +package com.depromeet.domains.user.jobs; + +import com.depromeet.domains.user.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserGuideJob { + private final UserService userService; + + public void run() { + userService.createGuidePopup(); + } + +} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserLevelRepository.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserLevelRepository.java deleted file mode 100644 index 9c747ed8..00000000 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserLevelRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.depromeet.domains.user.repository; - -import com.depromeet.user.UserLevel; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserLevelRepository extends JpaRepository { - UserLevel findUserLevelById(long userLevelId); -} diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserRepository.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserRepository.java index 58b560c8..83538a82 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserRepository.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/repository/UserRepository.java @@ -1,10 +1,14 @@ package com.depromeet.domains.user.repository; import com.depromeet.user.User; -import com.depromeet.user.UserLevel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + @Repository public interface UserRepository extends JpaRepository { + + List findAllByCreatedAtBefore(LocalDateTime createdAt); } diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/scheduler/UserLevelBatchScheduler.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/scheduler/UserLevelBatchScheduler.java index f5833d93..a349eec6 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/scheduler/UserLevelBatchScheduler.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/scheduler/UserLevelBatchScheduler.java @@ -1,8 +1,8 @@ package com.depromeet.domains.user.scheduler; +import com.depromeet.domains.user.jobs.UserGuideJob; import com.depromeet.domains.user.jobs.UserLevelJob; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -10,9 +10,15 @@ @RequiredArgsConstructor public class UserLevelBatchScheduler { private final UserLevelJob userLevelJob; + private final UserGuideJob userGuideJob; @Scheduled(cron = "0 0/30 * * * *") public void runUserLevelJob() { userLevelJob.run(); } + + @Scheduled(cron = "0 0/30 * * * *") + public void runUserGuideJob() { + userGuideJob.run(); + } } diff --git a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/service/UserService.java b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/service/UserService.java index fee6e873..c5ebd04a 100644 --- a/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/service/UserService.java +++ b/backend/streetdrop-batch/src/main/java/com/depromeet/domains/user/service/UserService.java @@ -1,84 +1,66 @@ package com.depromeet.domains.user.service; +import com.depromeet.common.error.code.GlobalErrorCode; +import com.depromeet.common.error.exceptions.BusinessException; +import com.depromeet.domains.item.dto.UserItemCount; import com.depromeet.domains.item.repository.ItemRepository; -import com.depromeet.domains.level.LevelUpdatePolicy; -import com.depromeet.domains.level.LevelUpdatePolicyImpl; +import com.depromeet.domains.level.LevelUpdater; import com.depromeet.domains.popup.service.PopupService; -import com.depromeet.domains.user.UserItemDto; -import com.depromeet.domains.user.repository.UserLevelRepository; import com.depromeet.domains.user.repository.UserRepository; +import com.depromeet.level.data.LevelPolicy; import com.depromeet.user.User; -import com.depromeet.user.UserLevel; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.HashMap; +import java.time.LocalDateTime; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; @Slf4j @Service +@RequiredArgsConstructor public class UserService { - public static final int LEVEL_ONE = 1; - public static final int LEVEL_TWO = 2; - public static final int LEVEL_THREE = 3; - private final UserRepository userRepository; - private final UserLevelRepository userLevelRepository; private final ItemRepository itemRepository; - private final Map levelUpdatePolicies; private final PopupService popupService; - public UserService(UserRepository userRepository, UserLevelRepository userLevelRepository, ItemRepository itemRepository, PopupService popupService) { - this.userRepository = userRepository; - this.userLevelRepository = userLevelRepository; - this.itemRepository = itemRepository; - levelUpdatePolicies = createLevelUpdatePolicies(); - this.popupService = popupService; - } - private Map createLevelUpdatePolicies() { - Map policies = new HashMap<>(); - policies.put(LEVEL_ONE, new LevelUpdatePolicyImpl(0, 4, getUserLevelById(1L))); - policies.put(LEVEL_TWO, new LevelUpdatePolicyImpl(5, 24, getUserLevelById(2L))); - policies.put(LEVEL_THREE, new LevelUpdatePolicyImpl(25, 55, getUserLevelById(3L))); - return policies; - } - - private UserLevel getUserLevelById(long userLevelId) { - return userLevelRepository.findUserLevelById(userLevelId); + @Transactional + public void createGuidePopup() { + var signUpUsers = userRepository.findAllByCreatedAtBefore(LocalDateTime.now().minusMinutes(30)); + signUpUsers.forEach(popupService::createGuidePopup); } @Transactional public void updateLevel() { - List usersToUpdateLevel = new ArrayList<>(); - var allUsers = userRepository.findAll(); - var userIds = allUsers.stream().map(User::getId).collect(Collectors.toList()); - var userItemDtoList = itemRepository.countItemsByUserIdIn(userIds); + var itemUpdateUsers = itemRepository.findAllItemUpdateUser(LocalDateTime.now().minusMinutes(30)).stream().distinct().toList(); - Map itemCountMap = userItemDtoList.stream() - .collect(Collectors.toMap(UserItemDto::getUserId, UserItemDto::getItemCount)); + var userIds = itemUpdateUsers.stream().map(User::getId).toList(); + List userItemDtoMap = itemRepository.countItemsByUserIdIn(userIds); - allUsers.forEach(user -> { - var itemCount = itemCountMap.getOrDefault(user.getId(), 0); - updateUserLevel(user, itemCount, usersToUpdateLevel); - }); - userRepository.saveAll(usersToUpdateLevel); - } - private void updateUserLevel(User user, Integer itemCount, List usersToUpdateLevel) { - for (Map.Entry entry : levelUpdatePolicies.entrySet()) { - LevelUpdatePolicy policy = entry.getValue(); - if (policy.isAcceptable(itemCount)) { - user.changeLevel(policy.getNewLevel().getId()); - popupService.createLevelUpPopup(user, policy.getNewLevel().getId()); - usersToUpdateLevel.add(user); - break; - } - } + itemUpdateUsers.forEach( + user -> { + var userId = user.getId(); + var itemCount = userItemDtoMap.stream() + .filter(dto -> dto.getUserId().equals(userId)) + .map(UserItemCount::getItemCount) + .findFirst() + .orElseThrow( + () -> new BusinessException(GlobalErrorCode.INTERNAL_SERVER_ERROR) + ); + + + LevelPolicy newLevel = LevelUpdater.getUpdateLevel(itemCount); + if (newLevel.getLevel() > user.getUserLevel().getId()) { + user.changeLevel(newLevel.getLevel()); + popupService.createLevelUpPopup(user, newLevel.getLevel()); + } + } + ); + + userRepository.saveAll(itemUpdateUsers); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/data/LevelPolicy.java b/backend/streetdrop-domain/src/main/java/com/depromeet/level/data/LevelPolicy.java similarity index 93% rename from backend/streetdrop-api/src/main/java/com/depromeet/domains/level/data/LevelPolicy.java rename to backend/streetdrop-domain/src/main/java/com/depromeet/level/data/LevelPolicy.java index cc99779c..06571098 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/level/data/LevelPolicy.java +++ b/backend/streetdrop-domain/src/main/java/com/depromeet/level/data/LevelPolicy.java @@ -1,4 +1,4 @@ -package com.depromeet.domains.level.data; +package com.depromeet.level.data; import lombok.AllArgsConstructor; import lombok.Getter; @@ -8,7 +8,7 @@ public enum LevelPolicy { LEVEL_1("드랍스타터", "신규유저", "https://street-drop.s3.ap-northeast-2.amazonaws.com/USER_PROFILE/Level1.png", 1L, 0L, 5L), LEVEL_2("프로 드랍퍼", "5개 이상 드랍한 유저", "https://street-drop.s3.ap-northeast-2.amazonaws.com/USER_PROFILE/Level2.png", 2L, 5L, 25L), - LEVEL_3("스폐셜 DJ", "25개 이상 드랍한 유저", "https://street-drop.s3.ap-northeast-2.amazonaws.com/USER_PROFILE/Level3.png", 3L, 25L, 50L); + LEVEL_3("스폐셜 DJ", "25개 이상 드랍한 유저", "https://street-drop.s3.ap-northeast-2.amazonaws.com/USER_PROFILE/Level3.png", 3L, 25L, Long.MAX_VALUE); private final String levelName; private final String description;