Skip to content

Commit a902846

Browse files
authored
Book 217 feature/#73 (#74)
* [BOOK-217] feat: 내서재에 독서기록 수 기능개발 (#73) * [BOOK-217] feat: domain - 내서제 읽기 카운트 동기화 및 카운팅 기능개발(#73) * [BOOK-217] feat: infra - entity 기록 수 비정규화 (#73) * [BOOK-217] fix: apis,domain 독서기록상세에서 author 정보 추가(#73)
1 parent 63a9fd4 commit a902846

File tree

10 files changed

+63
-19
lines changed

10 files changed

+63
-19
lines changed

apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ data class UserBookResponse private constructor(
1717
val publisher: String,
1818
val createdAt: String,
1919
val updatedAt: String,
20+
val recordCount: Int,
2021
) {
2122
companion object {
2223
fun from(
@@ -33,6 +34,7 @@ data class UserBookResponse private constructor(
3334
publisher = BookDataValidator.removeParenthesesFromPublisher(userBook.publisher),
3435
createdAt = userBook.createdAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
3536
updatedAt = userBook.updatedAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
37+
recordCount = userBook.recordCount,
3638
)
3739
}
3840
}

apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/response/ReadingRecordResponse.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ data class ReadingRecordResponse private constructor(
3939
val bookPublisher: String?,
4040

4141
@Schema(description = "도서 썸네일 URL", example = "https://example.com/book-cover.jpg")
42-
val bookCoverImageUrl: String?
42+
val bookCoverImageUrl: String?,
43+
44+
@Schema(description = "저자", example = "로버트 C. 마틴")
45+
val author: String?
4346
) {
4447
companion object {
4548
private val dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
@@ -56,7 +59,8 @@ data class ReadingRecordResponse private constructor(
5659
updatedAt = readingRecordInfoVO.updatedAt.format(dateTimeFormatter),
5760
bookTitle = readingRecordInfoVO.bookTitle,
5861
bookPublisher = readingRecordInfoVO.bookPublisher,
59-
bookCoverImageUrl = readingRecordInfoVO.bookCoverImageUrl
62+
bookCoverImageUrl = readingRecordInfoVO.bookCoverImageUrl,
63+
author = readingRecordInfoVO.author
6064
)
6165
}
6266
}

domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import org.yapp.domain.userbook.UserBookRepository
1313
import org.yapp.globalutils.annotation.DomainService
1414
import java.util.UUID
1515

16+
import org.yapp.domain.userbook.exception.UserBookNotFoundException
17+
import org.yapp.domain.userbook.exception.UserBookErrorCode
18+
1619
@DomainService
1720
class ReadingRecordDomainService(
1821
private val readingRecordRepository: ReadingRecordRepository,
@@ -28,6 +31,11 @@ class ReadingRecordDomainService(
2831
review: String,
2932
emotionTags: List<String>
3033
): ReadingRecordInfoVO {
34+
val userBook = userBookRepository.findById(userBookId)
35+
?: throw UserBookNotFoundException(
36+
UserBookErrorCode.USER_BOOK_NOT_FOUND,
37+
"User book not found with id: $userBookId"
38+
)
3139

3240
val readingRecord = ReadingRecord.create(
3341
userBookId = userBookId,
@@ -50,17 +58,19 @@ class ReadingRecordDomainService(
5058
}
5159
readingRecordTagRepository.saveAll(readingRecordTags)
5260

53-
val userBook = userBookRepository.findById(userBookId)
61+
userBookRepository.save(userBook.increaseReadingRecordCount())
5462

5563
return ReadingRecordInfoVO.newInstance(
5664
readingRecord = savedReadingRecord,
5765
emotionTags = tags.map { it.name },
58-
bookTitle = userBook?.title,
59-
bookPublisher = userBook?.publisher,
60-
bookCoverImageUrl = userBook?.coverImageUrl
66+
bookTitle = userBook.title,
67+
bookPublisher = userBook.publisher,
68+
bookCoverImageUrl = userBook.coverImageUrl,
69+
author = userBook.author
6170
)
6271
}
6372

73+
6474
fun findReadingRecordById(readingRecordId: UUID): ReadingRecordInfoVO {
6575
val readingRecord = readingRecordRepository.findById(readingRecordId)
6676
?: throw ReadingRecordNotFoundException(
@@ -82,7 +92,8 @@ class ReadingRecordDomainService(
8292
emotionTags = tags.map { it.name },
8393
bookTitle = userBook?.title,
8494
bookPublisher = userBook?.publisher,
85-
bookCoverImageUrl = userBook?.coverImageUrl
95+
bookCoverImageUrl = userBook?.coverImageUrl,
96+
author = userBook?.author
8697
)
8798
}
8899

@@ -105,7 +116,8 @@ class ReadingRecordDomainService(
105116
emotionTags = tags.map { it.name },
106117
bookTitle = userBook?.title,
107118
bookPublisher = userBook?.publisher,
108-
bookCoverImageUrl = userBook?.coverImageUrl
119+
bookCoverImageUrl = userBook?.coverImageUrl,
120+
author = userBook?.author
109121
)
110122
}
111123
}

domain/src/main/kotlin/org/yapp/domain/readingrecord/vo/ReadingRecordInfoVO.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ data class ReadingRecordInfoVO private constructor(
1414
val updatedAt: LocalDateTime,
1515
val bookTitle: String? = null,
1616
val bookPublisher: String? = null,
17-
val bookCoverImageUrl: String? = null
17+
val bookCoverImageUrl: String? = null,
18+
val author: String? = null
1819
) {
1920
init {
2021
require(emotionTags.size <= 3) { "Maximum 3 emotion tags are allowed" }
@@ -29,7 +30,8 @@ data class ReadingRecordInfoVO private constructor(
2930
emotionTags: List<String>,
3031
bookTitle: String? = null,
3132
bookPublisher: String? = null,
32-
bookCoverImageUrl: String? = null
33+
bookCoverImageUrl: String? = null,
34+
author: String? = null
3335
): ReadingRecordInfoVO {
3436
return ReadingRecordInfoVO(
3537
id = readingRecord.id,
@@ -42,7 +44,8 @@ data class ReadingRecordInfoVO private constructor(
4244
updatedAt = readingRecord.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다."),
4345
bookTitle = bookTitle,
4446
bookPublisher = bookPublisher,
45-
bookCoverImageUrl = bookCoverImageUrl
47+
bookCoverImageUrl = bookCoverImageUrl,
48+
author = author
4649
)
4750
}
4851
}

domain/src/main/kotlin/org/yapp/domain/userbook/UserBook.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ data class UserBook private constructor(
1515
val title: String,
1616
val author: String,
1717
val status: BookStatus,
18+
val readingRecordCount: Int = 0,
1819
val createdAt: LocalDateTime? = null,
1920
val updatedAt: LocalDateTime? = null,
2021
val deletedAt: LocalDateTime? = null,
@@ -23,6 +24,11 @@ data class UserBook private constructor(
2324
return this.copy(status = newStatus)
2425
}
2526

27+
fun increaseReadingRecordCount(): UserBook {
28+
return this.copy(readingRecordCount = this.readingRecordCount + 1)
29+
}
30+
31+
2632
companion object {
2733
fun create(
2834
userId: UUID,
@@ -57,6 +63,7 @@ data class UserBook private constructor(
5763
title: String,
5864
author: String,
5965
status: BookStatus,
66+
readingRecordCount: Int,
6067
createdAt: LocalDateTime? = null,
6168
updatedAt: LocalDateTime? = null,
6269
deletedAt: LocalDateTime? = null
@@ -71,6 +78,7 @@ data class UserBook private constructor(
7178
title = title,
7279
author = author,
7380
status = status,
81+
readingRecordCount = readingRecordCount,
7482
createdAt = createdAt,
7583
updatedAt = updatedAt,
7684
deletedAt = deletedAt

domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class UserBookDomainService(
3535
)
3636

3737
val savedUserBook = userBookRepository.save(userBook)
38-
return UserBookInfoVO.newInstance(savedUserBook)
38+
return UserBookInfoVO.newInstance(savedUserBook, savedUserBook.readingRecordCount)
3939
}
4040

4141
fun findUserBooksByDynamicCondition(
@@ -46,20 +46,20 @@ class UserBookDomainService(
4646
pageable: Pageable
4747
): Page<UserBookInfoVO> {
4848
val page = userBookRepository.findUserBooksByDynamicCondition(userId, status, sort, title, pageable)
49-
return page.map { UserBookInfoVO.newInstance(it) }
49+
return page.map { UserBookInfoVO.newInstance(it, it.readingRecordCount) }
5050
}
5151

5252
fun findAllByUserIdAndBookIsbnIn(userId: UUID, isbns: List<String>): List<UserBookInfoVO> {
5353
if (isbns.isEmpty()) {
5454
return emptyList()
5555
}
5656
val userBooks = userBookRepository.findAllByUserIdAndBookIsbnIn(userId, isbns)
57-
return userBooks.map { UserBookInfoVO.newInstance(it) }
57+
return userBooks.map { UserBookInfoVO.newInstance(it, it.readingRecordCount) }
5858
}
5959

6060
fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBookInfoVO? {
6161
val userBook = userBookRepository.findByUserIdAndBookIsbn(userId, isbn)
62-
return userBook?.let { UserBookInfoVO.newInstance(it) }
62+
return userBook?.let { UserBookInfoVO.newInstance(it, it.readingRecordCount) }
6363
}
6464

6565
fun getUserBookStatusCounts(userId: UUID): UserBookStatusCountsVO {

domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ interface UserBookRepository {
3636
limit: Int,
3737
excludeIds: Set<UUID>
3838
): List<UserBook>
39+
40+
3941
}

domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookInfoVO.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ data class UserBookInfoVO private constructor(
1515
val author: String,
1616
val status: BookStatus,
1717
val createdAt: LocalDateTime,
18-
val updatedAt: LocalDateTime
18+
val updatedAt: LocalDateTime,
19+
val recordCount: Int
1920
) {
2021
init {
2122
require(coverImageUrl.isNotBlank()) { "표지 이미지 URL은 비어 있을 수 없습니다." }
2223
require(publisher.isNotBlank()) { "출판사는 비어 있을 수 없습니다." }
2324
require(title.isNotBlank()) { "도서 제목은 비어 있을 수 없습니다." }
2425
require(author.isNotBlank()) { "저자는 비어 있을 수 없습니다." }
26+
require(recordCount >= 0) { "독서 기록 수는 0 이상이어야 합니다." }
2527
require(!createdAt.isAfter(updatedAt)) {
2628
"생성일(createdAt)은 수정일(updatedAt)보다 이후일 수 없습니다."
2729
}
@@ -30,6 +32,7 @@ data class UserBookInfoVO private constructor(
3032
companion object {
3133
fun newInstance(
3234
userBook: UserBook,
35+
recordCount: Int
3336
): UserBookInfoVO {
3437
return UserBookInfoVO(
3538
id = userBook.id,
@@ -42,7 +45,8 @@ data class UserBookInfoVO private constructor(
4245
author = userBook.author,
4346
status = userBook.status,
4447
createdAt = userBook.createdAt ?: throw IllegalStateException("createdAt은 null일 수 없습니다."),
45-
updatedAt = userBook.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다.")
48+
updatedAt = userBook.updatedAt ?: throw IllegalStateException("updatedAt은 null일 수 없습니다."),
49+
recordCount = recordCount
4650
)
4751
}
4852
}

infra/src/main/kotlin/org/yapp/infra/userbook/entity/UserBookEntity.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ class UserBookEntity(
6666
var status: BookStatus = status
6767
protected set
6868

69+
@Column(name = "reading_record_count", nullable = false)
70+
var readingRecordCount: Int = 0
71+
protected set
72+
6973
fun toDomain(): UserBook = UserBook.reconstruct(
7074
id = UserBook.Id.newInstance(this.id),
7175
userId = UserBook.UserId.newInstance(this.userId),
@@ -76,6 +80,7 @@ class UserBookEntity(
7680
title = this.title,
7781
author = this.author,
7882
status = this.status,
83+
readingRecordCount = this.readingRecordCount,
7984
createdAt = this.createdAt,
8085
updatedAt = this.updatedAt,
8186
deletedAt = this.deletedAt
@@ -93,7 +98,9 @@ class UserBookEntity(
9398
title = userBook.title,
9499
author = userBook.author,
95100
status = userBook.status,
96-
)
101+
).apply {
102+
this.readingRecordCount = userBook.readingRecordCount
103+
}
97104
}
98105
}
99106

infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import java.util.*
1414

1515
@Repository
1616
class UserBookRepositoryImpl(
17-
private val jpaUserBookRepository: JpaUserBookRepository,
17+
private val jpaUserBookRepository: JpaUserBookRepository
1818
) : UserBookRepository {
1919

2020
override fun findByUserIdAndBookIsbn(userId: UUID, isbn: String): UserBook? {
@@ -81,4 +81,6 @@ class UserBookRepositoryImpl(
8181
val entities = jpaUserBookRepository.findUnrecordedBooksSortedByPriority(userId, excludeIds, limit)
8282
return entities.map { it.toDomain() }
8383
}
84+
85+
8486
}

0 commit comments

Comments
 (0)