diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 5edd5da69..525ecbb7f 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -8,6 +8,8 @@ on: permissions: contents: read + checks: write + pull-requests: write env: RESOURCE_PATH: src/main/resources @@ -19,13 +21,6 @@ jobs: steps: - uses: actions/checkout@v4 -# - name: Cache Gradle packages -# uses: actions/cache@v3 -# with: -# path: ~/.gradle/caches -# key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} -# restore-keys: gradle-${{ runner.os }}- - - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -50,4 +45,17 @@ jobs: redis-version: 7 - name: ๐Ÿ˜ build with Gradle - run: ./gradlew build \ No newline at end of file + run: ./gradlew build --parallel --stacktrace + + - name: โœ‰๏ธ Post test results as a comment on the PR + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + + - name: ๐Ÿ™ Annotate failed test cases on changed lines + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + token: ${{ github.token }} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 47c529960..37b99f167 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -4,13 +4,12 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Pattern; -import konkuk.thip.book.adapter.in.web.response.BookDetailSearchResponse; -import konkuk.thip.book.adapter.in.web.response.BookMostSearchResponse; -import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; -import konkuk.thip.book.adapter.in.web.response.BookSearchListResponse; +import konkuk.thip.book.adapter.in.web.response.*; import konkuk.thip.book.application.port.in.BookMostSearchUseCase; import konkuk.thip.book.application.port.in.BookRecruitingRoomsUseCase; import konkuk.thip.book.application.port.in.BookSearchUseCase; +import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.common.security.annotation.UserId; import konkuk.thip.common.swagger.annotation.ExceptionDescription; @@ -32,6 +31,7 @@ public class BookQueryController { private final BookSearchUseCase bookSearchUseCase; private final BookMostSearchUseCase bookMostSearchUseCase; private final BookRecruitingRoomsUseCase bookRecruitingRoomsUseCase; + private final BookSelectableListUseCase bookSelectableListUseCase; @Operation( summary = "์ฑ… ๊ฒ€์ƒ‰๊ฒฐ๊ณผ ์กฐํšŒ", @@ -87,4 +87,19 @@ public BaseResponse showRecruitingRoomsWithBook( return BaseResponse.ok(bookRecruitingRoomsUseCase.getRecruitingRoomsWithBook(isbn, cursor)); } + @Operation( + summary = "์ €์žฅํ•œ ์ฑ… ๋˜๋Š” ์ฐธ์—ฌ ์ค‘ ๋ชจ์ž„์˜ ์ฑ… ์กฐํšŒ", + description = "์ €์žฅํ•œ ์ฑ… ๋˜๋Š” ์ฐธ์—ฌ ์ค‘์ธ ๋ชจ์ž„์˜ ์ฑ…์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. (๋ฐฉ ์ƒ์„ฑ์‹œ ์ฑ… ์„ ํƒ ํ™”๋ฉด ํŽ˜์ด์ง€)" + ) + @ExceptionDescription(BOOK_SELECTABLE_LIST) + @GetMapping("/books/selectable-list") + public BaseResponse showSelectableBookList( + @Parameter(description = "์ €์žฅํ•œ ์ฑ… ๋˜๋Š” ์ฐธ์—ฌ ์ค‘์ธ ๋ชจ์ž„์˜ ์ฑ…์„ ๊ตฌ๋ถ„ํ•˜๋Š” ํ•„๋“œ (SAVED : ์ €์žฅํ•œ ์ฑ… / JOINING : ๋ชจ์ž„ ๋ฐฉ์˜ ์ฑ…)", example = "SAVED") @RequestParam final String type, + @Parameter(hidden = true) @UserId final Long userId + ) { + return BaseResponse.ok( + BookSelectableListResponse.of(bookSelectableListUseCase.getSelectableBookList(BookSelectableType.from(type), userId)) + ); + } + } diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java new file mode 100644 index 000000000..b45bae078 --- /dev/null +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java @@ -0,0 +1,13 @@ +package konkuk.thip.book.adapter.in.web.response; + +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; + +import java.util.List; + +public record BookSelectableListResponse( + List bookList +) { + public static BookSelectableListResponse of(List bookSelectableResults) { + return new BookSelectableListResponse(bookSelectableResults); + } +} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java b/src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java similarity index 86% rename from src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java rename to src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java index 449786262..b910d2441 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java +++ b/src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java @@ -1,7 +1,6 @@ -package konkuk.thip.saved.adapter.out.jpa; +package konkuk.thip.book.adapter.out.jpa; import jakarta.persistence.*; -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.common.entity.BaseJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import lombok.*; diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java index 3c8ef0358..aef360221 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java @@ -7,20 +7,25 @@ import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.Optional; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NOT_FOUND; -import static konkuk.thip.common.exception.code.ErrorCode.ROOM_NOT_FOUND; +import static konkuk.thip.common.exception.code.ErrorCode.*; @Repository @RequiredArgsConstructor public class BookCommandPersistenceAdapter implements BookCommandPort { + private final UserJpaRepository userJpaRepository; private final RoomJpaRepository roomJpaRepository; private final BookJpaRepository bookJpaRepository; + private final SavedBookJpaRepository savedBookJpaRepository; private final BookMapper bookMapper; @Override @@ -61,4 +66,24 @@ public Book findBookByRoomId(Long roomId) { ).getBookJpaEntity(); return bookMapper.toDomainEntity(bookJpaEntity); } + + // ์‚ฌ์šฉ์ž๊ฐ€ ์ฑ…์„ ์ €์žฅ + @Override + public void saveSavedBook(Long userId, Long bookId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + BookJpaEntity book = bookJpaRepository.findById(bookId) + .orElseThrow(() -> new EntityNotFoundException(BOOK_NOT_FOUND)); + SavedBookJpaEntity entity = SavedBookJpaEntity.builder() + .userJpaEntity(user) + .bookJpaEntity(book) + .build(); + savedBookJpaRepository.save(entity); + } + + // ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅํ•œ ์ฑ…์„ ์‚ญ์ œ + @Override + public void deleteSavedBook(Long userId, Long bookId) { + savedBookJpaRepository.deleteByUserIdAndBookId(userId, bookId); + } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java index 87e41c31b..26075f1f5 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java @@ -2,15 +2,52 @@ import konkuk.thip.book.adapter.out.mapper.BookMapper; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.book.application.port.out.BookQueryPort; +import konkuk.thip.book.domain.Book; +import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.stream.Collectors; + +import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; + @Repository @RequiredArgsConstructor public class BookQueryPersistenceAdapter implements BookQueryPort { + private final UserJpaRepository userJpaRepository; private final BookJpaRepository bookJpaRepository; + private final SavedBookJpaRepository savedBookJpaRepository; private final BookMapper bookMapper; + @Override + public boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId) { + return savedBookJpaRepository.existsByUserIdAndBookId(userId, bookId); + } + + @Override + public List findSavedBooksByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + return bookJpaRepository.findSavedBooksByUserId(user.getUserId()).stream() + .map(bookMapper::toDomainEntity) + .collect(Collectors.toList()); + } + + @Override + public List findJoiningRoomsBooksByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + return bookJpaRepository.findJoiningRoomsBooksByUserId(user.getUserId()) + .stream() + .map(bookMapper::toDomainEntity) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java index c7b763298..9bca9cf9b 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java @@ -2,11 +2,26 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; public interface BookJpaRepository extends JpaRepository { Optional findByIsbn(String isbn); - List findByIsbnIn(List isbnList); + + @Query("SELECT DISTINCT b FROM BookJpaEntity b " + + "JOIN SavedBookJpaEntity s ON s.bookJpaEntity.bookId = b.bookId " + + "WHERE s.userJpaEntity.userId = :userId " + + "ORDER BY s.createdAt DESC") + List findSavedBooksByUserId(Long userId); + + @Query("SELECT DISTINCT b FROM BookJpaEntity b " + + "JOIN RoomJpaEntity r ON r.bookJpaEntity.bookId = b.bookId " + + "JOIN RoomParticipantJpaEntity rp ON rp.roomJpaEntity.roomId = r.roomId " + + "WHERE rp.userJpaEntity.userId = :userId " + + "AND r.status = 'ACTIVE' " + + "AND r.startDate <= CURRENT_TIMESTAMP " + // ์ง„ํ–‰ ์ค‘์ธ ๋ฐฉ๋งŒ ์กฐํšŒ (๋ชจ์ง‘ ์ค‘ / ๋งŒ๋ฃŒ๋œ ๋ฐฉ x) + "ORDER BY r.roomPercentage DESC") // ๋ฐฉ์˜ ์ง„ํ–‰๋ฅ ์ด ๋†’์€ ์ˆœ์„œ๋กœ ์ •๋ ฌ + List findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java new file mode 100644 index 000000000..19202fb07 --- /dev/null +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java @@ -0,0 +1,16 @@ +package konkuk.thip.book.adapter.out.persistence.repository; + +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +public interface SavedBookJpaRepository extends JpaRepository { + @Query("SELECT CASE WHEN COUNT(s) > 0 THEN true ELSE false END FROM SavedBookJpaEntity s " + + "WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") + boolean existsByUserIdAndBookId(Long userId, Long bookId); + + @Modifying + @Query("DELETE FROM SavedBookJpaEntity s WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") + void deleteByUserIdAndBookId(Long userId, Long bookId); +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 9ff727b82..e65264d07 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -1,6 +1,8 @@ package konkuk.thip.book.application.mapper; import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; +import konkuk.thip.book.domain.Book; import konkuk.thip.common.util.DateUtil; import konkuk.thip.room.application.port.out.dto.RoomQueryDto; import org.mapstruct.Mapper; @@ -23,4 +25,14 @@ public interface BookQueryMapper { BookRecruitingRoomsResponse.RecruitingRoomDto toRecruitingRoomDto(RoomQueryDto dto); List toRecruitingRoomDtoList(List roomDtos); + + @Mapping(target = "bookId", source = "book.id") + @Mapping(target = "bookTitle", source = "book.title") + @Mapping(target = "authorName", source = "book.authorName") + @Mapping(target = "publisher", source = "book.publisher") + @Mapping(target = "bookImageUrl", source = "book.imageUrl") + @Mapping(target = "isbn", source = "book.isbn") + BookSelectableResult toBookSelectableResult(Book book); + + List toBookSelectableResultList(List books); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java new file mode 100644 index 000000000..794453c23 --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java @@ -0,0 +1,10 @@ +package konkuk.thip.book.application.port.in; + +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; + +import java.util.List; + +public interface BookSelectableListUseCase { + List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); +} diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java new file mode 100644 index 000000000..986767beb --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java @@ -0,0 +1,11 @@ +package konkuk.thip.book.application.port.in.dto; + +public record BookSelectableResult( + Long bookId, + String bookTitle, + String authorName, + String publisher, + String bookImageUrl, + String isbn +) { +} diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java new file mode 100644 index 000000000..290e88072 --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java @@ -0,0 +1,27 @@ +package konkuk.thip.book.application.port.in.dto; + +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; +import lombok.Getter; + +@Getter +public enum BookSelectableType { + SAVED("SAVED"), // ์ €์žฅ๋œ ์ฑ… + JOINING("JOINING") // ์ฐธ์—ฌ ์ค‘์ธ ๋ชจ์ž„ ๋ฐฉ์˜ ์ฑ… + ; + + private final String type; + + BookSelectableType(String type) { + this.type = type; + } + + public static BookSelectableType from(String type) { + for (BookSelectableType bookSelectableType : BookSelectableType.values()) { + if (bookSelectableType.type.equals(type)) { + return bookSelectableType; + } + } + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("ํƒ€์ž…์€ SAVED ๋˜๋Š” JOINING์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ํƒ€์ž…: " + type)); + } +} diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java index f6343f700..c3b85b1a8 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java @@ -23,4 +23,8 @@ default Book getByIsbnOrThrow(String isbn){ void updateForPageCount(Book book); Book findBookByRoomId(Long roomId); + + void saveSavedBook(Long userId, Long bookId); + void deleteSavedBook(Long userId, Long bookId); + } diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java index 186919761..bb9e0ab00 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java @@ -1,4 +1,14 @@ package konkuk.thip.book.application.port.out; +import konkuk.thip.book.domain.Book; + +import java.util.List; + public interface BookQueryPort { + + boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId); + + List findSavedBooksByUserId(Long userId); + + List findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java b/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java index 3df7edc9c..9711da9bf 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java @@ -6,8 +6,6 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.application.port.out.BookRedisCommandPort; import konkuk.thip.book.application.port.out.BookRedisQueryPort; -import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; @@ -17,6 +15,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service @@ -53,26 +52,30 @@ public void updateDailySearchRank() { // Top 5 ์ƒ์„ธ์ •๋ณด ์ €์žฅ ์ถ”๊ฐ€ List bookRankDetails = new ArrayList<>(); - int rank = 1; + AtomicInteger rank = new AtomicInteger(1); for (String isbn : isbns) { - try { - Book book = bookCommandPort.getByIsbnOrThrow(isbn); - bookRankDetails.add(BookMostSearchResult.BookRankInfo.builder() - .rank(rank++) - .title(book.getTitle()) - .imageUrl(book.getImageUrl()) - .isbn(isbn) - .build()); - } catch (EntityNotFoundException e) { - // DB์— ์—†์œผ๋ฉด Naver API์—์„œ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ - NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); - bookRankDetails.add(BookMostSearchResult.BookRankInfo.builder() - .rank(rank++) - .title(naverResult.title()) - .imageUrl(naverResult.imageUrl()) - .isbn(isbn) - .build()); - } + bookCommandPort.findByIsbn(isbn).ifPresentOrElse( + book -> bookRankDetails.add( + BookMostSearchResult.BookRankInfo.builder() + .rank(rank.getAndIncrement()) + .title(book.getTitle()) + .imageUrl(book.getImageUrl()) + .isbn(isbn) + .build() + ), + () -> { + // DB์— ์—†์œผ๋ฉด Naver API์—์„œ ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ + NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + bookRankDetails.add( + BookMostSearchResult.BookRankInfo.builder() + .rank(rank.getAndIncrement()) + .title(naverResult.title()) + .imageUrl(naverResult.imageUrl()) + .isbn(isbn) + .build() + ); + } + ); } bookRedisCommandPort.saveBookSearchRankDetail(bookRankDetails, yesterday); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java index d99f550ac..cfbdc97f7 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java @@ -6,16 +6,13 @@ import konkuk.thip.book.application.port.in.dto.BookIsSavedResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; -import konkuk.thip.book.domain.SavedBooks; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NOT_SAVED_DB_CANNOT_DELETE; +import static konkuk.thip.common.exception.code.ErrorCode.*; @Service @RequiredArgsConstructor @@ -23,57 +20,58 @@ public class BookSavedService implements BookSavedUseCase { private final BookApiQueryPort bookApiQueryPort; private final BookCommandPort bookCommandPort; - private final SavedCommandPort savedCommandPort; - private final SavedQueryPort savedQueryPort; + private final BookQueryPort bookQueryPort; @Override @Transactional - public BookIsSavedResult changeSavedBook(String isbn, boolean isSave, Long userId) { + public BookIsSavedResult changeSavedBook(String isbn, boolean isSaveRequest, Long userId) { - Book book; + // Book ์กฐํšŒ ์‹œ๋„ + Book book = bookCommandPort.findByIsbn(isbn) + .orElseGet(() -> { + if (!isSaveRequest) { // ์‚ญ์ œ ์š”์ฒญ์ธ๋ฐ ์ฑ…์ด ์—†์œผ๋ฉด ์ €์žฅํ•˜์ง€ ์•Š์€ ์ฑ…์ด๋ฏ€๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ + throw new BusinessException(BOOK_NOT_SAVED_DB_CANNOT_DELETE); + } + return registerBookByIsbn(isbn); + }); - try { - // Book ์กฐํšŒ ์‹œ๋„ - book = bookCommandPort.getByIsbnOrThrow(isbn); - } catch (EntityNotFoundException e) { - // ์ฑ…์ด DB์— ์—†์„ ๋•Œ ์ฒ˜๋ฆฌ + boolean alreadySaved = bookQueryPort.existsSavedBookByUserIdAndBookId(userId, book.getId()); + validateSaveBookAction(isSaveRequest, alreadySaved); - if (!isSave) { - // ์‚ญ์ œ ์š”์ฒญ์ธ๋ฐ ์ฑ…์ด ์—†์œผ๋ฉด ์ €์žฅํ•˜์ง€ ์•Š์€ ์ฑ…์ด๋ฏ€๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ - throw new BusinessException(BOOK_NOT_SAVED_DB_CANNOT_DELETE); - } - - // ์ €์žฅ ์š”์ฒญ์ด๋ฉด ๋„ค์ด๋ฒ„ API๋กœ ์ฑ… ์ •๋ณด ์กฐํšŒ ํ›„ ์ €์žฅ - NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); - Book newBook = Book.withoutId( - naverResult.title(), - naverResult.isbn(), - naverResult.author(), - false, - naverResult.publisher(), - naverResult.imageUrl(), - null, - naverResult.description()); + if (isSaveRequest) { + bookCommandPort.saveSavedBook(userId, book.getId()); + } else { + bookCommandPort.deleteSavedBook(userId, book.getId()); + } - Long newBookId = bookCommandPort.save(newBook); + return BookIsSavedResult.of(isbn, isSaveRequest); + } - book = bookCommandPort.findById(newBookId); - } + private Book registerBookByIsbn(String isbn) { + // ์ €์žฅ ์š”์ฒญ์ด๋ฉด ๋„ค์ด๋ฒ„ API๋กœ ์ฑ… ์ •๋ณด ์กฐํšŒ ํ›„ ์ €์žฅ + NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + Book newBook = Book.withoutId( + naverResult.title(), + naverResult.isbn(), + naverResult.author(), + false, + naverResult.publisher(), + naverResult.imageUrl(), + null, + naverResult.description()); - // ์œ ์ €๊ฐ€ ์ €์žฅํ•œ ์ฑ… ๋ชฉ๋ก ์กฐํšŒ - SavedBooks savedBooks = savedQueryPort.findSavedBooksByUserId(userId); + Long savedBookId = bookCommandPort.save(newBook); + return bookCommandPort.findById(savedBookId); + } - if (isSave) { - // ์ €์žฅ ์š”์ฒญ ์‹œ ์ด๋ฏธ ์ €์žฅ๋˜์–ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ - savedBooks.validateNotAlreadySaved(book); - savedCommandPort.saveBook(userId, book.getId()); - } else { - // ์‚ญ์ œ ์š”์ฒญ ์‹œ ์ €์žฅ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ - savedBooks.validateCanDelete(book); - savedCommandPort.deleteBook(userId, book.getId()); + private void validateSaveBookAction(boolean isSaveRequest, boolean alreadySaved) { + if (isSaveRequest && alreadySaved) { + // ์ด๋ฏธ ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ฑ…์„ ๋‹ค์‹œ ์ €์žฅํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ + throw new BusinessException(BOOK_ALREADY_SAVED); + } else if (!isSaveRequest && !alreadySaved) { + // ์ €์žฅ๋˜์ง€ ์•Š์€ ์ฑ…์„ ์‚ญ์ œํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ + throw new BusinessException(BOOK_NOT_SAVED_CANNOT_DELETE); } - - return BookIsSavedResult.of(isbn, isSave); } } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java index 0609fb188..d1826a244 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -4,17 +4,16 @@ import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; import konkuk.thip.book.application.port.in.dto.BookDetailSearchResult; -import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.application.port.out.BookApiQueryPort; +import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.application.port.out.BookRedisCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.recentSearch.application.port.out.RecentSearchCommandPort; import konkuk.thip.recentSearch.domain.RecentSearch; import konkuk.thip.room.application.port.out.RoomQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.application.port.out.UserQueryPort; import konkuk.thip.user.domain.User; @@ -38,7 +37,7 @@ public class BookSearchService implements BookSearchUseCase { private final RoomQueryPort roomQueryPort; private final UserQueryPort userQueryPort; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; + private final BookQueryPort bookQueryPort; private final RecentSearchCommandPort recentSearchCommandPort; private final BookCommandPort bookCommandPort; private final UserCommandPort userCommandPort; @@ -90,32 +89,30 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { //์ฑ… ๊ฒ€์ƒ‰์ˆœ์œ„ ์ •๋ณด ์—…๋ฐ์ดํŠธ bookRedisCommandPort.incrementBookSearchCount(isbn,LocalDate.now()); - Book book; - try { - // DB์—์„œ ์ฑ… ์ •๋ณด ์กฐํšŒ (์—†์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ) - book = bookCommandPort.getByIsbnOrThrow(isbn); - } catch (EntityNotFoundException e) { - // ์ฑ…์ด DB์— ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ - return BookDetailSearchResult.of( - naverDetailBookParseResult, - 0, // ๋ชจ์ง‘ ์ค‘์ธ ๋ฐฉ ๊ฐœ์ˆ˜ - 0, // ์ฝ๊ธฐ ์ฐธ์—ฌ์ž ์ˆ˜ - false // ์ €์žฅ ์—ฌ๋ถ€ - ); - } - - //์ด์ฑ…์— ๋ชจ์ง‘์ค‘์ธ ๋ชจ์ž„๋ฐฉ ๊ฐœ์ˆ˜ - int recruitingRoomCount = getRecruitingRoomCount(book); - // ์ด์ฑ…์— ์ฝ๊ธฐ ์ฐธ์—ฌ์ค‘์ธ ์‚ฌ์šฉ์ž ์ˆ˜ - int readCount = getReadCount(book); - // ์‚ฌ์šฉ์ž์˜ ํ•ด๋‹น ์ฑ… ์ €์žฅ ์—ฌ๋ถ€ - boolean isSaved = savedQueryPort.existsByUserIdAndBookId(user.getId(), book.getId()); - - return BookDetailSearchResult.of( - naverDetailBookParseResult, - recruitingRoomCount, - readCount, - isSaved); + return bookCommandPort.findByIsbn(isbn) + .map(book -> { + //์ด์ฑ…์— ๋ชจ์ง‘์ค‘์ธ ๋ชจ์ž„๋ฐฉ ๊ฐœ์ˆ˜ + int recruitingRoomCount = getRecruitingRoomCount(book); + // ์ด์ฑ…์— ์ฝ๊ธฐ ์ฐธ์—ฌ์ค‘์ธ ์‚ฌ์šฉ์ž ์ˆ˜ + int readCount = getReadCount(book); + // ์‚ฌ์šฉ์ž์˜ ํ•ด๋‹น ์ฑ… ์ €์žฅ ์—ฌ๋ถ€ + boolean isSaved = bookQueryPort.existsSavedBookByUserIdAndBookId(user.getId(), book.getId()); + + return BookDetailSearchResult.of( + naverDetailBookParseResult, + recruitingRoomCount, + readCount, + isSaved + ); + }) + .orElseGet(() -> + BookDetailSearchResult.of( + naverDetailBookParseResult, + 0, // ๋ชจ์ง‘ ์ค‘์ธ ๋ฐฉ ๊ฐœ์ˆ˜ + 0, // ์ฝ๊ธฐ ์ฐธ์—ฌ์ž ์ˆ˜ + false // ์ €์žฅ ์—ฌ๋ถ€ + ) + ); } private int getRecruitingRoomCount(Book book) { diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java new file mode 100644 index 000000000..af3a13f97 --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -0,0 +1,32 @@ +package konkuk.thip.book.application.service; + +import konkuk.thip.book.application.mapper.BookQueryMapper; +import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; +import konkuk.thip.book.application.port.out.BookQueryPort; +import konkuk.thip.book.domain.Book; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class BookSelectableListService implements BookSelectableListUseCase { + + private final BookQueryPort bookQueryPort; + private final BookQueryMapper bookQueryMapper; + + @Override + @Transactional(readOnly = true) + public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { + List bookList = switch(bookSelectableType) { + case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); + case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); + }; + + return bookQueryMapper.toBookSelectableResultList(bookList); + } +} diff --git a/src/main/java/konkuk/thip/book/domain/SavedBooks.java b/src/main/java/konkuk/thip/book/domain/SavedBooks.java deleted file mode 100644 index 0871a4408..000000000 --- a/src/main/java/konkuk/thip/book/domain/SavedBooks.java +++ /dev/null @@ -1,41 +0,0 @@ -package konkuk.thip.book.domain; - -import konkuk.thip.common.exception.InvalidStateException; -import lombok.Getter; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static konkuk.thip.common.exception.code.ErrorCode.*; - -@Getter -public class SavedBooks { - private final Set books; - - public SavedBooks(List books) { - // Set์œผ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์ค‘๋ณต ์—ฌ๋ถ€ ๊ฒ€์‚ฌ - Set bookSet = new HashSet<>(books); - if (bookSet.size() != books.size()) { - throw new InvalidStateException(DUPLICATED_BOOKS_IN_COLLECTION); - } - // ๋ถˆ๋ณ€ Set์œผ๋กœ ์ €์žฅ (Collections.unmodifiableSet ์‚ฌ์šฉ) - this.books = Collections.unmodifiableSet(bookSet); - } - - // ์ค‘๋ณต ์ €์žฅ ๊ฒ€์ฆ - public void validateNotAlreadySaved(Book book) { - if (books.contains(book)) { - throw new InvalidStateException(BOOK_ALREADY_SAVED); - } - } - - // ์‚ญ์ œ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ฒ€์ฆ - public void validateCanDelete(Book book) { - if (!books.contains(book)) { - throw new InvalidStateException(BOOK_NOT_SAVED_CANNOT_DELETE); - } - } -} - diff --git a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java index 2b3d6ec96..d4b274557 100644 --- a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java @@ -256,6 +256,9 @@ public enum SwaggerResponseDescription { USER_NOT_FOUND, JSON_PROCESSING_ERROR ))), + BOOK_SELECTABLE_LIST(new LinkedHashSet<>(Set.of( + USER_NOT_FOUND + ))), // Recent Search RECENT_SEARCH_DELETE(new LinkedHashSet<>(Set.of( diff --git a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java b/src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java similarity index 86% rename from src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java rename to src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java index b71090707..2e1f1ea7a 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java @@ -1,8 +1,7 @@ -package konkuk.thip.saved.adapter.out.jpa; +package konkuk.thip.feed.adapter.out.jpa; import jakarta.persistence.*; import konkuk.thip.common.entity.BaseJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import lombok.*; diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java index 84c0d789e..196d91c38 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java @@ -3,14 +3,12 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.ContentJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.*; import konkuk.thip.feed.adapter.out.mapper.ContentMapper; import konkuk.thip.feed.adapter.out.mapper.FeedMapper; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.Tag.TagJpaRepository; import konkuk.thip.feed.application.port.out.FeedCommandPort; import konkuk.thip.feed.domain.Feed; @@ -34,10 +32,11 @@ public class FeedCommandPersistenceAdapter implements FeedCommandPort { private final BookJpaRepository bookJpaRepository; private final TagJpaRepository tagJpaRepository; private final FeedTagJpaRepository feedTagJpaRepository; + private final SavedFeedJpaRepository savedFeedJpaRepository; + private final FeedMapper feedMapper; private final ContentMapper contentMapper; - @Override public Optional findById(Long id) { return feedJpaRepository.findById(id) @@ -108,4 +107,22 @@ private void applyFeedTags(Feed feed, FeedJpaEntity feedJpaEntity) { } } + @Override + public void saveSavedFeed(Long userId, Long feedId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + FeedJpaEntity feed = feedJpaRepository.findById(feedId) + .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); + SavedFeedJpaEntity entity = SavedFeedJpaEntity.builder() + .userJpaEntity(user) + .feedJpaEntity(feed) + .build(); + savedFeedJpaRepository.save(entity); + } + + @Override + public void deleteSavedFeed(Long userId, Long feedId) { + savedFeedJpaRepository.deleteByUserIdAndFeedId(userId, feedId); + } + } diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java index d0b9829ea..9774e14c5 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java @@ -1,24 +1,42 @@ package konkuk.thip.feed.adapter.out.persistence; import konkuk.thip.common.entity.StatusType; +import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.CursorBasedList; +import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; import konkuk.thip.feed.adapter.out.mapper.FeedMapper; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; +import konkuk.thip.feed.domain.Feed; +import konkuk.thip.feed.domain.SavedFeeds; +import konkuk.thip.feed.application.port.out.dto.FeedIdAndTagProjection; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; + +import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @Repository @RequiredArgsConstructor public class FeedQueryPersistenceAdapter implements FeedQueryPort { private final FeedJpaRepository feedJpaRepository; + private final FeedTagJpaRepository feedTagJpaRepository; + private final UserJpaRepository userJpaRepository; + private final SavedFeedJpaRepository savedFeedJpaRepository; private final FeedMapper feedMapper; @Override @@ -92,4 +110,41 @@ public int countAllFeedsByUserId(Long userId) { public int countPublicFeedsByUserId(Long userId) { return (int) feedJpaRepository.countPublicFeedsByUserId(userId, StatusType.ACTIVE); } + + @Override + public SavedFeeds findSavedFeedsByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + List savedFeedEntities = + savedFeedJpaRepository.findAllByUserId(user.getUserId()); + + List feedIds = savedFeedEntities.stream() + .map(entity -> entity.getFeedJpaEntity().getPostId()) + .toList(); + + // Projection ๊ธฐ๋ฐ˜ ์กฐํšŒ + List results = feedTagJpaRepository.findFeedIdAndTagsByFeedIds(feedIds); + + Map> feedTagsMap = results.stream() + .collect(Collectors.groupingBy( + FeedIdAndTagProjection::getFeedId, + Collectors.mapping(FeedIdAndTagProjection::getTagJpaEntity, Collectors.toList()) + )); + + List feeds = savedFeedEntities.stream() + .map(entity -> { + FeedJpaEntity feedJpa = entity.getFeedJpaEntity(); + List tags = feedTagsMap.getOrDefault(feedJpa.getPostId(), List.of()); + return feedMapper.toDomainEntity(feedJpa, tags); + }) + .toList(); + + return new SavedFeeds(feeds); + } + + @Override + public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { + return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); + } } diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java index cf5519abc..84e27e2ba 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java @@ -2,7 +2,7 @@ import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity; -import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; +import konkuk.thip.feed.application.port.out.dto.FeedIdAndTagProjection; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java similarity index 89% rename from src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java rename to src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java index e98baf1f3..d9b76cfab 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java @@ -1,6 +1,6 @@ -package konkuk.thip.saved.adapter.out.persistence.repository; +package konkuk.thip.feed.adapter.out.persistence.repository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java b/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java index 31c86b5c2..c34b0d631 100644 --- a/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java @@ -16,4 +16,7 @@ default Feed getByIdOrThrow(Long id) { return findById(id) .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); } + + void saveSavedFeed(Long userId, Long feedId); + void deleteSavedFeed(Long userId, Long feedId); } diff --git a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java index c98f201b3..102ee5035 100644 --- a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java @@ -3,6 +3,7 @@ import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.CursorBasedList; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; +import konkuk.thip.feed.domain.SavedFeeds; import java.util.Set; @@ -29,4 +30,11 @@ public interface FeedQueryPort { int countAllFeedsByUserId(Long userId); int countPublicFeedsByUserId(Long userId); + + /** + * ์ €์žฅ๋œ ํ”ผ๋“œ ์กฐํšŒ + */ + SavedFeeds findSavedFeedsByUserId(Long userId); + + Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); } diff --git a/src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java b/src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java similarity index 75% rename from src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java rename to src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java index ab1ef2c5d..b4de5ae3b 100644 --- a/src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java @@ -1,4 +1,4 @@ -package konkuk.thip.saved.application.port.out.dto; +package konkuk.thip.feed.application.port.out.dto; import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; diff --git a/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java b/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java index deac8cfdf..de9f126ec 100644 --- a/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java +++ b/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java @@ -8,7 +8,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; @@ -33,7 +32,6 @@ public class BasicFeedShowAllService implements FeedShowAllUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -50,7 +48,7 @@ public FeedShowAllResponse showAllFeeds(Long userId, String cursor) { .collect(Collectors.toUnmodifiableSet()); // 3. ์œ ์ €๊ฐ€ ์ €์žฅํ•œ ํ”ผ๋“œ๋“ค, ์ข‹์•„ํ•œ ํ”ผ๋“œ๋“ค ์กฐํšŒ - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response ๋กœ์˜ ๋งคํ•‘ diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java b/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java index 4404d9343..09fe96bac 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java @@ -68,12 +68,9 @@ public Long createFeed(FeedCreateCommand command, List images) { * ISBN์œผ๋กœ ์ฑ…์„ ์กฐํšŒํ•˜๊ณ , ์—†์œผ๋ฉด ์™ธ๋ถ€ API(Naver)์—์„œ ์ƒ์„ธ ์ •๋ณด๋ฅผ ์กฐํšŒํ•ด ์ƒˆ๋กœ ์ €์žฅ ํ›„ ID ๋ฐ˜ํ™˜ */ private Long findOrCreateBookByIsbn(String isbn) { - try { - Book existing = bookCommandPort.getByIsbnOrThrow(isbn); - return existing.getId(); - } catch (EntityNotFoundException e) { - return saveNewBookWithFromExternalApi(isbn); - } + return bookCommandPort.findByIsbn(isbn) + .map(Book::getId) + .orElseGet(() -> saveNewBookWithFromExternalApi(isbn)); } /** diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java index 9825e8d78..6236b2d15 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java @@ -5,10 +5,9 @@ import konkuk.thip.feed.application.port.in.dto.FeedIsSavedCommand; import konkuk.thip.feed.application.port.in.dto.FeedIsSavedResult; import konkuk.thip.feed.application.port.out.FeedCommandPort; +import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.domain.Feed; import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,8 +16,7 @@ public class FeedSavedService implements FeedSavedUseCase { private final FeedCommandPort feedCommandPort; - private final SavedCommandPort savedCommandPort; - private final SavedQueryPort savedQueryPort; + private final FeedQueryPort feedQueryPort; @Override @Transactional @@ -28,16 +26,16 @@ public FeedIsSavedResult changeSavedFeed(FeedIsSavedCommand feedIsSavedCommand) Feed feed = feedCommandPort.getByIdOrThrow(feedIsSavedCommand.feedId()); // 2. ์œ ์ €๊ฐ€ ์ €์žฅํ•œ ํ”ผ๋“œ ๋ชฉ๋ก ์กฐํšŒ - SavedFeeds savedFeeds = savedQueryPort.findSavedFeedsByUserId(feedIsSavedCommand.userId()); + SavedFeeds savedFeeds = feedQueryPort.findSavedFeedsByUserId(feedIsSavedCommand.userId()); if (feedIsSavedCommand.isSaved()) { // ์ €์žฅ ์š”์ฒญ ์‹œ ์ด๋ฏธ ์ €์žฅ๋˜์–ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ savedFeeds.validateNotAlreadySaved(feed); - savedCommandPort.saveFeed(feedIsSavedCommand.userId(), feed.getId()); + feedCommandPort.saveSavedFeed(feedIsSavedCommand.userId(), feed.getId()); } else { // ์‚ญ์ œ ์š”์ฒญ ์‹œ ์ €์žฅ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ savedFeeds.validateCanDelete(feed); - savedCommandPort.deleteFeed(feedIsSavedCommand.userId(), feed.getId()); + feedCommandPort.deleteSavedFeed(feedIsSavedCommand.userId(), feed.getId()); } return FeedIsSavedResult.of(feed.getId(), feedIsSavedCommand.isSaved()); diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java b/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java index fe2b9cf27..9fb50f692 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java @@ -9,7 +9,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +23,6 @@ public class FeedShowAllOfUserService implements FeedShowAllOfUserUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -57,7 +55,7 @@ public FeedShowByUserResponse showPublicFeedsOfFeedOwner(Long userId, Long feedO .collect(Collectors.toUnmodifiableSet()); // 3. ์œ ์ €๊ฐ€ ์ €์žฅํ•œ ํ”ผ๋“œ๋“ค, ์ข‹์•„ํ•œ ํ”ผ๋“œ๋“ค ์กฐํšŒ - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response ๋กœ์˜ ๋งคํ•‘ diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java b/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java index b6dc7ed7c..788c43023 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java @@ -2,14 +2,13 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.BusinessException; import konkuk.thip.feed.adapter.in.web.response.FeedShowSingleResponse; import konkuk.thip.feed.application.mapper.FeedQueryMapper; import konkuk.thip.feed.application.port.in.FeedShowSingleUseCase; import konkuk.thip.feed.application.port.out.FeedCommandPort; +import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.domain.Feed; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.domain.User; import lombok.RequiredArgsConstructor; @@ -17,8 +16,6 @@ import java.util.Set; -import static konkuk.thip.common.exception.code.ErrorCode.FEED_CAN_NOT_SHOW_PRIVATE_ONE; - @Service @RequiredArgsConstructor public class FeedShowSingleService implements FeedShowSingleUseCase { @@ -27,7 +24,7 @@ public class FeedShowSingleService implements FeedShowSingleUseCase { private final UserCommandPort userCommandPort; private final BookCommandPort bookCommandPort; private final PostLikeQueryPort postLikeQueryPort; - private final SavedQueryPort savedQueryPort; + private final FeedQueryPort feedQueryPort; private final FeedQueryMapper feedQueryMapper; @Override @@ -43,7 +40,7 @@ public FeedShowSingleResponse showSingleFeed(Long feedId, Long userId) { Book book = bookCommandPort.findById(feed.getTargetBookId()); // 4. ์œ ์ €๊ฐ€ ํ•ด๋‹น ํ”ผ๋“œ๋ฅผ ์ข‹์•„ํ•˜๋Š”์ง€, ์ €์žฅํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ ์กฐํšŒ - Set savedFeedIds = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId); + Set savedFeedIds = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId); Set likedFeedIds = postLikeQueryPort.findPostIdsLikedByUser(Set.of(feedId), userId); boolean isSaved = savedFeedIds.contains(feedId); boolean isLiked = likedFeedIds.contains(feedId); diff --git a/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java b/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java index b0d4681f9..3319ec6fe 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java @@ -8,7 +8,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; @@ -32,7 +31,6 @@ public class FollowingPriorityFeedShowAllService implements FeedShowAllUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -49,7 +47,7 @@ public FeedShowAllResponse showAllFeeds(Long userId, String cursor) { .collect(Collectors.toUnmodifiableSet()); // 3. ์œ ์ €๊ฐ€ ์ €์žฅํ•œ ํ”ผ๋“œ๋“ค, ์ข‹์•„ํ•œ ํ”ผ๋“œ๋“ค ์กฐํšŒ - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response ๋กœ์˜ ๋งคํ•‘ diff --git a/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java b/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java index b6a8b1f2f..48ae56693 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java @@ -50,15 +50,14 @@ public Long createRoom(RoomCreateCommand command, Long userId) { } private Long resolveBookAndEnsurePage(String isbn) { - try { - Book existing = bookCommandPort.getByIsbnOrThrow(isbn); - if (!existing.hasPageCount()) { - updateBookPageCount(existing); - } - return existing.getId(); - } catch (EntityNotFoundException e) { - return saveNewBookWithPageCount(isbn); - } + return bookCommandPort.findByIsbn(isbn) + .map(book -> { + if (!book.hasPageCount()) { + updateBookPageCount(book); + } + return book.getId(); + }) + .orElseGet(() -> saveNewBookWithPageCount(isbn)); } private void updateBookPageCount(Book book) { diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java deleted file mode 100644 index 08e68a425..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -public class SavedBookCommandController { - -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java deleted file mode 100644 index 1f8c76c18..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -public class SavedBookQueryController { - -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java deleted file mode 100644 index 0e2c5fd7d..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java +++ /dev/null @@ -1,4 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -public class SavedFeedCommandController { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java deleted file mode 100644 index 2cab73285..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java +++ /dev/null @@ -1,4 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -public class SavedFeedQueryController { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java b/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java deleted file mode 100644 index 51b7aa5e6..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.saved.adapter.in.web.request; - -import lombok.Getter; - -@Getter -public class UserSignupRequest { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java b/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java deleted file mode 100644 index c4b9cd168..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.saved.adapter.in.web.response; - -import lombok.Getter; - -@Getter -public class DummyResponse { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java b/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java deleted file mode 100644 index 70e7c7bdc..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package konkuk.thip.saved.adapter.out.mapper; - -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.domain.SavedBook; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import org.springframework.stereotype.Component; - -@Component -public class SavedBookMapper { - - public SavedBookJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, BookJpaEntity bookJpaEntity) { - return SavedBookJpaEntity.builder() - .userJpaEntity(userJpaEntity) - .bookJpaEntity(bookJpaEntity) - .build(); - } - - public SavedBook toDomainEntity(SavedBookJpaEntity savedBookJpaEntity) { - return SavedBook.builder() - .id(savedBookJpaEntity.getSavedId()) - .userId(savedBookJpaEntity.getUserJpaEntity().getUserId()) - .bookId(savedBookJpaEntity.getBookJpaEntity().getBookId()) - .createdAt(savedBookJpaEntity.getCreatedAt()) - .modifiedAt(savedBookJpaEntity.getModifiedAt()) - .status(savedBookJpaEntity.getStatus()) - .build(); - } -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java b/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java deleted file mode 100644 index 7556c7c0d..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package konkuk.thip.saved.adapter.out.mapper; - -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.domain.SavedFeed; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import org.springframework.stereotype.Component; - -@Component -public class SavedFeedMapper { - - public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) { - return SavedFeedJpaEntity.builder() - .userJpaEntity(userJpaEntity) - .feedJpaEntity(feedJpaEntity) - .build(); - } - - public SavedFeed toDomainEntity(SavedFeedJpaEntity savedFeedJpaEntity) { - return SavedFeed.builder() - .id(savedFeedJpaEntity.getSavedId()) - .userId(savedFeedJpaEntity.getUserJpaEntity().getUserId()) - .feedId(savedFeedJpaEntity.getFeedJpaEntity().getPostId()) - .createdAt(savedFeedJpaEntity.getCreatedAt()) - .modifiedAt(savedFeedJpaEntity.getModifiedAt()) - .status(savedFeedJpaEntity.getStatus()) - .build(); - } -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java deleted file mode 100644 index cc61dd5cf..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java +++ /dev/null @@ -1,71 +0,0 @@ -package konkuk.thip.saved.adapter.out.persistence; - -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; -import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.mapper.SavedBookMapper; -import konkuk.thip.saved.adapter.out.mapper.SavedFeedMapper; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import static konkuk.thip.common.exception.code.ErrorCode.*; - -@Repository -@RequiredArgsConstructor -public class SavedCommandPersistenceAdapter implements SavedCommandPort { - - private final UserJpaRepository userJpaRepository; - private final BookJpaRepository bookJpaRepository; - private final FeedJpaRepository feedJpaRepository; - private final SavedBookJpaRepository savedBookJpaRepository; - private final SavedFeedJpaRepository savedFeedJpaRepository; - private final SavedBookMapper savedBookMapper; - private final SavedFeedMapper savedFeedMapper; - - @Override - public void saveBook(Long userId, Long bookId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - BookJpaEntity book = bookJpaRepository.findById(bookId) - .orElseThrow(() -> new EntityNotFoundException(BOOK_NOT_FOUND)); - SavedBookJpaEntity entity = SavedBookJpaEntity.builder() - .userJpaEntity(user) - .bookJpaEntity(book) - .build(); - savedBookJpaRepository.save(entity); - } - - @Override - public void deleteBook(Long userId, Long bookId) { - savedBookJpaRepository.deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); - } - - @Override - public void saveFeed(Long userId, Long feedId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - FeedJpaEntity feed = feedJpaRepository.findById(feedId) - .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); - SavedFeedJpaEntity entity = SavedFeedJpaEntity.builder() - .userJpaEntity(user) - .feedJpaEntity(feed) - .build(); - savedFeedJpaRepository.save(entity); - } - - @Override - public void deleteFeed(Long userId, Long feedId) { - savedFeedJpaRepository.deleteByUserIdAndFeedId(userId, feedId); - } - - -} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java deleted file mode 100644 index 5ad5f28d6..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java +++ /dev/null @@ -1,98 +0,0 @@ -package konkuk.thip.saved.adapter.out.persistence; - -import konkuk.thip.book.adapter.out.mapper.BookMapper; -import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; -import konkuk.thip.feed.adapter.out.mapper.FeedMapper; -import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; -import konkuk.thip.feed.domain.Feed; -import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; -import konkuk.thip.saved.application.port.out.SavedQueryPort; -import konkuk.thip.book.domain.SavedBooks; -import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; - -@Repository -@RequiredArgsConstructor -public class SavedQueryPersistenceAdapter implements SavedQueryPort { - - private final SavedBookJpaRepository savedBookJpaRepository; - private final SavedFeedJpaRepository savedFeedJpaRepository; - private final UserJpaRepository userJpaRepository; - private final BookMapper bookMapper; - private final FeedMapper feedMapper; - private final FeedTagJpaRepository feedTagJpaRepository; - - @Override - public boolean existsByUserIdAndBookId(Long userId, Long bookId) { - return savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); - } - - @Override - public SavedBooks findSavedBooksByUserId(Long userId) { - - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - List savedBookEntities = savedBookJpaRepository.findByUserJpaEntity_UserId(user.getUserId()); - - // SavedBookJpaEntity์—์„œ BookJpaEntity๋ฅผ ๊บผ๋‚ด ๋„๋ฉ”์ธ Book์œผ๋กœ ๋ณ€ํ™˜ - List books = savedBookEntities.stream() - .map(entity -> bookMapper.toDomainEntity(entity.getBookJpaEntity())) - .collect(Collectors.toList()); - - return new SavedBooks(books); - } - - @Override - public SavedFeeds findSavedFeedsByUserId(Long userId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - - List savedFeedEntities = - savedFeedJpaRepository.findAllByUserId(user.getUserId()); - - List feedIds = savedFeedEntities.stream() - .map(entity -> entity.getFeedJpaEntity().getPostId()) - .toList(); - - // Projection ๊ธฐ๋ฐ˜ ์กฐํšŒ - List results = feedTagJpaRepository.findFeedIdAndTagsByFeedIds(feedIds); - - Map> feedTagsMap = results.stream() - .collect(Collectors.groupingBy( - FeedIdAndTagProjection::getFeedId, - Collectors.mapping(FeedIdAndTagProjection::getTagJpaEntity, Collectors.toList()) - )); - - List feeds = savedFeedEntities.stream() - .map(entity -> { - FeedJpaEntity feedJpa = entity.getFeedJpaEntity(); - List tags = feedTagsMap.getOrDefault(feedJpa.getPostId(), List.of()); - return feedMapper.toDomainEntity(feedJpa, tags); - }) - .toList(); - - return new SavedFeeds(feeds); - } - - @Override - public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { - return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); - } -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java deleted file mode 100644 index 05bbe9a31..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package konkuk.thip.saved.adapter.out.persistence.repository; - -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface SavedBookJpaRepository extends JpaRepository { - boolean existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); - void deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); - List findByUserJpaEntity_UserId(Long userId); -} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java b/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java deleted file mode 100644 index 72c0dffcf..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java +++ /dev/null @@ -1,5 +0,0 @@ -package konkuk.thip.saved.application.port.in; - -public interface DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java deleted file mode 100644 index d41e92a5b..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyCommand { - -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java deleted file mode 100644 index b77840db8..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyQuery { -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java deleted file mode 100644 index f8ea5bd63..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class DummyResult { - -} diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java deleted file mode 100644 index 21e26934f..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.saved.application.port.out; - - -public interface SavedCommandPort { - void saveBook(Long userId, Long bookId); - void deleteBook(Long userId, Long bookId); - void saveFeed(Long userId, Long feedId); - void deleteFeed(Long userId, Long feedId); -} diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java deleted file mode 100644 index e43fd246f..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java +++ /dev/null @@ -1,14 +0,0 @@ -package konkuk.thip.saved.application.port.out; - -import konkuk.thip.book.domain.SavedBooks; -import konkuk.thip.feed.domain.SavedFeeds; - -import java.util.Set; - -public interface SavedQueryPort { - boolean existsByUserIdAndBookId(Long userId, Long bookId); - SavedBooks findSavedBooksByUserId(Long userId); - SavedFeeds findSavedFeedsByUserId(Long userId); - - Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); -} diff --git a/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java b/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java deleted file mode 100644 index 7b44ae989..000000000 --- a/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java +++ /dev/null @@ -1,11 +0,0 @@ -package konkuk.thip.saved.application.service; - -import konkuk.thip.saved.application.port.in.DummyUseCase; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class SavedBookService implements DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java b/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java deleted file mode 100644 index 1ecf7ea0f..000000000 --- a/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.saved.application.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class SavedFeedService { -} diff --git a/src/main/java/konkuk/thip/saved/domain/SavedBook.java b/src/main/java/konkuk/thip/saved/domain/SavedBook.java deleted file mode 100644 index 180a06f86..000000000 --- a/src/main/java/konkuk/thip/saved/domain/SavedBook.java +++ /dev/null @@ -1,16 +0,0 @@ -package konkuk.thip.saved.domain; - -import konkuk.thip.common.entity.BaseDomainEntity; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -@Getter -@SuperBuilder -public class SavedBook extends BaseDomainEntity { - - private Long id; - - private Long userId; - - private Long bookId; -} diff --git a/src/main/java/konkuk/thip/saved/domain/SavedFeed.java b/src/main/java/konkuk/thip/saved/domain/SavedFeed.java deleted file mode 100644 index 540b7b454..000000000 --- a/src/main/java/konkuk/thip/saved/domain/SavedFeed.java +++ /dev/null @@ -1,16 +0,0 @@ -package konkuk.thip.saved.domain; - -import konkuk.thip.common.entity.BaseDomainEntity; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -@Getter -@SuperBuilder -public class SavedFeed extends BaseDomainEntity { - - private Long id; - - private Long userId; - - private Long feedId; -} diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java index 8e4058e9c..5675a01c9 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java @@ -7,8 +7,8 @@ import konkuk.thip.common.exception.code.ErrorCode; import konkuk.thip.common.security.util.JwtUtil; import konkuk.thip.common.util.TestEntityFactory; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; @@ -122,7 +122,7 @@ void saveBook_success() throws Exception { // ์‹ค์ œ ์ €์žฅ๋๋Š”์ง€ ๊ฒ€์ฆ Optional bookJpaEntity = bookJpaRepository.findByIsbn(testIsbn); - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookJpaEntity.get().getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(userId, bookJpaEntity.get().getBookId()); assertThat(exists).isTrue(); } @@ -148,7 +148,7 @@ void saveBook_whenBookNotExist_thenSaveAndSuccess() throws Exception { // ์‹ค์ œ ์ €์žฅ๋๋Š”์ง€ ๊ฒ€์ฆ Optional bookJpaEntity = bookJpaRepository.findByIsbn(newIsbn); assertThat(bookJpaEntity).isPresent(); - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookJpaEntity.get().getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(userId, bookJpaEntity.get().getBookId()); assertThat(exists).isTrue(); } @@ -203,7 +203,7 @@ void deleteBook_success() throws Exception { .andExpect(jsonPath("$.data.isSaved").value(false)); // ์‹ค์ œ ์‚ญ์ œ๋๋Š”์ง€ ๊ฒ€์ฆ - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(user.getUserId(), book.getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(user.getUserId(), book.getBookId()); assertThat(exists).isFalse(); } diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java index fa61ec37e..8bba70a64 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java @@ -13,8 +13,8 @@ import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java new file mode 100644 index 000000000..5bdd6f766 --- /dev/null +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java @@ -0,0 +1,91 @@ +package konkuk.thip.book.adapter.in.web; + +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; +import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; +import org.junit.jupiter.api.BeforeEach; +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.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +@ActiveProfiles("test") +@Transactional +@DisplayName("[ํ†ตํ•ฉ] ์ €์žฅํ•œ ์ฑ… ๋ฐ ์ฐธ์—ฌ ์ค‘ ์ฑ… ๋ฆฌ์ŠคํŠธ ์กฐํšŒ API ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ") +class BookGetSelectableListApiTest { + + @Autowired private MockMvc mockMvc; + + @Autowired private UserJpaRepository userJpaRepository; + @Autowired private AliasJpaRepository aliasJpaRepository; + @Autowired private BookJpaRepository bookJpaRepository; + @Autowired private SavedBookJpaRepository savedBookJpaRepository; + @Autowired private CategoryJpaRepository categoryJpaRepository; + @Autowired private RoomJpaRepository roomJpaRepository; + @Autowired private RoomParticipantJpaRepository roomParticipantJpaRepository; + + private UserJpaEntity user; + private BookJpaEntity savedBook; + private BookJpaEntity joiningBook; + + @BeforeEach + void setUp() { + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + user = userJpaRepository.save(TestEntityFactory.createUser(alias)); + savedBook = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("1111111111111")); + joiningBook = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("2222222222222")); + + // SAVED ์ฑ… ๋“ฑ๋ก + savedBookJpaRepository.save(TestEntityFactory.createSavedBook(user, savedBook)); + + // JOINING ์ฑ…์šฉ ๋ฐฉ ์ƒ์„ฑ + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + RoomJpaEntity room = roomJpaRepository.save(TestEntityFactory.createRoom(joiningBook, category)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); + } + + @Test + @DisplayName("SAVED ํƒ€์ž… - ์ €์žฅํ•œ ์ฑ… ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ์„ฑ๊ณต") + void getSelectableBooks_saved_success() throws Exception { + mockMvc.perform(get("/books/selectable-list") + .param("type", "SAVED") + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.bookList").isArray()) + .andExpect(jsonPath("$.data.bookList.length()").value(1)) + .andExpect(jsonPath("$.data.bookList[0].isbn").value(savedBook.getIsbn())); + } + + @Test + @DisplayName("JOINING ํƒ€์ž… - ์ฐธ์—ฌ ์ค‘์ธ ์ฑ… ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ์„ฑ๊ณต") + void getSelectableBooks_joining_success() throws Exception { + mockMvc.perform(get("/books/selectable-list") + .param("type", "JOINING") + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.bookList").isArray()) + .andExpect(jsonPath("$.data.bookList.length()").value(1)) + .andExpect(jsonPath("$.data.bookList[0].isbn").value(joiningBook.getIsbn())); + } +} \ No newline at end of file diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index 509dcc937..7fb3ebdde 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -1,6 +1,7 @@ package konkuk.thip.common.util; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; import konkuk.thip.comment.adapter.out.jpa.CommentLikeJpaEntity; import konkuk.thip.common.post.PostType; @@ -16,7 +17,7 @@ import konkuk.thip.room.adapter.out.jpa.RoomParticipantJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; import konkuk.thip.room.domain.Category; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -321,4 +322,11 @@ public static PostLikeJpaEntity createPostLike(UserJpaEntity user, PostJpaEntity .postJpaEntity(post) .build(); } + + public static SavedBookJpaEntity createSavedBook(UserJpaEntity user, BookJpaEntity book) { + return SavedBookJpaEntity.builder() + .userJpaEntity(user) + .bookJpaEntity(book) + .build(); + } } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java index 93f0478c9..06a32f674 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java @@ -8,8 +8,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java index 76b976d60..9d14f1d86 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java @@ -11,8 +11,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.Tag.TagJpaRepository; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java index ce32c3aab..d3c039f14 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java @@ -7,7 +7,7 @@ import konkuk.thip.feed.adapter.out.persistence.repository.Content.ContentJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java index fdd138f04..361e21191 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java @@ -13,8 +13,8 @@ import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java index 4051a58f7..9568412a3 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java @@ -8,8 +8,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java index eb8ac5476..03b730420 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java @@ -1,17 +1,15 @@ package konkuk.thip.feed.adapter.in.web; -import com.amazonaws.services.cloudformation.model.StackResourceDriftInformation; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; -import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.persistence.repository.Content.ContentJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository;