diff --git a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt index 58ea83e3..5e946bf8 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt @@ -6,8 +6,10 @@ import org.springframework.data.domain.Sort import org.springframework.data.web.PageableDefault import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -70,4 +72,13 @@ class BookController( return ResponseEntity.ok(response) } + @DeleteMapping("/my-library/{userBookId}") + override fun deleteBookFromMyLibrary( + @AuthenticationPrincipal userId: UUID, + @PathVariable userBookId: UUID, + ): ResponseEntity { + bookUseCase.deleteBookFromMyLibrary(userId, userBookId) + return ResponseEntity.noContent().build() + } + } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt index 90d40e86..0da233fd 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt @@ -140,4 +140,24 @@ interface BookControllerApi { @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC) pageable: Pageable ): ResponseEntity + + @Operation(summary = "내 서재에 저장한 도서 삭제", description = "내 서재에 저장한 도서를 삭제합니다.") + @ApiResponses( + value = [ + ApiResponse( + responseCode = "204", + description = "성공적으로 도서를 삭제했습니다." + ), + ApiResponse( + responseCode = "404", + description = "해당하는 도서를 찾을 수 없습니다.", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ) + ] + ) + @DeleteMapping("/my-library/{userBookId}") + fun deleteBookFromMyLibrary( + @AuthenticationPrincipal userId: UUID, + @Parameter(description = "삭제할 도서 ID") @PathVariable userBookId: UUID + ): ResponseEntity } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt index 2965ad31..4cf813e3 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt @@ -83,4 +83,9 @@ class UserBookService( completedCount = userBookStatusCountsVO.completedCount ) } + + fun deleteUserBook(userBookId: UUID, userId: UUID) { + validateUserBookExists(userBookId, userId) + userBookDomainService.deleteById(userBookId) + } } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt index 17e8aa22..5eadd7ec 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt @@ -13,6 +13,7 @@ import org.yapp.apis.book.dto.response.UserBookResponse import org.yapp.apis.book.service.BookManagementService import org.yapp.apis.book.service.BookQueryService import org.yapp.apis.book.service.UserBookService +import org.yapp.apis.readingrecord.service.ReadingRecordService import org.yapp.apis.user.service.UserService import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBookSortType @@ -26,7 +27,8 @@ class BookUseCase( private val bookQueryService: BookQueryService, private val userService: UserService, private val userBookService: UserBookService, - private val bookManagementService: BookManagementService + private val bookManagementService: BookManagementService, + private val readingRecordService: ReadingRecordService ) { fun searchBooks( request: BookSearchRequest @@ -84,6 +86,16 @@ class BookUseCase( return userBookService.findUserBooksByDynamicConditionWithStatusCounts(userId, status, sort, title, pageable) } + @Transactional + fun deleteBookFromMyLibrary( + userId: UUID, + userBookId: UUID + ) { + userService.validateUserExists(userId) + readingRecordService.deleteAllByUserBookId(userBookId) + userBookService.deleteUserBook(userBookId, userId) + } + private fun mergeWithUserBookStatus( searchedBooks: List, userId: UUID? diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt index ad0f3bcd..6bee0161 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt @@ -47,6 +47,9 @@ class ReadingRecordService( return page.map { ReadingRecordResponse.from(it) } } + fun deleteAllByUserBookId(userBookId: UUID) { + readingRecordDomainService.deleteAllByUserBookId(userBookId) + } fun updateReadingRecord( readingRecordId: UUID, request: UpdateReadingRecordRequest diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt index 5c63076d..64f5012c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt @@ -1,6 +1,5 @@ package org.yapp.apis.readingrecord.usecase -import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional import org.yapp.apis.book.service.UserBookService diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt index b0af61db..cafb5faf 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt @@ -146,6 +146,9 @@ class ReadingRecordDomainService( return buildReadingRecordInfoVO(savedReadingRecord) } + fun deleteAllByUserBookId(userBookId: UUID) { + readingRecordRepository.deleteAllByUserBookId(userBookId) + } fun deleteReadingRecord(readingRecordId: UUID) { val readingRecord = readingRecordRepository.findById(readingRecordId) ?: throw ReadingRecordNotFoundException( diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordRepository.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordRepository.kt index 854f0735..ace1edbb 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordRepository.kt @@ -9,6 +9,7 @@ interface ReadingRecordRepository { fun save(readingRecord: ReadingRecord): ReadingRecord + fun deleteAllByUserBookId(userBookId: UUID) fun findById(id: UUID): ReadingRecord? diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt index cb41b205..666b0109 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -6,11 +6,12 @@ import org.yapp.domain.userbook.vo.HomeBookVO import org.yapp.domain.userbook.vo.UserBookInfoVO import org.yapp.domain.userbook.vo.UserBookStatusCountsVO import org.yapp.globalutils.annotation.DomainService +import org.yapp.domain.readingrecord.ReadingRecordRepository import java.util.* @DomainService class UserBookDomainService( - private val userBookRepository: UserBookRepository + private val userBookRepository: UserBookRepository, ) { fun upsertUserBook( userId: UUID, @@ -77,6 +78,10 @@ class UserBookDomainService( return userBookRepository.existsByIdAndUserId(userBookId, userId) } + fun deleteById(userBookId: UUID) { + userBookRepository.deleteById(userBookId) + } + fun findBooksWithRecordsOrderByLatest(userId: UUID): List { val resultTriples = userBookRepository.findRecordedBooksSortedByRecency(userId) @@ -103,7 +108,8 @@ class UserBookDomainService( return userBooks.map { userBook -> HomeBookVO.newInstance( userBook = userBook, - lastRecordedAt = userBook.updatedAt ?: throw IllegalStateException("UserBook의 updatedAt이 null입니다: ${userBook.id}"), + lastRecordedAt = userBook.updatedAt + ?: throw IllegalStateException("UserBook의 updatedAt이 null입니다: ${userBook.id}"), recordCount = 0 ) } diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt index c60635ee..58f08862 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt @@ -12,6 +12,7 @@ interface UserBookRepository { fun existsByIdAndUserId(id: UUID, userId: UUID): Boolean fun findById(id: UUID): UserBook? fun save(userBook: UserBook): UserBook + fun deleteById(id: UUID) fun findAllByUserId(userId: UUID): List fun findAllByUserIdAndBookIsbn13In(userId: UUID, bookIsbn13s: List): List fun findUserBooksByDynamicCondition( diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/JpaReadingRecordRepository.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/JpaReadingRecordRepository.kt index 84f43f5d..926fca08 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/JpaReadingRecordRepository.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/JpaReadingRecordRepository.kt @@ -11,6 +11,8 @@ interface JpaReadingRecordRepository : JpaRepository, fun findAllByUserBookId(userBookId: UUID): List + fun deleteAllByUserBookId(userBookId: UUID) + fun findAllByUserBookId(userBookId: UUID, pageable: Pageable): Page fun findAllByUserBookIdIn(userBookIds: List): List diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/impl/ReadingRecordRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/impl/ReadingRecordRepositoryImpl.kt index 87b31177..ba5b0fa2 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/impl/ReadingRecordRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/impl/ReadingRecordRepositoryImpl.kt @@ -21,6 +21,10 @@ class ReadingRecordRepositoryImpl( return savedEntity.toDomain() } + override fun deleteAllByUserBookId(userBookId: UUID) { + jpaReadingRecordRepository.deleteAllByUserBookId(userBookId) + } + override fun findById(id: UUID): ReadingRecord? { return jpaReadingRecordRepository.findByIdOrNull(id)?.toDomain() } diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt index 08bbffb4..e2b0488e 100644 --- a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt @@ -38,6 +38,10 @@ class UserBookRepositoryImpl( return savedEntity.toDomain() } + override fun deleteById(id: UUID) { + jpaUserBookRepository.deleteById(id) + } + override fun findAllByUserId(userId: UUID): List { return jpaUserBookRepository.findAllByUserId(userId).map { it.toDomain() } }