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 index dc908d62..7d0446f8 100644 --- a/adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt +++ b/adapters/in-web/src/main/kotlin/com/pokit/category/CategoryShareController.kt @@ -6,6 +6,7 @@ import com.pokit.category.dto.request.DuplicateCategoryRequest import com.pokit.category.dto.response.SharedContentsResponse import com.pokit.category.port.`in`.CategoryUseCase import com.pokit.common.wrapper.ResponseWrapper.wrapOk +import com.pokit.common.wrapper.ResponseWrapper.wrapUnit import com.pokit.content.port.`in`.ContentUseCase import io.swagger.v3.oas.annotations.Operation import org.springframework.data.domain.Pageable @@ -64,4 +65,14 @@ class CategoryShareController( ) .wrapOk() + @Operation(summary = "포킷 초대 수락 API") + @PostMapping("/accept/{categoryId}") + fun acceptCategory( + @AuthenticationPrincipal user: PrincipalUser, + @PathVariable categoryId: Long, + ): ResponseEntity { + return categoryUseCase.acceptCategory(user.id, categoryId) + .wrapUnit() + } + } diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/SharedCategoryAdapter.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/SharedCategoryAdapter.kt new file mode 100644 index 00000000..c67d0c11 --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/impl/SharedCategoryAdapter.kt @@ -0,0 +1,24 @@ +package com.pokit.out.persistence.category.impl + +import com.pokit.category.model.SharedCategory +import com.pokit.category.port.out.SharedCategoryPort +import com.pokit.out.persistence.category.persist.SharedCategoryEntity +import com.pokit.out.persistence.category.persist.SharedCategoryRepository +import com.pokit.out.persistence.category.persist.toDomain +import org.springframework.stereotype.Repository + +@Repository +class SharedCategoryAdapter( + private val sharedCategoryRepository: SharedCategoryRepository +) : SharedCategoryPort { + override fun persist(sharedCategory: SharedCategory): SharedCategory { + return sharedCategoryRepository.save(SharedCategoryEntity.of(sharedCategory)) + .toDomain() + } + + override fun loadByUserIdAndCategoryId(userId: Long, categoryId: Long): SharedCategory? { + return sharedCategoryRepository.findByUserIdAndCategoryId(userId, categoryId) + ?.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 f9bd9342..84f33f7d 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 @@ -27,6 +27,12 @@ class CategoryEntity( @Column(name = "open_type") @Enumerated(EnumType.STRING) var openType: OpenType = OpenType.PRIVATE, + + @Column(name = "user_count") + var userCount: Int = 0, + + @Column(name = "is_shared") + var isShared: Boolean = false ) : BaseEntity() { @Column(name = "is_deleted") @@ -44,6 +50,8 @@ class CategoryEntity( name = category.categoryName, image = CategoryImageEntity.of(category.categoryImage), openType = category.openType, + userCount = category.userCount, + isShared = category.isShared ) } } @@ -55,4 +63,6 @@ fun CategoryEntity.toDomain() = Category( userId = this.userId, createdAt = this.createdAt, openType = this.openType, + userCount = this.userCount, + isShared = this.isShared ) diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryEntity.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryEntity.kt new file mode 100644 index 00000000..a9f984eb --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryEntity.kt @@ -0,0 +1,31 @@ +package com.pokit.out.persistence.category.persist + +import com.pokit.category.model.SharedCategory +import com.pokit.out.persistence.BaseEntity +import jakarta.persistence.* + +@Table(name = "SHARED_CATEGORY") +@Entity +class SharedCategoryEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = 0L, + + @Column(name = "user_id") + val userId: Long, + + @Column(name = "category_id") + val categoryId: Long, +) : BaseEntity() { + companion object { + fun of(sharedCategory: SharedCategory) = SharedCategoryEntity( + userId = sharedCategory.userId, + categoryId = sharedCategory.categoryId + ) + } +} + +internal fun SharedCategoryEntity.toDomain() = SharedCategory( + userId = this.userId, + categoryId = this.categoryId +) diff --git a/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryRepository.kt b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryRepository.kt new file mode 100644 index 00000000..41d04d13 --- /dev/null +++ b/adapters/out-persistence/src/main/kotlin/com/pokit/out/persistence/category/persist/SharedCategoryRepository.kt @@ -0,0 +1,7 @@ +package com.pokit.out.persistence.category.persist + +import org.springframework.data.jpa.repository.JpaRepository + +interface SharedCategoryRepository : JpaRepository { + fun findByUserIdAndCategoryId(userId: Long, categoryId: Long): SharedCategoryEntity? +} 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 28516d1c..ab9adc57 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 @@ -18,4 +18,5 @@ interface CategoryUseCase { fun getSharedCategory(categoryId: Long, userId: Long): Category fun completeShare(categoryId: Long) fun duplicateCategory(originCategoryId: Long, categoryName: String, userId: Long, categoryImageId: Int) + fun acceptCategory(userId: Long, categoryId: Long) } diff --git a/application/src/main/kotlin/com/pokit/category/port/out/SharedCategoryPort.kt b/application/src/main/kotlin/com/pokit/category/port/out/SharedCategoryPort.kt new file mode 100644 index 00000000..151e5042 --- /dev/null +++ b/application/src/main/kotlin/com/pokit/category/port/out/SharedCategoryPort.kt @@ -0,0 +1,9 @@ +package com.pokit.category.port.out + +import com.pokit.category.model.SharedCategory + +interface SharedCategoryPort { + fun persist(sharedCategory: SharedCategory): SharedCategory + + fun loadByUserIdAndCategoryId(userId: Long, categoryId: Long): SharedCategory? +} 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 bbfa7f88..f80bde59 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 @@ -4,15 +4,14 @@ import com.pokit.category.dto.CategoriesResponse import com.pokit.category.dto.CategoryCommand 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.* import com.pokit.category.model.CategoryStatus.UNCATEGORIZED -import com.pokit.category.model.OpenType -import com.pokit.category.model.duplicate import com.pokit.category.port.`in`.CategoryUseCase import com.pokit.category.port.out.CategoryImagePort import com.pokit.category.port.out.CategoryPort +import com.pokit.category.port.out.SharedCategoryPort import com.pokit.common.exception.AlreadyExistsException +import com.pokit.common.exception.ClientValidationException import com.pokit.common.exception.InvalidRequestException import com.pokit.common.exception.NotFoundCustomException import com.pokit.content.port.out.ContentPort @@ -27,7 +26,8 @@ import org.springframework.transaction.annotation.Transactional class CategoryService( private val categoryPort: CategoryPort, private val categoryImagePort: CategoryImagePort, - private val contentPort: ContentPort + private val contentPort: ContentPort, + private val sharedCategoryPort: SharedCategoryPort ) : CategoryUseCase { companion object { private const val MAX_CATEGORY_COUNT = 30 @@ -154,6 +154,22 @@ class CategoryService( contentPort.duplicateContent(originCategoryId, newCategory.categoryId) } + @Transactional + override fun acceptCategory(userId: Long, categoryId: Long) { + val category = categoryPort.loadByIdOrThrow(categoryId) + + sharedCategoryPort.loadByUserIdAndCategoryId(userId, categoryId) + ?.let { throw ClientValidationException(CategoryErrorCode.ALREADY_ACCEPTED) } + + category.addUserCount() + category.shared() + + categoryPort.persist(category) + + val sharedCategory = SharedCategory(userId = userId, categoryId = category.categoryId) + sharedCategoryPort.persist(sharedCategory) + } + override fun getAllCategoryImages(): List = categoryImagePort.loadAll() 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 9b0d9f1e..30d7a94c 100644 --- a/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt +++ b/domain/src/main/kotlin/com/pokit/category/exception/CategoryErrorCode.kt @@ -14,5 +14,6 @@ enum class CategoryErrorCode( NOT_FOUND_UNCATEGORIZED_IMAGE("미분류 카테고리 이미지를 찾는데 실패했습니다.", "CA_006"), SHARE_ALREADY_EXISTS_CATEGORY("직접 생성한 포킷은 공유받을 수 없습니다.\n 다른 유저의 포킷을 공유받아보세요.", "CA_007"), NOT_FOUND_UNCATEGORIZED("사용자가 미분류 카테고리가 없습니다.(서버 에러)", "CA_008"), + ALREADY_ACCEPTED("이미 초대를 수락한 포킷입니다.", "CA_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 b8b4534d..f7a312f5 100644 --- a/domain/src/main/kotlin/com/pokit/category/model/Category.kt +++ b/domain/src/main/kotlin/com/pokit/category/model/Category.kt @@ -10,6 +10,8 @@ data class Category( var contentCount: Int = 0, val createdAt: LocalDateTime = LocalDateTime.now(), var openType: OpenType, + var userCount: Int = 0, + var isShared: Boolean = false, ) { fun update(categoryName: String, categoryImage: CategoryImage) { this.categoryName = categoryName @@ -20,6 +22,14 @@ data class Category( this.openType = OpenType.PUBLIC return this } + + fun addUserCount() { + this.userCount++ + } + + fun shared() { + this.isShared = true + } } data class RemindCategory( diff --git a/domain/src/main/kotlin/com/pokit/category/model/SharedCategory.kt b/domain/src/main/kotlin/com/pokit/category/model/SharedCategory.kt new file mode 100644 index 00000000..e570dd89 --- /dev/null +++ b/domain/src/main/kotlin/com/pokit/category/model/SharedCategory.kt @@ -0,0 +1,7 @@ +package com.pokit.category.model + +data class SharedCategory( + val id: Long = 0, + val userId: Long, + val categoryId: Long +)