Skip to content

Commit

Permalink
[feat #46] 즐겨찾기 리마인드 api (#52)
Browse files Browse the repository at this point in the history
* fix: 오탈자 수정

* rename: 폴더 구조 변경

* feat: 즐겨찾기 리마인드 api

* fix: 필요없는 코드 제거

* fix: domain 임시 모킹

* rename: 폴더구조 변경

* refactor: 주석제거 (#53)

* feat: 페이징 처리

* feat: response 칼럼추가

* feat: response 칼럼 추가
  • Loading branch information
jimin3263 authored Aug 4, 2024
1 parent 2609c42 commit 3ccbcb7
Show file tree
Hide file tree
Showing 27 changed files with 215 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.pokit.content

import com.pokit.auth.model.PrincipalUser
import com.pokit.common.dto.SliceResponseDto
import com.pokit.common.wrapper.ResponseWrapper.wrapOk
import com.pokit.common.wrapper.ResponseWrapper.wrapSlice
import com.pokit.content.dto.response.RemindContentResponse
import com.pokit.content.dto.response.toResponse
import com.pokit.content.port.`in`.ContentUseCase
import io.swagger.v3.oas.annotations.Operation
import org.springframework.data.domain.Pageable
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.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v1/remind")
class RemindController(
private val contentUseCase: ContentUseCase
) {
@GetMapping("/bookmark")
@Operation(summary = "즐겨찾기 링크 모음 조회 API")
fun getBookmarkContents(
@AuthenticationPrincipal user: PrincipalUser,
@PageableDefault(
page = 0,
size = 10,
sort = ["createdAt"],
direction = Sort.Direction.DESC
) pageable: Pageable,
): ResponseEntity<SliceResponseDto<RemindContentResponse>> =
contentUseCase.getBookmarkContents(user.id, pageable)
.map { it.toResponse() }
.wrapSlice()
.wrapOk()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pokit.content.dto.request

import com.pokit.content.dto.ContentCommand
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pokit.content.dto.request

import com.pokit.content.dto.ContentCommand
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.pokit.content.dto.response

import com.pokit.category.model.RemindCategory
import java.time.format.DateTimeFormatter

data class RemindContentResponse(
val contentId: Long,
val category: RemindCategory,
val title: String,
val data: String,
val createdAt: String,
val domain: String,
val isRead: Boolean,
val thumbNail: String,
)

fun RemindContentResult.toResponse(): RemindContentResponse {
val formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd")

return RemindContentResponse(
contentId = this.contentId,
category = this.category,
data = this.data,
title = this.title,
createdAt = this.createdAt.format(formatter),
domain = this.domain,
isRead = this.isRead,
thumbNail = this.thumbNail
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.pokit.bookmark.port.out.BookmarkPort
import com.pokit.out.persistence.bookmark.persist.BookMarkRepository
import com.pokit.out.persistence.bookmark.persist.BookmarkEntity
import com.pokit.out.persistence.bookmark.persist.toDomain
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.stereotype.Repository

@Repository
Expand Down Expand Up @@ -32,4 +34,12 @@ class BookMarkAdapter(
false
)?.toDomain()
}

override fun isBookmarked(contentId: Long, userId: Long): Boolean =
bookMarkRepository.existsByContentIdAndUserIdAndDeleted(contentId, userId, false)

override fun loadByUserId(userId: Long, pageable: Pageable): Slice<Bookmark> =
bookMarkRepository.findByUserIdAndDeleted(userId, false, pageable)
.map { it.toDomain() }

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.pokit.out.persistence.bookmark.persist

import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.repository.JpaRepository

interface BookMarkRepository : JpaRepository<BookmarkEntity, Long> {
Expand All @@ -8,4 +10,8 @@ interface BookMarkRepository : JpaRepository<BookmarkEntity, Long> {
userId: Long,
deleted: Boolean
): BookmarkEntity?

fun findByUserIdAndDeleted(userId: Long, deleted: Boolean, pageable: Pageable): Slice<BookmarkEntity>

fun existsByContentIdAndUserIdAndDeleted(contentId: Long, userId: Long, deleted: Boolean): Boolean
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pokit.out.persistence.bookmark.persist

import com.pokit.bookmark.model.Bookmark
import com.pokit.out.persistence.BaseEntity
import jakarta.persistence.*

// 콕 엔티티
Expand All @@ -20,7 +21,7 @@ class BookmarkEntity(

@Column(name = "deleted")
var deleted: Boolean = false
) {
): BaseEntity() {
fun delete() {
this.deleted = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CategoryEntity(
val image: CategoryImageEntity,
) : BaseEntity() {

@Column(name = "id_deleted")
@Column(name = "is_deleted")
var deleted: Boolean = false

fun delete() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class ContentAdapter(
dateBetween(condition.startDate, condition.endDate),
categoryIn(condition.categoryIds)
)
.groupBy(contentEntity)
.orderBy(getSort(contentEntity.createdAt, order!!))
.limit(pageable.pageSize + 1L)

Expand All @@ -95,6 +94,10 @@ class ContentAdapter(
return SliceImpl(contents, pageable, hasNext)
}

override fun loadByContentIds(contentIds: List<Long>): List<Content> =
contentRepository.findByIdIn(contentIds)
.map { it.toDomain() }

private fun isUnread(read: Boolean?): Predicate? {
return read?.let {
userLogEntity.id.isNull.or(userLogEntity.type.ne(LogType.READ))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ class ContentEntity(
val alertYn: String,

@Column(name = "is_deleted")
var deleted: Boolean = false,

@Column(name = "domain")
val domain: String
var deleted: Boolean = false
) : BaseEntity() {
fun delete() {
this.deleted = true
Expand All @@ -50,8 +47,7 @@ class ContentEntity(
data = content.data,
title = content.title,
memo = content.memo,
alertYn = content.alertYn,
domain = content.domain
alertYn = content.alertYn
)
}
}
Expand All @@ -64,6 +60,5 @@ fun ContentEntity.toDomain() = Content(
title = this.title,
memo = this.memo,
alertYn = this.alertYn,
createdAt = this.createdAt,
domain = this.domain
createdAt = this.createdAt
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ interface ContentRepository : JpaRepository<ContentEntity, Long> {
(select ct.id from CategoryEntity ct where ct.userId = :userId)
""")
fun deleteByUserId(@Param("userId") userId: Long)

fun findByIdIn(ids: List<Long>): List<ContentEntity>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.pokit.out.persistence.log

import com.pokit.log.model.LogType
import com.pokit.log.model.UserLog
import com.pokit.log.port.out.UserLogPort
import com.pokit.out.persistence.log.persist.UserLogEntity
Expand All @@ -13,4 +14,8 @@ class UserLogAdapter(
) : UserLogPort {
override fun persist(userLog: UserLog) =
userLogRepository.save(UserLogEntity.of(userLog)).toDomain()

override fun isContentRead(contentId: Long, userId: Long): Boolean =
userLogRepository.existsByContentIdAndUserIdAndType(contentId, userId, LogType.READ)

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.pokit.out.persistence.log.persist

import com.pokit.log.model.LogType
import org.springframework.data.jpa.repository.JpaRepository

interface UserLogRepository : JpaRepository<UserLogEntity, Long> {
fun findByContentIdAndUserId(contentId: Long, userId: Long): UserLogEntity?
fun existsByContentIdAndUserIdAndType(contentId: Long, userId: Long, type: LogType): Boolean
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.pokit.content

import com.pokit.content.dto.ContentCommand
import com.pokit.content.dto.request.ContentCommand
import com.pokit.content.dto.request.ContentSearchCondition
import com.pokit.content.model.Content
import com.pokit.content.model.ContentType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.pokit.bookmark.port.out

import com.pokit.bookmark.model.Bookmark
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice

interface BookmarkPort {
fun persist(bookmark: Bookmark): Bookmark

fun delete(userId: Long, contentId: Long)

fun loadByContentIdAndUserId(contentId: Long, userId: Long): Bookmark?

fun loadByUserId(userId: Long, pageable: Pageable): Slice<Bookmark>

fun isBookmarked(contentId: Long, userId: Long): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ class CategoryService(

@Transactional
override fun update(categoryCommand: CategoryCommand, userId: Long, categoryId: Long): Category {
val category = categoryPort.loadByIdAndUserId(categoryId, userId)
?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY)
val category = categoryPort.loadCategoryOrThrow(categoryId, userId)

val categoryImage = categoryImagePort.loadById(categoryCommand.categoryImageId)
?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY_IMAGE)
Expand Down Expand Up @@ -99,3 +98,8 @@ class CategoryService(
categoryImagePort.loadAll()

}

fun CategoryPort.loadCategoryOrThrow(categoryId: Long, userId: Long): Category {
return loadByIdAndUserId(categoryId, userId)
?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.pokit.content.port.`in`

import com.pokit.content.dto.ContentCommand
import com.pokit.content.dto.request.ContentCommand
import com.pokit.content.dto.ContentsResponse
import com.pokit.content.dto.request.ContentSearchCondition
import com.pokit.content.dto.response.BookMarkContentResponse
import com.pokit.content.dto.response.GetContentResponse
import com.pokit.content.dto.response.RemindContentResult
import com.pokit.content.model.Content
import com.pokit.user.model.User
import org.springframework.data.domain.Pageable
Expand All @@ -18,6 +19,7 @@ interface ContentUseCase {
fun update(user: User, contentCommand: ContentCommand, contentId: Long): Content

fun delete(user: User, contentId: Long)

fun cancelBookmark(user: User, contentId: Long)

fun getContents(
Expand All @@ -27,4 +29,6 @@ interface ContentUseCase {
): Slice<ContentsResponse>

fun getContent(userId: Long, contentId: Long): GetContentResponse

fun getBookmarkContents(userId: Long, pageable: Pageable): Slice<RemindContentResult>
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ interface ContentPort {
): Slice<ContentsResponse>

fun deleteByUserId(userId: Long)

fun loadByContentIds(contentIds: List<Long>): List<Content>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import com.pokit.bookmark.model.Bookmark
import com.pokit.bookmark.port.out.BookmarkPort
import com.pokit.category.exception.CategoryErrorCode
import com.pokit.category.model.Category
import com.pokit.category.model.toRemindCategory
import com.pokit.category.port.out.CategoryPort
import com.pokit.category.port.service.loadCategoryOrThrow
import com.pokit.common.exception.NotFoundCustomException
import com.pokit.content.dto.ContentCommand
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.request.ContentSearchCondition
import com.pokit.content.dto.response.BookMarkContentResponse
import com.pokit.content.dto.response.GetContentResponse
import com.pokit.content.dto.response.toGetContentResponse
import com.pokit.content.dto.toDomain
import com.pokit.content.exception.ContentErrorCode
import com.pokit.content.model.Content
import com.pokit.content.port.`in`.ContentUseCase
Expand All @@ -23,6 +26,7 @@ import com.pokit.log.port.out.UserLogPort
import com.pokit.user.model.User
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.domain.SliceImpl
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -46,9 +50,9 @@ class ContentService(
@Transactional
override fun create(user: User, contentCommand: ContentCommand): Content {
verifyCategory(contentCommand.categoryId, user.id)
val content = contentCommand.toDomain()
content.parseDomain()
return contentPort.persist(content)
return contentPort.persist(
contentCommand.toDomain()
)
}

@Transactional
Expand Down Expand Up @@ -94,11 +98,26 @@ class ContentService(
userLogPort.persist(userLog) // 읽음 처리

val content = verifyContent(userId, contentId)
val bookmark = bookMarkPort.loadByContentIdAndUserId(contentId, userId)
val bookmarkStatus = bookMarkPort.isBookmarked(contentId, userId)

return content.toGetContentResponse(bookmarkStatus)
}

override fun getBookmarkContents(userId: Long, pageable: Pageable): Slice<RemindContentResult> {
val bookMarks = bookMarkPort.loadByUserId(userId, pageable)
val contentIds = bookMarks.content.map { it.contentId }
val contentsById = contentPort.loadByContentIds(contentIds).associateBy { it.id }

val remindContents = contentIds.map { contentId ->
val content = contentsById[contentId]
content?.let {
val isRead = userLogPort.isContentRead(it.id, userId)
val category = categoryPort.loadCategoryOrThrow(it.categoryId, userId).toRemindCategory()
it.toRemindContentResult(isRead, category)
}
}

return bookmark
?.let { content.toGetContentResponse(it) } // 즐겨찾기 true
?: content.toGetContentResponse() // 즐겨찾기 false
return SliceImpl(remindContents, pageable, bookMarks.hasNext())
}

private fun verifyContent(userId: Long, contentId: Long): Content {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ import com.pokit.log.model.UserLog

interface UserLogPort {
fun persist(userLog: UserLog): UserLog

fun isContentRead(contentId: Long, userId: Long): Boolean
}
Loading

0 comments on commit 3ccbcb7

Please sign in to comment.