Skip to content

Commit

Permalink
Feat: 책 좋아요 기능 구현하기 (#96)
Browse files Browse the repository at this point in the history
* #94 - refactor: Requset Dto 이름 변경

등록시 말고도 사용하기 위해

* #94 - feat: 도서 좋아요 기능 구현

DB에 등록되지 않은 책 좋아요시 api 통신을 통해 책을 등록후 좋아요를 추가한다.

* #94 - test: 도서 좋아요 기능 테스트 작성
  • Loading branch information
GGHDMS authored Feb 9, 2024
1 parent 97286a5 commit 3d0dfdf
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cotato.bookitlist.book.controller;

import cotato.bookitlist.book.annotation.IsValidIsbn;
import cotato.bookitlist.book.dto.request.BookRegisterRequest;
import cotato.bookitlist.book.dto.request.BookIsbn13Request;
import cotato.bookitlist.book.dto.response.BookApiListResponse;
import cotato.bookitlist.book.dto.response.BookApiResponse;
import cotato.bookitlist.book.dto.response.BookListResponse;
Expand Down Expand Up @@ -67,7 +67,7 @@ public ResponseEntity<BookResponse> getBook(

@PostMapping
public ResponseEntity<Void> registerBook(
@Valid @RequestBody BookRegisterRequest request
@Valid @RequestBody BookIsbn13Request request
) {
Long bookId = bookService.registerBook(request.isbn13());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cotato.bookitlist.book.controller;

import cotato.bookitlist.book.dto.request.BookIsbn13Request;
import cotato.bookitlist.book.service.BookLikeService;
import cotato.bookitlist.config.security.jwt.AuthDetails;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;

@RestController
@RequestMapping("/books/likes")
@RequiredArgsConstructor
public class BookLikeController {

private final BookLikeService bookLikeService;

@PostMapping
public ResponseEntity<Void> registerLike(
@Valid @RequestBody BookIsbn13Request request,
@AuthenticationPrincipal AuthDetails details
) {
Long bookLikeId = bookLikeService.registerLike(request.isbn13(), details.getId());

URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(bookLikeId)
.toUri();

return ResponseEntity.created(location).build();
}

}
6 changes: 5 additions & 1 deletion src/main/java/cotato/bookitlist/book/domain/entity/Book.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class Book extends BaseEntity {
private Integer price;
private String cover;

private int markCount = 0;
private int likeCount = 0;

private boolean deleted = false;

Expand All @@ -53,4 +53,8 @@ public static Book of(String title, String author, String publisher, LocalDate p
return new Book(title, author, publisher, pubDate, description, link, isbn13, price, cover);
}

public void increaseLikeCount() {
this.likeCount++;
}

}
39 changes: 39 additions & 0 deletions src/main/java/cotato/bookitlist/book/domain/entity/BookLike.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cotato.bookitlist.book.domain.entity;

import cotato.bookitlist.member.domain.Member;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BookLike {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "book_like_id")
private Long id;

@ManyToOne
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne
@JoinColumn(name = "book_id")
private Book book;

private BookLike(Book book, Member member) {
this.book = book;
this.member = member;
}

public static BookLike of(Book book, Member member ) {
return new BookLike(book, member);
}

public void increaseBookLikeCount() {
book.increaseLikeCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import cotato.bookitlist.book.annotation.IsValidIsbn;

public record BookRegisterRequest(
public record BookIsbn13Request(
@IsValidIsbn
String isbn13
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cotato.bookitlist.book.repository;

import cotato.bookitlist.book.domain.entity.BookLike;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookLikeRepository extends JpaRepository<BookLike, Long> {
}
36 changes: 36 additions & 0 deletions src/main/java/cotato/bookitlist/book/service/BookLikeService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cotato.bookitlist.book.service;

import cotato.bookitlist.book.domain.entity.Book;
import cotato.bookitlist.book.domain.entity.BookLike;
import cotato.bookitlist.book.repository.BookLikeRepository;
import cotato.bookitlist.book.repository.BookRepository;
import cotato.bookitlist.member.domain.Member;
import cotato.bookitlist.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class BookLikeService {

private final BookService bookService;
private final BookRepository bookRepository;
private final BookLikeRepository bookLikeRepository;
private final MemberRepository memberRepository;

public Long registerLike(String isbn13, Long memberId) {
Book book = bookRepository.findByIsbn13(isbn13)
.orElseGet(() -> bookRepository.getReferenceById(
bookService.registerBook(isbn13)
));

Member member = memberRepository.getReferenceById(memberId);

BookLike bookLike = BookLike.of(book, member);
bookLike.increaseBookLikeCount();

return bookLikeRepository.save(bookLike).getId();
}
}
25 changes: 0 additions & 25 deletions src/main/java/cotato/bookitlist/mark/domain/MarkBook.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cotato.bookitlist.book.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import cotato.bookitlist.book.dto.request.BookRegisterRequest;
import cotato.bookitlist.book.dto.request.BookIsbn13Request;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -144,7 +144,7 @@ void givenInvalidIsbn13_whenExternalFinding_thenReturnErrorResponse() throws Exc
@DisplayName("[DB] isbn13을 통해 책을 등록한다.")
void givenIsbn13_whenRegisteringBook_thenReturnBookResponse() throws Exception {
//given
BookRegisterRequest request = new BookRegisterRequest("9791197045318");
BookIsbn13Request request = new BookIsbn13Request("9791197045318");

//when&then
mockMvc.perform(post("/books")
Expand All @@ -161,7 +161,7 @@ void givenIsbn13_whenRegisteringBook_thenReturnBookResponse() throws Exception {
@DisplayName("[DB] 이미 등록된 책을 등록하면 에러를 반환한다.")
void givenRegisteredIsbn13_whenRegisteringBook_thenReturnErrorResponse() throws Exception {
//given
BookRegisterRequest request = new BookRegisterRequest("9788931514803");
BookIsbn13Request request = new BookIsbn13Request("9788931514803");

//when&then
mockMvc.perform(post("/books")
Expand All @@ -178,7 +178,7 @@ void givenRegisteredIsbn13_whenRegisteringBook_thenReturnErrorResponse() throws
@DisplayName("[DB] 잘못된 형식의 isbn13으로 등록하면 에러를 반환한다.")
void givenInvalidIsbn13_whenRegisteringBook_thenReturnErrorResponse() throws Exception {
//given
BookRegisterRequest request = new BookRegisterRequest("9788931514invalid");
BookIsbn13Request request = new BookIsbn13Request("9788931514invalid");

//when&then
mockMvc.perform(post("/books")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cotato.bookitlist.book.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import cotato.bookitlist.annotation.WithCustomMockUser;
import cotato.bookitlist.book.dto.request.BookIsbn13Request;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("도서 좋아요 컨트롤러 테스트")
@ActiveProfiles("test")
class BookLikeControllerTest {

@Autowired
MockMvc mockMvc;

@Autowired
ObjectMapper objectMapper;

@Test
@WithCustomMockUser
@DisplayName("도서 좋아요를 등록한다.")
void givenIsbn13_whenRegisteringLike_thenRegisterLike() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9791127278199");

//when & then
mockMvc.perform(post("/books/likes")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsBytes(request))
)
.andExpect(status().isCreated())
.andExpect(header().exists("Location"))
.andDo(print())
;
}

@Test
@WithCustomMockUser
@DisplayName("DB에 등록되지 않은 도서 좋아요를 등록한다.")
void givenIsbn13NonExistedInDB_whenRegisteringLike_thenRegisterLike() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9788966262281");

//when & then
mockMvc.perform(post("/books/likes")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsBytes(request))
)
.andExpect(status().isCreated())
.andExpect(header().exists("Location"))
.andDo(print())
;
}
}

Loading

0 comments on commit 3d0dfdf

Please sign in to comment.