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

[Refactor] 베스트 셀러 조회 API 리팩토링 #123

Merged
merged 7 commits into from
Jun 18, 2024
13 changes: 7 additions & 6 deletions src/main/java/com/jisungin/api/book/BookController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

import com.jisungin.api.ApiResponse;
import com.jisungin.api.book.request.BookCreateRequest;
import com.jisungin.api.book.request.BookPageRequest;
import com.jisungin.application.OffsetLimit;
import com.jisungin.application.PageResponse;
import com.jisungin.application.book.BestSellerService;
import com.jisungin.application.book.BookService;
import com.jisungin.application.book.response.BestSellerResponse;
import com.jisungin.application.book.response.BookResponse;
import com.jisungin.application.book.response.BookFindAllResponse;
import com.jisungin.application.book.response.BookResponse;
import com.jisungin.application.book.response.BookWithRankingResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -44,8 +42,11 @@ public ApiResponse<PageResponse<BookFindAllResponse>> getBooks(
}

@GetMapping("/books/best-seller")
public ApiResponse<PageResponse<BestSellerResponse>> getBestSellers(@ModelAttribute BookPageRequest page) {
return ApiResponse.ok(bestSellerService.getBestSellers(page.toService()));
public ApiResponse<PageResponse<BookWithRankingResponse>> getBestSellers(
@RequestParam(required = false, defaultValue = "1") Integer page,
@RequestParam(required = false, defaultValue = "5") Integer size
) {
return ApiResponse.ok(bestSellerService.getBestSellers(OffsetLimit.ofRange(page, size)));
}

@PostMapping("/books")
Expand Down
30 changes: 0 additions & 30 deletions src/main/java/com/jisungin/api/book/request/BookPageRequest.java

This file was deleted.

7 changes: 7 additions & 0 deletions src/main/java/com/jisungin/application/OffsetLimit.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public static OffsetLimit of(Integer page, Integer size, String order) {
.build();
}

public static OffsetLimit ofRange(Integer page, Integer size) {
return OffsetLimit.builder()
.offset(calculateOffset(page, size))
.limit(page * size - 1)
.build();
}

private static Integer calculateOffset(Integer page, Integer size) {
return (max(1, page) - 1) * min(size, MAX_SIZE);
}
Expand Down
23 changes: 14 additions & 9 deletions src/main/java/com/jisungin/application/book/BestSellerService.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.jisungin.application.book;

import com.jisungin.application.OffsetLimit;
import com.jisungin.application.PageResponse;
import com.jisungin.application.book.event.BestSellerUpdatedEvent;
import com.jisungin.application.book.request.BookServicePageRequest;
import com.jisungin.application.book.response.BestSellerResponse;
import com.jisungin.application.book.response.BookWithRankingResponse;
import com.jisungin.domain.book.repository.BestSellerRepository;
import com.jisungin.infra.crawler.CrawledBook;
import com.jisungin.infra.crawler.Crawler;
import com.jisungin.infra.crawler.CrawlingBook;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
Expand All @@ -20,16 +21,20 @@ public class BestSellerService {
private final BestSellerRepository bestSellerRepository;
private final ApplicationEventPublisher eventPublisher;

public PageResponse<BestSellerResponse> getBestSellers(BookServicePageRequest page) {
return bestSellerRepository.findBestSellerByPage(page);
}
public PageResponse<BookWithRankingResponse> getBestSellers(OffsetLimit offsetLimit) {
List<BookWithRankingResponse> response = bestSellerRepository.findBooksWithRank(offsetLimit.getOffset(),
offsetLimit.getLimit());

Long count = bestSellerRepository.count();

return PageResponse.of(response.size(), count, response);
}

public void updateBestSellers() {
Map<Long, CrawlingBook> crawledBooks = crawler.crawlBestSellerBook();
Map<Long, CrawledBook> crawledBookMap = crawler.crawlBestSellerBook();

bestSellerRepository.updateAll(crawledBooks);
eventPublisher.publishEvent(new BestSellerUpdatedEvent(crawledBooks));
bestSellerRepository.updateAll(crawledBookMap);
eventPublisher.publishEvent(new BestSellerUpdatedEvent(crawledBookMap));
}

}
17 changes: 6 additions & 11 deletions src/main/java/com/jisungin/application/book/BookService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
import com.jisungin.application.OffsetLimit;
import com.jisungin.application.PageResponse;
import com.jisungin.application.book.request.BookCreateServiceRequest;
import com.jisungin.application.book.request.BookCreateServiceRequests;
import com.jisungin.application.book.response.BookFindAllResponse;
import com.jisungin.application.book.response.BookResponse;
import com.jisungin.application.talkroom.response.TalkRoomQueryResponse;
import com.jisungin.domain.book.Book;
import com.jisungin.domain.book.repository.BookRepository;
import com.jisungin.domain.rating.repository.RatingRepository;
import com.jisungin.exception.BusinessException;
import com.jisungin.exception.ErrorCode;
import com.jisungin.infra.crawler.Crawler;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -56,17 +57,11 @@ public BookResponse createBook(BookCreateServiceRequest request) {
}

@Transactional
public void addNewBooks(List<BookCreateServiceRequest> requests) {
requests.stream()
.filter(request -> !bookRepository.existsBookByIsbn(request.getIsbn()))
.map(BookCreateServiceRequest::toEntity)
.forEach(bookRepository::save);
}
public void addNewBooks(BookCreateServiceRequests requests) {
Set<String> existIsbns = bookRepository.findExistIsbns(requests.getIsbns());
List<Book> newBooks = requests.toEntitiesNotInclude(existIsbns);

private List<Long> extractTalkRoomIds(List<TalkRoomQueryResponse> talkRooms) {
return talkRooms.stream()
.map(TalkRoomQueryResponse::getId)
.toList();
bookRepository.saveAll(newBooks);
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package com.jisungin.application.book.event;

import com.jisungin.infra.crawler.CrawlingBook;
import com.jisungin.application.book.request.BookCreateServiceRequest;
import com.jisungin.infra.crawler.CrawledBook;
import java.util.List;
import java.util.Map;
import lombok.Getter;

@Getter
public class BestSellerUpdatedEvent {

private final Map<Long, CrawlingBook> crawledBooks;
private final Map<Long, CrawledBook> crawledBookMap;

public BestSellerUpdatedEvent(Map<Long, CrawlingBook> crawledBooks) {
this.crawledBooks = crawledBooks;
public BestSellerUpdatedEvent(Map<Long, CrawledBook> crawledBookMap) {
this.crawledBookMap = crawledBookMap;
}

public List<BookCreateServiceRequest> getServiceRequests() {
return crawledBookMap.values().stream()
.map(CrawledBook::toServiceRequest)
.toList();
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.jisungin.application.book.event;

import com.jisungin.application.book.BookService;
import com.jisungin.application.book.request.BookCreateServiceRequest;
import com.jisungin.infra.crawler.CrawlingBook;
import java.util.List;
import java.util.Map;
import com.jisungin.application.book.request.BookCreateServiceRequests;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
Expand All @@ -17,13 +14,7 @@ public class BestSellerUpdatedEventListener {

@EventListener
public void handleBestSellerUpdatedEvent(BestSellerUpdatedEvent event) {
Map<Long, CrawlingBook> crawledBook = event.getCrawledBooks();

List<BookCreateServiceRequest> bookCreateServiceRequests = crawledBook.values().stream()
.map(CrawlingBook::toServiceRequest)
.toList();

bookService.addNewBooks(bookCreateServiceRequests);
bookService.addNewBooks(BookCreateServiceRequests.of(event.getServiceRequests()));
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.jisungin.application.book.request;

import com.jisungin.domain.book.Book;
import com.jisungin.infra.crawler.CrawlingBook;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.jisungin.application.book.request;

import com.jisungin.domain.book.Book;
import java.util.List;
import java.util.Set;
import lombok.Builder;
import lombok.Getter;

@Getter
public class BookCreateServiceRequests {

private final List<BookCreateServiceRequest> requests;

@Builder
private BookCreateServiceRequests(List<BookCreateServiceRequest> requests) {
this.requests = requests;
}

public static BookCreateServiceRequests of(List<BookCreateServiceRequest> requests) {
return BookCreateServiceRequests.builder()
.requests(requests)
.build();
}

public List<String> getIsbns() {
return requests.stream()
.map(BookCreateServiceRequest::getIsbn)
.toList();
}

public List<Book> toEntitiesNotInclude(Set<String> existIsbns) {
return requests.stream()
.filter(request -> !existIsbns.contains(request.getIsbn()))
.map(BookCreateServiceRequest::toEntity)
.toList();
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.jisungin.application.book.response;

import com.jisungin.infra.crawler.CrawledBook;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BookWithRankingResponse {

private Long ranking;
private String isbn;
private String title;
private String publisher;
private String thumbnail;
private String[] authors;
private LocalDateTime dateTime;

@Builder
private BookWithRankingResponse(Long ranking, String isbn, String title, String publisher, String thumbnail,
String[] authors, LocalDateTime dateTime) {
this.ranking = ranking;
this.isbn = isbn;
this.title = title;
this.publisher = publisher;
this.thumbnail = thumbnail;
this.authors = authors;
this.dateTime = dateTime;
}

public static BookWithRankingResponse of(Long ranking, String isbn, String title, String publisher, String thumbnail,
String[] authors, LocalDateTime dateTime) {
return BookWithRankingResponse.builder()
.ranking(ranking)
.isbn(isbn)
.title(title)
.publisher(publisher)
.thumbnail(thumbnail)
.authors(authors)
.dateTime(dateTime)
.build();
}

public static BookWithRankingResponse ofRankIncrement(Long ranking, CrawledBook crawledBook) {
return BookWithRankingResponse.builder()
.ranking(ranking + 1)
.isbn(crawledBook.getIsbn())
.title(crawledBook.getTitle())
.publisher(crawledBook.getPublisher())
.thumbnail(crawledBook.getThumbnail())
.authors(crawledBook.getAuthors())
.dateTime(crawledBook.getDateTime())
.build();
}

}
Loading
Loading