Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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());

Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
}
}

Expand All @@ -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");
Expand All @@ -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");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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),
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,6 +30,7 @@
public class LibraryController {

private final LibraryInterestApplication libraryInterestApplication;
private final LibraryEvaluationApplication libraryEvaluationApplication;

/**
* 관심있어요 등록
Expand Down Expand Up @@ -55,4 +66,73 @@ public ResponseEntity<Void> unregisterAsInterest(@AuthenticationPrincipal User u
.build();
}

/**
* 작품 평가하기
*
* @param user 로그인 유저 객체
* @param request 서재 (작품 평가) 생성 객체
* @return 201 CREATED
*/
@PostMapping("/user-novels")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<Void> 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<UserNovelGetResponse> 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<Void> 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<Void> deleteEvaluation(@AuthenticationPrincipal User user,
@PathVariable Long novelId) {
libraryEvaluationApplication.deleteEvaluation(user, novelId);
return ResponseEntity
.status(NO_CONTENT)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -38,14 +47,79 @@ public void createUserNovelAttractivePoint(UserNovel userNovel, AttractivePoint
}

public void createUserNovelAttractivePoints(UserNovel userNovel, List<String> request) {
for (String stringAttractivePoint : request) {
AttractivePoint attractivePoint = getAttractivePointByString(stringAttractivePoint);
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint));
}
List<UserNovelAttractivePoint> attractivePoints = request.stream()
.map(this::getAttractivePointByString)
.map(attractivePoint -> UserNovelAttractivePoint.create(userNovel, attractivePoint))
.toList();

userNovelAttractivePointRepository.saveAll(attractivePoints);
}

public void deleteUserNovelAttractivePoints(List<UserNovelAttractivePoint> userNovelAttractivePoints) {
userNovelAttractivePointRepository.deleteAll(userNovelAttractivePoints);
}

public List<String> getAttractivePoints(Novel novel) {
Map<String, Integer> attractivePointMap = makeAttractivePointMapExcludingZero(novel);

if (attractivePointMap.isEmpty()) {
return Collections.emptyList();
}

return getTOP3AttractivePoints(attractivePointMap);
}

private Map<String, Integer> makeAttractivePointMapExcludingZero(Novel novel) {
Map<String, Integer> 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<String> getTOP3AttractivePoints(Map<String, Integer> attractivePointMap) {
Map<Integer, List<String>> groupedByValue = groupAttractivePointByValue(attractivePointMap);

List<String> result = new ArrayList<>();
List<Integer> sortedKeys = new ArrayList<>(groupedByValue.keySet());
sortedKeys.sort(Collections.reverseOrder());

Random random = new Random();

for (Integer key : sortedKeys) {
List<String> 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<Integer, List<String>> groupAttractivePointByValue(Map<String, Integer> attractivePointMap) {
Map<Integer, List<String>> groupedByValue = new HashMap<>();

for (Map.Entry<String, Integer> 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());
}

}
Loading