diff --git a/src/main/kotlin/com/numberone/daepiro/domain/community/dto/response/ArticleResponse.kt b/src/main/kotlin/com/numberone/daepiro/domain/community/dto/response/ArticleResponse.kt index b449c80..5953502 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/community/dto/response/ArticleResponse.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/community/dto/response/ArticleResponse.kt @@ -123,7 +123,7 @@ data class ArticleDetailResponse( val files: List?, @Schema(description = "해당 게시글에 작성된 댓글 목록 (계층형)") - val comments: List = emptyList(), + val comments: List = emptyList(), @Schema(description = "좋아요를 누른 게시글인지?", example = "true") val isLiked: Boolean = false, @@ -140,7 +140,7 @@ data class ArticleDetailResponse( isLiked: Boolean = false, isVerified: Boolean, files: List? = emptyList(), - comments: List = emptyList() + comments: List = emptyList() ): ArticleDetailResponse { return ArticleDetailResponse( id = article.id!!, @@ -237,3 +237,47 @@ data class ArticleLikeResponse( } } } + +data class CommentResponseWithIsMine( + @Schema(description = "댓글 아이디(PK)") + val id: Long, + @Schema(description = "댓글 본문") + val body: String, + @Schema(description = "작성자 정보") + val author: AuthorResponse? = null, + @Schema(description = "좋아요 개수") + val likeCount: Int = 0, + @Schema(description = "부모 댓글 Id") + val parentCommentId: Long? = null, + @Schema(description = "생성일시", example = "2024-12-02T21:48:14.929554") + val createdAt: LocalDateTime? = null, + @Schema(description = "수정일시", example = "2024-12-02T21:48:14.929554") + val lastModifiedAt: LocalDateTime? = null, + @Schema(description = "해당 댓글에 자식으로 포함된 댓글들") + var children: MutableList = mutableListOf(), + @Schema(description = "좋아요를 누른 댓글인지??", example = "true") + var isLiked: Boolean = false, + @Schema(description = "내 댓글 여부", example = "true") + val isMine: Boolean, +) { + companion object { + fun of( + comment: CommentResponse, + isMine: Boolean + ): CommentResponseWithIsMine { + return CommentResponseWithIsMine( + id = comment.id, + body = comment.body, + author = comment.author, + likeCount = comment.likeCount, + parentCommentId = comment.parentCommentId, + createdAt = comment.createdAt, + lastModifiedAt = comment.lastModifiedAt, + children = comment.children, + isLiked = comment.isLiked, + isMine = isMine + ) + } + } + +} diff --git a/src/main/kotlin/com/numberone/daepiro/domain/community/service/ArticleService.kt b/src/main/kotlin/com/numberone/daepiro/domain/community/service/ArticleService.kt index b391115..deb91e8 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/community/service/ArticleService.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/community/service/ArticleService.kt @@ -12,6 +12,7 @@ import com.numberone.daepiro.domain.community.dto.response.ArticleLikeResponse import com.numberone.daepiro.domain.community.dto.response.ArticleListResponse import com.numberone.daepiro.domain.community.dto.response.ArticleSimpleResponse import com.numberone.daepiro.domain.community.dto.response.CommentResponse +import com.numberone.daepiro.domain.community.dto.response.CommentResponseWithIsMine import com.numberone.daepiro.domain.community.entity.Article import com.numberone.daepiro.domain.community.entity.ReportedDocument import com.numberone.daepiro.domain.community.event.ArticleAddressMappingEvent @@ -184,11 +185,15 @@ class ArticleService( return@let authorVerifiedAddressIds.contains(article.address?.id) } ?: false + + return ArticleDetailResponse.of( article = article, isLiked = isLikedArticle, files = files, - comments = roots, + comments = roots.map { + CommentResponseWithIsMine.of(it, it.author?.userId == userId) + }, isVerified = isVerifiedAuthor, ) } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/dto/response/SituationCommentResponse.kt b/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/dto/response/SituationCommentResponse.kt index 2d86330..f49d8c1 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/dto/response/SituationCommentResponse.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/dto/response/SituationCommentResponse.kt @@ -2,6 +2,7 @@ package com.numberone.daepiro.domain.disasterSituation.dto.response import com.numberone.daepiro.domain.community.entity.Comment import com.numberone.daepiro.domain.user.entity.UserEntity +import com.numberone.daepiro.global.utils.SecurityContextUtils import io.swagger.v3.oas.annotations.media.Schema import org.springframework.data.annotation.LastModifiedDate import java.time.LocalDateTime @@ -50,8 +51,11 @@ data class SituationCommentResponse( " }\n" + " ]" ) - val childComments: List + val childComments: List, ) { + @Schema(description = "내 좋아요 여부", example = "true") + var isLiked: Boolean = false + companion object { fun of( comment: Comment, @@ -80,7 +84,7 @@ data class SituationCommentResponse( listOf(), mapOf() ) - }, + } ) } } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/service/DisasterSituationService.kt b/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/service/DisasterSituationService.kt index 3a7b116..2f2d9c2 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/service/DisasterSituationService.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/disasterSituation/service/DisasterSituationService.kt @@ -14,7 +14,9 @@ import com.numberone.daepiro.domain.disaster.entity.Disaster import com.numberone.daepiro.domain.disasterSituation.dto.request.CreateSituationCommentRequest import com.numberone.daepiro.domain.disasterSituation.dto.response.DisasterSituationResponse import com.numberone.daepiro.domain.disasterSituation.dto.response.SituationCommentResponse +import com.numberone.daepiro.domain.user.repository.UserLikeRepository import com.numberone.daepiro.domain.user.repository.UserRepository +import com.numberone.daepiro.domain.user.repository.findAllLikedCommentId import com.numberone.daepiro.domain.user.repository.findByIdOrThrow import com.numberone.daepiro.global.dto.ApiResult import org.springframework.stereotype.Service @@ -28,7 +30,8 @@ class DisasterSituationService( private val addressRepository: AddressRepository, private val userRepository: UserRepository, private val commentRepository: CommentRepository, - private val userAddressVerifiedService: UserAddressVerifiedService + private val userAddressVerifiedService: UserAddressVerifiedService, + private val userLikeRepository: UserLikeRepository ) { @Transactional fun createDisasterSituation(disasters: List) { @@ -66,7 +69,7 @@ class DisasterSituationService( return ApiResult.ok(articles.map { val commentEntities = commentRepository.findPopularComments(it.id!!) val comments = commentEntities.map { comment -> - Pair(comment, listOf())//todo 의미없이 pair를 썼음. 고쳐야함. + Pair(comment, listOf())//todo 의미없이 pair를 썼음. 고쳐야함. 쩔수 없나. } DisasterSituationResponse.of( @@ -92,19 +95,29 @@ class DisasterSituationService( .map { Pair(it, commentRepository.findChildComments(it.id!!)) } val article = articleRepository.findByIdOrThrow(situationId) - return ApiResult.ok(comments.map { - SituationCommentResponse.of( - it.first, - user, - userAddressVerifiedService.getVerifiedOne(user.id!!, article.address!!.id!!), - it.second, - it.second.associate { - it.id!! to userAddressVerifiedService.getVerifiedOne( - it.authUser!!.id!!, - article.address!!.id!! - ) - } - ) - }) + val result = comments + .filter { !it.first.isDeleted() || it.second.isNotEmpty() } + .map { + SituationCommentResponse.of( + it.first, + user, + userAddressVerifiedService.getVerifiedOne(user.id!!, article.address!!.id!!), + it.second, + it.second.associate { + it.id!! to userAddressVerifiedService.getVerifiedOne( + it.authUser!!.id!!, + article.address!!.id!! + ) + } + ) + } + val likedCommentIdSet = userLikeRepository.findAllLikedCommentId(userId) + result.forEach { + it.isLiked = likedCommentIdSet.contains(it.id) + it.childComments.forEach { childComment -> + childComment.isLiked = likedCommentIdSet.contains(childComment.id) + } + } + return ApiResult.ok(result) } } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/api/UserApiV1.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/api/UserApiV1.kt index 1c7945b..a25fa61 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/user/api/UserApiV1.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/api/UserApiV1.kt @@ -1,6 +1,7 @@ package com.numberone.daepiro.domain.user.api import com.numberone.daepiro.domain.user.dto.request.OnboardingRequest +import com.numberone.daepiro.domain.user.dto.request.UpdateFcmTokenRequest import com.numberone.daepiro.domain.user.dto.request.UpdateGpsRequest import com.numberone.daepiro.domain.user.dto.response.CheckNicknameResponse import com.numberone.daepiro.domain.user.dto.response.DisasterWithRegionResponse @@ -59,4 +60,14 @@ interface UserApiV1 { @DeleteMapping @Operation(summary = "회원 탈퇴", description = "회원 탈퇴를 진행합니다.") fun deleteUser(): ApiResult + + @PutMapping("/fcm") + @Operation(summary = "FCM 토큰 설정", description = "사용자의 FCM 토큰을 설정합니다.") + fun updateFcmToken( + @RequestBody @Valid request: UpdateFcmTokenRequest + ): ApiResult + + @PutMapping("/logout") + @Operation(summary = "로그아웃", description = "로그아웃을 진행합니다.(FCM 토큰 삭제)") + fun logout(): ApiResult } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/controller/UserController.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/controller/UserController.kt index ed1f906..b489695 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/user/controller/UserController.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/controller/UserController.kt @@ -2,6 +2,7 @@ package com.numberone.daepiro.domain.user.controller import com.numberone.daepiro.domain.user.api.UserApiV1 import com.numberone.daepiro.domain.user.dto.request.OnboardingRequest +import com.numberone.daepiro.domain.user.dto.request.UpdateFcmTokenRequest import com.numberone.daepiro.domain.user.dto.request.UpdateGpsRequest import com.numberone.daepiro.domain.user.dto.response.CheckNicknameResponse import com.numberone.daepiro.domain.user.dto.response.DisasterWithRegionResponse @@ -56,4 +57,19 @@ class UserController( userService.deleteUser(SecurityContextUtils.getUserId()) return ApiResult.ok() } + + override fun updateFcmToken(request: UpdateFcmTokenRequest): ApiResult { + userService.updateFcmToken( + request, + SecurityContextUtils.getUserId() + ) + return ApiResult.ok() + } + + override fun logout(): ApiResult { + userService.logout(SecurityContextUtils.getUserId()) + return ApiResult.ok() + } + + } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/OnboardingRequest.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/OnboardingRequest.kt index 503bc5f..969c803 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/OnboardingRequest.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/OnboardingRequest.kt @@ -21,8 +21,11 @@ data class OnboardingRequest( ) val disasterTypes: List, - @Schema(description = "FCM 토큰", example = "cT9YaTTeTEeGeohmih6qWf:APA91bGoybhRF2sB6JC7mPNaqJi3XjPhwbTyy91iexup7QDB_AXxI5lug_l-4o0y9T4uYKoysdf12Wde5X01fv-dqWck1kk33d4O5TN3oz_4nzuUVa2ffE3S9ELAcZm2a308W7NGD3Sc") - val fcmToken: String + @Schema( + description = "FCM 토큰(온보딩 시 설정 안 한다면 null 가능)", + example = "cT9YaTTeTEeGeohmih6qWf:APA91bGoybhRF2sB6JC7mPNaqJi3XjPhwbTyy91iexup7QDB_AXxI5lug_l-4o0y9T4uYKoysdf12Wde5X01fv-dqWck1kk33d4O5TN3oz_4nzuUVa2ffE3S9ELAcZm2a308W7NGD3Sc" + ) + val fcmToken: String? ) data class AddressRequest( diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/UpdateFcmTokenRequest.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/UpdateFcmTokenRequest.kt new file mode 100644 index 0000000..b486192 --- /dev/null +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/dto/request/UpdateFcmTokenRequest.kt @@ -0,0 +1,8 @@ +package com.numberone.daepiro.domain.user.dto.request + +import io.swagger.v3.oas.annotations.media.Schema + +data class UpdateFcmTokenRequest( + @Schema(description = "FCM 토큰", example = "cT9YaTTeTEeGeohmih6qWf:APA91bGoybhRF2sB6JC7mPNaqJi3XjPhwbTyy91iexup7QDB_AXxI5lug_l-4o0y9T4uYKoysdf12Wde5X01fv-dqWck1kk33d4O5TN3oz_4nzuUVa2ffE3S9ELAcZm2a308W7NGD3Sc") + val fcmToken: String +) diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/entity/UserEntity.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/entity/UserEntity.kt index f717c9f..db72a9f 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/user/entity/UserEntity.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/entity/UserEntity.kt @@ -55,7 +55,7 @@ class UserEntity( val userDisasterTypes: List = emptyList(), @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL]) - val cheeringList : List = emptyList(), + val cheeringList: List = emptyList(), var fcmToken: String? = null, ) : PrimaryKeyEntity() { @@ -82,7 +82,7 @@ class UserEntity( this.nickname = nickname } - fun initFcmToken(fcmToken: String) { + fun initFcmToken(fcmToken: String?) { this.fcmToken = fcmToken } diff --git a/src/main/kotlin/com/numberone/daepiro/domain/user/service/UserService.kt b/src/main/kotlin/com/numberone/daepiro/domain/user/service/UserService.kt index 3c09ab9..9101a59 100644 --- a/src/main/kotlin/com/numberone/daepiro/domain/user/service/UserService.kt +++ b/src/main/kotlin/com/numberone/daepiro/domain/user/service/UserService.kt @@ -13,6 +13,7 @@ import com.numberone.daepiro.domain.disaster.repository.findByTypeOrThrow import com.numberone.daepiro.domain.disaster.service.DisasterService import com.numberone.daepiro.domain.user.dto.request.AddressRequest import com.numberone.daepiro.domain.user.dto.request.OnboardingRequest +import com.numberone.daepiro.domain.user.dto.request.UpdateFcmTokenRequest import com.numberone.daepiro.domain.user.dto.request.UpdateGpsRequest import com.numberone.daepiro.domain.user.dto.response.CheckNicknameResponse import com.numberone.daepiro.domain.user.dto.response.DisasterWithRegionResponse @@ -151,4 +152,14 @@ class UserService( val user = userRepository.findByIdOrThrow(userId) user.delete() } + + fun updateFcmToken(request: UpdateFcmTokenRequest, userId: Long) { + val user = userRepository.findByIdOrThrow(userId) + user.initFcmToken(request.fcmToken) + } + + fun logout(userId: Long) { + val user = userRepository.findByIdOrThrow(userId) + user.initFcmToken(null) + } }