From f1586b5728c74dea21dab981cb8504939df931ce Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:59:39 +0900 Subject: [PATCH 01/46] =?UTF-8?q?[BOOK-207]=20refactor:=20domain,=20apis?= =?UTF-8?q?=20-=20BookDetailRequest=20=EB=B0=8F=20AladinApiHelper=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/request/BookDetailRequest.kt | 4 +--- .../kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt | 7 +++---- domain/src/main/kotlin/org/yapp/domain/book/Book.kt | 5 +---- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt index 8efa078c..90c0a9c8 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt @@ -15,13 +15,11 @@ data class BookDetailRequest private constructor( val itemIdType: String? = "ISBN", val optResult: List? = null ) { - fun validIsbn(): String = itemId!! - fun toAladinRequest(): AladinBookLookupRequest { return AladinBookLookupRequest.create( - itemId = this.itemId!!, + itemId = this.validIsbn(), itemIdType = this.itemIdType ?: "ISBN", optResult = this.optResult ) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt b/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt index dd759b0f..db2a1572 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt @@ -1,14 +1,13 @@ -package org.yapp.infra.external.aladin.helper +package org.yapp.apis.book.helper import mu.KotlinLogging import org.yapp.globalutils.annotation.Helper import org.yapp.infra.external.aladin.AladinApi -import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest // Import Aladin DTOs -import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest // Import Aladin DTOs +import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest +import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse - @Helper class AladinApiHelper( private val aladinApi: AladinApi diff --git a/domain/src/main/kotlin/org/yapp/domain/book/Book.kt b/domain/src/main/kotlin/org/yapp/domain/book/Book.kt index a86022c0..d9286495 100644 --- a/domain/src/main/kotlin/org/yapp/domain/book/Book.kt +++ b/domain/src/main/kotlin/org/yapp/domain/book/Book.kt @@ -3,11 +3,8 @@ package org.yapp.domain.book import org.yapp.globalutils.util.UuidGenerator import org.yapp.globalutils.validator.IsbnValidator import java.time.LocalDateTime -import java.util.UUID +import java.util.* -/** - * Represents a book in the domain model. - */ data class Book private constructor( val id: Id, val isbn: Isbn, From 4b275fca056b6dd8e1a9788a47f7faf998852c66 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 22:00:48 +0900 Subject: [PATCH 02/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20Aladi?= =?UTF-8?q?nBookQueryService=EC=97=90=EC=84=9C=20Companion=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=82=AC=EC=9A=A9=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20import=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/book/service/impl/AladinBookQueryService.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt index 4aa47c28..5875a216 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt @@ -3,13 +3,13 @@ package org.yapp.apis.book.service.impl import mu.KotlinLogging import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service +import org.yapp.apis.book.constant.BookQueryServiceQualifier import org.yapp.apis.book.dto.request.BookDetailRequest import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse -import org.yapp.apis.book.constant.BookQueryServiceQualifier +import org.yapp.apis.book.helper.AladinApiHelper import org.yapp.apis.book.service.BookQueryService -import org.yapp.infra.external.aladin.helper.AladinApiHelper import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse @@ -24,7 +24,7 @@ class AladinBookQueryService( log.info("Service - Converting BookSearchRequest to AladinBookSearchRequest and calling Aladin API for book search.") val aladinSearchRequest = request.toAladinRequest() val response: AladinSearchResponse = aladinApiHelper.searchBooks(aladinSearchRequest) - return BookSearchResponse.Companion.from(response) + return BookSearchResponse.from(response) } @@ -32,6 +32,6 @@ class AladinBookQueryService( log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.") val aladinLookupRequest = request.toAladinRequest() val aladinResponse: AladinBookDetailResponse = aladinApiHelper.lookupBook(aladinLookupRequest) - return BookDetailResponse.Companion.from(aladinResponse) + return BookDetailResponse.from(aladinResponse) } } From 5fa8152bfde20291fd250b2c2d85f2f3bcb14c9a Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 22:03:37 +0900 Subject: [PATCH 03/46] =?UTF-8?q?[BOOK-207]=20fix:=20apis,=20infra=20-=20A?= =?UTF-8?q?ladinBookLookupRequest=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20import=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/request/BookDetailRequest.kt | 4 ++-- .../org/yapp/apis/book/dto/response/BookSearchResponse.kt | 2 -- .../kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt | 6 +++--- .../kotlin/org/yapp/infra/external/aladin/AladinApi.kt | 2 +- .../external/aladin/request/AladinBookLookupRequest.kt | 8 ++------ 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt index 90c0a9c8..ef01fab2 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt @@ -1,9 +1,9 @@ package org.yapp.apis.book.dto.request import jakarta.validation.constraints.NotBlank -import jakarta.validation.constraints.Pattern // Pattern 어노테이션 추가 +import jakarta.validation.constraints.Pattern import org.yapp.globalutils.util.RegexUtils -import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest +import org.yapp.infra.external.aladin.request.AladinBookLookupRequest data class BookDetailRequest private constructor( @field:NotBlank(message = "아이템 ID는 필수입니다.") diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt index 4a75bb9d..0ebe6258 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt @@ -1,10 +1,8 @@ package org.yapp.apis.book.dto.response import org.yapp.domain.userbook.BookStatus -import org.yapp.domain.userbook.UserBook import org.yapp.infra.external.aladin.response.AladinSearchResponse import org.yapp.infra.external.aladin.response.BookItem -import java.time.LocalDateTime data class BookSearchResponse private constructor( val version: String?, diff --git a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt b/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt index db2a1572..6c911604 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt @@ -3,8 +3,8 @@ package org.yapp.apis.book.helper import mu.KotlinLogging import org.yapp.globalutils.annotation.Helper import org.yapp.infra.external.aladin.AladinApi -import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest +import org.yapp.infra.external.aladin.request.AladinBookLookupRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse @@ -15,7 +15,7 @@ class AladinApiHelper( private val log = KotlinLogging.logger {} fun searchBooks(request: AladinBookSearchRequest): AladinSearchResponse { - return aladinApi.searchBooks(request) // Pass the DTO directly to AladinApi + return aladinApi.searchBooks(request) .onSuccess { response -> log.info("Aladin search successful for query: '${request.query}', total results: ${response.totalResults}") } @@ -29,7 +29,7 @@ class AladinApiHelper( } fun lookupBook(request: AladinBookLookupRequest): AladinBookDetailResponse { - return aladinApi.lookupBook(request) // Pass the DTO directly to AladinApi + return aladinApi.lookupBook(request) .onSuccess { response -> log.info("Aladin lookup successful for itemId: '${request.itemId}', title: ${response.item?.firstOrNull()?.title}") } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt index 1680564d..8600288d 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt @@ -2,8 +2,8 @@ package org.yapp.infra.external.aladin import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component -import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest +import org.yapp.infra.external.aladin.request.AladinBookLookupRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 1c5d2052..99c911fe 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -1,10 +1,6 @@ -package org.yapp.infra.external.aladin.dto +package org.yapp.infra.external.aladin.request -/** - * 알라딘 ItemLookUp API 호출을 위한 요청 DTO. - * 내부적으로 알라딘 API 파라미터 규칙에 맞게 변환하는 책임을 가집니다. - */ -data class AladinBookLookupRequest private constructor( // private constructor 유지 +data class AladinBookLookupRequest private constructor( val itemId: String, val itemIdType: String, val optResult: List? From da20c1472edea5b3a037ec6f4b7a9a5ee49d0975 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 22:08:24 +0900 Subject: [PATCH 04/46] =?UTF-8?q?[BOOK-207]=20chore:=20apis=20-=20BookDeta?= =?UTF-8?q?ilResponse=EC=97=90=EC=84=9C=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/response/BookDetailResponse.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 63bcf923..628a8aff 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -3,10 +3,6 @@ package org.yapp.apis.book.dto.response import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import java.math.BigDecimal -/** - * 단일 도서의 상세 정보를 나타내는 DTO. - * 외부 API 응답 및 도메인 Book 객체로부터 변환됩니다. - */ data class BookDetailResponse private constructor( val version: String?, val title: String, @@ -28,8 +24,6 @@ data class BookDetailResponse private constructor( val publisher: String, ) { companion object { - - fun from(response: AladinBookDetailResponse): BookDetailResponse { val bookItem = response.item?.firstOrNull() ?: throw IllegalArgumentException("No book item found in detail response.") From 698cbc2256a98961bc1e8710644abcad91bb3c67 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:12:35 +0900 Subject: [PATCH 05/46] =?UTF-8?q?[BOOK-207]=20chore:=20apis=20-=20Swagger?= =?UTF-8?q?=20UI=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20/swagger-ui=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/src/main/resources/application.yml b/apis/src/main/resources/application.yml index afda8bd2..aaf88d7c 100644 --- a/apis/src/main/resources/application.yml +++ b/apis/src/main/resources/application.yml @@ -25,7 +25,7 @@ spring: springdoc: swagger-ui: - path: /swagger-ui.html + path: /swagger-ui enabled: true api-docs: path: /v3/api-docs From e26349a93af8bc801c26dd8aa79c87bc84d6e3bd Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:13:23 +0900 Subject: [PATCH 06/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20Aladin=20?= =?UTF-8?q?API=20Manager=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AladinApiHelper.kt => manager/AladinApiManager.kt} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename apis/src/main/kotlin/org/yapp/apis/book/{helper/AladinApiHelper.kt => manager/AladinApiManager.kt} (93%) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt b/apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt similarity index 93% rename from apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt rename to apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt index 6c911604..d7d5e4fd 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt @@ -1,15 +1,15 @@ -package org.yapp.apis.book.helper +package org.yapp.apis.book.manager import mu.KotlinLogging -import org.yapp.globalutils.annotation.Helper +import org.springframework.stereotype.Component import org.yapp.infra.external.aladin.AladinApi import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest import org.yapp.infra.external.aladin.request.AladinBookLookupRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse -@Helper -class AladinApiHelper( +@Component +class AladinApiManager( private val aladinApi: AladinApi ) { private val log = KotlinLogging.logger {} From cec68cd8a415cd003baa5522ed5085cbb643858c Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:14:09 +0900 Subject: [PATCH 07/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20AladinBoo?= =?UTF-8?q?kQueryService=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20BookQueryService=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A5=BC=20sealed=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/constant/BookQueryServiceQualifier.kt | 5 ----- .../service/{impl => }/AladinBookQueryService.kt | 15 +++++---------- .../yapp/apis/book/service/BookQueryService.kt | 3 +-- .../org/yapp/apis/book/usecase/BookUseCase.kt | 14 ++++---------- 4 files changed, 10 insertions(+), 27 deletions(-) delete mode 100644 apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt rename apis/src/main/kotlin/org/yapp/apis/book/service/{impl => }/AladinBookQueryService.kt (72%) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt b/apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt deleted file mode 100644 index 778576d9..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.yapp.apis.book.constant - -object BookQueryServiceQualifier { - const val ALADIN = "aladinBookQueryService" -} diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt similarity index 72% rename from apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt rename to apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt index 5875a216..e80b7abe 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt @@ -1,37 +1,32 @@ -package org.yapp.apis.book.service.impl +package org.yapp.apis.book.service import mu.KotlinLogging -import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service -import org.yapp.apis.book.constant.BookQueryServiceQualifier import org.yapp.apis.book.dto.request.BookDetailRequest import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse -import org.yapp.apis.book.helper.AladinApiHelper -import org.yapp.apis.book.service.BookQueryService +import org.yapp.apis.book.manager.AladinApiManager import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse @Service -@Qualifier(BookQueryServiceQualifier.ALADIN) class AladinBookQueryService( - private val aladinApiHelper: AladinApiHelper, + private val aladinApiManager: AladinApiManager, ) : BookQueryService { private val log = KotlinLogging.logger {} override fun searchBooks(request: BookSearchRequest): BookSearchResponse { log.info("Service - Converting BookSearchRequest to AladinBookSearchRequest and calling Aladin API for book search.") val aladinSearchRequest = request.toAladinRequest() - val response: AladinSearchResponse = aladinApiHelper.searchBooks(aladinSearchRequest) + val response: AladinSearchResponse = aladinApiManager.searchBooks(aladinSearchRequest) return BookSearchResponse.from(response) } - override fun getBookDetail(request: BookDetailRequest): BookDetailResponse { log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.") val aladinLookupRequest = request.toAladinRequest() - val aladinResponse: AladinBookDetailResponse = aladinApiHelper.lookupBook(aladinLookupRequest) + val aladinResponse: AladinBookDetailResponse = aladinApiManager.lookupBook(aladinLookupRequest) return BookDetailResponse.from(aladinResponse) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt index 01a8b89a..9f966f88 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt @@ -5,8 +5,7 @@ import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse - -interface BookQueryService { +sealed interface BookQueryService { fun searchBooks(request: BookSearchRequest): BookSearchResponse fun getBookDetail(request: BookDetailRequest): BookDetailResponse } 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 2d273923..49dbe721 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 @@ -1,33 +1,27 @@ package org.yapp.apis.book.usecase -import org.springframework.beans.factory.annotation.Qualifier -import org.springframework.data.domain.Page + import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest import org.yapp.apis.auth.service.UserAuthService -import org.yapp.apis.book.dto.request.BookCreateRequest -import org.yapp.apis.book.dto.request.BookDetailRequest -import org.yapp.apis.book.dto.request.BookSearchRequest -import org.yapp.apis.book.dto.request.UserBookRegisterRequest + +import org.yapp.apis.book.dto.request.* import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse import org.yapp.apis.book.dto.response.UserBookPageResponse import org.yapp.apis.book.dto.response.UserBookResponse -import org.yapp.apis.book.constant.BookQueryServiceQualifier -import org.yapp.apis.book.dto.request.UpsertUserBookRequest import org.yapp.apis.book.service.BookManagementService import org.yapp.apis.book.service.BookQueryService import org.yapp.apis.book.service.UserBookService import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBookSortType import org.yapp.globalutils.annotation.UseCase -import java.util.UUID +import java.util.* @UseCase @Transactional(readOnly = true) class BookUseCase( - @Qualifier(BookQueryServiceQualifier.ALADIN) private val bookQueryService: BookQueryService, private val userAuthService: UserAuthService, private val userBookService: UserBookService, From e87dd78388e283a27a14ff8be8425af8c3a929c6 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:14:31 +0900 Subject: [PATCH 08/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis,=20infra=20-=20?= =?UTF-8?q?AladinBookLookupRequest=20=EB=B0=8F=20AladinBookSearchRequest?= =?UTF-8?q?=EC=9D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/book/dto/request/BookDetailRequest.kt | 7 +++++-- .../yapp/apis/book/dto/request/BookSearchRequest.kt | 8 ++------ .../org/yapp/infra/external/aladin/AladinApi.kt | 7 +++---- .../aladin/request/AladinBookLookupRequest.kt | 11 +++++++---- .../aladin/request/AladinBookSearchRequest.kt | 5 ++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt index ef01fab2..e20cd228 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt @@ -18,7 +18,7 @@ data class BookDetailRequest private constructor( fun validIsbn(): String = itemId!! fun toAladinRequest(): AladinBookLookupRequest { - return AladinBookLookupRequest.create( + return AladinBookLookupRequest.of( itemId = this.validIsbn(), itemIdType = this.itemIdType ?: "ISBN", optResult = this.optResult @@ -26,7 +26,10 @@ data class BookDetailRequest private constructor( } companion object { - fun of(isbn: String?, optResult: List? = null): BookDetailRequest { + fun of( + isbn: String?, + optResult: List? = null + ): BookDetailRequest { return BookDetailRequest( itemId = isbn, itemIdType = "ISBN", diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt index 6fb7a7ff..530d708a 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt @@ -2,7 +2,6 @@ package org.yapp.apis.book.dto.request import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest - data class BookSearchRequest private constructor( val query: String? = null, val queryType: String? = null, @@ -10,21 +9,18 @@ data class BookSearchRequest private constructor( val maxResults: Int? = null, val start: Int? = null, val sort: String? = null, - val cover: String? = null, val categoryId: Int? = null ) { - fun validQuery(): String = query!! - fun toAladinRequest(): AladinBookSearchRequest { - return AladinBookSearchRequest.create( + fun toAladinRequest(): AladinBookSearchRequest { + return AladinBookSearchRequest.of( query = this.validQuery(), queryType = this.queryType, searchTarget = this.searchTarget, maxResults = this.maxResults, start = this.start, sort = this.sort, - cover = this.cover, categoryId = this.categoryId ) } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt index 8600288d..a77e9d4e 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt @@ -16,15 +16,14 @@ class AladinApi( fun searchBooks(request: AladinBookSearchRequest): Result { return runCatching { val aladinApiParams = request.toMap() - aladinRestClient.itemSearch(ttbKey, aladinApiParams) // Map으로 전달 + aladinRestClient.itemSearch(ttbKey, aladinApiParams) } } fun lookupBook(request: AladinBookLookupRequest): Result { return runCatching { - val aladinApiParams = request.toMap().toMutableMap() - aladinApiParams["Cover"] = "Big" - aladinRestClient.itemLookUp(ttbKey, aladinApiParams) // Map으로 전달 + val aladinApiParams = request.toMap() + aladinRestClient.itemLookUp(ttbKey, aladinApiParams) } } } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 99c911fe..57d07f44 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -3,7 +3,8 @@ package org.yapp.infra.external.aladin.request data class AladinBookLookupRequest private constructor( val itemId: String, val itemIdType: String, - val optResult: List? + val optResult: List?, + val cover: String? ) { fun toMap(): Map { val params = mutableMapOf() @@ -14,16 +15,18 @@ data class AladinBookLookupRequest private constructor( params["OptResult"] = it } } + cover?.let { params["Cover"] = it } return params } companion object { - fun create( + fun of( itemId: String, itemIdType: String, - optResult: List? = null + optResult: List? = null, + cover: String? = null ): AladinBookLookupRequest { - return AladinBookLookupRequest(itemId, itemIdType, optResult) + return AladinBookLookupRequest(itemId, itemIdType, optResult, cover) } } } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt index 32714ebc..74d6ea9e 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt @@ -24,14 +24,13 @@ data class AladinBookSearchRequest private constructor( } companion object { - fun create( + fun of( query: String, queryType: String? = null, searchTarget: String? = null, maxResults: Int? = null, start: Int? = null, sort: String? = null, - cover: String? = null, categoryId: Int? = null ): AladinBookSearchRequest { return AladinBookSearchRequest( @@ -41,7 +40,7 @@ data class AladinBookSearchRequest private constructor( maxResults = maxResults, start = start, sort = sort, - cover = cover, + cover = "Big", categoryId = categoryId ) } From e5b23c0efd1f57fc98c1f1e45d314051016cf098 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:17:07 +0900 Subject: [PATCH 09/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20getBo?= =?UTF-8?q?okDetail=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20userId=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/book/controller/BookController.kt | 2 +- .../org/yapp/apis/book/usecase/BookUseCase.kt | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) 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 a036e959..7696d250 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 @@ -45,7 +45,7 @@ class BookController( @AuthenticationPrincipal userId: UUID, @Valid @ModelAttribute request: BookDetailRequest ): ResponseEntity { - val response = bookUseCase.getBookDetail(request) + val response = bookUseCase.getBookDetail(request, userId) return ResponseEntity.ok(response) } 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 49dbe721..fc109dca 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 @@ -27,7 +27,10 @@ class BookUseCase( private val userBookService: UserBookService, private val bookManagementService: BookManagementService ) { - fun searchBooks(request: BookSearchRequest, userId: UUID): BookSearchResponse { + fun searchBooks( + request: BookSearchRequest, + userId: UUID + ): BookSearchResponse { userAuthService.validateUserExists(userId) val searchResponse = bookQueryService.searchBooks(request) @@ -45,12 +48,19 @@ class BookUseCase( return searchResponse.from(updatedBooks) } - fun getBookDetail(bookDetailRequest: BookDetailRequest): BookDetailResponse { + fun getBookDetail( + bookDetailRequest: BookDetailRequest, + userId: UUID + ): BookDetailResponse { + userAuthService.validateUserExists(userId) return bookQueryService.getBookDetail(bookDetailRequest) } @Transactional - fun upsertBookToMyLibrary(userId: UUID, request: UserBookRegisterRequest): UserBookResponse { + fun upsertBookToMyLibrary( + userId: UUID, + request: UserBookRegisterRequest + ): UserBookResponse { userAuthService.validateUserExists(userId) val bookDetailResponse = bookQueryService.getBookDetail(BookDetailRequest.of(request.validBookIsbn())) From 6bb691c9e04bf74812770ade8c531865c2251706 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 20:18:41 +0900 Subject: [PATCH 10/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20BookUseCa?= =?UTF-8?q?se=EC=97=90=20AladinBookQueryService=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20Qualifier=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt | 2 ++ 1 file changed, 2 insertions(+) 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 fc109dca..2c6966d2 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 @@ -1,6 +1,7 @@ package org.yapp.apis.book.usecase +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest @@ -22,6 +23,7 @@ import java.util.* @UseCase @Transactional(readOnly = true) class BookUseCase( + @Qualifier("aladinBookQueryService") private val bookQueryService: BookQueryService, private val userAuthService: UserAuthService, private val userBookService: UserBookService, From 491b53861cdf9eabd9772a42639fc0f9642cb1f8 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 23:40:49 +0900 Subject: [PATCH 11/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookD?= =?UTF-8?q?etailRequest=20=EB=B0=8F=20BookSearchRequest=EC=9D=98=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/request/BookDetailRequest.kt | 29 +++++-------------- .../book/dto/request/BookSearchRequest.kt | 15 ++-------- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt index e20cd228..caff905c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt @@ -3,37 +3,24 @@ package org.yapp.apis.book.dto.request import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Pattern import org.yapp.globalutils.util.RegexUtils -import org.yapp.infra.external.aladin.request.AladinBookLookupRequest + data class BookDetailRequest private constructor( - @field:NotBlank(message = "아이템 ID는 필수입니다.") + @field:NotBlank(message = "ISBN은 비어 있을 수 없습니다.") @field:Pattern( - regexp = RegexUtils.NOT_BLANK_AND_NOT_NULL_STRING_PATTERN, - message = "아이템 ID는 유효한 ISBN 형식이 아닙니다." + regexp = RegexUtils.ISBN13_PATTERN, + message = "유효한 13자리 ISBN 형식이 아닙니다." ) - val itemId: String? = null, - val itemIdType: String? = "ISBN", - val optResult: List? = null + val isbn: String? = null, ) { - fun validIsbn(): String = itemId!! - - fun toAladinRequest(): AladinBookLookupRequest { - return AladinBookLookupRequest.of( - itemId = this.validIsbn(), - itemIdType = this.itemIdType ?: "ISBN", - optResult = this.optResult - ) - } + fun validIsbn(): String = isbn!! companion object { - fun of( + fun from( isbn: String?, - optResult: List? = null ): BookDetailRequest { return BookDetailRequest( - itemId = isbn, - itemIdType = "ISBN", - optResult = optResult + isbn = isbn, ) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt index 530d708a..8044fc6b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt @@ -1,8 +1,9 @@ package org.yapp.apis.book.dto.request -import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest +import jakarta.validation.constraints.NotBlank data class BookSearchRequest private constructor( + @field:NotBlank(message = "검색어는 필수입니다.") val query: String? = null, val queryType: String? = null, val searchTarget: String? = null, @@ -12,16 +13,4 @@ data class BookSearchRequest private constructor( val categoryId: Int? = null ) { fun validQuery(): String = query!! - - fun toAladinRequest(): AladinBookSearchRequest { - return AladinBookSearchRequest.of( - query = this.validQuery(), - queryType = this.queryType, - searchTarget = this.searchTarget, - maxResults = this.maxResults, - start = this.start, - sort = this.sort, - categoryId = this.categoryId - ) - } } From 76193e2f43be959deb1f8995162b717fb855f3db Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 23:42:11 +0900 Subject: [PATCH 12/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20Aladi?= =?UTF-8?q?n=20API=20=EA=B4=80=EB=A0=A8=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20AladinApiManager?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/book/exception/BookErrorCode.kt | 8 +--- ...FoundException.kt => UserBookException.kt} | 2 +- .../apis/book/manager/AladinApiManager.kt | 44 ------------------- .../book/service/AladinBookQueryService.kt | 38 +++++++++++++--- .../yapp/apis/book/service/UserBookService.kt | 14 +++--- .../yapp/infra/external/aladin/AladinApi.kt | 2 +- .../infra/external/aladin/AladinRestClient.kt | 10 ++--- .../aladin/request/AladinBookLookupRequest.kt | 17 +++---- .../aladin/request/AladinBookSearchRequest.kt | 9 ++-- 9 files changed, 57 insertions(+), 87 deletions(-) rename apis/src/main/kotlin/org/yapp/apis/book/exception/{UserBookNotFoundException.kt => UserBookException.kt} (85%) delete mode 100644 apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt diff --git a/apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt b/apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt index b662c3b9..2034b7ff 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt @@ -9,13 +9,9 @@ enum class BookErrorCode( private val code: String, private val message: String ) : BaseErrorCode { - - /* 404 NOT_FOUND */ - BOOK_NOT_FOUND(HttpStatus.NOT_FOUND, "BOOK_001", "Book not found."), - /* 500 INTERNAL_SERVER_ERROR */ - ALADIN_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "BOOK_002", "Error fetching book from external API."); - + ALADIN_API_SEARCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "BOOK_001", "알라딘 도서 검색 API 호출에 실패했습니다."), + ALADIN_API_LOOKUP_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "BOOK_002", "알라딘 도서 상세 조회 API 호출에 실패했습니다."); override fun getHttpStatus(): HttpStatus = httpStatus override fun getCode(): String = code diff --git a/apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookNotFoundException.kt b/apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt similarity index 85% rename from apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookNotFoundException.kt rename to apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt index 7598e468..5070455d 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookNotFoundException.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt @@ -2,7 +2,7 @@ package org.yapp.apis.book.exception import org.yapp.globalutils.exception.CommonException -class UserBookNotFoundException( +class UserBookException ( errorCode: UserBookErrorCode, message: String? = null ) : CommonException(errorCode, message) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt b/apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt deleted file mode 100644 index d7d5e4fd..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/book/manager/AladinApiManager.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.yapp.apis.book.manager - -import mu.KotlinLogging -import org.springframework.stereotype.Component -import org.yapp.infra.external.aladin.AladinApi -import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest -import org.yapp.infra.external.aladin.request.AladinBookLookupRequest -import org.yapp.infra.external.aladin.response.AladinBookDetailResponse -import org.yapp.infra.external.aladin.response.AladinSearchResponse - -@Component -class AladinApiManager( - private val aladinApi: AladinApi -) { - private val log = KotlinLogging.logger {} - - fun searchBooks(request: AladinBookSearchRequest): AladinSearchResponse { - return aladinApi.searchBooks(request) - .onSuccess { response -> - log.info("Aladin search successful for query: '${request.query}', total results: ${response.totalResults}") - } - .getOrElse { exception -> - log.error("Failed to call Aladin search API for request: '$request'", exception) - throw IllegalStateException( - "Failed to retrieve search results from Aladin API: ${exception.message}", - exception - ) - } - } - - fun lookupBook(request: AladinBookLookupRequest): AladinBookDetailResponse { - return aladinApi.lookupBook(request) - .onSuccess { response -> - log.info("Aladin lookup successful for itemId: '${request.itemId}', title: ${response.item?.firstOrNull()?.title}") - } - .getOrElse { exception -> - log.error("Failed to call Aladin lookup API for request: '$request'", exception) - throw IllegalStateException( - "Failed to retrieve book details from Aladin API: ${exception.message}", - exception - ) - } - } -} diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt index e80b7abe..f8cf195c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt @@ -6,27 +6,53 @@ import org.yapp.apis.book.dto.request.BookDetailRequest import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse -import org.yapp.apis.book.manager.AladinApiManager +import org.yapp.apis.book.exception.BookErrorCode +import org.yapp.apis.book.exception.BookException +import org.yapp.infra.external.aladin.AladinApi +import org.yapp.infra.external.aladin.request.AladinBookLookupRequest +import org.yapp.infra.external.aladin.request.AladinBookSearchRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse @Service class AladinBookQueryService( - private val aladinApiManager: AladinApiManager, + private val aladinApi: AladinApi ) : BookQueryService { private val log = KotlinLogging.logger {} override fun searchBooks(request: BookSearchRequest): BookSearchResponse { log.info("Service - Converting BookSearchRequest to AladinBookSearchRequest and calling Aladin API for book search.") - val aladinSearchRequest = request.toAladinRequest() - val response: AladinSearchResponse = aladinApiManager.searchBooks(aladinSearchRequest) + val aladinSearchRequest = AladinBookSearchRequest.of( + request.validQuery(), + request.queryType, + request.searchTarget, + request.maxResults, + request.start, + request.sort, + request.categoryId + ) + val response: AladinSearchResponse = aladinApi.searchBooks(aladinSearchRequest) + .onSuccess { response -> + log.info("Aladin search successful for query: '${request.query}', total results: ${response.totalResults}") + } + .getOrElse { exception -> + log.error("Failed to call Aladin search API for request: '$request'", exception) + throw BookException(BookErrorCode.ALADIN_API_SEARCH_FAILED, exception.message) + } return BookSearchResponse.from(response) } override fun getBookDetail(request: BookDetailRequest): BookDetailResponse { log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.") - val aladinLookupRequest = request.toAladinRequest() - val aladinResponse: AladinBookDetailResponse = aladinApiManager.lookupBook(aladinLookupRequest) + val aladinLookupRequest = AladinBookLookupRequest.from(request.validIsbn()) + val aladinResponse: AladinBookDetailResponse = aladinApi.lookupBook(aladinLookupRequest) + .onSuccess { response -> + log.info("Aladin lookup successful for itemId: '${aladinLookupRequest.itemId}', title: ${response.item?.firstOrNull()?.title}") + } + .getOrElse { exception -> + log.error("Failed to call Aladin lookup API for request: '$request'", exception) + throw BookException(BookErrorCode.ALADIN_API_LOOKUP_FAILED, exception.message) + } return BookDetailResponse.from(aladinResponse) } } 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 b2742c46..93ced786 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 @@ -8,9 +8,8 @@ import org.yapp.apis.book.dto.request.UpsertUserBookRequest import org.yapp.apis.book.dto.response.UserBookPageResponse import org.yapp.apis.book.dto.response.UserBookResponse import org.yapp.apis.book.exception.UserBookErrorCode -import org.yapp.apis.book.exception.UserBookNotFoundException +import org.yapp.apis.book.exception.UserBookException import org.yapp.domain.userbook.BookStatus -import org.yapp.domain.userbook.UserBook import org.yapp.domain.userbook.UserBookDomainService import org.yapp.domain.userbook.UserBookSortType import java.util.* @@ -33,12 +32,13 @@ class UserBookService( return UserBookResponse.from(userBookInfoVO) } - fun validateUserBookExists(userId: UUID, userBookId: UUID): UserBook { - return userBookDomainService.findByIdAndUserId(userBookId, userId) - ?: throw UserBookNotFoundException( + fun validateUserBookExists(userBookId: UUID, userId: UUID) { + if (!userBookDomainService.existsByUserBookIdAndUserId(userBookId, userId)) { + throw UserBookException( UserBookErrorCode.USER_BOOK_NOT_FOUND, - "User book not found with id: $userBookId and userId: $userId" + "UserBook not found or access denied: $userBookId" ) + } } fun findAllByUserIdAndBookIsbnIn(userBooksByIsbnsRequest: UserBooksByIsbnsRequest): List { @@ -49,8 +49,6 @@ class UserBookService( return userBooks.map { UserBookResponse.from(it) } } - - private fun findUserBooksByDynamicCondition( userId: UUID, status: BookStatus?, diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt index a77e9d4e..f58fe191 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt @@ -2,8 +2,8 @@ package org.yapp.infra.external.aladin import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component -import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest import org.yapp.infra.external.aladin.request.AladinBookLookupRequest +import org.yapp.infra.external.aladin.request.AladinBookSearchRequest import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt index e8e858f6..9d7f412c 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt @@ -9,11 +9,9 @@ import org.yapp.infra.external.aladin.response.AladinSearchResponse @Component class AladinRestClient( - @Qualifier("aladinApiRestClient") private val restClient: RestClient + @Qualifier("aladinApiRestClient") + private val restClient: RestClient ) { - - private val client = restClient - private val API_VERSION = "20131101" private val DEFAULT_OUTPUT_FORMAT = "JS" @@ -38,7 +36,7 @@ class AladinRestClient( uriBuilder.addCommonQueryParams(params) - return client.get() + return restClient.get() .uri(uriBuilder.build().toUriString()) .retrieve() .body(AladinSearchResponse::class.java) @@ -54,7 +52,7 @@ class AladinRestClient( uriBuilder.addCommonQueryParams(params) - return client.get() + return restClient.get() .uri(uriBuilder.build().toUriString()) .retrieve() .body(AladinBookDetailResponse::class.java) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 57d07f44..1dab61a5 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -2,31 +2,24 @@ package org.yapp.infra.external.aladin.request data class AladinBookLookupRequest private constructor( val itemId: String, - val itemIdType: String, - val optResult: List?, + val itemIdType: String = "ISBN13", + val output: String?, val cover: String? ) { fun toMap(): Map { val params = mutableMapOf() params["ItemId"] = itemId params["ItemIdType"] = itemIdType - optResult?.let { - if (it.isNotEmpty()) { - params["OptResult"] = it - } - } + output?.let { params["Output"] = it } cover?.let { params["Cover"] = it } return params } companion object { - fun of( + fun from( itemId: String, - itemIdType: String, - optResult: List? = null, - cover: String? = null ): AladinBookLookupRequest { - return AladinBookLookupRequest(itemId, itemIdType, optResult, cover) + return AladinBookLookupRequest(itemId, "ISBN13", "JS", "Big") } } } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt index 74d6ea9e..7f9b5707 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt @@ -1,4 +1,4 @@ -package org.yapp.infra.external.aladin.dto +package org.yapp.infra.external.aladin.request data class AladinBookSearchRequest private constructor( val query: String, @@ -8,7 +8,8 @@ data class AladinBookSearchRequest private constructor( val start: Int?, val sort: String?, val cover: String?, - val categoryId: Int? + val categoryId: Int?, + val output: String? ) { fun toMap(): Map { val params = mutableMapOf() @@ -20,6 +21,7 @@ data class AladinBookSearchRequest private constructor( sort?.let { params["Sort"] = it } cover?.let { params["Cover"] = it } categoryId?.let { params["CategoryId"] = it } + output?.let { params["Output"] = it } return params } @@ -41,7 +43,8 @@ data class AladinBookSearchRequest private constructor( start = start, sort = sort, cover = "Big", - categoryId = categoryId + categoryId = categoryId, + output = "JS" ) } } From 08bb18c3232b0b8c7dc2ad766893e829538195f8 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 23:42:39 +0900 Subject: [PATCH 13/46] =?UTF-8?q?[BOOK-207]=20refactor:=20domain,=20infra?= =?UTF-8?q?=20-=20UserBookRepository=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=EC=84=9C=20findByIdAndUser?= =?UTF-8?q?Id=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20existsByIdAndUserId?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EC=97=AC=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt | 2 +- .../kotlin/org/yapp/domain/userbook/UserBookDomainService.kt | 4 ++-- .../kotlin/org/yapp/domain/userbook/UserBookRepository.kt | 2 +- .../yapp/infra/userbook/repository/JpaUserBookRepository.kt | 2 +- .../infra/userbook/repository/impl/UserBookRepositoryImpl.kt | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) 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 2c6966d2..0f966c0f 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 @@ -65,7 +65,7 @@ class BookUseCase( ): UserBookResponse { userAuthService.validateUserExists(userId) - val bookDetailResponse = bookQueryService.getBookDetail(BookDetailRequest.of(request.validBookIsbn())) + val bookDetailResponse = bookQueryService.getBookDetail(BookDetailRequest.from(request.validBookIsbn())) val bookCreateResponse = bookManagementService.findOrCreateBook(BookCreateRequest.from(bookDetailResponse)) val upsertUserBookRequest = UpsertUserBookRequest.of( userId = userId, 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 2928196b..02626eea 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -68,8 +68,8 @@ class UserBookDomainService( return userBookRepository.countUserBooksByStatus(userId, status) } - fun findByIdAndUserId(userBookId: UUID, userId: UUID): UserBook? { - return userBookRepository.findByIdAndUserId(userBookId, userId) + fun existsByUserBookIdAndUserId(userBookId: UUID, userId: UUID): Boolean { + return userBookRepository.existsByIdAndUserId(userBookId, userId) } fun findBooksWithRecordsOrderByLatest(userId: UUID): List { 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 56b2a9ac..4b2c23ba 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt @@ -10,7 +10,7 @@ interface UserBookRepository { fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBook? fun findByBookIdAndUserId(bookId: UUID, userId: UUID): UserBook? - fun findByIdAndUserId(id: UUID, userId: UUID): UserBook? + fun existsByIdAndUserId(id: UUID, userId: UUID): Boolean fun findById(id: UUID): UserBook? fun save(userBook: UserBook): UserBook diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt index d6887977..c63fee6b 100644 --- a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt @@ -7,7 +7,7 @@ import java.util.* interface JpaUserBookRepository : JpaRepository, JpaUserBookQuerydslRepository { fun findByUserIdAndBookIsbn(userId: UUID, bookIsbn: String): UserBookEntity? fun findByBookIdAndUserId(bookId: UUID, userId: UUID): UserBookEntity? - fun findByIdAndUserId(id: UUID, userId: UUID): UserBookEntity? + fun existsByIdAndUserId(id: UUID, userId: UUID): Boolean fun findAllByUserId(userId: UUID): List fun findAllByUserIdAndBookIsbnIn(userId: UUID, bookIsbnList: List): List } 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 3cda1f8f..38320e2b 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 @@ -25,8 +25,8 @@ class UserBookRepositoryImpl( return jpaUserBookRepository.findByBookIdAndUserId(bookId, userId)?.toDomain() } - override fun findByIdAndUserId(id: UUID, userId: UUID): UserBook? { - return jpaUserBookRepository.findByIdAndUserId(id, userId)?.toDomain() + override fun existsByIdAndUserId(id: UUID, userId: UUID): Boolean { + return jpaUserBookRepository.existsByIdAndUserId(id, userId) } override fun findById(id: UUID): UserBook? { From d8d05933578e085a60e2b5ed5e2b117e9fc5684b Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Sun, 3 Aug 2025 23:42:47 +0900 Subject: [PATCH 14/46] =?UTF-8?q?[BOOK-207]=20refactor:=20global-utils=20-?= =?UTF-8?q?=20RegexUtils.kt=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EB=B0=8F=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20URL=20=ED=8C=A8=ED=84=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=98=EA=B3=A0=20ISBN13=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/globalutils/util/RegexUtils.kt | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt index eec3afda..f8d123d8 100644 --- a/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt @@ -1,18 +1,5 @@ package org.yapp.globalutils.util object RegexUtils { - - val EMAIL_PATTERN = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") - - val PROFILE_IMAGE_URL_PATTERN = Regex("^https?://[a-zA-Z0-9.-]+(/.*)?$") - - const val NOT_BLANK_AND_NOT_NULL_STRING_PATTERN = "^(?!null$|NULL$|\\s*$).+" // Removed the old ISBN pattern - - fun isValidEmail(email: String): Boolean { - return email.matches(EMAIL_PATTERN) - } - - fun isValidProfileImageUrl(url: String): Boolean { - return url.matches(PROFILE_IMAGE_URL_PATTERN) - } + const val ISBN13_PATTERN = "^(978|979)\\d{10}$" } From 894c9d5d479b8f456ccda8ee3a0dcac9511c4996 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 00:24:35 +0900 Subject: [PATCH 15/46] =?UTF-8?q?[BOOK-207]=20feat:=20infra=20-=20Aladin?= =?UTF-8?q?=20API=20=EC=9D=91=EB=8B=B5=EC=9D=84=20=EC=9C=84=ED=95=9C=20Ala?= =?UTF-8?q?dinDetailResponse=20=EB=B0=8F=20AladinSearchResponse=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aladin/response/AladinDetailResponse.kt | 52 +++++++++++++++++++ .../aladin/response/AladinSearchResponse.kt | 41 +++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt create mode 100644 infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt new file mode 100644 index 00000000..4f0389b5 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt @@ -0,0 +1,52 @@ +package org.yapp.infra.external.aladin.response + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty +import java.math.BigDecimal + +@JsonIgnoreProperties(ignoreUnknown = true) +data class AladinBookDetailResponse internal constructor( + @JsonProperty("version") val version: String?, + @JsonProperty("title") val title: String?, + @JsonProperty("link") val link: String?, + @JsonProperty("pubDate") val pubDate: String?, + @JsonProperty("totalResults") val totalResults: Int?, + @JsonProperty("startIndex") val startIndex: Int?, + @JsonProperty("itemsPerPage") val itemsPerPage: Int?, + @JsonProperty("query") val query: String? = null, + @JsonProperty("searchCategoryId") val searchCategoryId: Int? = null, + @JsonProperty("searchCategoryName") val searchCategoryName: String? = null, + @JsonProperty("item") val item: List = emptyList() +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class AladinDetailItem internal constructor( + @JsonProperty("title") val title: String, + @JsonProperty("link") val link: String, + @JsonProperty("author") val author: String?, + @JsonProperty("pubDate") val pubDate: String?, + @JsonProperty("description") val description: String?, + @JsonProperty("isbn") val isbn: String?, + @JsonProperty("isbn13") val isbn13: String, + @JsonProperty("itemId") val itemId: Long, + @JsonProperty("priceSales") val priceSales: BigDecimal, + @JsonProperty("priceStandard") val priceStandard: BigDecimal, + @JsonProperty("mallType") val mallType: String, + @JsonProperty("stockStatus") val stockStatus: String?, + @JsonProperty("mileage") val mileage: Int?, + @JsonProperty("cover") val cover: String, + @JsonProperty("categoryId") val categoryId: Int, + @JsonProperty("categoryName") val categoryName: String, + @JsonProperty("publisher") val publisher: String?, + @JsonProperty("subInfo") val subInfo: AladinDetailSubInfo +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class AladinDetailSubInfo( + @JsonProperty("subTitle") + val subTitle: String?, + @JsonProperty("originalTitle") + val originalTitle: String?, + @JsonProperty("itemPage") + val itemPage: Int? +) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt new file mode 100644 index 00000000..623c4e94 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt @@ -0,0 +1,41 @@ +package org.yapp.infra.external.aladin.response + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty +import java.math.BigDecimal + +@JsonIgnoreProperties(ignoreUnknown = true) +data class AladinSearchResponse internal constructor( + @JsonProperty("version") val version: String?, + @JsonProperty("title") val title: String?, + @JsonProperty("link") val link: String?, + @JsonProperty("pubDate") val pubDate: String?, + @JsonProperty("totalResults") val totalResults: Int?, + @JsonProperty("startIndex") val startIndex: Int?, + @JsonProperty("itemsPerPage") val itemsPerPage: Int?, + @JsonProperty("query") val query: String? = null, + @JsonProperty("searchCategoryId") val searchCategoryId: Int? = null, + @JsonProperty("searchCategoryName") val searchCategoryName: String? = null, + @JsonProperty("item") val item: List = emptyList() +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class AladinSearchItem internal constructor( + @JsonProperty("title") val title: String, + @JsonProperty("link") val link: String, + @JsonProperty("author") val author: String?, + @JsonProperty("pubDate") val pubDate: String?, + @JsonProperty("description") val description: String?, + @JsonProperty("isbn") val isbn: String?, + @JsonProperty("isbn13") val isbn13: String, + @JsonProperty("itemId") val itemId: Long, + @JsonProperty("priceSales") val priceSales: BigDecimal, + @JsonProperty("priceStandard") val priceStandard: BigDecimal, + @JsonProperty("mallType") val mallType: String, + @JsonProperty("stockStatus") val stockStatus: String?, + @JsonProperty("mileage") val mileage: Int?, + @JsonProperty("cover") val cover: String, + @JsonProperty("categoryId") val categoryId: Int, + @JsonProperty("categoryName") val categoryName: String, + @JsonProperty("publisher") val publisher: String? +) From b50c97b0c99313a14f1de72e7e0e131d1a754c79 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 00:25:06 +0900 Subject: [PATCH 16/46] =?UTF-8?q?[BOOK-207]=20chore:=20apis=20-=20AladinBo?= =?UTF-8?q?okQueryService=EC=9D=98=20getBookDetail=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20=EB=B3=80=EC=88=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/service/AladinBookQueryService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt index f8cf195c..5e06d9fe 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt @@ -45,7 +45,7 @@ class AladinBookQueryService( override fun getBookDetail(request: BookDetailRequest): BookDetailResponse { log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.") val aladinLookupRequest = AladinBookLookupRequest.from(request.validIsbn()) - val aladinResponse: AladinBookDetailResponse = aladinApi.lookupBook(aladinLookupRequest) + val response: AladinBookDetailResponse = aladinApi.lookupBook(aladinLookupRequest) .onSuccess { response -> log.info("Aladin lookup successful for itemId: '${aladinLookupRequest.itemId}', title: ${response.item?.firstOrNull()?.title}") } @@ -53,6 +53,6 @@ class AladinBookQueryService( log.error("Failed to call Aladin lookup API for request: '$request'", exception) throw BookException(BookErrorCode.ALADIN_API_LOOKUP_FAILED, exception.message) } - return BookDetailResponse.from(aladinResponse) + return BookDetailResponse.from(response) } } From 86ca1b00024b4b403f428c8201fa50975e8d9fba Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:31:54 +0900 Subject: [PATCH 17/46] =?UTF-8?q?[BOOK-207]=20fix:=20infra=20-=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=9C=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/external/aladin/request/AladinBookSearchRequest.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt index 7f9b5707..863d60b3 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt @@ -9,7 +9,6 @@ data class AladinBookSearchRequest private constructor( val sort: String?, val cover: String?, val categoryId: Int?, - val output: String? ) { fun toMap(): Map { val params = mutableMapOf() @@ -21,7 +20,6 @@ data class AladinBookSearchRequest private constructor( sort?.let { params["Sort"] = it } cover?.let { params["Cover"] = it } categoryId?.let { params["CategoryId"] = it } - output?.let { params["Output"] = it } return params } @@ -44,7 +42,6 @@ data class AladinBookSearchRequest private constructor( sort = sort, cover = "Big", categoryId = categoryId, - output = "JS" ) } } From cb8b0e26cf9123651d7fef47637a613454a1822e Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:32:45 +0900 Subject: [PATCH 18/46] =?UTF-8?q?[BOOK-207]=20feat:=20global-utils=20-=20I?= =?UTF-8?q?SBN10=EC=9D=84=20ISBN13=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8A=94=20IsbnConverter=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/globalutils/util/IsbnConverter.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt new file mode 100644 index 00000000..f59e18fd --- /dev/null +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt @@ -0,0 +1,25 @@ +package org.yapp.globalutils.util + +object IsbnConverter { + fun toIsbn13(isbn10: String?): String? { + val sanitized = isbn10?.replace("-", "")?.uppercase() + + if (sanitized.isNullOrBlank() || sanitized.length != 10) { + return null + } + if (!sanitized.matches(Regex("^[0-9]{9}[0-9X]$"))) { + return null + } + + val stem = "978" + sanitized.substring(0, 9) + + val sum = stem.mapIndexed { index, c -> + val digit = c.digitToInt() + if ((index + 1) % 2 == 0) digit * 3 else digit + }.sum() + + val checkDigit = (10 - (sum % 10)) % 10 + + return stem + checkDigit + } +} From b923e195abe13822ba310be64d08e7c57271525f Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:33:13 +0900 Subject: [PATCH 19/46] =?UTF-8?q?[BOOK-207]=20refactor:=20infra=20-=20Alad?= =?UTF-8?q?inRestClient=EC=97=90=EC=84=9C=20addCommonQueryParams=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EB=8C=80=EC=86=8C=EB=AC=B8=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/external/aladin/AladinRestClient.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt index 9d7f412c..ffb93338 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt @@ -15,18 +15,6 @@ class AladinRestClient( private val API_VERSION = "20131101" private val DEFAULT_OUTPUT_FORMAT = "JS" - private fun UriComponentsBuilder.addCommonQueryParams(params: Map) { - params.forEach { (key, value) -> - if (key == "OptResult" && value is List<*>) { - this.queryParam(key, value.joinToString(",")) - } else { - this.queryParam(key, value) - } - } - this.queryParam("output", DEFAULT_OUTPUT_FORMAT) - .queryParam("Version", API_VERSION) - } - fun itemSearch( ttbKey: String?, params: Map @@ -58,4 +46,16 @@ class AladinRestClient( .body(AladinBookDetailResponse::class.java) ?: throw IllegalStateException("Aladin ItemLookUp API 응답이 null 입니다.") } + + private fun UriComponentsBuilder.addCommonQueryParams(params: Map) { + params.forEach { (key, value) -> + if (key == "OptResult" && value is List<*>) { + this.queryParam(key, value.joinToString(",")) + } else { + this.queryParam(key, value) + } + } + this.queryParam("Output", DEFAULT_OUTPUT_FORMAT) + .queryParam("Version", API_VERSION) + } } From 94895cc0234828b04552eb61cb7a1fc91f0b28c0 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:33:49 +0900 Subject: [PATCH 20/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookD?= =?UTF-8?q?etailResponse=20=EB=B0=8F=20BookSearchResponse=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=9D=98=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20ISBN=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/response/BookDetailResponse.kt | 54 +++++++----------- .../book/dto/response/BookSearchResponse.kt | 57 ++++++++++--------- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 628a8aff..32323c5f 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -1,52 +1,40 @@ package org.yapp.apis.book.dto.response +import org.yapp.globalutils.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinBookDetailResponse -import java.math.BigDecimal data class BookDetailResponse private constructor( val version: String?, val title: String, - val link: String?, - val author: String, - val pubDate: String?, - val description: String?, - val isbn: String?, + val link: String, + val author: String?, + val pubDate: String, + val description: String, val isbn13: String?, - val itemId: Long?, - val priceSales: BigDecimal?, - val priceStandard: BigDecimal?, - val mallType: String?, - val stockStatus: String?, - val mileage: Int?, + val mallType: String, val cover: String, - val categoryId: Int?, - val categoryName: String?, - val publisher: String, + val categoryName: String, + val publisher: String?, + val totalPage: Int? ) { companion object { fun from(response: AladinBookDetailResponse): BookDetailResponse { - val bookItem = response.item?.firstOrNull() + val item = response.item.firstOrNull() ?: throw IllegalArgumentException("No book item found in detail response.") return BookDetailResponse( version = response.version, - title = bookItem.title ?: "", - link = bookItem.link, - author = bookItem.author ?: "", - pubDate = bookItem.pubDate, - description = bookItem.description ?: "", - isbn = bookItem.isbn ?: bookItem.isbn13 ?: "", - isbn13 = bookItem.isbn13, - itemId = bookItem.itemId, - priceSales = bookItem.priceSales, - priceStandard = bookItem.priceStandard, - mallType = bookItem.mallType, - stockStatus = bookItem.stockStatus, - mileage = bookItem.mileage, - cover = bookItem.cover ?: "", - categoryId = bookItem.categoryId, - categoryName = bookItem.categoryName, - publisher = bookItem.publisher ?: "", + title = item.title, + link = item.link, + author = item.author ?: "", + pubDate = item.pubDate ?: "", + description = item.description ?: "", + mallType = item.mallType, + isbn13 = item.isbn13 ?: IsbnConverter.toIsbn13(item.isbn) ?: throw IllegalArgumentException("Either isbn13 or isbn must be provided"), + cover = item.cover, + categoryName = item.categoryName, + publisher = item.publisher ?: "", + totalPage = item.subInfo.itemPage ?: 4032 ) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt index 0ebe6258..ba1d2831 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt @@ -1,8 +1,8 @@ package org.yapp.apis.book.dto.response import org.yapp.domain.userbook.BookStatus +import org.yapp.globalutils.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinSearchResponse -import org.yapp.infra.external.aladin.response.BookItem data class BookSearchResponse private constructor( val version: String?, @@ -17,25 +17,12 @@ data class BookSearchResponse private constructor( val searchCategoryName: String?, val books: List ) { - fun from(updatedBooks: List): BookSearchResponse { - return BookSearchResponse( - version = this.version, - title = this.title, - link = this.link, - pubDate = this.pubDate, - totalResults = this.totalResults, - startIndex = this.startIndex, - itemsPerPage = this.itemsPerPage, - query = this.query, - searchCategoryId = this.searchCategoryId, - searchCategoryName = this.searchCategoryName, - books = updatedBooks - ) + fun withUpdatedBooks(updatedBooks: List): BookSearchResponse { + return this.copy(books = updatedBooks) } companion object { fun from(response: AladinSearchResponse): BookSearchResponse { - val books = response.item?.mapNotNull { BookSummary.fromAladinItem(it) } ?: emptyList() return BookSearchResponse( version = response.version, title = response.title, @@ -47,17 +34,26 @@ data class BookSearchResponse private constructor( query = response.query, searchCategoryId = response.searchCategoryId, searchCategoryName = response.searchCategoryName, - books = books + books = response.item.map { + BookSummary.of( + isbn = it.isbn, + isbn13 = it.isbn13, + title = it.title, + author = it.author, + publisher = it.publisher, + coverImageUrl = it.cover + ) + } ) } } data class BookSummary private constructor( - val isbn: String, + val isbn13: String, val title: String, val author: String?, val publisher: String?, - val coverImageUrl: String?, + val coverImageUrl: String, val userBookStatus: BookStatus ) { fun updateStatus(newStatus: BookStatus): BookSummary { @@ -65,17 +61,22 @@ data class BookSearchResponse private constructor( } companion object { - private val unknownTitle = "제목없음" + fun of( + isbn: String?, + isbn13: String?, + title: String?, + author: String?, + publisher: String?, + coverImageUrl: String + ): BookSummary { + require(!title.isNullOrBlank()) { "Title is required" } - - fun fromAladinItem(item: BookItem): BookSummary? { - val isbn = item.isbn ?: item.isbn13 ?: return null return BookSummary( - isbn = isbn, - title = item.title ?: unknownTitle, - author = item.author, - publisher = item.publisher, - coverImageUrl = item.cover, + isbn13 = isbn13 ?: IsbnConverter.toIsbn13(isbn) ?: throw IllegalArgumentException("Either isbn13 or isbn must be provided"), + title = title, + author = author, + publisher = publisher, + coverImageUrl = coverImageUrl, userBookStatus = BookStatus.BEFORE_REGISTRATION ) } From ee479c6c552d9a75945c30f892ead034127408c5 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:34:07 +0900 Subject: [PATCH 21/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookC?= =?UTF-8?q?reateRequest=EC=97=90=EC=84=9C=20ISBN=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EA=B0=9C=EC=84=A0=ED=95=98?= =?UTF-8?q?=EC=97=AC=20ISBN13=EB=A7=8C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt index 62afb3bb..5e0185d4 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt @@ -32,7 +32,7 @@ data class BookCreateRequest private constructor( companion object { fun from(bookDetail: BookDetailResponse): BookCreateRequest { - val finalIsbn = bookDetail.isbn ?: bookDetail.isbn13 + val finalIsbn = bookDetail.isbn13 ?: throw IllegalArgumentException("ISBN이 존재하지 않습니다.") return BookCreateRequest( From 4ebdc1a47bfd5719ee7465ad89664b80ea5f4ecf Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:34:27 +0900 Subject: [PATCH 22/46] =?UTF-8?q?[BOOK-207]=20refactor:=20infra=20-=20Alad?= =?UTF-8?q?inDetailResponse=20=EB=B0=8F=20AladinSearchResponse=EC=9D=98=20?= =?UTF-8?q?isbn13=20=ED=95=84=EB=93=9C=EB=A5=BC=20nullable=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/infra/external/aladin/response/AladinDetailResponse.kt | 2 +- .../yapp/infra/external/aladin/response/AladinSearchResponse.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt index 4f0389b5..404ec5b8 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt @@ -27,7 +27,7 @@ data class AladinDetailItem internal constructor( @JsonProperty("pubDate") val pubDate: String?, @JsonProperty("description") val description: String?, @JsonProperty("isbn") val isbn: String?, - @JsonProperty("isbn13") val isbn13: String, + @JsonProperty("isbn13") val isbn13: String?, @JsonProperty("itemId") val itemId: Long, @JsonProperty("priceSales") val priceSales: BigDecimal, @JsonProperty("priceStandard") val priceStandard: BigDecimal, diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt index 623c4e94..925d631c 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt @@ -27,7 +27,7 @@ data class AladinSearchItem internal constructor( @JsonProperty("pubDate") val pubDate: String?, @JsonProperty("description") val description: String?, @JsonProperty("isbn") val isbn: String?, - @JsonProperty("isbn13") val isbn13: String, + @JsonProperty("isbn13") val isbn13: String?, @JsonProperty("itemId") val itemId: Long, @JsonProperty("priceSales") val priceSales: BigDecimal, @JsonProperty("priceStandard") val priceStandard: BigDecimal, From 968c7947b35bd924f0de4c1c78a4b698dd5679be Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:34:43 +0900 Subject: [PATCH 23/46] =?UTF-8?q?[BOOK-207]=20delete:=20infra=20-=20Aladin?= =?UTF-8?q?ResponseBase.kt=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aladin/response/AladinResponseBase.kt | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt deleted file mode 100644 index 56e7975c..00000000 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.yapp.infra.external.aladin.response - -import com.fasterxml.jackson.annotation.JsonProperty -import java.math.BigDecimal - -data class BookItem internal constructor( - @JsonProperty("title") val title: String?, - @JsonProperty("link") val link: String?, - @JsonProperty("author") val author: String?, - @JsonProperty("pubDate") val pubDate: String?, - @JsonProperty("description") val description: String?, - @JsonProperty("isbn") val isbn: String?, - @JsonProperty("isbn13") val isbn13: String?, - @JsonProperty("itemId") val itemId: Long?, - @JsonProperty("priceSales") val priceSales: BigDecimal?, - @JsonProperty("priceStandard") val priceStandard: BigDecimal?, - @JsonProperty("mallType") val mallType: String?, - @JsonProperty("stockStatus") val stockStatus: String? = null, - @JsonProperty("mileage") val mileage: Int?, - @JsonProperty("cover") val cover: String?, - @JsonProperty("categoryId") val categoryId: Int?, - @JsonProperty("categoryName") val categoryName: String?, - @JsonProperty("publisher") val publisher: String?, - @JsonProperty("itemPage") val itemPage: Int? -) - -data class AladinSearchResponse internal constructor( - @JsonProperty("version") val version: String?, - @JsonProperty("title") val title: String?, - @JsonProperty("link") val link: String?, - @JsonProperty("pubDate") val pubDate: String?, - @JsonProperty("totalResults") val totalResults: Int?, - @JsonProperty("startIndex") val startIndex: Int?, - @JsonProperty("itemsPerPage") val itemsPerPage: Int?, - @JsonProperty("query") val query: String? = null, - @JsonProperty("searchCategoryId") val searchCategoryId: Int? = null, - @JsonProperty("searchCategoryName") val searchCategoryName: String? = null, - @JsonProperty("item") val item: List? = null -) - -data class AladinBookDetailResponse( - @JsonProperty("version") val version: String?, - @JsonProperty("title") val title: String?, - @JsonProperty("link") val link: String?, - @JsonProperty("pubDate") val pubDate: String?, - @JsonProperty("totalResults") val totalResults: Int?, - @JsonProperty("startIndex") val startIndex: Int?, - @JsonProperty("itemsPerPage") val itemsPerPage: Int?, - @JsonProperty("query") val query: String? = null, - @JsonProperty("searchCategoryId") val searchCategoryId: Int? = null, - @JsonProperty("searchCategoryName") val searchCategoryName: String? = null, - @JsonProperty("item") val item: List? = null -) From 9e4a8b62f84c20df2d1350f73771beae75f6b2e4 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:34:55 +0900 Subject: [PATCH 24/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookU?= =?UTF-8?q?seCase=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=B1=85=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EB=B3=91=ED=95=A9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/usecase/BookUseCase.kt | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) 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 0f966c0f..7d25c1ad 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 @@ -36,18 +36,9 @@ class BookUseCase( userAuthService.validateUserExists(userId) val searchResponse = bookQueryService.searchBooks(request) - val isbns = searchResponse.books.map { it.isbn } + val booksWithUserStatus = mergeWithUserBookStatus(searchResponse.books, userId) - val userBooksReponse = userBookService.findAllByUserIdAndBookIsbnIn(UserBooksByIsbnsRequest.of(userId, isbns)) - val statusMap = userBooksReponse.associateBy({ it.bookIsbn }, { it.status }) - - val updatedBooks = searchResponse.books.map { bookSummary -> - statusMap[bookSummary.isbn]?.let { status -> - bookSummary.updateStatus(status) - } ?: bookSummary - } - - return searchResponse.from(updatedBooks) + return searchResponse.withUpdatedBooks(booksWithUserStatus) } fun getBookDetail( @@ -88,4 +79,39 @@ class BookUseCase( return userBookService.findUserBooksByDynamicConditionWithStatusCounts(userId, status, sort, title, pageable) } + + private fun mergeWithUserBookStatus( + searchedBooks: List, + userId: UUID + ): List { + if (searchedBooks.isEmpty()) { + return emptyList() + } + + val isbn13List = searchedBooks.map { it.isbn13 } + val userBookStatusMap = getUserBookStatusMap(isbn13List, userId) + + // 검색 결과에 사용자 책 상태 적용 + return searchedBooks.map { bookSummary -> + val userStatus = userBookStatusMap[bookSummary.isbn13] + if (userStatus != null) { + bookSummary.updateStatus(userStatus) + } else { + bookSummary + } + } + } + + private fun getUserBookStatusMap( + isbn13List: List, + userId: UUID + ): Map { + val userBooksResponse = userBookService.findAllByUserIdAndBookIsbnIn( + UserBooksByIsbnsRequest.of(userId, isbn13List) + ) + + return userBooksResponse.associate { userBook -> + userBook.bookIsbn to userBook.status + } + } } From cf02829790de40a48e2374295bce6404c4dacd09 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:42:54 +0900 Subject: [PATCH 25/46] =?UTF-8?q?[BOOK-207]=20fix:=20infra=20-=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=9C=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/external/aladin/request/AladinBookLookupRequest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 1dab61a5..8e5d277d 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -3,14 +3,12 @@ package org.yapp.infra.external.aladin.request data class AladinBookLookupRequest private constructor( val itemId: String, val itemIdType: String = "ISBN13", - val output: String?, val cover: String? ) { fun toMap(): Map { val params = mutableMapOf() params["ItemId"] = itemId params["ItemIdType"] = itemIdType - output?.let { params["Output"] = it } cover?.let { params["Cover"] = it } return params } @@ -19,7 +17,7 @@ data class AladinBookLookupRequest private constructor( fun from( itemId: String, ): AladinBookLookupRequest { - return AladinBookLookupRequest(itemId, "ISBN13", "JS", "Big") + return AladinBookLookupRequest(itemId, "ISBN13", "Big") } } } From 0c1d6c7de7610cf2c150119610749288345036cf Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:43:55 +0900 Subject: [PATCH 26/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20AuthorExt?= =?UTF-8?q?ractor=20=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?BookDetailResponse=EC=97=90=EC=84=9C=20=EC=A0=80=EC=9E=90=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/response/BookDetailResponse.kt | 3 ++- .../org/yapp/apis/util/AuthorExtractor.kt | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 32323c5f..7f62bf1d 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -1,5 +1,6 @@ package org.yapp.apis.book.dto.response +import org.yapp.apis.util.AuthorExtractor import org.yapp.globalutils.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinBookDetailResponse @@ -26,7 +27,7 @@ data class BookDetailResponse private constructor( version = response.version, title = item.title, link = item.link, - author = item.author ?: "", + author = AuthorExtractor.extractAuthors(item.author), pubDate = item.pubDate ?: "", description = item.description ?: "", mallType = item.mallType, diff --git a/apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt b/apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt new file mode 100644 index 00000000..85819fa5 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt @@ -0,0 +1,21 @@ +package org.yapp.apis.util + +object AuthorExtractor { + private const val AUTHOR_MARKER = "(지은이)" + private const val CLOSING_PAREN = ")" + private const val DELIMITER = "), " + + fun extractAuthors(authorString: String?): String { + if (authorString.isNullOrBlank() || !authorString.contains(AUTHOR_MARKER)) { + return "" + } + + var authorsPart = authorString.substringBefore(" $AUTHOR_MARKER") + + if (authorsPart.contains(CLOSING_PAREN)) { + authorsPart = authorsPart.substringAfterLast(DELIMITER) + } + + return authorsPart.trim() + } +} From a84de0f99ba7013461be7fc003c68ee10dae9d0f Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:44:15 +0900 Subject: [PATCH 27/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20NicknameG?= =?UTF-8?q?enerator=20=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EB=9E=9C=EB=8D=A4=ED=95=9C=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/util/{generateNickname.kt => NicknameGenerator.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apis/src/main/kotlin/org/yapp/apis/util/{generateNickname.kt => NicknameGenerator.kt} (100%) diff --git a/apis/src/main/kotlin/org/yapp/apis/util/generateNickname.kt b/apis/src/main/kotlin/org/yapp/apis/util/NicknameGenerator.kt similarity index 100% rename from apis/src/main/kotlin/org/yapp/apis/util/generateNickname.kt rename to apis/src/main/kotlin/org/yapp/apis/util/NicknameGenerator.kt From 29a386b5b48877005f85152648b8f0396342fa0e Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:49:02 +0900 Subject: [PATCH 28/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis,=20global-u?= =?UTF-8?q?tils=20-=20IsbnConverter=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC?= =?UTF-8?q?=20apis=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=98=EA=B3=A0=20=EA=B4=80=EB=A0=A8=EB=90=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/response/BookDetailResponse.kt | 2 +- .../book/dto/response/BookSearchResponse.kt | 2 +- .../org/yapp/apis/util/IsbnConverter.kt | 33 +++++++++++++++++++ .../yapp/globalutils/util/IsbnConverter.kt | 25 -------------- .../org/yapp/globalutils/util/RegexUtils.kt | 1 + 5 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt delete mode 100644 global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 7f62bf1d..56f8b043 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -1,7 +1,7 @@ package org.yapp.apis.book.dto.response import org.yapp.apis.util.AuthorExtractor -import org.yapp.globalutils.util.IsbnConverter +import org.yapp.apis.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinBookDetailResponse data class BookDetailResponse private constructor( diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt index ba1d2831..08f9194b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt @@ -1,7 +1,7 @@ package org.yapp.apis.book.dto.response import org.yapp.domain.userbook.BookStatus -import org.yapp.globalutils.util.IsbnConverter +import org.yapp.apis.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinSearchResponse data class BookSearchResponse private constructor( diff --git a/apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt b/apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt new file mode 100644 index 00000000..caa5ba26 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt @@ -0,0 +1,33 @@ +package org.yapp.apis.util + +import org.yapp.globalutils.util.RegexUtils + +object IsbnConverter { + private const val ISBN10_LENGTH = 10 + private const val ISBN13_PREFIX = "978" + private const val ISBN13_MODULUS = 10 + private const val WEIGHT_EVEN = 3 + private const val WEIGHT_ODD = 1 + + fun toIsbn13(isbn10: String?): String? { + val sanitized = isbn10?.replace("-", "")?.uppercase() + + if (sanitized.isNullOrBlank() || sanitized.length != ISBN10_LENGTH) { + return null + } + if (!sanitized.matches(Regex(RegexUtils.ISBN10_PATTERN))) { + return null + } + + val stem = ISBN13_PREFIX + sanitized.substring(0, ISBN10_LENGTH - 1) + + val sum = stem.mapIndexed { index, c -> + val digit = c.digitToInt() + if ((index + 1) % 2 == 0) digit * WEIGHT_EVEN else digit * WEIGHT_ODD + }.sum() + + val checkDigit = (ISBN13_MODULUS - (sum % ISBN13_MODULUS)) % ISBN13_MODULUS + + return stem + checkDigit + } +} diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt deleted file mode 100644 index f59e18fd..00000000 --- a/global-utils/src/main/kotlin/org/yapp/globalutils/util/IsbnConverter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.yapp.globalutils.util - -object IsbnConverter { - fun toIsbn13(isbn10: String?): String? { - val sanitized = isbn10?.replace("-", "")?.uppercase() - - if (sanitized.isNullOrBlank() || sanitized.length != 10) { - return null - } - if (!sanitized.matches(Regex("^[0-9]{9}[0-9X]$"))) { - return null - } - - val stem = "978" + sanitized.substring(0, 9) - - val sum = stem.mapIndexed { index, c -> - val digit = c.digitToInt() - if ((index + 1) % 2 == 0) digit * 3 else digit - }.sum() - - val checkDigit = (10 - (sum % 10)) % 10 - - return stem + checkDigit - } -} diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt index f8d123d8..ff1810f8 100644 --- a/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt @@ -1,5 +1,6 @@ package org.yapp.globalutils.util object RegexUtils { + const val ISBN10_PATTERN = "^[0-9]{9}[0-9X]$" const val ISBN13_PATTERN = "^(978|979)\\d{10}$" } From e38eaac320b71045cc5910416add1a26a710e8d3 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 02:56:50 +0900 Subject: [PATCH 29/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20=EC=B1=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=9A=94=EC=B2=AD=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20Swagger=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=B0=8F=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/request/BookCreateRequest.kt | 56 +++++++++++++ .../book/dto/request/BookDetailRequest.kt | 14 +++- .../book/dto/request/BookSearchRequest.kt | 57 +++++++++++++ .../book/dto/request/UpsertUserBookRequest.kt | 79 ++++++++++++++++++- .../dto/request/UserBookRegisterRequest.kt | 22 +++++- 5 files changed, 225 insertions(+), 3 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt index 5e0185d4..6e08d0ef 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt @@ -1,27 +1,83 @@ package org.yapp.apis.book.dto.request +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Max +import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Size import org.yapp.apis.book.dto.response.BookDetailResponse +@Schema( + title = "책 생성 요청", + description = "시스템에 새로운 책 정보를 생성하는 요청 (주로 내부 API에서 사용)" +) data class BookCreateRequest private constructor( @field:NotBlank(message = "ISBN은 필수입니다.") + @Schema( + description = "책의 13자리 ISBN 코드", + example = "9788932473901", + required = true, + minLength = 13, + maxLength = 13 + ) val isbn: String? = null, @field:NotBlank(message = "제목은 필수입니다.") + @field:Size(max = 500, message = "제목은 500자 이내여야 합니다.") + @Schema( + description = "책 제목", + example = "해리 포터와 마법사의 돌", + required = true, + maxLength = 500 + ) val title: String? = null, @field:NotBlank(message = "저자는 필수입니다.") + @field:Size(max = 200, message = "저자는 200자 이내여야 합니다.") + @Schema( + description = "저자명 (여러 저자인 경우 쉼표로 구분)", + example = "J.K. 롤링", + required = true, + maxLength = 200 + ) val author: String? = null, @field:NotBlank(message = "출판사는 필수입니다.") + @field:Size(max = 200, message = "출판사는 200자 이내여야 합니다.") + @Schema( + description = "출판사명", + example = "문학수첩", + required = true, + maxLength = 200 + ) val publisher: String? = null, + @field:Min(value = 1000, message = "출간연도는 1000년 이후여야 합니다.") + @field:Max(value = 2100, message = "출간연도는 2100년 이전이어야 합니다.") + @Schema( + description = "출간연도 (4자리 년도)", + example = "2000", + minimum = "1000", + maximum = "2100" + ) val publicationYear: Int? = null, @field:Size(max = 2048, message = "표지 URL은 2048자 이내여야 합니다.") + @Schema( + description = "책 표지 이미지 URL", + example = "https://image.aladin.co.kr/product/123/45/cover/1234567890123.jpg", + required = true, + maxLength = 2048, + format = "uri" + ) val coverImageUrl: String, + @field:Size(max = 2000, message = "책 설명은 2000자 이내여야 합니다.") + @Schema( + description = "책 소개 및 줄거리", + example = "11살 해리 포터는 이모네 집에서 갖은 구박을 당하며 지낸다...", + maxLength = 2000 + ) val description: String? = null ) { fun validIsbn(): String = isbn!! diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt index caff905c..d8bb18fa 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt @@ -1,16 +1,28 @@ package org.yapp.apis.book.dto.request +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Pattern import org.yapp.globalutils.util.RegexUtils - +@Schema( + title = "책 상세 정보 요청", + description = "특정 ISBN을 통한 책 상세 정보 조회 요청" +) data class BookDetailRequest private constructor( @field:NotBlank(message = "ISBN은 비어 있을 수 없습니다.") @field:Pattern( regexp = RegexUtils.ISBN13_PATTERN, message = "유효한 13자리 ISBN 형식이 아닙니다." ) + @Schema( + description = "조회할 책의 13자리 ISBN 코드", + example = "9788932473901", + required = true, + pattern = "\\d{13}", + minLength = 13, + maxLength = 13 + ) val isbn: String? = null, ) { fun validIsbn(): String = isbn!! diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt index 8044fc6b..c6e20006 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt @@ -1,15 +1,72 @@ package org.yapp.apis.book.dto.request +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Max +import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotBlank +@Schema( + title = "책 검색 요청", + description = "알라딘 API를 통한 책 검색 요청 정보" +) data class BookSearchRequest private constructor( @field:NotBlank(message = "검색어는 필수입니다.") + @Schema( + description = "검색할 키워드 (제목, 저자, 출판사 등)", + example = "해리포터", + required = true + ) val query: String? = null, + + @Schema( + description = "검색 유형", + example = "Title", + allowableValues = ["Title", "Author", "Publisher", "Keyword"], + defaultValue = "Keyword" + ) val queryType: String? = null, + + @Schema( + description = "검색 대상", + example = "Book", + allowableValues = ["Book", "Foreign", "Music", "DVD"], + defaultValue = "Book" + ) val searchTarget: String? = null, + + @field:Min(value = 1, message = "최대 결과 수는 1 이상이어야 합니다.") + @field:Max(value = 100, message = "최대 결과 수는 100 이하여야 합니다.") + @Schema( + description = "한 번에 가져올 최대 결과 수 (1-100)", + example = "10", + minimum = "1", + maximum = "100", + defaultValue = "10" + ) val maxResults: Int? = null, + + @field:Min(value = 1, message = "시작 인덱스는 1 이상이어야 합니다.") + @Schema( + description = "검색 시작 인덱스 (페이징)", + example = "1", + minimum = "1", + defaultValue = "1" + ) val start: Int? = null, + + @Schema( + description = "정렬 방식", + example = "Accuracy", + allowableValues = ["Accuracy", "PublishTime", "Title", "SalesPoint"], + defaultValue = "Accuracy" + ) val sort: String? = null, + + @Schema( + description = "카테고리 ID (0: 전체)", + example = "0", + defaultValue = "0" + ) val categoryId: Int? = null ) { fun validQuery(): String = query!! diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt index cd987615..fd466fa5 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt @@ -1,18 +1,95 @@ package org.yapp.apis.book.dto.request +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size import org.yapp.apis.book.dto.response.BookCreateResponse import org.yapp.domain.userbook.BookStatus import java.util.UUID - +@Schema( + title = "사용자 도서 생성/수정 요청", + description = "사용자 서재에 도서를 생성하거나 기존 도서 정보를 수정하는 내부 API 요청 (주로 내부 서비스에서 사용)" +) data class UpsertUserBookRequest private constructor( + @field:NotNull(message = "사용자 ID는 필수입니다.") + @Schema( + description = "사용자 고유 식별자", + example = "550e8400-e29b-41d4-a716-446655440000", + required = true, + format = "uuid" + ) val userId: UUID? = null, + + @field:NotNull(message = "책 ID는 필수입니다.") + @Schema( + description = "책 고유 식별자", + example = "550e8400-e29b-41d4-a716-446655440001", + required = true, + format = "uuid" + ) val bookId: UUID? = null, + + @field:NotBlank(message = "책 ISBN은 필수입니다.") + @Schema( + description = "책의 13자리 ISBN 코드", + example = "9788932473901", + required = true, + minLength = 13, + maxLength = 13 + ) val bookIsbn: String? = null, + + @field:NotBlank(message = "책 제목은 필수입니다.") + @field:Size(max = 500, message = "책 제목은 500자 이내여야 합니다.") + @Schema( + description = "책 제목", + example = "해리 포터와 마법사의 돌", + required = true, + maxLength = 500 + ) val bookTitle: String? = null, + + @field:NotBlank(message = "저자는 필수입니다.") + @field:Size(max = 200, message = "저자는 200자 이내여야 합니다.") + @Schema( + description = "저자명", + example = "J.K. 롤링", + required = true, + maxLength = 200 + ) val bookAuthor: String? = null, + + @field:NotBlank(message = "출판사는 필수입니다.") + @field:Size(max = 200, message = "출판사는 200자 이내여야 합니다.") + @Schema( + description = "출판사명", + example = "문학수첩", + required = true, + maxLength = 200 + ) val bookPublisher: String? = null, + + @field:NotBlank(message = "표지 이미지 URL은 필수입니다.") + @field:Size(max = 2048, message = "표지 이미지 URL은 2048자 이내여야 합니다.") + @Schema( + description = "책 표지 이미지 URL", + example = "https://image.aladin.co.kr/product/123/45/cover/1234567890123.jpg", + required = true, + maxLength = 2048, + format = "uri" + ) val bookCoverImageUrl: String? = null, + + @field:NotNull(message = "도서 상태는 필수입니다.") + @Schema( + description = "사용자의 도서 읽기 상태", + example = "READING", + required = true, + allowableValues = ["BEFORE_REGISTRATION", "BEFORE_READING", "READING", "COMPLETED"], + enumAsRef = true + ) val status: BookStatus? = null ) { fun validUserId(): UUID = userId!! diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt index be008096..a41422c6 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt @@ -1,13 +1,33 @@ package org.yapp.apis.book.dto.request +import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull import org.yapp.domain.userbook.BookStatus - +@Schema( + title = "사용자 도서 등록 요청", + description = "사용자의 서재에 도서를 등록하거나 상태를 변경하는 요청" +) data class UserBookRegisterRequest private constructor( @field:NotBlank(message = "ISBN은 필수입니다.") + @Schema( + description = "등록할 책의 13자리 ISBN 코드", + example = "9788932473901", + required = true, + minLength = 13, + maxLength = 13 + ) val bookIsbn: String? = null, + @field:NotNull(message = "도서 상태는 필수입니다.") + @Schema( + description = "사용자의 도서 읽기 상태", + example = "READING", + required = true, + allowableValues = ["BEFORE_REGISTRATION", "BEFORE_READING", "READING", "COMPLETED"], + enumAsRef = true + ) val bookStatus: BookStatus ) { fun validBookIsbn(): String = bookIsbn!! From c3d3765583a734c7acaadcb20cbb51ecf1806b96 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 03:22:24 +0900 Subject: [PATCH 30/46] =?UTF-8?q?[BOOK-207]=20feat:=20apis=20-=20BookDetai?= =?UTF-8?q?lResponse=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B1=85=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20UserBookS?= =?UTF-8?q?ervice=EC=97=90=20=EC=83=81=ED=83=9C=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/book/dto/response/BookDetailResponse.kt | 13 ++++++++++--- .../org/yapp/apis/book/service/UserBookService.kt | 5 +++++ .../org/yapp/apis/book/usecase/BookUseCase.kt | 9 +++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 56f8b043..15897e67 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -2,6 +2,7 @@ package org.yapp.apis.book.dto.response import org.yapp.apis.util.AuthorExtractor import org.yapp.apis.util.IsbnConverter +import org.yapp.domain.userbook.BookStatus import org.yapp.infra.external.aladin.response.AladinBookDetailResponse data class BookDetailResponse private constructor( @@ -16,10 +17,15 @@ data class BookDetailResponse private constructor( val cover: String, val categoryName: String, val publisher: String?, - val totalPage: Int? + val totalPage: Int?, + val userBookStatus: BookStatus ) { + fun withUserBookStatus(newUserBookStatus: BookStatus): BookDetailResponse { + return this.copy(userBookStatus = newUserBookStatus) + } + companion object { - fun from(response: AladinBookDetailResponse): BookDetailResponse { + fun from(response: AladinBookDetailResponse, userBookStatus: BookStatus = BookStatus.BEFORE_REGISTRATION): BookDetailResponse { val item = response.item.firstOrNull() ?: throw IllegalArgumentException("No book item found in detail response.") @@ -35,7 +41,8 @@ data class BookDetailResponse private constructor( cover = item.cover, categoryName = item.categoryName, publisher = item.publisher ?: "", - totalPage = item.subInfo.itemPage ?: 4032 + totalPage = item.subInfo.itemPage ?: 4032, + userBookStatus = userBookStatus ) } } 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 93ced786..8138eb49 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 @@ -49,6 +49,11 @@ class UserBookService( return userBooks.map { UserBookResponse.from(it) } } + fun findUserBookStatusByIsbn(userId: UUID, isbn: String): BookStatus { + val userBook = userBookDomainService.findByUserIdAndBookIsbn(userId, isbn) + return userBook.status + } + private fun findUserBooksByDynamicCondition( userId: UUID, status: BookStatus?, 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 7d25c1ad..8f74e9a9 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 @@ -46,7 +46,13 @@ class BookUseCase( userId: UUID ): BookDetailResponse { userAuthService.validateUserExists(userId) - return bookQueryService.getBookDetail(bookDetailRequest) + + val bookDetailResponse = bookQueryService.getBookDetail(bookDetailRequest) + val isbn13 = bookDetailResponse.isbn13 + ?: return bookDetailResponse.withUserBookStatus(BookStatus.BEFORE_REGISTRATION) + val userBookStatus = userBookService.findUserBookStatusByIsbn(userId, isbn13) + + return bookDetailResponse.withUserBookStatus(userBookStatus) } @Transactional @@ -91,7 +97,6 @@ class BookUseCase( val isbn13List = searchedBooks.map { it.isbn13 } val userBookStatusMap = getUserBookStatusMap(isbn13List, userId) - // 검색 결과에 사용자 책 상태 적용 return searchedBooks.map { bookSummary -> val userStatus = userBookStatusMap[bookSummary.isbn13] if (userStatus != null) { From 62eabda612b94fbd5a57c5c73e4302c0fcde424d Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 03:22:50 +0900 Subject: [PATCH 31/46] =?UTF-8?q?[BOOK-207]=20feat:=20domain=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B1=85=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/userbook/UserBookDomainService.kt | 8 ++++++++ .../userbook/exception/UserBookErrorCode.kt | 16 ++++++++++++++++ .../exception/UserBookNotFoundException.kt | 9 +++++++++ 3 files changed, 33 insertions(+) create mode 100644 domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt create mode 100644 domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt 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 02626eea..9a06d22f 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -2,6 +2,8 @@ package org.yapp.domain.userbook import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable +import org.yapp.domain.userbook.exception.UserBookErrorCode +import org.yapp.domain.userbook.exception.UserBookNotFoundException import org.yapp.domain.userbook.vo.HomeBookVO import org.yapp.domain.userbook.vo.UserBookInfoVO import org.yapp.domain.userbook.vo.UserBookStatusCountsVO @@ -57,6 +59,12 @@ class UserBookDomainService( return userBooks.map { UserBookInfoVO.newInstance(it) } } + fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBookInfoVO { + val userBook = userBookRepository.findByUserIdAndBookIsbn(userId, isbn) + ?: throw UserBookNotFoundException(UserBookErrorCode.USER_BOOK_NOT_FOUND) + return UserBookInfoVO.newInstance(userBook) + } + fun getUserBookStatusCounts(userId: UUID): UserBookStatusCountsVO { val statusCounts = BookStatus.entries.associateWith { status -> countUserBooksByStatus(userId, status) diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt new file mode 100644 index 00000000..bfadebdd --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt @@ -0,0 +1,16 @@ +package org.yapp.domain.userbook.exception + +import org.springframework.http.HttpStatus +import org.yapp.globalutils.exception.BaseErrorCode + +enum class UserBookErrorCode( + private val status: HttpStatus, + private val code: String, + private val message: String +) : BaseErrorCode { + USER_BOOK_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_BOOK_001", "유저의 책 정보를 찾을 수 없습니다."); + + override fun getHttpStatus(): HttpStatus = status + override fun getCode(): String = code + override fun getMessage(): String = message +} diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt new file mode 100644 index 00000000..e83c0360 --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt @@ -0,0 +1,9 @@ +package org.yapp.domain.userbook.exception + +import org.yapp.domain.user.exception.UserErrorCode +import org.yapp.globalutils.exception.CommonException + +class UserBookNotFoundException ( + errorCode: UserBookErrorCode, + message: String? = null +) : CommonException(errorCode, message) From 75fd2644f103869e789113d4eac2fed108e6910b Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 03:28:48 +0900 Subject: [PATCH 32/46] =?UTF-8?q?[BOOK-207]=20fix:=20apis,=20domain=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B1=85=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20nullable?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EA=B3=A0=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/yapp/apis/book/service/UserBookService.kt | 4 ++-- .../kotlin/org/yapp/apis/book/usecase/BookUseCase.kt | 4 +++- .../org/yapp/domain/userbook/UserBookDomainService.kt | 9 +++------ 3 files changed, 8 insertions(+), 9 deletions(-) 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 8138eb49..06947892 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 @@ -49,9 +49,9 @@ class UserBookService( return userBooks.map { UserBookResponse.from(it) } } - fun findUserBookStatusByIsbn(userId: UUID, isbn: String): BookStatus { + fun findUserBookStatusByIsbn(userId: UUID, isbn: String): BookStatus? { val userBook = userBookDomainService.findByUserIdAndBookIsbn(userId, isbn) - return userBook.status + return userBook?.status } private fun findUserBooksByDynamicCondition( 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 8f74e9a9..500c84e9 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 @@ -50,7 +50,9 @@ class BookUseCase( val bookDetailResponse = bookQueryService.getBookDetail(bookDetailRequest) val isbn13 = bookDetailResponse.isbn13 ?: return bookDetailResponse.withUserBookStatus(BookStatus.BEFORE_REGISTRATION) - val userBookStatus = userBookService.findUserBookStatusByIsbn(userId, isbn13) + + val userBookStatus = userBookService.findUserBookStatusByIsbn(userId, isbn13) + ?: BookStatus.BEFORE_REGISTRATION return bookDetailResponse.withUserBookStatus(userBookStatus) } 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 9a06d22f..445ec5f4 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -2,8 +2,6 @@ package org.yapp.domain.userbook import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable -import org.yapp.domain.userbook.exception.UserBookErrorCode -import org.yapp.domain.userbook.exception.UserBookNotFoundException import org.yapp.domain.userbook.vo.HomeBookVO import org.yapp.domain.userbook.vo.UserBookInfoVO import org.yapp.domain.userbook.vo.UserBookStatusCountsVO @@ -59,10 +57,9 @@ class UserBookDomainService( return userBooks.map { UserBookInfoVO.newInstance(it) } } - fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBookInfoVO { + fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBookInfoVO? { val userBook = userBookRepository.findByUserIdAndBookIsbn(userId, isbn) - ?: throw UserBookNotFoundException(UserBookErrorCode.USER_BOOK_NOT_FOUND) - return UserBookInfoVO.newInstance(userBook) + return userBook?.let { UserBookInfoVO.newInstance(it) } } fun getUserBookStatusCounts(userId: UUID): UserBookStatusCountsVO { @@ -111,4 +108,4 @@ class UserBookDomainService( ) } } -} +} \ No newline at end of file From ea24d9dbf068c0ed0dc924ee97ad81f5d0c24d85 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 03:33:49 +0900 Subject: [PATCH 33/46] =?UTF-8?q?[BOOK-207]=20chore:=20domain=20-=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20import=EB=AC=B8=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/domain/userbook/exception/UserBookNotFoundException.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt index e83c0360..b6680af0 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt @@ -1,6 +1,5 @@ package org.yapp.domain.userbook.exception -import org.yapp.domain.user.exception.UserErrorCode import org.yapp.globalutils.exception.CommonException class UserBookNotFoundException ( From 70fc7ae886ca1658393e63f7129f05f11d990f1d Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:28:21 +0900 Subject: [PATCH 34/46] =?UTF-8?q?[BOOK-207]=20feat:=20global-utils,=20infr?= =?UTF-8?q?a=20-=20=EC=B1=85=20=ED=91=9C=EC=A7=80=20=ED=81=AC=EA=B8=B0=20?= =?UTF-8?q?=EC=97=B4=EA=B1=B0=ED=98=95=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?AladinBookLookupRequest,=20AladinBookSearchRequest=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/yapp/globalutils/book/BookCoverSize.kt | 10 ++++++++++ .../external/aladin/request/AladinBookLookupRequest.kt | 4 +++- .../external/aladin/request/AladinBookSearchRequest.kt | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt new file mode 100644 index 00000000..0cbbc2f6 --- /dev/null +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt @@ -0,0 +1,10 @@ +package org.yapp.globalutils.book + +enum class BookCoverSize(val apiValue: String) { + BIG("Big"), + MID_BIG("MidBig"), + MID("Mid"), + SMALL("Small"), + MINI("Mini"), + NONE("None") +} diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 8e5d277d..52df11a3 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -1,5 +1,7 @@ package org.yapp.infra.external.aladin.request +import org.yapp.globalutils.book.BookCoverSize + data class AladinBookLookupRequest private constructor( val itemId: String, val itemIdType: String = "ISBN13", @@ -17,7 +19,7 @@ data class AladinBookLookupRequest private constructor( fun from( itemId: String, ): AladinBookLookupRequest { - return AladinBookLookupRequest(itemId, "ISBN13", "Big") + return AladinBookLookupRequest(itemId, "ISBN13", BookCoverSize.BIG.apiValue) } } } diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt index 863d60b3..bbd5d70f 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt @@ -1,5 +1,7 @@ package org.yapp.infra.external.aladin.request +import org.yapp.globalutils.book.BookCoverSize + data class AladinBookSearchRequest private constructor( val query: String, val queryType: String?, @@ -40,7 +42,7 @@ data class AladinBookSearchRequest private constructor( maxResults = maxResults, start = start, sort = sort, - cover = "Big", + cover = BookCoverSize.BIG.apiValue, categoryId = categoryId, ) } From 10ba6bd7305386eb9e8253689036ca628a8c5508 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:28:46 +0900 Subject: [PATCH 35/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookC?= =?UTF-8?q?reateRequest=20=EB=B0=8F=20BookDetailResponse=EC=97=90=EC=84=9C?= =?UTF-8?q?=20cover=20=ED=95=84=EB=93=9C=20=EC=9D=B4=EB=A6=84=EC=9D=84=20c?= =?UTF-8?q?overImageUrl=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/request/BookCreateRequest.kt | 2 +- .../org/yapp/apis/book/dto/response/BookDetailResponse.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt index 6e08d0ef..0a0008ff 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt @@ -97,7 +97,7 @@ data class BookCreateRequest private constructor( author = bookDetail.author, publisher = bookDetail.publisher, publicationYear = parsePublicationYear(bookDetail.pubDate), - coverImageUrl = bookDetail.cover, + coverImageUrl = bookDetail.coverImageUrl, description = bookDetail.description, ) } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 15897e67..4f140027 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -14,7 +14,7 @@ data class BookDetailResponse private constructor( val description: String, val isbn13: String?, val mallType: String, - val cover: String, + val coverImageUrl: String, val categoryName: String, val publisher: String?, val totalPage: Int?, @@ -38,7 +38,7 @@ data class BookDetailResponse private constructor( description = item.description ?: "", mallType = item.mallType, isbn13 = item.isbn13 ?: IsbnConverter.toIsbn13(item.isbn) ?: throw IllegalArgumentException("Either isbn13 or isbn must be provided"), - cover = item.cover, + coverImageUrl = item.cover, categoryName = item.categoryName, publisher = item.publisher ?: "", totalPage = item.subInfo.itemPage ?: 4032, From 414d19395f97e3ed94efd262c4303e1f03fa3e9d Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:29:13 +0900 Subject: [PATCH 36/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20UserB?= =?UTF-8?q?ookRegisterRequest=EC=97=90=EC=84=9C=20bookStatus=EB=A5=BC=20nu?= =?UTF-8?q?llable=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apis/book/dto/request/UserBookRegisterRequest.kt | 9 ++------- .../kotlin/org/yapp/apis/book/usecase/BookUseCase.kt | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt index a41422c6..ef140ed1 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt @@ -28,13 +28,8 @@ data class UserBookRegisterRequest private constructor( allowableValues = ["BEFORE_REGISTRATION", "BEFORE_READING", "READING", "COMPLETED"], enumAsRef = true ) - val bookStatus: BookStatus + val bookStatus: BookStatus? = null ) { fun validBookIsbn(): String = bookIsbn!! - - companion object { - fun create(bookIsbn: String, bookStatus: BookStatus): UserBookRegisterRequest { - return UserBookRegisterRequest(bookIsbn, bookStatus) - } - } + fun validBookStatus(): BookStatus = bookStatus!! } 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 500c84e9..7569f2bc 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 @@ -69,7 +69,7 @@ class BookUseCase( val upsertUserBookRequest = UpsertUserBookRequest.of( userId = userId, bookCreateResponse, - status = request.bookStatus, + status = request.validBookStatus(), ) val userBookResponse = userBookService.upsertUserBook(upsertUserBookRequest) From 91b35e8d606154fc39e0fd0740476c2b6c5d6829 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:29:25 +0900 Subject: [PATCH 37/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookS?= =?UTF-8?q?earchResponse=EC=97=90=EC=84=9C=20=EC=A0=80=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A5=BC=20AuthorExtractor=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EC=B6=94=EC=B6=9C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/response/BookSearchResponse.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt index 08f9194b..7dbfd7f9 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt @@ -1,5 +1,6 @@ package org.yapp.apis.book.dto.response +import org.yapp.apis.util.AuthorExtractor import org.yapp.domain.userbook.BookStatus import org.yapp.apis.util.IsbnConverter import org.yapp.infra.external.aladin.response.AladinSearchResponse @@ -39,7 +40,7 @@ data class BookSearchResponse private constructor( isbn = it.isbn, isbn13 = it.isbn13, title = it.title, - author = it.author, + author = AuthorExtractor.extractAuthors(it.author), publisher = it.publisher, coverImageUrl = it.cover ) From a99a698d197cb8999094b8b538fb00c8ca971546 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:52:11 +0900 Subject: [PATCH 38/46] =?UTF-8?q?[BOOK-207]=20refactor:=20domain,=20apis,?= =?UTF-8?q?=20infra=20-=20ReadingRecordService=EC=97=90=EC=84=9C=20UserBoo?= =?UTF-8?q?kService=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReadingRecordService.kt | 14 -------------- .../usecase/ReadingRecordUseCase.kt | 1 - .../readingrecord/ReadingRecordDomainService.kt | 10 ++++++++-- .../exception/ReadingRecordErrorCode.kt | 16 ++++++++++++++++ .../exception/ReadingRecordNotFoundException.kt | 2 +- .../impl/ReadingRecordRepositoryImpl.kt | 3 +-- 6 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt rename {apis/src/main/kotlin/org/yapp/apis => domain/src/main/kotlin/org/yapp/domain}/readingrecord/exception/ReadingRecordNotFoundException.kt (80%) 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 f015fd54..69389bae 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 @@ -3,12 +3,8 @@ package org.yapp.apis.readingrecord.service import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.yapp.apis.book.service.UserBookService import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse -import org.yapp.apis.readingrecord.exception.ReadingRecordErrorCode -import org.yapp.apis.readingrecord.exception.ReadingRecordNotFoundException -import org.yapp.domain.book.BookDomainService import org.yapp.domain.readingrecord.ReadingRecordDomainService import org.yapp.domain.readingrecord.ReadingRecordSortType import java.util.* @@ -17,7 +13,6 @@ import java.util.* @Service class ReadingRecordService( private val readingRecordDomainService: ReadingRecordDomainService, - private val userBookService: UserBookService, ) { fun createReadingRecord( @@ -25,8 +20,6 @@ class ReadingRecordService( userBookId: UUID, request: CreateReadingRecordRequest ): ReadingRecordResponse { - userBookService.validateUserBookExists(userId, userBookId) - val readingRecordInfoVO = readingRecordDomainService.createReadingRecord( userBookId = userBookId, pageNumber = request.validPageNumber(), @@ -43,13 +36,6 @@ class ReadingRecordService( readingRecordId: UUID ): ReadingRecordResponse { val readingRecordInfoVO = readingRecordDomainService.findReadingRecordById(readingRecordId) - ?: throw ReadingRecordNotFoundException( - ReadingRecordErrorCode.READING_RECORD_NOT_FOUND, - "Reading record not found with id: $readingRecordId" - ) - - userBookService.validateUserBookExists(userId, readingRecordInfoVO.userBookId.value) - return ReadingRecordResponse.from(readingRecordInfoVO) } 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 99271b5b..1dc09059 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 @@ -54,7 +54,6 @@ class ReadingRecordUseCase( pageable: Pageable ): Page { userAuthService.validateUserExists(userId) - userBookService.validateUserBookExists(userId, userBookId) return readingRecordService.getReadingRecordsByDynamicCondition( 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 fedac437..bdf772a8 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt @@ -2,6 +2,8 @@ package org.yapp.domain.readingrecord import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable +import org.yapp.domain.readingrecord.exception.ReadingRecordErrorCode +import org.yapp.domain.readingrecord.exception.ReadingRecordNotFoundException import org.yapp.domain.readingrecord.vo.ReadingRecordInfoVO import org.yapp.domain.readingrecordtag.ReadingRecordTag import org.yapp.domain.readingrecordtag.ReadingRecordTagRepository @@ -59,8 +61,12 @@ class ReadingRecordDomainService( ) } - fun findReadingRecordById(readingRecordId: UUID): ReadingRecordInfoVO? { - val readingRecord = readingRecordRepository.findById(readingRecordId) ?: return null + fun findReadingRecordById(readingRecordId: UUID): ReadingRecordInfoVO { + val readingRecord = readingRecordRepository.findById(readingRecordId) + ?: throw ReadingRecordNotFoundException( + ReadingRecordErrorCode.READING_RECORD_NOT_FOUND, + "Reading record not found with id: $readingRecordId" + ) return buildReadingRecordInfoVO(readingRecord) } diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt new file mode 100644 index 00000000..9ec94af4 --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt @@ -0,0 +1,16 @@ +package org.yapp.domain.readingrecord.exception + +import org.springframework.http.HttpStatus +import org.yapp.globalutils.exception.BaseErrorCode + +enum class ReadingRecordErrorCode( + private val status: HttpStatus, + private val code: String, + private val message: String +) : BaseErrorCode { + READING_RECORD_NOT_FOUND(HttpStatus.NOT_FOUND, "READING_RECORD_001", "독서 기록을 찾을 수 없습니다."); + + override fun getHttpStatus(): HttpStatus = status + override fun getCode(): String = code + override fun getMessage(): String = message +} diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/exception/ReadingRecordNotFoundException.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordNotFoundException.kt similarity index 80% rename from apis/src/main/kotlin/org/yapp/apis/readingrecord/exception/ReadingRecordNotFoundException.kt rename to domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordNotFoundException.kt index 91f1f359..fdf1c580 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/exception/ReadingRecordNotFoundException.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordNotFoundException.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.readingrecord.exception +package org.yapp.domain.readingrecord.exception import org.yapp.globalutils.exception.CommonException 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 fe87c118..03663a40 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 @@ -7,10 +7,9 @@ import org.springframework.stereotype.Repository import org.yapp.domain.readingrecord.ReadingRecord import org.yapp.domain.readingrecord.ReadingRecordRepository import org.yapp.domain.readingrecord.ReadingRecordSortType -import org.yapp.domain.userbook.UserBookSortType import org.yapp.infra.readingrecord.entity.ReadingRecordEntity import org.yapp.infra.readingrecord.repository.JpaReadingRecordRepository -import java.util.UUID +import java.util.* @Repository class ReadingRecordRepositoryImpl( From 1f73a871849d1c23ec745d1216ba66a3d25bb5c0 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:52:26 +0900 Subject: [PATCH 39/46] =?UTF-8?q?[BOOK-207]=20chore:=20apis=20-=20CreateRe?= =?UTF-8?q?adingRecordRequest=EC=97=90=20Swagger=20=EC=98=88=EC=A0=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=84=A4=EB=AA=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateReadingRecordRequest.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt index 3d441264..f61835ba 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt @@ -6,9 +6,19 @@ import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Size - -@Schema(description = "독서 기록 생성 요청") +@Schema( + description = "독서 기록 생성 요청", + example = """ + { + "pageNumber": 42, + "quote": "이것은 기억에 남는 문장입니다.", + "review": "이 책은 매우 인상적이었습니다.", + "emotionTags": ["감동적"] + } + """ +) data class CreateReadingRecordRequest private constructor( + @field:Min(1, message = "페이지 번호는 1 이상이어야 합니다.") @field:Max(9999, message = "페이지 번호는 9999 이하여야 합니다.") @Schema(description = "현재 읽은 페이지 번호", example = "42", required = true) From 19cbea0e9d6cb05d586b1da0ad736c37d0f8e895 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 13:58:09 +0900 Subject: [PATCH 40/46] =?UTF-8?q?[BOOK-207]=20fix:=20apis=20-=20ReadingRec?= =?UTF-8?q?ordUseCase=EC=97=90=EC=84=9C=20userBookService=EC=9D=98=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1dc09059..477cfafa 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 @@ -26,7 +26,7 @@ class ReadingRecordUseCase( request: CreateReadingRecordRequest ): ReadingRecordResponse { userAuthService.validateUserExists(userId) - userBookService.validateUserBookExists(userId, userBookId) + userBookService.validateUserBookExists(userBookId, userId) return readingRecordService.createReadingRecord( userId = userId, @@ -54,7 +54,7 @@ class ReadingRecordUseCase( pageable: Pageable ): Page { userAuthService.validateUserExists(userId) - userBookService.validateUserBookExists(userId, userBookId) + userBookService.validateUserBookExists(userBookId, userId) return readingRecordService.getReadingRecordsByDynamicCondition( userBookId = userBookId, From f9acaa99b0a6504e87e173cc1e4652447db01843 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:04:00 +0900 Subject: [PATCH 41/46] =?UTF-8?q?[BOOK-207]=20refactor:=20infra=20-=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=ED=98=B8=EC=B6=9C=20named=20para?= =?UTF-8?q?meter=20=ED=98=95=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/aladin/request/AladinBookLookupRequest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt index 52df11a3..a0575fc2 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt @@ -19,7 +19,11 @@ data class AladinBookLookupRequest private constructor( fun from( itemId: String, ): AladinBookLookupRequest { - return AladinBookLookupRequest(itemId, "ISBN13", BookCoverSize.BIG.apiValue) + return AladinBookLookupRequest( + itemId = itemId, + itemIdType = "ISBN13", + cover = BookCoverSize.BIG.apiValue + ) } } } From 804e4878305d174f198da894817b8ce5a956160e Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:08:04 +0900 Subject: [PATCH 42/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20conve?= =?UTF-8?q?rImageUrl=20validation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/request/BookCreateRequest.kt | 4 +++- .../org/yapp/apis/book/service/BookManagementService.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt index 0a0008ff..bb89d0f0 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt @@ -63,6 +63,7 @@ data class BookCreateRequest private constructor( val publicationYear: Int? = null, @field:Size(max = 2048, message = "표지 URL은 2048자 이내여야 합니다.") + @field:NotBlank(message = "표지 이미지 URL은 필수입니다.") @Schema( description = "책 표지 이미지 URL", example = "https://image.aladin.co.kr/product/123/45/cover/1234567890123.jpg", @@ -70,7 +71,7 @@ data class BookCreateRequest private constructor( maxLength = 2048, format = "uri" ) - val coverImageUrl: String, + val coverImageUrl: String? = null, @field:Size(max = 2000, message = "책 설명은 2000자 이내여야 합니다.") @Schema( @@ -84,6 +85,7 @@ data class BookCreateRequest private constructor( fun validTitle(): String = title!! fun validAuthor(): String = author!! fun validPublisher(): String = publisher!! + fun validCoverImageUrl(): String = coverImageUrl!! companion object { diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt index b7c6df90..19d3a6a1 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt @@ -15,7 +15,7 @@ class BookManagementService( request.validTitle(), request.validAuthor(), request.validPublisher(), - request.coverImageUrl, + request.validCoverImageUrl(), request.publicationYear, request.description ) From 8526a79e24654683f8a1ec137140947119a47a9a Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:11:41 +0900 Subject: [PATCH 43/46] =?UTF-8?q?[BOOK-207]=20fix:=20apis=20-=20emotionTag?= =?UTF-8?q?s=20=ED=95=84=EB=93=9C=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../readingrecord/dto/request/CreateReadingRecordRequest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt index f61835ba..7cb7a658 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt @@ -34,8 +34,8 @@ data class CreateReadingRecordRequest private constructor( @Schema(description = "감상평", example = "이 책은 매우 인상적이었습니다.", required = true) val review: String? = null, - @field:Size(max = 3, message = "감정 태그는 최대 3개까지 가능합니다.") - @Schema(description = "감정 태그 목록 (최대 3개)", example = "[\"감동적\", \"슬픔\", \"희망\"]") + @field:Size(max = 1, message = "감정 태그는 최대 1개까지 가능합니다. (단일 감정만 받지만, 확장성을 위해 리스트 형태로 관리됩니다.)") + @Schema(description = "감정 태그 목록 (현재는 최대 1개, 확장 가능)", example = "[\"감동적\"]") val emotionTags: List<@Size(max = 10, message = "감정 태그는 10자를 초과할 수 없습니다.") String> = emptyList() ) { fun validPageNumber(): Int = pageNumber!! From 1b917e20e350c8d4d74e45d071e27899e073f207 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:14:14 +0900 Subject: [PATCH 44/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20BookD?= =?UTF-8?q?etailResponse=EC=97=90=EC=84=9C=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20totalPage=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/dto/response/BookDetailResponse.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt index 4f140027..14011d0c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt @@ -25,6 +25,8 @@ data class BookDetailResponse private constructor( } companion object { + private const val DEFAULT_MAX_PAGE_COUNT = 4032 + fun from(response: AladinBookDetailResponse, userBookStatus: BookStatus = BookStatus.BEFORE_REGISTRATION): BookDetailResponse { val item = response.item.firstOrNull() ?: throw IllegalArgumentException("No book item found in detail response.") @@ -41,7 +43,7 @@ data class BookDetailResponse private constructor( coverImageUrl = item.cover, categoryName = item.categoryName, publisher = item.publisher ?: "", - totalPage = item.subInfo.itemPage ?: 4032, + totalPage = item.subInfo.itemPage ?: DEFAULT_MAX_PAGE_COUNT, userBookStatus = userBookStatus ) } From 61a7d933d8fca55a7e7df52fc5e599720cd29266 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:22:53 +0900 Subject: [PATCH 45/46] =?UTF-8?q?[BOOK-207]=20refactor:=20apis=20-=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EC=97=90=20@Validated=20=EB=B0=8F=20@Valid=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/org/yapp/apis/auth/service/TokenService.kt | 7 +++++-- .../kotlin/org/yapp/apis/auth/service/UserAuthService.kt | 7 +++++-- .../org/yapp/apis/book/service/AladinBookQueryService.kt | 7 +++++-- .../org/yapp/apis/book/service/BookManagementService.kt | 5 ++++- .../kotlin/org/yapp/apis/book/service/BookQueryService.kt | 5 +++-- .../kotlin/org/yapp/apis/book/service/UserBookService.kt | 7 +++++-- .../apis/readingrecord/service/ReadingRecordService.kt | 5 ++++- 7 files changed, 31 insertions(+), 12 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt index 7249f8a3..b6ef2fe8 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt @@ -1,6 +1,8 @@ package org.yapp.apis.auth.service +import jakarta.validation.Valid import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.auth.dto.request.TokenGenerateRequest import org.yapp.apis.auth.dto.request.TokenRefreshRequest import org.yapp.apis.auth.dto.response.RefreshTokenResponse @@ -9,6 +11,7 @@ import org.yapp.domain.token.TokenDomainRedisService import java.util.* @Service +@Validated class TokenService( private val tokenDomainRedisService: TokenDomainRedisService, ) { @@ -16,7 +19,7 @@ class TokenService( tokenDomainRedisService.deleteRefreshTokenByToken(token) } - fun saveRefreshToken(tokenGenerateRequest: TokenGenerateRequest): RefreshTokenResponse { + fun saveRefreshToken(@Valid tokenGenerateRequest: TokenGenerateRequest): RefreshTokenResponse { val token = tokenDomainRedisService.saveRefreshToken( tokenGenerateRequest.validUserId(), tokenGenerateRequest.validRefreshToken(), @@ -34,7 +37,7 @@ class TokenService( tokenDomainRedisService.validateRefreshTokenByToken(refreshToken) } - fun getUserIdByToken(tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { + fun getUserIdByToken(@Valid tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { val userId = tokenDomainRedisService.getUserIdByToken(tokenRefreshRequest.validRefreshToken()) return UserIdResponse.from(userId) } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt index e4845c27..6e9160bc 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt @@ -1,6 +1,8 @@ package org.yapp.apis.auth.service +import jakarta.validation.Valid import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.auth.dto.request.FindOrCreateUserRequest import org.yapp.apis.auth.dto.request.FindUserIdentityRequest import org.yapp.apis.auth.dto.response.CreateUserResponse @@ -13,6 +15,7 @@ import org.yapp.domain.user.vo.UserIdentityVO import java.util.* @Service +@Validated class UserAuthService( private val userDomainService: UserDomainService ) { @@ -33,12 +36,12 @@ class UserAuthService( } } - fun findUserIdentityByUserId(findUserIdentityRequest: FindUserIdentityRequest): UserAuthInfoResponse { + fun findUserIdentityByUserId(@Valid findUserIdentityRequest: FindUserIdentityRequest): UserAuthInfoResponse { val userIdentity = userDomainService.findUserIdentityById(findUserIdentityRequest.validUserId()) return UserAuthInfoResponse.from(userIdentity) } - fun findOrCreateUser(findOrCreateUserRequest: FindOrCreateUserRequest): CreateUserResponse { + fun findOrCreateUser(@Valid findOrCreateUserRequest: FindOrCreateUserRequest): CreateUserResponse { userDomainService.findUserByProviderTypeAndProviderId( findOrCreateUserRequest.validProviderType(), findOrCreateUserRequest.validProviderId() diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt index 5e06d9fe..8fa97598 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt @@ -1,7 +1,9 @@ package org.yapp.apis.book.service +import jakarta.validation.Valid import mu.KotlinLogging import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.book.dto.request.BookDetailRequest import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse @@ -15,12 +17,13 @@ import org.yapp.infra.external.aladin.response.AladinBookDetailResponse import org.yapp.infra.external.aladin.response.AladinSearchResponse @Service +@Validated class AladinBookQueryService( private val aladinApi: AladinApi ) : BookQueryService { private val log = KotlinLogging.logger {} - override fun searchBooks(request: BookSearchRequest): BookSearchResponse { + override fun searchBooks(@Valid request: BookSearchRequest): BookSearchResponse { log.info("Service - Converting BookSearchRequest to AladinBookSearchRequest and calling Aladin API for book search.") val aladinSearchRequest = AladinBookSearchRequest.of( request.validQuery(), @@ -42,7 +45,7 @@ class AladinBookQueryService( return BookSearchResponse.from(response) } - override fun getBookDetail(request: BookDetailRequest): BookDetailResponse { + override fun getBookDetail(@Valid request: BookDetailRequest): BookDetailResponse { log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.") val aladinLookupRequest = AladinBookLookupRequest.from(request.validIsbn()) val response: AladinBookDetailResponse = aladinApi.lookupBook(aladinLookupRequest) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt index 19d3a6a1..559b118b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt @@ -1,15 +1,18 @@ package org.yapp.apis.book.service +import jakarta.validation.Valid import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.book.dto.request.BookCreateRequest import org.yapp.apis.book.dto.response.BookCreateResponse import org.yapp.domain.book.BookDomainService @Service +@Validated class BookManagementService( private val bookDomainService: BookDomainService ) { - fun findOrCreateBook(request: BookCreateRequest): BookCreateResponse { + fun findOrCreateBook(@Valid request: BookCreateRequest): BookCreateResponse { val bookInfoVO = bookDomainService.findOrCreate( request.validIsbn(), request.validTitle(), diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt index 9f966f88..754974c0 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt @@ -1,11 +1,12 @@ package org.yapp.apis.book.service +import jakarta.validation.Valid import org.yapp.apis.book.dto.request.BookDetailRequest import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse sealed interface BookQueryService { - fun searchBooks(request: BookSearchRequest): BookSearchResponse - fun getBookDetail(request: BookDetailRequest): BookDetailResponse + fun searchBooks(@Valid request: BookSearchRequest): BookSearchResponse + fun getBookDetail(@Valid request: BookDetailRequest): BookDetailResponse } 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 06947892..4a318616 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 @@ -1,8 +1,10 @@ package org.yapp.apis.book.service +import jakarta.validation.Valid import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest import org.yapp.apis.book.dto.request.UpsertUserBookRequest import org.yapp.apis.book.dto.response.UserBookPageResponse @@ -15,10 +17,11 @@ import org.yapp.domain.userbook.UserBookSortType import java.util.* @Service +@Validated class UserBookService( private val userBookDomainService: UserBookDomainService ) { - fun upsertUserBook(upsertUserBookRequest: UpsertUserBookRequest): UserBookResponse { + fun upsertUserBook(@Valid upsertUserBookRequest: UpsertUserBookRequest): UserBookResponse { val userBookInfoVO = userBookDomainService.upsertUserBook( upsertUserBookRequest.validUserId(), upsertUserBookRequest.validBookId(), @@ -41,7 +44,7 @@ class UserBookService( } } - fun findAllByUserIdAndBookIsbnIn(userBooksByIsbnsRequest: UserBooksByIsbnsRequest): List { + fun findAllByUserIdAndBookIsbnIn(@Valid userBooksByIsbnsRequest: UserBooksByIsbnsRequest): List { val userBooks = userBookDomainService.findAllByUserIdAndBookIsbnIn( userBooksByIsbnsRequest.validUserId(), userBooksByIsbnsRequest.validIsbns(), 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 69389bae..bbad35b7 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 @@ -1,8 +1,10 @@ package org.yapp.apis.readingrecord.service +import jakarta.validation.Valid import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse import org.yapp.domain.readingrecord.ReadingRecordDomainService @@ -11,6 +13,7 @@ import java.util.* @Service +@Validated class ReadingRecordService( private val readingRecordDomainService: ReadingRecordDomainService, ) { @@ -18,7 +21,7 @@ class ReadingRecordService( fun createReadingRecord( userId: UUID, userBookId: UUID, - request: CreateReadingRecordRequest + @Valid request: CreateReadingRecordRequest ): ReadingRecordResponse { val readingRecordInfoVO = readingRecordDomainService.createReadingRecord( userBookId = userBookId, From 033d2242e4cea40bc39fcb6dd739574d79169cb9 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Mon, 4 Aug 2025 14:25:19 +0900 Subject: [PATCH 46/46] =?UTF-8?q?[BOOK-207]=20fix:=20apis=20-=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=9D=98=20dto=EB=A5=BC=20=EB=B0=94?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=9B=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EC=9D=98=20=EA=B2=BD=EC=9A=B0=20@Valid=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt | 2 +- .../kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt | 2 +- .../main/kotlin/org/yapp/apis/book/service/BookQueryService.kt | 2 +- .../org/yapp/apis/readingrecord/service/ReadingRecordService.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt index b6ef2fe8..93b49fde 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt @@ -37,7 +37,7 @@ class TokenService( tokenDomainRedisService.validateRefreshTokenByToken(refreshToken) } - fun getUserIdByToken(@Valid tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { + fun getUserIdByToken(tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { val userId = tokenDomainRedisService.getUserIdByToken(tokenRefreshRequest.validRefreshToken()) return UserIdResponse.from(userId) } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt index 8fa97598..02ca2d99 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt @@ -23,7 +23,7 @@ class AladinBookQueryService( ) : BookQueryService { private val log = KotlinLogging.logger {} - override fun searchBooks(@Valid request: BookSearchRequest): BookSearchResponse { + override fun searchBooks(request: BookSearchRequest): BookSearchResponse { log.info("Service - Converting BookSearchRequest to AladinBookSearchRequest and calling Aladin API for book search.") val aladinSearchRequest = AladinBookSearchRequest.of( request.validQuery(), diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt index 754974c0..cc1527e4 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt @@ -7,6 +7,6 @@ import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse sealed interface BookQueryService { - fun searchBooks(@Valid request: BookSearchRequest): BookSearchResponse + fun searchBooks(request: BookSearchRequest): BookSearchResponse fun getBookDetail(@Valid request: BookDetailRequest): BookDetailResponse } 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 bbad35b7..4dd2d3d0 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 @@ -21,7 +21,7 @@ class ReadingRecordService( fun createReadingRecord( userId: UUID, userBookId: UUID, - @Valid request: CreateReadingRecordRequest + request: CreateReadingRecordRequest ): ReadingRecordResponse { val readingRecordInfoVO = readingRecordDomainService.createReadingRecord( userBookId = userBookId,