-
Notifications
You must be signed in to change notification settings - Fork 2
[#145] 좋아요 추가, 삭제 api 구현 #160
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
Changes from all commits
cefee9d
4dc2869
01fcf81
3a41c0b
a272f17
e783770
55c9dc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.dadok.gaerval.domain.bookshelf.api; | ||
|
||
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.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.dadok.gaerval.domain.bookshelf.service.BookshelfLikeService; | ||
import com.dadok.gaerval.global.config.security.UserPrincipal; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequestMapping("/api/bookshelves/{bookshelfId}/like") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class BookshelfLikeController { | ||
|
||
private final BookshelfLikeService bookshelfLikeService; | ||
|
||
/*** | ||
* <Pre> | ||
* 책장 좋아요 추가 | ||
* </Pre> | ||
* @param bookshelfId | ||
* @param userPrincipal | ||
* @return status : ok | ||
*/ | ||
@PostMapping() | ||
@PreAuthorize(value = "hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") | ||
public ResponseEntity<Void> createLike(@PathVariable Long bookshelfId, | ||
@AuthenticationPrincipal UserPrincipal userPrincipal) { | ||
bookshelfLikeService.createBookshelfLike(userPrincipal.getUserId(), bookshelfId); | ||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
/*** | ||
* <Pre> | ||
* 책장 좋아요 해제 | ||
* </Pre> | ||
* @param bookshelfId | ||
* @param userPrincipal | ||
* @return status : ok | ||
*/ | ||
@DeleteMapping() | ||
@PreAuthorize(value = "hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") | ||
public ResponseEntity<Void> deleteLike(@PathVariable Long bookshelfId, | ||
@AuthenticationPrincipal UserPrincipal userPrincipal) { | ||
bookshelfLikeService.deleteBookshelfLike(userPrincipal.getUserId(), bookshelfId); | ||
return ResponseEntity.ok().build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
package com.dadok.gaerval.domain.bookshelf.entity; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
import javax.persistence.CascadeType; | ||
import javax.persistence.Column; | ||
|
@@ -54,6 +56,10 @@ public class Bookshelf extends BaseTimeColumn { | |
@OneToMany(mappedBy = "bookshelf", cascade = CascadeType.ALL, orphanRemoval = true) | ||
private final List<BookshelfItem> bookshelfItems = new ArrayList<>(); | ||
|
||
@JsonManagedReference | ||
@OneToMany(mappedBy = "bookshelf", cascade = CascadeType.PERSIST, orphanRemoval = true) | ||
private final Set<BookshelfLike> bookshelfLikes = new HashSet<>(); | ||
Comment on lines
+59
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시, 좋아요의 개수는 매번 카운트 해서 보여주기로 했나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아직 이야기된것은 없으나 그럴거라 예상합니다. 책장 상세 조회 응답에 추가할 예정입니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 그렇다면
|
||
|
||
@Column(name = "job_id", nullable = true) | ||
private Long jobId; | ||
|
||
|
@@ -77,6 +83,11 @@ public void addBookShelfItem(BookshelfItem bookshelfItem) { | |
bookshelfItems.add(bookshelfItem); | ||
} | ||
|
||
public void addBookShelfLike(BookshelfLike bookshelfLike) { | ||
CommonValidator.validateNotnull(bookshelfLike, "bookshelfLike"); | ||
bookshelfLikes.add(bookshelfLike); | ||
} | ||
|
||
public void changeIsPublic(Boolean isPublic) { | ||
CommonValidator.validateNotnull(isPublic, "isPublic"); | ||
this.isPublic = isPublic; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package com.dadok.gaerval.domain.bookshelf.entity; | ||
|
||
import java.util.Objects; | ||
|
||
import javax.persistence.Entity; | ||
import javax.persistence.FetchType; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.JoinColumn; | ||
import javax.persistence.ManyToOne; | ||
import javax.persistence.Table; | ||
import javax.persistence.UniqueConstraint; | ||
|
||
import com.dadok.gaerval.domain.bookshelf.exception.BookshelfUserNotMatchedException; | ||
import com.dadok.gaerval.domain.user.entity.User; | ||
import com.dadok.gaerval.global.common.JacocoExcludeGenerated; | ||
import com.dadok.gaerval.global.common.entity.BaseTimeColumn; | ||
import com.dadok.gaerval.global.util.CommonValidator; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity(name = "bookshelf_likes") | ||
@Table( | ||
uniqueConstraints = { | ||
@UniqueConstraint(name = "bookshelf_id_user_id_unique_key", | ||
columnNames = {"bookshelf_id", "user_id"}) | ||
} | ||
) | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class BookshelfLike extends BaseTimeColumn { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(nullable = false, name = "user_id") | ||
private User user; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(nullable = false, name = "bookshelf_id") | ||
private Bookshelf bookshelf; | ||
|
||
private BookshelfLike(User user, Bookshelf bookshelf) { | ||
CommonValidator.validateNotnull(bookshelf, "bookshelf"); | ||
CommonValidator.validateNotnull(user, "user"); | ||
validateNotOwner(user, bookshelf); | ||
this.user = user; | ||
this.bookshelf = bookshelf; | ||
bookshelf.addBookShelfLike(this); | ||
} | ||
|
||
public static BookshelfLike create(User user, Bookshelf bookshelf) { | ||
return new BookshelfLike(user, bookshelf); | ||
} | ||
|
||
private void validateNotOwner(User user, Bookshelf bookshelf) { | ||
if (Objects.equals(bookshelf.getUser().getId(), user.getId())) { | ||
throw new BookshelfUserNotMatchedException(); | ||
} | ||
} | ||
|
||
@Override | ||
@JacocoExcludeGenerated | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
BookshelfLike that = (BookshelfLike)o; | ||
return user.equals(that.user) && bookshelf.equals(that.bookshelf); | ||
} | ||
|
||
@Override | ||
@JacocoExcludeGenerated | ||
public int hashCode() { | ||
return Objects.hash(user, bookshelf); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.dadok.gaerval.domain.bookshelf.exception; | ||
|
||
import static com.dadok.gaerval.global.error.ErrorCode.*; | ||
|
||
import com.dadok.gaerval.global.error.exception.BusinessException; | ||
|
||
public class AlreadyExistsBookshelfLikeException extends BusinessException { | ||
|
||
public AlreadyExistsBookshelfLikeException(Long bookshelfId) { | ||
super(ALREADY_EXISTS_BOOKSHELF_LIKE, String.format(ALREADY_EXISTS_BOOKSHELF_LIKE.getMessage(), bookshelfId)); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.dadok.gaerval.domain.bookshelf.repository; | ||
|
||
import java.util.Optional; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import com.dadok.gaerval.domain.bookshelf.entity.BookshelfLike; | ||
|
||
public interface BookshelfLikeRepository extends JpaRepository<BookshelfLike, Long>, BookshelfLikeSupport { | ||
|
||
Optional<BookshelfLike> findByUserIdAndBookshelfId(Long userId, Long bookshelfId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.dadok.gaerval.domain.bookshelf.repository; | ||
|
||
public interface BookshelfLikeSupport { | ||
|
||
boolean existsLike(Long bookshelfId, Long userId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.dadok.gaerval.domain.bookshelf.repository; | ||
|
||
import static com.dadok.gaerval.domain.bookshelf.entity.QBookshelfLike.*; | ||
|
||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequiredArgsConstructor | ||
public class BookshelfLikeSupportImpl implements BookshelfLikeSupport { | ||
|
||
private final JPAQueryFactory query; | ||
|
||
@Override | ||
public boolean existsLike(Long bookshelfId, Long userId) { | ||
Integer fetchOne = query.selectOne().from(bookshelfLike) | ||
.where(bookshelfLike.bookshelf.id.eq(bookshelfId), bookshelfLike.user.id.eq(userId)) | ||
.fetchFirst(); | ||
|
||
return fetchOne != null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.dadok.gaerval.domain.bookshelf.service; | ||
|
||
public interface BookshelfLikeService { | ||
|
||
void createBookshelfLike(Long userId, Long bookshelfId); | ||
|
||
void deleteBookshelfLike(Long userId, Long bookshelfId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.dadok.gaerval.domain.bookshelf.service; | ||
|
||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import com.dadok.gaerval.domain.bookshelf.entity.Bookshelf; | ||
import com.dadok.gaerval.domain.bookshelf.entity.BookshelfLike; | ||
import com.dadok.gaerval.domain.bookshelf.exception.AlreadyExistsBookshelfLikeException; | ||
import com.dadok.gaerval.domain.bookshelf.repository.BookshelfLikeRepository; | ||
import com.dadok.gaerval.domain.user.entity.User; | ||
import com.dadok.gaerval.domain.user.service.UserService; | ||
import com.dadok.gaerval.global.error.exception.ResourceNotfoundException; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class DefaultBookshelfLikeService implements BookshelfLikeService { | ||
|
||
private final BookshelfLikeRepository bookshelfLikeRepository; | ||
|
||
private final UserService userService; | ||
|
||
private final BookshelfService bookshelfService; | ||
|
||
@Override | ||
public void createBookshelfLike(Long userId, Long bookshelfId) { | ||
User user = userService.getById(userId); | ||
Bookshelf bookshelf = bookshelfService.getById(bookshelfId); | ||
if (bookshelfLikeRepository.existsLike(bookshelfId, userId)) { | ||
throw new AlreadyExistsBookshelfLikeException(bookshelf.getId()); | ||
} | ||
bookshelfLikeRepository.save(BookshelfLike.create(user, bookshelf)); | ||
} | ||
|
||
@Override | ||
public void deleteBookshelfLike(Long userId, Long bookshelfId) { | ||
BookshelfLike bookshelfLike = bookshelfLikeRepository.findByUserIdAndBookshelfId(userId, bookshelfId) | ||
.orElseThrow(() -> new ResourceNotfoundException(BookshelfLike.class)); | ||
bookshelfLikeRepository.deleteById(bookshelfLike.getId()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
만약에, 집 컴퓨터와 폰으로 좋아요를 안 누른 상태의 같은 페이지를 열고 집 컴퓨터로만 좋아요를 눌렀다고 가정해볼게요.
해당 상태로 폰을 들고 밖으로 나가서 페이지를 본다면 해당 책장에 좋아요가 안눌려있겠죠?
이 때에 다시 좋아요를 누른다면 서버의 좋아요 상태는 어떻게 바뀌어야 할까요?
클라이언트는 무엇을 보고 어떤 메소드를 선택해서 요청을 보내야 할까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이부분은 무슨 말씀일까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youngjijang
같은 페이지를 다른 브라우저에서 보고 있을 때,
서로 다른 상태를 가지고있을 때 모바일 브라우저로 다시 좋아요를 누른다면
프론트엔드와 백엔드는 어떤 액션을 취해야 하는지 생각을 여쭤본겁니다!