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
@@ -0,0 +1,19 @@
package org.yapp.apis.emotion.dto.response

import io.swagger.v3.oas.annotations.media.Schema
import java.util.UUID

@Schema(name = "EmotionDetailDto", description = "세부 감정")
data class EmotionDetailDto private constructor(
@field:Schema(description = "세부 감정 ID", example = "123e4567-e89b-12d3-a456-426614174000")
val id: UUID,

@field:Schema(description = "세부 감정 이름", example = "설레는")
val name: String
) {
companion object {
fun of(id: UUID, name: String): EmotionDetailDto {
return EmotionDetailDto(id = id, name = name)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,4 @@ data class EmotionListResponse private constructor(
}
}

@Schema(name = "EmotionDetailDto", description = "세부 감정")
data class EmotionDetailDto private constructor(
@field:Schema(description = "세부 감정 ID", example = "123e4567-e89b-12d3-a456-426614174000")
val id: UUID,

@field:Schema(description = "세부 감정 이름", example = "설레는")
val name: String
) {
companion object {
fun of(id: UUID, name: String): EmotionDetailDto {
return EmotionDetailDto(id = id, name = name)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ 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
Expand All @@ -18,6 +17,7 @@ import org.springframework.web.bind.annotation.*
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.request.UpdateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponseV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordsWithPrimaryEmotionResponse
import org.yapp.domain.readingrecord.ReadingRecordSortType
import org.yapp.globalutils.exception.ErrorResponse
import java.util.*
Expand Down Expand Up @@ -116,7 +116,7 @@ interface ReadingRecordControllerApiV2 {
) sort: ReadingRecordSortType?,
@PageableDefault(size = 10, sort = ["updatedAt"], direction = Sort.Direction.DESC)
@Parameter(description = "페이지네이션 정보 (기본값: 10개). 정렬은 sort 파라미터로 제어되며, Pageable의 sort는 무시됩니다.") pageable: Pageable
): ResponseEntity<Page<ReadingRecordResponseV2>>
): ResponseEntity<ReadingRecordsWithPrimaryEmotionResponse>

@Operation(
summary = "독서 기록 수정 (V2)",
Expand Down Expand Up @@ -149,7 +149,7 @@ interface ReadingRecordControllerApiV2 {
): ResponseEntity<ReadingRecordResponseV2>

@Operation(
summary = "독서 기록 삭제",
summary = "독서 기록 삭제 (V2)",
description = "독서 기록을 삭제합니다."
)
@ApiResponses(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.yapp.apis.readingrecord.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
Expand All @@ -12,6 +11,7 @@ import org.springframework.web.bind.annotation.*
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.request.UpdateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponseV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordsWithPrimaryEmotionResponse
import org.yapp.apis.readingrecord.usecase.ReadingRecordUseCaseV2
import org.yapp.domain.readingrecord.ReadingRecordSortType
import java.util.UUID
Expand Down Expand Up @@ -55,7 +55,7 @@ class ReadingRecordControllerV2(
@RequestParam(required = false) sort: ReadingRecordSortType?,
@PageableDefault(size = 10, sort = ["updatedAt"], direction = Sort.Direction.DESC)
pageable: Pageable
): ResponseEntity<Page<ReadingRecordResponseV2>> {
): ResponseEntity<ReadingRecordsWithPrimaryEmotionResponse> {
val response = readingRecordUseCaseV2.getReadingRecordsByUserBookId(
userId = userId,
userBookId = userBookId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.yapp.apis.readingrecord.dto.response

import io.swagger.v3.oas.annotations.media.Schema

@Schema(name = "PrimaryEmotionDto", description = "대분류 감정")
data class PrimaryEmotionDto private constructor(
@field:Schema(description = "감정 코드", example = "JOY")
val code: String,

@field:Schema(description = "감정 표시 이름", example = "즐거움")
val displayName: String
) {
companion object {
fun of(code: String, displayName: String): PrimaryEmotionDto {
return PrimaryEmotionDto(code = code, displayName = displayName)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.yapp.apis.readingrecord.dto.response

import io.swagger.v3.oas.annotations.media.Schema
import org.springframework.data.domain.Page

@Schema(
name = "ReadingRecordsWithPrimaryEmotionResponse",
description = "독서 기록 목록과 대표 감정 응답"
)
data class ReadingRecordsWithPrimaryEmotionResponse private constructor(
@field:Schema(description = "해당 책의 대표(최다) 감정")
val primaryEmotion: PrimaryEmotionDto?,

@field:Schema(description = "독서 기록 목록 (페이징)")
val records: Page<ReadingRecordResponseV2>
) {
companion object {
fun of(
primaryEmotion: PrimaryEmotionDto?,
records: Page<ReadingRecordResponseV2>
): ReadingRecordsWithPrimaryEmotionResponse {
return ReadingRecordsWithPrimaryEmotionResponse(
primaryEmotion = primaryEmotion,
records = records
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import org.springframework.data.domain.Pageable
import org.springframework.transaction.annotation.Transactional
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.request.UpdateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.response.PrimaryEmotionDto
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponseV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordsWithPrimaryEmotionResponse
import org.yapp.domain.detailtag.DetailTagDomainService
import org.yapp.domain.readingrecord.PrimaryEmotion
import org.yapp.domain.readingrecord.ReadingRecord
Expand All @@ -14,6 +16,7 @@ import org.yapp.domain.readingrecord.ReadingRecordSortType
import org.yapp.domain.readingrecord.vo.ReadingRecordInfoVO
import org.yapp.domain.readingrecorddetailtag.ReadingRecordDetailTagDomainService
import org.yapp.domain.user.UserDomainService
import org.yapp.domain.userbook.UserBookDomainService
import org.yapp.globalutils.annotation.ApplicationService
import java.util.*

Expand All @@ -22,7 +25,8 @@ class ReadingRecordServiceV2(
private val readingRecordDomainService: ReadingRecordDomainService,
private val detailTagDomainService: DetailTagDomainService,
private val readingRecordDetailTagDomainService: ReadingRecordDetailTagDomainService,
private val userDomainService: UserDomainService
private val userDomainService: UserDomainService,
private val userBookDomainService: UserBookDomainService
) {
@Transactional
fun createReadingRecord(
Expand Down Expand Up @@ -73,36 +77,58 @@ class ReadingRecordServiceV2(
userBookId: UUID,
sort: ReadingRecordSortType?,
pageable: Pageable
): Page<ReadingRecordResponseV2> {
): ReadingRecordsWithPrimaryEmotionResponse {
val primaryEmotion = readingRecordDomainService.findPrimaryEmotionByUserBookId(userBookId)
val primaryEmotionDto = toPrimaryEmotionDto(primaryEmotion)

val readingRecordPage = readingRecordDomainService.findByDynamicCondition(userBookId, sort, pageable)
if (readingRecordPage.isEmpty) {
return Page.empty(pageable)
return ReadingRecordsWithPrimaryEmotionResponse.of(
primaryEmotion = primaryEmotionDto,
records = Page.empty(pageable)
)
}

val readingRecords = readingRecordPage.content.toList()
val readingRecordIds = readingRecords.map { it.id.value }
val readingRecordIds = readingRecordPage.content.map { it.id.value }
val detailTagsMap = buildDetailTagsMap(readingRecordIds)
val recordsPage = toResponsePage(readingRecordPage, detailTagsMap)

return ReadingRecordsWithPrimaryEmotionResponse.of(
primaryEmotion = primaryEmotionDto,
records = recordsPage
)
}

private fun toPrimaryEmotionDto(primaryEmotion: PrimaryEmotion?): PrimaryEmotionDto? =
primaryEmotion?.let { PrimaryEmotionDto.of(code = it.name, displayName = it.displayName) }

// Fetch detail tags
val readingRecordDetailTags = readingRecordDetailTagDomainService.findByReadingRecordIdIn(readingRecordIds)
val detailTagIds = readingRecordDetailTags.map { it.detailTagId.value }.distinct()
val detailTagsById = detailTagDomainService.findAllById(detailTagIds).associateBy { it.id.value }
private fun buildDetailTagsMap(readingRecordIds: List<UUID>): Map<UUID, List<ReadingRecordInfoVO.DetailEmotionInfo>> {
val detailTags = readingRecordDetailTagDomainService.findByReadingRecordIdIn(readingRecordIds)
val tagLookup = detailTagDomainService
.findAllById(detailTags.map { it.detailTagId.value }.distinct())
.associateBy { it.id.value }

val detailTagsByReadingRecordId = readingRecordDetailTags
return detailTags
.groupBy { it.readingRecordId.value }
.mapValues { (_, tags) ->
tags.mapNotNull { detailTagsById[it.detailTagId.value] }
.map { ReadingRecordInfoVO.DetailEmotionInfo(it.id.value, it.name) }
tags.mapNotNull { tag ->
tagLookup[tag.detailTagId.value]?.let {
ReadingRecordInfoVO.DetailEmotionInfo(it.id.value, it.name)
}
}
}
}

return readingRecordPage.map { readingRecord ->
val detailEmotions = detailTagsByReadingRecordId[readingRecord.id.value] ?: emptyList()
ReadingRecordResponseV2.from(
ReadingRecordInfoVO.newInstance(
readingRecord = readingRecord,
detailEmotions = detailEmotions
)
private fun toResponsePage(
readingRecordPage: Page<ReadingRecord>,
detailTagsByRecordId: Map<UUID, List<ReadingRecordInfoVO.DetailEmotionInfo>>
): Page<ReadingRecordResponseV2> = readingRecordPage.map { record ->
ReadingRecordResponseV2.from(
ReadingRecordInfoVO.newInstance(
readingRecord = record,
detailEmotions = detailTagsByRecordId[record.id.value] ?: emptyList()
)
}
)
}

@Transactional
Expand Down Expand Up @@ -206,10 +232,16 @@ class ReadingRecordServiceV2(
emptyList()
}

val userBook = userBookDomainService.findById(readingRecord.userBookId.value)

return ReadingRecordResponseV2.from(
ReadingRecordInfoVO.newInstance(
readingRecord = readingRecord,
detailEmotions = detailEmotions
detailEmotions = detailEmotions,
bookTitle = userBook?.title,
bookPublisher = userBook?.publisher,
bookCoverImageUrl = userBook?.coverImageUrl,
author = userBook?.author
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package org.yapp.apis.readingrecord.usecase

import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

import org.yapp.apis.book.service.UserBookService
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.request.UpdateReadingRecordRequestV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponseV2
import org.yapp.apis.readingrecord.dto.response.ReadingRecordsWithPrimaryEmotionResponse
import org.yapp.apis.readingrecord.service.ReadingRecordServiceV2
import org.yapp.apis.user.service.UserService
import org.yapp.domain.readingrecord.ReadingRecordSortType
Expand Down Expand Up @@ -52,7 +51,7 @@ class ReadingRecordUseCaseV2(
userBookId: UUID,
sort: ReadingRecordSortType?,
pageable: Pageable
): Page<ReadingRecordResponseV2> {
): ReadingRecordsWithPrimaryEmotionResponse {
userService.validateUserExists(userId)
userBookService.validateUserBookExists(userBookId, userId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ data class ReadingRecord private constructor(
emotionTags: List<String>?
): ReadingRecord {
return this.copy(
pageNumber = pageNumber?.let { PageNumber.newInstance(it) } ?: this.pageNumber,
pageNumber = pageNumber?.let { PageNumber.newInstance(it) },
quote = quote?.let { Quote.newInstance(it) } ?: this.quote,
review = if (review != null) Review.newInstance(review) else this.review,
review = review?.let { Review.newInstance(it) },
primaryEmotion = primaryEmotion ?: this.primaryEmotion,
emotionTags = emotionTags?.map { EmotionTag.newInstance(it) } ?: this.emotionTags,
updatedAt = LocalDateTime.now()
Expand Down
Loading