Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 작품평가 수정 API 구현 #107

Merged
merged 31 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
953bc62
[FEAT] 작품평가 수정을 위한 엔드포인트
rinarina0429 Jul 16, 2024
61f8bf5
[FEAT] 작품평가 수정을 위한 dto
rinarina0429 Jul 16, 2024
15711e7
[FEAT] 평가한 적 없는 경우 error
rinarina0429 Jul 16, 2024
3d83f20
[FEAT] updateUserNovel() 함수
rinarina0429 Jul 16, 2024
7fa465c
[FEAT] 작품평가 수정을 위한 서비스 로직
rinarina0429 Jul 16, 2024
4405e7e
[RENAME] 파일명 및 함수명 변경
rinarina0429 Jul 16, 2024
dffa507
[REFACTOR] 매력포인트, 키워드 모두 삭제하고 새로 생성하는 방식 아닌 필요한 부분만 삭제 및 생성으로 수정
rinarina0429 Jul 17, 2024
d5288a9
[REMOVE] UserNovelService에서 미사용 함수 제거
rinarina0429 Jul 17, 2024
ab6e92c
[REFACTOR] 커스텀쿼리문 사용
rinarina0429 Jul 17, 2024
4d8e2ab
[MERGE] pull origin dev
rinarina0429 Jul 21, 2024
6cec146
[RENAME] NovelKeyword->UserNovelKeyword 수정에 따라 함수명 변경
rinarina0429 Jul 21, 2024
43516fb
Merge branch 'dev' of https://github.com/Team-WSS/WSS-Server into fea…
rinarina0429 Jul 30, 2024
74eb33a
[REFACTOR] dto 필드 기준 개행 추가
rinarina0429 Jul 30, 2024
4a7180e
[REFACTOR] getUserNovelOrNull -> getUserNovelOrException 사용
rinarina0429 Jul 30, 2024
4c4787f
[FEAT] Modifying에 (clearAutomatically = true, flushAutomatically = tr…
rinarina0429 Jul 30, 2024
5b3e398
[REFACTOR] 삼항연산자 개행 추가
rinarina0429 Jul 30, 2024
6229cff
[FEAT] request dto에서 날짜 유효성 검사
rinarina0429 Jul 30, 2024
fc9a6af
[FIX] 작품 평가 생성 및 수정 LocalDate로 받게 수정
rinarina0429 Aug 1, 2024
35ee928
[FIX] 작품 평가 생성 및 수정 LocalDate에 @PastOrPresent 추가
rinarina0429 Aug 1, 2024
cd69c6c
[FIX] 서비스 로직에서 LocalDate 받도록 수정
rinarina0429 Aug 1, 2024
d687994
[REMOVE] 미사용 함수 convertToLocalDate 제거
rinarina0429 Aug 1, 2024
24e8e25
[FIX] userNovelKeywords FetchType.EAGER로 변경
rinarina0429 Aug 1, 2024
eba9024
[FIX] UserNovelGetResponse에서 LocalDate 바로 주도록 수정
rinarina0429 Aug 1, 2024
78dc530
Merge branch 'dev' of https://github.com/Team-WSS/WSS-Server into fea…
rinarina0429 Aug 1, 2024
d7aee2b
[REMOVE] userNovelRating의 @ColumnDefault 제거
rinarina0429 Aug 1, 2024
1ae4544
[REMOVE] UserNovelRatingValidator에서 null인 경우 valid 반환하는 로직 제거
rinarina0429 Aug 1, 2024
d27e1a5
[FEAT] userNovelRating에 @NotNull 어노테이션 추가
rinarina0429 Aug 1, 2024
95f5fb6
[FIX] dto의 attractivePoints, keywordIs @JsonFormat 제거 후 @NotNull 추가
rinarina0429 Aug 1, 2024
f86a4f7
[FIX] UserNovelRatingValidator에서 null인 경우 valid 반환하는 로직 복구
rinarina0429 Aug 1, 2024
8339c07
[FIX] UserNovel 내의 userNovelKeywords 지연로딩으로 수정
rinarina0429 Aug 2, 2024
3516df8
[FIX] 구조 변경으로 LazyInitializationException 터지지 않도록 수정
rinarina0429 Aug 2, 2024
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
@@ -1,6 +1,7 @@
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;
Expand All @@ -10,13 +11,15 @@
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.domain.Novel;
import org.websoso.WSSServer.domain.User;
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.service.NovelService;
import org.websoso.WSSServer.service.UserNovelService;
import org.websoso.WSSServer.service.UserService;
Expand All @@ -31,23 +34,36 @@ public class UserNovelController {
private final UserNovelService userNovelService;

@GetMapping("/{novelId}")
public ResponseEntity<UserNovelGetResponse> getUserNovel(Principal principal, @PathVariable Long novelId) {
public ResponseEntity<UserNovelGetResponse> getEvaluation(Principal principal, @PathVariable Long novelId) {
User user = userService.getUserOrException(Long.valueOf(principal.getName()));
Novel novel = novelService.getNovelOrException(novelId);
return ResponseEntity
.status(OK)
.body(userNovelService.getUserNovelInfo(user, novel));
.body(userNovelService.getEvaluation(user, novel));
}

@PostMapping
public ResponseEntity<Void> createUserNovel(Principal principal,
@Valid @RequestBody UserNovelCreateRequest request) {
public ResponseEntity<Void> createEvaluation(Principal principal,
@Valid @RequestBody UserNovelCreateRequest request) {
User user = userService.getUserOrException(Long.valueOf(principal.getName()));
userNovelService.createUserNovel(user, request);
userNovelService.createEvaluation(user, request);

return ResponseEntity
.status(CREATED)
.build();
}

@PutMapping("/{novelId}")
public ResponseEntity<Void> updateEvaluation(Principal principal, @PathVariable Long novelId,
@Valid @RequestBody UserNovelUpdateRequest request) {

User user = userService.getUserOrException(Long.valueOf(principal.getName()));
Novel novel = novelService.getNovelOrException(novelId);
userNovelService.updateEvaluation(user, novel, request);

return ResponseEntity
.status(NO_CONTENT)
.build();
}

}
7 changes: 7 additions & 0 deletions src/main/java/org/websoso/WSSServer/domain/UserNovel.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,11 @@ public void setIsInterest(Boolean isInterest) {
this.isInterest = isInterest;
}

public void updateUserNovel(Float userNovelRating, ReadStatus status, LocalDate startDate, LocalDate endDate) {
this.userNovelRating = userNovelRating;
this.status = status;
this.startDate = startDate;
this.endDate = endDate;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.websoso.WSSServer.dto.userNovel;

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.util.List;
import org.hibernate.annotations.ColumnDefault;
import org.websoso.WSSServer.domain.common.ReadStatus;
import org.websoso.WSSServer.validation.UserNovelRatingConstraint;

public record UserNovelUpdateRequest(
@UserNovelRatingConstraint
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved
@ColumnDefault("0.0")
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved
Float userNovelRating,
@NotNull(message = "읽기 상태는 null일 수 없습니다.")
ReadStatus status,
@Pattern(regexp = "\\d{4}-\\d{2}-\\d{2}", message = "시작 날짜는 yyyy-MM-dd 형식이어야 합니다.")
String startDate,
@Pattern(regexp = "\\d{4}-\\d{2}-\\d{2}", message = "종료 날짜는 yyyy-MM-dd 형식이어야 합니다.")
String endDate,
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved
@JsonSetter(nulls = Nulls.AS_EMPTY)
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved
@Size(max = 3, message = "매력 포인트는 최대 3개까지 가능합니다.")
List<String> attractivePoints,
@JsonSetter(nulls = Nulls.AS_EMPTY)
@Size(max = 10, message = "키워드는 최대 10개까지 가능합니다.")
List<Integer> keywordIds
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public enum CustomUserNovelError implements ICustomError {

USER_NOVEL_NOT_FOUND("USER_NOVEL-001", "해당 작품이 유저의 서재에 등록되어 있지 않습니다.", NOT_FOUND),
USER_NOVEL_ALREADY_EXISTS("USER_NOVEL-002", "이미 서재에 등록된 작품입니다.", CONFLICT),
ALREADY_INTERESTED("USER_NOVEL-003", "이미 해당 작품이 관심 작품으로 등록되어 있습니다.", CONFLICT);
ALREADY_INTERESTED("USER_NOVEL-003", "이미 해당 작품이 관심 작품으로 등록되어 있습니다.", CONFLICT),
NOT_EVALUATED("USER_NOVEL-005", "평가하지 않은 작품입니다.", CONFLICT);

private final String code;
private final String description;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package org.websoso.WSSServer.repository;

import java.util.List;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.websoso.WSSServer.domain.Keyword;
import org.websoso.WSSServer.domain.Novel;
import org.websoso.WSSServer.domain.NovelKeyword;

Expand All @@ -13,4 +18,9 @@ public interface NovelKeywordRepository extends JpaRepository<NovelKeyword, Long

List<NovelKeyword> findAllByNovel(Novel novel);

@Modifying
@Transactional
@Query("DELETE FROM NovelKeyword nk WHERE nk.keyword IN :keywords AND nk.novel = :novel AND nk.userId = :userId")
void deleteByKeywordsAndNovelAAndUserId(Set<Keyword> keywords, Novel novel, Long userId);

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package org.websoso.WSSServer.repository;

import java.util.List;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.websoso.WSSServer.domain.AttractivePoint;
import org.websoso.WSSServer.domain.Novel;
import org.websoso.WSSServer.domain.UserNovel;
import org.websoso.WSSServer.domain.UserNovelAttractivePoint;
Expand All @@ -13,4 +18,10 @@ public interface UserNovelAttractivePointRepository extends JpaRepository<UserNo
List<UserNovelAttractivePoint> findAllByUserNovel(UserNovel userNovel);

Integer countByUserNovel_NovelAndAttractivePoint_AttractivePointName(Novel novel, String attractivePoint);

@Modifying
@Transactional
@Query("DELETE FROM UserNovelAttractivePoint un WHERE un.userNovel = :userNovel AND un.attractivePoint IN :attractivePoints")
void deleteByAttractivePointsAndUserNovel(Set<AttractivePoint> attractivePoints, UserNovel userNovel);
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved

}
89 changes: 83 additions & 6 deletions src/main/java/org/websoso/WSSServer/service/UserNovelService.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.websoso.WSSServer.service;

import static org.websoso.WSSServer.exception.error.CustomNovelError.NOVEL_NOT_FOUND;
import static org.websoso.WSSServer.exception.error.CustomUserNovelError.NOT_EVALUATED;
import static org.websoso.WSSServer.exception.error.CustomUserNovelError.USER_NOVEL_ALREADY_EXISTS;
import static org.websoso.WSSServer.exception.error.CustomUserNovelError.USER_NOVEL_NOT_FOUND;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -21,6 +24,7 @@
import org.websoso.WSSServer.dto.keyword.KeywordGetResponse;
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.exception.exception.CustomNovelException;
import org.websoso.WSSServer.exception.exception.CustomUserNovelException;
import org.websoso.WSSServer.repository.NovelKeywordRepository;
Expand Down Expand Up @@ -48,7 +52,7 @@ public UserNovel getUserNovelOrNull(User user, Novel novel) {
return userNovelRepository.findByNovelAndUser(novel, user).orElse(null);
}

public void createUserNovel(User user, UserNovelCreateRequest request) {
public void createEvaluation(User user, UserNovelCreateRequest request) {

Novel novel = novelRepository.findById(request.novelId())
.orElseThrow(() -> new CustomNovelException(NOVEL_NOT_FOUND, "novel with the given id is not found"));
Expand All @@ -65,16 +69,89 @@ public void createUserNovel(User user, UserNovelCreateRequest request) {
user,
novel));

for (String stringAttractivePoint : request.attractivePoints()) {
createUserNovelAttractivePoints(userNovel, request.attractivePoints());
createNovelKeywords(novel, user.getUserId(), request.keywordIds());

}

public void updateEvaluation(User user, Novel novel, UserNovelUpdateRequest request) {

UserNovel userNovel = getUserNovelOrNull(user, novel);

if (userNovel == null) {
throw new CustomUserNovelException(USER_NOVEL_NOT_FOUND,
"user novel with the given user and novel is not found");
}
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved

if (userNovel.getStatus() == null) {
throw new CustomUserNovelException(NOT_EVALUATED, "this novel has not been evaluated by the user");
}
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved

updateUserNovel(userNovel, request);
updateUserNovelAttractivePoints(userNovel, request.attractivePoints());
updateNovelKeywords(novel, user.getUserId(), request.keywordIds());

}

private void updateUserNovel(UserNovel userNovel, UserNovelUpdateRequest request) {
LocalDate startDate = request.startDate() == null ? null : convertToLocalDate(request.startDate());
LocalDate endDate = request.endDate() == null ? null : convertToLocalDate(request.endDate());
Kim-TaeUk marked this conversation as resolved.
Show resolved Hide resolved

userNovel.updateUserNovel(request.userNovelRating(), request.status(), startDate, endDate);
}

private void updateUserNovelAttractivePoints(UserNovel userNovel, List<String> request) {

Set<AttractivePoint> previousAttractivePoints = userNovelAttractivePointRepository.findAllByUserNovel(userNovel)
.stream()
.map(UserNovelAttractivePoint::getAttractivePoint)
.collect(Collectors.toSet());

for (String stringAttractivePoint : request) {
AttractivePoint attractivePoint = attractivePointService.getAttractivePointByString(stringAttractivePoint);
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint));
if (previousAttractivePoints.contains(attractivePoint)) {
previousAttractivePoints.remove(attractivePoint);
} else {
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint));
}
}

for (Integer keywordId : request.keywordIds()) {
userNovelAttractivePointRepository.deleteByAttractivePointsAndUserNovel(previousAttractivePoints, userNovel);

}

private void updateNovelKeywords(Novel novel, Long userId, List<Integer> request) {

Set<Keyword> previousKeywords = novelKeywordRepository.findAllByNovelAndUserId(novel, userId)
.stream()
.map(NovelKeyword::getKeyword)
.collect(Collectors.toSet());

for (Integer keywordId : request) {
Keyword keyword = keywordService.getKeywordOrException(keywordId);
novelKeywordRepository.save(NovelKeyword.create(novel, keyword, user.getUserId()));
if (previousKeywords.contains(keyword)) {
previousKeywords.remove(keyword);
} else {
novelKeywordRepository.save(NovelKeyword.create(novel, keyword, userId));
}
}

novelKeywordRepository.deleteByKeywordsAndNovelAAndUserId(previousKeywords, novel, userId);

}

private void createUserNovelAttractivePoints(UserNovel userNovel, List<String> request) {
for (String stringAttractivePoint : request) {
AttractivePoint attractivePoint = attractivePointService.getAttractivePointByString(stringAttractivePoint);
userNovelAttractivePointRepository.save(UserNovelAttractivePoint.create(userNovel, attractivePoint));
}
}

private void createNovelKeywords(Novel novel, Long userId, List<Integer> request) {
for (Integer keywordId : request) {
Keyword keyword = keywordService.getKeywordOrException(keywordId);
novelKeywordRepository.save(NovelKeyword.create(novel, keyword, userId));
}
}

public UserNovel createUserNovelByInterest(User user, Novel novel) {
Expand All @@ -91,7 +168,7 @@ private LocalDate convertToLocalDate(String string) {
}

@Transactional(readOnly = true)
public UserNovelGetResponse getUserNovelInfo(User user, Novel novel) {
public UserNovelGetResponse getEvaluation(User user, Novel novel) {

UserNovel userNovel = getUserNovelOrNull(user, novel);
if (userNovel == null) {
Expand Down
Loading