Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
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

interface WithdrawStrategy {

fun getProviderType(): ProviderType

fun withdraw(request: WithdrawStrategyRequest)
fun withdraw(@Valid request: WithdrawStrategyRequest)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.yapp.apis.book.service

import jakarta.validation.Valid
import org.yapp.apis.book.dto.request.BookDetailRequest
import org.yapp.apis.book.dto.request.BookSearchRequest
import org.yapp.apis.book.dto.response.BookDetailResponse
import org.yapp.apis.book.dto.response.BookSearchResponse

sealed interface BookQueryService {
fun searchBooks(request: BookSearchRequest): BookSearchResponse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

일관성을 위한 검토 제안

searchBooks 메서드에도 동일한 검증 패턴을 적용하는 것을 고려해보시기 바랍니다. 현재 getBookDetail에만 @Valid 어노테이션이 적용되어 있어 일관성 측면에서 검토가 필요할 수 있습니다.

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt at line
10, the searchBooks method lacks the @Valid annotation used in getBookDetail,
causing inconsistency in validation. Add the @Valid annotation to the
searchBooks method parameter to ensure consistent validation behavior across
methods.

fun getBookDetail(request: BookDetailRequest): BookDetailResponse
fun getBookDetail(@Valid request: BookDetailRequest): BookDetailResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines 7 to 11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

와일드카드(*) import 사용 자제 권고

org.yapp.apis.book.dto.request.* 와일드카드 import 는 IDE 설정에 따라 자동 적용될 수 있지만,

  1. 클래스 충돌 가능성 증가, 2) 읽기/리뷰 시 실제 사용 타입을 한눈에 파악하기 어렵다는 단점이 있습니다.
    현재 파일에서 사용되는 Request DTO 들이 5-6개 수준이므로 명시적 import 로 유지해도 가독성·유지보수성이 충분히 확보됩니다.
-import org.yapp.apis.book.dto.request.*
+import org.yapp.apis.book.dto.request.BookCreateRequest
+import org.yapp.apis.book.dto.request.BookDetailRequest
+import org.yapp.apis.book.dto.request.BookSearchRequest
+import org.yapp.apis.book.dto.request.UpsertUserBookRequest
+import org.yapp.apis.book.dto.request.UserBookRegisterRequest
+import org.yapp.apis.book.dto.request.UserBooksByIsbn13sRequest

반대로 BookSearchResponse.* 대신 BookSummary 단일 import 로 구체화한 변경은 👍 긍정적입니다.
필요하다면 IDE 설정(“Add import on demand”) 기준치를 상향해 와일드카드 자동 전환을 방지하는 것도 고려해 주세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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.request.*
+import org.yapp.apis.book.dto.request.BookCreateRequest
+import org.yapp.apis.book.dto.request.BookDetailRequest
+import org.yapp.apis.book.dto.request.BookSearchRequest
+import org.yapp.apis.book.dto.request.UpsertUserBookRequest
+import org.yapp.apis.book.dto.request.UserBookRegisterRequest
+import org.yapp.apis.book.dto.request.UserBooksByIsbn13sRequest
import org.yapp.apis.book.dto.response.BookDetailResponse
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
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt around lines 7
to 11, replace the wildcard import 'org.yapp.apis.book.dto.request.*' with
explicit imports of only the Request DTO classes actually used in this file.
This avoids potential class conflicts and improves code readability by making
dependencies clear. Review the file to identify all used Request DTOs and import
them individually instead of using the wildcard.

import org.yapp.apis.book.dto.response.UserBookResponse
import org.yapp.apis.book.service.BookManagementService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -69,4 +70,13 @@ class ReadingRecordController(
)
return ResponseEntity.ok(response)
}

@GetMapping("/{userBookId}/seed/stats")
override fun getReadingRecordSeedStats(
@AuthenticationPrincipal userId: UUID,
@PathVariable userBookId: UUID
): ResponseEntity<SeedStatsResponse> {
val stats = readingRecordUseCase.getSeedStats(userId, userBookId)
return ResponseEntity.ok(stats)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Import 문 개선사항을 확인해주세요.

wildcard import로 변경되었는데, 사용하지 않는 annotation들이 많이 import될 가능성이 있습니다. 명시적 import가 더 나을 수 있습니다.

🤖 Prompt for AI Agents
In
apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt
at line 17, the import statement uses a wildcard import for Spring web
annotations, which may import unused annotations unnecessarily. Replace the
wildcard import with explicit imports of only the annotations actually used in
this file to improve clarity and reduce unnecessary imports.

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.*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Import 문 일관성 확인

java.util.* wildcard import로 변경되었는데, UUID만 사용한다면 명시적 import가 더 적절할 수 있습니다.

🤖 Prompt for AI Agents
In
apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt
at line 23, the import statement uses a wildcard import for java.util.*, but
only UUID is used. Replace the wildcard import with an explicit import of
java.util.UUID to maintain import consistency and clarity.


@Tag(name = "Reading Records", description = "독서 기록 관련 API")
@RequestMapping("/api/v1/reading-records")
Expand Down Expand Up @@ -110,4 +106,28 @@ interface ReadingRecordControllerApi {
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
@Parameter(description = "페이지네이션 정보 (페이지 번호, 페이지 크기, 정렬 방식)") pageable: Pageable
): ResponseEntity<Page<ReadingRecordResponse>>

@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<SeedStatsResponse>
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,7 +16,6 @@ import java.util.*
class ReadingRecordService(
private val readingRecordDomainService: ReadingRecordDomainService,
) {

fun createReadingRecord(
userId: UUID,
userBookId: UUID,
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
) {
Expand Down Expand Up @@ -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)
}
}

