diff --git a/app/src/main/kotlin/com/wespot/comment/PostCommentDeletedController.kt b/app/src/main/kotlin/com/wespot/comment/PostCommentDeletedController.kt new file mode 100644 index 00000000..d84c4f19 --- /dev/null +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentDeletedController.kt @@ -0,0 +1,27 @@ +package com.wespot.comment + +import com.wespot.comment.port.`in`.PostCommentDeletedUseCase +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/v1/post/comment") +class PostCommentDeletedController( + private val postCommentDeletedUseCase: PostCommentDeletedUseCase +) { + + @DeleteMapping("/{commentId}") + fun deletePostComment( + @PathVariable commentId: Long + ): ResponseEntity { + postCommentDeletedUseCase.deleteComment(commentId) + + return ResponseEntity.noContent() + .build() + } + + +} diff --git a/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt b/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt index 19a3f41a..95ff34dd 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt @@ -1,9 +1,11 @@ package com.wespot.comment import com.wespot.comment.port.`in`.PostCommentLikeUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -13,11 +15,11 @@ class PostCommentLikeController( private val postCommentLikeUseCase: PostCommentLikeUseCase ) { - @PatchMapping("/{commentId}/like") + @PostMapping("/{commentId}/like") fun likePostComment(@PathVariable commentId: Long): ResponseEntity { postCommentLikeUseCase.likeComment(commentId) - return ResponseEntity.noContent() + return ResponseEntity.status(HttpStatus.CREATED) .build() } diff --git a/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt b/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt index 1d2fff4b..b1a95f3f 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt @@ -1,9 +1,10 @@ package com.wespot.comment import com.wespot.comment.port.`in`.PostCommentReportUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -13,11 +14,11 @@ class PostCommentReportController( private val postCommentReportUseCase: PostCommentReportUseCase ) { - @PatchMapping("/{commentId}/report") - fun likePostComment(@PathVariable commentId: Long): ResponseEntity { + @PostMapping("/{commentId}/report") + fun reportPostComment(@PathVariable commentId: Long): ResponseEntity { postCommentReportUseCase.reportComment(commentId) - return ResponseEntity.noContent() + return ResponseEntity.status(HttpStatus.CREATED) .build() } diff --git a/app/src/main/kotlin/com/wespot/post/ModifyPostNotificationSettingController.kt b/app/src/main/kotlin/com/wespot/post/ModifyPostNotificationSettingController.kt new file mode 100644 index 00000000..d0642587 --- /dev/null +++ b/app/src/main/kotlin/com/wespot/post/ModifyPostNotificationSettingController.kt @@ -0,0 +1,27 @@ +package com.wespot.post + +import com.wespot.post.dto.request.ModifyPostNotificationSettingRequest +import com.wespot.post.port.`in`.ModifyPostNotificationSettingUseCase +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/v2/post") +class ModifyPostNotificationSettingController( + private val modifyPostNotificationSettingUseCase: ModifyPostNotificationSettingUseCase +) { + + @PatchMapping("/notification-setting") + fun modifyPostNotificationSetting( + @RequestBody modifyPostNotificationSettingRequest: ModifyPostNotificationSettingRequest + ): ResponseEntity { + modifyPostNotificationSettingUseCase.changeSetting(modifyPostNotificationSettingRequest = modifyPostNotificationSettingRequest) + + return ResponseEntity.noContent() + .build() + } + +} diff --git a/app/src/main/kotlin/com/wespot/post/PostBlockController.kt b/app/src/main/kotlin/com/wespot/post/PostBlockController.kt index 0de48793..3fd1eb19 100644 --- a/app/src/main/kotlin/com/wespot/post/PostBlockController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostBlockController.kt @@ -1,6 +1,7 @@ package com.wespot.post import com.wespot.post.port.`in`.PostBlockUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -17,7 +18,8 @@ class PostBlockController( fun blockPost(@PathVariable postId: Long): ResponseEntity { postBlockUseCase.blockPost(postId) - return ResponseEntity.noContent().build() + return ResponseEntity.status(HttpStatus.CREATED) + .build() } } diff --git a/app/src/main/kotlin/com/wespot/post/PostCategoryController.kt b/app/src/main/kotlin/com/wespot/post/PostCategoryController.kt index 9e9f3eb5..f0143304 100644 --- a/app/src/main/kotlin/com/wespot/post/PostCategoryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostCategoryController.kt @@ -1,7 +1,7 @@ package com.wespot.post -import com.wespot.post.dto.response.PostCategoryDetailResponses -import com.wespot.post.dto.response.PostCategoryResponse +import com.wespot.post.dto.response.FilterChipResponse +import com.wespot.post.dto.response.PostCategoryItemsResponse import com.wespot.post.port.`in`.PostCategoryUseCase import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -15,14 +15,14 @@ class PostCategoryController( ) { @GetMapping("/details") - fun getPostCategoryDetailResponse(): ResponseEntity> { + fun getPostCategoryDetailResponse(): ResponseEntity> { val response = postCategoryUseCase.getCategoriesDetail() return ResponseEntity.ok(response) } @GetMapping - fun getPostCategoryResponse(): ResponseEntity> { + fun getPostCategoryResponse(): ResponseEntity> { val response = postCategoryUseCase.getCategories() return ResponseEntity.ok(response) diff --git a/app/src/main/kotlin/com/wespot/post/PostEditController.kt b/app/src/main/kotlin/com/wespot/post/PostEditController.kt index 43480a70..740ab2dc 100644 --- a/app/src/main/kotlin/com/wespot/post/PostEditController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostEditController.kt @@ -2,6 +2,7 @@ package com.wespot.post import com.wespot.post.dto.request.UpdatedPostRequest import com.wespot.post.port.`in`.PostEditUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* diff --git a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index 6a61e834..a5fc56fe 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -1,64 +1,82 @@ package com.wespot.post -import com.wespot.post.dto.response.PostResponse -import com.wespot.post.port.`in`.PostInquiryByCategoryUseCase +import com.wespot.common.dto.PostPagingResponse +import com.wespot.post.dto.response.PostComponentResponse +import com.wespot.post.port.`in`.PostInquiryUseCase import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/api/v1/post") class PostInquiryController( - private val postInquiryByCategoryUseCase: PostInquiryByCategoryUseCase + private val postInquiryByCategoryUseCase: PostInquiryUseCase ) { - @GetMapping("/all") - fun findAllPosts(): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findAllPosts() - - return ResponseEntity.ok(responses) - } - @GetMapping("/details") - fun findPostsByCategoryId(categoryId: Long): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findPostsByCategoryId(categoryId) + fun findPostsByCategoryId( + @RequestParam categoryId: Long, + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findPostsByCategoryId( + categoryId, + inquirySize = inquirySize, + cursorId = cursorId + ) return ResponseEntity.ok(responses) } @GetMapping - fun findPostsByMajorCategoryName(majorCategoryName: String): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findPostsByMajorCategoryName(majorCategoryName) + fun findPostsByMajorCategoryName( + @RequestParam majorCategoryName: String, + @RequestParam(required = false, defaultValue = "0") countOfPostsViewed: Long, + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findPostsByMajorCategoryName( + majorCategoryName = majorCategoryName, + countOfPostsViewed = countOfPostsViewed, + inquirySize = inquirySize, + cursorId = cursorId + ) return ResponseEntity.ok(responses) } @GetMapping("/{postId}") - fun findDetailPost(@PathVariable postId: Long): ResponseEntity { + fun findDetailPost(@PathVariable postId: Long): ResponseEntity { val responses = postInquiryByCategoryUseCase.findPostById(postId) return ResponseEntity.ok(responses) } @GetMapping("/commented") - fun findCommentedPosts(): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findCommentedPosts() + fun findCommentedPosts( + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findCommentedPosts(inquirySize = inquirySize, cursorId = cursorId) return ResponseEntity.ok(responses) } @GetMapping("/scrapped") - fun findScrappedPosts(): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findScrappedPosts() + fun findScrappedPosts( + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findScrappedPosts(inquirySize = inquirySize, cursorId = cursorId) return ResponseEntity.ok(responses) } @GetMapping("/written") - fun findWrittenPosts(): ResponseEntity> { - val responses = postInquiryByCategoryUseCase.findWrittenPosts() + fun findWrittenPosts( + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findWrittenPosts(inquirySize = inquirySize, cursorId = cursorId) return ResponseEntity.ok(responses) } diff --git a/app/src/main/kotlin/com/wespot/post/PostLikeController.kt b/app/src/main/kotlin/com/wespot/post/PostLikeController.kt index 97bc5058..a3095dac 100644 --- a/app/src/main/kotlin/com/wespot/post/PostLikeController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostLikeController.kt @@ -1,9 +1,10 @@ package com.wespot.post import com.wespot.post.port.`in`.PostLikeUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -14,11 +15,11 @@ class PostLikeController( private val postLikeUseCase: PostLikeUseCase ) { - @PatchMapping("/{postId}/like") + @PostMapping("/{postId}/like") fun likePost(@PathVariable postId: Long): ResponseEntity { postLikeUseCase.likePost(postId) - return ResponseEntity.noContent() + return ResponseEntity.status(HttpStatus.CREATED) .build() } diff --git a/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt b/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt index 457ea030..665c0680 100644 --- a/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt @@ -1,9 +1,10 @@ package com.wespot.post import com.wespot.post.port.`in`.PostNotificationUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -14,11 +15,11 @@ class PostNotificationSettingController( private val postNotificationUseCase: PostNotificationUseCase ) { - @PatchMapping("/{postId}/notification/comment") + @PostMapping("/{postId}/notification/comment") fun updatePostNotificationSettingForComment(@PathVariable postId: Long): ResponseEntity { postNotificationUseCase.toggleNotification(postId) - return ResponseEntity.noContent() + return ResponseEntity.status(HttpStatus.CREATED) .build() } diff --git a/app/src/main/kotlin/com/wespot/post/PostReportController.kt b/app/src/main/kotlin/com/wespot/post/PostReportController.kt index f5e20675..f5942a67 100644 --- a/app/src/main/kotlin/com/wespot/post/PostReportController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostReportController.kt @@ -2,8 +2,12 @@ package com.wespot.post import com.wespot.post.dto.request.PostReportRequest import com.wespot.post.port.`in`.PostReportUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api/v1/post") @@ -14,7 +18,9 @@ class PostReportController( @PostMapping("/{postId}/report") fun reportPost(@PathVariable postId: Long, request: PostReportRequest): ResponseEntity { postReportUseCase.reportPost(postId, request) - return ResponseEntity.noContent().build() + + return ResponseEntity.status(HttpStatus.CREATED) + .build() } } diff --git a/app/src/main/kotlin/com/wespot/post/PostScrapController.kt b/app/src/main/kotlin/com/wespot/post/PostScrapController.kt index 1da0cc7d..d531ba8b 100644 --- a/app/src/main/kotlin/com/wespot/post/PostScrapController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostScrapController.kt @@ -1,8 +1,10 @@ package com.wespot.post import com.wespot.post.port.`in`.PostScrapUseCase +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -12,11 +14,11 @@ class PostScrapController( private val postScrapUseCase: PostScrapUseCase ) { - @PatchMapping("/{postId}/scrap") - fun scrapPost(postId: Long): ResponseEntity { + @PostMapping("/{postId}/scrap") + fun scrapPost(@PathVariable postId: Long): ResponseEntity { postScrapUseCase.scrapPost(postId) - return ResponseEntity.noContent() + return ResponseEntity.status(HttpStatus.CREATED) .build() } diff --git a/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt b/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt index 5357c0e6..426408cb 100644 --- a/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt @@ -1,10 +1,11 @@ package com.wespot.post -import com.wespot.post.dto.response.PostResponse +import com.wespot.common.dto.PostPagingResponse import com.wespot.post.port.`in`.PostSearchedUseCase import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @@ -14,8 +15,16 @@ class PostSearchedController( ) { @GetMapping("/search") - fun searchPost(keyword: String): ResponseEntity> { - val response = postSearchedUseCase.search(keyword = keyword) + fun searchPost( + @RequestParam keyword: String, + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val response = postSearchedUseCase.search( + keyword = keyword, + inquirySize = inquirySize, + cursorId = cursorId + ) return ResponseEntity.ok(response) } diff --git a/app/src/main/kotlin/com/wespot/report/ReportReasonController.kt b/app/src/main/kotlin/com/wespot/report/ReportReasonController.kt new file mode 100644 index 00000000..4d99826b --- /dev/null +++ b/app/src/main/kotlin/com/wespot/report/ReportReasonController.kt @@ -0,0 +1,23 @@ +package com.wespot.report + +import com.wespot.report.dto.ReportReasonResponse +import com.wespot.report.port.`in`.ReportReasonUseCase +import org.springframework.http.ResponseEntity +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/reports") +class ReportReasonController( + private val reportReasonService: ReportReasonUseCase +) { + + @GetMapping + fun getReportReasons(): ResponseEntity> { + val response = reportReasonService.getReportReasons() + + return ResponseEntity.ok(response) + } + +} diff --git a/app/src/test/kotlin/com/wespot/post/vo/PostSearchKeywordTest.kt b/app/src/test/kotlin/com/wespot/post/vo/PostSearchKeywordTest.kt new file mode 100644 index 00000000..8383d52c --- /dev/null +++ b/app/src/test/kotlin/com/wespot/post/vo/PostSearchKeywordTest.kt @@ -0,0 +1,35 @@ +package com.wespot.post.vo + +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals + +class PostSearchKeywordTest { + + @Test + fun `단어가 3개 이하이면 | 로 이어진 키워드가 나옵니다`() { + // given + val keyword = "안녕 만나서 반가워" + val postSearchKeyword = PostSearchKeyword.from(keyword) + + // when + val actual = postSearchKeyword.keywordsToSearchInRegex() + + // then + assertEquals("안녕|만나서|반가워", actual) + } + + @Test + fun `단어가 3개 이상이면 하나의 단어로 나옵니다`() { + // given + val keyword = "안녕 만나서 반가워 오늘은 날씨가 좋네요" + val postSearchKeyword = PostSearchKeyword.from(keyword) + + // when + val actual = postSearchKeyword.keywordsToSearchInRegex() + + // then + assertEquals("안녕 만나서 반가워 오늘은 날씨가 좋네요", actual) + } + + +} diff --git a/core/src/main/kotlin/com/wespot/comment/port/in/PostCommentDeletedUseCase.kt b/core/src/main/kotlin/com/wespot/comment/port/in/PostCommentDeletedUseCase.kt new file mode 100644 index 00000000..fc148030 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/comment/port/in/PostCommentDeletedUseCase.kt @@ -0,0 +1,7 @@ +package com.wespot.comment.port.`in` + +interface PostCommentDeletedUseCase { + + fun deleteComment(commentId: Long) + +} diff --git a/core/src/main/kotlin/com/wespot/comment/port/out/PostCommentPort.kt b/core/src/main/kotlin/com/wespot/comment/port/out/PostCommentPort.kt index cb8f11eb..2456fe56 100644 --- a/core/src/main/kotlin/com/wespot/comment/port/out/PostCommentPort.kt +++ b/core/src/main/kotlin/com/wespot/comment/port/out/PostCommentPort.kt @@ -14,4 +14,6 @@ interface PostCommentPort { fun deleteByPostId(postId: Long) + fun deleteByCommentId(commentId:Long) + } diff --git a/core/src/main/kotlin/com/wespot/comment/service/PostCommentDeletedService.kt b/core/src/main/kotlin/com/wespot/comment/service/PostCommentDeletedService.kt new file mode 100644 index 00000000..29622b3e --- /dev/null +++ b/core/src/main/kotlin/com/wespot/comment/service/PostCommentDeletedService.kt @@ -0,0 +1,31 @@ +package com.wespot.comment.service + +import com.wespot.EventUtils +import com.wespot.auth.service.SecurityUtils +import com.wespot.comment.event.PostCommentDeleteEvent +import com.wespot.comment.port.`in`.PostCommentDeletedUseCase +import com.wespot.comment.port.out.PostCommentPort +import com.wespot.user.port.out.UserPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class PostCommentDeletedService( + private val userPort: UserPort, + private val postCommentPort: PostCommentPort +) : PostCommentDeletedUseCase { + + @Transactional + override fun deleteComment(commentId: Long) { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val postComment = postCommentPort.findById(id = commentId) ?: throw IllegalArgumentException("존재하지 않는 댓글입니다.") + + if (!postComment.isAuthor(loginUser.id)) { + throw IllegalArgumentException("댓글 작성자만 삭제할 수 있습니다.") + } + + postCommentPort.deleteByCommentId(commentId) + EventUtils.publish(PostCommentDeleteEvent(postComment)) + } + +} diff --git a/core/src/main/kotlin/com/wespot/comment/service/PostCommentLikeService.kt b/core/src/main/kotlin/com/wespot/comment/service/PostCommentLikeService.kt index 20449501..ed60c80c 100644 --- a/core/src/main/kotlin/com/wespot/comment/service/PostCommentLikeService.kt +++ b/core/src/main/kotlin/com/wespot/comment/service/PostCommentLikeService.kt @@ -27,7 +27,7 @@ class PostCommentLikeService( val removedLikePostComment = postComment.removeLike() postCommentPort.save(removedLikePostComment) } - ?: { + ?: run { val postCommentLike = PostCommentLike(postCommentId = commentId, userId = loginUser.id) postCommentLikePort.save(postCommentLike) val addedLikePostComment = postComment.addLike() diff --git a/core/src/main/kotlin/com/wespot/comment/service/PostCommentReportService.kt b/core/src/main/kotlin/com/wespot/comment/service/PostCommentReportService.kt index ba3d0272..ab778b86 100644 --- a/core/src/main/kotlin/com/wespot/comment/service/PostCommentReportService.kt +++ b/core/src/main/kotlin/com/wespot/comment/service/PostCommentReportService.kt @@ -27,7 +27,7 @@ class PostCommentReportService( val removedReportPostComment = postComment.removeReport() postCommentPort.save(removedReportPostComment) } - ?: { + ?: run { val postCommentReport = PostCommentReport(postCommentId = commentId, userId = loginUser.id) postCommentReportPort.save(postCommentReport) val addedReportPostComment = postComment.addReport() diff --git a/core/src/main/kotlin/com/wespot/comment/service/listener/PostCommentDeleteEventListener.kt b/core/src/main/kotlin/com/wespot/comment/service/listener/PostCommentDeleteEventListener.kt new file mode 100644 index 00000000..de130859 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/comment/service/listener/PostCommentDeleteEventListener.kt @@ -0,0 +1,23 @@ +package com.wespot.comment.service.listener + +import com.wespot.comment.event.PostCommentDeleteEvent +import com.wespot.comment.port.out.PostCommentLikePort +import com.wespot.comment.port.out.PostCommentReportPort +import org.springframework.stereotype.Component +import org.springframework.transaction.event.TransactionPhase +import org.springframework.transaction.event.TransactionalEventListener + +@Component +class PostCommentDeleteEventListener( + private val postCommentLikePort: PostCommentLikePort, + private val postCommentReportPort: PostCommentReportPort +) { + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + private fun listenPostCommentDeleteEvent(postCommentDeleteEvent: PostCommentDeleteEvent) { + val commentId = listOf(postCommentDeleteEvent.postComment.id) + postCommentLikePort.deleteByPostCommentIdIn(commentId) + postCommentReportPort.deleteByPostCommentIdIn(commentId) + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt new file mode 100644 index 00000000..b19f0798 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt @@ -0,0 +1,15 @@ +package com.wespot.common.dto + +import com.wespot.common.dto.view.ImageContentV2Response + +data class PostPagingResponse( + val data: List, + + val background: ImageContentV2Response? = null, + val thumbnail: ImageContentV2Response? = null, + + val lastCursorId: Long? = null, + val hasNext: Boolean +) { + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/ColorResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/view/ColorResponse.kt new file mode 100644 index 00000000..ad7e4420 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/ColorResponse.kt @@ -0,0 +1,23 @@ +package com.wespot.common.dto.view + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.view.color.Color + +@JsonInclude(JsonInclude.Include.NON_NULL) +class ColorResponse( + val value: String, + val type: String, +) { + + companion object { + + fun from(color: Color): ColorResponse { + return ColorResponse( + value = color.value, + type = color.type + ) + } + + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/GradationResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/view/GradationResponse.kt new file mode 100644 index 00000000..903e04f9 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/GradationResponse.kt @@ -0,0 +1,23 @@ +package com.wespot.common.dto.view + +import com.wespot.post.nudge.server_driven.Gradation +import com.wespot.view.color.Color + +data class GradationResponse( + val startColor: Color, + val endColor: Color, + val angle: Int, +) { + companion object { + + fun from(gradation: Gradation): GradationResponse { + return GradationResponse( + startColor = gradation.startColor, + endColor = gradation.endColor, + angle = gradation.angle + ) + } + + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/HotPostComponentResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/view/HotPostComponentResponse.kt new file mode 100644 index 00000000..81bcc509 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/HotPostComponentResponse.kt @@ -0,0 +1,83 @@ +package com.wespot.common.dto.view + +import com.wespot.post.nudge.server_driven.HotPostComponent + +class HotPostComponentResponse( + val type: String, + val id: Long, + val content: HotPostContentResponse +) { + + data class HotPostContentResponse( + val title: HotPostTitleResponse?, + val posts: List + ) { + + data class HotPostTitleResponse( + val icon: IconV2Response, + val text: RichTextV2Response, + ) { + + } + + data class HotPostResponse( + val headerSection: HotPostHeaderSectionResponse, + val infoSection: HotPostInfoSectionResponse, + val createdAt: RichTextV2Response, + val gradation: GradationResponse, + ) { + + companion object { + } + + } + + data class HotPostHeaderSectionResponse( + val profileImage: ImageContentV2Response, + val nickname: RichTextV2Response, + ) { + + } + + data class HotPostInfoSectionResponse( + val title: RichTextV2Response?, + val description: RichTextV2Response, + ) { + + } + + } + + companion object { + + fun from(hotPostComponent: HotPostComponent): HotPostComponentResponse { + val title = hotPostComponent.content.title + val posts = hotPostComponent.content.posts + return HotPostComponentResponse( + type = hotPostComponent.type, + id = hotPostComponent.id, + content = HotPostContentResponse( + title = HotPostContentResponse.HotPostTitleResponse( + icon = IconV2Response.from(title.icon), + text = RichTextV2Response.from(title.text) + ), + posts = posts.map { post -> + HotPostContentResponse.HotPostResponse( + headerSection = HotPostContentResponse.HotPostHeaderSectionResponse( + profileImage = ImageContentV2Response.from(post.headerSection.profileImage), + nickname = RichTextV2Response.from(post.headerSection.nickname) + ), + infoSection = HotPostContentResponse.HotPostInfoSectionResponse( + title = post.infoSection.title?.let { RichTextV2Response.from(it) }, + description = RichTextV2Response.from(post.infoSection.description) + ), + createdAt = RichTextV2Response.from(post.createdAt), + gradation = GradationResponse.from(post.gradation) + ) + } + ) + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/IconV2Response.kt b/core/src/main/kotlin/com/wespot/common/dto/view/IconV2Response.kt new file mode 100644 index 00000000..ca0b9b6c --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/IconV2Response.kt @@ -0,0 +1,23 @@ +package com.wespot.common.dto.view + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.view.icon.IconV2 + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class IconV2Response( + val url: String, + val color: ColorResponse +) { + + companion object { + + fun from(icon: IconV2): IconV2Response { + return IconV2Response( + url = icon.url, + color = ColorResponse.from(icon.color) + ) + } + + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt b/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt new file mode 100644 index 00000000..0351b083 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt @@ -0,0 +1,24 @@ +package com.wespot.common.dto.view + +import com.wespot.view.image.ImageContentV2 + +data class ImageContentV2Response( + val url: String, + val width: Int? = null, + val height: Int? = null, +) { + + companion object { + + fun from(image: ImageContentV2): ImageContentV2Response { + return ImageContentV2Response( + url = image.url, + width = image.width, + height = image.height + ) + } + + } + + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/MessageComponentResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/view/MessageComponentResponse.kt new file mode 100644 index 00000000..09045dec --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/MessageComponentResponse.kt @@ -0,0 +1,35 @@ +package com.wespot.common.dto.view + +import com.wespot.post.nudge.server_driven.MessageComponent + +data class MessageComponentResponse( + val type: String, + val id: Long, + val content: MessageContentComponentResponse, +) { + + data class MessageContentComponentResponse( + val thumbnail: ImageContentV2Response, + val title: RichTextV2Response, + val description: RichTextV2Response, + val icon: IconV2Response, + ) + + companion object { + fun from( + messageComponent: MessageComponent + ): MessageComponentResponse { + return MessageComponentResponse( + id = messageComponent.id, + type = messageComponent.type, + content = MessageContentComponentResponse( + thumbnail = ImageContentV2Response.from(messageComponent.content.thumbnail), + title = RichTextV2Response.from(messageComponent.content.title), + description = RichTextV2Response.from(messageComponent.content.description), + icon = IconV2Response.from(messageComponent.content.icon) + ) + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/RichTextV2Response.kt b/core/src/main/kotlin/com/wespot/common/dto/view/RichTextV2Response.kt new file mode 100644 index 00000000..95e86169 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/RichTextV2Response.kt @@ -0,0 +1,26 @@ +package com.wespot.common.dto.view + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.view.color.Color +import com.wespot.view.text.RichTextV2 + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class RichTextV2Response( + val text: String, + val color: Color, + val typography: String, + val maxLine: Int? = null, +) { + + companion object { + fun from(richText: RichTextV2): RichTextV2Response { + return RichTextV2Response( + text = richText.text, + color = richText.color, + typography = richText.typography, + maxLine = richText.maxLine + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/dto/view/VoteComponentResponse.kt b/core/src/main/kotlin/com/wespot/common/dto/view/VoteComponentResponse.kt new file mode 100644 index 00000000..d3ddda00 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/view/VoteComponentResponse.kt @@ -0,0 +1,67 @@ +package com.wespot.common.dto.view + +import com.wespot.post.nudge.server_driven.VoteComponent + +data class VoteComponentResponse( + val id: Long, + val type: String, + val content: VoteContentResponse, +) { + + data class VoteContentResponse( + val badge: VoteBadgeResponse, + val text: RichTextV2Response, + val actionIcon: VoteActionIconResponse, + val gradation: GradationResponse, + ) { + + data class VoteBadgeResponse( + val backgroundColor: ColorResponse, + val text: RichTextV2Response, + ) { + companion object { + + fun from(badge: VoteComponent.VoteContent.VoteBadge): VoteBadgeResponse { + return VoteBadgeResponse( + backgroundColor = ColorResponse.from(badge.backgroundColor), + text = RichTextV2Response.from(badge.text) + ) + } + + } + } + + data class VoteActionIconResponse( + val backgroundColor: ColorResponse, + val icon: IconV2Response, + ) { + companion object { + + fun from(actionIcon: VoteComponent.VoteContent.VoteActionIcon): VoteActionIconResponse { + return VoteActionIconResponse( + backgroundColor = ColorResponse.from(actionIcon.backgroundColor), + icon = IconV2Response.from(actionIcon.icon) + ) + } + + } + } + } + + companion object { + + fun from(voteComponent: VoteComponent): VoteComponentResponse { + return VoteComponentResponse( + id = voteComponent.id, + type = voteComponent.type, + content = VoteContentResponse( + badge = VoteContentResponse.VoteBadgeResponse.from(voteComponent.content.badge), + text = RichTextV2Response.from(voteComponent.content.text), + actionIcon = VoteContentResponse.VoteActionIconResponse.from(voteComponent.content.actionIcon), + gradation = GradationResponse.from(voteComponent.content.gradation) + ) + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/common/service/view/OnBoardingService.kt b/core/src/main/kotlin/com/wespot/common/service/view/OnBoardingService.kt index 34cfc904..ed41ce89 100644 --- a/core/src/main/kotlin/com/wespot/common/service/view/OnBoardingService.kt +++ b/core/src/main/kotlin/com/wespot/common/service/view/OnBoardingService.kt @@ -65,6 +65,7 @@ class OnBoardingService( ): OnBoardingCategory { val iosLatestVersion = latestVersionPort.get(LatestVersionType.IOS) val androidLatestVersion = latestVersionPort.get(LatestVersionType.ANDROID) + print(androidLatestVersion) val userVersion = userVersionPort.findByUserId(userId) if (userVersion?.hasLatestVersion( iosLatestVersion = iosLatestVersion, diff --git a/core/src/main/kotlin/com/wespot/image/dto/PresignedResponse.kt b/core/src/main/kotlin/com/wespot/image/dto/PresignedResponse.kt index a0c4773d..a65a2a0c 100644 --- a/core/src/main/kotlin/com/wespot/image/dto/PresignedResponse.kt +++ b/core/src/main/kotlin/com/wespot/image/dto/PresignedResponse.kt @@ -2,6 +2,6 @@ package com.wespot.image.dto class PresignedResponse( val url: String, - val imageUrl: String, + val imageName: String, ) { } diff --git a/core/src/main/kotlin/com/wespot/notification/service/listener/CommentNotificationListener.kt b/core/src/main/kotlin/com/wespot/notification/service/listener/CommentNotificationListener.kt index 1d48654f..8a1a9d4f 100644 --- a/core/src/main/kotlin/com/wespot/notification/service/listener/CommentNotificationListener.kt +++ b/core/src/main/kotlin/com/wespot/notification/service/listener/CommentNotificationListener.kt @@ -26,7 +26,7 @@ class CommentNotificationListener( @Async @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) @Transactional(propagation = Propagation.REQUIRES_NEW) - fun listenCreatedPostCommentEvent(postCommentCreatedEvent: PostCommentCreatedEvent) { + fun listenCreatedPostCommentEvent(postCommentCreatedEvent: PostCommentCreatedEvent) { // TODO : 일단, 본인은 알림 못받게 해야하고, 익명 프로필로 val postComment = postCommentCreatedEvent.postComment val post = postPort.findById(postComment.postId) ?: throw CustomException( status = HttpStatus.BAD_REQUEST, diff --git a/core/src/main/kotlin/com/wespot/post/dto/request/CreatedPostRequest.kt b/core/src/main/kotlin/com/wespot/post/dto/request/CreatedPostRequest.kt index 087c4d24..a4b65145 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/request/CreatedPostRequest.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/request/CreatedPostRequest.kt @@ -4,14 +4,7 @@ data class CreatedPostRequest( val categoryId: Long, val title: String?, val description: String, - val imagesRequest: List?, + val imagesRequest: List?, ) { - data class CreatedPostImageRequest( - val url: String, - val width: Int, - val height: Int - ) { - } - } diff --git a/core/src/main/kotlin/com/wespot/post/dto/request/ModifyPostNotificationSettingRequest.kt b/core/src/main/kotlin/com/wespot/post/dto/request/ModifyPostNotificationSettingRequest.kt new file mode 100644 index 00000000..999d1979 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/request/ModifyPostNotificationSettingRequest.kt @@ -0,0 +1,6 @@ +package com.wespot.post.dto.request + +data class ModifyPostNotificationSettingRequest( + val isEnablePostNotification: Boolean +) { +} diff --git a/core/src/main/kotlin/com/wespot/post/dto/request/PostReportRequest.kt b/core/src/main/kotlin/com/wespot/post/dto/request/PostReportRequest.kt index 443b156e..597c4866 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/request/PostReportRequest.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/request/PostReportRequest.kt @@ -1,6 +1,6 @@ package com.wespot.post.dto.request data class PostReportRequest( - val reason: String, + val reportReasonId: Long, ) { } diff --git a/core/src/main/kotlin/com/wespot/post/dto/request/UpdatedPostRequest.kt b/core/src/main/kotlin/com/wespot/post/dto/request/UpdatedPostRequest.kt index 0fc6a98a..3e356818 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/request/UpdatedPostRequest.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/request/UpdatedPostRequest.kt @@ -4,14 +4,7 @@ class UpdatedPostRequest( val categoryId: Long, val title: String?, val description: String, - val imagesRequest: List?, + val imagesRequest: List?, ) { - data class UpdatedPostImageRequest( - val url: String, - val width: Int, - val height: Int - ) { - } - } diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/FilterChipResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/FilterChipResponse.kt new file mode 100644 index 00000000..490d7d47 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/response/FilterChipResponse.kt @@ -0,0 +1,34 @@ +package com.wespot.post.dto.response + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.common.dto.view.IconV2Response +import com.wespot.common.dto.view.RichTextV2Response +import com.wespot.view.chip.FilterChip + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class FilterChipResponse( + val type: String, + val content: FilterChipContentResponse, +) { + + data class FilterChipContentResponse( + val id: Long, + val icon: IconV2Response, + val text: RichTextV2Response, + val target: String, + ) + + companion object { + fun from(filterChip: FilterChip): FilterChipResponse { + return FilterChipResponse( + type = filterChip.type, + content = FilterChipContentResponse( + id = filterChip.content.id, + icon = IconV2Response.from(filterChip.content.icon), + text = RichTextV2Response.from(filterChip.content.text), + target = filterChip.content.target + ) + ) + } + } +} diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryComponentResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryComponentResponse.kt new file mode 100644 index 00000000..34918009 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryComponentResponse.kt @@ -0,0 +1,26 @@ +package com.wespot.post.dto.response + +import com.wespot.common.dto.view.IconV2Response +import com.wespot.common.dto.view.RichTextV2Response +import com.wespot.post.server_driven.PostCategoryComponent + +data class PostCategoryComponentResponse( + val text: RichTextV2Response, + val target: String, + val icon: IconV2Response, +) { + companion object { + + fun from(category: PostCategoryComponent?): PostCategoryComponentResponse? { + return category?.let { + PostCategoryComponentResponse( + text = RichTextV2Response.from(it.text), + target = it.target, + icon = IconV2Response.from(it.icon) + ) + } + } + + } + +} diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryDetailResponses.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryDetailResponses.kt index ae03f9a1..ea2035dc 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryDetailResponses.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryDetailResponses.kt @@ -1,8 +1,10 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.post.PostCategories import com.wespot.post.PostCategory +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostCategoryDetailResponses( val majorCategoriesName: String, val categories: List diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryItemsResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryItemsResponse.kt new file mode 100644 index 00000000..7239d379 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryItemsResponse.kt @@ -0,0 +1,37 @@ +package com.wespot.post.dto.response + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.post.server_driven.PostCategoryItems + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class PostCategoryItemsResponse( + val category: String, + val chips: List +) { + + data class PostCategoryItemResponse( + val id: Long, + val text: String + ) { + + companion object { + fun from(postCategoryItem: PostCategoryItems.PostCategoryItem): PostCategoryItemResponse { + return PostCategoryItemResponse( + id = postCategoryItem.id, + text = postCategoryItem.text + ) + } + } + + } + + companion object { + fun from(postCategoryItems: PostCategoryItems): PostCategoryItemsResponse { + return PostCategoryItemsResponse( + category = postCategoryItems.category, + chips = postCategoryItems.chips.map { PostCategoryItemResponse.from(it) } + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryResponse.kt index e2b356ba..79f6d512 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryResponse.kt @@ -1,14 +1,16 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.post.PostCategories +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostCategoryResponse( val majorCategoryName: String, ) { companion object { - private const val ALL_INCLUDE_CATEGORY_NAME = "전체" + private val ALL_INCLUDE_CATEGORY_NAME = "전체" val ALL_INCLUDE_CATEGORY = PostCategoryResponse(majorCategoryName = ALL_INCLUDE_CATEGORY_NAME) fun from(eachMajorCategory: PostCategories.EachMajorCategory): PostCategoryResponse { diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostCommentResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostCommentResponse.kt index 5926d998..a9215bb6 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostCommentResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostCommentResponse.kt @@ -1,17 +1,21 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.comment.PostComment +import com.wespot.common.TimeExpressionUtil import com.wespot.post.PostProfile -import java.time.LocalDateTime +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostCommentResponse( + val id: Long, + val isMe: Boolean, val authorImage: String, val authorName: String, val content: String, val likeCount: Long, - val didIPushLike: Boolean, + val hasPushedLike: Boolean, val isReported: Boolean, - val createdAt: LocalDateTime, + val createdAt: String, ) { companion object { @@ -20,17 +24,20 @@ data class PostCommentResponse( fun of( isPostOwner: Boolean = false, + isCommentOwner: Boolean = false, postComment: PostComment, postProfile: PostProfile ): PostCommentResponse { return PostCommentResponse( + id = postComment.id, authorImage = postProfile.url, + isMe = isCommentOwner, authorName = if (isPostOwner) OWNER_NAME else VIEWER_NAME, content = postComment.content.content, likeCount = postComment.likeCount, - didIPushLike = postComment.postCommentStatusByViewer?.isViewerPushedLike ?: false, + hasPushedLike = postComment.postCommentStatusByViewer?.isViewerPushedLike ?: false, isReported = postComment.reportCount > 0, - createdAt = postComment.createdAt + createdAt = TimeExpressionUtil.commentTime(createdAt = postComment.createdAt) ) } diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt new file mode 100644 index 00000000..833266d6 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt @@ -0,0 +1,169 @@ +package com.wespot.post.dto.response + +import com.fasterxml.jackson.annotation.JsonInclude +import com.wespot.common.dto.view.IconV2Response +import com.wespot.common.dto.view.ImageContentV2Response +import com.wespot.common.dto.view.RichTextV2Response +import com.wespot.post.server_driven.PostComponent + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class PostComponentResponse( + val id: Long, + val type: String, + val isMyPost: Boolean, + val content: PostContentResponse, +) { + + data class PostContentResponse( + val category: PostCategoryComponentResponse? = null, + val headerSection: PostHeaderSectionResponse, + val infoSection: PostInfoSectionResponse, + val contentSection: PostContentSectionResponse? = null, + val footerSection: PostFooterSectionResponse, + val button: PostButtonComponentResponse? = null, + ) { + + data class PostHeaderSectionResponse( + val profileImage: ImageContentV2Response, + val nickname: RichTextV2Response, + val createdAt: RichTextV2Response, + val category: PostCategoryComponentResponse? = null, + val button: PostButtonComponentResponse? = null, + ) { + companion object { + + fun from(headerSection: PostComponent.PostContent.PostHeaderSection): PostHeaderSectionResponse { + return PostHeaderSectionResponse( + profileImage = ImageContentV2Response( + url = headerSection.profileImage.url, + width = headerSection.profileImage.width, + height = headerSection.profileImage.height, + ), + nickname = RichTextV2Response.from(headerSection.nickname), + createdAt = RichTextV2Response.from(headerSection.createdAt), + category = PostCategoryComponentResponse.from(headerSection.category), + button = PostButtonComponentResponse.from(headerSection.button) + ) + } + + } + } + + data class PostInfoSectionResponse( + val title: RichTextV2Response? = null, + val description: RichTextV2Response, + val seeMore: RichTextV2Response, + val maxLine: Int, + ) { + companion object { + + fun from(postInfoSection: PostComponent.PostContent.PostInfoSection): PostInfoSectionResponse { + return PostInfoSectionResponse( + title = postInfoSection.title?.let { RichTextV2Response.from(postInfoSection.title!!) }, + description = RichTextV2Response.from(postInfoSection.description), + seeMore = RichTextV2Response.from(postInfoSection.seeMore), + maxLine = postInfoSection.maxLine + ) + } + + } + } + + data class PostContentSectionResponse( + val type: String, + val images: List, + ) { + companion object { + + fun from(postContentSection: PostComponent.PostContent.PostContentSection?): PostContentSectionResponse? { + return postContentSection?.let { + PostContentSectionResponse( + type = it.type, + images = it.images + ) + } + } + + } + } + + data class PostFooterSectionResponse( + val reactions: List, + val scrap: ScrapComponent, + ) { + data class ReactionItem( + val type: String, + val icon: IconV2Response, + val count: RichTextV2Response, + val selected: Boolean, + ) + + data class ScrapComponent( + val icon: IconV2Response, + val selected: Boolean = false, + ) + + companion object { + + fun from(postFooterSection: PostComponent.PostContent.PostFooterSection): PostFooterSectionResponse { + return PostFooterSectionResponse( + reactions = postFooterSection.reactions.map { reaction -> + ReactionItem( + type = reaction.type, + icon = IconV2Response.from(reaction.icon), + count = RichTextV2Response.from(reaction.count), + selected = reaction.selected + ) + }, + scrap = ScrapComponent( + icon = IconV2Response.from(postFooterSection.scrap.icon), + selected = postFooterSection.scrap.selected + ) + ) + } + + } + } + + data class PostButtonComponentResponse( + val type: String = "notification", + val icon: IconV2Response, + val text: RichTextV2Response, + val isSelected: Boolean = false, + ) { + companion object { + + fun from(postButtonComponent: PostComponent.PostContent.PostButtonComponent?): PostButtonComponentResponse? { + return postButtonComponent?.let { + PostButtonComponentResponse( + icon = IconV2Response.from(it.icon), + text = RichTextV2Response.from(it.text), + type = it.type, + ) + } + } + + } + } + } + + companion object { + fun from(postComponent: PostComponent): PostComponentResponse { + val content = postComponent.content + return PostComponentResponse( + id = postComponent.id, + type = postComponent.type, + isMyPost = postComponent.isMyPost, + content = PostContentResponse( + category = PostCategoryComponentResponse.from(content.category), + headerSection = PostContentResponse.PostHeaderSectionResponse.from(content.headerSection), + infoSection = PostContentResponse.PostInfoSectionResponse.from(content.infoSection), + contentSection = PostContentResponse.PostContentSectionResponse.from(content.contentSection), + footerSection = PostContentResponse.PostFooterSectionResponse.from(content.footerSection), + button = PostContentResponse.PostButtonComponentResponse.from(content.button), + ) + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostImageResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostImageResponse.kt index fe743454..ac9bb3a3 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostImageResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostImageResponse.kt @@ -1,8 +1,10 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.post.PostImage import java.time.LocalDateTime +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostImageResponse( val id: Long, val postId: Long, diff --git a/core/src/main/kotlin/com/wespot/post/dto/response/PostResponse.kt b/core/src/main/kotlin/com/wespot/post/dto/response/PostResponse.kt index af7917ba..d29db365 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostResponse.kt @@ -1,9 +1,11 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.post.Post import com.wespot.user.dto.response.UserResponse import java.time.LocalDateTime +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostResponse( val id: Long = 0L, val category: PostCategoryDetailResponses.PostCategoryDetailResponse, diff --git a/core/src/main/kotlin/com/wespot/post/port/in/ModifyPostNotificationSettingUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/ModifyPostNotificationSettingUseCase.kt new file mode 100644 index 00000000..6b20b025 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/port/in/ModifyPostNotificationSettingUseCase.kt @@ -0,0 +1,10 @@ +package com.wespot.post.port.`in` + +import com.wespot.post.dto.request.ModifyPostNotificationSettingRequest + + +interface ModifyPostNotificationSettingUseCase { + + fun changeSetting(modifyPostNotificationSettingRequest: ModifyPostNotificationSettingRequest) + +} diff --git a/core/src/main/kotlin/com/wespot/post/port/in/PostCategoryUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostCategoryUseCase.kt index dfffc4aa..5664f78c 100644 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostCategoryUseCase.kt +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostCategoryUseCase.kt @@ -1,12 +1,12 @@ package com.wespot.post.port.`in` -import com.wespot.post.dto.response.PostCategoryDetailResponses -import com.wespot.post.dto.response.PostCategoryResponse +import com.wespot.post.dto.response.FilterChipResponse +import com.wespot.post.dto.response.PostCategoryItemsResponse interface PostCategoryUseCase { - fun getCategoriesDetail(): List + fun getCategoriesDetail(): List - fun getCategories(): List + fun getCategories(): List } diff --git a/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryByCategoryUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryByCategoryUseCase.kt deleted file mode 100644 index ede2b5d3..00000000 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryByCategoryUseCase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.wespot.post.port.`in` - -import com.wespot.post.dto.response.PostResponse - -interface PostInquiryByCategoryUseCase { - - fun findPostsByCategoryId(categoryId: Long): List - - fun findPostsByMajorCategoryName(majorCategoryName: String): List - - fun findPostById(postId: Long): PostResponse - - fun findCommentedPosts(): List - - fun findScrappedPosts(): List - - fun findWrittenPosts(): List - - fun findAllPosts(): List - -} diff --git a/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt new file mode 100644 index 00000000..368d35b1 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt @@ -0,0 +1,38 @@ +package com.wespot.post.port.`in` + +import com.wespot.common.dto.PostPagingResponse +import com.wespot.post.dto.response.PostComponentResponse + +interface PostInquiryUseCase { + + fun findPostsByCategoryId( + categoryId: Long, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse + + fun findPostsByMajorCategoryName( + majorCategoryName: String, + countOfPostsViewed: Long, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse + + fun findPostById(postId: Long): PostComponentResponse + + fun findCommentedPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse + + fun findScrappedPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse + + fun findWrittenPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse + +} diff --git a/core/src/main/kotlin/com/wespot/post/port/in/PostNudgeModalUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostNudgeUseCase.kt similarity index 83% rename from core/src/main/kotlin/com/wespot/post/port/in/PostNudgeModalUseCase.kt rename to core/src/main/kotlin/com/wespot/post/port/in/PostNudgeUseCase.kt index 0d66f968..5dc51dce 100644 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostNudgeModalUseCase.kt +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostNudgeUseCase.kt @@ -2,7 +2,7 @@ package com.wespot.post.port.`in` import com.wespot.post.nudge.NudgeItem -interface PostNudgeModalUseCase { +interface PostNudgeUseCase { fun findAllNudgeModalsBySequence(startSequence: Int, endSequence: Int): List diff --git a/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt index a1c34c0c..9d2429b1 100644 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt @@ -1,9 +1,13 @@ package com.wespot.post.port.`in` -import com.wespot.post.dto.response.PostResponse +import com.wespot.common.dto.PostPagingResponse interface PostSearchedUseCase { - fun search(keyword: String): List + fun search( + keyword: String, + inquirySize: Long, + cursorId: Long? + ): PostPagingResponse } diff --git a/core/src/main/kotlin/com/wespot/post/port/out/PostPort.kt b/core/src/main/kotlin/com/wespot/post/port/out/PostPort.kt index 2ac927b4..bd328e68 100644 --- a/core/src/main/kotlin/com/wespot/post/port/out/PostPort.kt +++ b/core/src/main/kotlin/com/wespot/post/port/out/PostPort.kt @@ -6,26 +6,41 @@ interface PostPort { fun save(post: Post): Post + fun searchByTitleAndDescription( + keyword: String, + viewerId: Long? = null, + inquirySize: Long, + cursorId: Long? + ): List + fun searchByTitle(title: String, viewerId: Long? = null): List fun searchByDescription(description: String, viewerId: Long? = null): List fun findById(postId: Long, viewerId: Long? = null): Post? - fun findAllByCategoryId(categoryId: Long, viewerId: Long? = null): List + fun findAllByCategoryId(categoryId: Long, viewerId: Long? = null, inquirySize: Long, cursorId: Long?): List fun findAllByCategoryIdIn( categoryIds: List, - viewerId: Long? = null + viewerId: Long? = null, + inquirySize: Long, + cursorId: Long? ): List - fun findAllByUserId(authorId: Long): List + fun findAllByUserId(authorId: Long, inquirySize: Long, cursorId: Long?): List - fun findAllByPostIdIn(postIds: List, viewerId: Long? = null): List + fun findAllByPostIdIn( + postIds: List, + viewerId: Long? = null, + inquirySize: Long, + cursorId: Long? + ): List - fun findAllRecentPostByLimit( - limit: Int, - viewerId: Long? = null + fun findAllRecentPost( + viewerId: Long? = null, + inquirySize: Long, + cursorId: Long? ): List fun deleteById(id: Long) diff --git a/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt index 851387f9..f82f0a89 100644 --- a/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt @@ -14,11 +14,12 @@ class HotPostInquiryService( @Transactional(readOnly = true) override fun topPost(user: User, countOfView: Int): List { - val countOfPostToStatistic = 100 + val countOfPostToStatistic = 100L val posts = - postPort.findAllRecentPostByLimit(limit = countOfPostToStatistic, viewerId = user.id) - return posts.sortedByDescending { it.scoreOfPost() } + postPort.findAllRecentPost(inquirySize = countOfPostToStatistic, viewerId = user.id, cursorId = null) + return posts + .sortedByDescending { it.createdAt } + .sortedByDescending { it.scoreOfPost() } .take(countOfView) } - } diff --git a/core/src/main/kotlin/com/wespot/post/service/ModifyPostNotificationSettingService.kt b/core/src/main/kotlin/com/wespot/post/service/ModifyPostNotificationSettingService.kt new file mode 100644 index 00000000..131a9ea0 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/service/ModifyPostNotificationSettingService.kt @@ -0,0 +1,24 @@ +package com.wespot.post.service + +import com.wespot.auth.service.SecurityUtils +import com.wespot.post.dto.request.ModifyPostNotificationSettingRequest +import com.wespot.post.port.`in`.ModifyPostNotificationSettingUseCase +import com.wespot.user.port.out.UserPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class ModifyPostNotificationSettingService( + private val userPort: UserPort +) : ModifyPostNotificationSettingUseCase { + + @Transactional + override fun changeSetting(modifyPostNotificationSettingRequest: ModifyPostNotificationSettingRequest) { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + loginUser.changeSettings( + isEnablePostNotification = modifyPostNotificationSettingRequest.isEnablePostNotification + ) + userPort.save(loginUser) + } + +} diff --git a/core/src/main/kotlin/com/wespot/post/service/PostBlockService.kt b/core/src/main/kotlin/com/wespot/post/service/PostBlockService.kt index 318e3e5c..90649e2b 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostBlockService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostBlockService.kt @@ -25,7 +25,7 @@ class PostBlockService( ?.let { postBlockPort.deleteById(it.id) } - ?: { + ?: run { postBlockPort.save(PostBlock(postId = post.id, userId = loginUser.id)) } } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostCategoryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostCategoryService.kt index 10d77ad7..43318ad7 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostCategoryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostCategoryService.kt @@ -1,10 +1,12 @@ package com.wespot.post.service import com.wespot.post.PostCategories -import com.wespot.post.dto.response.PostCategoryDetailResponses -import com.wespot.post.dto.response.PostCategoryResponse +import com.wespot.post.dto.response.FilterChipResponse +import com.wespot.post.dto.response.PostCategoryItemsResponse import com.wespot.post.port.`in`.PostCategoryUseCase import com.wespot.post.port.out.PostCategoryPort +import com.wespot.post.server_driven.PostCategoryItems +import com.wespot.view.chip.FilterChip import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -14,20 +16,31 @@ class PostCategoryService( ) : PostCategoryUseCase { @Transactional(readOnly = true) - override fun getCategoriesDetail(): List { + override fun getCategoriesDetail(): List { val postCategories = PostCategories.from(postCategories = postCategoryPort.findAll()) return postCategories.eachMajorCategories - .map { PostCategoryDetailResponses.from(it) } + .map { PostCategoryItems.from(it) } + .map { PostCategoryItemsResponse.from(it) } } @Transactional(readOnly = true) - override fun getCategories(): List { + override fun getCategories(): List { + val ALL_INCLUDE_CATEGORY_NAME = "전체" val postCategories = PostCategories.from(postCategories = postCategoryPort.findAll()) val eachMajorCategories = postCategories.eachMajorCategories - .map { PostCategoryResponse.from(it) } + var id = 1L - return listOf(PostCategoryResponse.ALL_INCLUDE_CATEGORY) + eachMajorCategories + val firstElement = FilterChip.of(id = id++, text = ALL_INCLUDE_CATEGORY_NAME) + val otherElements = eachMajorCategories.map { + FilterChip.of( + id = id++, + eachMajorCategory = it + ) + } + + return (listOf(firstElement) + otherElements) + .map { FilterChipResponse.from(it) } } } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostCommentInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostCommentInquiryService.kt index 02fff9f2..19c0731c 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostCommentInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostCommentInquiryService.kt @@ -1,11 +1,10 @@ package com.wespot.post.service import com.wespot.auth.service.SecurityUtils -import com.wespot.post.dto.response.PostCommentResponse -import com.wespot.post.port.`in`.PostCommentInquiryUseCase -import com.wespot.comment.port.out.PostCommentLikePort import com.wespot.comment.port.out.PostCommentPort import com.wespot.exception.CustomException +import com.wespot.post.dto.response.PostCommentResponse +import com.wespot.post.port.`in`.PostCommentInquiryUseCase import com.wespot.post.port.out.PostPort import com.wespot.post.port.out.PostProfilePort import com.wespot.user.port.out.UserPort @@ -36,6 +35,7 @@ class PostCommentInquiryService( return postComments.map { PostCommentResponse.of( isPostOwner = post.isAuthor(loginUser.id), + isCommentOwner = it.isAuthor(loginUser.id), postComment = it, postProfile = userIdToProfile[it.user.id]!! ) diff --git a/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt b/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt index e701a161..89982ffe 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt @@ -43,9 +43,7 @@ class PostCreatedService( ?.map { PostImage.of( cloudFrontUrl = cloudFrontUrl, - imageUrl = it.url, - width = it.width, - height = it.height + imageUrl = it, ) }, toSavePost = { toSavedPost -> postPort.save(toSavedPost) } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostEditService.kt b/core/src/main/kotlin/com/wespot/post/service/PostEditService.kt index d4803943..f45498a4 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostEditService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostEditService.kt @@ -34,7 +34,7 @@ class PostEditService( title = request.title, description = request.description, images = request.imagesRequest?.map { - PostImage.of(cloudFrontUrl = cloudFrontUrl, imageUrl = it.url, width = it.width, height = it.height) + PostImage.of(cloudFrontUrl = cloudFrontUrl, imageUrl = it) }, toUpdatePost = { updatedPost -> postPort.save(updatedPost) } ).id diff --git a/core/src/main/kotlin/com/wespot/post/service/PostInquiryByCategoryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostInquiryByCategoryService.kt deleted file mode 100644 index ab272fd0..00000000 --- a/core/src/main/kotlin/com/wespot/post/service/PostInquiryByCategoryService.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.wespot.post.service - -import com.wespot.auth.service.SecurityUtils -import com.wespot.comment.port.out.PostCommentPort -import com.wespot.exception.CustomException -import com.wespot.post.dto.response.PostResponse -import com.wespot.post.port.`in`.PostInquiryByCategoryUseCase -import com.wespot.post.port.out.PostCategoryPort -import com.wespot.post.port.out.PostPort -import com.wespot.post.port.out.PostScrapPort -import com.wespot.user.port.out.UserPort -import org.springframework.http.HttpStatus -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -@Service -class PostInquiryByCategoryService( - private val userPort: UserPort, - private val postCategoryPort: PostCategoryPort, - private val postPort: PostPort, - private val postCommentPort: PostCommentPort, - private val postScrapPort: PostScrapPort -) : PostInquiryByCategoryUseCase { - - @Transactional(readOnly = false) - override fun findPostsByCategoryId(categoryId: Long): List { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val postCategory = postCategoryPort.findById(categoryId) ?: throw CustomException( - status = HttpStatus.BAD_REQUEST, - message = "존재하지 않는 카테고리입니다." - ) - - return postPort.findAllByCategoryId(categoryId = postCategory.id, viewerId = loginUser.id) - .map { PostResponse.from(it) } - } - - @Transactional(readOnly = false) - override fun findPostsByMajorCategoryName(majorCategoryName: String): List { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val categoryIds = postCategoryPort.findAllByMajorCategoryName(majorCategoryName) - .map { it.id } - - return postPort.findAllByCategoryIdIn(categoryIds = categoryIds, viewerId = loginUser.id) - .map { PostResponse.from(it) } - } - - @Transactional(readOnly = false) - override fun findPostById(postId: Long): PostResponse { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val post = postPort.findById(postId, viewerId = loginUser.id) ?: throw CustomException( - status = HttpStatus.BAD_REQUEST, - message = "존재하지 않는 게시글입니다." - ) - - return PostResponse.from(post) - } - - @Transactional(readOnly = false) - override fun findCommentedPosts(): List { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val postIds = postCommentPort.findAllByUserId(userId = loginUser.id) - .map { it.postId } - .distinct() - - return postPort.findAllByPostIdIn(postIds = postIds, viewerId = loginUser.id) - .map { PostResponse.from(it) } - } - - @Transactional(readOnly = false) - override fun findScrappedPosts(): List { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val postIds = postScrapPort.findAllByUserId(userId = loginUser.id) - .map { it.postId } - .distinct() - - return postPort.findAllByPostIdIn(postIds = postIds, viewerId = loginUser.id) - .map { PostResponse.from(it) } - } - - @Transactional(readOnly = false) - override fun findWrittenPosts(): List { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - - return postPort.findAllByUserId(authorId = loginUser.id).map { PostResponse.from(it) } - } - - @Transactional(readOnly = false) - override fun findAllPosts(): List { - TODO("Not yet implemented") - } - - -} diff --git a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt new file mode 100644 index 00000000..197dd9af --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt @@ -0,0 +1,276 @@ +package com.wespot.post.service + +import com.wespot.auth.service.SecurityUtils +import com.wespot.comment.port.out.PostCommentPort +import com.wespot.common.dto.PostPagingResponse +import com.wespot.common.dto.view.HotPostComponentResponse +import com.wespot.common.dto.view.ImageContentV2Response +import com.wespot.common.dto.view.MessageComponentResponse +import com.wespot.common.dto.view.VoteComponentResponse +import com.wespot.exception.CustomException +import com.wespot.post.Post +import com.wespot.post.PostCategory +import com.wespot.post.dto.response.PostComponentResponse +import com.wespot.post.nudge.HotPostNudge +import com.wespot.post.nudge.MessageNudge +import com.wespot.post.nudge.NudgeItem +import com.wespot.post.nudge.VoteNudge +import com.wespot.post.nudge.server_driven.HotPostComponent +import com.wespot.post.nudge.server_driven.MessageComponent +import com.wespot.post.nudge.server_driven.VoteComponent +import com.wespot.post.port.`in`.PostInquiryUseCase +import com.wespot.post.port.`in`.PostNudgeUseCase +import com.wespot.post.port.out.PostCategoryPort +import com.wespot.post.port.out.PostPort +import com.wespot.post.port.out.PostScrapPort +import com.wespot.post.server_driven.PostComponent +import com.wespot.user.User +import com.wespot.user.port.out.UserPort +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class PostInquiryService( + private val userPort: UserPort, + private val postCategoryPort: PostCategoryPort, + private val postPort: PostPort, + private val postCommentPort: PostCommentPort, + private val postScrapPort: PostScrapPort, + + private val nudgeModalUseCase: PostNudgeUseCase, +) : PostInquiryUseCase { + + @Transactional(readOnly = false) + override fun findPostsByCategoryId( + categoryId: Long, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val postCategory = postCategoryPort.findById(categoryId) ?: throw CustomException( + status = HttpStatus.BAD_REQUEST, + message = "존재하지 않는 카테고리입니다." + ) + + val posts = postPort.findAllByCategoryId( + categoryId = postCategory.id, + viewerId = loginUser.id, + cursorId = cursorId, + inquirySize = inquirySize + 1 + ) + + val data = posts.map { PostComponent.of(it, viewerId = loginUser.id, isCategoryScreen = true) } + .map { PostComponentResponse.from(it) } + val lastCursorId = data.minOfOrNull { it.id } + val hasNext = posts.size == (inquirySize.toInt() + 1) + + return PostPagingResponse( + data = data.take(inquirySize.toInt()), + background = ImageContentV2Response(url = postCategory.backgroundImage), + thumbnail = ImageContentV2Response(url = postCategory.thumbnail), + lastCursorId = lastCursorId, + hasNext = hasNext + ) + } + + private fun postPagingResponse( + posts: List, + inquirySize: Long, + viewerId: Long, + ): PostPagingResponse { + val data = posts.map { PostComponent.of(it, viewerId = viewerId) } + .map { PostComponentResponse.from(it) } + val lastCursorId = data.minOfOrNull { it.id } + val hasNext = posts.size == (inquirySize.toInt() + 1) + + return PostPagingResponse(data = data.take(inquirySize.toInt()), lastCursorId = lastCursorId, hasNext = hasNext) + } + + @Transactional(readOnly = false) + override fun findPostsByMajorCategoryName( + majorCategoryName: String, + countOfPostsViewed: Long, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val posts = findPostsByCategoryName( + majorCategoryName = majorCategoryName, + loginUser = loginUser, + inquirySize = inquirySize, + cursorId = cursorId + ) + + val startSequence = countOfPostsViewed.toInt() + 1 + val endSequence = countOfPostsViewed.toInt() + inquirySize.toInt() + val nudges = nudgeModalUseCase.findAllNudgeModalsBySequence( + startSequence = startSequence, + endSequence = endSequence, + ).sortedBy { it.mustViewSequence() } + + val data = mixPostAndNudge(startSequence, endSequence, posts.take(inquirySize.toInt()), nudges, loginUser.id) + val lastCursorId = posts.minOfOrNull { it.id } + val hasNext = posts.size == (inquirySize.toInt() + 1) + + return PostPagingResponse( + data = data, + lastCursorId = lastCursorId, + hasNext = hasNext + + ) + } + + private fun findPostsByCategoryName( + majorCategoryName: String, + loginUser: User, + inquirySize: Long, + cursorId: Long? + ): List { + if (majorCategoryName == PostCategory.ALL_INCLUDE_CATEGORY_NAME || majorCategoryName == "") { + return postPort.findAllRecentPost( + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ) + } + + val categoryIds = postCategoryPort.findAllByMajorCategoryName(majorCategoryName) + .map { it.id } + return postPort.findAllByCategoryIdIn( + categoryIds = categoryIds, + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ) + } + + private fun mixPostAndNudge( + startSequence: Int, + endSequence: Int, + posts: List, + nudges: List, + viewerId: Long, + ): MutableList { + val data = mutableListOf() + var nudgesIndex = 0 + + (startSequence..endSequence).asSequence().forEach { i -> + val post = posts[i - startSequence] + val component = PostComponent.of(post, viewerId) + data.add(PostComponentResponse.from(component)) + + if (nudges.size > nudgesIndex && nudges[nudgesIndex].mustViewSequence() == i) { + data.add(convertNudgeResponse(nudges, nudgesIndex++)!!) + } + } + + return data + } + + private fun convertNudgeResponse( + nudges: List, + nudgesIndex: Int, + ): Any? { + val nudgeItem = nudges[nudgesIndex] + val clazz = nudgeItem.nudgeType().clazz + val content = nudgeItem.content() + + return when (clazz) { + VoteNudge.VoteNudgeContent::class.java -> { + val element = VoteComponent.of( + Long.MAX_VALUE + nudgesIndex, + content as VoteNudge.VoteNudgeContent + ) + VoteComponentResponse.from(element) + } + + MessageNudge.MessageNudgeContent::class.java -> { + val element = MessageComponent.from(Long.MAX_VALUE + nudgesIndex) + MessageComponentResponse.from(element) + } + + HotPostNudge.HotPostNudgeContent::class.java -> { + val element = HotPostComponent.of( + Long.MAX_VALUE + nudgesIndex, + content as HotPostNudge.HotPostNudgeContent + ) + HotPostComponentResponse.from(element) + } + + else -> { + null + } + } + } + + @Transactional(readOnly = false) + override fun findPostById( + postId: Long, + ): PostComponentResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val post = postPort.findById(postId, viewerId = loginUser.id) ?: throw CustomException( + status = HttpStatus.BAD_REQUEST, + message = "존재하지 않는 게시글입니다." + ) + + val postComponent = PostComponent.fromDetail(post, loginUser.id) + return PostComponentResponse.from(postComponent) + } + + @Transactional(readOnly = false) + override fun findCommentedPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val postIds = postCommentPort.findAllByUserId(userId = loginUser.id) + .map { it.postId } + .distinct() + + val posts = postPort.findAllByPostIdIn( + postIds = postIds, + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ) + + return postPagingResponse(posts, inquirySize, loginUser.id) + } + + @Transactional(readOnly = false) + override fun findScrappedPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val postIds = postScrapPort.findAllByUserId(userId = loginUser.id) + .map { it.postId } + .distinct() + + postPort.findAllByPostIdIn( + postIds = postIds, + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ).let { posts -> + return postPagingResponse(posts, inquirySize, loginUser.id) + } + } + + @Transactional(readOnly = false) + override fun findWrittenPosts( + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + + postPort.findAllByUserId( + authorId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ).let { return postPagingResponse(it, inquirySize, loginUser.id) } + } + + +} diff --git a/core/src/main/kotlin/com/wespot/post/service/PostLikeService.kt b/core/src/main/kotlin/com/wespot/post/service/PostLikeService.kt index a71ec287..8228c8b9 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostLikeService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostLikeService.kt @@ -27,7 +27,7 @@ class PostLikeService( val removedLikePost = post.removeLike() postPort.save(removedLikePost) } - ?: { + ?: run { val postLike = PostLike(postId = postId, userId = loginUser.id) postLikePort.save(postLike) val addedLikePost = post.addLike() diff --git a/core/src/main/kotlin/com/wespot/post/service/PostNudgeModalService.kt b/core/src/main/kotlin/com/wespot/post/service/PostNudgeModalService.kt index 6deae9b6..68b1b64b 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostNudgeModalService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostNudgeModalService.kt @@ -9,7 +9,7 @@ import com.wespot.post.nudge.VoteNudge import com.wespot.post.nudge.vo.NudgeType import com.wespot.post.nudge.vo.NudgeTypeWithSequence import com.wespot.post.port.`in`.HotPostInquiryUseCase -import com.wespot.post.port.`in`.PostNudgeModalUseCase +import com.wespot.post.port.`in`.PostNudgeUseCase import com.wespot.user.User import com.wespot.user.port.out.UserPort import com.wespot.vote.port.`in`.VoteOptionUsageUseCase @@ -22,7 +22,7 @@ class PostNudgeModalService( private val messageV2UsingStatusUseCase: MessageV2UsingStatusUseCase, private val voteOptionUsageUseCase: VoteOptionUsageUseCase, private val hotPostInquiryUseCase: HotPostInquiryUseCase -) : PostNudgeModalUseCase { +) : PostNudgeUseCase { @Transactional(readOnly = true) override fun findAllNudgeModalsBySequence(startSequence: Int, endSequence: Int): List { diff --git a/core/src/main/kotlin/com/wespot/post/service/PostReportService.kt b/core/src/main/kotlin/com/wespot/post/service/PostReportService.kt index f6a3f2a4..b444459f 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostReportService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostReportService.kt @@ -6,6 +6,7 @@ import com.wespot.post.dto.request.PostReportRequest import com.wespot.post.port.`in`.PostReportUseCase import com.wespot.post.port.out.PostPort import com.wespot.post.port.out.PostReportPort +import com.wespot.report.port.out.ReportReasonPort import com.wespot.user.port.out.UserPort import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -15,6 +16,7 @@ class PostReportService( private val userPort: UserPort, private val postPort: PostPort, private val postReportPort: PostReportPort, + private val reportReasonPort: ReportReasonPort, ) : PostReportUseCase { @Transactional @@ -23,13 +25,20 @@ class PostReportService( val post = postPort.findById(postId) ?: throw IllegalArgumentException("존재하지 않는 게시글입니다.") + val reportReason = reportReasonPort.findById(postReportRequest.reportReasonId) + ?: throw IllegalArgumentException("존재하지 않는 신고 사유입니다.") postReportPort.findByPostIdAndUserId(postId, loginUser.id) ?.let { postReportPort.deleteById(id = it.id) } - ?: { - val postReport = PostReport(postId = post.id, userId = loginUser.id, reason = postReportRequest.reason) + ?: run { + val postReport = + PostReport( + postId = post.id, + userId = loginUser.id, + reportReason = reportReason + ) postReportPort.save(postReport) } } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostScrapService.kt b/core/src/main/kotlin/com/wespot/post/service/PostScrapService.kt index 1acb006f..56663306 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostScrapService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostScrapService.kt @@ -27,7 +27,7 @@ class PostScrapService( val deleteBookmark = post.deleteBookmark() postPort.save(deleteBookmark) } - ?: { + ?: run { val postScrap = PostScrap(postId = post.id, userId = loginUser.id) postScrapPort.save(postScrap) val addBookmark = post.addBookmark() diff --git a/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt index 148586fd..9f2e2862 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt @@ -1,28 +1,46 @@ package com.wespot.post.service -import com.wespot.post.dto.response.PostResponse +import com.wespot.auth.service.SecurityUtils +import com.wespot.common.dto.PostPagingResponse +import com.wespot.post.dto.response.PostComponentResponse import com.wespot.post.port.`in`.PostSearchedUseCase import com.wespot.post.port.out.PostPort +import com.wespot.post.server_driven.PostComponent import com.wespot.post.vo.PostSearchKeyword +import com.wespot.user.port.out.UserPort import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service class PostSearchedService( - private val postPort: PostPort + private val postPort: PostPort, + private val userPort: UserPort, ) : PostSearchedUseCase { @Transactional(readOnly = true) - override fun search(keyword: String): List { + override fun search( + keyword: String, + inquirySize: Long, + cursorId: Long? + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) val postSearchKeyword = PostSearchKeyword.from(keyword = keyword) - val keywordsToSearch = postSearchKeyword.keywordsToSearch() - val postsByTitle = keywordsToSearch.map { postPort.searchByTitle(it) }.flatten() - val postsByDescription = keywordsToSearch.map { postPort.searchByDescription(it) }.flatten() + val keywordsToSearch = postSearchKeyword.keywordsToSearchInRegex() + val posts = postPort.searchByTitleAndDescription( + keyword = keywordsToSearch, + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ) - return (postsByTitle + postsByDescription) - .distinct() - .sortedByDescending { it.createdAt } - .map { PostResponse.from(it) } - } + val data = posts.map { PostComponent.of(it, loginUser.id) }.map { PostComponentResponse.from(it) } + val hasNext = posts.size == (inquirySize + 1).toInt() + val lastCursorId = posts.minOfOrNull { it.id } + return PostPagingResponse( + data = data.take(inquirySize.toInt()), + hasNext = hasNext, + lastCursorId = lastCursorId + ) + } } diff --git a/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentCreatedEventListener.kt b/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentCreatedEventListener.kt index bbd4f00e..d49f457b 100644 --- a/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentCreatedEventListener.kt +++ b/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentCreatedEventListener.kt @@ -13,13 +13,13 @@ class PostCommentCreatedEventListener( ) { @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) - private fun listenPostCreatedEvent(postCommentCreatedEvent: PostCommentCreatedEvent) { + fun listenPostCreatedEvent(postCommentCreatedEvent: PostCommentCreatedEvent) { val postComment = postCommentCreatedEvent.postComment val post = postPort.findById(postId = postComment.postId) ?: throw CustomException(message = "존재하지 않는 게시글입니다.") - post.addComment() - postPort.save(post) + val addedCommentPost = post.addComment() + postPort.save(addedCommentPost) } } diff --git a/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentDeletedEventListener.kt b/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentDeletedEventListener.kt new file mode 100644 index 00000000..3ab3ac82 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/service/listener/PostCommentDeletedEventListener.kt @@ -0,0 +1,24 @@ +package com.wespot.post.service.listener + +import com.wespot.comment.event.PostCommentDeleteEvent +import com.wespot.exception.CustomException +import com.wespot.post.port.out.PostPort +import org.springframework.stereotype.Component +import org.springframework.transaction.event.TransactionPhase +import org.springframework.transaction.event.TransactionalEventListener + +@Component +class PostCommentDeletedEventListener( + private val postPort: PostPort +) { + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + fun listenPostCreatedEvent(postCommentDeleteEvent: PostCommentDeleteEvent) { + val postComment = postCommentDeleteEvent.postComment + val post = postPort.findById(postId = postComment.postId) + ?: throw CustomException(message = "존재하지 않는 게시글입니다.") + + val removedCommentPost = post.removeComment() + postPort.save(removedCommentPost) + } +} diff --git a/core/src/main/kotlin/com/wespot/report/dto/ReportReasonResponse.kt b/core/src/main/kotlin/com/wespot/report/dto/ReportReasonResponse.kt new file mode 100644 index 00000000..881df3b1 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/report/dto/ReportReasonResponse.kt @@ -0,0 +1,19 @@ +package com.wespot.report.dto + +import com.wespot.report.ReportReason + +data class ReportReasonResponse( + val id: Long, + val reason: String +) { + + companion object { + fun from(reportReason: ReportReason): ReportReasonResponse { + return ReportReasonResponse( + id = reportReason.id, + reason = reportReason.content + ) + } + } + +} diff --git a/core/src/main/kotlin/com/wespot/report/port/in/ReportReasonUseCase.kt b/core/src/main/kotlin/com/wespot/report/port/in/ReportReasonUseCase.kt new file mode 100644 index 00000000..ce8a09b0 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/report/port/in/ReportReasonUseCase.kt @@ -0,0 +1,9 @@ +package com.wespot.report.port.`in` + +import com.wespot.report.dto.ReportReasonResponse + +interface ReportReasonUseCase { + + fun getReportReasons(): List + +} diff --git a/core/src/main/kotlin/com/wespot/report/port/out/ReportReasonPort.kt b/core/src/main/kotlin/com/wespot/report/port/out/ReportReasonPort.kt new file mode 100644 index 00000000..41abb838 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/report/port/out/ReportReasonPort.kt @@ -0,0 +1,11 @@ +package com.wespot.report.port.out + +import com.wespot.report.ReportReason + +interface ReportReasonPort { + + fun findAll(): List + + fun findById(reportReasonId: Long): ReportReason? + +} diff --git a/core/src/main/kotlin/com/wespot/report/service/ReportReasonService.kt b/core/src/main/kotlin/com/wespot/report/service/ReportReasonService.kt new file mode 100644 index 00000000..3a4ad40f --- /dev/null +++ b/core/src/main/kotlin/com/wespot/report/service/ReportReasonService.kt @@ -0,0 +1,20 @@ +package com.wespot.report.service + +import com.wespot.report.dto.ReportReasonResponse +import com.wespot.report.port.`in`.ReportReasonUseCase +import com.wespot.report.port.out.ReportReasonPort +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class ReportReasonService( + private val reportReasonPort: ReportReasonPort +) : ReportReasonUseCase { + + @Transactional(readOnly = true) + override fun getReportReasons(): List { + return reportReasonPort.findAll() + .map { ReportReasonResponse.from(it) } + } + +} diff --git a/domain/src/main/kotlin/com/wespot/comment/PostComment.kt b/domain/src/main/kotlin/com/wespot/comment/PostComment.kt index 4e775c7b..d776df01 100644 --- a/domain/src/main/kotlin/com/wespot/comment/PostComment.kt +++ b/domain/src/main/kotlin/com/wespot/comment/PostComment.kt @@ -70,11 +70,19 @@ class PostComment( } fun removeReport(): PostComment { - TODO("Not yet implemented") + return update( + reportCount = this.reportCount - 1 + ) } fun addReport(): PostComment { - TODO("Not yet implemented") + return update( + reportCount = this.reportCount + 1 + ) + } + + fun isAuthor(id: Long): Boolean { + return user.id == id } } diff --git a/domain/src/main/kotlin/com/wespot/comment/PostCommentLike.kt b/domain/src/main/kotlin/com/wespot/comment/PostCommentLike.kt index aeef9eb1..2d383c14 100644 --- a/domain/src/main/kotlin/com/wespot/comment/PostCommentLike.kt +++ b/domain/src/main/kotlin/com/wespot/comment/PostCommentLike.kt @@ -3,9 +3,9 @@ package com.wespot.comment import java.time.LocalDateTime class PostCommentLike( - val id: Long=0L, + val id: Long = 0L, val postCommentId: Long, val userId: Long, - val createdAt: LocalDateTime=LocalDateTime.now() + val createdAt: LocalDateTime = LocalDateTime.now() ) { } diff --git a/domain/src/main/kotlin/com/wespot/comment/event/PostCommentDeleteEvent.kt b/domain/src/main/kotlin/com/wespot/comment/event/PostCommentDeleteEvent.kt new file mode 100644 index 00000000..7e2de6a8 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/comment/event/PostCommentDeleteEvent.kt @@ -0,0 +1,9 @@ +package com.wespot.comment.event + +import com.wespot.comment.PostComment + +data class PostCommentDeleteEvent( + val postComment: PostComment +) { + +} diff --git a/domain/src/main/kotlin/com/wespot/common/EverProfile.kt b/domain/src/main/kotlin/com/wespot/common/EverProfile.kt new file mode 100644 index 00000000..816ab965 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/common/EverProfile.kt @@ -0,0 +1,88 @@ +package com.wespot.common + +enum class EverProfile( + val profileUrl: String +) { + + BLUE1("https://dw2d2daekmyur.cloudfront.net/blue_profile_1.png"), + BLUE2("https://dw2d2daekmyur.cloudfront.net/blue_profile_2.png"), + BLUE3("https://dw2d2daekmyur.cloudfront.net/blue_profile_3.png"), + BLUE4("https://dw2d2daekmyur.cloudfront.net/blue_profile_4.png"), + BLUE5("https://dw2d2daekmyur.cloudfront.net/blue_profile_5.png"), + BLUE6("https://dw2d2daekmyur.cloudfront.net/blue_profile_6.png"), + BLUE7("https://dw2d2daekmyur.cloudfront.net/blue_profile_7.png"), + BLUE8("https://dw2d2daekmyur.cloudfront.net/blue_profile_8.png"), + + GRAY1("https://dw2d2daekmyur.cloudfront.net/gray_profile_1.png"), + GRAY2("https://dw2d2daekmyur.cloudfront.net/gray_profile_2.png"), + GRAY3("https://dw2d2daekmyur.cloudfront.net/gray_profile_3.png"), + GRAY4("https://dw2d2daekmyur.cloudfront.net/gray_profile_4.png"), + GRAY5("https://dw2d2daekmyur.cloudfront.net/gray_profile_5.png"), + GRAY6("https://dw2d2daekmyur.cloudfront.net/gray_profile_6.png"), + GRAY7("https://dw2d2daekmyur.cloudfront.net/gray_profile_7.png"), + GRAY8("https://dw2d2daekmyur.cloudfront.net/gray_profile_8.png"), + + GREEN1("https://dw2d2daekmyur.cloudfront.net/green_profile_1.png"), + GREEN2("https://dw2d2daekmyur.cloudfront.net/green_profile_2.png"), + GREEN3("https://dw2d2daekmyur.cloudfront.net/green_profile_3.png"), + GREEN4("https://dw2d2daekmyur.cloudfront.net/green_profile_4.png"), + GREEN5("https://dw2d2daekmyur.cloudfront.net/green_profile_5.png"), + GREEN6("https://dw2d2daekmyur.cloudfront.net/green_profile_6.png"), + GREEN7("https://dw2d2daekmyur.cloudfront.net/green_profile_7.png"), + GREEN8("https://dw2d2daekmyur.cloudfront.net/green_profile_8.png"), + + ORANGE1("https://dw2d2daekmyur.cloudfront.net/orange_profile_1.png"), + ORANGE2("https://dw2d2daekmyur.cloudfront.net/orange_profile_2.png"), + ORANGE3("https://dw2d2daekmyur.cloudfront.net/orange_profile_3.png"), + ORANGE4("https://dw2d2daekmyur.cloudfront.net/orange_profile_4.png"), + ORANGE5("https://dw2d2daekmyur.cloudfront.net/orange_profile_5.png"), + ORANGE6("https://dw2d2daekmyur.cloudfront.net/orange_profile_6.png"), + ORANGE7("https://dw2d2daekmyur.cloudfront.net/orange_profile_7.png"), + ORANGE8("https://dw2d2daekmyur.cloudfront.net/orange_profile_8.png"), + + ORANGE_YELLOW_1("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_1.png"), + ORANGE_YELLOW_2("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_2.png"), + ORANGE_YELLOW_3("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_3.png"), + ORANGE_YELLOW_4("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_4.png"), + ORANGE_YELLOW_5("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_5.png"), + ORANGE_YELLOW_6("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_6.png"), + ORANGE_YELLOW_7("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_7.png"), + ORANGE_YELLOW_8("https://dw2d2daekmyur.cloudfront.net/orange_yellow_profile_8.png"), + + PINK1("https://dw2d2daekmyur.cloudfront.net/pink_profile_1.png"), + PINK2("https://dw2d2daekmyur.cloudfront.net/pink_profile_2.png"), + PINK3("https://dw2d2daekmyur.cloudfront.net/pink_profile_3.png"), + PINK4("https://dw2d2daekmyur.cloudfront.net/pink_profile_4.png"), + PINK5("https://dw2d2daekmyur.cloudfront.net/pink_profile_5.png"), + PINK6("https://dw2d2daekmyur.cloudfront.net/pink_profile_6.png"), + PINK7("https://dw2d2daekmyur.cloudfront.net/pink_profile_7.png"), + PINK8("https://dw2d2daekmyur.cloudfront.net/pink_profile_8.png"), + + PURPLE1("https://dw2d2daekmyur.cloudfront.net/purple_profile_1.png"), + PURPLE2("https://dw2d2daekmyur.cloudfront.net/purple_profile_2.png"), + PURPLE3("https://dw2d2daekmyur.cloudfront.net/purple_profile_3.png"), + PURPLE4("https://dw2d2daekmyur.cloudfront.net/purple_profile_4.png"), + PURPLE5("https://dw2d2daekmyur.cloudfront.net/purple_profile_5.png"), + PURPLE6("https://dw2d2daekmyur.cloudfront.net/purple_profile_6.png"), + PURPLE7("https://dw2d2daekmyur.cloudfront.net/purple_profile_7.png"), + PURPLE8("https://dw2d2daekmyur.cloudfront.net/purple_profile_8.png"), + + YELLOW1("https://dw2d2daekmyur.cloudfront.net/yellow_profile_1.png"), + YELLOW2("https://dw2d2daekmyur.cloudfront.net/yellow_profile_2.png"), + YELLOW3("https://dw2d2daekmyur.cloudfront.net/yellow_profile_3.png"), + YELLOW4("https://dw2d2daekmyur.cloudfront.net/yellow_profile_4.png"), + YELLOW5("https://dw2d2daekmyur.cloudfront.net/yellow_profile_5.png"), + YELLOW6("https://dw2d2daekmyur.cloudfront.net/yellow_profile_6.png"), + YELLOW7("https://dw2d2daekmyur.cloudfront.net/yellow_profile_7.png"), + YELLOW8("https://dw2d2daekmyur.cloudfront.net/yellow_profile_8.png"), + ; + + companion object { + fun randomProfile(): EverProfile { + val values = entries.toTypedArray() + + return values[(values.indices).random()] + } + } + +} diff --git a/domain/src/main/kotlin/com/wespot/common/TimeExpressionUtil.kt b/domain/src/main/kotlin/com/wespot/common/TimeExpressionUtil.kt new file mode 100644 index 00000000..bf6617e5 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/common/TimeExpressionUtil.kt @@ -0,0 +1,30 @@ +package com.wespot.common + +import java.time.Duration +import java.time.LocalDateTime + +object TimeExpressionUtil { + + fun postTime(now: LocalDateTime = LocalDateTime.now(), createdAt: LocalDateTime): String { + return timeToString(createdAt, now) + } + + private fun timeToString(createdAt: LocalDateTime, now: LocalDateTime): String { + if (createdAt.year != now.year) { + return "${createdAt.year % 100}.${createdAt.monthValue}.${createdAt.dayOfMonth} ${createdAt.hour}:${createdAt.minute}" + } + + val duration = Duration.between(createdAt, now) + return when { + duration.toMinutes() <= 10 -> "방금" + duration.toDays() < 1 -> "${duration.toHours()}시간 전" + duration.toDays() < 30 -> "${duration.toDays()}일 전" + else -> "${createdAt.monthValue}.${createdAt.dayOfMonth} ${createdAt.hour}:${createdAt.minute}" + } + } + + fun commentTime(now: LocalDateTime = LocalDateTime.now(), createdAt: LocalDateTime): String { + return timeToString(createdAt, now) + } + +} diff --git a/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt b/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt new file mode 100644 index 00000000..792e1c92 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt @@ -0,0 +1,31 @@ +package com.wespot.common.view + +enum class ColorType( + val value: String +) { + + GRAY100("Gray100"), + GRAY200("Gray200"), + GRAY300("Gray300"), + GRAY400("Gray400"), + GRAY500("Gray500"), + GRAY600("Gray600"), + GRAY700("Gray700"), + GRAY800("Gray800"), + GRAY900("Gray900"), + + PRIMARY100("Primary100"), + PRIMARY200("Primary200"), + PRIMARY300("Primary300"), + PRIMARY400("Primary400"), + PRIMARY500("Primary500"), + PRIMARY600("Primary600"), + PRIMARY700("Primary700"), + PRIMARY800("Primary800"), + PRIMARY900("Primary900"), + + WHITE("White"), + BLACK("Black"), + ; + +} diff --git a/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt b/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt new file mode 100644 index 00000000..2e5e37a4 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt @@ -0,0 +1,24 @@ +package com.wespot.common.view + +enum class TypographType( + val value: String +) { + + BODY00("Body00"), + BODY01("Body01"), + BODY02("Body02"), + BODY03("Body03"), + BODY04("Body04"), + BODY05("Body05"), + BODY06("Body06"), + BODY07("Body07"), + BODY08("Body08"), + BODY09("Body09"), + BODY10("Body10"), + BODY11("Body11"), + BODY12("Body12"), + + BADGE("Badge"), + ; + +} diff --git a/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt b/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt index 88ac40cf..307a7e55 100644 --- a/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt +++ b/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt @@ -48,7 +48,7 @@ data class MessageV2( companion object { - // const val COUNT_OF_MAX_ABLE_TO_SEND_MESSAGE_PER_DAY=3 +// const val COUNT_OF_MAX_ABLE_TO_SEND_MESSAGE_PER_DAY = 3 const val COUNT_OF_MAX_ABLE_TO_SEND_MESSAGE_PER_DAY = 100 // 개발하는 동안 100개로 유지 fun createInitial( @@ -341,6 +341,7 @@ data class MessageV2( view = ExceptionView.TOAST, ) } + if (viewer.isMeSender(senderId = sender.id)) { throw CustomException( message = "받은 쪽지에 대해서만 답장할 수 있습니다.", @@ -357,6 +358,8 @@ data class MessageV2( ) } + // TODO : 상대방에게 답장할 수 있는지 확인 (차단 여부) + val newMessage = MessageV2( id = 0L, content = content, diff --git a/domain/src/main/kotlin/com/wespot/notification/NotificationType.kt b/domain/src/main/kotlin/com/wespot/notification/NotificationType.kt index 109ea480..304a8945 100644 --- a/domain/src/main/kotlin/com/wespot/notification/NotificationType.kt +++ b/domain/src/main/kotlin/com/wespot/notification/NotificationType.kt @@ -20,7 +20,7 @@ enum class NotificationType( PROFILE_UPDATE({ it.isEnableMarketingNotification() }, SpecificNotificationType.UPDATE), // 프로필 업데이트 이벤트 UPDATE_REQUIRED({ it.isEnableMarketingNotification() }, SpecificNotificationType.UPDATE), // 업데이트를 아직 안한 유저 - COMMENT({it.isEnableMarketingNotification()},SpecificNotificationType.POST) + COMMENT({ it.isEnableMarketingNotification() }, SpecificNotificationType.POST) ; fun isVote(): Boolean { diff --git a/domain/src/main/kotlin/com/wespot/post/Post.kt b/domain/src/main/kotlin/com/wespot/post/Post.kt index 404f01a0..3b934ff7 100644 --- a/domain/src/main/kotlin/com/wespot/post/Post.kt +++ b/domain/src/main/kotlin/com/wespot/post/Post.kt @@ -19,6 +19,7 @@ class Post( val commentCount: Long = 0, val bookmarkedCount: Long = 0, val images: PostImages? = null, + val profile: PostProfile = PostProfile.DEFAULT_PROFILE, val postStatusByViewer: PostStatusByViewer? = null, @@ -67,6 +68,7 @@ class Post( commentCount: Long = this.commentCount, bookmarkedCount: Long = this.bookmarkedCount, images: PostImages? = this.images, + profile: PostProfile = this.profile, createdAt: LocalDateTime = this.createdAt ): Post { return Post( @@ -79,7 +81,8 @@ class Post( commentCount = commentCount, bookmarkedCount = bookmarkedCount, images = images, - createdAt = createdAt + createdAt = createdAt, + profile = profile, ) } @@ -123,6 +126,10 @@ class Post( return copyAndUpdateField(commentCount = this.commentCount + 1) } + fun removeComment(): Post { + return copyAndUpdateField(commentCount = this.commentCount - 1) + } + fun scoreOfPost(): Long { return likeCount * 3 + commentCount * 2 + bookmarkedCount } diff --git a/domain/src/main/kotlin/com/wespot/post/PostCategory.kt b/domain/src/main/kotlin/com/wespot/post/PostCategory.kt index 7e4ab39f..ad19e4de 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostCategory.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostCategory.kt @@ -6,6 +6,13 @@ class PostCategory( val id: Long, val majorCategoryName: String, val name: String, + val thumbnail: String, + val backgroundImage: String, val createdAt: LocalDateTime ) { + + companion object { + const val ALL_INCLUDE_CATEGORY_NAME = "전체" + } + } diff --git a/domain/src/main/kotlin/com/wespot/post/PostImage.kt b/domain/src/main/kotlin/com/wespot/post/PostImage.kt index 11e52984..db0a0eed 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostImage.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostImage.kt @@ -6,11 +6,9 @@ import org.springframework.http.HttpStatus import java.time.LocalDateTime class PostImage( - val id: Long, + val id: Long = 0L, val postId: Long, val url: String, - val width: Int = 0, - val height: Int = 0, val createdAt: LocalDateTime ) { @@ -28,19 +26,21 @@ class PostImage( companion object { - fun of(postId: Long = 0L, cloudFrontUrl: String, imageUrl: String, width: Int, height: Int): PostImage { + fun of(postId: Long = 0L, cloudFrontUrl: String, imageUrl: String): PostImage { return PostImage( id = 0L, postId = postId, url = "${cloudFrontUrl}/${imageUrl}", - width = width, - height = height, createdAt = LocalDateTime.now() ) } } + fun isNew(): Boolean { + return id == 0L + } + fun addedPost(postId: Long): PostImage { return update( postId = postId @@ -51,16 +51,12 @@ class PostImage( id: Long = this.id, postId: Long = this.postId, url: String = this.url, - width: Int = this.width, - height: Int = this.height, createdAt: LocalDateTime = this.createdAt ): PostImage { return PostImage( id = id, postId = postId, url = url, - width = width, - height = height, createdAt = createdAt ) } diff --git a/domain/src/main/kotlin/com/wespot/post/PostImages.kt b/domain/src/main/kotlin/com/wespot/post/PostImages.kt index c0c0804b..300fe6dc 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostImages.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostImages.kt @@ -8,6 +8,10 @@ data class PostImages( val postImages: List ) { + fun isThereOnlyNewImages(): Boolean { + return postImages.all { it.isNew() } + } + companion object { private const val IMAGE_MAX_COUNT_INCLUSIVE = 3 diff --git a/domain/src/main/kotlin/com/wespot/post/PostProfile.kt b/domain/src/main/kotlin/com/wespot/post/PostProfile.kt index 41203589..3a76ebbe 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostProfile.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostProfile.kt @@ -1,22 +1,30 @@ package com.wespot.post +import com.wespot.common.EverProfile import java.time.LocalDateTime data class PostProfile( val id: Long = 0L, val userId: Long, - val url: String, - val name: String, + val url: String = EverProfile.randomProfile().profileUrl, + val name: String = INITIAL_NAME, val createdAt: LocalDateTime = LocalDateTime.now() ) { + companion object { + + val DEFAULT_PROFILE: PostProfile = PostProfile( + userId = 0L, + ) + private const val INITIAL_NAME = "익명의 글쓴이" - fun from(userId: Long): PostProfile { // TODO : 새로운 익명 프로필 이미지 주입 로직 추가 + + fun from(userId: Long): PostProfile { return PostProfile( userId = userId, - url = "", - name = INITIAL_NAME ) } + } + } diff --git a/domain/src/main/kotlin/com/wespot/post/PostReport.kt b/domain/src/main/kotlin/com/wespot/post/PostReport.kt index 28951268..241e63b8 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostReport.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostReport.kt @@ -1,12 +1,13 @@ package com.wespot.post +import com.wespot.report.ReportReason import java.time.LocalDateTime class PostReport( val id: Long = 0L, val postId: Long, val userId: Long, - val reason: String = "", + val reportReason: ReportReason, val createdAt: LocalDateTime = LocalDateTime.now() ) { } diff --git a/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/Gradation.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/Gradation.kt new file mode 100644 index 00000000..8b78be0f --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/Gradation.kt @@ -0,0 +1,9 @@ +package com.wespot.post.nudge.server_driven + +import com.wespot.view.color.Color + +data class Gradation( + val startColor: Color, + val endColor: Color, + val angle: Int, +) diff --git a/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt new file mode 100644 index 00000000..fb420a1a --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt @@ -0,0 +1,121 @@ +package com.wespot.post.nudge.server_driven + +import com.wespot.common.TimeExpressionUtil +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.post.Post +import com.wespot.post.nudge.HotPostNudge +import com.wespot.view.color.Color +import com.wespot.view.icon.IconV2 +import com.wespot.view.image.ImageContentV2 +import com.wespot.view.text.RichTextV2 + +class HotPostComponent( + val type: String = "HotPostItem", + val id: Long, + val content: HotPostContent +) { + + data class HotPostContent( + val title: HotPostTitle, + val posts: List + ) { + + data class HotPostTitle( + val icon: IconV2 = IconV2.HOT_POST_ICON, + val text: RichTextV2 = RichTextV2.HOT_POST_TEXT, + ) { + + } + + data class HotPost( + val headerSection: HotPostHeaderSection, + val infoSection: HotPostInfoSection, + val createdAt: RichTextV2, + val gradation: Gradation, + ) { + + companion object { + + fun from(post: Post): HotPost { + return HotPost( + headerSection = HotPostHeaderSection( + profileImage = ImageContentV2( + url = post.profile.url, + width = 24, + height = 24 + ), + nickname = RichTextV2( + text = post.profile.name, + color = Color(value = ColorType.GRAY900.value), + typography = TypographType.BADGE.value, + maxLine = 1 + ), + ), + infoSection = HotPostInfoSection( + title = post.title?.let { + RichTextV2( + text = it.content, + color = Color(value = ColorType.GRAY900.value), + typography = TypographType.BODY05.value, + maxLine = 1 + ) + }, + description = RichTextV2( + text = post.description.content, + color = Color(value = ColorType.GRAY900.value), + typography = post.title?.let { TypographType.BODY07.value } + ?: TypographType.BODY05.value, + maxLine = post.title?.let { 1 } ?: 2 + ), + ), + createdAt = RichTextV2( + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), + color = Color(value = ColorType.GRAY500.value), + typography = TypographType.BODY12.value, + maxLine = 1 + ), + gradation = Gradation( + startColor = Color(value = "#F6D1FF", type = Color.HEX), + endColor = Color(value = "#A7A7FF", type = Color.HEX), + angle = 90 + ) + ) + } + + } + + data class HotPostHeaderSection( + val profileImage: ImageContentV2, + val nickname: RichTextV2, + ) { + + } + + data class HotPostInfoSection( + val title: RichTextV2?, + val description: RichTextV2, + ) { + + } + + } + + } + + companion object { + + fun of(id: Long, hotPostNudgeContent: HotPostNudge.HotPostNudgeContent): HotPostComponent { + return HotPostComponent( + id = id, + content = HotPostContent( + title = HotPostContent.HotPostTitle(), + posts = hotPostNudgeContent.posts + .map { HotPostContent.HotPost.from(post = it) } + ) + ) + } + + } + +} diff --git a/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/MessageComponent.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/MessageComponent.kt new file mode 100644 index 00000000..0feb9222 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/MessageComponent.kt @@ -0,0 +1,53 @@ +package com.wespot.post.nudge.server_driven + +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.view.color.Color +import com.wespot.view.icon.IconV2 +import com.wespot.view.image.ImageContentV2 +import com.wespot.view.text.RichTextV2 + +data class MessageComponent( + val type: String = "BannerItem", + val id: Long, + val content: MessageContentComponent, +) { + + data class MessageContentComponent( + val thumbnail: ImageContentV2, + val title: RichTextV2, + val description: RichTextV2, + val icon: IconV2, + ) + + companion object { + fun from( + id: Long, + ): MessageComponent { + return MessageComponent( + id = id, + content = MessageContentComponent( + thumbnail = ImageContentV2( + url = "https://dw2d2daekmyur.cloudfront.net/message_nudge.png", + width = 40, + height = 40 + ), + title = RichTextV2( + text = "답장을 기다리는 쪽지가 있어요", + color = Color(value = ColorType.GRAY100.value), + typography = TypographType.BODY03.value, + maxLine = 1 + ), + description = RichTextV2( + text = "눌러서 바로 확인하기", + color = Color(value = ColorType.PRIMARY300.value), + typography = TypographType.BODY07.value, + maxLine = 1 + ), + icon = IconV2.MOVE_TO_MESSAGE_ICON + ) + ) + } + } + +} diff --git a/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt new file mode 100644 index 00000000..471a9721 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt @@ -0,0 +1,72 @@ +package com.wespot.post.nudge.server_driven + +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.post.nudge.VoteNudge +import com.wespot.view.color.Color +import com.wespot.view.icon.IconV2 +import com.wespot.view.text.RichTextV2 + +data class VoteComponent( + val id: Long, + val type: String = "VoteItem", + val content: VoteContent, +) { + + data class VoteContent( + val badge: VoteBadge, + val text: RichTextV2, + val actionIcon: VoteActionIcon, + val gradation: Gradation, + ) { + + data class VoteBadge( + val backgroundColor: Color, + val text: RichTextV2, + ) + + data class VoteActionIcon( + val backgroundColor: Color, + val icon: IconV2, + ) + } + + companion object { + + fun of(id: Long, voteNudgeContent: VoteNudge.VoteNudgeContent): VoteComponent { + val voteOption = voteNudgeContent.voteOption + + return VoteComponent( + id = id, + content = VoteContent( + badge = VoteContent.VoteBadge( + backgroundColor = Color("#FF8D65", type = "Hex"), + text = RichTextV2( + text = "비밀 투표", + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BADGE.value, + maxLine = 1 + ) + ), + text = RichTextV2( + text = "지금 우리 반에서 가장\n${voteOption.content.content}는?", + color = Color(value = ColorType.GRAY900.value), + typography = TypographType.BODY04.value, + maxLine = 2 + ), + actionIcon = VoteContent.VoteActionIcon( + backgroundColor = Color(value = ColorType.GRAY900.value), + icon = IconV2.MOVE_TO_VOTE_ICON + ), + gradation = Gradation( + startColor = Color(value = "#FEF0B5", type = Color.HEX), + endColor = Color(value = "#FBAA8B", type = Color.HEX), + angle = 90 + ) + ) + ) + } + + } + +} diff --git a/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryComponent.kt b/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryComponent.kt new file mode 100644 index 00000000..cd1a15df --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryComponent.kt @@ -0,0 +1,11 @@ +package com.wespot.post.server_driven + +import com.wespot.view.icon.IconV2 +import com.wespot.view.text.RichTextV2 + +data class PostCategoryComponent( + val text: RichTextV2, + val target: String, + val icon: IconV2, +) { +} diff --git a/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryItems.kt b/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryItems.kt new file mode 100644 index 00000000..95925674 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryItems.kt @@ -0,0 +1,39 @@ +package com.wespot.post.server_driven + +import com.wespot.post.PostCategories +import com.wespot.post.PostCategory + +class PostCategoryItems( + val category: String, + val chips: List +) { + + data class PostCategoryItem( + val id: Long, + val text: String, + ) { + + companion object { + fun from(postCategory: PostCategory): PostCategoryItem { + return PostCategoryItem( + id = postCategory.id, + text = postCategory.name + ) + } + } + + } + + companion object { + + fun from(eachMajorCategory: PostCategories.EachMajorCategory): PostCategoryItems { + return PostCategoryItems( + category = eachMajorCategory.majorCategoryName(), + chips = eachMajorCategory.postCategories + .map { PostCategoryItem.from(it) } + ) + } + + } + +} diff --git a/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt new file mode 100644 index 00000000..c808fbce --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -0,0 +1,288 @@ +package com.wespot.post.server_driven + +import com.wespot.common.TimeExpressionUtil +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.post.Post +import com.wespot.view.color.Color +import com.wespot.view.icon.IconV2 +import com.wespot.view.image.ImageContentV2 +import com.wespot.view.text.RichTextV2 + +data class PostComponent( + val id: Long, + val type: String = "PostItem", + val isMyPost: Boolean, + val content: PostContent, +) { + + data class PostContent( + val category: PostCategoryComponent? = null, + val headerSection: PostHeaderSection, + val infoSection: PostInfoSection, + val contentSection: PostContentSection? = null, + val footerSection: PostFooterSection, + val button: PostButtonComponent? = null, + ) { + + data class PostHeaderSection( + val profileImage: ImageContentV2, + val nickname: RichTextV2, + val createdAt: RichTextV2, + val category: PostCategoryComponent? = null, + val button: PostButtonComponent? = null, + ) { + + } + + data class PostInfoSection( + val title: RichTextV2? = null, + val description: RichTextV2, + val seeMore: RichTextV2 = RichTextV2( + text = "더보기", + color = Color(value = ColorType.PRIMARY300.value), + typography = TypographType.BODY06.value, + maxLine = 1 + ), + val maxLine: Int = 5, + ) { + + } + + data class PostContentSection( + val type: String = "Images", + val images: List, + ) { + + } + + data class PostFooterSection( + val reactions: List, + val scrap: PostScrapComponent + ) { + data class PostReactionItem( + val type: String, + val icon: IconV2, + val count: RichTextV2, + val selected: Boolean, + ) { + + } + + data class PostScrapComponent( + val icon: IconV2, + val selected: Boolean = false, + ) { + + } + + } + + data class PostButtonComponent( + val type: String = "notification", + val icon: IconV2, + val text: RichTextV2, + val isSelected: Boolean = false, + ) { + + } + + } + + companion object { + + fun of( + post: Post, + viewerId: Long, + isCategoryScreen: Boolean = false, + ): PostComponent { + return PostComponent( + id = post.id, + isMyPost = post.isAuthor(viewerId), + content = PostContent( + headerSection = PostContent.PostHeaderSection( + profileImage = ImageContentV2( + url = post.profile.url, + width = 36, + height = 36, + ), + nickname = RichTextV2( + text = post.profile.name, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY06.value, + maxLine = 1, + ), + createdAt = RichTextV2( + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), + color = Color(value = ColorType.GRAY400.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + category = if (!isCategoryScreen) PostCategoryComponent( + text = RichTextV2( + text = post.category.name, + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + target = post.category.id.toString(), + icon = IconV2.RIGHT_ARROW + ) else null + ), + infoSection = PostContent.PostInfoSection( + title = post.title?.let { + RichTextV2( + text = post.title.content, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY04.value, + maxLine = 1, + ) + }, + description = RichTextV2( + text = post.description.content, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY00.value, + maxLine = post.title?.let { 3 } ?: 5, + ), + ), + contentSection = post.images?.let { + PostContent.PostContentSection( + images = it.postImages.map { image -> image.url } + ) + }, + footerSection = PostContent.PostFooterSection( + reactions = listOf( + PostContent.PostFooterSection.PostReactionItem( + type = "Chat", + icon = IconV2.COMMENT_BALLON, + count = RichTextV2( + text = post.commentCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + selected = false, + ), + PostContent.PostFooterSection.PostReactionItem( + type = "Like", + icon = IconV2.THUMBS_UP, + count = RichTextV2( + text = post.likeCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + selected = post.postStatusByViewer?.isViewerPushedLike ?: false, + ), + ), + scrap = PostContent.PostFooterSection.PostScrapComponent( + icon = IconV2.BOOKMARK, + selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, + ), + ) + ), + ) + } + + fun fromDetail(post: Post, viewerId: Long): PostComponent { + return PostComponent( + id = post.id, + isMyPost = post.isAuthor(viewerId), + content = PostContent( + category = PostCategoryComponent( + text = RichTextV2( + text = post.category.name, + color = Color(value = "#FFFFFF", type = Color.HEX), + typography = TypographType.BODY06.value, + maxLine = 1, + ), + target = post.category.id.toString(), + icon = IconV2.RIGHT_ARROW_IN_BLACK + ), + headerSection = PostContent.PostHeaderSection( + profileImage = ImageContentV2( + url = post.profile.url, + width = 40, + height = 40, + ), + nickname = RichTextV2( + text = post.profile.name, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY06.value, + maxLine = 1 + ), + createdAt = RichTextV2( + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), + color = Color(value = ColorType.GRAY400.value), + typography = TypographType.BODY09.value, + maxLine = 1 + ), + button = PostContent.PostButtonComponent( + icon = IconV2.COMMENT_NOTIFICATION_CHECK, + text = RichTextV2( + text = "댓글 알림", + color = Color(value = ColorType.GRAY100.value), + typography = TypographType.BODY09.value, + maxLine = 1 + ), + isSelected = post.postStatusByViewer?.isViewerPushedNotification ?: false, + ) + ), + infoSection = PostContent.PostInfoSection( + title = post.title?.let { + RichTextV2( + text = post.title.content, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY04.value, + maxLine = 1, + ) + }, + description = RichTextV2( + text = post.description.content, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY00.value, + maxLine = post.title?.let { 3 } ?: 5, + ), + ), + contentSection = post.images?.let { + PostContent.PostContentSection( + images = it.postImages.map { image -> image.url } + ) + }, + footerSection = PostContent.PostFooterSection( + reactions = listOf( + PostContent.PostFooterSection.PostReactionItem( + type = "Chat", + icon = IconV2.COMMENT_BALLON, + count = RichTextV2( + text = post.commentCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + selected = false, + ), + PostContent.PostFooterSection.PostReactionItem( + type = "Like", + icon = IconV2.THUMBS_UP, + count = RichTextV2( + text = post.likeCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, + ), + selected = post.postStatusByViewer?.isViewerPushedLike ?: false, + ), + ), + scrap = PostContent.PostFooterSection.PostScrapComponent( + icon = IconV2.BOOKMARK, + selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, + ), + ) + ), + ) + } + + } + +} diff --git a/domain/src/main/kotlin/com/wespot/post/vo/PostSearchKeyword.kt b/domain/src/main/kotlin/com/wespot/post/vo/PostSearchKeyword.kt index 74159e52..2fd6245a 100644 --- a/domain/src/main/kotlin/com/wespot/post/vo/PostSearchKeyword.kt +++ b/domain/src/main/kotlin/com/wespot/post/vo/PostSearchKeyword.kt @@ -34,4 +34,14 @@ data class PostSearchKeyword( return listOf(keyword) } + fun keywordsToSearchInRegex(): String { + val keywordsToSearch = keywordsToSearch() + + if (keywordsToSearch.size == 1) { + return keywordsToSearch[0] + } + + return keywordsToSearch.joinToString(separator = "|") { it.replace(" ", "") } + } + } diff --git a/domain/src/main/kotlin/com/wespot/report/ReportReason.kt b/domain/src/main/kotlin/com/wespot/report/ReportReason.kt new file mode 100644 index 00000000..2af13823 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/report/ReportReason.kt @@ -0,0 +1,7 @@ +package com.wespot.report + +data class ReportReason( + val id: Long = 0L, + val content: String, +) { +} diff --git a/domain/src/main/kotlin/com/wespot/user/Setting.kt b/domain/src/main/kotlin/com/wespot/user/Setting.kt index 81df00b9..2790330d 100644 --- a/domain/src/main/kotlin/com/wespot/user/Setting.kt +++ b/domain/src/main/kotlin/com/wespot/user/Setting.kt @@ -6,4 +6,5 @@ data class Setting( val isEnableMarketingNotification: Boolean = false, val isEnableMessage: Boolean = true, + val isEnablePostNotification: Boolean = false, ) diff --git a/domain/src/main/kotlin/com/wespot/user/User.kt b/domain/src/main/kotlin/com/wespot/user/User.kt index c9ad032e..61dd7148 100644 --- a/domain/src/main/kotlin/com/wespot/user/User.kt +++ b/domain/src/main/kotlin/com/wespot/user/User.kt @@ -244,11 +244,13 @@ data class User( isEnableMessageNotification: Boolean? = null, isEnableMarketingNotification: Boolean? = null, isEnableMessage: Boolean? = null, + isEnablePostNotification: Boolean? = null ) { isEnableVoteNotification?.let { this.setting = this.setting.copy(isEnableVoteNotification = it) } isEnableMessageNotification?.let { this.setting = this.setting.copy(isEnableMessageNotification = it) } isEnableMarketingNotification?.let { this.setting = this.setting.copy(isEnableMarketingNotification = it) } isEnableMessage?.let { this.setting = this.setting.copy(isEnableMessage = it) } + isEnablePostNotification?.let { this.setting = this.setting.copy(isEnablePostNotification = it) } } fun isEnableVoteNotification() = setting.isEnableVoteNotification diff --git a/domain/src/main/kotlin/com/wespot/user/message/UsedAnswerMessage.kt b/domain/src/main/kotlin/com/wespot/user/message/UsedAnswerMessage.kt index 93d1fd42..369d01bc 100644 --- a/domain/src/main/kotlin/com/wespot/user/message/UsedAnswerMessage.kt +++ b/domain/src/main/kotlin/com/wespot/user/message/UsedAnswerMessage.kt @@ -3,10 +3,10 @@ package com.wespot.user.message import java.time.LocalDateTime class UsedAnswerMessage( - val id: Long, + val id: Long = 0L, val userId: Long, val isUsedAnswerMessageFeature: Boolean = false, - val createdAt: LocalDateTime, + val createdAt: LocalDateTime = LocalDateTime.now(), ) { companion object { diff --git a/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt b/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt new file mode 100644 index 00000000..870b6c3f --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt @@ -0,0 +1,63 @@ +package com.wespot.view.chip + +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.post.PostCategories +import com.wespot.view.color.Color +import com.wespot.view.icon.IconV2 +import com.wespot.view.text.RichTextV2 + +data class FilterChip( + val type: String = "FilterChip", + val content: FilterChipContent, +) { + + data class FilterChipContent( + val id: Long, + val icon: IconV2 = IconV2(url = "", color = Color()), + val text: RichTextV2, + val target: String, + ) { + } + + companion object { + + fun of( + id: Long, + text: String, + ): FilterChip { + return FilterChip( + content = FilterChipContent( + id = id, + icon = IconV2(url = "", color = Color()), + text = RichTextV2( + text = text, + color = Color(value = ColorType.GRAY600.value), + typography = TypographType.BODY06.value, + maxLine = 1 + ), + target = text + ) + ) + } + + fun of( + id: Long, + eachMajorCategory: PostCategories.EachMajorCategory + ): FilterChip { + return FilterChip( + content = FilterChipContent( + id = id, + icon = IconV2(url = "", color = Color()), + text = RichTextV2( + text = eachMajorCategory.majorCategoryName(), + color = Color(value = ColorType.GRAY600.value), + typography = TypographType.BODY06.value, + maxLine = 1, + ), + target = eachMajorCategory.majorCategoryName() + ) + ) + } + } +} diff --git a/domain/src/main/kotlin/com/wespot/view/color/Color.kt b/domain/src/main/kotlin/com/wespot/view/color/Color.kt new file mode 100644 index 00000000..72559829 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/color/Color.kt @@ -0,0 +1,18 @@ +package com.wespot.view.color + +import com.wespot.common.view.ColorType + +data class Color( + val value: String = "GRAY600", + val type: String = "Token", +) { + + companion object { + + const val TOKEN = "Token" + const val HEX = "Hex" + + val DEFAULT_COLOR = Color(value = ColorType.GRAY300.value) + } + +} diff --git a/domain/src/main/kotlin/com/wespot/view/color/StringColor.kt b/domain/src/main/kotlin/com/wespot/view/color/StringColor.kt index 8243e4bf..2e788c38 100644 --- a/domain/src/main/kotlin/com/wespot/view/color/StringColor.kt +++ b/domain/src/main/kotlin/com/wespot/view/color/StringColor.kt @@ -5,7 +5,7 @@ import com.wespot.exception.ExceptionView import org.springframework.http.HttpStatus data class StringColor( - val value: String + val value: String, ) { companion object { diff --git a/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt new file mode 100644 index 00000000..cbd30afc --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt @@ -0,0 +1,54 @@ +package com.wespot.view.icon + +import com.wespot.common.view.ColorType +import com.wespot.view.color.Color + +data class IconV2( + val url: String, + val color: Color = Color.DEFAULT_COLOR, +) { + companion object { + + val RIGHT_ARROW_IN_BLACK = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/right_arrow_in_black.png", + color = Color(value = ColorType.GRAY300.value) + ) + + val HOT_POST_ICON: IconV2 = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/hot_post.png", + color = Color.DEFAULT_COLOR + ) + + val MOVE_TO_VOTE_ICON: IconV2 = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/move_to_message.png", + color = Color(value = ColorType.WHITE.value) + ) + + val MOVE_TO_MESSAGE_ICON: IconV2 = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/move_to_message.png", + color = Color(value = ColorType.WHITE.value) + ) + + val COMMENT_BALLON = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/comment_ballon.png", + ) + + val THUMBS_UP = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/thumbs_up.png", + ) + + val COMMENT_NOTIFICATION_CHECK = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/comment_notification_check.png", + ) + + val BOOKMARK = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/bookmark.png", + ) + + val RIGHT_ARROW = IconV2( + url = "https://dw2d2daekmyur.cloudfront.net/right_arrow.png", + color = Color(value = ColorType.GRAY600.value) + ) + + } +} diff --git a/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt b/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt new file mode 100644 index 00000000..f4901b92 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt @@ -0,0 +1,8 @@ +package com.wespot.view.image + +data class ImageContentV2( + val url: String, + val width: Int? = null, + val height: Int? = null, +) { +} diff --git a/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt b/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt new file mode 100644 index 00000000..ec6e6f72 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt @@ -0,0 +1,23 @@ +package com.wespot.view.text + +import com.wespot.common.view.ColorType +import com.wespot.common.view.TypographType +import com.wespot.view.color.Color + +data class RichTextV2( + val text: String, + val color: Color = Color.DEFAULT_COLOR, + val typography: String = "Body01", + val maxLine: Int? = null, +) { + + companion object { + val HOT_POST_TEXT: RichTextV2 = RichTextV2( + text = "실시간 인기글", + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY02.value, + maxLine = 1 + ) + } + +} diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/comment/adapter/PostCommentAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/comment/adapter/PostCommentAdapter.kt index ef844910..c8785708 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/comment/adapter/PostCommentAdapter.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/comment/adapter/PostCommentAdapter.kt @@ -48,6 +48,10 @@ class PostCommentAdapter( postCommentJpaRepository.deleteByPostId(postId) } + override fun deleteByCommentId(commentId: Long) { + postCommentJpaRepository.deleteById(commentId) + } + private fun getCompletePostComments( postCommentEntities: List, viewerId: Long? diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2JpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2JpaRepository.kt index a229b876..7ddcf12c 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2JpaRepository.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2JpaRepository.kt @@ -2,20 +2,19 @@ package com.wespot.message.v2 import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query -import java.time.LocalDate import java.time.LocalDateTime interface MessageV2JpaRepository : JpaRepository { fun countBySenderIdAndBaseEntityCreatedAtBetween(senderId: Long, from: LocalDateTime, to: LocalDateTime): Int - fun findAllByMessageRoomIdIsNullAndSenderId(senderId: Long): List + fun findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBlockedFalse(senderId: Long): List - fun findAllByMessageRoomIdIsNullAndReceiverId(receiverId: Long): List + fun findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBlockedFalse(receiverId: Long): List - fun findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBookmarkedTrue(senderId: Long): List + fun findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBookmarkedTrueAndIsSenderBlockedFalse(senderId: Long): List - fun findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBookmarkedTrue(receiverId: Long): List + fun findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBookmarkedTrueAndIsReceiverBlockedFalse(receiverId: Long): List fun findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBlockedTrue(senderId: Long): List diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2PersistenceAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2PersistenceAdapter.kt index 63b437dd..116d8506 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2PersistenceAdapter.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/message/v2/MessageV2PersistenceAdapter.kt @@ -42,7 +42,7 @@ class MessageV2PersistenceAdapter( } override fun findAllMessageRoomBySenderId(senderId: Long): List { - val messageRooms = messageV2JpaRepository.findAllByMessageRoomIdIsNullAndSenderId(senderId = senderId) + val messageRooms = messageV2JpaRepository.findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBlockedFalse(senderId = senderId) return getCompleteMessageV2(messageRooms) } @@ -87,19 +87,19 @@ class MessageV2PersistenceAdapter( } override fun findAllMessageRoomByReceiverId(receiverId: Long): List { - val messageRooms = messageV2JpaRepository.findAllByMessageRoomIdIsNullAndReceiverId(receiverId = receiverId) + val messageRooms = messageV2JpaRepository.findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBlockedFalse(receiverId = receiverId) return getCompleteMessageV2(messageRooms) } override fun findAllMessageRoomBySenderIdAndIsSenderBookmarkedTrue(senderId: Long): List { val messageRooms = - messageV2JpaRepository.findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBookmarkedTrue(senderId = senderId) + messageV2JpaRepository.findAllByMessageRoomIdIsNullAndSenderIdAndIsSenderBookmarkedTrueAndIsSenderBlockedFalse(senderId = senderId) return getCompleteMessageV2(messageRooms) } override fun findAllMessageRoomByReceiverIdAndIsReceiverBookmarkedTrue(receiverId: Long): List { val messageRooms = - messageV2JpaRepository.findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBookmarkedTrue(receiverId = receiverId) + messageV2JpaRepository.findAllByMessageRoomIdIsNullAndReceiverIdAndIsReceiverBookmarkedTrueAndIsReceiverBlockedFalse(receiverId = receiverId) return getCompleteMessageV2(messageRooms) } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostCategoryEntity.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostCategoryEntity.kt index 8a7c9514..4c42c1af 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostCategoryEntity.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostCategoryEntity.kt @@ -17,6 +17,12 @@ class PostCategoryEntity( @field:NotNull val name: String, + @field:NotNull + val thumbnail: String, + + @field:NotNull + val backgroundImage: String, + @Embedded val baseEntity: BaseEntity ) { diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostReportEntity.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostReportEntity.kt index 4db5ed98..cc06f03b 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostReportEntity.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/PostReportEntity.kt @@ -18,7 +18,7 @@ class PostReportEntity( val userId: Long, @field:NotNull - val reason: String, + val reportReasonId: Long, @Embedded val baseEntity: BaseEntity diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt index 0fab08b8..2ad7b83b 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt @@ -8,6 +8,7 @@ import com.wespot.post.PostStatusByViewer import com.wespot.post.mapper.PostCategoryMapper import com.wespot.post.mapper.PostImageMapper import com.wespot.post.mapper.PostMapper +import com.wespot.post.mapper.PostProfileMapper import com.wespot.post.port.out.PostPort import com.wespot.post.repository.* import com.wespot.user.User @@ -25,11 +26,18 @@ class PostAdapter( private val postLikeJpaRepository: PostLikeJpaRepository, private val postNotificationJpaRepository: PostNotificationJpaRepository, private val postScrapJpaRepository: PostScrapJpaRepository, + private val postProfileJpaRepository: PostProfileJpaRepository, ) : PostPort, PostValidatePort { override fun save(post: Post): Post { val postEntity = PostMapper.toEntity(post) val savedPostEntity = postJpaRepository.save(postEntity) + + post.images?.let { + if (it.isThereOnlyNewImages()) { + postImageJpaRepository.deleteByPostId(savedPostEntity.id) + } + } val savedPostImages: PostImages? = post.images?.postImages ?.map { PostImageMapper.toEntity(it) } ?.let { postImageJpaRepository.saveAll(it) } @@ -40,12 +48,30 @@ class PostAdapter( entity = savedPostEntity, postCategory = post.category, user = post.user, - postImages = savedPostImages + postImages = savedPostImages, + postProfile = post.profile, + ) + } + + override fun searchByTitleAndDescription( + keyword: String, + viewerId: Long?, + inquirySize: Long, + cursorId: Long? + ): List { + val posts = postJpaRepository.searchByTitleAndDescription( + pattern = keyword, + cursorId = cursorId ?: Long.MAX_VALUE, + limit = inquirySize.toInt() ) + + return getCompletePost(posts, viewerId) } override fun searchByTitle(title: String, viewerId: Long?): List { - val posts = postJpaRepository.findAllByTitleContaining(title = title) + val posts = postJpaRepository.findByTitleContainingOrderByBaseEntityCreatedAtDesc( + title = title, + ) return getCompletePost(posts, viewerId) } @@ -67,6 +93,9 @@ class PostAdapter( val postIdToPostImages = postImageJpaRepository.findAllByPostIdIn(postIds) .map { PostImageMapper.toDomain(it) } .groupBy { it.postId } + val userIdToPostProfile = postProfileJpaRepository.findAllByUserIdIn(userIds) + .map { PostProfileMapper.toDomain(it) } + .associateBy { it.userId } val postIdToPostStatusByViewer = getPostIdToPostStatusByViewer(postIds, viewer = viewer) @@ -76,7 +105,8 @@ class PostAdapter( postCategory = categoryIdToCategory[postEntity.categoryId]!!, user = userIdToUser[postEntity.userId]!!, postImages = postIdToPostImages[postEntity.id]?.let { postImages -> PostImages(postImages) }, - postStatusByViewer = postIdToPostStatusByViewer?.let { postIdToPostStatusByViewer[postEntity.id] } + postStatusByViewer = postIdToPostStatusByViewer?.let { postIdToPostStatusByViewer[postEntity.id] }, + postProfile = userIdToPostProfile[postEntity.userId]!! ) } } @@ -104,8 +134,13 @@ class PostAdapter( } } - override fun searchByDescription(description: String, viewerId: Long?): List { - val posts = postJpaRepository.findAllByDescriptionContaining(description = description) + override fun searchByDescription( + description: String, + viewerId: Long?, + ): List { + val posts = postJpaRepository.findAllByDescriptionContainingOrderByBaseEntityCreatedAtDesc( + description = description, + ) return getCompletePost(posts, viewerId) } @@ -116,37 +151,69 @@ class PostAdapter( ?.first() } - override fun findAllByCategoryId(categoryId: Long, viewerId: Long?): List { - val posts = postJpaRepository.findByCategoryId(categoryId) + override fun findAllByCategoryId( + categoryId: Long, + viewerId: Long?, + inquirySize: Long, + cursorId: Long? + ): List { + val posts = postJpaRepository.findByCategoryIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryId = categoryId, + cursorId = cursorId ?: Long.MAX_VALUE, + pageable = PageRequest.of(0, inquirySize.toInt()) + ) return getCompletePost(posts, viewerId) } override fun findAllByCategoryIdIn( categoryIds: List, - viewerId: Long? + viewerId: Long?, inquirySize: Long, cursorId: Long? ): List { - val posts = postJpaRepository.findByCategoryIdIn(categoryIds) + val posts = postJpaRepository.findByCategoryIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryIds = categoryIds, + cursorId = cursorId ?: Long.MAX_VALUE, + pageable = PageRequest.of(0, inquirySize.toInt()) + ) return getCompletePost(posts, viewerId) } - override fun findAllByUserId(authorId: Long): List { - val posts = postJpaRepository.findAllByUserId(authorId) + override fun findAllByUserId(authorId: Long, inquirySize: Long, cursorId: Long?): List { + val posts = postJpaRepository.findAllByUserIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + userId = authorId, + cursorId = cursorId ?: Long.MAX_VALUE, + pageable = PageRequest.of(0, inquirySize.toInt()) + ) return getCompletePost(posts, authorId) } - override fun findAllByPostIdIn(postIds: List, viewerId: Long?): List { - val posts = postJpaRepository.findAllByIdIn(postIds) + override fun findAllByPostIdIn( + postIds: List, + viewerId: Long?, + inquirySize: Long, + cursorId: Long? + ): List { + val posts = postJpaRepository.findAllByIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + postIds = postIds, + cursorId = cursorId ?: Long.MAX_VALUE, + pageable = PageRequest.of(0, inquirySize.toInt()) + ) return getCompletePost(posts, viewerId) } - override fun findAllRecentPostByLimit(limit: Int, viewerId: Long?): List { - val pageable = PageRequest.of(0, limit) + override fun findAllRecentPost( + viewerId: Long?, + inquirySize: Long, + cursorId: Long?, + ): List { val findAllByOrderByBaseEntityCreatedAtDesc = - postJpaRepository.findAllByOrderByBaseEntityCreatedAtDesc(pageable) + postJpaRepository.findAllByIdLessThanOrderByBaseEntityCreatedAtDesc( + cursorId = cursorId ?: Long.MAX_VALUE, + pageable = PageRequest.of(0, inquirySize.toInt()) + ) return getCompletePost(findAllByOrderByBaseEntityCreatedAtDesc, viewerId) } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostReportAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostReportAdapter.kt index 377c2233..b7128e5d 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostReportAdapter.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostReportAdapter.kt @@ -4,15 +4,24 @@ import com.wespot.post.PostReport import com.wespot.post.mapper.PostReportMapper import com.wespot.post.port.out.PostReportPort import com.wespot.post.repository.PostReportJpaRepository +import com.wespot.report.port.out.ReportReasonPort import org.springframework.stereotype.Repository @Repository class PostReportAdapter( - private val postReportJpaRepository: PostReportJpaRepository + private val postReportJpaRepository: PostReportJpaRepository, + private val reportReasonPort: ReportReasonPort, ) : PostReportPort { override fun findByPostIdAndUserId(postId: Long, userId: Long): PostReport? { - return postReportJpaRepository.findByPostIdAndUserId(postId, userId)?.let { PostReportMapper.toDomain(it) } + return postReportJpaRepository.findByPostIdAndUserId(postId, userId) + ?.let { + PostReportMapper.toDomain( + it, + reportReason = reportReasonPort.findById(it.reportReasonId) + ?: throw IllegalStateException("신고 사유가 존재하지 않습니다.") + ) + } } override fun deleteById(id: Long) { @@ -22,7 +31,10 @@ class PostReportAdapter( override fun save(postBlock: PostReport): PostReport { val postBlockEntity = PostReportMapper.toEntity(postBlock) val savedPostBlockEntity = postReportJpaRepository.save(postBlockEntity) - return PostReportMapper.toDomain(savedPostBlockEntity) + return PostReportMapper.toDomain( + savedPostBlockEntity, + reportReason = postBlock.reportReason + ) } override fun deleteByPostId(postId: Long) { diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostCategoryMapper.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostCategoryMapper.kt index b34eb737..58688826 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostCategoryMapper.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostCategoryMapper.kt @@ -12,6 +12,8 @@ object PostCategoryMapper { id = postCategory.id, majorCategoryName = postCategory.majorCategoryName, name = postCategory.name, + thumbnail = postCategory.thumbnail, + backgroundImage = postCategory.backgroundImage, baseEntity = BaseEntity(createdAt = postCategory.createdAt, updatedAt = LocalDateTime.now()) ) } @@ -21,6 +23,8 @@ object PostCategoryMapper { id = entity.id, majorCategoryName = entity.majorCategoryName, name = entity.name, + thumbnail = entity.thumbnail, + backgroundImage = entity.backgroundImage, createdAt = entity.baseEntity.createdAt ) } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostMapper.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostMapper.kt index 86affaa8..421ef6a5 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostMapper.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostMapper.kt @@ -28,7 +28,8 @@ object PostMapper { postCategory: PostCategory, user: User, postImages: PostImages? = null, - postStatusByViewer: PostStatusByViewer? = null + postStatusByViewer: PostStatusByViewer? = null, + postProfile: PostProfile, ): Post { return Post( id = entity.id, @@ -41,7 +42,8 @@ object PostMapper { bookmarkedCount = entity.bookmarkCount, images = postImages, postStatusByViewer = postStatusByViewer, - createdAt = entity.baseEntity.createdAt + createdAt = entity.baseEntity.createdAt, + profile = postProfile ) } } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostReportMapper.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostReportMapper.kt index 5582a64e..4259cd32 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostReportMapper.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/mapper/PostReportMapper.kt @@ -3,6 +3,7 @@ package com.wespot.post.mapper import com.wespot.common.BaseEntity import com.wespot.post.PostReport import com.wespot.post.PostReportEntity +import com.wespot.report.ReportReason import java.time.LocalDateTime object PostReportMapper { @@ -12,7 +13,7 @@ object PostReportMapper { id = postReport.id, postId = postReport.postId, userId = postReport.userId, - reason = postReport.reason, + reportReasonId = postReport.reportReason.id, baseEntity = BaseEntity( createdAt = postReport.createdAt, updatedAt = LocalDateTime.now() @@ -20,12 +21,12 @@ object PostReportMapper { ) } - fun toDomain(entity: PostReportEntity): PostReport { + fun toDomain(entity: PostReportEntity, reportReason: ReportReason): PostReport { return PostReport( id = entity.id, postId = entity.postId, userId = entity.userId, - reason = entity.reason, + reportReason = reportReason, createdAt = entity.baseEntity.createdAt ) } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostImageJpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostImageJpaRepository.kt index ccef1181..42ce701f 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostImageJpaRepository.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostImageJpaRepository.kt @@ -9,4 +9,6 @@ interface PostImageJpaRepository : JpaRepository { fun deleteByPostId(postId: Long) + fun postId(postId: Long): MutableList + } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostJpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostJpaRepository.kt index 0f5de975..cd12497f 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostJpaRepository.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostJpaRepository.kt @@ -3,21 +3,64 @@ package com.wespot.post.repository import com.wespot.post.PostEntity import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query interface PostJpaRepository : JpaRepository { - fun findAllByTitleContaining(title: String): List + fun findByTitleContainingOrderByBaseEntityCreatedAtDesc( + title: String, + ): List - fun findAllByDescriptionContaining(description: String): List + fun findAllByDescriptionContainingOrderByBaseEntityCreatedAtDesc( + description: String, + ): List - fun findByCategoryId(categoryId: Long): List + fun findByCategoryIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryId: Long, + cursorId: Long, + pageable: Pageable - fun findByCategoryIdIn(categoryIds: List): List + ): List - fun findAllByUserId(authorId: Long): List + fun findByCategoryIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryIds: List, + cursorId: Long, + pageable: Pageable + ): List - fun findAllByIdIn(postIds: List): List + fun findAllByUserIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + userId: Long, + cursorId: Long, + pageable: Pageable + ): List - fun findAllByOrderByBaseEntityCreatedAtDesc(pageable: Pageable): List + fun findAllByIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + postIds: List, + cursorId: Long, + pageable: Pageable + ): List + + fun findAllByIdLessThanOrderByBaseEntityCreatedAtDesc( + cursorId: Long, + pageable: Pageable + ): List + + @Query( + value = """ + SELECT * + FROM post + WHERE (title REGEXP :pattern + OR description REGEXP :pattern) + AND id < :cursorId + ORDER BY created_at DESC + LIMIT :limit + """, + nativeQuery = true + ) + fun searchByTitleAndDescription( + pattern: String, + cursorId: Long, + limit: Int, + ): List } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostProfileJpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostProfileJpaRepository.kt index ca8a8be2..0e23c1da 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostProfileJpaRepository.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostProfileJpaRepository.kt @@ -8,5 +8,6 @@ interface PostProfileJpaRepository : JpaRepository { fun findByUserId(userId: Long): PostProfileEntity? fun findAllByUserIdIn(userIds: List): List + fun userId(userId: Long): MutableList } diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonAdapter.kt new file mode 100644 index 00000000..52989b7f --- /dev/null +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonAdapter.kt @@ -0,0 +1,22 @@ +package com.wespot.report + +import com.wespot.report.port.out.ReportReasonPort +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Repository + +@Repository +class ReportReasonAdapter( + private val reportReasonJpaRepository: ReportReasonJpaRepository +) : ReportReasonPort { + + override fun findAll(): List { + return reportReasonJpaRepository.findAll() + .map { ReportReasonMapper.toDomain(it) } + } + + override fun findById(reportReasonId: Long): ReportReason? { + return reportReasonJpaRepository.findByIdOrNull(reportReasonId) + ?.let { ReportReasonMapper.toDomain(it) } + } + +} diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaEntity.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaEntity.kt new file mode 100644 index 00000000..5b8455fd --- /dev/null +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaEntity.kt @@ -0,0 +1,19 @@ +package com.wespot.report + +import jakarta.persistence.* +import org.jetbrains.annotations.NotNull + +@Entity +@Table(name = "report_reason") +data class ReportReasonJpaEntity( + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @field:NotNull + val id: Long = 0L, + + @field:NotNull + val content: String + +) { +} diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaRepository.kt new file mode 100644 index 00000000..0fd8b4eb --- /dev/null +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaRepository.kt @@ -0,0 +1,6 @@ +package com.wespot.report + +import org.springframework.data.jpa.repository.JpaRepository + +interface ReportReasonJpaRepository : JpaRepository { +} diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonMapper.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonMapper.kt new file mode 100644 index 00000000..c7c4ca8c --- /dev/null +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonMapper.kt @@ -0,0 +1,19 @@ +package com.wespot.report + +object ReportReasonMapper { + + fun toDomain(entity: ReportReasonJpaEntity): ReportReason { + return ReportReason( + id = entity.id, + content = entity.content + ) + } + + fun toEntity(domain: ReportReason): ReportReasonJpaEntity { + return ReportReasonJpaEntity( + id = domain.id, + content = domain.content + ) + } + +} diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/user/entity/SettingJpaEntity.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/user/entity/SettingJpaEntity.kt index 520ebba5..9506f8e8 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/user/entity/SettingJpaEntity.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/user/entity/SettingJpaEntity.kt @@ -20,4 +20,7 @@ class SettingJpaEntity( @Column(name = "is_enable_message_v2") val isEnableMessageV2: Boolean, - ) + @field: NotNull + val isEnablePostNotification: Boolean + +) diff --git a/infrastructure/mysql/src/main/kotlin/com/wespot/user/mapper/SettingMapper.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/user/mapper/SettingMapper.kt index ab37e01b..1a184bc6 100644 --- a/infrastructure/mysql/src/main/kotlin/com/wespot/user/mapper/SettingMapper.kt +++ b/infrastructure/mysql/src/main/kotlin/com/wespot/user/mapper/SettingMapper.kt @@ -10,7 +10,8 @@ object SettingMapper { isEnableVoteNotification = settingJpaEntity.isEnableVoteNotification, isEnableMessageNotification = settingJpaEntity.isEnableMessageNotification, isEnableMarketingNotification = settingJpaEntity.isEnableMarketingNotification, - isEnableMessage = settingJpaEntity.isEnableMessageV2 + isEnableMessage = settingJpaEntity.isEnableMessageV2, + isEnablePostNotification = settingJpaEntity.isEnablePostNotification, ) fun mapToJpaEntity(setting: Setting): SettingJpaEntity = @@ -18,7 +19,8 @@ object SettingMapper { isEnableVoteNotification = setting.isEnableVoteNotification, isEnableMessageNotification = setting.isEnableMessageNotification, isEnableMarketingNotification = setting.isEnableMarketingNotification, - isEnableMessageV2 = setting.isEnableMessage + isEnableMessageV2 = setting.isEnableMessage, + isEnablePostNotification = setting.isEnablePostNotification, ) }