From e684395444cf6c886f3e503baec9e6afc34fa3f5 Mon Sep 17 00:00:00 2001 From: kpeel Date: Sun, 27 Jul 2025 17:28:44 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=20=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B8=90=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/PostCommentLikeController.kt | 3 +- .../comment/PostCommentReportController.kt | 4 +- .../com/wespot/post/PostCategoryController.kt | 8 +- .../com/wespot/post/PostInquiryController.kt | 71 +++-- .../com/wespot/post/PostLikeController.kt | 3 +- .../post/PostNotificationSettingController.kt | 3 +- .../com/wespot/post/PostScrapController.kt | 7 +- .../com/wespot/post/PostSearchedController.kt | 7 +- .../wespot/common/dto/PostPagingResponse.kt | 9 + .../wespot/common/dto/view/ColorResponse.kt | 23 ++ .../common/dto/view/GradationResponse.kt | 23 ++ .../dto/view/HotPostComponentResponse.kt | 83 ++++++ .../wespot/common/dto/view/IconV2Response.kt | 23 ++ .../common/dto/view/ImageContentV2Response.kt | 24 ++ .../dto/view/MessageComponentResponse.kt | 35 +++ .../common/dto/view/RichTextV2Response.kt | 26 ++ .../common/dto/view/VoteComponentResponse.kt | 67 +++++ .../common/service/view/OnBoardingService.kt | 1 + .../post/dto/response/FilterChipResponse.kt | 34 +++ .../response/PostCategoryComponentResponse.kt | 26 ++ .../response/PostCategoryDetailResponses.kt | 2 + .../dto/response/PostCategoryItemsResponse.kt | 37 +++ .../post/dto/response/PostCategoryResponse.kt | 2 + .../post/dto/response/PostCommentResponse.kt | 8 +- .../dto/response/PostComponentResponse.kt | 167 ++++++++++++ .../post/dto/response/PostImageResponse.kt | 6 + .../wespot/post/dto/response/PostResponse.kt | 2 + .../post/port/in/PostCategoryUseCase.kt | 8 +- .../port/in/PostInquiryByCategoryUseCase.kt | 21 -- .../wespot/post/port/in/PostInquiryUseCase.kt | 43 +++ ...dgeModalUseCase.kt => PostNudgeUseCase.kt} | 2 +- .../post/port/in/PostSearchedUseCase.kt | 6 +- .../com/wespot/post/port/out/PostPort.kt | 20 +- .../post/service/HotPostInquiryService.kt | 9 +- .../post/service/PostCategoryService.kt | 27 +- .../service/PostInquiryByCategoryService.kt | 93 ------- .../wespot/post/service/PostInquiryService.kt | 253 ++++++++++++++++++ .../post/service/PostNudgeModalService.kt | 4 +- .../post/service/PostSearchedService.kt | 22 +- .../kotlin/com/wespot/message/v2/MessageV2.kt | 2 +- .../src/main/kotlin/com/wespot/post/Post.kt | 2 +- .../post/nudge/server_driven/Gradation.kt | 9 + .../nudge/server_driven/HotPostComponent.kt | 99 +++++++ .../nudge/server_driven/MessageComponent.kt | 40 +++ .../post/nudge/server_driven/VoteComponent.kt | 60 +++++ .../server_driven/PostCategoryComponent.kt | 11 + .../post/server_driven/PostCategoryItems.kt | 39 +++ .../post/server_driven/PostComponent.kt | 253 ++++++++++++++++++ .../wespot/user/message/UsedAnswerMessage.kt | 4 +- .../kotlin/com/wespot/view/chip/FilterChip.kt | 50 ++++ .../kotlin/com/wespot/view/color/Color.kt | 12 + .../com/wespot/view/color/StringColor.kt | 2 +- .../kotlin/com/wespot/view/icon/IconV2.kt | 27 ++ .../com/wespot/view/image/ImageContentV2.kt | 8 + .../kotlin/com/wespot/view/text/RichTextV2.kt | 19 ++ .../message/v2/MessageV2JpaRepository.kt | 9 +- .../message/v2/MessageV2PersistenceAdapter.kt | 8 +- .../com/wespot/post/adapter/PostAdapter.kt | 67 ++++- .../post/repository/PostJpaRepository.kt | 50 +++- 59 files changed, 1760 insertions(+), 223 deletions(-) create mode 100644 core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/ColorResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/GradationResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/HotPostComponentResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/IconV2Response.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/MessageComponentResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/RichTextV2Response.kt create mode 100644 core/src/main/kotlin/com/wespot/common/dto/view/VoteComponentResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/post/dto/response/FilterChipResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryComponentResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/post/dto/response/PostCategoryItemsResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt delete mode 100644 core/src/main/kotlin/com/wespot/post/port/in/PostInquiryByCategoryUseCase.kt create mode 100644 core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt rename core/src/main/kotlin/com/wespot/post/port/in/{PostNudgeModalUseCase.kt => PostNudgeUseCase.kt} (83%) delete mode 100644 core/src/main/kotlin/com/wespot/post/service/PostInquiryByCategoryService.kt create mode 100644 core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/nudge/server_driven/Gradation.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/nudge/server_driven/MessageComponent.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryComponent.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/server_driven/PostCategoryItems.kt create mode 100644 domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt create mode 100644 domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt create mode 100644 domain/src/main/kotlin/com/wespot/view/color/Color.kt create mode 100644 domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt create mode 100644 domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt create mode 100644 domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt diff --git a/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt b/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt index 19a3f41a..d5f4b6bd 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt @@ -4,6 +4,7 @@ import com.wespot.comment.port.`in`.PostCommentLikeUseCase 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,7 +14,7 @@ class PostCommentLikeController( private val postCommentLikeUseCase: PostCommentLikeUseCase ) { - @PatchMapping("/{commentId}/like") + @PostMapping("/{commentId}/like") fun likePostComment(@PathVariable commentId: Long): ResponseEntity { postCommentLikeUseCase.likeComment(commentId) diff --git a/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt b/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt index 1d2fff4b..14bde9d9 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt @@ -2,8 +2,8 @@ package com.wespot.comment import com.wespot.comment.port.`in`.PostCommentReportUseCase 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,7 +13,7 @@ class PostCommentReportController( private val postCommentReportUseCase: PostCommentReportUseCase ) { - @PatchMapping("/{commentId}/report") + @PostMapping("/{commentId}/report") fun likePostComment(@PathVariable commentId: Long): ResponseEntity { postCommentReportUseCase.reportComment(commentId) 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/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index 6a61e834..ea129615 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -1,64 +1,95 @@ 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() + fun findAllPosts( + @RequestParam countOfPostsViewed: Long = 0L, + @RequestParam(required = false, defaultValue = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findAllPosts( + countOfPostsViewed, + inquirySize, + cursorId + ) 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 = "10") inquirySize: Long, + @RequestParam(required = false) cursorId: Long? = null, + ): ResponseEntity { + val responses = postInquiryByCategoryUseCase.findPostsByMajorCategoryName( + majorCategoryName, + 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..52eb9d1c 100644 --- a/app/src/main/kotlin/com/wespot/post/PostLikeController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostLikeController.kt @@ -4,6 +4,7 @@ import com.wespot.post.port.`in`.PostLikeUseCase 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,7 +15,7 @@ class PostLikeController( private val postLikeUseCase: PostLikeUseCase ) { - @PatchMapping("/{postId}/like") + @PostMapping("/{postId}/like") fun likePost(@PathVariable postId: Long): ResponseEntity { postLikeUseCase.likePost(postId) diff --git a/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt b/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt index 457ea030..ee6d2ebd 100644 --- a/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt @@ -4,6 +4,7 @@ import com.wespot.post.port.`in`.PostNotificationUseCase 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,7 +15,7 @@ class PostNotificationSettingController( private val postNotificationUseCase: PostNotificationUseCase ) { - @PatchMapping("/{postId}/notification/comment") + @PostMapping("/{postId}/notification/comment") fun updatePostNotificationSettingForComment(@PathVariable postId: Long): ResponseEntity { postNotificationUseCase.toggleNotification(postId) diff --git a/app/src/main/kotlin/com/wespot/post/PostScrapController.kt b/app/src/main/kotlin/com/wespot/post/PostScrapController.kt index 1da0cc7d..eb0a65e6 100644 --- a/app/src/main/kotlin/com/wespot/post/PostScrapController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostScrapController.kt @@ -2,7 +2,8 @@ package com.wespot.post import com.wespot.post.port.`in`.PostScrapUseCase 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,8 +13,8 @@ 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() diff --git a/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt b/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt index 5357c0e6..a6e3d590 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.post.dto.response.PostComponentResponse 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,7 +15,9 @@ class PostSearchedController( ) { @GetMapping("/search") - fun searchPost(keyword: String): ResponseEntity> { + fun searchPost( + @RequestParam keyword: String, + ): ResponseEntity> { val response = postSearchedUseCase.search(keyword = keyword) return ResponseEntity.ok(response) 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..cfb92669 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt @@ -0,0 +1,9 @@ +package com.wespot.common.dto + +data class PostPagingResponse( + val data: List, + 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..ad06382d --- /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, + val height: Int, +) { + + 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/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..8947cd68 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,7 +1,9 @@ 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, ) { 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..7fb431b5 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,15 +1,18 @@ package com.wespot.post.dto.response +import com.fasterxml.jackson.annotation.JsonInclude import com.wespot.comment.PostComment import com.wespot.post.PostProfile import java.time.LocalDateTime +@JsonInclude(JsonInclude.Include.NON_NULL) data class PostCommentResponse( + 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, ) { @@ -25,10 +28,11 @@ data class PostCommentResponse( ): PostCommentResponse { return PostCommentResponse( authorImage = postProfile.url, + isMe = isPostOwner, 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 ) 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..a3302caf --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt @@ -0,0 +1,167 @@ +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 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.map { image -> ImageContentV2Response.from(image) } + ) + } + } + + } + } + + 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, + 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..cd053ed8 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,12 +1,16 @@ 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, val url: String, + val width: Int, + val height: Int, val createdAt: LocalDateTime ) { @@ -17,6 +21,8 @@ data class PostImageResponse( id = postImage.id, postId = postImage.postId, url = postImage.url, + width = postImage.width, + height = postImage.height, createdAt = postImage.createdAt ) } 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/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..79c0519a --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt @@ -0,0 +1,43 @@ +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, + 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 + + fun findAllPosts( + countOfPostsViewed: Long, + 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..4153b104 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,11 @@ package com.wespot.post.port.`in` -import com.wespot.post.dto.response.PostResponse +import com.wespot.post.dto.response.PostComponentResponse interface PostSearchedUseCase { - fun search(keyword: String): List + fun search( + keyword: String, + ): List } 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..1dfc3475 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 @@ -12,20 +12,28 @@ interface PostPort { 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 + 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..56d4e9d7 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.findAllRecentPostByLimit(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/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/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..9961a6d5 --- /dev/null +++ b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt @@ -0,0 +1,253 @@ +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.MessageComponentResponse +import com.wespot.common.dto.view.VoteComponentResponse +import com.wespot.exception.CustomException +import com.wespot.post.Post +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.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 + ) + + return postPagingResponse(posts, inquirySize) + } + + private fun postPagingResponse( + posts: List, + inquirySize: Long + ): PostPagingResponse { + val data = posts.map { PostComponent.from(it) } + .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, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val categoryIds = postCategoryPort.findAllByMajorCategoryName(majorCategoryName) + .map { it.id } + + val posts = postPort.findAllByCategoryIdIn( + categoryIds = categoryIds, + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + cursorId = cursorId + ) + + return postPagingResponse(posts, inquirySize) + } + + @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) + 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) + } + + @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) + } + } + + @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) } + } + + @Transactional(readOnly = false) + override fun findAllPosts( + countOfPostsViewed: Long, + inquirySize: Long, + cursorId: Long?, + ): PostPagingResponse { + val loginUser = SecurityUtils.getLoginUser(userPort = userPort) + val posts = postPort.findAllRecentPostByLimit( + viewerId = loginUser.id, + inquirySize = inquirySize + 1, + 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) + val lastCursorId = posts.minOfOrNull { it.id } + val hasNext = posts.size == (inquirySize.toInt() + 1) + + return PostPagingResponse( + data = data, + lastCursorId = lastCursorId, + hasNext = hasNext + ) + } + + private fun mixPostAndNudge( + startSequence: Int, + endSequence: Int, + posts: List, + nudges: List, + ): MutableList { + val data = mutableListOf() + var nudgesIndex = 0 + + (startSequence..endSequence).asSequence().forEach { i -> + val post = posts[i - startSequence] + val component = PostComponent.from(post) + 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 + } + } + } + + +} 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/PostSearchedService.kt b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt index 148586fd..7a1ad2ac 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt @@ -1,8 +1,9 @@ package com.wespot.post.service -import com.wespot.post.dto.response.PostResponse +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 org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -13,16 +14,25 @@ class PostSearchedService( ) : PostSearchedUseCase { @Transactional(readOnly = true) - override fun search(keyword: String): List { + override fun search( + keyword: String, + ): List { 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 postsByTitle = + keywordsToSearch.map { postPort.searchByTitle(it) } + .flatten() + val postsByDescription = + keywordsToSearch.map { postPort.searchByDescription(it) } + .flatten() return (postsByTitle + postsByDescription) + .asSequence() .distinct() .sortedByDescending { it.createdAt } - .map { PostResponse.from(it) } + .distinct() + .map { PostComponent.from(it) } + .map { PostComponentResponse.from(it) } + .toList() } - } 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..ada94db5 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( diff --git a/domain/src/main/kotlin/com/wespot/post/Post.kt b/domain/src/main/kotlin/com/wespot/post/Post.kt index 404f01a0..c89cd113 100644 --- a/domain/src/main/kotlin/com/wespot/post/Post.kt +++ b/domain/src/main/kotlin/com/wespot/post/Post.kt @@ -9,7 +9,7 @@ import com.wespot.user.User import org.springframework.http.HttpStatus import java.time.LocalDateTime -class Post( +class Post( // TODO : AnonymousProfile 포함 val id: Long = 0L, val category: PostCategory, val user: User, 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..ddab52b3 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt @@ -0,0 +1,99 @@ +package com.wespot.post.nudge.server_driven + +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 = "", + width = 40, + height = 40 + ), // TODO : 익명 프로필 추가하면 이거 넣음 + nickname = RichTextV2(text = "익명의 글쓴이"), + ), + infoSection = HotPostInfoSection( + title = post.title?.let { RichTextV2(text = it.content) }, + description = RichTextV2( + text = post.description.content, + ), + ), + createdAt = RichTextV2( + text = post.createdAt.toString(), + ), + gradation = Gradation( + startColor = Color("#FFFFFF"), + endColor = Color("#FFFFFF"), + angle = 1 + ) + ) + } + + } + + 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..86c0ba32 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/MessageComponent.kt @@ -0,0 +1,40 @@ +package com.wespot.post.nudge.server_driven + +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://example.com/image.jpg", + width = 100, + height = 100 + ), + title = RichTextV2(text = "답장을 기다리는 쪽지가 있어요"), + description = RichTextV2(text = "눌러서 바로 확인하기"), + 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..4c118f48 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt @@ -0,0 +1,60 @@ +package com.wespot.post.nudge.server_driven + +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 = "VoteComponent", + 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("#FFFFFF"), + text = RichTextV2(text = "비밀 투표") + ), + text = RichTextV2(text = "지금 우리 반에서 가장\n${voteOption.content} 친구는?"), + actionIcon = VoteContent.VoteActionIcon( + backgroundColor = Color("#FFFFFF"), + icon = IconV2.MOVE_TO_VOTE_ICON + ), + gradation = Gradation( + startColor = Color("#FFFFFF"), + endColor = Color("#FFFFFF"), + angle = 1 + ) + ) + ) + } + + } + +} 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..80ef1100 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -0,0 +1,253 @@ +package com.wespot.post.server_driven + +import com.wespot.post.Post +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 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 = "더보기"), + val maxLine: Int = 3, + ) { + + } + + 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 { + + private const val AUTHOR_NAME = "익명의 글쓴이" + + fun from(post: Post): PostComponent { + return PostComponent( + id = post.id, + content = PostContent( + headerSection = PostContent.PostHeaderSection( + profileImage = ImageContentV2( + url = "", + width = 40, + height = 40, + ), // TODO : 익명 프로필 나오면 레츠고 + nickname = RichTextV2( + text = AUTHOR_NAME, + ), + createdAt = RichTextV2( + text = post.createdAt.toString(), + ), + category = PostCategoryComponent( + text = RichTextV2( + text = post.category.name, + ), + target = post.category.majorCategoryName, + icon = IconV2( + url = "", + ) + ) + ), + infoSection = PostContent.PostInfoSection( + title = post.title?.let { + RichTextV2( + text = post.title.content, + ) + }, + description = RichTextV2( + text = post.description.content, + ), + maxLine = 3, + ), + contentSection = post.images?.let { + PostContent.PostContentSection( + images = it.postImages.map { image -> + ImageContentV2( + url = image.url, + width = image.width, + height = image.height, + ) + } + ) + }, + footerSection = PostContent.PostFooterSection( + reactions = listOf( + PostContent.PostFooterSection.PostReactionItem( + type = "Chat", + icon = IconV2( + url = "" + ), + count = RichTextV2(text = post.commentCount.toString()), + selected = false, + ), + PostContent.PostFooterSection.PostReactionItem( + type = "Like", + icon = IconV2( + url = "" + ), + count = RichTextV2(text = post.likeCount.toString()), + selected = post.postStatusByViewer?.isViewerPushedLike ?: false, + ), + ), + scrap = PostContent.PostFooterSection.PostScrapComponent( + icon = IconV2( + url = "", + ), + selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, + ), + ) + ), + ) + } + + fun fromDetail( post: Post): PostComponent { + return PostComponent( + id = post.id, + content = PostContent( + category = PostCategoryComponent( + text = RichTextV2( + text = post.category.name, + ), + target = post.category.id.toString(), + icon = IconV2( + url = "", + ) + ), + headerSection = PostContent.PostHeaderSection( + profileImage = ImageContentV2( + url = "", + width = 40, + height = 40, + ), // TODO : 익명 프로필 나오면 레츠고 + nickname = RichTextV2( + text = AUTHOR_NAME, + ), + createdAt = RichTextV2( + text = post.createdAt.toString(), + ), + button = PostContent.PostButtonComponent( + icon = IconV2( + url = "", + ), + text = RichTextV2( + text = "댓글 알림", + ), + isSelected = post.postStatusByViewer?.isViewerPushedNotification ?: false, + ) + ), + infoSection = PostContent.PostInfoSection( + title = post.title?.let { + RichTextV2( + text = post.title.content, + ) + }, + description = RichTextV2( + text = post.description.content, + ), + maxLine = 3, + ), + contentSection = post.images?.let { + PostContent.PostContentSection( + images = it.postImages.map { image -> + ImageContentV2( + url = image.url, + width = image.width, + height = image.height, + ) + } + ) + }, + footerSection = PostContent.PostFooterSection( + reactions = listOf( + PostContent.PostFooterSection.PostReactionItem( + type = "Chat", + icon = IconV2( + url = "" + ), + count = RichTextV2(text = post.commentCount.toString()), + selected = false, + ), + PostContent.PostFooterSection.PostReactionItem( + type = "Like", + icon = IconV2( + url = "" + ), + count = RichTextV2(text = post.likeCount.toString()), + selected = post.postStatusByViewer?.isViewerPushedLike ?: false, + ), + ), + scrap = PostContent.PostFooterSection.PostScrapComponent( + icon = IconV2( + url = "", + ), + selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, + ), + ) + ), + ) + } + + } + +} 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..73ce53db --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt @@ -0,0 +1,50 @@ +package com.wespot.view.chip + +import com.wespot.post.PostCategories +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, + val text: RichTextV2, + val target: String, + ) { + } + + companion object { + + fun of( + id: Long, + text: String, + ): FilterChip { + return FilterChip( + content = FilterChipContent( + id = id, + icon = IconV2(url = ""), + text = RichTextV2(text = text), + target = text + ) + ) + } + + fun of( + id: Long, + eachMajorCategory: PostCategories.EachMajorCategory + ): FilterChip { + return FilterChip( + content = FilterChipContent( + id = id, + icon = IconV2(url = ""), + text = RichTextV2(text = eachMajorCategory.majorCategoryName()), + 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..a3140ec5 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/color/Color.kt @@ -0,0 +1,12 @@ +package com.wespot.view.color + +data class Color( + val value: String, + val type: String = "Hex", +) { + + companion object { + val DEFAULT_COLOR = Color(value = "#FFFFFF") + } + +} 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..00965a5c --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt @@ -0,0 +1,27 @@ +package com.wespot.view.icon + +import com.wespot.view.color.Color + +data class IconV2( + val url: String, + val color: Color = Color.DEFAULT_COLOR, +) { + companion object { + + val HOT_POST_ICON: IconV2 = IconV2( + url = "https://cdn.wespot.app/icons/hot_post.svg", + color = Color.DEFAULT_COLOR + ) + + val MOVE_TO_VOTE_ICON: IconV2 = IconV2( + url = "https://cdn.wespot.app/icons/move_to_vote.svg", + color = Color.DEFAULT_COLOR + ) + + val MOVE_TO_MESSAGE_ICON: IconV2 = IconV2( + url = "https://cdn.wespot.app/icons/move_to_message.svg", + color = Color.DEFAULT_COLOR + ) + + } +} 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..e514766f --- /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, + val height: Int, +) { +} 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..72284e01 --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt @@ -0,0 +1,19 @@ +package com.wespot.view.text + +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 = "Hot Post", + maxLine = 1 + ) + } + +} 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/adapter/PostAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt index 0fab08b8..f4eadbbf 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 @@ -45,7 +45,9 @@ class PostAdapter( } override fun searchByTitle(title: String, viewerId: Long?): List { - val posts = postJpaRepository.findAllByTitleContaining(title = title) + val posts = postJpaRepository.findByTitleContainingOrderByBaseEntityCreatedAtDesc( + title = title, + ) return getCompletePost(posts, viewerId) } @@ -104,8 +106,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 +123,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 findAllRecentPostByLimit( + 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/repository/PostJpaRepository.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/repository/PostJpaRepository.kt index 0f5de975..aa2b5a70 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 @@ -6,18 +6,42 @@ import org.springframework.data.jpa.repository.JpaRepository interface PostJpaRepository : JpaRepository { - fun findAllByTitleContaining(title: String): List - - fun findAllByDescriptionContaining(description: String): List - - fun findByCategoryId(categoryId: Long): List - - fun findByCategoryIdIn(categoryIds: List): List - - fun findAllByUserId(authorId: Long): List - - fun findAllByIdIn(postIds: List): List - - fun findAllByOrderByBaseEntityCreatedAtDesc(pageable: Pageable): List + fun findByTitleContainingOrderByBaseEntityCreatedAtDesc( + title: String, + ): List + + fun findAllByDescriptionContainingOrderByBaseEntityCreatedAtDesc( + description: String, + ): List + + fun findByCategoryIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryId: Long, + cursorId: Long, + pageable: Pageable + + ): List + + fun findByCategoryIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + categoryIds: List, + cursorId: Long, + pageable: Pageable + ): List + + fun findAllByUserIdAndIdLessThanOrderByBaseEntityCreatedAtDesc( + userId: Long, + cursorId: Long, + pageable: Pageable + ): List + + fun findAllByIdInAndIdLessThanOrderByBaseEntityCreatedAtDesc( + postIds: List, + cursorId: Long, + pageable: Pageable + ): List + + fun findAllByIdLessThanOrderByBaseEntityCreatedAtDesc( + cursorId: Long, + pageable: Pageable + ): List } From e68cc65fc1ec7f6d4b72f48fbe4ad59f229687ac Mon Sep 17 00:00:00 2001 From: kpeel Date: Mon, 28 Jul 2025 22:27:55 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=EC=83=89=EC=83=81=20=EB=B0=8F=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EA=B4=80=EB=A0=A8=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B0=8F=20?= =?UTF-8?q?=EC=97=B4=EA=B1=B0=ED=98=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wespot/post/PostInquiryController.kt | 4 +- .../post/dto/response/PostCategoryResponse.kt | 2 +- .../wespot/post/port/in/PostInquiryUseCase.kt | 11 +- .../com/wespot/post/port/out/PostPort.kt | 2 +- .../post/service/HotPostInquiryService.kt | 2 +- .../wespot/post/service/PostInquiryService.kt | 219 +++++++++++------- .../kotlin/com/wespot/common/EverProfile.kt | 88 +++++++ .../com/wespot/common/view/ColorType.kt | 31 +++ .../com/wespot/common/view/TypographType.kt | 23 ++ .../src/main/kotlin/com/wespot/post/Post.kt | 7 +- .../kotlin/com/wespot/post/PostCategory.kt | 5 + .../kotlin/com/wespot/post/PostProfile.kt | 18 +- .../nudge/server_driven/HotPostComponent.kt | 39 +++- .../nudge/server_driven/MessageComponent.kt | 23 +- .../post/nudge/server_driven/VoteComponent.kt | 26 ++- .../kotlin/com/wespot/view/chip/FilterChip.kt | 23 +- .../kotlin/com/wespot/view/color/Color.kt | 8 +- .../kotlin/com/wespot/view/icon/IconV2.kt | 11 +- .../kotlin/com/wespot/view/text/RichTextV2.kt | 6 +- .../com/wespot/post/adapter/PostAdapter.kt | 17 +- .../com/wespot/post/mapper/PostMapper.kt | 6 +- .../repository/PostProfileJpaRepository.kt | 1 + 22 files changed, 427 insertions(+), 145 deletions(-) create mode 100644 domain/src/main/kotlin/com/wespot/common/EverProfile.kt create mode 100644 domain/src/main/kotlin/com/wespot/common/view/ColorType.kt create mode 100644 domain/src/main/kotlin/com/wespot/common/view/TypographType.kt diff --git a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index ea129615..c89b13e0 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -45,11 +45,13 @@ class PostInquiryController( @GetMapping 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=majorCategoryName, + countOfPostsViewed=countOfPostsViewed, inquirySize = inquirySize, cursorId = cursorId ) 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 8947cd68..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 @@ -10,7 +10,7 @@ data class PostCategoryResponse( 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/port/in/PostInquiryUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt index 79c0519a..4b5073ad 100644 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt @@ -13,6 +13,7 @@ interface PostInquiryUseCase { fun findPostsByMajorCategoryName( majorCategoryName: String, + countOfPostsViewed: Long, inquirySize: Long, cursorId: Long?, ): PostPagingResponse @@ -34,10 +35,10 @@ interface PostInquiryUseCase { cursorId: Long?, ): PostPagingResponse - fun findAllPosts( - countOfPostsViewed: Long, - inquirySize: Long, - cursorId: Long? - ): PostPagingResponse +// fun findAllPosts( +// countOfPostsViewed: Long, +// 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 1dfc3475..5a6ad5f5 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 @@ -30,7 +30,7 @@ interface PostPort { cursorId: Long? ): List - fun findAllRecentPostByLimit( + fun findAllRecentPost( viewerId: Long? = null, inquirySize: Long, cursorId: 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 56d4e9d7..f82f0a89 100644 --- a/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/HotPostInquiryService.kt @@ -16,7 +16,7 @@ class HotPostInquiryService( override fun topPost(user: User, countOfView: Int): List { val countOfPostToStatistic = 100L val posts = - postPort.findAllRecentPostByLimit(inquirySize = countOfPostToStatistic, viewerId = user.id, cursorId = null) + postPort.findAllRecentPost(inquirySize = countOfPostToStatistic, viewerId = user.id, cursorId = null) return posts .sortedByDescending { it.createdAt } .sortedByDescending { it.scoreOfPost() } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt index 9961a6d5..417c6316 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt @@ -8,6 +8,7 @@ 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 @@ -22,6 +23,7 @@ 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 @@ -75,101 +77,15 @@ class PostInquiryService( @Transactional(readOnly = false) override fun findPostsByMajorCategoryName( majorCategoryName: String, - inquirySize: Long, - cursorId: Long?, - ): PostPagingResponse { - val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val categoryIds = postCategoryPort.findAllByMajorCategoryName(majorCategoryName) - .map { it.id } - - val posts = postPort.findAllByCategoryIdIn( - categoryIds = categoryIds, - viewerId = loginUser.id, - inquirySize = inquirySize + 1, - cursorId = cursorId - ) - - return postPagingResponse(posts, inquirySize) - } - - @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) - 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) - } - - @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) - } - } - - @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) } - } - - @Transactional(readOnly = false) - override fun findAllPosts( countOfPostsViewed: Long, inquirySize: Long, cursorId: Long?, ): PostPagingResponse { val loginUser = SecurityUtils.getLoginUser(userPort = userPort) - val posts = postPort.findAllRecentPostByLimit( - viewerId = loginUser.id, - inquirySize = inquirySize + 1, + val posts = findPostsByCategoryName( + majorCategoryName = majorCategoryName, + loginUser = loginUser, + inquirySize = inquirySize, cursorId = cursorId ) @@ -249,5 +165,128 @@ class PostInquiryService( } } + // @Transactional(readOnly = false) +// override fun findAllPosts( +// countOfPostsViewed: Long, +// inquirySize: Long, +// cursorId: Long?, +// ): PostPagingResponse { +// val loginUser = SecurityUtils.getLoginUser(userPort = userPort) +// val posts = postPort.findAllRecentPost( +// viewerId = loginUser.id, +// inquirySize = inquirySize + 1, +// 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) +// 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 + ) + } + + @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) + 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) + } + + @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) + } + } + + @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) } + } + +// } + } 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/view/ColorType.kt b/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt new file mode 100644 index 00000000..5577db8b --- /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"), + ; + + +} 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..eeba924c --- /dev/null +++ b/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt @@ -0,0 +1,23 @@ +package com.wespot.common.view + +enum class TypographType( + val value: String +) { + + 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/post/Post.kt b/domain/src/main/kotlin/com/wespot/post/Post.kt index c89cd113..41ee5207 100644 --- a/domain/src/main/kotlin/com/wespot/post/Post.kt +++ b/domain/src/main/kotlin/com/wespot/post/Post.kt @@ -9,7 +9,7 @@ import com.wespot.user.User import org.springframework.http.HttpStatus import java.time.LocalDateTime -class Post( // TODO : AnonymousProfile 포함 +class Post( val id: Long = 0L, val category: PostCategory, val user: User, @@ -19,6 +19,7 @@ class Post( // TODO : AnonymousProfile 포함 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( // TODO : AnonymousProfile 포함 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( // TODO : AnonymousProfile 포함 commentCount = commentCount, bookmarkedCount = bookmarkedCount, images = images, - createdAt = createdAt + createdAt = createdAt, + profile = profile, ) } diff --git a/domain/src/main/kotlin/com/wespot/post/PostCategory.kt b/domain/src/main/kotlin/com/wespot/post/PostCategory.kt index 7e4ab39f..baf02abb 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostCategory.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostCategory.kt @@ -8,4 +8,9 @@ class PostCategory( val name: String, val createdAt: LocalDateTime ) { + + companion object { + const val ALL_INCLUDE_CATEGORY_NAME = "전체" + } + } 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/nudge/server_driven/HotPostComponent.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt index ddab52b3..ae304e0f 100644 --- 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 @@ -1,5 +1,7 @@ package com.wespot.post.nudge.server_driven +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 @@ -38,25 +40,44 @@ class HotPostComponent( return HotPost( headerSection = HotPostHeaderSection( profileImage = ImageContentV2( - url = "", - width = 40, - height = 40 - ), // TODO : 익명 프로필 추가하면 이거 넣음 - nickname = RichTextV2(text = "익명의 글쓴이"), + 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) }, + 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 = post.createdAt.toString(), + color = Color(value = ColorType.GRAY500.value), + typography = TypographType.BODY12.value, + maxLine = 1 ), gradation = Gradation( - startColor = Color("#FFFFFF"), - endColor = Color("#FFFFFF"), - angle = 1 + startColor = Color(value = "#F6D1FF", type = Color.HEX), + endColor = Color(value = "#A7A7FF", type = Color.HEX), + angle = 90 ) ) } 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 index 86c0ba32..0feb9222 100644 --- 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 @@ -1,5 +1,8 @@ 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 @@ -25,12 +28,22 @@ data class MessageComponent( id = id, content = MessageContentComponent( thumbnail = ImageContentV2( - url = "https://example.com/image.jpg", - width = 100, - height = 100 + 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 ), - title = RichTextV2(text = "답장을 기다리는 쪽지가 있어요"), - description = RichTextV2(text = "눌러서 바로 확인하기"), 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 index 4c118f48..4e24ed3f 100644 --- 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 @@ -1,5 +1,7 @@ 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 @@ -38,18 +40,28 @@ data class VoteComponent( id = id, content = VoteContent( badge = VoteContent.VoteBadge( - backgroundColor = Color("#FFFFFF"), - text = RichTextV2(text = "비밀 투표") + 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 ), - text = RichTextV2(text = "지금 우리 반에서 가장\n${voteOption.content} 친구는?"), actionIcon = VoteContent.VoteActionIcon( - backgroundColor = Color("#FFFFFF"), + backgroundColor = Color(value = ColorType.GRAY900.value), icon = IconV2.MOVE_TO_VOTE_ICON ), gradation = Gradation( - startColor = Color("#FFFFFF"), - endColor = Color("#FFFFFF"), - angle = 1 + 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/view/chip/FilterChip.kt b/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt index 73ce53db..870b6c3f 100644 --- a/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt +++ b/domain/src/main/kotlin/com/wespot/view/chip/FilterChip.kt @@ -1,6 +1,9 @@ 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 @@ -11,7 +14,7 @@ data class FilterChip( data class FilterChipContent( val id: Long, - val icon: IconV2, + val icon: IconV2 = IconV2(url = "", color = Color()), val text: RichTextV2, val target: String, ) { @@ -26,8 +29,13 @@ data class FilterChip( return FilterChip( content = FilterChipContent( id = id, - icon = IconV2(url = ""), - text = RichTextV2(text = text), + icon = IconV2(url = "", color = Color()), + text = RichTextV2( + text = text, + color = Color(value = ColorType.GRAY600.value), + typography = TypographType.BODY06.value, + maxLine = 1 + ), target = text ) ) @@ -40,8 +48,13 @@ data class FilterChip( return FilterChip( content = FilterChipContent( id = id, - icon = IconV2(url = ""), - text = RichTextV2(text = eachMajorCategory.majorCategoryName()), + 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 index a3140ec5..dfce4abf 100644 --- a/domain/src/main/kotlin/com/wespot/view/color/Color.kt +++ b/domain/src/main/kotlin/com/wespot/view/color/Color.kt @@ -1,11 +1,15 @@ package com.wespot.view.color data class Color( - val value: String, - val type: String = "Hex", + val value: String = "GRAY600", + val type: String = "Token", ) { companion object { + + const val TOKEN = "Token" + const val HEX = "Hex" + val DEFAULT_COLOR = Color(value = "#FFFFFF") } diff --git a/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt index 00965a5c..3c9b80ed 100644 --- a/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt +++ b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt @@ -1,5 +1,6 @@ package com.wespot.view.icon +import com.wespot.common.view.ColorType import com.wespot.view.color.Color data class IconV2( @@ -9,18 +10,18 @@ data class IconV2( companion object { val HOT_POST_ICON: IconV2 = IconV2( - url = "https://cdn.wespot.app/icons/hot_post.svg", + url = "https://dw2d2daekmyur.cloudfront.net/hot_post.png", color = Color.DEFAULT_COLOR ) val MOVE_TO_VOTE_ICON: IconV2 = IconV2( - url = "https://cdn.wespot.app/icons/move_to_vote.svg", - color = Color.DEFAULT_COLOR + url = "https://dw2d2daekmyur.cloudfront.net/move_to_message.png", + color = Color(value = ColorType.WHITE.value) ) val MOVE_TO_MESSAGE_ICON: IconV2 = IconV2( - url = "https://cdn.wespot.app/icons/move_to_message.svg", - color = Color.DEFAULT_COLOR + url = "https://dw2d2daekmyur.cloudfront.net/move_to_message.png", + color = Color(value = ColorType.WHITE.value) ) } diff --git a/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt b/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt index 72284e01..ec6e6f72 100644 --- a/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt +++ b/domain/src/main/kotlin/com/wespot/view/text/RichTextV2.kt @@ -1,5 +1,7 @@ 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( @@ -11,7 +13,9 @@ data class RichTextV2( companion object { val HOT_POST_TEXT: RichTextV2 = RichTextV2( - text = "Hot Post", + text = "실시간 인기글", + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY02.value, maxLine = 1 ) } 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 f4eadbbf..4420022f 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 @@ -1,6 +1,7 @@ package com.wespot.post.adapter import com.wespot.comment.port.out.PostValidatePort +import com.wespot.exception.CustomException import com.wespot.post.Post import com.wespot.post.PostEntity import com.wespot.post.PostImages @@ -8,6 +9,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,6 +27,7 @@ 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 { @@ -35,12 +38,16 @@ class PostAdapter( ?.let { postImageJpaRepository.saveAll(it) } ?.map { PostImageMapper.toDomain(it) } ?.let { PostImages(it) } + val postProfile = postProfileJpaRepository.findByIdOrNull(post.user.id) ?: throw CustomException( + message = "Post profile not found for user id: ${post.user.id}", + ) return PostMapper.toDomain( entity = savedPostEntity, postCategory = post.category, user = post.user, - postImages = savedPostImages + postImages = savedPostImages, + postProfile = PostProfileMapper.toDomain(postProfile), ) } @@ -69,6 +76,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) @@ -78,7 +88,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]!! ) } } @@ -176,7 +187,7 @@ class PostAdapter( return getCompletePost(posts, viewerId) } - override fun findAllRecentPostByLimit( + override fun findAllRecentPost( viewerId: Long?, inquirySize: Long, cursorId: Long?, 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/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 } From aac6791af9aed264f33ecfb3552a0351737d8bb7 Mon Sep 17 00:00:00 2001 From: kpeel Date: Mon, 28 Jul 2025 22:28:33 +0900 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EB=90=9C=20findAllPosts=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wespot/post/PostInquiryController.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index c89b13e0..99280231 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -12,20 +12,20 @@ class PostInquiryController( private val postInquiryByCategoryUseCase: PostInquiryUseCase ) { - @GetMapping("/all") - fun findAllPosts( - @RequestParam countOfPostsViewed: Long = 0L, - @RequestParam(required = false, defaultValue = "10") inquirySize: Long, - @RequestParam(required = false) cursorId: Long? = null, - ): ResponseEntity { - val responses = postInquiryByCategoryUseCase.findAllPosts( - countOfPostsViewed, - inquirySize, - cursorId - ) - - return ResponseEntity.ok(responses) - } +// @GetMapping("/all") +// fun findAllPosts( +// @RequestParam countOfPostsViewed: Long = 0L, +// @RequestParam(required = false, defaultValue = "10") inquirySize: Long, +// @RequestParam(required = false) cursorId: Long? = null, +// ): ResponseEntity { +// val responses = postInquiryByCategoryUseCase.findAllPosts( +// countOfPostsViewed, +// inquirySize, +// cursorId +// ) +// +// return ResponseEntity.ok(responses) +// } @GetMapping("/details") fun findPostsByCategoryId( From 401195a1a47716a35364408b501dfa9591193799 Mon Sep 17 00:00:00 2001 From: kpeel Date: Mon, 28 Jul 2025 22:29:06 +0900 Subject: [PATCH 04/14] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/kotlin/com/wespot/post/PostInquiryController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index 99280231..6ef81c7c 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -50,8 +50,8 @@ class PostInquiryController( @RequestParam(required = false) cursorId: Long? = null, ): ResponseEntity { val responses = postInquiryByCategoryUseCase.findPostsByMajorCategoryName( - majorCategoryName=majorCategoryName, - countOfPostsViewed=countOfPostsViewed, + majorCategoryName = majorCategoryName, + countOfPostsViewed = countOfPostsViewed, inquirySize = inquirySize, cursorId = cursorId ) From 4e6e18cf5feccceb8d5ed67acf5283e3c88c92b0 Mon Sep 17 00:00:00 2001 From: kpeel Date: Mon, 28 Jul 2025 22:33:02 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wespot/post/PostInquiryController.kt | 15 ---- .../wespot/post/port/in/PostInquiryUseCase.kt | 6 -- .../wespot/post/service/PostInquiryService.kt | 77 ++++++------------- 3 files changed, 24 insertions(+), 74 deletions(-) diff --git a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt index 6ef81c7c..a5fc56fe 100644 --- a/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostInquiryController.kt @@ -12,21 +12,6 @@ class PostInquiryController( private val postInquiryByCategoryUseCase: PostInquiryUseCase ) { -// @GetMapping("/all") -// fun findAllPosts( -// @RequestParam countOfPostsViewed: Long = 0L, -// @RequestParam(required = false, defaultValue = "10") inquirySize: Long, -// @RequestParam(required = false) cursorId: Long? = null, -// ): ResponseEntity { -// val responses = postInquiryByCategoryUseCase.findAllPosts( -// countOfPostsViewed, -// inquirySize, -// cursorId -// ) -// -// return ResponseEntity.ok(responses) -// } - @GetMapping("/details") fun findPostsByCategoryId( @RequestParam categoryId: Long, 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 index 4b5073ad..368d35b1 100644 --- a/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt +++ b/core/src/main/kotlin/com/wespot/post/port/in/PostInquiryUseCase.kt @@ -35,10 +35,4 @@ interface PostInquiryUseCase { cursorId: Long?, ): PostPagingResponse -// fun findAllPosts( -// countOfPostsViewed: Long, -// inquirySize: Long, -// cursorId: Long? -// ): PostPagingResponse - } diff --git a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt index 417c6316..07f81d17 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt @@ -107,6 +107,30 @@ class PostInquiryService( ) } + 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, @@ -165,59 +189,6 @@ class PostInquiryService( } } - // @Transactional(readOnly = false) -// override fun findAllPosts( -// countOfPostsViewed: Long, -// inquirySize: Long, -// cursorId: Long?, -// ): PostPagingResponse { -// val loginUser = SecurityUtils.getLoginUser(userPort = userPort) -// val posts = postPort.findAllRecentPost( -// viewerId = loginUser.id, -// inquirySize = inquirySize + 1, -// 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) -// 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 - ) - } - @Transactional(readOnly = false) override fun findPostById( postId: Long, From b0ea4a57961f57c8064da38f416ade9300303472 Mon Sep 17 00:00:00 2001 From: kpeel Date: Tue, 29 Jul 2025 21:26:30 +0900 Subject: [PATCH 06/14] feat: make width and height in ImageContentV2 optional; update PostComponent for dynamic profile and post data --- .../com/wespot/common/view/TypographType.kt | 1 + .../post/server_driven/PostComponent.kt | 123 +++++++++++++----- .../com/wespot/view/image/ImageContentV2.kt | 4 +- 3 files changed, 91 insertions(+), 37 deletions(-) diff --git a/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt b/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt index eeba924c..2e5e37a4 100644 --- a/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt +++ b/domain/src/main/kotlin/com/wespot/common/view/TypographType.kt @@ -4,6 +4,7 @@ enum class TypographType( val value: String ) { + BODY00("Body00"), BODY01("Body01"), BODY02("Body02"), BODY03("Body03"), 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 index 80ef1100..c14c7a54 100644 --- a/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -1,9 +1,14 @@ package com.wespot.post.server_driven +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 +import kotlin.math.max +import kotlin.reflect.typeOf data class PostComponent( val id: Long, @@ -33,8 +38,13 @@ data class PostComponent( data class PostInfoSection( val title: RichTextV2? = null, val description: RichTextV2, - val seeMore: RichTextV2 = RichTextV2(text = "더보기"), - val maxLine: Int = 3, + val seeMore: RichTextV2 = RichTextV2( + text = "더보기", + color = Color(value = ColorType.PRIMARY300.value), + typography = TypographType.BODY06.value, + maxLine = 1 + ), + val maxLine: Int = 5, ) { } @@ -81,31 +91,38 @@ data class PostComponent( companion object { - private const val AUTHOR_NAME = "익명의 글쓴이" - fun from(post: Post): PostComponent { return PostComponent( id = post.id, content = PostContent( headerSection = PostContent.PostHeaderSection( profileImage = ImageContentV2( - url = "", - width = 40, - height = 40, - ), // TODO : 익명 프로필 나오면 레츠고 + url = post.profile.url, + width = 36, + height = 36, + ), nickname = RichTextV2( - text = AUTHOR_NAME, + text = post.profile.name, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY06.value, + maxLine = 1, ), createdAt = RichTextV2( text = post.createdAt.toString(), + color = Color(value = ColorType.GRAY400.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), category = PostCategoryComponent( text = RichTextV2( text = post.category.name, + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), target = post.category.majorCategoryName, icon = IconV2( - url = "", + url = "https://dw2d2daekmyur.cloudfront.net/right_arrow.png", ) ) ), @@ -113,21 +130,22 @@ data class PostComponent( 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, ), - maxLine = 3, ), contentSection = post.images?.let { PostContent.PostContentSection( images = it.postImages.map { image -> - ImageContentV2( - url = image.url, - width = image.width, - height = image.height, - ) + ImageContentV2(url = image.url) } ) }, @@ -136,23 +154,33 @@ data class PostComponent( PostContent.PostFooterSection.PostReactionItem( type = "Chat", icon = IconV2( - url = "" + url = "https://dw2d2daekmyur.cloudfront.net/TALK_BALLON.png" + ), + count = RichTextV2( + text = post.commentCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), - count = RichTextV2(text = post.commentCount.toString()), selected = false, ), PostContent.PostFooterSection.PostReactionItem( type = "Like", icon = IconV2( - url = "" + url = "https://dw2d2daekmyur.cloudfront.net/THUMBS_UP.png", + ), + count = RichTextV2( + text = post.likeCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), - count = RichTextV2(text = post.likeCount.toString()), selected = post.postStatusByViewer?.isViewerPushedLike ?: false, ), ), scrap = PostContent.PostFooterSection.PostScrapComponent( icon = IconV2( - url = "", + url = "https://dw2d2daekmyur.cloudfront.net/BOOKMARK.png", ), selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, ), @@ -161,37 +189,49 @@ data class PostComponent( ) } - fun fromDetail( post: Post): PostComponent { + fun fromDetail(post: Post): PostComponent { return PostComponent( id = post.id, 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( - url = "", + url = "https://dw2d2daekmyur.cloudfront.net/right_arrow_in_black.png", ) ), headerSection = PostContent.PostHeaderSection( profileImage = ImageContentV2( - url = "", + url = post.profile.url, width = 40, height = 40, - ), // TODO : 익명 프로필 나오면 레츠고 + ), nickname = RichTextV2( - text = AUTHOR_NAME, + text = post.profile.name, + color = Color(value = ColorType.WHITE.value), + typography = TypographType.BODY06.value, + maxLine = 1 ), createdAt = RichTextV2( text = post.createdAt.toString(), + color = Color(value = ColorType.GRAY400.value), + typography = TypographType.BODY09.value, + maxLine = 1 ), button = PostContent.PostButtonComponent( icon = IconV2( - url = "", + url = "https://dw2d2daekmyur.cloudfront.net/comment_notification_check.png", ), text = RichTextV2( text = "댓글 알림", + color = Color(value = ColorType.GRAY100.value), + typography = TypographType.BODY09.value, + maxLine = 1 ), isSelected = post.postStatusByViewer?.isViewerPushedNotification ?: false, ) @@ -200,20 +240,23 @@ data class PostComponent( 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, ), - maxLine = 3, ), contentSection = post.images?.let { PostContent.PostContentSection( images = it.postImages.map { image -> ImageContentV2( url = image.url, - width = image.width, - height = image.height, ) } ) @@ -223,23 +266,33 @@ data class PostComponent( PostContent.PostFooterSection.PostReactionItem( type = "Chat", icon = IconV2( - url = "" + url = "https://dw2d2daekmyur.cloudfront.net/TALK_BALLON.png" + ), + count = RichTextV2( + text = post.commentCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), - count = RichTextV2(text = post.commentCount.toString()), selected = false, ), PostContent.PostFooterSection.PostReactionItem( type = "Like", icon = IconV2( - url = "" + url = "https://dw2d2daekmyur.cloudfront.net/THUMBS_UP.png", + ), + count = RichTextV2( + text = post.likeCount.toString(), + color = Color(value = ColorType.GRAY300.value), + typography = TypographType.BADGE.value, + maxLine = 1, ), - count = RichTextV2(text = post.likeCount.toString()), selected = post.postStatusByViewer?.isViewerPushedLike ?: false, ), ), scrap = PostContent.PostFooterSection.PostScrapComponent( icon = IconV2( - url = "", + url = "https://dw2d2daekmyur.cloudfront.net/BOOKMARK.png", ), selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, ), diff --git a/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt b/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt index e514766f..f4901b92 100644 --- a/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt +++ b/domain/src/main/kotlin/com/wespot/view/image/ImageContentV2.kt @@ -2,7 +2,7 @@ package com.wespot.view.image data class ImageContentV2( val url: String, - val width: Int, - val height: Int, + val width: Int? = null, + val height: Int? = null, ) { } From b8e650625a6722c0fc066fefe46ee1dacd6b2446 Mon Sep 17 00:00:00 2001 From: kpeel Date: Tue, 29 Jul 2025 21:27:22 +0900 Subject: [PATCH 07/14] feat: make width and height in ImageContentV2Response optional --- .../com/wespot/common/dto/view/ImageContentV2Response.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index ad06382d..473d4c03 100644 --- a/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt +++ b/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt @@ -4,8 +4,8 @@ import com.wespot.view.image.ImageContentV2 data class ImageContentV2Response( val url: String, - val width: Int, - val height: Int, + val width: Int?, + val height: Int?, ) { companion object { From 3faaa5e15879568f085e98e0a1074fb16877c686 Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 01:20:10 +0900 Subject: [PATCH 08/14] feat: format createdAt fields using TimeExpressionUtil in Post and Comment responses --- .../post/dto/response/PostCommentResponse.kt | 6 ++-- .../com/wespot/common/TimeExpressionUtil.kt | 30 +++++++++++++++++++ .../nudge/server_driven/HotPostComponent.kt | 3 +- .../post/server_driven/PostComponent.kt | 7 ++--- .../com/wespot/post/adapter/PostAdapter.kt | 5 +--- 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 domain/src/main/kotlin/com/wespot/common/TimeExpressionUtil.kt 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 7fb431b5..7f44e3cc 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 @@ -2,8 +2,8 @@ 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( @@ -14,7 +14,7 @@ data class PostCommentResponse( val likeCount: Long, val hasPushedLike: Boolean, val isReported: Boolean, - val createdAt: LocalDateTime, + val createdAt: String, ) { companion object { @@ -34,7 +34,7 @@ data class PostCommentResponse( likeCount = postComment.likeCount, hasPushedLike = postComment.postCommentStatusByViewer?.isViewerPushedLike ?: false, isReported = postComment.reportCount > 0, - createdAt = postComment.createdAt + createdAt = TimeExpressionUtil.commentTime(createdAt = postComment.createdAt) ) } 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/post/nudge/server_driven/HotPostComponent.kt b/domain/src/main/kotlin/com/wespot/post/nudge/server_driven/HotPostComponent.kt index ae304e0f..fb420a1a 100644 --- 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 @@ -1,5 +1,6 @@ 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 @@ -69,7 +70,7 @@ class HotPostComponent( ), ), createdAt = RichTextV2( - text = post.createdAt.toString(), + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), color = Color(value = ColorType.GRAY500.value), typography = TypographType.BODY12.value, maxLine = 1 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 index c14c7a54..9fc1c29c 100644 --- a/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -1,5 +1,6 @@ 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 @@ -7,8 +8,6 @@ import com.wespot.view.color.Color import com.wespot.view.icon.IconV2 import com.wespot.view.image.ImageContentV2 import com.wespot.view.text.RichTextV2 -import kotlin.math.max -import kotlin.reflect.typeOf data class PostComponent( val id: Long, @@ -108,7 +107,7 @@ data class PostComponent( maxLine = 1, ), createdAt = RichTextV2( - text = post.createdAt.toString(), + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), color = Color(value = ColorType.GRAY400.value), typography = TypographType.BADGE.value, maxLine = 1, @@ -218,7 +217,7 @@ data class PostComponent( maxLine = 1 ), createdAt = RichTextV2( - text = post.createdAt.toString(), + text = TimeExpressionUtil.postTime(createdAt = post.createdAt), color = Color(value = ColorType.GRAY400.value), typography = TypographType.BODY09.value, maxLine = 1 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 4420022f..0d001b18 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 @@ -38,16 +38,13 @@ class PostAdapter( ?.let { postImageJpaRepository.saveAll(it) } ?.map { PostImageMapper.toDomain(it) } ?.let { PostImages(it) } - val postProfile = postProfileJpaRepository.findByIdOrNull(post.user.id) ?: throw CustomException( - message = "Post profile not found for user id: ${post.user.id}", - ) return PostMapper.toDomain( entity = savedPostEntity, postCategory = post.category, user = post.user, postImages = savedPostImages, - postProfile = PostProfileMapper.toDomain(postProfile), + postProfile = post.profile, ) } From 1a88e56b5b9b94583671832a66a63cc839a20886 Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 01:22:29 +0900 Subject: [PATCH 09/14] feat: make width and height optional in image requests and responses --- .../com/wespot/post/dto/request/CreatedPostRequest.kt | 4 +--- .../com/wespot/post/dto/response/PostImageResponse.kt | 4 ---- .../com/wespot/post/service/PostCreatedService.kt | 2 -- .../kotlin/com/wespot/post/service/PostEditService.kt | 2 +- domain/src/main/kotlin/com/wespot/post/PostImage.kt | 10 +--------- 5 files changed, 3 insertions(+), 19 deletions(-) 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..7a6e7d8d 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 @@ -8,9 +8,7 @@ data class CreatedPostRequest( ) { data class CreatedPostImageRequest( - val url: String, - val width: Int, - val height: Int + val url: String ) { } 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 cd053ed8..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 @@ -9,8 +9,6 @@ data class PostImageResponse( val id: Long, val postId: Long, val url: String, - val width: Int, - val height: Int, val createdAt: LocalDateTime ) { @@ -21,8 +19,6 @@ data class PostImageResponse( id = postImage.id, postId = postImage.postId, url = postImage.url, - width = postImage.width, - height = postImage.height, createdAt = postImage.createdAt ) } 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..a05eba8f 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostCreatedService.kt @@ -44,8 +44,6 @@ class PostCreatedService( PostImage.of( cloudFrontUrl = cloudFrontUrl, imageUrl = it.url, - width = it.width, - height = it.height ) }, 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..ff4274b7 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.url) }, toUpdatePost = { updatedPost -> postPort.save(updatedPost) } ).id diff --git a/domain/src/main/kotlin/com/wespot/post/PostImage.kt b/domain/src/main/kotlin/com/wespot/post/PostImage.kt index 11e52984..2c5df471 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostImage.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostImage.kt @@ -9,8 +9,6 @@ class PostImage( val id: Long, val postId: Long, val url: String, - val width: Int = 0, - val height: Int = 0, val createdAt: LocalDateTime ) { @@ -28,13 +26,11 @@ 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() ) } @@ -51,16 +47,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 ) } From b12aa90a3f639f8cc52d2d82eb295bd2c9d19c4a Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 23:06:41 +0900 Subject: [PATCH 10/14] feat: add keywordsToSearchInRegex method to PostSearchKeyword for regex keyword generation --- .../wespot/post/vo/PostSearchKeywordTest.kt | 35 +++++++++++++++++++ .../com/wespot/post/vo/PostSearchKeyword.kt | 10 ++++++ 2 files changed, 45 insertions(+) create mode 100644 app/src/test/kotlin/com/wespot/post/vo/PostSearchKeywordTest.kt 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/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(" ", "") } + } + } From 2e564405ffda99ba9cf2a55e165df8287466422c Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 23:11:31 +0900 Subject: [PATCH 11/14] feat: update color handling and simplify image data structures in PostComponent --- .../wespot/post/dto/request/UpdatedPostRequest.kt | 4 +--- .../post/dto/response/PostComponentResponse.kt | 4 ++-- .../main/kotlin/com/wespot/common/view/ColorType.kt | 2 +- .../wespot/post/nudge/server_driven/VoteComponent.kt | 2 +- .../com/wespot/post/server_driven/PostComponent.kt | 12 +++--------- .../src/main/kotlin/com/wespot/view/color/Color.kt | 4 +++- 6 files changed, 11 insertions(+), 17 deletions(-) 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..65b437ad 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 @@ -8,9 +8,7 @@ class UpdatedPostRequest( ) { data class UpdatedPostImageRequest( - val url: String, - val width: Int, - val height: Int + val url: String ) { } 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 index a3302caf..c696798c 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt @@ -70,7 +70,7 @@ data class PostComponentResponse( data class PostContentSectionResponse( val type: String, - val images: List, + val images: List, ) { companion object { @@ -78,7 +78,7 @@ data class PostComponentResponse( return postContentSection?.let { PostContentSectionResponse( type = it.type, - images = it.images.map { image -> ImageContentV2Response.from(image) } + images = it.images ) } } diff --git a/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt b/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt index 5577db8b..792e1c92 100644 --- a/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt +++ b/domain/src/main/kotlin/com/wespot/common/view/ColorType.kt @@ -25,7 +25,7 @@ enum class ColorType( PRIMARY900("Primary900"), WHITE("White"), + BLACK("Black"), ; - } 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 index 4e24ed3f..3c5818b4 100644 --- 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 @@ -9,7 +9,7 @@ import com.wespot.view.text.RichTextV2 data class VoteComponent( val id: Long, - val type: String = "VoteComponent", + val type: String = "VoteItem", val content: VoteContent, ) { 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 index 9fc1c29c..e83cb91d 100644 --- a/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -50,7 +50,7 @@ data class PostComponent( data class PostContentSection( val type: String = "Images", - val images: List, + val images: List, ) { } @@ -143,9 +143,7 @@ data class PostComponent( ), contentSection = post.images?.let { PostContent.PostContentSection( - images = it.postImages.map { image -> - ImageContentV2(url = image.url) - } + images = it.postImages.map { image -> image.url } ) }, footerSection = PostContent.PostFooterSection( @@ -253,11 +251,7 @@ data class PostComponent( ), contentSection = post.images?.let { PostContent.PostContentSection( - images = it.postImages.map { image -> - ImageContentV2( - url = image.url, - ) - } + images = it.postImages.map { image -> image.url } ) }, footerSection = PostContent.PostFooterSection( diff --git a/domain/src/main/kotlin/com/wespot/view/color/Color.kt b/domain/src/main/kotlin/com/wespot/view/color/Color.kt index dfce4abf..680b299b 100644 --- a/domain/src/main/kotlin/com/wespot/view/color/Color.kt +++ b/domain/src/main/kotlin/com/wespot/view/color/Color.kt @@ -1,5 +1,7 @@ package com.wespot.view.color +import com.wespot.common.view.ColorType + data class Color( val value: String = "GRAY600", val type: String = "Token", @@ -10,7 +12,7 @@ data class Color( const val TOKEN = "Token" const val HEX = "Hex" - val DEFAULT_COLOR = Color(value = "#FFFFFF") + val DEFAULT_COLOR = Color(value = ColorType.BLACK.value) } } From b82cf30f07a54abffb5bb1e2a0b48bdfd5a2af21 Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 23:30:02 +0900 Subject: [PATCH 12/14] feat: implement searchByTitleAndDescription method for enhanced post search functionality --- .../com/wespot/post/PostSearchedController.kt | 12 ++++-- .../post/port/in/PostSearchedUseCase.kt | 6 ++- .../com/wespot/post/port/out/PostPort.kt | 7 ++++ .../post/service/PostSearchedService.kt | 42 +++++++++++-------- .../com/wespot/post/adapter/PostAdapter.kt | 15 +++++++ .../post/repository/PostJpaRepository.kt | 19 +++++++++ 6 files changed, 79 insertions(+), 22 deletions(-) diff --git a/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt b/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt index a6e3d590..426408cb 100644 --- a/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostSearchedController.kt @@ -1,6 +1,6 @@ package com.wespot.post -import com.wespot.post.dto.response.PostComponentResponse +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 @@ -17,8 +17,14 @@ class PostSearchedController( @GetMapping("/search") fun searchPost( @RequestParam keyword: String, - ): ResponseEntity> { - val response = postSearchedUseCase.search(keyword = keyword) + @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/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt b/core/src/main/kotlin/com/wespot/post/port/in/PostSearchedUseCase.kt index 4153b104..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,11 +1,13 @@ package com.wespot.post.port.`in` -import com.wespot.post.dto.response.PostComponentResponse +import com.wespot.common.dto.PostPagingResponse interface PostSearchedUseCase { fun search( keyword: String, - ): List + 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 5a6ad5f5..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,6 +6,13 @@ 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 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 7a1ad2ac..b65bb679 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt @@ -1,38 +1,46 @@ package com.wespot.post.service +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 { + 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) - .asSequence() - .distinct() - .sortedByDescending { it.createdAt } - .distinct() - .map { PostComponent.from(it) } - .map { PostComponentResponse.from(it) } - .toList() + val data = posts.map { PostComponent.from(it) }.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/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt b/infrastructure/mysql/src/main/kotlin/com/wespot/post/adapter/PostAdapter.kt index 0d001b18..af285f24 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 @@ -48,6 +48,21 @@ class PostAdapter( ) } + 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.findByTitleContainingOrderByBaseEntityCreatedAtDesc( title = title, 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 aa2b5a70..ff41e8b2 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,6 +3,7 @@ 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 { @@ -44,4 +45,22 @@ interface PostJpaRepository : JpaRepository { 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 + } From 122545d4b03df3899437f5ba53935556f55d0109 Mon Sep 17 00:00:00 2001 From: kpeel Date: Thu, 31 Jul 2025 23:32:00 +0900 Subject: [PATCH 13/14] fix: correct text formatting in VoteComponent prompt --- .../kotlin/com/wespot/post/nudge/server_driven/VoteComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 3c5818b4..471a9721 100644 --- 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 @@ -49,7 +49,7 @@ data class VoteComponent( ) ), text = RichTextV2( - text = "지금 우리 반에서 가장\n${voteOption.content.content} 친구는?", + text = "지금 우리 반에서 가장\n${voteOption.content.content}는?", color = Color(value = ColorType.GRAY900.value), typography = TypographType.BODY04.value, maxLine = 2 From a1bce7aaf48a967127b620f7dad92840a7be969f Mon Sep 17 00:00:00 2001 From: kpeel Date: Tue, 26 Aug 2025 13:42:19 +0900 Subject: [PATCH 14/14] feat: add report reason functionality with CRUD operations and notification settings --- .../comment/PostCommentDeletedController.kt | 27 ++++++++++ .../comment/PostCommentLikeController.kt | 3 +- .../comment/PostCommentReportController.kt | 5 +- ...ModifyPostNotificationSettingController.kt | 27 ++++++++++ .../com/wespot/post/PostBlockController.kt | 4 +- .../com/wespot/post/PostEditController.kt | 1 + .../com/wespot/post/PostLikeController.kt | 4 +- .../post/PostNotificationSettingController.kt | 4 +- .../com/wespot/post/PostReportController.kt | 10 +++- .../com/wespot/post/PostScrapController.kt | 3 +- .../wespot/report/ReportReasonController.kt | 23 ++++++++ .../port/in/PostCommentDeletedUseCase.kt | 7 +++ .../comment/port/out/PostCommentPort.kt | 2 + .../service/PostCommentDeletedService.kt | 31 +++++++++++ .../comment/service/PostCommentLikeService.kt | 2 +- .../service/PostCommentReportService.kt | 2 +- .../PostCommentDeleteEventListener.kt | 23 ++++++++ .../wespot/common/dto/PostPagingResponse.kt | 6 +++ .../common/dto/view/ImageContentV2Response.kt | 4 +- .../com/wespot/image/dto/PresignedResponse.kt | 2 +- .../listener/CommentNotificationListener.kt | 2 +- .../post/dto/request/CreatedPostRequest.kt | 7 +-- .../ModifyPostNotificationSettingRequest.kt | 6 +++ .../post/dto/request/PostReportRequest.kt | 2 +- .../post/dto/request/UpdatedPostRequest.kt | 7 +-- .../post/dto/response/PostCommentResponse.kt | 5 +- .../dto/response/PostComponentResponse.kt | 2 + .../ModifyPostNotificationSettingUseCase.kt | 10 ++++ .../ModifyPostNotificationSettingService.kt | 24 +++++++++ .../wespot/post/service/PostBlockService.kt | 2 +- .../post/service/PostCommentInquiryService.kt | 6 +-- .../wespot/post/service/PostCreatedService.kt | 2 +- .../wespot/post/service/PostEditService.kt | 2 +- .../wespot/post/service/PostInquiryService.kt | 35 ++++++++---- .../wespot/post/service/PostLikeService.kt | 2 +- .../wespot/post/service/PostReportService.kt | 13 ++++- .../wespot/post/service/PostScrapService.kt | 2 +- .../post/service/PostSearchedService.kt | 2 +- .../PostCommentCreatedEventListener.kt | 6 +-- .../PostCommentDeletedEventListener.kt | 24 +++++++++ .../wespot/report/dto/ReportReasonResponse.kt | 19 +++++++ .../report/port/in/ReportReasonUseCase.kt | 9 ++++ .../report/port/out/ReportReasonPort.kt | 11 ++++ .../report/service/ReportReasonService.kt | 20 +++++++ .../kotlin/com/wespot/comment/PostComment.kt | 12 ++++- .../com/wespot/comment/PostCommentLike.kt | 4 +- .../comment/event/PostCommentDeleteEvent.kt | 9 ++++ .../kotlin/com/wespot/message/v2/MessageV2.kt | 3 ++ .../wespot/notification/NotificationType.kt | 2 +- .../src/main/kotlin/com/wespot/post/Post.kt | 4 ++ .../kotlin/com/wespot/post/PostCategory.kt | 2 + .../main/kotlin/com/wespot/post/PostImage.kt | 6 ++- .../main/kotlin/com/wespot/post/PostImages.kt | 4 ++ .../main/kotlin/com/wespot/post/PostReport.kt | 3 +- .../post/server_driven/PostComponent.kt | 53 ++++++++----------- .../kotlin/com/wespot/report/ReportReason.kt | 7 +++ .../main/kotlin/com/wespot/user/Setting.kt | 1 + .../src/main/kotlin/com/wespot/user/User.kt | 2 + .../kotlin/com/wespot/view/color/Color.kt | 2 +- .../kotlin/com/wespot/view/icon/IconV2.kt | 26 +++++++++ .../comment/adapter/PostCommentAdapter.kt | 4 ++ .../com/wespot/post/PostCategoryEntity.kt | 6 +++ .../com/wespot/post/PostReportEntity.kt | 2 +- .../com/wespot/post/adapter/PostAdapter.kt | 7 ++- .../wespot/post/adapter/PostReportAdapter.kt | 18 +++++-- .../wespot/post/mapper/PostCategoryMapper.kt | 4 ++ .../wespot/post/mapper/PostReportMapper.kt | 7 +-- .../post/repository/PostImageJpaRepository.kt | 2 + .../post/repository/PostJpaRepository.kt | 14 ++--- .../com/wespot/report/ReportReasonAdapter.kt | 22 ++++++++ .../wespot/report/ReportReasonJpaEntity.kt | 19 +++++++ .../report/ReportReasonJpaRepository.kt | 6 +++ .../com/wespot/report/ReportReasonMapper.kt | 19 +++++++ .../wespot/user/entity/SettingJpaEntity.kt | 5 +- .../com/wespot/user/mapper/SettingMapper.kt | 6 ++- 75 files changed, 577 insertions(+), 114 deletions(-) create mode 100644 app/src/main/kotlin/com/wespot/comment/PostCommentDeletedController.kt create mode 100644 app/src/main/kotlin/com/wespot/post/ModifyPostNotificationSettingController.kt create mode 100644 app/src/main/kotlin/com/wespot/report/ReportReasonController.kt create mode 100644 core/src/main/kotlin/com/wespot/comment/port/in/PostCommentDeletedUseCase.kt create mode 100644 core/src/main/kotlin/com/wespot/comment/service/PostCommentDeletedService.kt create mode 100644 core/src/main/kotlin/com/wespot/comment/service/listener/PostCommentDeleteEventListener.kt create mode 100644 core/src/main/kotlin/com/wespot/post/dto/request/ModifyPostNotificationSettingRequest.kt create mode 100644 core/src/main/kotlin/com/wespot/post/port/in/ModifyPostNotificationSettingUseCase.kt create mode 100644 core/src/main/kotlin/com/wespot/post/service/ModifyPostNotificationSettingService.kt create mode 100644 core/src/main/kotlin/com/wespot/post/service/listener/PostCommentDeletedEventListener.kt create mode 100644 core/src/main/kotlin/com/wespot/report/dto/ReportReasonResponse.kt create mode 100644 core/src/main/kotlin/com/wespot/report/port/in/ReportReasonUseCase.kt create mode 100644 core/src/main/kotlin/com/wespot/report/port/out/ReportReasonPort.kt create mode 100644 core/src/main/kotlin/com/wespot/report/service/ReportReasonService.kt create mode 100644 domain/src/main/kotlin/com/wespot/comment/event/PostCommentDeleteEvent.kt create mode 100644 domain/src/main/kotlin/com/wespot/report/ReportReason.kt create mode 100644 infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonAdapter.kt create mode 100644 infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaEntity.kt create mode 100644 infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonJpaRepository.kt create mode 100644 infrastructure/mysql/src/main/kotlin/com/wespot/report/ReportReasonMapper.kt 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 d5f4b6bd..95ff34dd 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentLikeController.kt @@ -1,6 +1,7 @@ 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 @@ -18,7 +19,7 @@ class PostCommentLikeController( 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 14bde9d9..b1a95f3f 100644 --- a/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt +++ b/app/src/main/kotlin/com/wespot/comment/PostCommentReportController.kt @@ -1,6 +1,7 @@ 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.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -14,10 +15,10 @@ class PostCommentReportController( ) { @PostMapping("/{commentId}/report") - fun likePostComment(@PathVariable commentId: Long): ResponseEntity { + 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/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/PostLikeController.kt b/app/src/main/kotlin/com/wespot/post/PostLikeController.kt index 52eb9d1c..a3095dac 100644 --- a/app/src/main/kotlin/com/wespot/post/PostLikeController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostLikeController.kt @@ -1,8 +1,8 @@ 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 @@ -19,7 +19,7 @@ class PostLikeController( 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 ee6d2ebd..665c0680 100644 --- a/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostNotificationSettingController.kt @@ -1,8 +1,8 @@ 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 @@ -19,7 +19,7 @@ class PostNotificationSettingController( 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 eb0a65e6..d531ba8b 100644 --- a/app/src/main/kotlin/com/wespot/post/PostScrapController.kt +++ b/app/src/main/kotlin/com/wespot/post/PostScrapController.kt @@ -1,6 +1,7 @@ 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.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -17,7 +18,7 @@ class PostScrapController( 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/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/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 index cfb92669..b19f0798 100644 --- a/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt +++ b/core/src/main/kotlin/com/wespot/common/dto/PostPagingResponse.kt @@ -1,7 +1,13 @@ 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/ImageContentV2Response.kt b/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt index 473d4c03..0351b083 100644 --- a/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt +++ b/core/src/main/kotlin/com/wespot/common/dto/view/ImageContentV2Response.kt @@ -4,8 +4,8 @@ import com.wespot.view.image.ImageContentV2 data class ImageContentV2Response( val url: String, - val width: Int?, - val height: Int?, + val width: Int? = null, + val height: Int? = null, ) { companion object { 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 7a6e7d8d..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,12 +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 - ) { - } - } 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 65b437ad..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,12 +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 - ) { - } - } 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 7f44e3cc..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 @@ -7,6 +7,7 @@ import com.wespot.post.PostProfile @JsonInclude(JsonInclude.Include.NON_NULL) data class PostCommentResponse( + val id: Long, val isMe: Boolean, val authorImage: String, val authorName: String, @@ -23,12 +24,14 @@ 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 = isPostOwner, + isMe = isCommentOwner, authorName = if (isPostOwner) OWNER_NAME else VIEWER_NAME, content = postComment.content.content, likeCount = postComment.likeCount, 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 index c696798c..833266d6 100644 --- a/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt +++ b/core/src/main/kotlin/com/wespot/post/dto/response/PostComponentResponse.kt @@ -10,6 +10,7 @@ import com.wespot.post.server_driven.PostComponent data class PostComponentResponse( val id: Long, val type: String, + val isMyPost: Boolean, val content: PostContentResponse, ) { @@ -152,6 +153,7 @@ data class PostComponentResponse( return PostComponentResponse( id = postComponent.id, type = postComponent.type, + isMyPost = postComponent.isMyPost, content = PostContentResponse( category = PostCategoryComponentResponse.from(content.category), headerSection = PostContentResponse.PostHeaderSectionResponse.from(content.headerSection), 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/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/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 a05eba8f..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,7 +43,7 @@ class PostCreatedService( ?.map { PostImage.of( cloudFrontUrl = cloudFrontUrl, - imageUrl = it.url, + 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 ff4274b7..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) + PostImage.of(cloudFrontUrl = cloudFrontUrl, imageUrl = it) }, toUpdatePost = { updatedPost -> postPort.save(updatedPost) } ).id diff --git a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt index 07f81d17..197dd9af 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostInquiryService.kt @@ -4,6 +4,7 @@ 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 @@ -59,14 +60,26 @@ class PostInquiryService( inquirySize = inquirySize + 1 ) - return postPagingResponse(posts, inquirySize) + 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 + inquirySize: Long, + viewerId: Long, ): PostPagingResponse { - val data = posts.map { PostComponent.from(it) } + 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) @@ -96,7 +109,7 @@ class PostInquiryService( endSequence = endSequence, ).sortedBy { it.mustViewSequence() } - val data = mixPostAndNudge(startSequence, endSequence, posts.take(inquirySize.toInt()), nudges) + 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) @@ -104,6 +117,7 @@ class PostInquiryService( data = data, lastCursorId = lastCursorId, hasNext = hasNext + ) } @@ -136,13 +150,14 @@ class PostInquiryService( 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.from(post) + val component = PostComponent.of(post, viewerId) data.add(PostComponentResponse.from(component)) if (nudges.size > nudgesIndex && nudges[nudgesIndex].mustViewSequence() == i) { @@ -199,7 +214,7 @@ class PostInquiryService( message = "존재하지 않는 게시글입니다." ) - val postComponent = PostComponent.fromDetail(post) + val postComponent = PostComponent.fromDetail(post, loginUser.id) return PostComponentResponse.from(postComponent) } @@ -220,7 +235,7 @@ class PostInquiryService( cursorId = cursorId ) - return postPagingResponse(posts, inquirySize) + return postPagingResponse(posts, inquirySize, loginUser.id) } @Transactional(readOnly = false) @@ -239,7 +254,7 @@ class PostInquiryService( inquirySize = inquirySize + 1, cursorId = cursorId ).let { posts -> - return postPagingResponse(posts, inquirySize) + return postPagingResponse(posts, inquirySize, loginUser.id) } } @@ -254,10 +269,8 @@ class PostInquiryService( authorId = loginUser.id, inquirySize = inquirySize + 1, cursorId = cursorId - ).let { return postPagingResponse(it, inquirySize) } + ).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/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 b65bb679..9f2e2862 100644 --- a/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt +++ b/core/src/main/kotlin/com/wespot/post/service/PostSearchedService.kt @@ -33,7 +33,7 @@ class PostSearchedService( cursorId = cursorId ) - val data = posts.map { PostComponent.from(it) }.map { PostComponentResponse.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 } 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/message/v2/MessageV2.kt b/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt index ada94db5..307a7e55 100644 --- a/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt +++ b/domain/src/main/kotlin/com/wespot/message/v2/MessageV2.kt @@ -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 41ee5207..3b934ff7 100644 --- a/domain/src/main/kotlin/com/wespot/post/Post.kt +++ b/domain/src/main/kotlin/com/wespot/post/Post.kt @@ -126,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 baf02abb..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,8 @@ class PostCategory( val id: Long, val majorCategoryName: String, val name: String, + val thumbnail: String, + val backgroundImage: String, val createdAt: LocalDateTime ) { diff --git a/domain/src/main/kotlin/com/wespot/post/PostImage.kt b/domain/src/main/kotlin/com/wespot/post/PostImage.kt index 2c5df471..db0a0eed 100644 --- a/domain/src/main/kotlin/com/wespot/post/PostImage.kt +++ b/domain/src/main/kotlin/com/wespot/post/PostImage.kt @@ -6,7 +6,7 @@ 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 createdAt: LocalDateTime @@ -37,6 +37,10 @@ class PostImage( } + fun isNew(): Boolean { + return id == 0L + } + fun addedPost(postId: Long): PostImage { return update( postId = postId 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/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/server_driven/PostComponent.kt b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt index e83cb91d..c808fbce 100644 --- a/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt +++ b/domain/src/main/kotlin/com/wespot/post/server_driven/PostComponent.kt @@ -12,6 +12,7 @@ import com.wespot.view.text.RichTextV2 data class PostComponent( val id: Long, val type: String = "PostItem", + val isMyPost: Boolean, val content: PostContent, ) { @@ -90,9 +91,14 @@ data class PostComponent( companion object { - fun from(post: Post): PostComponent { + 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( @@ -112,18 +118,16 @@ data class PostComponent( typography = TypographType.BADGE.value, maxLine = 1, ), - category = PostCategoryComponent( + 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.majorCategoryName, - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/right_arrow.png", - ) - ) + target = post.category.id.toString(), + icon = IconV2.RIGHT_ARROW + ) else null ), infoSection = PostContent.PostInfoSection( title = post.title?.let { @@ -150,9 +154,7 @@ data class PostComponent( reactions = listOf( PostContent.PostFooterSection.PostReactionItem( type = "Chat", - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/TALK_BALLON.png" - ), + icon = IconV2.COMMENT_BALLON, count = RichTextV2( text = post.commentCount.toString(), color = Color(value = ColorType.GRAY300.value), @@ -163,9 +165,7 @@ data class PostComponent( ), PostContent.PostFooterSection.PostReactionItem( type = "Like", - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/THUMBS_UP.png", - ), + icon = IconV2.THUMBS_UP, count = RichTextV2( text = post.likeCount.toString(), color = Color(value = ColorType.GRAY300.value), @@ -176,9 +176,7 @@ data class PostComponent( ), ), scrap = PostContent.PostFooterSection.PostScrapComponent( - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/BOOKMARK.png", - ), + icon = IconV2.BOOKMARK, selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, ), ) @@ -186,9 +184,10 @@ data class PostComponent( ) } - fun fromDetail(post: Post): PostComponent { + fun fromDetail(post: Post, viewerId: Long): PostComponent { return PostComponent( id = post.id, + isMyPost = post.isAuthor(viewerId), content = PostContent( category = PostCategoryComponent( text = RichTextV2( @@ -198,9 +197,7 @@ data class PostComponent( maxLine = 1, ), target = post.category.id.toString(), - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/right_arrow_in_black.png", - ) + icon = IconV2.RIGHT_ARROW_IN_BLACK ), headerSection = PostContent.PostHeaderSection( profileImage = ImageContentV2( @@ -221,9 +218,7 @@ data class PostComponent( maxLine = 1 ), button = PostContent.PostButtonComponent( - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/comment_notification_check.png", - ), + icon = IconV2.COMMENT_NOTIFICATION_CHECK, text = RichTextV2( text = "댓글 알림", color = Color(value = ColorType.GRAY100.value), @@ -258,9 +253,7 @@ data class PostComponent( reactions = listOf( PostContent.PostFooterSection.PostReactionItem( type = "Chat", - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/TALK_BALLON.png" - ), + icon = IconV2.COMMENT_BALLON, count = RichTextV2( text = post.commentCount.toString(), color = Color(value = ColorType.GRAY300.value), @@ -271,9 +264,7 @@ data class PostComponent( ), PostContent.PostFooterSection.PostReactionItem( type = "Like", - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/THUMBS_UP.png", - ), + icon = IconV2.THUMBS_UP, count = RichTextV2( text = post.likeCount.toString(), color = Color(value = ColorType.GRAY300.value), @@ -284,9 +275,7 @@ data class PostComponent( ), ), scrap = PostContent.PostFooterSection.PostScrapComponent( - icon = IconV2( - url = "https://dw2d2daekmyur.cloudfront.net/BOOKMARK.png", - ), + icon = IconV2.BOOKMARK, selected = post.postStatusByViewer?.isViewerPushedScrap ?: false, ), ) 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/view/color/Color.kt b/domain/src/main/kotlin/com/wespot/view/color/Color.kt index 680b299b..72559829 100644 --- a/domain/src/main/kotlin/com/wespot/view/color/Color.kt +++ b/domain/src/main/kotlin/com/wespot/view/color/Color.kt @@ -12,7 +12,7 @@ data class Color( const val TOKEN = "Token" const val HEX = "Hex" - val DEFAULT_COLOR = Color(value = ColorType.BLACK.value) + val DEFAULT_COLOR = Color(value = ColorType.GRAY300.value) } } diff --git a/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt index 3c9b80ed..cbd30afc 100644 --- a/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt +++ b/domain/src/main/kotlin/com/wespot/view/icon/IconV2.kt @@ -9,6 +9,11 @@ data class IconV2( ) { 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 @@ -24,5 +29,26 @@ data class IconV2( 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/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/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 af285f24..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 @@ -1,7 +1,6 @@ package com.wespot.post.adapter import com.wespot.comment.port.out.PostValidatePort -import com.wespot.exception.CustomException import com.wespot.post.Post import com.wespot.post.PostEntity import com.wespot.post.PostImages @@ -33,6 +32,12 @@ class PostAdapter( 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) } 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/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 ff41e8b2..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 @@ -47,13 +47,13 @@ interface PostJpaRepository : JpaRepository { @Query( value = """ - SELECT * - FROM post - WHERE (title REGEXP :pattern - OR description REGEXP :pattern) - AND id < :cursorId - ORDER BY created_at DESC - LIMIT :limit + SELECT * + FROM post + WHERE (title REGEXP :pattern + OR description REGEXP :pattern) + AND id < :cursorId + ORDER BY created_at DESC + LIMIT :limit """, nativeQuery = true ) 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, ) }