This file was deleted.

This file was deleted.

20 changes: 0 additions & 20 deletions apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import java.util.*
class ReadingRecordTagDomainService(
private val readingRecordTagRepository: ReadingRecordTagRepository
) {
fun countTagsByUserIdAndCategories(userId: UUID, categories: List<String>): TagStatsVO {
val categoryStats = readingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories)
fun countTagsByUserIdAndUserBookIdAndCategories(
userId: UUID,
userBookId: UUID,
categories: List<String>
): TagStatsVO {
val categoryStats = readingRecordTagRepository.countTagsByUserIdAndUserBookIdAndCategories(userId, userBookId, categories)
return TagStatsVO.newInstance(categoryStats)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import java.util.UUID
interface ReadingRecordTagRepository {
fun saveAll(readingRecordTags: List<ReadingRecordTag>): List<ReadingRecordTag>
fun findByReadingRecordId(readingRecordId: UUID): List<ReadingRecordTag>
fun countTagsByUserIdAndCategories(userId: UUID, categories: List<String>): Map<String, Int>
fun countTagsByUserIdAndUserBookIdAndCategories(userId: UUID, userBookId: UUID, categories: List<String>): Map<String, Int>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ enum class GeneralEmotionTagCategory(
) {
WARMTH("따뜻함"),
JOY("즐거움"),
TENSION("긴장감"),
SADNESS("슬픔");
SADNESS("슬픔"),
REALIZATION("깨달음");

companion object {
private val BY_DISPLAY_NAME = entries.associateBy { it.displayName }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ package org.yapp.infra.readingrecordtag.repository
import java.util.*

interface JpaReadingRecordTagQuerydslRepository {
fun countTagsByUserIdAndCategories(userId: UUID, categories: List<String>): Map<String, Int>
}
fun countTagsByUserIdAndUserBookIdAndCategories(
userId: UUID,
userBookId: UUID,
categories: List<String>
): Map<String, Int>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

와일드카드 임포트보다는 명시적 임포트 사용을 권장합니다.

java.util.* 와일드카드 임포트보다는 java.util.UUID처럼 명시적 임포트를 사용하는 것이 좋습니다. 명시적 임포트가 의존성을 명확하게 하고, 네이밍 충돌을 방지하며, 코드 가독성을 향상시킵니다.

-import java.util.*
+import java.util.UUID
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import java.util.*
-import java.util.*
+import java.util.UUID
🤖 Prompt for AI Agents
In
infra/src/main/kotlin/org/yapp/infra/readingrecordtag/repository/JpaReadingRecordTagRepository.kt
at line 5, replace the wildcard import 'import java.util.*' with an explicit
import of only the needed classes, such as 'import java.util.UUID'. This change
clarifies dependencies, prevents naming conflicts, and improves code
readability.


interface JpaReadingRecordTagRepository : JpaRepository<ReadingRecordTagEntity, UUID>, JpaReadingRecordTagQuerydslRepository {
fun findByReadingRecordId(readingRecordId: UUID): List<ReadingRecordTagEntity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>
): Map<String, Int> {
if (categories.isEmpty()) {
Expand All @@ -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)
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ReadingRecordTagRepositoryImpl(
return jpaReadingRecordTagRepository.findByReadingRecordId(readingRecordId).map { it.toDomain() }
}

override fun countTagsByUserIdAndCategories(userId: UUID, categories: List<String>): Map<String, Int> {
return jpaReadingRecordTagRepository.countTagsByUserIdAndCategories(userId, categories)
override fun countTagsByUserIdAndUserBookIdAndCategories(userId: UUID, userBookId: UUID, categories: List<String>): Map<String, Int> {
return jpaReadingRecordTagRepository.countTagsByUserIdAndUserBookIdAndCategories(userId, userBookId, categories)
}
}