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
1 change: 0 additions & 1 deletion README.md

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,13 @@ public void addBookAuthor(BookAuthors bookAuthor) {
}

public void replaceBookAuthors(List<BookAuthors> newMappings) {
// ✅ orphan removal을 위해 기존 매핑 clear
this.bookAuthors.clear();
for (BookAuthors m : newMappings) addBookAuthor(m);

// ✅ 새로운 매핑 추가
for (BookAuthors m : newMappings) {
addBookAuthor(m);
}
}

public void addBookGenre(BookGenres bookGenre) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
import com.example.booklog.domain.library.books.service.client.KakaoBookClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -32,6 +32,7 @@ public class BookImportService {
private final AuthorsRepository authorsRepository;
private final BookSearchConverter bookSearchConverter;
private final ObjectMapper objectMapper; // ✅ Spring Bean 주입
private final EntityManager entityManager; // ✅ orphan removal 즉시 처리용

/**
* 카카오 도서 검색 -> books/authors/book_authors 업서트 -> 검색 응답 반환
Expand Down Expand Up @@ -112,9 +113,18 @@ public BookSearchResponse searchAndUpsert(String query, int page, int size) {
.build());
}

// ✅ 기존 매핑 삭제를 먼저 DB에 반영
if (book.getId() != null) {
book.getBookAuthors().clear();
booksRepository.save(book);
entityManager.flush();
}

// ✅ 새로운 매핑 추가
book.replaceBookAuthors(mappings);

Books saved = booksRepository.save(book);

items.add(bookSearchConverter.toResponse(saved, doc));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.example.booklog.domain.search.controller;

import com.example.booklog.domain.search.dto.BookSearchResponse;
import com.example.booklog.domain.search.service.BookSearchService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

/**
* 통합 검색 API 컨트롤러
* - 도서 검색: /api/v1/search/books
* - 작가 검색: /api/v1/search/authors (추후 구현)
* - 통합 검색: /api/v1/search (추후 구현)
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/search")
public class SearchController {

private final BookSearchService bookSearchService;

/**
* 도서 검색
* GET /api/v1/search/books?query={검색어}&page={페이지}&size={크기}
*/
@GetMapping("/books")
public BookSearchResponse searchBooks(
@RequestParam String query,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size
) {
return bookSearchService.searchBooks(query, page, size);
}

/**
* 작가 검색 (추후 구현)
* GET /api/v1/search/authors?query={검색어}&page={페이지}&size={크기}
*/
// TODO: 작가 검색 구현
// @GetMapping("/authors")
// public AuthorSearchResponse searchAuthors(...)

/**
* 통합 검색 (추후 구현)
* GET /api/v1/search?query={검색어}&page={페이지}&size={크기}
*/
// TODO: 통합 검색 구현
// @GetMapping
// public IntegratedSearchResponse search(...)

/**
* 최근 검색어 조회 (추후 구현)
* GET /api/v1/search/recent
*/
// TODO: 최근 검색어 구현
// @GetMapping("/recent")
// public RecentSearchResponse getRecentSearches()

/**
* 추천 검색어 조회 (추후 구현)
* GET /api/v1/search/recommendations
*/
// TODO: 추천 검색어 구현
// @GetMapping("/recommendations")
// public RecommendationSearchResponse getRecommendations()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.booklog.domain.search.dto;

import java.time.LocalDateTime;
import java.util.List;

/**
* 도서 검색 결과 항목 DTO
*/
public record BookSearchItemResponse(
Long bookId,
String title,
String thumbnailUrl,
String publisherName,
String isbn13,
List<String> authors,
List<String> translators,
LocalDateTime publishedAt
) {}

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.booklog.domain.search.dto;

import java.util.List;

/**
* 도서 검색 응답 DTO
*/
public record BookSearchResponse(
int page,
int size,
boolean isEnd,
int totalCount,
List<BookSearchItemResponse> items
) {}

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.example.booklog.domain.search.service;

import com.example.booklog.domain.library.books.service.BookImportService;
import com.example.booklog.domain.search.dto.BookSearchItemResponse;
import com.example.booklog.domain.search.dto.BookSearchResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
* 도서 검색 서비스
* - 카카오 API를 통한 도서 검색 및 DB 업서트
*/
@Service
@RequiredArgsConstructor
public class BookSearchService {

private final BookImportService bookImportService;

/**
* 도서 검색 및 업서트
* @param query 검색어
* @param page 페이지 번호
* @param size 페이지 크기
* @return 검색 결과
*/
public BookSearchResponse searchBooks(String query, int page, int size) {
// 기존 BookImportService의 검색 로직 활용
com.example.booklog.domain.library.books.dto.BookSearchResponse legacyResponse =
bookImportService.searchAndUpsert(query, page, size);

// DTO 변환 (legacy -> search domain)
return convertToSearchResponse(legacyResponse);
}

/**
* legacy DTO를 search domain DTO로 변환
*/
private BookSearchResponse convertToSearchResponse(
com.example.booklog.domain.library.books.dto.BookSearchResponse legacyResponse) {

var items = legacyResponse.items().stream()
.map(this::convertToSearchItem)
.toList();

return new BookSearchResponse(
legacyResponse.page(),
legacyResponse.size(),
legacyResponse.isEnd(),
legacyResponse.totalCount(),
items
);
}

private BookSearchItemResponse convertToSearchItem(
com.example.booklog.domain.library.books.dto.BookSearchItemResponse legacyItem) {

return new BookSearchItemResponse(
legacyItem.bookId(),
legacyItem.title(),
legacyItem.thumbnailUrl(),
legacyItem.publisherName(),
legacyItem.isbn13(),
legacyItem.authors(),
legacyItem.translators(),
legacyItem.publishedAt()
);
}
}