From 160071102e32710b857bbbed7a35c2f0e60c95a5 Mon Sep 17 00:00:00 2001 From: Jimin Lim <50178026+jimin3263@users.noreply.github.com> Date: Mon, 5 Aug 2024 00:42:19 +0900 Subject: [PATCH] =?UTF-8?q?[feat=20#55]=20=EC=95=88=EC=9D=BD=EC=9D=8C=20?= =?UTF-8?q?=EC=BB=A8=ED=85=90=EC=B8=A0=20api=20=EC=B6=94=EA=B0=80=20=20(#6?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 오탈자 수정 * rename: 폴더 구조 변경 * feat: 즐겨찾기 리마인드 api * fix: 필요없는 코드 제거 * fix: domain 임시 모킹 * rename: 폴더구조 변경 * refactor: 주석제거 (#53) * feat: 페이징 처리 * feat: response 칼럼추가 * feat: response 칼럼 추가 * feat: domain 의존 분리 * feat: 읽지 않음 컨텐츠 조회 --- .../com/pokit/content/ContentController.kt | 7 ++-- .../com/pokit/content/RemindController.kt | 16 +++++++++ .../content/dto/response/ContentsResponse.kt | 35 +++++++++++++++++++ .../content/impl/ContentAdapter.kt | 7 ++-- .../pokit/content/port/in/ContentUseCase.kt | 6 ++-- .../com/pokit/content/port/out/ContentPort.kt | 4 +-- .../content/port/service/ContentService.kt | 26 +++++++++++--- .../ContentsResult.kt} | 19 +++++----- .../dto/response/RemindContentResult.kt | 15 +++++++- 9 files changed, 108 insertions(+), 27 deletions(-) create mode 100644 adapters/in-web/src/main/kotlin/com/pokit/content/dto/response/ContentsResponse.kt rename domain/src/main/kotlin/com/pokit/content/dto/{ContentsResponse.kt => response/ContentsResult.kt} (65%) diff --git a/adapters/in-web/src/main/kotlin/com/pokit/content/ContentController.kt b/adapters/in-web/src/main/kotlin/com/pokit/content/ContentController.kt index 7f796ef2..5c8c5412 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/content/ContentController.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/content/ContentController.kt @@ -8,14 +8,11 @@ import com.pokit.common.dto.SliceResponseDto import com.pokit.common.wrapper.ResponseWrapper.wrapOk import com.pokit.common.wrapper.ResponseWrapper.wrapSlice import com.pokit.common.wrapper.ResponseWrapper.wrapUnit -import com.pokit.content.dto.ContentsResponse import com.pokit.content.dto.request.ContentSearchParams import com.pokit.content.dto.request.CreateContentRequest import com.pokit.content.dto.request.UpdateContentRequest import com.pokit.content.dto.request.toDto -import com.pokit.content.dto.response.BookMarkContentResponse -import com.pokit.content.dto.response.ContentResponse -import com.pokit.content.dto.response.toResponse +import com.pokit.content.dto.response.* import com.pokit.content.exception.ContentErrorCode import com.pokit.content.port.`in`.ContentUseCase import io.swagger.v3.oas.annotations.Operation @@ -113,6 +110,7 @@ class ContentController( condition.copy(categoryId = categoryId).toDto(), pageable ) + .map { it.toResponse() } .wrapSlice() .wrapOk() } @@ -145,6 +143,7 @@ class ContentController( condition.toDto(), pageable ) + .map { it.toResponse() } .wrapSlice() .wrapOk() } diff --git a/adapters/in-web/src/main/kotlin/com/pokit/content/RemindController.kt b/adapters/in-web/src/main/kotlin/com/pokit/content/RemindController.kt index 993cf260..2efe24ff 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/content/RemindController.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/content/RemindController.kt @@ -37,4 +37,20 @@ class RemindController( .map { it.toResponse() } .wrapSlice() .wrapOk() + + @GetMapping("/unread") + @Operation(summary = "읽지 않음 컨텐츠 조회 API") + fun getUnreadContents( + @AuthenticationPrincipal user: PrincipalUser, + @PageableDefault( + page = 0, + size = 10, + sort = ["createdAt"], + direction = Sort.Direction.DESC + ) pageable: Pageable, + ): ResponseEntity> = + contentUseCase.getUnreadContents(user.id, pageable) + .map { it.toResponse() } + .wrapSlice() + .wrapOk() } diff --git a/adapters/in-web/src/main/kotlin/com/pokit/content/dto/response/ContentsResponse.kt b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/response/ContentsResponse.kt new file mode 100644 index 00000000..d2b8cc2e --- /dev/null +++ b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/response/ContentsResponse.kt @@ -0,0 +1,35 @@ +package com.pokit.content.dto.response + +import java.time.format.DateTimeFormatter + +data class ContentsResponse( + val contentId: Long, + val categoryId: Long, + val categoryName: String, + val data: String, + val domain: String, + val title: String, + val memo: String, + val alertYn: String, + val createdAt: String, + val isRead: Boolean, + val thumbNail: String, +) + +fun ContentsResult.toResponse(): ContentsResponse { + val formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd") + + return ContentsResponse( + contentId = this.contentId, + categoryId = this.categoryId, + categoryName = this.categoryName, + data = this.data, + domain = this.domain, + title = this.title, + memo = this.memo, + alertYn = this.alertYn, + createdAt = this.createdAt.format(formatter), + isRead = this.isRead, + thumbNail = this.thumbNail + ) +} diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContentAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContentAdapter.kt index 96815ad9..a7768a96 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContentAdapter.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContentAdapter.kt @@ -1,6 +1,6 @@ package com.pokit.out.persistence.content.impl -import com.pokit.content.dto.ContentsResponse +import com.pokit.content.dto.response.ContentsResult import com.pokit.content.dto.request.ContentSearchCondition import com.pokit.content.model.Content import com.pokit.content.port.out.ContentPort @@ -53,7 +53,7 @@ class ContentAdapter( userId: Long, condition: ContentSearchCondition, pageable: Pageable, - ): Slice { + ): Slice { var hasNext = false val order = pageable.sort.getOrderFor("createdAt") @@ -72,6 +72,7 @@ class ContentAdapter( dateBetween(condition.startDate, condition.endDate), categoryIn(condition.categoryIds) ) + .groupBy(contentEntity) .orderBy(getSort(contentEntity.createdAt, order!!)) .limit(pageable.pageSize + 1L) @@ -84,7 +85,7 @@ class ContentAdapter( } val contents = contentEntityList.map { - ContentsResponse.of( + ContentsResult.of( it[contentEntity]!!.toDomain(), it[categoryEntity.name]!!, it[userLogEntity.count()]!! diff --git a/application/src/main/kotlin/com/pokit/content/port/in/ContentUseCase.kt b/application/src/main/kotlin/com/pokit/content/port/in/ContentUseCase.kt index b67c89af..c854a667 100644 --- a/application/src/main/kotlin/com/pokit/content/port/in/ContentUseCase.kt +++ b/application/src/main/kotlin/com/pokit/content/port/in/ContentUseCase.kt @@ -1,7 +1,7 @@ package com.pokit.content.port.`in` import com.pokit.content.dto.request.ContentCommand -import com.pokit.content.dto.ContentsResponse +import com.pokit.content.dto.response.ContentsResult import com.pokit.content.dto.request.ContentSearchCondition import com.pokit.content.dto.response.BookMarkContentResponse import com.pokit.content.dto.response.GetContentResponse @@ -26,9 +26,11 @@ interface ContentUseCase { userId: Long, condition: ContentSearchCondition, pageable: Pageable, - ): Slice + ): Slice fun getContent(userId: Long, contentId: Long): GetContentResponse fun getBookmarkContents(userId: Long, pageable: Pageable): Slice + + fun getUnreadContents(userId: Long, pageable: Pageable): Slice } diff --git a/application/src/main/kotlin/com/pokit/content/port/out/ContentPort.kt b/application/src/main/kotlin/com/pokit/content/port/out/ContentPort.kt index c1f0d663..8a9c86a8 100644 --- a/application/src/main/kotlin/com/pokit/content/port/out/ContentPort.kt +++ b/application/src/main/kotlin/com/pokit/content/port/out/ContentPort.kt @@ -1,6 +1,6 @@ package com.pokit.content.port.out -import com.pokit.content.dto.ContentsResponse +import com.pokit.content.dto.response.ContentsResult import com.pokit.content.dto.request.ContentSearchCondition import com.pokit.content.model.Content import org.springframework.data.domain.Pageable @@ -19,7 +19,7 @@ interface ContentPort { userId: Long, condition: ContentSearchCondition, pageable: Pageable, - ): Slice + ): Slice fun deleteByUserId(userId: Long) diff --git a/application/src/main/kotlin/com/pokit/content/port/service/ContentService.kt b/application/src/main/kotlin/com/pokit/content/port/service/ContentService.kt index 8e8753fb..8e37d89d 100644 --- a/application/src/main/kotlin/com/pokit/content/port/service/ContentService.kt +++ b/application/src/main/kotlin/com/pokit/content/port/service/ContentService.kt @@ -11,7 +11,7 @@ import com.pokit.common.exception.NotFoundCustomException import com.pokit.content.dto.request.ContentCommand import com.pokit.content.dto.request.toDomain import com.pokit.content.dto.response.* -import com.pokit.content.dto.ContentsResponse +import com.pokit.content.dto.response.ContentsResult import com.pokit.content.dto.request.ContentSearchCondition import com.pokit.content.dto.response.BookMarkContentResponse import com.pokit.content.dto.response.GetContentResponse @@ -50,9 +50,9 @@ class ContentService( @Transactional override fun create(user: User, contentCommand: ContentCommand): Content { verifyCategory(contentCommand.categoryId, user.id) - return contentPort.persist( - contentCommand.toDomain() - ) + val content = contentCommand.toDomain() + content.parseDomain() + return contentPort.persist(content) } @Transactional @@ -80,7 +80,7 @@ class ContentService( userId: Long, condition: ContentSearchCondition, pageable: Pageable, - ): Slice { + ): Slice { val contents = contentPort.loadAllByUserIdAndContentId( userId, condition, @@ -120,6 +120,22 @@ class ContentService( return SliceImpl(remindContents, pageable, bookMarks.hasNext()) } + override fun getUnreadContents(userId: Long, pageable: Pageable): Slice { + val contentSearchCondition = ContentSearchCondition( + isRead = false, + categoryId = null, + favorites = null, + startDate = null, + endDate = null, + categoryIds = null + ) + + val unreadContents = contentPort.loadAllByUserIdAndContentId(userId, contentSearchCondition, pageable) + val remindContents = unreadContents.content.map { it.toRemindContentResult() } + + return SliceImpl(remindContents, pageable, unreadContents.hasNext()) + } + private fun verifyContent(userId: Long, contentId: Long): Content { return contentPort.loadByUserIdAndId(userId, contentId) ?: throw NotFoundCustomException(ContentErrorCode.NOT_FOUND_CONTENT) diff --git a/domain/src/main/kotlin/com/pokit/content/dto/ContentsResponse.kt b/domain/src/main/kotlin/com/pokit/content/dto/response/ContentsResult.kt similarity index 65% rename from domain/src/main/kotlin/com/pokit/content/dto/ContentsResponse.kt rename to domain/src/main/kotlin/com/pokit/content/dto/response/ContentsResult.kt index 7f08d8b7..5f78136f 100644 --- a/domain/src/main/kotlin/com/pokit/content/dto/ContentsResponse.kt +++ b/domain/src/main/kotlin/com/pokit/content/dto/response/ContentsResult.kt @@ -1,9 +1,9 @@ -package com.pokit.content.dto +package com.pokit.content.dto.response import com.pokit.content.model.Content -import java.time.format.DateTimeFormatter +import java.time.LocalDateTime -data class ContentsResponse( +data class ContentsResult( val contentId: Long, val categoryId: Long, val categoryName: String, @@ -12,14 +12,13 @@ data class ContentsResponse( val title: String, val memo: String, val alertYn: String, - val createdAt: String, - val isRead: Boolean + val createdAt: LocalDateTime, + val isRead: Boolean, + val thumbNail: String = "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/category-image/-3+1.png" // TODO 추가 예정 ) { companion object { - fun of(content: Content, categoryName: String, isRead: Long): ContentsResponse { - val formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd") - - return ContentsResponse( + fun of(content: Content, categoryName: String, isRead: Long): ContentsResult { + return ContentsResult( contentId = content.id, categoryId = content.categoryId, categoryName = categoryName, @@ -28,7 +27,7 @@ data class ContentsResponse( title = content.title, memo = content.memo, alertYn = content.alertYn, - createdAt = content.createdAt.format(formatter), + createdAt = content.createdAt, isRead = isRead > 0 ) } diff --git a/domain/src/main/kotlin/com/pokit/content/dto/response/RemindContentResult.kt b/domain/src/main/kotlin/com/pokit/content/dto/response/RemindContentResult.kt index 825ffa06..c0c27190 100644 --- a/domain/src/main/kotlin/com/pokit/content/dto/response/RemindContentResult.kt +++ b/domain/src/main/kotlin/com/pokit/content/dto/response/RemindContentResult.kt @@ -23,6 +23,19 @@ fun Content.toRemindContentResult(isRead: Boolean, category: RemindCategory): Re title = this.title, createdAt = this.createdAt, isRead = isRead, - domain = this.domain + domain = this.domain, + ) +} + +fun ContentsResult.toRemindContentResult(): RemindContentResult { + return RemindContentResult( + contentId = this.contentId, + category = RemindCategory(this.categoryId, this.categoryName), + data = this.data, + title = this.title, + createdAt = this.createdAt, + domain = this.domain, + isRead = this.isRead, + thumbNail = this.thumbNail ) }