diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt index e0d417e7..0cc42989 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt @@ -1,5 +1,6 @@ package org.yapp.apis.auth.strategy.withdraw +import jakarta.validation.Valid import org.yapp.apis.auth.dto.request.WithdrawStrategyRequest import org.yapp.domain.user.ProviderType @@ -7,5 +8,5 @@ interface WithdrawStrategy { fun getProviderType(): ProviderType - fun withdraw(request: WithdrawStrategyRequest) + fun withdraw(@Valid request: WithdrawStrategyRequest) } 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..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 @@ -1,5 +1,6 @@ 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 @@ -7,5 +8,5 @@ import org.yapp.apis.book.dto.response.BookSearchResponse sealed interface BookQueryService { fun searchBooks(request: BookSearchRequest): BookSearchResponse - fun getBookDetail(request: BookDetailRequest): BookDetailResponse + fun getBookDetail(@Valid 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 72703d19..1c0067af 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 @@ -4,11 +4,10 @@ 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.book.dto.request.UserBooksByIsbn13sRequest 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.BookSearchResponse.* +import org.yapp.apis.book.dto.response.BookSearchResponse.BookSummary import org.yapp.apis.book.dto.response.UserBookPageResponse import org.yapp.apis.book.dto.response.UserBookResponse import org.yapp.apis.book.service.BookManagementService diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordController.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordController.kt index 04111fbd..dfea775b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordController.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordController.kt @@ -20,6 +20,7 @@ import org.yapp.apis.readingrecord.usecase.ReadingRecordUseCase import org.yapp.domain.readingrecord.ReadingRecordSortType import java.util.UUID import jakarta.validation.Valid +import org.yapp.apis.readingrecord.dto.response.SeedStatsResponse @RestController @RequestMapping("/api/v1/reading-records") @@ -69,4 +70,13 @@ class ReadingRecordController( ) return ResponseEntity.ok(response) } + + @GetMapping("/{userBookId}/seed/stats") + override fun getReadingRecordSeedStats( + @AuthenticationPrincipal userId: UUID, + @PathVariable userBookId: UUID + ): ResponseEntity { + val stats = readingRecordUseCase.getSeedStats(userId, userBookId) + return ResponseEntity.ok(stats) + } } diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt index b1766a75..684ed83b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt @@ -14,17 +14,13 @@ 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.PathVariable -import org.springframework.web.bind.annotation.PostMapping -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.* import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse +import org.yapp.apis.readingrecord.dto.response.SeedStatsResponse import org.yapp.domain.readingrecord.ReadingRecordSortType import org.yapp.globalutils.exception.ErrorResponse -import java.util.UUID +import java.util.* @Tag(name = "Reading Records", description = "독서 기록 관련 API") @RequestMapping("/api/v1/reading-records") @@ -110,4 +106,28 @@ interface ReadingRecordControllerApi { @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC) @Parameter(description = "페이지네이션 정보 (페이지 번호, 페이지 크기, 정렬 방식)") pageable: Pageable ): ResponseEntity> + + @Operation( + summary = "씨앗 통계 조회", + description = "사용자가 등록한 책에 기록된 씨앗 개수를 조회합니다." + ) + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "씨앗 통계 조회 성공", + content = [Content(schema = Schema(implementation = SeedStatsResponse::class))] + ), + ApiResponse( + responseCode = "404", + description = "사용자를 찾을 수 없음", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ) + ] + ) + @GetMapping("/{userBookId}/seed/stats") + fun getReadingRecordSeedStats( + @AuthenticationPrincipal userId: UUID, + @PathVariable userBookId: UUID + ): ResponseEntity } diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/response/SeedStatsResponse.kt similarity index 95% rename from apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt rename to apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/response/SeedStatsResponse.kt index 63f9df3c..9e602000 100644 --- a/apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/response/SeedStatsResponse.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.seed.dto.response +package org.yapp.apis.readingrecord.dto.response import org.yapp.domain.readingrecordtag.vo.TagStatsVO import org.yapp.globalutils.tag.GeneralEmotionTagCategory 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 4dd2d3d0..505764a5 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,6 +1,5 @@ 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 @@ -17,7 +16,6 @@ import java.util.* class ReadingRecordService( private val readingRecordDomainService: ReadingRecordDomainService, ) { - fun createReadingRecord( userId: UUID, userBookId: UUID, diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordTagService.kt similarity index 62% rename from apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt rename to apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordTagService.kt index aab6358e..893765b6 100644 --- a/apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordTagService.kt @@ -1,21 +1,22 @@ -package org.yapp.apis.seed.service +package org.yapp.apis.readingrecord.service import org.springframework.stereotype.Service -import org.yapp.apis.seed.dto.response.SeedStatsResponse +import org.yapp.apis.readingrecord.dto.response.SeedStatsResponse import org.yapp.domain.readingrecordtag.ReadingRecordTagDomainService import org.yapp.globalutils.tag.GeneralEmotionTagCategory import java.util.* @Service -class SeedService( +class ReadingRecordTagService( private val readingRecordTagDomainService: ReadingRecordTagDomainService ) { - fun getSeedStatsByUserId(userId: UUID): SeedStatsResponse { - val tagStatsVO = readingRecordTagDomainService.countTagsByUserIdAndCategories( + fun getSeedStatsByUserIdAndUserBookId(userId: UUID, userBookId: UUID): SeedStatsResponse { + val tagStatsVO = readingRecordTagDomainService.countTagsByUserIdAndUserBookIdAndCategories( userId = userId, + userBookId = userBookId, categories = GeneralEmotionTagCategory.entries.map { it.displayName } ) return SeedStatsResponse.from(tagStatsVO) } -} +} 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 376a5223..f5a12d26 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 @@ -6,7 +6,9 @@ import org.springframework.transaction.annotation.Transactional 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.dto.response.SeedStatsResponse import org.yapp.apis.readingrecord.service.ReadingRecordService +import org.yapp.apis.readingrecord.service.ReadingRecordTagService import org.yapp.apis.user.service.UserService import org.yapp.domain.readingrecord.ReadingRecordSortType import org.yapp.globalutils.annotation.UseCase @@ -16,6 +18,7 @@ import java.util.* @Transactional(readOnly = true) class ReadingRecordUseCase( private val readingRecordService: ReadingRecordService, + private val readingRecordTagService: ReadingRecordTagService, private val userService: UserService, private val userBookService: UserBookService, ) { @@ -62,4 +65,13 @@ class ReadingRecordUseCase( pageable = pageable ) } + + fun getSeedStats( + userId: UUID, + userBookId: UUID + ): SeedStatsResponse { + userService.validateUserExists(userId) + userBookService.validateUserBookExists(userBookId, userId) + return readingRecordTagService.getSeedStatsByUserIdAndUserBookId(userId, userBookId) + } } diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedController.kt b/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedController.kt deleted file mode 100644 index 4cafbbc9..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedController.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.yapp.apis.seed.controller - -import org.springframework.http.ResponseEntity -import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController -import org.yapp.apis.seed.dto.response.SeedStatsResponse -import org.yapp.apis.seed.usecase.SeedUseCase -import java.util.* - -@RestController -@RequestMapping("/api/v1/seeds") -class SeedController( - private val seedUseCase: SeedUseCase -) : SeedControllerApi { - - override fun getSeedStats( - @AuthenticationPrincipal userId: UUID - ): ResponseEntity { - val stats = seedUseCase.getSeedStats(userId) - return ResponseEntity.ok(stats) - } -} diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedControllerApi.kt deleted file mode 100644 index e0d2c4f1..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedControllerApi.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.yapp.apis.seed.controller - -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.media.Content -import io.swagger.v3.oas.annotations.media.Schema -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 org.springframework.http.ResponseEntity -import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.GetMapping -import org.yapp.apis.seed.dto.response.SeedStatsResponse -import org.yapp.globalutils.exception.ErrorResponse -import java.util.* - -@Tag(name = "Seed", description = "씨앗(감정 태그) 관련 API") -interface SeedControllerApi { - - @Operation( - summary = "씨앗 통계 조회", - description = "사용자가 모은 감정 태그별 씨앗 개수를 조회합니다." - ) - @ApiResponses( - value = [ - ApiResponse( - responseCode = "200", - description = "씨앗 통계 조회 성공", - content = [Content(schema = Schema(implementation = SeedStatsResponse::class))] - ), - ApiResponse( - responseCode = "404", - description = "사용자를 찾을 수 없음", - content = [Content(schema = Schema(implementation = ErrorResponse::class))] - ) - ] - ) - @GetMapping("/stats") - fun getSeedStats( - @AuthenticationPrincipal userId: UUID - ): ResponseEntity -} \ No newline at end of file diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt deleted file mode 100644 index 0abd7920..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.yapp.apis.seed.usecase - -import org.springframework.transaction.annotation.Transactional -import org.yapp.apis.seed.dto.response.SeedStatsResponse -import org.yapp.apis.seed.service.SeedService -import org.yapp.apis.user.service.UserService -import org.yapp.globalutils.annotation.UseCase -import java.util.* - -@UseCase -@Transactional(readOnly = true) -class SeedUseCase( - private val userService: UserService, - private val seedService: SeedService -) { - fun getSeedStats(userId: UUID): SeedStatsResponse { - userService.validateUserExists(userId) - return seedService.getSeedStatsByUserId(userId) - } -} diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt index 9f71507c..b97ff04a 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt @@ -8,8 +8,12 @@ import java.util.* class ReadingRecordTagDomainService( private val readingRecordTagRepository: ReadingRecordTagRepository ) { - fun countTagsByUserIdAndCategories(userId: UUID, categories: List): TagStatsVO { - val categoryStats = readingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories) + fun countTagsByUserIdAndUserBookIdAndCategories( + userId: UUID, + userBookId: UUID, + categories: List + ): TagStatsVO { + val categoryStats = readingRecordTagRepository.countTagsByUserIdAndUserBookIdAndCategories(userId, userBookId, categories) return TagStatsVO.newInstance(categoryStats) } } diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt index 985efb22..d3fc565c 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt @@ -5,5 +5,5 @@ import java.util.UUID interface ReadingRecordTagRepository { fun saveAll(readingRecordTags: List): List fun findByReadingRecordId(readingRecordId: UUID): List - fun countTagsByUserIdAndCategories(userId: UUID, categories: List): Map + fun countTagsByUserIdAndUserBookIdAndCategories(userId: UUID, userBookId: UUID, categories: List): Map } diff --git a/global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt b/global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt index 2714ed7b..3f8d35ba 100644 --- a/global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt @@ -5,8 +5,8 @@ enum class GeneralEmotionTagCategory( ) { WARMTH("따뜻함"), JOY("즐거움"), - TENSION("긴장감"), - SADNESS("슬픔"); + SADNESS("슬픔"), + REALIZATION("깨달음"); companion object { private val BY_DISPLAY_NAME = entries.associateBy { it.displayName } diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt index f22bfb81..0a14cb79 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt @@ -3,5 +3,9 @@ package org.yapp.infra.readingrecordtag.repository import java.util.* interface JpaReadingRecordTagQuerydslRepository { - fun countTagsByUserIdAndCategories(userId: UUID, categories: List): Map -} \ No newline at end of file + fun countTagsByUserIdAndUserBookIdAndCategories( + userId: UUID, + userBookId: UUID, + categories: List + ): Map +} diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagRepository.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagRepository.kt index b316c33d..5c5a207f 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagRepository.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagRepository.kt @@ -2,7 +2,7 @@ package org.yapp.infra.readingrecordtag.repository import org.springframework.data.jpa.repository.JpaRepository import org.yapp.infra.readingrecordtag.entity.ReadingRecordTagEntity -import java.util.UUID +import java.util.* interface JpaReadingRecordTagRepository : JpaRepository, JpaReadingRecordTagQuerydslRepository { fun findByReadingRecordId(readingRecordId: UUID): List diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt index eb432296..ded1daa7 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt @@ -20,8 +20,9 @@ class JpaReadingRecordTagQuerydslRepositoryImpl( private val userBook = QUserBookEntity.userBookEntity private val tag = QTagEntity.tagEntity - override fun countTagsByUserIdAndCategories( + override fun countTagsByUserIdAndUserBookIdAndCategories( userId: UUID, + userBookId: UUID, categories: List ): Map { if (categories.isEmpty()) { @@ -36,6 +37,7 @@ class JpaReadingRecordTagQuerydslRepositoryImpl( .join(tag).on(readingRecordTag.tagId.eq(tag.id)) .where( userBook.userIdEq(userId), + readingRecord.userBookIdEq(userBookId), readingRecord.isNotDeleted(), readingRecordTag.isNotDeleted(), tag.nameIn(categories) @@ -54,6 +56,10 @@ class JpaReadingRecordTagQuerydslRepositoryImpl( return this.userId.eq(userId) } + private fun QReadingRecordEntity.userBookIdEq(userBookId: UUID): BooleanExpression { + return this.userBookId.eq(userBookId) + } + private fun QReadingRecordEntity.isNotDeleted(): BooleanExpression { return this.deletedAt.isNull } diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt index bad43986..3c165338 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt @@ -20,7 +20,7 @@ class ReadingRecordTagRepositoryImpl( return jpaReadingRecordTagRepository.findByReadingRecordId(readingRecordId).map { it.toDomain() } } - override fun countTagsByUserIdAndCategories(userId: UUID, categories: List): Map { - return jpaReadingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories) + override fun countTagsByUserIdAndUserBookIdAndCategories(userId: UUID, userBookId: UUID, categories: List): Map { + return jpaReadingRecordTagRepository.countTagsByUserIdAndUserBookIdAndCategories(userId, userBookId, categories) } }