diff --git a/src/main/java/org/websoso/WSSServer/application/LibraryEvaluationApplication.java b/src/main/java/org/websoso/WSSServer/application/LibraryEvaluationApplication.java index 61301e1a..bd46de95 100644 --- a/src/main/java/org/websoso/WSSServer/application/LibraryEvaluationApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/LibraryEvaluationApplication.java @@ -72,7 +72,7 @@ public void createEvaluation(User user, UserNovelCreateRequest request) { */ public UserNovelGetResponse getEvaluation(User user, Long novelId) { Novel novel = novelService.getNovelOrException(novelId); - UserNovel userNovel = libraryService.getUserNovelOrNull(user, novel); + UserNovel userNovel = libraryService.getLibraryOrNull(user, novel); if (userNovel == null) { return UserNovelGetResponse.of(novel, null, Collections.emptyList(), Collections.emptyList()); @@ -93,7 +93,7 @@ public UserNovelGetResponse getEvaluation(User user, Long novelId) { */ @Transactional public void updateEvaluation(User user, Long novelId, UserNovelUpdateRequest request) { - UserNovel userNovel = libraryService.getUserNovelOrException(user, novelId); + UserNovel userNovel = libraryService.getLibraryOrException(user, novelId); userNovel.updateUserNovel(request.userNovelRating(), request.status(), request.startDate(), request.endDate()); @@ -109,7 +109,7 @@ public void updateEvaluation(User user, Long novelId, UserNovelUpdateRequest req * @param novelId 소설 ID */ public void deleteEvaluation(User user, Long novelId) { - UserNovel userNovel = libraryService.getUserNovelOrException(user, novelId); + UserNovel userNovel = libraryService.getLibraryOrException(user, novelId); if (userNovel.getStatus() == null) { throw new CustomUserNovelException(NOT_EVALUATED, "this novel has not been evaluated by the user"); diff --git a/src/main/java/org/websoso/WSSServer/application/LibraryInterestApplication.java b/src/main/java/org/websoso/WSSServer/application/LibraryInterestApplication.java index abb385d8..49260426 100644 --- a/src/main/java/org/websoso/WSSServer/application/LibraryInterestApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/LibraryInterestApplication.java @@ -30,7 +30,7 @@ public class LibraryInterestApplication { public void registerAsInterest(User user, Long novelId) { Novel novel = novelService.getNovelOrException(novelId); - UserNovel userNovel = user == null ? null : libraryService.getUserNovelOrNull(user, novel); + UserNovel userNovel = user == null ? null : libraryService.getLibraryOrNull(user, novel); if (userNovel != null && userNovel.getIsInterest()) { throw new CustomUserNovelException(ALREADY_INTERESTED, "already registered as interested"); @@ -40,7 +40,7 @@ public void registerAsInterest(User user, Long novelId) { try { userNovel = createUserNovelByInterest(user, novel); } catch (DataIntegrityViolationException e) { - userNovel = libraryService.getUserNovelOrException(user, novelId); + userNovel = libraryService.getLibraryOrException(user, novelId); } } @@ -54,7 +54,7 @@ public void registerAsInterest(User user, Long novelId) { * @param novelId 소설 ID */ public void unregisterAsInterest(User user, Long novelId) { - UserNovel userNovel = libraryService.getUserNovelOrException(user, novelId); + UserNovel userNovel = libraryService.getLibraryOrException(user, novelId); if (!userNovel.getIsInterest()) { throw new CustomUserNovelException(NOT_INTERESTED, "not registered as interest"); @@ -69,7 +69,7 @@ public void unregisterAsInterest(User user, Long novelId) { } private UserNovel createUserNovelByInterest(User user, Novel novel) { - if (libraryService.getUserNovelOrNull(user, novel) != null) { + if (libraryService.getLibraryOrNull(user, novel) != null) { throw new CustomUserNovelException(USER_NOVEL_ALREADY_EXISTS, "this novel is already registered"); } diff --git a/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java b/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java index d6264327..834d8c1f 100644 --- a/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java +++ b/src/main/java/org/websoso/WSSServer/application/SearchNovelApplication.java @@ -18,6 +18,8 @@ import org.websoso.WSSServer.domain.Avatar; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.GenrePreference; +import org.websoso.WSSServer.library.service.AttractivePointService; +import org.websoso.WSSServer.library.service.KeywordService; import org.websoso.WSSServer.user.domain.User; import org.websoso.WSSServer.domain.common.GenreName; import org.websoso.WSSServer.dto.novel.FilteredNovelsGetResponse; @@ -50,6 +52,8 @@ public class SearchNovelApplication { private final NovelServiceImpl novelService; private final PopularNovelService popularNovelService; private final GenreServiceImpl genreService; + private final AttractivePointService libraryAttractivePointService; + private final KeywordService libraryKeywordService; private final KeywordServiceImpl keywordService; private final LibraryService libraryService; @@ -113,7 +117,7 @@ public NovelGetResponseBasic getNovelInfoBasic(User user, Long novelId) { : Math.round(libraryService.getRatingSum(novel) / novelRatingCount * 10.0f) / 10.0f; return NovelGetResponseBasic.of( novel, - libraryService.getUserNovelOrNull(user, novel), + libraryService.getLibraryOrNull(user, novel), getNovelGenreNames(novelGenres), getRandomNovelGenreImage(novelGenres), libraryService.getInterestCount(novel), @@ -130,8 +134,8 @@ public NovelGetResponseInfoTab getNovelInfoInfoTab(Long novelId) { return NovelGetResponseInfoTab.of( novel, novelService.getPlatforms(novel), - libraryService.getAttractivePoints(novel), - libraryService.getKeywordNameAndCount(novel), + libraryAttractivePointService.getAttractivePoints(novel), + libraryKeywordService.getKeywordNameAndCount(novel), libraryService.getWatchingCount(novel), libraryService.getWatchedCount(novel), libraryService.getQuitCount(novel) diff --git a/src/main/java/org/websoso/WSSServer/controller/LibraryController.java b/src/main/java/org/websoso/WSSServer/controller/LibraryController.java index 179c25c3..e90aac23 100644 --- a/src/main/java/org/websoso/WSSServer/controller/LibraryController.java +++ b/src/main/java/org/websoso/WSSServer/controller/LibraryController.java @@ -1,17 +1,27 @@ package org.websoso.WSSServer.controller; +import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.NO_CONTENT; +import static org.springframework.http.HttpStatus.OK; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; 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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.websoso.WSSServer.application.LibraryEvaluationApplication; import org.websoso.WSSServer.application.LibraryInterestApplication; +import org.websoso.WSSServer.dto.userNovel.UserNovelCreateRequest; +import org.websoso.WSSServer.dto.userNovel.UserNovelGetResponse; +import org.websoso.WSSServer.dto.userNovel.UserNovelUpdateRequest; import org.websoso.WSSServer.user.domain.User; @RestController @@ -20,6 +30,7 @@ public class LibraryController { private final LibraryInterestApplication libraryInterestApplication; + private final LibraryEvaluationApplication libraryEvaluationApplication; /** * 관심있어요 등록 @@ -55,4 +66,73 @@ public ResponseEntity unregisterAsInterest(@AuthenticationPrincipal User u .build(); } + /** + * 작품 평가하기 + * + * @param user 로그인 유저 객체 + * @param request 서재 (작품 평가) 생성 객체 + * @return 201 CREATED + */ + @PostMapping("/user-novels") + @PreAuthorize("isAuthenticated()") + public ResponseEntity createEvaluation(@AuthenticationPrincipal User user, + @Valid @RequestBody UserNovelCreateRequest request) { + libraryEvaluationApplication.createEvaluation(user, request); + return ResponseEntity + .status(CREATED) + .build(); + } + + /** + * 작품 평가 불러오기 + * + * @param user 로그인 유저 객체 + * @param novelId 소설 ID + * @return UserNovelGetResponse + */ + @GetMapping("/user-novels/{novelId}") + @PreAuthorize("isAuthenticated()") + public ResponseEntity getEvaluation(@AuthenticationPrincipal User user, + @PathVariable Long novelId) { + return ResponseEntity + .status(OK) + .body(libraryEvaluationApplication.getEvaluation(user, novelId)); + } + + /** + * 작품 평가 업데이트하기 + * + * @param user 로그인 유저 객체 + * @param novelId 소설 ID + * @param request 서재 (작품 평가) 업데이트 객체 + * @return 204 NO_CONTENT + */ + @PutMapping("/user-novels/{novelId}") + @PreAuthorize("isAuthenticated() and @authorizationService.validate(#novelId, #user, T(org.websoso.WSSServer.library.domain.UserNovel))") + public ResponseEntity updateEvaluation(@AuthenticationPrincipal User user, + @PathVariable Long novelId, + @Valid @RequestBody UserNovelUpdateRequest request) { + libraryEvaluationApplication.updateEvaluation(user, novelId, request); + return ResponseEntity + .status(NO_CONTENT) + .build(); + } + + /** + * 작품 평가 삭제하기 + * + * @param user 로그인 유저 객체 + * @param novelId 소설 ID + * @return 204 NO_CONTENT + */ + @DeleteMapping("/user-novels/{novelId}") + @PreAuthorize("isAuthenticated() and @authorizationService.validate(#novelId, #user, T(org.websoso.WSSServer.library.domain.UserNovel))") + public ResponseEntity deleteEvaluation(@AuthenticationPrincipal User user, + @PathVariable Long novelId) { + libraryEvaluationApplication.deleteEvaluation(user, novelId); + return ResponseEntity + .status(NO_CONTENT) + .build(); + } + } diff --git a/src/main/java/org/websoso/WSSServer/library/domain/Keyword.java b/src/main/java/org/websoso/WSSServer/library/domain/Keyword.java index 0467a365..655a8d0d 100644 --- a/src/main/java/org/websoso/WSSServer/library/domain/Keyword.java +++ b/src/main/java/org/websoso/WSSServer/library/domain/Keyword.java @@ -10,6 +10,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import java.io.Serializable; +import java.util.Arrays; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -31,4 +32,8 @@ public class Keyword implements Serializable { @JoinColumn(name = "keyword_category_id", nullable = false) private KeywordCategory keywordCategory; + public boolean containsAllWords(String[] words) { + return Arrays.stream(words) + .allMatch(keywordName::contains); + } } diff --git a/src/main/java/org/websoso/WSSServer/library/service/AttractivePointService.java b/src/main/java/org/websoso/WSSServer/library/service/AttractivePointService.java index 803ae323..0ea2a9a7 100644 --- a/src/main/java/org/websoso/WSSServer/library/service/AttractivePointService.java +++ b/src/main/java/org/websoso/WSSServer/library/service/AttractivePointService.java @@ -2,22 +2,31 @@ import static org.websoso.WSSServer.exception.error.CustomAttractivePointError.INVALID_ATTRACTIVE_POINT; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Random; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.domain.common.AttractivePointName; import org.websoso.WSSServer.library.domain.AttractivePoint; import org.websoso.WSSServer.exception.exception.CustomAttractivePointException; import org.websoso.WSSServer.library.domain.UserNovel; import org.websoso.WSSServer.library.domain.UserNovelAttractivePoint; import org.websoso.WSSServer.library.repository.AttractivePointRepository; import org.websoso.WSSServer.library.repository.UserNovelAttractivePointRepository; +import org.websoso.WSSServer.novel.domain.Novel; @Service @RequiredArgsConstructor @Transactional public class AttractivePointService { + private static final int ATTRACTIVE_POINT_SIZE = 3; + private final AttractivePointRepository attractivePointRepository; private final UserNovelAttractivePointRepository userNovelAttractivePointRepository; @@ -38,14 +47,79 @@ public void createUserNovelAttractivePoint(UserNovel userNovel, AttractivePoint } public void createUserNovelAttractivePoints(UserNovel userNovel, List request) { - for (String stringAttractivePoint : request) { - AttractivePoint attractivePoint = getAttractivePointByString(stringAttractivePoint); - userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint)); - } + List attractivePoints = request.stream() + .map(this::getAttractivePointByString) + .map(attractivePoint -> UserNovelAttractivePoint.create(userNovel, attractivePoint)) + .toList(); + + userNovelAttractivePointRepository.saveAll(attractivePoints); } public void deleteUserNovelAttractivePoints(List userNovelAttractivePoints) { userNovelAttractivePointRepository.deleteAll(userNovelAttractivePoints); } + public List getAttractivePoints(Novel novel) { + Map attractivePointMap = makeAttractivePointMapExcludingZero(novel); + + if (attractivePointMap.isEmpty()) { + return Collections.emptyList(); + } + + return getTOP3AttractivePoints(attractivePointMap); + } + + private Map makeAttractivePointMapExcludingZero(Novel novel) { + Map attractivePointMap = new HashMap<>(); + + for (AttractivePointName point : AttractivePointName.values()) { + attractivePointMap.put(point.getLabel(), getAttractivePointCount(novel, point)); + } + + attractivePointMap.entrySet().removeIf(entry -> entry.getValue() == 0); + + return attractivePointMap; + } + + private List getTOP3AttractivePoints(Map attractivePointMap) { + Map> groupedByValue = groupAttractivePointByValue(attractivePointMap); + + List result = new ArrayList<>(); + List sortedKeys = new ArrayList<>(groupedByValue.keySet()); + sortedKeys.sort(Collections.reverseOrder()); + + Random random = new Random(); + + for (Integer key : sortedKeys) { + List items = groupedByValue.get(key); + if (result.size() + items.size() > ATTRACTIVE_POINT_SIZE) { + Collections.shuffle(items, random); + items = items.subList(0, ATTRACTIVE_POINT_SIZE - result.size()); + } + result.addAll(items); + if (result.size() >= ATTRACTIVE_POINT_SIZE) { + break; + } + } + + return result; + } + + private Map> groupAttractivePointByValue(Map attractivePointMap) { + Map> groupedByValue = new HashMap<>(); + + for (Map.Entry entry : attractivePointMap.entrySet()) { + groupedByValue + .computeIfAbsent(entry.getValue(), k -> new ArrayList<>()) + .add(entry.getKey()); + } + + return groupedByValue; + } + + public int getAttractivePointCount(Novel novel, AttractivePointName point) { + return userNovelAttractivePointRepository.countByUserNovel_NovelAndAttractivePoint_AttractivePointName( + novel, point.getLabel()); + } + } diff --git a/src/main/java/org/websoso/WSSServer/library/service/KeywordService.java b/src/main/java/org/websoso/WSSServer/library/service/KeywordService.java index 47a0a7f6..3c69c853 100644 --- a/src/main/java/org/websoso/WSSServer/library/service/KeywordService.java +++ b/src/main/java/org/websoso/WSSServer/library/service/KeywordService.java @@ -4,11 +4,14 @@ import static org.websoso.WSSServer.exception.error.CustomKeywordError.KEYWORD_NOT_FOUND; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.websoso.WSSServer.dto.keyword.KeywordCountGetResponse; import org.websoso.WSSServer.library.domain.Keyword; import org.websoso.WSSServer.library.domain.KeywordCategory; import org.websoso.WSSServer.domain.common.KeywordCategoryName; @@ -22,12 +25,15 @@ import org.websoso.WSSServer.library.repository.KeywordCategoryRepository; import org.websoso.WSSServer.library.repository.KeywordRepository; import org.websoso.WSSServer.library.repository.UserNovelKeywordRepository; +import org.websoso.WSSServer.novel.domain.Novel; @Service @RequiredArgsConstructor @Transactional public class KeywordService { + private static final int KEYWORD_SIZE = 5; + private final KeywordRepository keywordRepository; private final KeywordCategoryRepository keywordCategoryRepository; private final UserNovelKeywordRepository userNovelKeywordRepository; @@ -45,7 +51,7 @@ public KeywordByCategoryGetResponse searchKeywordByCategory(String query) { List categories = Arrays.stream(KeywordCategoryName.values()) .map(category -> CategoryGetResponse.of(getKeywordCategory(category.getLabel()), sortByCategory(category, searchedKeywords))) - .collect(Collectors.toList()); + .toList(); return KeywordByCategoryGetResponse.of(categories); } @@ -75,7 +81,7 @@ private List sortByCategory(KeywordCategoryName keywordCateg return searchedKeyword.stream() .filter(keyword -> keyword.getKeywordCategory().getKeywordCategoryName() .equals(keywordCategoryName.getLabel())) - .map(KeywordGetResponse::of).collect(Collectors.toList()); + .map(KeywordGetResponse::of).toList(); } private List searchKeyword(String query) { @@ -84,16 +90,29 @@ private List searchKeyword(String query) { } String[] words = query.split(" "); return keywordRepository.findAll().stream() - .filter(keyword -> containsAllWords(keyword.getKeywordName(), words)).toList(); + .filter(keyword -> keyword.containsAllWords(words)).toList(); } - private boolean containsAllWords(String keywordName, String[] words) { - for (String word : words) { - if (!keywordName.contains(word)) { - return false; - } + public List getKeywordNameAndCount(Novel novel) { + List userNovelKeywords = getKeywords(novel); + + if (userNovelKeywords.isEmpty()) { + return Collections.emptyList(); } - return true; + + Map keywordFrequencyMap = userNovelKeywords.stream() + .collect(Collectors.groupingBy(UserNovelKeyword::getKeyword, Collectors.counting())); + + return keywordFrequencyMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(KEYWORD_SIZE) + .map(entry -> KeywordCountGetResponse.of(entry.getKey(), entry.getValue().intValue())) + .toList(); } + public List getKeywords(Novel novel) { + return userNovelKeywordRepository.findAllByUserNovel_Novel(novel); + } + + } diff --git a/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java b/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java index 0472b9c1..aebeb9f0 100644 --- a/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java +++ b/src/main/java/org/websoso/WSSServer/library/service/LibraryService.java @@ -6,28 +6,16 @@ import static org.websoso.WSSServer.exception.error.CustomUserNovelError.USER_NOVEL_NOT_FOUND; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.user.domain.User; -import org.websoso.WSSServer.domain.common.AttractivePointName; import org.websoso.WSSServer.domain.common.ReadStatus; -import org.websoso.WSSServer.dto.keyword.KeywordCountGetResponse; import org.websoso.WSSServer.exception.exception.CustomUserNovelException; -import org.websoso.WSSServer.library.domain.Keyword; import org.websoso.WSSServer.library.domain.UserNovel; -import org.websoso.WSSServer.library.domain.UserNovelKeyword; -import org.websoso.WSSServer.library.repository.UserNovelAttractivePointRepository; -import org.websoso.WSSServer.library.repository.UserNovelKeywordRepository; import org.websoso.WSSServer.library.repository.UserNovelRepository; import org.websoso.WSSServer.novel.domain.Novel; @@ -35,16 +23,11 @@ @RequiredArgsConstructor public class LibraryService { - private static final int KEYWORD_SIZE = 5; - private static final int ATTRACTIVE_POINT_SIZE = 3; - private final UserNovelRepository userNovelRepository; - private final UserNovelKeywordRepository userNovelKeywordRepository; - private final UserNovelAttractivePointRepository userNovelAttractivePointRepository; // TODO: novelId로 불러옴 @Transactional(readOnly = true) - public UserNovel getUserNovelOrException(User user, Long novelId) { + public UserNovel getLibraryOrException(User user, Long novelId) { return userNovelRepository.findByNovel_NovelIdAndUser(novelId, user) .orElseThrow(() -> new CustomUserNovelException(USER_NOVEL_NOT_FOUND, "user novel with the given user and novel is not found")); @@ -53,11 +36,12 @@ public UserNovel getUserNovelOrException(User user, Long novelId) { // TODO: Novel 객체로 불러옴 // TODO: 사용자 객체가 Null이면 Null로 반환함 헷갈리수도? @Transactional(readOnly = true) - public UserNovel getUserNovelOrNull(User user, Novel novel) { + public UserNovel getLibraryOrNull(User user, Novel novel) { if (user == null) { return null; } - return userNovelRepository.findByNovelAndUser(novel, user).orElse(null); + + return userNovelRepository.findByNovel_NovelIdAndUser(novel.getNovelId(), user).orElse(null); } @Transactional @@ -88,9 +72,6 @@ public int getInterestCount(Novel novel) { return userNovelRepository.countByNovelAndIsInterestTrue(novel); } - public List getKeywords(Novel novel) { - return userNovelKeywordRepository.findAllByUserNovel_Novel(novel); - } public int getWatchingCount(Novel novel) { return userNovelRepository.countByNovelAndStatus(novel, WATCHING); @@ -104,91 +85,12 @@ public int getQuitCount(Novel novel) { return userNovelRepository.countByNovelAndStatus(novel, QUIT); } - public int getAttractivePointCount(Novel novel, AttractivePointName point) { - return userNovelAttractivePointRepository.countByUserNovel_NovelAndAttractivePoint_AttractivePointName( - novel, point.getLabel()); - } - public List getTasteNovels(List preferGenres) { return userNovelRepository.findTasteNovels(preferGenres); } - public List getKeywordNameAndCount(Novel novel) { - List userNovelKeywords = getKeywords(novel); - - if (userNovelKeywords.isEmpty()) { - return Collections.emptyList(); - } - - Map keywordFrequencyMap = userNovelKeywords.stream() - .collect(Collectors.groupingBy(UserNovelKeyword::getKeyword, Collectors.counting())); - - return keywordFrequencyMap.entrySet().stream() - .sorted(Map.Entry.comparingByValue().reversed()) - .limit(KEYWORD_SIZE) - .map(entry -> KeywordCountGetResponse.of(entry.getKey(), entry.getValue().intValue())) - .toList(); - } - - public List getAttractivePoints(Novel novel) { - Map attractivePointMap = makeAttractivePointMapExcludingZero(novel); - - if (attractivePointMap.isEmpty()) { - return Collections.emptyList(); - } - - return getTOP3AttractivePoints(attractivePointMap); - } public List getTodayPopularNovelIds(PageRequest pageRequest) { return userNovelRepository.findTodayPopularNovelsId(pageRequest); } - - private Map makeAttractivePointMapExcludingZero(Novel novel) { - Map attractivePointMap = new HashMap<>(); - - for (AttractivePointName point : AttractivePointName.values()) { - attractivePointMap.put(point.getLabel(), getAttractivePointCount(novel, point)); - } - - attractivePointMap.entrySet().removeIf(entry -> entry.getValue() == 0); - - return attractivePointMap; - } - - private List getTOP3AttractivePoints(Map attractivePointMap) { - Map> groupedByValue = groupAttractivePointByValue(attractivePointMap); - - List result = new ArrayList<>(); - List sortedKeys = new ArrayList<>(groupedByValue.keySet()); - sortedKeys.sort(Collections.reverseOrder()); - - Random random = new Random(); - - for (Integer key : sortedKeys) { - List items = groupedByValue.get(key); - if (result.size() + items.size() > ATTRACTIVE_POINT_SIZE) { - Collections.shuffle(items, random); - items = items.subList(0, ATTRACTIVE_POINT_SIZE - result.size()); - } - result.addAll(items); - if (result.size() >= ATTRACTIVE_POINT_SIZE) { - break; - } - } - - return result; - } - - private Map> groupAttractivePointByValue(Map attractivePointMap) { - Map> groupedByValue = new HashMap<>(); - - for (Map.Entry entry : attractivePointMap.entrySet()) { - groupedByValue - .computeIfAbsent(entry.getValue(), k -> new ArrayList<>()) - .add(entry.getKey()); - } - - return groupedByValue; - } }