From 132626e51df8f2f69634bfb708b2c4cfa723bf8e Mon Sep 17 00:00:00 2001 From: Jimin Lim <50178026+jimin3263@users.noreply.github.com> Date: Sun, 18 Aug 2024 23:45:34 +0900 Subject: [PATCH] =?UTF-8?q?[feat=20#106]=20=EA=B3=B5=EC=9C=A0=20=EC=BB=A8?= =?UTF-8?q?=ED=85=90=EC=B8=A0=20=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20API?= =?UTF-8?q?=20(#107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 공유 컨텐츠 미리보기 API * fix: 타이틀 max 1000으로 조정 * fix: 오탈자 수정 * fix: datetime 포맷 수정 * fix: 필요없는 반환 삭제 --- .../pokit/category/CategoryShareController.kt | 51 ++++++++++++++ .../dto/response/SharedContentsResponse.kt | 66 +++++++++++++++++++ .../com/pokit/common/dto/DateFormatter.kt | 7 ++ .../dto/request/CreateContentRequest.kt | 2 +- .../dto/request/UpdateContentRequest.kt | 2 +- .../category/impl/CategoryAdapter.kt | 8 +++ .../category/persist/CategoryEntity.kt | 11 +++- .../category/persist/CategoryRepository.kt | 2 + .../persistence/content/impl/ContenAdapter.kt | 0 .../content/impl/ContentAdapter.kt | 39 ++++++++--- .../pokit/category/port/in/CategoryUseCase.kt | 2 + .../pokit/category/port/out/CategoryPort.kt | 3 + .../category/port/service/CategoryService.kt | 33 +++++++++- .../pokit/content/port/in/ContentUseCase.kt | 10 +-- .../com/pokit/content/port/out/ContentPort.kt | 8 +++ .../content/port/service/ContentService.kt | 5 +- .../pokit/user/port/service/UserService.kt | 4 +- .../pokit/category/dto/CategoriesResponse.kt | 2 +- .../category/exception/CategoryErrorCode.kt | 5 +- .../com/pokit/category/model/Category.kt | 8 ++- .../dto/response/SharedContentsResult.kt | 29 ++++++++ 21 files changed, 274 insertions(+), 23 deletions(-) create mode 100644 adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt create mode 100644 adapters/in-web/src/main/kotlin/com/pokit/category/dto/response/SharedContentsResponse.kt create mode 100644 adapters/in-web/src/main/kotlin/com/pokit/common/dto/DateFormatter.kt delete mode 100644 adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContenAdapter.kt create mode 100644 domain/src/main/kotlin/com/pokit/content/dto/response/SharedContentsResult.kt diff --git a/adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt b/adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt new file mode 100644 index 00000000..847223a5 --- /dev/null +++ b/adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt @@ -0,0 +1,51 @@ +package com.pokit.category + +import com.pokit.auth.model.PrincipalUser +import com.pokit.category.dto.response.SharedContentsResponse +import com.pokit.category.port.`in`.CategoryUseCase +import com.pokit.common.wrapper.ResponseWrapper.wrapOk +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.* + +@RestController +@RequestMapping("/api/v1/category/share") +class CategoryShareController( + private val categoryUseCase: CategoryUseCase, + private val contentUseCase: ContentUseCase, +) { + @Operation(summary = "포킷 공유 후 callback API") + @PostMapping("/callback") + fun completeShare( + @AuthenticationPrincipal user: PrincipalUser, + @RequestParam("categoryId") categoryId: Long, + ): ResponseEntity { + return categoryUseCase.completeShare(categoryId, user.id) + .wrapOk() + } + + @Operation(summary = "포킷 공유 시 포킷 내 컨텐츠 미리보기 API") + @GetMapping("/{categoryId}") + fun getSharedContents( + @AuthenticationPrincipal user: PrincipalUser, + @PathVariable categoryId: Long, + @PageableDefault( + page = 0, + size = 10, + sort = ["createdAt"], + direction = Sort.Direction.DESC + ) pageable: Pageable, + ): ResponseEntity { + val category = categoryUseCase.getSharedCategory(categoryId, user.id) + val content = contentUseCase.getSharedContents(categoryId, pageable) + return SharedContentsResponse + .from(content, category) + .wrapOk() + } + +} diff --git a/adapters/in-web/src/main/kotlin/com/pokit/category/dto/response/SharedContentsResponse.kt b/adapters/in-web/src/main/kotlin/com/pokit/category/dto/response/SharedContentsResponse.kt new file mode 100644 index 00000000..503c71a7 --- /dev/null +++ b/adapters/in-web/src/main/kotlin/com/pokit/category/dto/response/SharedContentsResponse.kt @@ -0,0 +1,66 @@ +package com.pokit.category.dto.response + +import com.pokit.category.model.Category +import com.pokit.common.dto.DateFormatters +import com.pokit.common.dto.SliceResponseDto +import com.pokit.common.wrapper.ResponseWrapper.wrapSlice +import com.pokit.content.dto.response.SharedContentResult +import org.springframework.data.domain.Slice +import org.springframework.data.domain.SliceImpl + +data class SharedContentsResponse( + var category: SharedCategoryResponse, + var contents: SliceResponseDto +) { + companion object { + fun from(sharedContents: Slice, category: Category): SharedContentsResponse { + val sharedContentResponse = sharedContents.content.map { SharedContentResponse.of(it) } + val contents = SliceImpl(sharedContentResponse, sharedContents.pageable, sharedContents.hasNext()) + + return SharedContentsResponse( + category = SharedCategoryResponse.of(category), + contents = contents.wrapSlice(), + ) + } + } +} + +data class SharedContentResponse( + val contentId: Long, + val data: String, + val domain: String, + val title: String, + val memo: String, + val createdAt: String, + val thumbNail: String, +) { + companion object { + fun of(content: SharedContentResult): SharedContentResponse { + return SharedContentResponse( + contentId = content.contentId, + data = content.data, + domain = content.domain, + title = content.title, + memo = content.memo, + createdAt = content.createdAt.format(DateFormatters.DATE_FORMAT_YYYY_MM_DD), + thumbNail = content.thumbNail, + ) + } + } +} + +data class SharedCategoryResponse( + val categoryId: Long = 0L, + var categoryName: String, + var contentCount: Int = 0, +) { + companion object { + fun of(category: Category): SharedCategoryResponse { + return SharedCategoryResponse( + categoryId = category.categoryId, + categoryName = category.categoryName, + contentCount = category.contentCount, + ) + } + } +} diff --git a/adapters/in-web/src/main/kotlin/com/pokit/common/dto/DateFormatter.kt b/adapters/in-web/src/main/kotlin/com/pokit/common/dto/DateFormatter.kt new file mode 100644 index 00000000..9aec66c3 --- /dev/null +++ b/adapters/in-web/src/main/kotlin/com/pokit/common/dto/DateFormatter.kt @@ -0,0 +1,7 @@ +package com.pokit.common.dto + +import java.time.format.DateTimeFormatter + +object DateFormatters { + val DATE_FORMAT_YYYY_MM_DD: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd") +} diff --git a/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/CreateContentRequest.kt b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/CreateContentRequest.kt index a6aa34fa..3ffd7b56 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/CreateContentRequest.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/CreateContentRequest.kt @@ -12,7 +12,7 @@ data class CreateContentRequest( ) val data: String, @field:NotBlank(message = "제목은 필수값입니다.") - @field:Size(min = 1, max = 20, message = "최대 20자까지만 입력 가능합니다.") + @field:Size(min = 1, max =1000, message = "최대 1000자까지만 입력 가능합니다.") val title: String, val categoryId: Int, @field:Size(max = 100, message = "최대 100자까지만 입력 가능합니다.") diff --git a/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/UpdateContentRequest.kt b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/UpdateContentRequest.kt index 66219ada..675cdc8b 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/UpdateContentRequest.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/content/dto/request/UpdateContentRequest.kt @@ -7,7 +7,7 @@ data class UpdateContentRequest( @field:NotBlank(message = "링크는 필수값입니다.") val data: String, @field:NotBlank(message = "제목은 필수값입니다.") - @field:Size(min = 1, max = 20, message = "최대 20자까지만 입력 가능합니다.") + @field:Size(min = 1, max = 1000, message = "최대 1000자까지만 입력 가능합니다.") val title: String, val categoryId: Long, @field:Size(max = 100, message = "최대 100자까지만 입력 가능합니다.") diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/CategoryAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/CategoryAdapter.kt index e6cf1dbb..6f40e9bd 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/CategoryAdapter.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/CategoryAdapter.kt @@ -1,6 +1,7 @@ package com.pokit.out.persistence.category.impl import com.pokit.category.model.Category +import com.pokit.category.model.OpenType import com.pokit.category.port.out.CategoryPort import com.pokit.out.persistence.category.persist.CategoryEntity import com.pokit.out.persistence.category.persist.CategoryRepository @@ -21,6 +22,9 @@ class CategoryAdapter( override fun loadByIdAndUserId(id: Long, userId: Long): Category? = categoryRepository.findByIdAndUserIdAndDeleted(id, userId, false)?.toDomain() + override fun loadById(id: Long): Category? = + categoryRepository.findByIdOrNull(id)?.toDomain() + override fun existsByNameAndUserId(name: String, userId: Long): Boolean = categoryRepository.existsByNameAndUserIdAndDeleted(name, userId, false) @@ -36,4 +40,8 @@ class CategoryAdapter( override fun countByUserId(userId: Long): Int = categoryRepository.countByUserIdAndDeleted(userId, false) + + override fun loadByIdAndOpenType(id: Long, openType: OpenType): Category? = + categoryRepository.findByIdAndOpenTypeAndDeleted(id, openType, false)?.toDomain() + } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryEntity.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryEntity.kt index a2aa2bcb..f9bd9342 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryEntity.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryEntity.kt @@ -1,6 +1,7 @@ package com.pokit.out.persistence.category.persist import com.pokit.category.model.Category +import com.pokit.category.model.OpenType import com.pokit.out.persistence.BaseEntity import jakarta.persistence.* @@ -22,6 +23,10 @@ class CategoryEntity( @OneToOne @JoinColumn(name = "image_id", foreignKey = ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) val image: CategoryImageEntity, + + @Column(name = "open_type") + @Enumerated(EnumType.STRING) + var openType: OpenType = OpenType.PRIVATE, ) : BaseEntity() { @Column(name = "is_deleted") @@ -37,7 +42,8 @@ class CategoryEntity( id = category.categoryId, userId = category.userId, name = category.categoryName, - image = CategoryImageEntity.of(category.categoryImage) + image = CategoryImageEntity.of(category.categoryImage), + openType = category.openType, ) } } @@ -47,5 +53,6 @@ fun CategoryEntity.toDomain() = Category( categoryName = this.name, categoryImage = this.image.toDomain(), userId = this.userId, - createdAt = this.createdAt + createdAt = this.createdAt, + openType = this.openType, ) diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryRepository.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryRepository.kt index 52fd5d4e..c542fa63 100644 --- a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryRepository.kt +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/CategoryRepository.kt @@ -1,5 +1,6 @@ package com.pokit.out.persistence.category.persist +import com.pokit.category.model.OpenType import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.springframework.data.jpa.repository.JpaRepository @@ -9,4 +10,5 @@ interface CategoryRepository : JpaRepository { fun findByUserIdAndDeleted(userId: Long, deleted: Boolean, pageable: Pageable): Slice fun findByIdAndUserIdAndDeleted(id: Long, userId: Long, deleted: Boolean): CategoryEntity? fun countByUserIdAndDeleted(userId: Long, deleted: Boolean): Int + fun findByIdAndOpenTypeAndDeleted(id: Long, openType: OpenType, deleted:Boolean): CategoryEntity? } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContenAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/content/impl/ContenAdapter.kt deleted file mode 100644 index e69de29b..00000000 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 d2fd1384..5eb0b74f 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,7 +1,9 @@ package com.pokit.out.persistence.content.impl +import com.pokit.category.model.OpenType import com.pokit.content.dto.request.ContentSearchCondition import com.pokit.content.dto.response.ContentsResult +import com.pokit.content.dto.response.SharedContentResult import com.pokit.content.model.Content import com.pokit.content.port.out.ContentPort import com.pokit.log.model.LogType @@ -152,18 +154,43 @@ class ContentAdapter( return SliceImpl(contentResults, pageable, hasNext) } + override fun loadByCategoryIdAndOpenType(categoryId: Long, opentype: OpenType, pageable: Pageable): Slice { + val contents = queryFactory.select(contentEntity) + .from(contentEntity) + .join(categoryEntity).on(categoryEntity.id.eq(contentEntity.categoryId)) + .where( + categoryEntity.id.eq(categoryId), + categoryEntity.openType.eq(opentype), + contentEntity.deleted.isFalse, + ) + .offset(pageable.offset) + .limit((pageable.pageSize + 1).toLong()) + .orderBy(getSortOrder(contentEntity.createdAt, "createdAt", pageable)) + .fetch() + + val hasNext = getHasNext(contents, pageable) + + val contentResults = contents.map { + SharedContentResult.of( + it.toDomain(), + ) + } + + return SliceImpl(contentResults, pageable, hasNext) + } + override fun loadByContentIds(contentIds: List): List = contentRepository.findByIdIn(contentIds) .map { it.toDomain() } - private fun getHasNext( - contentEntityList: MutableList, + private fun getHasNext( + items: MutableList, pageable: Pageable, ): Boolean { var hasNext = false - if (contentEntityList.size > pageable.pageSize) { + if (items.size > pageable.pageSize) { hasNext = true - contentEntityList.removeAt(contentEntityList.size - 1) + items.removeAt(items.size - 1) } return hasNext } @@ -215,10 +242,6 @@ class ContentAdapter( } } - private fun getSort(property: DateTimePath, order: Sort.Order) = - if (order.isDescending) property.desc() - else property.asc() - private fun getSortOrder(property: DateTimePath, sortField: String, pageable: Pageable): OrderSpecifier<*> { val order = pageable.sort.getOrderFor(sortField) ?.direction diff --git a/application/src/main/kotlin/com/pokit/category/port/in/CategoryUseCase.kt b/application/src/main/kotlin/com/pokit/category/port/in/CategoryUseCase.kt index 97a8e47d..aab18577 100644 --- a/application/src/main/kotlin/com/pokit/category/port/in/CategoryUseCase.kt +++ b/application/src/main/kotlin/com/pokit/category/port/in/CategoryUseCase.kt @@ -15,4 +15,6 @@ interface CategoryUseCase { fun getAllCategoryImages(): List fun getCategories(userId: Long, pageable: Pageable, filterUncategorized: Boolean): Slice fun getCategory(userId: Long, categoryId: Long): Category + fun getSharedCategory(categoryId: Long, userId: Long): Category + fun completeShare(categoryId: Long, userId: Long) } diff --git a/application/src/main/kotlin/com/pokit/category/port/out/CategoryPort.kt b/application/src/main/kotlin/com/pokit/category/port/out/CategoryPort.kt index a47fb7ae..de76abb5 100644 --- a/application/src/main/kotlin/com/pokit/category/port/out/CategoryPort.kt +++ b/application/src/main/kotlin/com/pokit/category/port/out/CategoryPort.kt @@ -1,14 +1,17 @@ package com.pokit.category.port.out import com.pokit.category.model.Category +import com.pokit.category.model.OpenType import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice interface CategoryPort { fun loadAllByUserId(userId: Long, pageable: Pageable): Slice fun loadByIdAndUserId(id: Long, userId: Long): Category? + fun loadById(id: Long): Category? fun existsByNameAndUserId(name: String, userId: Long): Boolean fun persist(category: Category): Category fun delete(category: Category) fun countByUserId(userId: Long): Int + fun loadByIdAndOpenType(id: Long, openType: OpenType): Category? } diff --git a/application/src/main/kotlin/com/pokit/category/port/service/CategoryService.kt b/application/src/main/kotlin/com/pokit/category/port/service/CategoryService.kt index 6999ec33..9db1e246 100644 --- a/application/src/main/kotlin/com/pokit/category/port/service/CategoryService.kt +++ b/application/src/main/kotlin/com/pokit/category/port/service/CategoryService.kt @@ -2,11 +2,12 @@ package com.pokit.category.port.service import com.pokit.category.dto.CategoriesResponse import com.pokit.category.dto.CategoryCommand -import com.pokit.category.dto.toCategoriesRespoonse +import com.pokit.category.dto.toCategoriesResponse import com.pokit.category.exception.CategoryErrorCode import com.pokit.category.model.Category import com.pokit.category.model.CategoryImage import com.pokit.category.model.CategoryStatus.UNCATEGORIZED +import com.pokit.category.model.OpenType import com.pokit.category.port.`in`.CategoryUseCase import com.pokit.category.port.out.CategoryImagePort import com.pokit.category.port.out.CategoryPort @@ -53,6 +54,7 @@ class CategoryService( categoryName = command.categoryName, categoryImage = categoryImage, userId = userId, + openType = OpenType.PRIVATE, ) ) } @@ -86,7 +88,7 @@ class CategoryService( val contentCount = contentPort.fetchContentCountByCategoryId(category.categoryId) category.copy(contentCount = contentCount) }.map { category -> - category.toCategoriesRespoonse() + category.toCategoriesResponse() } val filteredCategories = if (filterUncategorized) { @@ -101,6 +103,29 @@ class CategoryService( override fun getCategory(userId: Long, categoryId: Long): Category = categoryPort.loadCategoryOrThrow(categoryId, userId) + override fun getSharedCategory(categoryId: Long, userId: Long): Category { + val category = categoryPort.loadByIdAndOpenType(categoryId, OpenType.PUBLIC) + ?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY) + + if (category.userId == userId) { + throw InvalidRequestException(CategoryErrorCode.SHARE_ALREADY_EXISTS_CATEGORY) + } + + category.apply { + contentCount = contentPort.fetchContentCountByCategoryId(categoryId) + } + + return category + } + + @Transactional + override fun completeShare(categoryId: Long, userId: Long) { + val category = categoryPort.loadCategoryOrThrow(categoryId, userId) + .completeShare() + + categoryPort.persist(category) + } + override fun getAllCategoryImages(): List = categoryImagePort.loadAll() @@ -110,3 +135,7 @@ fun CategoryPort.loadCategoryOrThrow(categoryId: Long, userId: Long): Category { return loadByIdAndUserId(categoryId, userId) ?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY) } + +fun CategoryPort.loadByIdOrThrow(categoryId: Long) = + loadById(categoryId) + ?: throw NotFoundCustomException(CategoryErrorCode.NOT_FOUND_CATEGORY) 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 630c71fb..80c38a3a 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,11 +1,8 @@ package com.pokit.content.port.`in` import com.pokit.content.dto.request.ContentCommand -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.ContentResult -import com.pokit.content.dto.response.RemindContentResult +import com.pokit.content.dto.response.* import com.pokit.user.model.User import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice @@ -27,6 +24,11 @@ interface ContentUseCase { pageable: Pageable, ): Slice + fun getSharedContents( + categoryId: Long, + pageable: Pageable, + ): Slice + fun getContentsByCategoryName(userId: Long, categoryName: String, pageable: Pageable): Slice fun getContent(userId: Long, contentId: Long): ContentResult 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 b864db7d..e83199e0 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,7 +1,9 @@ package com.pokit.content.port.out +import com.pokit.category.model.OpenType import com.pokit.content.dto.response.ContentsResult import com.pokit.content.dto.request.ContentSearchCondition +import com.pokit.content.dto.response.SharedContentResult import com.pokit.content.model.Content import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice @@ -34,4 +36,10 @@ interface ContentPort { fun loadByContentIds(contentIds: List): List fun loadBookmarkedContentsByUserId(userId: Long, pageable: Pageable): Slice + + fun loadByCategoryIdAndOpenType( + categoryId: Long, + opentype: OpenType, + pageable: Pageable + ): Slice } 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 4bdd5b08..cd037305 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 @@ -5,6 +5,7 @@ import com.pokit.bookmark.port.out.BookmarkPort import com.pokit.category.exception.CategoryErrorCode import com.pokit.category.model.Category import com.pokit.category.model.RemindCategory +import com.pokit.category.model.OpenType import com.pokit.category.port.out.CategoryPort import com.pokit.category.port.service.loadCategoryOrThrow import com.pokit.common.exception.NotFoundCustomException @@ -91,10 +92,12 @@ class ContentService( return contents } + override fun getSharedContents(categoryId: Long, pageable: Pageable): Slice = + contentPort.loadByCategoryIdAndOpenType(categoryId, OpenType.PUBLIC, pageable) + override fun getContentsByCategoryName(userId: Long, categoryName: String, pageable: Pageable): Slice = contentPort.loadByUserIdAndCategoryName(userId, categoryName, pageable) - @Transactional override fun getContent(userId: Long, contentId: Long): ContentResult { val userLog = UserLog( diff --git a/application/src/main/kotlin/com/pokit/user/port/service/UserService.kt b/application/src/main/kotlin/com/pokit/user/port/service/UserService.kt index bcdb6ce7..7e2727e2 100644 --- a/application/src/main/kotlin/com/pokit/user/port/service/UserService.kt +++ b/application/src/main/kotlin/com/pokit/user/port/service/UserService.kt @@ -3,6 +3,7 @@ package com.pokit.user.port.service import com.pokit.category.exception.CategoryErrorCode import com.pokit.category.model.Category import com.pokit.category.model.CategoryStatus.UNCATEGORIZED +import com.pokit.category.model.OpenType import com.pokit.category.port.out.CategoryImagePort import com.pokit.category.port.out.CategoryPort import com.pokit.common.exception.ClientValidationException @@ -45,7 +46,8 @@ class UserService( val category = Category( userId = savedUser.id, categoryName = UNCATEGORIZED.displayName, - categoryImage = image + categoryImage = image, + openType = OpenType.PRIVATE, ) categoryPort.persist(category) diff --git a/domain/src/main/kotlin/com/pokit/category/dto/CategoriesResponse.kt b/domain/src/main/kotlin/com/pokit/category/dto/CategoriesResponse.kt index 30830e38..2da2719e 100644 --- a/domain/src/main/kotlin/com/pokit/category/dto/CategoriesResponse.kt +++ b/domain/src/main/kotlin/com/pokit/category/dto/CategoriesResponse.kt @@ -13,7 +13,7 @@ data class CategoriesResponse( val createdAt: String ) -fun Category.toCategoriesRespoonse(): CategoriesResponse { +fun Category.toCategoriesResponse(): CategoriesResponse { val formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd") return CategoriesResponse( diff --git a/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt b/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt index d201fc27..e48ff1a8 100644 --- a/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt +++ b/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt @@ -11,5 +11,8 @@ enum class CategoryErrorCode( UNAVAILABLE_CATEGORY_NAME("사용할 수 없는 포킷명입니다.", "C_003"), NOT_FOUND_CATEGORY_IMAGE("포킷 이미지 정보를 찾을 수 없습니다.", "C_004"), MAX_CATEGORY_LIMIT_EXCEEDED("최대 30개의 포킷을 생성할 수 있습니다. 포킷을 삭제한 뒤에 추가해주세요.", "C_005"), - NOT_FOUND_UNCATEGORIZED_IMAGE("미분류 카테고리 이미지를 찾는데 실패했습니다.", "C_006") + NOT_FOUND_UNCATEGORIZED_IMAGE("미분류 카테고리 이미지를 찾는데 실패했습니다.", "C_006"), + SHARE_ALREADY_EXISTS_CATEGORY("직접 생성한 포킷은 공유받을 수 없습니다.\n 다른 유저의 포킷을 공유받아보세요.", "C_007"), + SHARE_MAX_CATEGORY_LIMIT_EXCEEDED("최대 30개의 포킷을 생성할 수 있습니다.\n 포킷을 삭제한 뒤에 저장해주세요.", "C_008"), + SHARE_ALREADY_EXISTS_CATEGORY_NAME("같은 이름의 포킷이 존재합니다.\n 공유 받은 포킷명을 변경해야 저장 가능합니다.", "C_009"), } diff --git a/domain/src/main/kotlin/com/pokit/category/model/Category.kt b/domain/src/main/kotlin/com/pokit/category/model/Category.kt index b3d24056..64bb8808 100644 --- a/domain/src/main/kotlin/com/pokit/category/model/Category.kt +++ b/domain/src/main/kotlin/com/pokit/category/model/Category.kt @@ -8,12 +8,18 @@ data class Category( var categoryName: String, var categoryImage: CategoryImage, var contentCount: Int = 0, - val createdAt: LocalDateTime = LocalDateTime.now() + val createdAt: LocalDateTime = LocalDateTime.now(), + var openType: OpenType, ) { fun update(categoryName: String, categoryImage: CategoryImage) { this.categoryName = categoryName this.categoryImage = categoryImage } + + fun completeShare(): Category { + this.openType = OpenType.PUBLIC + return this + } } data class RemindCategory( diff --git a/domain/src/main/kotlin/com/pokit/content/dto/response/SharedContentsResult.kt b/domain/src/main/kotlin/com/pokit/content/dto/response/SharedContentsResult.kt new file mode 100644 index 00000000..9ae1f45c --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/content/dto/response/SharedContentsResult.kt @@ -0,0 +1,29 @@ +package com.pokit.content.dto.response + +import com.pokit.content.model.Content +import com.pokit.content.model.ContentDefault +import java.time.LocalDateTime + +data class SharedContentResult ( + val contentId: Long, + val data: String, + val domain: String, + val title: String, + val memo: String, + val createdAt: LocalDateTime, + val thumbNail: String, +) { + companion object { + fun of(content: Content): SharedContentResult { + return SharedContentResult( + contentId = content.id, + data = content.data, + domain = content.domain, + title = content.title, + memo = content.memo, + createdAt = content.createdAt, + thumbNail = content.thumbNail ?: ContentDefault.THUMB_NAIL + ) + } + } +}