From 3a06361bf1de2b6ae429ac4802fa22b4809c3c3b Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:25:38 +0900 Subject: [PATCH 01/17] =?UTF-8?q?[BOOK-154]=20feat:=20apis=20-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20Con?= =?UTF-8?q?troller=20=EC=9E=91=EC=84=B1=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/org/yapp/apis/ApisApplication.kt | 4 +++- .../apis/book/controller/BookController.kt | 19 +++++++++++++++---- .../apis/book/controller/BookControllerApi.kt | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt b/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt index a2d59910..c71e6270 100644 --- a/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt +++ b/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt @@ -3,6 +3,7 @@ package org.yapp.apis import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration import org.springframework.boot.runApplication +import org.springframework.data.web.config.EnableSpringDataWebSupport /** * Main application class for the apis module. @@ -14,9 +15,10 @@ import org.springframework.boot.runApplication "org.yapp.domain", "org.yapp.gateway", "org.yapp.globalutils" - ] , + ], exclude = [JpaRepositoriesAutoConfiguration::class] ) +@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) class ApisApplication fun main(args: Array) { 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 f29c7f1b..f678e9c5 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 @@ -1,6 +1,10 @@ package org.yapp.apis.book.controller import jakarta.validation.Valid +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Sort +import org.springframework.data.web.PageableDefault import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping @@ -8,14 +12,17 @@ import org.springframework.web.bind.annotation.ModelAttribute import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController 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.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.usecase.BookUseCase +import org.yapp.domain.userbook.BookStatus import java.util.UUID @RestController @@ -53,10 +60,14 @@ class BookController( @GetMapping("/my-library") override fun getUserLibraryBooks( - @AuthenticationPrincipal userId: UUID - ): ResponseEntity> { - - val response = bookUseCase.getUserLibraryBooks(userId) + @AuthenticationPrincipal userId: UUID, + @RequestParam(required = false) status: BookStatus?, + @RequestParam(required = false) sort: String?, + @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC) + pageable: Pageable + ): ResponseEntity { + val response = bookUseCase.getUserLibraryBooks(userId, status, sort, pageable) return ResponseEntity.ok(response) } + } diff --git a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt index 84ed0c5c..0ab92eb3 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt @@ -8,18 +8,25 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import io.swagger.v3.oas.annotations.tags.Tag import jakarta.validation.Valid +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Sort +import org.springframework.data.web.PageableDefault import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam 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.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.domain.userbook.BookStatus import org.yapp.globalutils.exception.ErrorResponse import java.util.UUID @@ -130,5 +137,12 @@ interface BookControllerApi { ] ) @GetMapping("/my-library") - fun getUserLibraryBooks(@AuthenticationPrincipal userId: UUID): ResponseEntity> + fun getUserLibraryBooks( + @AuthenticationPrincipal userId: UUID, + @RequestParam(required = false) status: BookStatus?, + @RequestParam(required = false) sort: String?, + @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC) + pageable: Pageable + ): ResponseEntity + } From bd130c0c87f88f6d836582ffa0d04f9c86a88d71 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:25:56 +0900 Subject: [PATCH 02/17] =?UTF-8?q?[BOOK-154]=20feat:=20apis=20-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20dto?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/response/UserBookPageResponse.kt | 36 +++++++++++++++++++ .../book/dto/response/UserBookResponse.kt | 5 +-- .../org/yapp/infra}/common/BaseTimeEntity.kt | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt rename {domain/src/main/kotlin/org/yapp/domain => infra/src/main/kotlin/org/yapp/infra}/common/BaseTimeEntity.kt (96%) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt new file mode 100644 index 00000000..1b890794 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt @@ -0,0 +1,36 @@ +package org.yapp.apis.book.dto.response + +import io.swagger.v3.oas.annotations.media.Schema +import org.springframework.data.domain.Page + +@Schema(description = "사용자의 책 페이지 응답") +data class UserBookPageResponse private constructor( + + @Schema(description = "책 목록 (페이지네이션)", implementation = UserBookResponse::class) + val books: Page, + + @Schema(description = "읽기 전 상태의 책 개수") + val beforeReadingCount: Long, + + @Schema(description = "읽고 있는 책 개수") + val readingCount: Long, + + @Schema(description = "완독한 책 개수") + val completedCount: Long +) { + companion object { + fun from( + books: Page, + beforeReadingCount: Long, + readingCount: Long, + completedCount: Long + ): UserBookPageResponse { + return UserBookPageResponse( + books = books, + beforeReadingCount = beforeReadingCount, + readingCount = readingCount, + completedCount = completedCount + ) + } + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt index 2819998f..f28a6402 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt @@ -2,6 +2,7 @@ package org.yapp.apis.book.dto.response import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.vo.UserBookInfoVO +import org.yapp.globalutils.validator.BookDataValidator import java.time.format.DateTimeFormatter import java.util.UUID @@ -26,10 +27,10 @@ data class UserBookResponse private constructor( userId = userBook.userId.value, bookIsbn = userBook.bookIsbn.value, bookTitle = userBook.title, - bookAuthor = userBook.author, + bookAuthor = BookDataValidator.removeParenthesesFromAuthor(userBook.author), status = userBook.status, coverImageUrl = userBook.coverImageUrl, - publisher = userBook.publisher, + publisher = BookDataValidator.removeParenthesesFromPublisher(userBook.publisher), createdAt = userBook.createdAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), updatedAt = userBook.updatedAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), ) diff --git a/domain/src/main/kotlin/org/yapp/domain/common/BaseTimeEntity.kt b/infra/src/main/kotlin/org/yapp/infra/common/BaseTimeEntity.kt similarity index 96% rename from domain/src/main/kotlin/org/yapp/domain/common/BaseTimeEntity.kt rename to infra/src/main/kotlin/org/yapp/infra/common/BaseTimeEntity.kt index 91538097..00e10807 100644 --- a/domain/src/main/kotlin/org/yapp/domain/common/BaseTimeEntity.kt +++ b/infra/src/main/kotlin/org/yapp/infra/common/BaseTimeEntity.kt @@ -1,4 +1,4 @@ -package org.yapp.domain.common +package org.yapp.infra.common import jakarta.persistence.Column import jakarta.persistence.EntityListeners From a08c0f791896c89f038e7bc624c4fc2eaee71b00 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:26:08 +0900 Subject: [PATCH 03/17] =?UTF-8?q?[BOOK-154]=20feat:=20apis=20-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20ser?= =?UTF-8?q?vice=20=EC=9E=91=EC=84=B1=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/service/BookManagementService.kt | 2 +- .../yapp/apis/book/service/UserBookService.kt | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) 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 0f2067d1..b7c6df90 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 @@ -14,7 +14,7 @@ class BookManagementService( request.validIsbn(), request.validTitle(), request.validAuthor(), - request.validAuthor(), + request.validPublisher(), request.coverImageUrl, request.publicationYear, request.description 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 9d51096c..72966d2f 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,11 +1,16 @@ package org.yapp.apis.book.service +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest +import org.yapp.apis.book.dto.response.UserBookPageResponse import org.yapp.apis.book.dto.response.UserBookResponse import org.yapp.apis.book.dto.request.UpsertUserBookRequest +import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBookDomainService import org.yapp.domain.userbook.vo.UserBookInfoVO +import org.yapp.domain.userbook.vo.UserBookStatusCountsVO import java.util.UUID @@ -18,9 +23,9 @@ class UserBookService( userBookDomainService.upsertUserBook( upsertUserBookRequest.userId, upsertUserBookRequest.bookIsbn, - upsertUserBookRequest.bookPublisher, - upsertUserBookRequest.bookAuthor, upsertUserBookRequest.bookTitle, + upsertUserBookRequest.bookAuthor, + upsertUserBookRequest.bookPublisher, upsertUserBookRequest.bookCoverImageUrl, upsertUserBookRequest.status ) @@ -42,5 +47,30 @@ class UserBookService( .map { UserBookResponse.from(it) } } -} + fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page { + return userBookDomainService.findUserBooksByDynamicCondition(userId, status, sort, pageable) + .map { UserBookResponse.from(it) } + } + + fun findUserBooksByDynamicConditionWithStatusCounts( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): UserBookPageResponse { + val books = findUserBooksByDynamicCondition(userId, status, sort, pageable) + val statusCounts = userBookDomainService.getUserBookStatusCounts(userId) + return UserBookPageResponse.from( + books = books, + beforeReadingCount = statusCounts.beforeReadingCount, + readingCount = statusCounts.readingCount, + completedCount = statusCounts.completedCount + ) + } +} From 21daa1d676796f0891e46d282008b6664f4f1686 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:26:13 +0900 Subject: [PATCH 04/17] =?UTF-8?q?[BOOK-154]=20feat:=20apis=20-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20use?= =?UTF-8?q?case=20=EC=9E=91=EC=84=B1=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/book/usecase/BookUseCase.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 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 9dd53009..4cd095fd 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,8 @@ 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 @@ -10,12 +12,14 @@ import org.yapp.apis.book.dto.request.BookSearchRequest import org.yapp.apis.book.dto.request.UserBookRegisterRequest 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.globalutils.annotation.UseCase import java.util.UUID @@ -66,9 +70,14 @@ class BookUseCase( return userBookResponse } - fun getUserLibraryBooks(userId: UUID): List { + fun getUserLibraryBooks( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): UserBookPageResponse { userAuthService.validateUserExists(userId) - return userBookService.findAllUserBooks(userId) + return userBookService.findUserBooksByDynamicConditionWithStatusCounts(userId, status, sort, pageable) } } From 7a51a7fb6799fd46cb552f24a28f097d8850a280 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:26:46 +0900 Subject: [PATCH 05/17] =?UTF-8?q?[BOOK-154]=20feat:=20apis,buildSrc=20-=20?= =?UTF-8?q?=EB=82=B4=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80?= =?UTF-8?q?=EC=83=89=20querydsl=20config=20setting=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/config/InfraConfig.kt | 3 ++- build.gradle.kts | 23 +++++++++++++++++++ buildSrc/src/main/kotlin/Dependencies.kt | 8 ++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt b/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt index d6d5242e..318b1c11 100644 --- a/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt +++ b/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt @@ -10,7 +10,8 @@ import org.yapp.infra.InfraBaseConfigGroup InfraBaseConfigGroup.JPA, InfraBaseConfigGroup.ASYNC, InfraBaseConfigGroup.REDIS, - InfraBaseConfigGroup.REST_CLIENT + InfraBaseConfigGroup.REST_CLIENT, + InfraBaseConfigGroup.QUERY_DSL ] ) class InfraConfig diff --git a/build.gradle.kts b/build.gradle.kts index e6fe519d..957cef7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -75,6 +75,29 @@ subprojects { } } +// QueryDSL 설정 +val querydslDir = "$buildDir/generated/querydsl" + +tasks.withType().configureEach { + kotlinOptions.jvmTarget = Versions.JAVA_VERSION +} + +sourceSets { + main { + kotlin { + srcDir(querydslDir) + } + } +} + + +tasks.withType().configureEach { + doFirst { + delete(querydslDir) + } +} + + // 루트 프로젝트에서 모든 JaCoCo 설정 관리 configure(subprojects) { jacoco { diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 52445198..ad06fb0e 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -8,7 +8,8 @@ object Dependencies { const val BOOT_STARTER_ACTUATOR = "org.springframework.boot:spring-boot-starter-actuator" const val BOOT_STARTER_TEST = "org.springframework.boot:spring-boot-starter-test" const val BOOT_STARTER_DATA_REDIS = "org.springframework.boot:spring-boot-starter-data-redis" - const val BOOT_STARTER_OAUTH2_RESOURCE_SERVER = "org.springframework.boot:spring-boot-starter-oauth2-resource-server" + const val BOOT_STARTER_OAUTH2_RESOURCE_SERVER = + "org.springframework.boot:spring-boot-starter-oauth2-resource-server" const val KOTLIN_REFLECT = "org.jetbrains.kotlin:kotlin-reflect" } @@ -51,4 +52,9 @@ object Dependencies { object Flyway { const val MYSQL = "org.flywaydb:flyway-mysql" } + + object QueryDsl { + const val JPA = "com.querydsl:querydsl-jpa:5.0.0:jakarta" + const val APT = "com.querydsl:querydsl-apt:5.0.0:jakarta" + } } From 74430fefe7dff2b758a63750c1b8a621b2a7e24e Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:27:52 +0900 Subject: [PATCH 06/17] =?UTF-8?q?[BOOK-154]=20refactor:=20domain,infra=20-?= =?UTF-8?q?=20JpaAuditing=EC=9C=BC=EB=A1=9C=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/org/yapp/domain/book/Book.kt | 12 +++---- .../main/kotlin/org/yapp/domain/user/User.kt | 31 ++++++------------- .../org/yapp/domain/user/UserDomainService.kt | 10 +++--- .../org/yapp/domain/userbook/UserBook.kt | 19 +++++------- .../org/yapp/infra/book/entity/BookEntity.kt | 8 ++--- .../infra/config/internal/jpa/JpaConfig.kt | 11 ++++++- .../org/yapp/infra/user/entity/UserEntity.kt | 8 ++--- .../infra/userbook/entity/UserBookEntity.kt | 8 ++--- 8 files changed, 42 insertions(+), 65 deletions(-) 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 4a4251f3..35c90551 100644 --- a/domain/src/main/kotlin/org/yapp/domain/book/Book.kt +++ b/domain/src/main/kotlin/org/yapp/domain/book/Book.kt @@ -14,8 +14,8 @@ data class Book private constructor( val publicationYear: Int?, val coverImageUrl: String, val description: String?, - val createdAt: LocalDateTime, - val updatedAt: LocalDateTime, + val createdAt: LocalDateTime? = null, + val updatedAt: LocalDateTime? = null, val deletedAt: LocalDateTime? = null ) { companion object { @@ -28,7 +28,6 @@ data class Book private constructor( publicationYear: Int? = null, description: String? = null ): Book { - val now = LocalDateTime.now() return Book( isbn = Isbn.newInstance(isbn), title = title, @@ -37,9 +36,6 @@ data class Book private constructor( publicationYear = publicationYear, coverImageUrl = coverImageUrl, description = description, - createdAt = now, - updatedAt = now, - deletedAt = null ) } @@ -51,8 +47,8 @@ data class Book private constructor( publicationYear: Int?, coverImageUrl: String, description: String?, - createdAt: LocalDateTime, - updatedAt: LocalDateTime, + createdAt: LocalDateTime? = null, + updatedAt: LocalDateTime? = null, deletedAt: LocalDateTime? = null ): Book { return Book( diff --git a/domain/src/main/kotlin/org/yapp/domain/user/User.kt b/domain/src/main/kotlin/org/yapp/domain/user/User.kt index b4989621..86fd6c67 100644 --- a/domain/src/main/kotlin/org/yapp/domain/user/User.kt +++ b/domain/src/main/kotlin/org/yapp/domain/user/User.kt @@ -28,16 +28,15 @@ data class User private constructor( val providerType: ProviderType, val providerId: ProviderId, val role: Role, - val createdAt: LocalDateTime, - val updatedAt: LocalDateTime, + val createdAt: LocalDateTime? = null, + val updatedAt: LocalDateTime? = null, val deletedAt: LocalDateTime? = null ) { fun restore(): User { require(this.isDeleted()) { "User is already active" } return this.copy( - deletedAt = null, - updatedAt = LocalDateTime.now() + deletedAt = null ) } @@ -47,10 +46,7 @@ data class User private constructor( nickname: String, profileImageUrl: String?, providerType: ProviderType, - providerId: String, - createdAt: LocalDateTime, - updatedAt: LocalDateTime, - deletedAt: LocalDateTime? = null + providerId: String ): User { return User( id = Id.newInstance(UuidGenerator.create()), @@ -59,10 +55,7 @@ data class User private constructor( profileImageUrl = profileImageUrl, providerType = providerType, providerId = ProviderId.newInstance(providerId), - role = Role.USER, - createdAt = createdAt, - updatedAt = updatedAt, - deletedAt = deletedAt + role = Role.USER ) } @@ -73,10 +66,7 @@ data class User private constructor( profileImageUrl: String?, providerType: ProviderType, providerId: String, - role: Role, - createdAt: LocalDateTime, - updatedAt: LocalDateTime, - deletedAt: LocalDateTime? = null + role: Role ): User { return User( id = Id.newInstance(UuidGenerator.create()), @@ -85,10 +75,7 @@ data class User private constructor( profileImageUrl = profileImageUrl, providerType = providerType, providerId = ProviderId.newInstance(providerId), - role = role, - createdAt = createdAt, - updatedAt = updatedAt, - deletedAt = deletedAt + role = role ) } @@ -100,8 +87,8 @@ data class User private constructor( providerType: ProviderType, providerId: ProviderId, role: Role, - createdAt: LocalDateTime, - updatedAt: LocalDateTime, + createdAt: LocalDateTime? = null, + updatedAt: LocalDateTime? = null, deletedAt: LocalDateTime? = null ): User { return User( diff --git a/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt index b4cc7728..036486ce 100644 --- a/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt @@ -28,7 +28,10 @@ class UserDomainService( ?.let { UserIdentityVO.newInstance(it) } } - fun findUserByProviderTypeAndProviderIdIncludingDeleted(providerType: ProviderType, providerId: String): UserIdentityVO? { + fun findUserByProviderTypeAndProviderIdIncludingDeleted( + providerType: ProviderType, + providerId: String + ): UserIdentityVO? { return userRepository.findByProviderTypeAndProviderIdIncludingDeleted(providerType, providerId) ?.let { UserIdentityVO.newInstance(it) } } @@ -48,15 +51,12 @@ class UserDomainService( providerType: ProviderType, providerId: String ): UserIdentityVO { - val now = timeProvider.now() val user = User.create( email = email, nickname = nickname, profileImageUrl = profileImageUrl, providerType = providerType, - providerId = providerId, - createdAt = now, - updatedAt = now + providerId = providerId ) val savedUser = userRepository.save(user) return UserIdentityVO.newInstance(savedUser) diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBook.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBook.kt index 3a170206..18508944 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBook.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBook.kt @@ -14,12 +14,12 @@ data class UserBook private constructor( val title: String, val author: String, val status: BookStatus, - val createdAt: LocalDateTime, - val updatedAt: LocalDateTime, + val createdAt: LocalDateTime? = null, + val updatedAt: LocalDateTime? = null, val deletedAt: LocalDateTime? = null, ) { fun updateStatus(newStatus: BookStatus): UserBook { - return this.copy(status = newStatus, updatedAt = LocalDateTime.now()) + return this.copy(status = newStatus) } companion object { @@ -30,9 +30,8 @@ data class UserBook private constructor( publisher: String, title: String, author: String, - initialStatus: BookStatus = BookStatus.BEFORE_READING + status: BookStatus ): UserBook { - val now = LocalDateTime.now() return UserBook( id = Id.newInstance(UuidGenerator.create()), userId = UserId.newInstance(userId), @@ -41,9 +40,7 @@ data class UserBook private constructor( publisher = publisher, title = title, author = author, - status = initialStatus, - createdAt = now, - updatedAt = now + status = status, ) } @@ -56,9 +53,9 @@ data class UserBook private constructor( title: String, author: String, status: BookStatus, - createdAt: LocalDateTime, - updatedAt: LocalDateTime, - deletedAt: LocalDateTime? + createdAt: LocalDateTime? = null, + updatedAt: LocalDateTime? = null, + deletedAt: LocalDateTime? = null ): UserBook { return UserBook( id = id, diff --git a/infra/src/main/kotlin/org/yapp/infra/book/entity/BookEntity.kt b/infra/src/main/kotlin/org/yapp/infra/book/entity/BookEntity.kt index a7a6ef88..0282ca8c 100644 --- a/infra/src/main/kotlin/org/yapp/infra/book/entity/BookEntity.kt +++ b/infra/src/main/kotlin/org/yapp/infra/book/entity/BookEntity.kt @@ -7,7 +7,7 @@ import jakarta.persistence.Table import org.hibernate.annotations.JdbcTypeCode import org.hibernate.annotations.SQLDelete import org.yapp.domain.book.Book -import org.yapp.domain.common.BaseTimeEntity +import org.yapp.infra.common.BaseTimeEntity import java.sql.Types @Entity @@ -74,11 +74,7 @@ class BookEntity private constructor( publicationYear = book.publicationYear, coverImageUrl = book.coverImageUrl, description = book.description - ).apply { - this.createdAt = book.createdAt - this.updatedAt = book.updatedAt - this.deletedAt = book.deletedAt - } + ) } override fun equals(other: Any?): Boolean { diff --git a/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt b/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt index a0f37562..b4315358 100644 --- a/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt +++ b/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt @@ -1,11 +1,20 @@ package org.yapp.infra.config.internal.jpa +import com.querydsl.jpa.impl.JPAQueryFactory +import jakarta.persistence.EntityManager +import jakarta.persistence.PersistenceContext import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaAuditing import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.transaction.annotation.EnableTransactionManagement import org.yapp.infra.InfraBaseConfig +@Configuration @EnableTransactionManagement +@EnableJpaAuditing @EntityScan(basePackages = ["org.yapp.infra"]) @EnableJpaRepositories(basePackages = ["org.yapp.infra"]) -class JpaConfig : InfraBaseConfig +class JpaConfig : InfraBaseConfig { +} diff --git a/infra/src/main/kotlin/org/yapp/infra/user/entity/UserEntity.kt b/infra/src/main/kotlin/org/yapp/infra/user/entity/UserEntity.kt index d8e06180..f1888fe0 100644 --- a/infra/src/main/kotlin/org/yapp/infra/user/entity/UserEntity.kt +++ b/infra/src/main/kotlin/org/yapp/infra/user/entity/UserEntity.kt @@ -4,7 +4,7 @@ import jakarta.persistence.* import org.hibernate.annotations.JdbcTypeCode import org.hibernate.annotations.SQLDelete import org.hibernate.annotations.SQLRestriction -import org.yapp.domain.common.BaseTimeEntity +import org.yapp.infra.common.BaseTimeEntity import org.yapp.domain.user.ProviderType import org.yapp.domain.user.User import org.yapp.globalutils.auth.Role @@ -73,11 +73,7 @@ class UserEntity private constructor( providerType = user.providerType, providerId = user.providerId.value, role = user.role - ).apply { - this.createdAt = user.createdAt - this.updatedAt = user.updatedAt - this.deletedAt = user.deletedAt - } + ) } override fun equals(other: Any?): Boolean { diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt index 0a144c27..8f688aa4 100644 --- a/infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt @@ -3,7 +3,7 @@ package org.yapp.infra.userbook.entity import jakarta.persistence.* import org.hibernate.annotations.JdbcTypeCode import org.hibernate.annotations.SQLDelete -import org.yapp.domain.common.BaseTimeEntity +import org.yapp.infra.common.BaseTimeEntity import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBook import java.sql.Types @@ -78,11 +78,7 @@ class UserBookEntity( title = userBook.title, author = userBook.author, status = userBook.status, - ).apply { - this.createdAt = userBook.createdAt - this.updatedAt = userBook.updatedAt - this.deletedAt = userBook.deletedAt - } + ) } } From 18d80d8246fb3ce208e8b69a8d2d16d8820b9d1e Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:28:41 +0900 Subject: [PATCH 07/17] =?UTF-8?q?[BOOK-154]=20feat:=20domain-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20domainservice,=20vo=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/userbook/UserBookDomainService.kt | 32 +++++++++++++++- .../domain/userbook/UserBookRepository.kt | 11 ++++++ .../yapp/domain/userbook/vo/UserBookInfoVO.kt | 4 +- .../userbook/vo/UserBookStatusCountsVO.kt | 38 +++++++++++++++++++ 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookStatusCountsVO.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 8063ca88..ba486f12 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -1,6 +1,9 @@ package org.yapp.domain.userbook +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable import org.yapp.domain.userbook.vo.UserBookInfoVO +import org.yapp.domain.userbook.vo.UserBookStatusCountsVO import org.yapp.globalutils.annotation.DomainService import java.util.UUID @@ -17,8 +20,7 @@ class UserBookDomainService( bookCoverImageUrl: String, status: BookStatus ): UserBookInfoVO { - val userBook = userBookRepository.findByUserIdAndBookIsbn(userId, bookIsbn) - ?.apply { updateStatus(status) } + val userBook = userBookRepository.findByUserIdAndBookIsbn(userId, bookIsbn)?.updateStatus(status) ?: UserBook.create( userId = userId, bookIsbn = bookIsbn, @@ -26,6 +28,7 @@ class UserBookDomainService( author = bookAuthor, publisher = bookPublisher, coverImageUrl = bookCoverImageUrl, + status = status ) val savedUserBook = userBookRepository.save(userBook) @@ -37,6 +40,16 @@ class UserBookDomainService( .map(UserBookInfoVO::newInstance) } + fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page { + return userBookRepository.findUserBooksByDynamicCondition(userId, status, sort, pageable) + .map(UserBookInfoVO::newInstance) + } + fun findAllByUserIdAndBookIsbnIn(userId: UUID, isbns: List): List { if (isbns.isEmpty()) { return emptyList() @@ -44,4 +57,19 @@ class UserBookDomainService( return userBookRepository.findAllByUserIdAndBookIsbnIn(userId, isbns) .map { UserBookInfoVO.newInstance(it) } } + + + fun getUserBookStatusCounts(userId: UUID): UserBookStatusCountsVO { + val statusCounts = mapOf( + BookStatus.BEFORE_READING to countUserBooksByStatus(userId, BookStatus.BEFORE_READING), + BookStatus.READING to countUserBooksByStatus(userId, BookStatus.READING), + BookStatus.COMPLETED to countUserBooksByStatus(userId, BookStatus.COMPLETED) + ) + return UserBookStatusCountsVO.newInstance(statusCounts) + } + + + private fun countUserBooksByStatus(userId: UUID, status: BookStatus): Long { + return userBookRepository.countUserBooksByStatus(userId, status) + } } 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 54933ec3..e6708655 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt @@ -1,5 +1,7 @@ package org.yapp.domain.userbook +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable import java.util.UUID @@ -13,4 +15,13 @@ interface UserBookRepository { fun findAllByUserIdAndBookIsbnIn(userId: UUID, bookIsbns: List): List + fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page + + fun countUserBooksByStatus(userId: UUID, status: BookStatus): Long + } diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt index 0c464781..eabc3213 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt @@ -39,8 +39,8 @@ data class UserBookInfoVO private constructor( title = userBook.title, author = userBook.author, status = userBook.status, - createdAt = userBook.createdAt, - updatedAt = userBook.updatedAt + createdAt = userBook.createdAt!!, + updatedAt = userBook.updatedAt!! ) } } diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookStatusCountsVO.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookStatusCountsVO.kt new file mode 100644 index 00000000..db50b30a --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookStatusCountsVO.kt @@ -0,0 +1,38 @@ +package org.yapp.domain.userbook.vo + +import org.yapp.domain.userbook.BookStatus + + +data class UserBookStatusCountsVO private constructor( + val beforeReadingCount: Long, + val readingCount: Long, + val completedCount: Long +) { + init { + require(beforeReadingCount >= 0) { "Before reading count cannot be negative" } + require(readingCount >= 0) { "Reading count cannot be negative" } + require(completedCount >= 0) { "Completed count cannot be negative" } + } + + companion object { + fun newInstance( + beforeReadingCount: Long, + readingCount: Long, + completedCount: Long + ): UserBookStatusCountsVO { + return UserBookStatusCountsVO( + beforeReadingCount = beforeReadingCount, + readingCount = readingCount, + completedCount = completedCount + ) + } + + fun newInstance(statusCounts: Map): UserBookStatusCountsVO { + return UserBookStatusCountsVO( + beforeReadingCount = statusCounts[BookStatus.BEFORE_READING] ?: 0L, + readingCount = statusCounts[BookStatus.READING] ?: 0L, + completedCount = statusCounts[BookStatus.COMPLETED] ?: 0L + ) + } + } +} From 9a4433aabdf5d494a4837cc8f4196ab65f2da8ac Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:29:05 +0900 Subject: [PATCH 08/17] =?UTF-8?q?[BOOK-154]=20feat:=20domain-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=8B=9C=20=EA=B4=84=ED=98=B8=20=EC=A0=9C=EA=B1=B0=ED=95=98?= =?UTF-8?q?=EB=8A=94=20validator=20=EB=B6=84=EB=A6=AC=20=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../globalutils/validator/BookDataValidator.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 global-utils/src/main/kotlin/org/yapp/globalutils/validator/BookDataValidator.kt diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/validator/BookDataValidator.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/validator/BookDataValidator.kt new file mode 100644 index 00000000..221c5bb6 --- /dev/null +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/validator/BookDataValidator.kt @@ -0,0 +1,14 @@ +package org.yapp.globalutils.validator + +object BookDataValidator { + + private val PARENTHESIS_REGEX = "\\s*\\([^)]*\\)\\s*".toRegex() + + fun removeParenthesesFromAuthor(author: String): String { + return author.replace(PARENTHESIS_REGEX, "") + } + + fun removeParenthesesFromPublisher(publisher: String): String { + return publisher.replace(PARENTHESIS_REGEX, "") + } +} From 43fae8011f36bb9076b7282b1778ddc87756d5a0 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:29:32 +0900 Subject: [PATCH 09/17] =?UTF-8?q?[BOOK-154]=20feat:=20infra-=20=EB=82=B4?= =?UTF-8?q?=EC=84=9C=EC=9E=AC=20=EB=8F=99=EC=A0=81=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=8B=9C=20querydsl=20config=20=EC=84=A4=EC=A0=95=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/build.gradle.kts | 8 ++++++++ .../org/yapp/infra/InfraBaseConfigGroup.kt | 4 +++- .../config/internal/querydsl/QuerydslConfig.kt | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 infra/src/main/kotlin/org/yapp/infra/config/internal/querydsl/QuerydslConfig.kt diff --git a/infra/build.gradle.kts b/infra/build.gradle.kts index 868fa00a..56f6a6f4 100644 --- a/infra/build.gradle.kts +++ b/infra/build.gradle.kts @@ -1,5 +1,9 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar +plugins { + kotlin(Plugins.Kotlin.Short.KAPT) version Versions.KOTLIN +} + dependencies { implementation(project(Dependencies.Projects.GLOBAL_UTILS)) implementation(project(Dependencies.Projects.DOMAIN)) @@ -17,6 +21,10 @@ dependencies { implementation(Dependencies.Flyway.MYSQL) + implementation(Dependencies.QueryDsl.JPA) + kapt(Dependencies.QueryDsl.APT) + + testImplementation(Dependencies.TestContainers.MYSQL) testImplementation(Dependencies.TestContainers.JUNIT_JUPITER) testImplementation(Dependencies.TestContainers.REDIS) diff --git a/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt b/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt index 209551ac..190b25b6 100644 --- a/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt +++ b/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt @@ -4,6 +4,7 @@ import org.yapp.infra.config.external.api.RestClientConfig import org.yapp.infra.config.external.redis.RedisConfig import org.yapp.infra.config.internal.async.AsyncConfig import org.yapp.infra.config.internal.jpa.JpaConfig +import org.yapp.infra.config.internal.querydsl.QuerydslConfig enum class InfraBaseConfigGroup( val configClass: Class @@ -11,5 +12,6 @@ enum class InfraBaseConfigGroup( ASYNC(AsyncConfig::class.java), JPA(JpaConfig::class.java), REDIS(RedisConfig::class.java), - REST_CLIENT(RestClientConfig::class.java) + REST_CLIENT(RestClientConfig::class.java), + QUERY_DSL(QuerydslConfig::class.java) } diff --git a/infra/src/main/kotlin/org/yapp/infra/config/internal/querydsl/QuerydslConfig.kt b/infra/src/main/kotlin/org/yapp/infra/config/internal/querydsl/QuerydslConfig.kt new file mode 100644 index 00000000..90930b55 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/config/internal/querydsl/QuerydslConfig.kt @@ -0,0 +1,17 @@ +package org.yapp.infra.config.internal.querydsl + +import com.querydsl.jpa.impl.JPAQueryFactory +import jakarta.persistence.EntityManager +import jakarta.persistence.PersistenceContext +import org.springframework.context.annotation.Bean +import org.yapp.infra.InfraBaseConfig + +class QuerydslConfig : InfraBaseConfig { + @PersistenceContext + private lateinit var entityManager: EntityManager + + @Bean + fun jpaQueryFactory(): JPAQueryFactory { + return JPAQueryFactory(entityManager) + } +} From ee7d032988257d42d0c06726a4cac75737ae95f4 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:30:08 +0900 Subject: [PATCH 10/17] =?UTF-8?q?[BOOK-154]=20feat:=20infra-=20querydslrep?= =?UTF-8?q?ository=20=EA=B5=AC=ED=98=84=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JpaUserBookQuerydslRepository.kt | 21 +++++ .../repository/JpaUserBookRepository.kt | 2 +- .../impl/JpaUserBookQuerydslRepositoryImpl.kt | 78 +++++++++++++++++++ .../repository/impl/UserBookRepositoryImpl.kt | 17 ++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt create mode 100644 infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt new file mode 100644 index 00000000..5149427b --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt @@ -0,0 +1,21 @@ +package org.yapp.infra.userbook.repository + +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.yapp.domain.userbook.BookStatus +import org.yapp.infra.userbook.entity.UserBookEntity +import java.util.UUID + +interface JpaUserBookQuerydslRepository { + fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page + + fun countUserBooksByStatus( + userId: UUID, + status: BookStatus + ): Long +} 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 53efb57b..e4a66dd5 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 @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository import org.yapp.infra.userbook.entity.UserBookEntity import java.util.* -interface JpaUserBookRepository : JpaRepository { +interface JpaUserBookRepository : JpaRepository, JpaUserBookQuerydslRepository { fun findByUserIdAndBookIsbn(userId: UUID, bookIsbn: String): UserBookEntity? 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/JpaUserBookQuerydslRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt new file mode 100644 index 00000000..46a0e90e --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt @@ -0,0 +1,78 @@ +package org.yapp.infra.userbook.repository.impl + +import com.querydsl.core.types.Order +import com.querydsl.core.types.OrderSpecifier +import com.querydsl.core.types.dsl.BooleanExpression +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.domain.Page +import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.Pageable +import org.yapp.domain.userbook.BookStatus +import org.yapp.infra.userbook.entity.QUserBookEntity +import org.yapp.infra.userbook.entity.UserBookEntity +import org.yapp.infra.userbook.repository.JpaUserBookQuerydslRepository +import java.util.UUID + +class JpaUserBookQuerydslRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : JpaUserBookQuerydslRepository { + + private val userBook = QUserBookEntity.userBookEntity + + override fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page { + val query = queryFactory + .selectFrom(userBook) + .where( + userBook.userId.eq(userId), + statusEq(status) + ) + .orderBy(*createOrderSpecifier(sort)) + .offset(pageable.offset) + .limit(pageable.pageSize.toLong()) + + val results = query.fetch() + val total = queryFactory + .select(userBook.count()) + .from(userBook) + .where( + userBook.userId.eq(userId), + statusEq(status) + ) + .fetchOne() ?: 0L + + return PageImpl(results, pageable, total) + } + + override fun countUserBooksByStatus( + userId: UUID, + status: BookStatus + ): Long { + return queryFactory + .select(userBook.count()) + .from(userBook) + .where( + userBook.userId.eq(userId), + userBook.status.eq(status) + ) + .fetchOne() ?: 0L + } + + private fun statusEq(status: BookStatus?): BooleanExpression? { + return status?.let { userBook.status.eq(it) } + } + + private fun createOrderSpecifier(sort: String?): Array> { + return when (sort) { + "title_asc" -> arrayOf(OrderSpecifier(Order.ASC, userBook.title)) + "title_desc" -> arrayOf(OrderSpecifier(Order.DESC, userBook.title)) + "date_asc" -> arrayOf(OrderSpecifier(Order.ASC, userBook.createdAt)) + "date_desc" -> arrayOf(OrderSpecifier(Order.DESC, userBook.createdAt)) + else -> arrayOf(OrderSpecifier(Order.DESC, userBook.createdAt)) // 기본 정렬 + } + } +} 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 79bc98ac..3acc6d42 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 @@ -1,6 +1,9 @@ package org.yapp.infra.userbook.repository.impl +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable import org.springframework.stereotype.Repository +import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBookRepository import org.yapp.domain.userbook.UserBook import org.yapp.infra.userbook.entity.UserBookEntity @@ -32,4 +35,18 @@ class UserBookRepositoryImpl( return jpaUserBookRepository.findAllByUserIdAndBookIsbnIn(userId, bookIsbns) .map { it.toDomain() } } + + override fun findUserBooksByDynamicCondition( + userId: UUID, + status: BookStatus?, + sort: String?, + pageable: Pageable + ): Page { + return jpaUserBookRepository.findUserBooksByDynamicCondition(userId, status, sort, pageable) + .map { it.toDomain() } + } + + override fun countUserBooksByStatus(userId: UUID, status: BookStatus): Long { + return jpaUserBookRepository.countUserBooksByStatus(userId, status) + } } From 1fb5894de8ccda5dcde50a66a5d9e3b9ad010245 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:07:50 +0900 Subject: [PATCH 11/17] [BOOK-154] refactor: Update QueryDSL build directory path (#48) --- build.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 957cef7b..8084f136 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -76,11 +76,8 @@ subprojects { } // QueryDSL 설정 -val querydslDir = "$buildDir/generated/querydsl" +val querydslDir = "${layout.buildDirectory.get()}/generated/querydsl" -tasks.withType().configureEach { - kotlinOptions.jvmTarget = Versions.JAVA_VERSION -} sourceSets { main { From ff6ad3510aee833ab40f9f85a361ca5cc121fe61 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:08:43 +0900 Subject: [PATCH 12/17] =?UTF-8?q?[BOOK-154]=20refactor:=20domain=20-=20?= =?UTF-8?q?=EB=8F=84=EC=84=9C=20=EC=83=81=ED=83=9C=EB=B3=84=20=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EB=8F=99=EC=A0=81=20=EA=B3=84=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/domain/userbook/UserBookDomainService.kt | 10 +++------- .../org/yapp/domain/userbook/vo/UserBookInfoVO.kt | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) 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 ba486f12..aa155081 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt @@ -58,17 +58,13 @@ class UserBookDomainService( .map { UserBookInfoVO.newInstance(it) } } - fun getUserBookStatusCounts(userId: UUID): UserBookStatusCountsVO { - val statusCounts = mapOf( - BookStatus.BEFORE_READING to countUserBooksByStatus(userId, BookStatus.BEFORE_READING), - BookStatus.READING to countUserBooksByStatus(userId, BookStatus.READING), - BookStatus.COMPLETED to countUserBooksByStatus(userId, BookStatus.COMPLETED) - ) + val statusCounts = BookStatus.entries.associateWith { status -> + countUserBooksByStatus(userId, status) + } return UserBookStatusCountsVO.newInstance(statusCounts) } - private fun countUserBooksByStatus(userId: UUID, status: BookStatus): Long { return userBookRepository.countUserBooksByStatus(userId, status) } diff --git a/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt index eabc3213..27cfbb03 100644 --- a/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt +++ b/domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt @@ -39,8 +39,8 @@ data class UserBookInfoVO private constructor( title = userBook.title, author = userBook.author, status = userBook.status, - createdAt = userBook.createdAt!!, - updatedAt = userBook.updatedAt!! + createdAt = userBook.createdAt ?: throw IllegalStateException("createdAt은 null일 수 없습니다."), + updatedAt = userBook.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다.") ) } } From cef9e459a99b53101d0187c174ad1f82c0e12363 Mon Sep 17 00:00:00 2001 From: KIM MIN WOO <79193811+minwoo1999@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:09:08 +0900 Subject: [PATCH 13/17] =?UTF-8?q?[BOOK-154]=20refactor:=20infra=20-=20Quer?= =?UTF-8?q?yDSL=20=EC=BF=BC=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B0=80=EB=8F=85=EC=84=B1=20=ED=96=A5?= =?UTF-8?q?=EC=83=81=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/config/internal/jpa/JpaConfig.kt | 4 --- .../impl/JpaUserBookQuerydslRepositoryImpl.kt | 29 ++++++++----------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt b/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt index b4315358..6d5592db 100644 --- a/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt +++ b/infra/src/main/kotlin/org/yapp/infra/config/internal/jpa/JpaConfig.kt @@ -1,10 +1,6 @@ package org.yapp.infra.config.internal.jpa -import com.querydsl.jpa.impl.JPAQueryFactory -import jakarta.persistence.EntityManager -import jakarta.persistence.PersistenceContext import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.jpa.repository.config.EnableJpaAuditing import org.springframework.data.jpa.repository.config.EnableJpaRepositories diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt index 46a0e90e..ab563ce9 100644 --- a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt @@ -25,25 +25,20 @@ class JpaUserBookQuerydslRepositoryImpl( sort: String?, pageable: Pageable ): Page { - val query = queryFactory + val baseQuery = queryFactory .selectFrom(userBook) .where( userBook.userId.eq(userId), statusEq(status) ) - .orderBy(*createOrderSpecifier(sort)) + + val results = baseQuery + .orderBy(createOrderSpecifier(sort)) // 가변 인자 제거 .offset(pageable.offset) .limit(pageable.pageSize.toLong()) + .fetch() - val results = query.fetch() - val total = queryFactory - .select(userBook.count()) - .from(userBook) - .where( - userBook.userId.eq(userId), - statusEq(status) - ) - .fetchOne() ?: 0L + val total = baseQuery.fetchCount() return PageImpl(results, pageable, total) } @@ -66,13 +61,13 @@ class JpaUserBookQuerydslRepositoryImpl( return status?.let { userBook.status.eq(it) } } - private fun createOrderSpecifier(sort: String?): Array> { + private fun createOrderSpecifier(sort: String?): OrderSpecifier<*> { return when (sort) { - "title_asc" -> arrayOf(OrderSpecifier(Order.ASC, userBook.title)) - "title_desc" -> arrayOf(OrderSpecifier(Order.DESC, userBook.title)) - "date_asc" -> arrayOf(OrderSpecifier(Order.ASC, userBook.createdAt)) - "date_desc" -> arrayOf(OrderSpecifier(Order.DESC, userBook.createdAt)) - else -> arrayOf(OrderSpecifier(Order.DESC, userBook.createdAt)) // 기본 정렬 + "title_asc" -> userBook.title.asc() + "title_desc" -> userBook.title.desc() + "date_asc" -> userBook.createdAt.asc() + "date_desc" -> userBook.createdAt.desc() + else -> userBook.createdAt.desc() } } } From 747ef27411036f0e6a83b96a0d5e2226674d5fb4 Mon Sep 17 00:00:00 2001 From: kimminwoo Date: Tue, 22 Jul 2025 12:10:41 +0900 Subject: [PATCH 14/17] =?UTF-8?q?refactor:=20apis,infra=20-=20pageconfig?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt | 2 -- apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt | 3 ++- infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt b/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt index c71e6270..fb5c2683 100644 --- a/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt +++ b/apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt @@ -3,7 +3,6 @@ package org.yapp.apis import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration import org.springframework.boot.runApplication -import org.springframework.data.web.config.EnableSpringDataWebSupport /** * Main application class for the apis module. @@ -18,7 +17,6 @@ import org.springframework.data.web.config.EnableSpringDataWebSupport ], exclude = [JpaRepositoriesAutoConfiguration::class] ) -@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) class ApisApplication fun main(args: Array) { diff --git a/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt b/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt index 318b1c11..0c03e16a 100644 --- a/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt +++ b/apis/src/main/kotlin/org/yapp/apis/config/InfraConfig.kt @@ -11,7 +11,8 @@ import org.yapp.infra.InfraBaseConfigGroup InfraBaseConfigGroup.ASYNC, InfraBaseConfigGroup.REDIS, InfraBaseConfigGroup.REST_CLIENT, - InfraBaseConfigGroup.QUERY_DSL + InfraBaseConfigGroup.QUERY_DSL, + InfraBaseConfigGroup.PAGE, ] ) class InfraConfig diff --git a/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt b/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt index 190b25b6..c1b1612b 100644 --- a/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt +++ b/infra/src/main/kotlin/org/yapp/infra/InfraBaseConfigGroup.kt @@ -4,6 +4,7 @@ import org.yapp.infra.config.external.api.RestClientConfig import org.yapp.infra.config.external.redis.RedisConfig import org.yapp.infra.config.internal.async.AsyncConfig import org.yapp.infra.config.internal.jpa.JpaConfig +import org.yapp.infra.config.internal.page.PageConfig import org.yapp.infra.config.internal.querydsl.QuerydslConfig enum class InfraBaseConfigGroup( @@ -11,6 +12,7 @@ enum class InfraBaseConfigGroup( ) { ASYNC(AsyncConfig::class.java), JPA(JpaConfig::class.java), + PAGE(PageConfig::class.java), REDIS(RedisConfig::class.java), REST_CLIENT(RestClientConfig::class.java), QUERY_DSL(QuerydslConfig::class.java) From 1a143d19d73b82b633053abf0276eb7a67e756fe Mon Sep 17 00:00:00 2001 From: kimminwoo Date: Tue, 22 Jul 2025 12:11:22 +0900 Subject: [PATCH 15/17] =?UTF-8?q?refactor:=20apis=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=201=EC=B0=A8=20=EB=B0=98=EC=98=81=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/dto/response/UserBookPageResponse.kt | 2 +- .../yapp/apis/book/service/UserBookService.kt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt index 1b890794..ed964dc2 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookPageResponse.kt @@ -19,7 +19,7 @@ data class UserBookPageResponse private constructor( val completedCount: Long ) { companion object { - fun from( + fun of( books: Page, beforeReadingCount: Long, readingCount: Long, 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 72966d2f..cacf880a 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 @@ -47,7 +47,7 @@ class UserBookService( .map { UserBookResponse.from(it) } } - fun findUserBooksByDynamicCondition( + private fun findUserBooksByDynamicCondition( userId: UUID, status: BookStatus?, sort: String?, @@ -63,14 +63,14 @@ class UserBookService( sort: String?, pageable: Pageable ): UserBookPageResponse { - val books = findUserBooksByDynamicCondition(userId, status, sort, pageable) - val statusCounts = userBookDomainService.getUserBookStatusCounts(userId) + val userBookResponsePage = findUserBooksByDynamicCondition(userId, status, sort, pageable) + val userBookStatusCountsVO = userBookDomainService.getUserBookStatusCounts(userId) - return UserBookPageResponse.from( - books = books, - beforeReadingCount = statusCounts.beforeReadingCount, - readingCount = statusCounts.readingCount, - completedCount = statusCounts.completedCount + return UserBookPageResponse.of( + books = userBookResponsePage, + beforeReadingCount = userBookStatusCountsVO.beforeReadingCount, + readingCount = userBookStatusCountsVO.readingCount, + completedCount = userBookStatusCountsVO.completedCount ) } } From 17b7df14e379429ec986ffb889008e2409beb09c Mon Sep 17 00:00:00 2001 From: kimminwoo Date: Tue, 22 Jul 2025 12:11:44 +0900 Subject: [PATCH 16/17] refactor: infra - querydsl refactoring deprecated code (#48) --- .../impl/JpaUserBookQuerydslRepositoryImpl.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt index ab563ce9..da6f5bc8 100644 --- a/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt @@ -33,12 +33,19 @@ class JpaUserBookQuerydslRepositoryImpl( ) val results = baseQuery - .orderBy(createOrderSpecifier(sort)) // 가변 인자 제거 + .orderBy(createOrderSpecifier(sort)) .offset(pageable.offset) .limit(pageable.pageSize.toLong()) .fetch() - val total = baseQuery.fetchCount() + val total = queryFactory + .select(userBook.count()) + .from(userBook) + .where( + userBook.userId.eq(userId), + statusEq(status) + ) + .fetchOne() ?: 0L return PageImpl(results, pageable, total) } From a482901a8a07a944a44ff2c91481a0d0bd73a302 Mon Sep 17 00:00:00 2001 From: kimminwoo Date: Tue, 22 Jul 2025 12:12:00 +0900 Subject: [PATCH 17/17] =?UTF-8?q?refactor:=20infra=20-=20pageconfig=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/infra/config/internal/page/PageConfig.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 infra/src/main/kotlin/org/yapp/infra/config/internal/page/PageConfig.kt diff --git a/infra/src/main/kotlin/org/yapp/infra/config/internal/page/PageConfig.kt b/infra/src/main/kotlin/org/yapp/infra/config/internal/page/PageConfig.kt new file mode 100644 index 00000000..1370d1b4 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/config/internal/page/PageConfig.kt @@ -0,0 +1,10 @@ +package org.yapp.infra.config.internal.page + +import org.springframework.context.annotation.Configuration +import org.springframework.data.web.config.EnableSpringDataWebSupport +import org.yapp.infra.InfraBaseConfig + +@Configuration +@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) +class PageConfig : InfraBaseConfig { +}