From ff940c05322ef07d4747fd9f3c34ed16425872f6 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:01:15 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[BOOK-204]=20feat:=20apis=20-=20=EC=94=A8?= =?UTF-8?q?=EC=95=97=20=ED=86=B5=EA=B3=84=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SeedController 및 SeedControllerApi 클래스 추가 * 사용자 인증을 통한 씨앗 통계 조회 기능 구현 * Swagger 문서화 주석 추가 --- .../apis/seed/controller/SeedController.kt | 23 +++++++++++ .../apis/seed/controller/SeedControllerApi.kt | 41 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedController.kt create mode 100644 apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedControllerApi.kt 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 new file mode 100644 index 00000000..4cafbbc9 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedController.kt @@ -0,0 +1,23 @@ +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 new file mode 100644 index 00000000..e0d2c4f1 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/seed/controller/SeedControllerApi.kt @@ -0,0 +1,41 @@ +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 From dd0eb2a9c90ab4c45d4a8129e1a019d16c0b4814 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:01:30 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[BOOK-204]=20chore:=20apis=20-=20BookUseCas?= =?UTF-8?q?e=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=EC=84=9C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=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 --- apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt | 2 -- 1 file changed, 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 85868587..2d273923 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,10 +27,8 @@ import java.util.UUID @UseCase @Transactional(readOnly = true) class BookUseCase( - @Qualifier(BookQueryServiceQualifier.ALADIN) private val bookQueryService: BookQueryService, - private val userAuthService: UserAuthService, private val userBookService: UserBookService, private val bookManagementService: BookManagementService From 9b16c0a1a79fed5efb48558b64cad242df65ee9b Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:01:40 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[BOOK-204]=20chore:=20apis=20-=20ReadingRec?= =?UTF-8?q?ordUseCase=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=EB=AC=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=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 --- .../apis/readingrecord/usecase/ReadingRecordUseCase.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 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 0c4e4a9f..749f9e75 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 @@ -3,7 +3,6 @@ package org.yapp.apis.readingrecord.usecase import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional -import org.springframework.beans.factory.annotation.Qualifier import org.yapp.apis.auth.service.UserAuthService import org.yapp.apis.book.service.UserBookService import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest @@ -11,10 +10,7 @@ import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse import org.yapp.apis.readingrecord.service.ReadingRecordService import org.yapp.domain.readingrecord.ReadingRecordSortType import org.yapp.globalutils.annotation.UseCase -import org.yapp.apis.book.constant.BookQueryServiceQualifier -import org.yapp.apis.book.service.BookQueryService -import org.yapp.domain.book.BookDomainService -import java.util.UUID +import java.util.* @UseCase @Transactional(readOnly = true) @@ -22,9 +18,6 @@ class ReadingRecordUseCase( private val readingRecordService: ReadingRecordService, private val userAuthService: UserAuthService, private val userBookService: UserBookService, - @Qualifier(BookQueryServiceQualifier.ALADIN) - private val bookQueryService: BookQueryService, - private val bookDomainService: BookDomainService ) { @Transactional fun createReadingRecord( From 8f3b55d499e9de0e108bf59796d4c3533724becf Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:03:16 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[BOOK-204]=20feat:=20apis=20-=20SeedUseCase?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=94=A8=EC=95=97=20=ED=86=B5=EA=B3=84=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=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 --- .../org/yapp/apis/seed/usecase/SeedUseCase.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt 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 new file mode 100644 index 00000000..fb78ae60 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt @@ -0,0 +1,20 @@ +package org.yapp.apis.seed.usecase + +import org.springframework.transaction.annotation.Transactional +import org.yapp.apis.auth.service.UserAuthService +import org.yapp.apis.seed.dto.response.SeedStatsResponse +import org.yapp.apis.seed.service.SeedService +import org.yapp.globalutils.annotation.UseCase +import java.util.* + +@UseCase +@Transactional(readOnly = true) +class SeedUseCase( + private val userAuthService: UserAuthService, + private val seedService: SeedService +) { + fun getSeedStats(userId: UUID): SeedStatsResponse { + userAuthService.validateUserExists(userId) + return seedService.getSeedStatsByUserId(userId) + } +} From 0ab3e8fab9fe66bafcb030123a0dc5475a67af51 Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:03:50 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[BOOK-204]=20feat:=20domain,=20apis=20-=20S?= =?UTF-8?q?eedService=20=EB=B0=8F=20ReadingRecordTagDomainService=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=ED=86=B5=EA=B3=84=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/yapp/apis/seed/service/SeedService.kt | 21 ++++++++++++++ .../ReadingRecordTagDomainService.kt | 15 ++++++++++ .../domain/readingrecordtag/vo/TagStatsVO.kt | 28 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt create mode 100644 domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt create mode 100644 domain/src/main/kotlin/org/yapp/domain/readingrecordtag/vo/TagStatsVO.kt diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt b/apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt new file mode 100644 index 00000000..aab6358e --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/seed/service/SeedService.kt @@ -0,0 +1,21 @@ +package org.yapp.apis.seed.service + +import org.springframework.stereotype.Service +import org.yapp.apis.seed.dto.response.SeedStatsResponse +import org.yapp.domain.readingrecordtag.ReadingRecordTagDomainService +import org.yapp.globalutils.tag.GeneralEmotionTagCategory +import java.util.* + +@Service +class SeedService( + private val readingRecordTagDomainService: ReadingRecordTagDomainService +) { + fun getSeedStatsByUserId(userId: UUID): SeedStatsResponse { + val tagStatsVO = readingRecordTagDomainService.countTagsByUserIdAndCategories( + userId = userId, + categories = GeneralEmotionTagCategory.entries.map { it.displayName } + ) + + return SeedStatsResponse.from(tagStatsVO) + } +} diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt new file mode 100644 index 00000000..9f71507c --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagDomainService.kt @@ -0,0 +1,15 @@ +package org.yapp.domain.readingrecordtag + +import org.yapp.domain.readingrecordtag.vo.TagStatsVO +import org.yapp.globalutils.annotation.DomainService +import java.util.* + +@DomainService +class ReadingRecordTagDomainService( + private val readingRecordTagRepository: ReadingRecordTagRepository +) { + fun countTagsByUserIdAndCategories(userId: UUID, categories: List): TagStatsVO { + val categoryStats = readingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories) + return TagStatsVO.newInstance(categoryStats) + } +} diff --git a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/vo/TagStatsVO.kt b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/vo/TagStatsVO.kt new file mode 100644 index 00000000..8c82f679 --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/vo/TagStatsVO.kt @@ -0,0 +1,28 @@ +package org.yapp.domain.readingrecordtag.vo + +import org.yapp.globalutils.tag.GeneralEmotionTagCategory +import java.util.EnumMap + +data class TagStatsVO private constructor( + val categoryStats: EnumMap +) { + init { + categoryStats.values.forEach { count -> + require(count >= 0) { "태그 개수는 0 이상이어야 합니다." } + } + } + + companion object { + fun newInstance(categoryStats: Map): TagStatsVO { + val enumStats = EnumMap(GeneralEmotionTagCategory::class.java) + + categoryStats.forEach { (displayName, count) -> + GeneralEmotionTagCategory.fromDisplayName(displayName)?.let { enumCategory -> + enumStats[enumCategory] = count + } + } + + return TagStatsVO(enumStats) + } + } +} From c1f4c1d08f374ed97c6dc46aae37c90ee616f7ef Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:04:34 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[BOOK-204]=20feat:=20domain,=20infra=20-=20?= =?UTF-8?q?ReadingRecordTagRepository=EC=97=90=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=ED=86=B5=EA=B3=84=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EA=B5=AC=ED=98=84=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 --- .../ReadingRecordTagRepository.kt | 1 + .../JpaReadingRecordTagQuerydslRepository.kt | 7 ++ .../JpaReadingRecordTagRepository.kt | 2 +- ...aReadingRecordTagQuerydslRepositoryImpl.kt | 68 +++++++++++++++++++ .../ReadingRecordTagRepositoryImpl.kt | 12 +++- 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt create mode 100644 infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt rename infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/{ => impl}/ReadingRecordTagRepositoryImpl.kt (63%) 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 0adf7682..985efb22 100644 --- a/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/readingrecordtag/ReadingRecordTagRepository.kt @@ -5,4 +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 } 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 new file mode 100644 index 00000000..f22bfb81 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagQuerydslRepository.kt @@ -0,0 +1,7 @@ +package org.yapp.infra.readingrecordtag.repository + +import java.util.* + +interface JpaReadingRecordTagQuerydslRepository { + fun countTagsByUserIdAndCategories(userId: UUID, categories: List): Map +} \ No newline at end of file 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 18045acd..b316c33d 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 @@ -4,6 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository import org.yapp.infra.readingrecordtag.entity.ReadingRecordTagEntity import java.util.UUID -interface JpaReadingRecordTagRepository : JpaRepository { +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 new file mode 100644 index 00000000..eb432296 --- /dev/null +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/JpaReadingRecordTagQuerydslRepositoryImpl.kt @@ -0,0 +1,68 @@ +package org.yapp.infra.readingrecordtag.repository.impl + +import com.querydsl.core.types.dsl.BooleanExpression +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.stereotype.Repository +import org.yapp.infra.readingrecord.entity.QReadingRecordEntity +import org.yapp.infra.readingrecordtag.entity.QReadingRecordTagEntity +import org.yapp.infra.readingrecordtag.repository.JpaReadingRecordTagQuerydslRepository +import org.yapp.infra.tag.entity.QTagEntity +import org.yapp.infra.userbook.entity.QUserBookEntity +import java.util.* + +@Repository +class JpaReadingRecordTagQuerydslRepositoryImpl( + private val queryFactory: JPAQueryFactory +) : JpaReadingRecordTagQuerydslRepository { + + private val readingRecordTag = QReadingRecordTagEntity.readingRecordTagEntity + private val readingRecord = QReadingRecordEntity.readingRecordEntity + private val userBook = QUserBookEntity.userBookEntity + private val tag = QTagEntity.tagEntity + + override fun countTagsByUserIdAndCategories( + userId: UUID, + categories: List + ): Map { + if (categories.isEmpty()) { + return emptyMap() + } + + val results = queryFactory + .select(tag.name, readingRecordTag.count()) + .from(readingRecordTag) + .join(readingRecord).on(readingRecordTag.readingRecordId.eq(readingRecord.id)) + .join(userBook).on(readingRecord.userBookId.eq(userBook.id)) + .join(tag).on(readingRecordTag.tagId.eq(tag.id)) + .where( + userBook.userIdEq(userId), + readingRecord.isNotDeleted(), + readingRecordTag.isNotDeleted(), + tag.nameIn(categories) + ) + .groupBy(tag.name) + .fetch() + + return results.associate { tuple -> + val tagName = tuple[tag.name] ?: "" + val count = tuple[readingRecordTag.count()]?.toInt() ?: 0 + tagName to count + } + } + + private fun QUserBookEntity.userIdEq(userId: UUID): BooleanExpression { + return this.userId.eq(userId) + } + + private fun QReadingRecordEntity.isNotDeleted(): BooleanExpression { + return this.deletedAt.isNull + } + + private fun QReadingRecordTagEntity.isNotDeleted(): BooleanExpression { + return this.deletedAt.isNull + } + + private fun QTagEntity.nameIn(categories: List): BooleanExpression { + return this.name.`in`(categories) + } +} diff --git a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/ReadingRecordTagRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt similarity index 63% rename from infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/ReadingRecordTagRepositoryImpl.kt rename to infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt index 58e29896..bad43986 100644 --- a/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/ReadingRecordTagRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/impl/ReadingRecordTagRepositoryImpl.kt @@ -1,9 +1,11 @@ -package org.yapp.infra.readingrecordtag.repository +package org.yapp.infra.readingrecordtag.repository.impl import org.springframework.stereotype.Repository import org.yapp.domain.readingrecordtag.ReadingRecordTag import org.yapp.domain.readingrecordtag.ReadingRecordTagRepository import org.yapp.infra.readingrecordtag.entity.ReadingRecordTagEntity +import org.yapp.infra.readingrecordtag.repository.JpaReadingRecordTagRepository +import java.util.* @Repository class ReadingRecordTagRepositoryImpl( @@ -14,7 +16,11 @@ class ReadingRecordTagRepositoryImpl( return jpaReadingRecordTagRepository.saveAll(entities).map { it.toDomain() } } - override fun findByReadingRecordId(readingRecordId: java.util.UUID): List { + override fun findByReadingRecordId(readingRecordId: UUID): List { return jpaReadingRecordTagRepository.findByReadingRecordId(readingRecordId).map { it.toDomain() } } -} \ No newline at end of file + + override fun countTagsByUserIdAndCategories(userId: UUID, categories: List): Map { + return jpaReadingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories) + } +} From fffcddde89a929e5144540e20fc4889a6321f42f Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:04:43 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[BOOK-204]=20feat:=20global-utils=20-=20?= =?UTF-8?q?=EC=9D=BC=EB=B0=98=20=EA=B0=90=EC=A0=95=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=97=B4=EA=B1=B0?= =?UTF-8?q?=ED=98=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tag/GeneralEmotionTagCategory.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt 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 new file mode 100644 index 00000000..2714ed7b --- /dev/null +++ b/global-utils/src/main/kotlin/org/yapp/globalutils/tag/GeneralEmotionTagCategory.kt @@ -0,0 +1,18 @@ +package org.yapp.globalutils.tag + +enum class GeneralEmotionTagCategory( + val displayName: String +) { + WARMTH("따뜻함"), + JOY("즐거움"), + TENSION("긴장감"), + SADNESS("슬픔"); + + companion object { + private val BY_DISPLAY_NAME = entries.associateBy { it.displayName } + + fun fromDisplayName(displayName: String): GeneralEmotionTagCategory? { + return BY_DISPLAY_NAME[displayName] + } + } +} From a68002e05fda966b94a64170831fddaa950a2ddf Mon Sep 17 00:00:00 2001 From: DongHoon Lee Date: Fri, 1 Aug 2025 21:04:50 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[BOOK-204]=20feat:=20apis=20-=20=EC=94=A8?= =?UTF-8?q?=EC=95=97=20=ED=86=B5=EA=B3=84=20=EC=9D=91=EB=8B=B5=20DTO=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 --- .../seed/dto/response/SeedStatsResponse.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt b/apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt new file mode 100644 index 00000000..63f9df3c --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/seed/dto/response/SeedStatsResponse.kt @@ -0,0 +1,31 @@ +package org.yapp.apis.seed.dto.response + +import org.yapp.domain.readingrecordtag.vo.TagStatsVO +import org.yapp.globalutils.tag.GeneralEmotionTagCategory + +data class SeedStatsResponse private constructor( + val categories: List +) { + data class SeedCategoryStats private constructor( + val name: String, + val count: Int + ) { + companion object { + fun of(name: String, count: Int): SeedCategoryStats { + return SeedCategoryStats(name, count) + } + } + } + + companion object { + fun from(tagStatsVO: TagStatsVO): SeedStatsResponse { + val categories = GeneralEmotionTagCategory.entries.map { category -> + SeedCategoryStats.of( + name = category.displayName, + count = tagStatsVO.categoryStats.getOrDefault(category, 0) + ) + } + return SeedStatsResponse(categories) + } + } +}