커뮤니티 API 추가
This commit is contained in:
		| @@ -0,0 +1,7 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| data class CreateCommunityPostRequest( | ||||
|     val content: String, | ||||
|     val isCommentAvailable: Boolean, | ||||
|     val isAdult: Boolean | ||||
| ) | ||||
| @@ -0,0 +1,24 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.BaseEntity | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import javax.persistence.Column | ||||
| import javax.persistence.Entity | ||||
| import javax.persistence.FetchType | ||||
| import javax.persistence.JoinColumn | ||||
| import javax.persistence.ManyToOne | ||||
|  | ||||
| @Entity | ||||
| data class CreatorCommunity( | ||||
|     @Column(columnDefinition = "TEXT", nullable = false) | ||||
|     var content: String, | ||||
|     var isCommentAvailable: Boolean, | ||||
|     var isAdult: Boolean, | ||||
|     @Column(nullable = true) | ||||
|     var imagePath: String? = null, | ||||
|     val isActive: Boolean = true | ||||
| ) : BaseEntity() { | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "member_id", nullable = false) | ||||
|     var member: Member? = null | ||||
| } | ||||
| @@ -0,0 +1,143 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.ApiResponse | ||||
| import kr.co.vividnext.sodalive.common.SodaException | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreateCommunityPostCommentRequest | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.ModifyCommunityPostCommentRequest | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeRequest | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import org.springframework.data.domain.Pageable | ||||
| import org.springframework.lang.Nullable | ||||
| import org.springframework.security.access.prepost.PreAuthorize | ||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | ||||
| import org.springframework.web.bind.annotation.GetMapping | ||||
| import org.springframework.web.bind.annotation.PathVariable | ||||
| import org.springframework.web.bind.annotation.PostMapping | ||||
| import org.springframework.web.bind.annotation.PutMapping | ||||
| import org.springframework.web.bind.annotation.RequestBody | ||||
| import org.springframework.web.bind.annotation.RequestParam | ||||
| import org.springframework.web.bind.annotation.RequestPart | ||||
| import org.springframework.web.bind.annotation.RestController | ||||
| import org.springframework.web.multipart.MultipartFile | ||||
|  | ||||
| @RestController("/creator-community") | ||||
| class CreatorCommunityController(private val service: CreatorCommunityService) { | ||||
|     @PostMapping | ||||
|     @PreAuthorize("hasRole('CREATOR')") | ||||
|     fun createCommunityPost( | ||||
|         @Nullable | ||||
|         @RequestPart("postImage") | ||||
|         postImage: MultipartFile?, | ||||
|         @RequestPart("request") requestString: String, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.createCommunityPost( | ||||
|                 postImage = postImage, | ||||
|                 requestString = requestString, | ||||
|                 member = member | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @PutMapping | ||||
|     @PreAuthorize("hasRole('CREATOR')") | ||||
|     fun modifyCommunityPost( | ||||
|         @Nullable | ||||
|         @RequestPart("postImage") | ||||
|         postImage: MultipartFile?, | ||||
|         @RequestPart("request") requestString: String, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.modifyCommunityPost( | ||||
|                 postImage = postImage, | ||||
|                 requestString = requestString, | ||||
|                 member = member | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @GetMapping | ||||
|     fun getCommunityPostList( | ||||
|         @RequestParam creatorId: Long, | ||||
|         @RequestParam timezone: String, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, | ||||
|         pageable: Pageable | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.getCommunityPostList( | ||||
|                 creatorId = creatorId, | ||||
|                 memberId = member.id!!, | ||||
|                 timezone = timezone, | ||||
|                 offset = pageable.offset, | ||||
|                 limit = pageable.pageSize.toLong(), | ||||
|                 isAdult = member.auth != null | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/like") | ||||
|     fun communityPostLike( | ||||
|         @RequestBody request: PostCommunityPostLikeRequest, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok(service.communityPostLike(request, member)) | ||||
|     } | ||||
|  | ||||
|     @PostMapping("/comment") | ||||
|     fun createCommunityPostComment( | ||||
|         @RequestBody request: CreateCommunityPostCommentRequest, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.createCommunityPostComment( | ||||
|                 comment = request.comment, | ||||
|                 postId = request.postId, | ||||
|                 parentId = request.parentId, | ||||
|                 member = member | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @PutMapping("/comment") | ||||
|     fun modifyCommunityPostComment( | ||||
|         @RequestBody request: ModifyCommunityPostCommentRequest, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.modifyCommunityPostComment(request = request, member = member) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @GetMapping("/{id}/comment") | ||||
|     fun getCommunityPostCommentList( | ||||
|         @PathVariable("id") postId: Long, | ||||
|         @RequestParam timezone: String, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, | ||||
|         pageable: Pageable | ||||
|     ) = run { | ||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|         ApiResponse.ok( | ||||
|             service.getCommunityPostCommentList( | ||||
|                 postId = postId, | ||||
|                 timezone = timezone, | ||||
|                 offset = pageable.offset, | ||||
|                 limit = pageable.pageSize.toLong() | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.QCreatorCommunity.creatorCommunity | ||||
| import kr.co.vividnext.sodalive.member.QMember.member | ||||
| import org.springframework.data.jpa.repository.JpaRepository | ||||
|  | ||||
| interface CreatorCommunityRepository : JpaRepository<CreatorCommunity, Long>, CreatorCommunityQueryRepository | ||||
|  | ||||
| interface CreatorCommunityQueryRepository { | ||||
|     fun findByIdAndMemberId(id: Long, memberId: Long): CreatorCommunity? | ||||
|  | ||||
|     fun getCommunityPostList( | ||||
|         creatorId: Long, | ||||
|         offset: Long, | ||||
|         limit: Long, | ||||
|         isAdult: Boolean | ||||
|     ): List<CreatorCommunity> | ||||
|  | ||||
|     fun findByIdAndActive(postId: Long): CreatorCommunity? | ||||
| } | ||||
|  | ||||
| class CreatorCommunityQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : CreatorCommunityQueryRepository { | ||||
|     override fun findByIdAndMemberId(id: Long, memberId: Long): CreatorCommunity? { | ||||
|         return queryFactory | ||||
|             .selectFrom(creatorCommunity) | ||||
|             .where( | ||||
|                 creatorCommunity.id.eq(id) | ||||
|                     .and(creatorCommunity.member.id.eq(memberId)) | ||||
|             ) | ||||
|             .fetchFirst() | ||||
|     } | ||||
|  | ||||
|     override fun getCommunityPostList( | ||||
|         creatorId: Long, | ||||
|         offset: Long, | ||||
|         limit: Long, | ||||
|         isAdult: Boolean | ||||
|     ): List<CreatorCommunity> { | ||||
|         var where = creatorCommunity.member.id.eq(creatorId) | ||||
|  | ||||
|         if (!isAdult) { | ||||
|             where = where.and(creatorCommunity.isAdult.isFalse) | ||||
|         } | ||||
|  | ||||
|         return queryFactory | ||||
|             .selectFrom(creatorCommunity) | ||||
|             .innerJoin(creatorCommunity.member, member) | ||||
|             .where(where) | ||||
|             .offset(offset) | ||||
|             .limit(limit) | ||||
|             .fetch() | ||||
|     } | ||||
|  | ||||
|     override fun findByIdAndActive(postId: Long): CreatorCommunity? { | ||||
|         return queryFactory | ||||
|             .selectFrom(creatorCommunity) | ||||
|             .where( | ||||
|                 creatorCommunity.id.eq(postId) | ||||
|                     .and(creatorCommunity.isActive.isTrue) | ||||
|             ) | ||||
|             .fetchFirst() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,277 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| import com.amazonaws.services.s3.model.ObjectMetadata | ||||
| import com.fasterxml.jackson.databind.ObjectMapper | ||||
| import kr.co.vividnext.sodalive.aws.s3.S3Uploader | ||||
| import kr.co.vividnext.sodalive.common.SodaException | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityComment | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityCommentRepository | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.GetCommunityPostCommentListResponse | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.ModifyCommunityPostCommentRequest | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLike | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLikeRepository | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeRequest | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeResponse | ||||
| import kr.co.vividnext.sodalive.fcm.FcmEvent | ||||
| import kr.co.vividnext.sodalive.fcm.FcmEventType | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import kr.co.vividnext.sodalive.utils.generateFileName | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.context.ApplicationEventPublisher | ||||
| import org.springframework.data.repository.findByIdOrNull | ||||
| import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | ||||
| import org.springframework.web.multipart.MultipartFile | ||||
| import java.time.Duration | ||||
| import java.time.LocalDateTime | ||||
|  | ||||
| @Service | ||||
| class CreatorCommunityService( | ||||
|     private val repository: CreatorCommunityRepository, | ||||
|     private val likeRepository: CreatorCommunityLikeRepository, | ||||
|     private val commentRepository: CreatorCommunityCommentRepository, | ||||
|  | ||||
|     private val s3Uploader: S3Uploader, | ||||
|     private val objectMapper: ObjectMapper, | ||||
|     private val applicationEventPublisher: ApplicationEventPublisher, | ||||
|  | ||||
|     @Value("\${cloud.aws.s3.bucket}") | ||||
|     private val imageBucket: String, | ||||
|  | ||||
|     @Value("\${cloud.aws.cloud-front.host}") | ||||
|     private val imageHost: String | ||||
| ) { | ||||
|     @Transactional | ||||
|     fun createCommunityPost(postImage: MultipartFile?, requestString: String, member: Member) { | ||||
|         val request = objectMapper.readValue(requestString, CreateCommunityPostRequest::class.java) | ||||
|  | ||||
|         val post = CreatorCommunity( | ||||
|             content = request.content, | ||||
|             isCommentAvailable = request.isCommentAvailable, | ||||
|             isAdult = request.isAdult | ||||
|         ) | ||||
|         post.member = member | ||||
|         repository.save(post) | ||||
|  | ||||
|         if (postImage != null) { | ||||
|             val metadata = ObjectMetadata() | ||||
|             metadata.contentLength = postImage.size | ||||
|  | ||||
|             // 커버 이미지 파일명 생성 | ||||
|             val imageFileName = generateFileName(prefix = "${post.id}-image") | ||||
|  | ||||
|             // 커버 이미지 업로드 | ||||
|             val imagePath = s3Uploader.upload( | ||||
|                 inputStream = postImage.inputStream, | ||||
|                 bucket = imageBucket, | ||||
|                 filePath = "creator_community/${post.id}/$imageFileName", | ||||
|                 metadata = metadata | ||||
|             ) | ||||
|  | ||||
|             post.imagePath = imagePath | ||||
|         } | ||||
|  | ||||
|         applicationEventPublisher.publishEvent( | ||||
|             FcmEvent( | ||||
|                 type = FcmEventType.CHANGE_NOTICE, | ||||
|                 title = member.nickname, | ||||
|                 message = "공지를 등록했습니다.", | ||||
|                 creatorId = member.id!! | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun modifyCommunityPost(postImage: MultipartFile?, requestString: String, member: Member) { | ||||
|         val request = objectMapper.readValue(requestString, ModifyCommunityPostRequest::class.java) | ||||
|  | ||||
|         val post = repository.findByIdAndMemberId(id = request.creatorCommunityId, memberId = member.id!!) | ||||
|             ?: throw SodaException("잘못된 요청입니다.") | ||||
|  | ||||
|         if (request.content != null) { | ||||
|             post.content = request.content | ||||
|         } | ||||
|  | ||||
|         if (request.isCommentAvailable != null) { | ||||
|             post.isCommentAvailable = request.isCommentAvailable | ||||
|         } | ||||
|  | ||||
|         if (request.isAdult != null) { | ||||
|             post.isAdult = request.isAdult | ||||
|         } | ||||
|  | ||||
|         if (postImage != null) { | ||||
|             val metadata = ObjectMetadata() | ||||
|             metadata.contentLength = postImage.size | ||||
|  | ||||
|             // 커버 이미지 파일명 생성 | ||||
|             val imageFileName = generateFileName(prefix = "${post.id}-image") | ||||
|  | ||||
|             // 커버 이미지 업로드 | ||||
|             val imagePath = s3Uploader.upload( | ||||
|                 inputStream = postImage.inputStream, | ||||
|                 bucket = imageBucket, | ||||
|                 filePath = "creator_community/${post.id}/$imageFileName", | ||||
|                 metadata = metadata | ||||
|             ) | ||||
|  | ||||
|             post.imagePath = imagePath | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun getCommunityPostList( | ||||
|         creatorId: Long, | ||||
|         memberId: Long, | ||||
|         timezone: String, | ||||
|         offset: Long, | ||||
|         limit: Long, | ||||
|         isAdult: Boolean | ||||
|     ): List<GetCommunityPostListResponse> { | ||||
|         val postList = repository.getCommunityPostList( | ||||
|             creatorId = creatorId, | ||||
|             offset = offset, | ||||
|             limit = limit, | ||||
|             isAdult = isAdult | ||||
|         ) | ||||
|  | ||||
|         return postList | ||||
|             .asSequence() | ||||
|             .map { | ||||
|                 val isLike = likeRepository.findByIdAndMemberId(id = it.id!!, memberId = memberId)?.isActive ?: false | ||||
|                 val likeCount = likeRepository.totalCountCommunityPostLikeByPostId(it.id!!) | ||||
|                 val commentCount = if (it.isCommentAvailable) { | ||||
|                     commentRepository.totalCountCommentByPostId(postId = it.id!!) | ||||
|                 } else { | ||||
|                     0 | ||||
|                 } | ||||
|                 val commentList = commentRepository.findByPostId( | ||||
|                     cloudFrontHost = imageHost, | ||||
|                     timezone = timezone, | ||||
|                     id = it.id!!, | ||||
|                     offset = 0, | ||||
|                     limit = 1 | ||||
|                 ) | ||||
|  | ||||
|                 val firstComment = if (commentList.isNotEmpty()) { | ||||
|                     commentList[0] | ||||
|                 } else { | ||||
|                     null | ||||
|                 } | ||||
|  | ||||
|                 GetCommunityPostListResponse( | ||||
|                     creatorNickname = it.member!!.nickname, | ||||
|                     creatorProfileUrl = if (it.member!!.profileImage != null) { | ||||
|                         "$imageHost/${it.member!!.profileImage}" | ||||
|                     } else { | ||||
|                         "$imageHost/profile/default-profile.png" | ||||
|                     }, | ||||
|                     imageUrl = if (it.imagePath != null) { | ||||
|                         "$imageHost/${it.imagePath!!}" | ||||
|                     } else { | ||||
|                         null | ||||
|                     }, | ||||
|                     content = it.content, | ||||
|                     date = getTimeAgoString(it.createdAt!!), | ||||
|                     isLike = isLike, | ||||
|                     likeCount = likeCount, | ||||
|                     commentCount = commentCount, | ||||
|                     firstComment = firstComment | ||||
|                 ) | ||||
|             } | ||||
|             .toList() | ||||
|     } | ||||
|  | ||||
|     private fun getTimeAgoString(dateTime: LocalDateTime): String { | ||||
|         val now = LocalDateTime.now() | ||||
|         val duration = Duration.between(dateTime, now) | ||||
|  | ||||
|         return when { | ||||
|             duration.toMinutes() < 60 -> "${duration.toMinutes()}분전" | ||||
|             duration.toHours() < 24 -> "${duration.toHours()}시간전" | ||||
|             duration.toDays() < 30 -> "${duration.toDays()}일전" | ||||
|             duration.toDays() < 365 -> "${duration.toDays() / 30}분전" | ||||
|             else -> "${duration.toDays() / 365}년전" | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun communityPostLike(request: PostCommunityPostLikeRequest, member: Member): PostCommunityPostLikeResponse { | ||||
|         var postLike = likeRepository.findByIdAndMemberId(id = request.postId, memberId = member.id!!) | ||||
|  | ||||
|         if (postLike == null) { | ||||
|             postLike = CreatorCommunityLike() | ||||
|             postLike.member = member | ||||
|  | ||||
|             val post = repository.findByIdAndActive(request.postId) | ||||
|                 ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") | ||||
|  | ||||
|             postLike.creatorCommunity = post | ||||
|             likeRepository.save(postLike) | ||||
|         } else { | ||||
|             postLike.isActive = !postLike.isActive | ||||
|         } | ||||
|  | ||||
|         return PostCommunityPostLikeResponse(like = postLike.isActive) | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun createCommunityPostComment(comment: String, postId: Long, parentId: Long?, member: Member) { | ||||
|         val post = repository.findByIdOrNull(id = postId) | ||||
|             ?: throw SodaException("잘못된 게시물 입니다.\n다시 시도해 주세요.") | ||||
|  | ||||
|         val postComment = CreatorCommunityComment(comment = comment) | ||||
|         postComment.creatorCommunity = post | ||||
|         postComment.member = member | ||||
|  | ||||
|         val parent = if (parentId != null) { | ||||
|             commentRepository.findByIdOrNull(id = parentId) | ||||
|         } else { | ||||
|             null | ||||
|         } | ||||
|  | ||||
|         if (parent != null) { | ||||
|             postComment.parent = parent | ||||
|         } | ||||
|  | ||||
|         commentRepository.save(postComment) | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun modifyCommunityPostComment(request: ModifyCommunityPostCommentRequest, member: Member) { | ||||
|         val postComment = commentRepository.findByIdOrNull(id = request.commentId) | ||||
|             ?: throw SodaException("잘못된 접근 입니다.\n확인 후 다시 시도해 주세요.") | ||||
|  | ||||
|         if (request.comment != null && postComment.member!!.id!! == member.id!!) { | ||||
|             postComment.comment = request.comment | ||||
|         } | ||||
|  | ||||
|         if ( | ||||
|             request.isActive != null && | ||||
|             ( | ||||
|                 postComment.member!!.id!! == member.id!! || | ||||
|                     postComment.creatorCommunity!!.member!!.id!! == member.id!! | ||||
|                 ) | ||||
|         ) { | ||||
|             postComment.isActive = request.isActive | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun getCommunityPostCommentList( | ||||
|         postId: Long, | ||||
|         timezone: String, | ||||
|         offset: Long, | ||||
|         limit: Long | ||||
|     ): GetCommunityPostCommentListResponse { | ||||
|         val commentList = commentRepository.findByPostId( | ||||
|             cloudFrontHost = imageHost, | ||||
|             timezone = timezone, | ||||
|             id = postId, | ||||
|             offset = offset, | ||||
|             limit = limit | ||||
|         ) | ||||
|  | ||||
|         val totalCount = commentRepository.totalCountCommentByPostId(postId = postId) | ||||
|  | ||||
|         return GetCommunityPostCommentListResponse(totalCount = totalCount, items = commentList) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.GetCommunityPostCommentListItem | ||||
|  | ||||
| data class GetCommunityPostListResponse @QueryProjection constructor( | ||||
|     val creatorNickname: String, | ||||
|     val creatorProfileUrl: String, | ||||
|     val imageUrl: String?, | ||||
|     val content: String, | ||||
|     val date: String, | ||||
|     val isLike: Boolean, | ||||
|     val likeCount: Int, | ||||
|     val commentCount: Int, | ||||
|     val firstComment: GetCommunityPostCommentListItem? | ||||
| ) | ||||
| @@ -0,0 +1,8 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity | ||||
|  | ||||
| data class ModifyCommunityPostRequest( | ||||
|     val creatorCommunityId: Long, | ||||
|     val content: String?, | ||||
|     val isCommentAvailable: Boolean?, | ||||
|     val isAdult: Boolean? | ||||
| ) | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment | ||||
|  | ||||
| data class CreateCommunityPostCommentRequest(val comment: String, val postId: Long, val parentId: Long?) | ||||
| @@ -0,0 +1,37 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.BaseEntity | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import javax.persistence.Column | ||||
| import javax.persistence.Entity | ||||
| import javax.persistence.FetchType | ||||
| import javax.persistence.JoinColumn | ||||
| import javax.persistence.ManyToOne | ||||
| import javax.persistence.OneToMany | ||||
|  | ||||
| @Entity | ||||
| data class CreatorCommunityComment( | ||||
|     @Column(columnDefinition = "TEXT", nullable = false) | ||||
|     var comment: String, | ||||
|     var isActive: Boolean = true | ||||
| ) : BaseEntity() { | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "parent_id", nullable = true) | ||||
|     var parent: CreatorCommunityComment? = null | ||||
|         set(value) { | ||||
|             value?.children?.add(this) | ||||
|             field = value | ||||
|         } | ||||
|  | ||||
|     @OneToMany(mappedBy = "parent") | ||||
|     var children: MutableList<CreatorCommunityComment> = mutableListOf() | ||||
|  | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "member_id", nullable = false) | ||||
|     var member: Member? = null | ||||
|  | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "creator_community_id", nullable = false) | ||||
|     var creatorCommunity: CreatorCommunity? = null | ||||
| } | ||||
| @@ -0,0 +1,91 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment | ||||
|  | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.QCreatorCommunityComment.creatorCommunityComment | ||||
| import org.springframework.data.jpa.repository.JpaRepository | ||||
| import java.time.ZoneId | ||||
| import java.time.format.DateTimeFormatter | ||||
|  | ||||
| interface CreatorCommunityCommentRepository : | ||||
|     JpaRepository<CreatorCommunityComment, Long>, CreatorCommunityCommentQueryRepository | ||||
|  | ||||
| interface CreatorCommunityCommentQueryRepository { | ||||
|     fun findByPostId( | ||||
|         cloudFrontHost: String, | ||||
|         timezone: String, | ||||
|         id: Long, | ||||
|         offset: Long, | ||||
|         limit: Long | ||||
|     ): List<GetCommunityPostCommentListItem> | ||||
|  | ||||
|     fun commentReplyCountByCommentId(commentId: Long): Int | ||||
|  | ||||
|     fun totalCountCommentByPostId(postId: Long): Int | ||||
| } | ||||
|  | ||||
| class CreatorCommunityCommentQueryRepositoryImpl( | ||||
|     private val queryFactory: JPAQueryFactory | ||||
| ) : CreatorCommunityCommentQueryRepository { | ||||
|     override fun findByPostId( | ||||
|         cloudFrontHost: String, | ||||
|         timezone: String, | ||||
|         id: Long, | ||||
|         offset: Long, | ||||
|         limit: Long | ||||
|     ): List<GetCommunityPostCommentListItem> { | ||||
|         return queryFactory | ||||
|             .selectFrom(creatorCommunityComment) | ||||
|             .where( | ||||
|                 creatorCommunityComment.isActive.isTrue | ||||
|                     .and(creatorCommunityComment.creatorCommunity.id.eq(id)) | ||||
|             ) | ||||
|             .offset(offset) | ||||
|             .limit(limit) | ||||
|             .fetch() | ||||
|             .asSequence() | ||||
|             .map { | ||||
|                 val date = it.createdAt!! | ||||
|                     .atZone(ZoneId.of("UTC")) | ||||
|                     .withZoneSameInstant(ZoneId.of(timezone)) | ||||
|  | ||||
|                 GetCommunityPostCommentListItem( | ||||
|                     id = it.id!!, | ||||
|                     writerId = it.member!!.id!!, | ||||
|                     nickname = it.member!!.nickname, | ||||
|                     profileUrl = if (it.member!!.profileImage != null) { | ||||
|                         "$cloudFrontHost/${it.member!!.profileImage}" | ||||
|                     } else { | ||||
|                         "$cloudFrontHost/profile/default-profile.png" | ||||
|                     }, | ||||
|                     comment = it.comment, | ||||
|                     date = date.format(DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")), | ||||
|                     replyCount = commentReplyCountByCommentId(it.id!!) | ||||
|                 ) | ||||
|             } | ||||
|             .toList() | ||||
|     } | ||||
|  | ||||
|     override fun commentReplyCountByCommentId(commentId: Long): Int { | ||||
|         return queryFactory.select(creatorCommunityComment.id) | ||||
|             .from(creatorCommunityComment) | ||||
|             .where( | ||||
|                 creatorCommunityComment.isActive.isTrue | ||||
|                     .and(creatorCommunityComment.parent.isNotNull) | ||||
|                     .and(creatorCommunityComment.parent.id.eq(commentId)) | ||||
|             ) | ||||
|             .fetch() | ||||
|             .size | ||||
|     } | ||||
|  | ||||
|     override fun totalCountCommentByPostId(postId: Long): Int { | ||||
|         return queryFactory.select(creatorCommunityComment.id) | ||||
|             .from(creatorCommunityComment) | ||||
|             .where( | ||||
|                 creatorCommunityComment.creatorCommunity.id.eq(postId) | ||||
|                     .and(creatorCommunityComment.isActive.isTrue) | ||||
|                     .and(creatorCommunityComment.parent.isNull) | ||||
|             ) | ||||
|             .fetch() | ||||
|             .size | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment | ||||
|  | ||||
| data class GetCommunityPostCommentListResponse( | ||||
|     val totalCount: Int, | ||||
|     val items: List<GetCommunityPostCommentListItem> | ||||
| ) | ||||
|  | ||||
| data class GetCommunityPostCommentListItem( | ||||
|     val id: Long, | ||||
|     val writerId: Long, | ||||
|     val nickname: String, | ||||
|     val profileUrl: String, | ||||
|     val comment: String, | ||||
|     val date: String, | ||||
|     val replyCount: Int | ||||
| ) | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment | ||||
|  | ||||
| data class ModifyCommunityPostCommentRequest(val commentId: Long, val comment: String?, val isActive: Boolean?) | ||||
| @@ -0,0 +1,22 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.BaseEntity | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import javax.persistence.Entity | ||||
| import javax.persistence.FetchType | ||||
| import javax.persistence.JoinColumn | ||||
| import javax.persistence.ManyToOne | ||||
|  | ||||
| @Entity | ||||
| data class CreatorCommunityLike( | ||||
|     var isActive: Boolean = true | ||||
| ) : BaseEntity() { | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "member_id", nullable = false) | ||||
|     var member: Member? = null | ||||
|  | ||||
|     @ManyToOne(fetch = FetchType.LAZY) | ||||
|     @JoinColumn(name = "creator_community_id", nullable = false) | ||||
|     var creatorCommunity: CreatorCommunity? = null | ||||
| } | ||||
| @@ -0,0 +1,30 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like | ||||
|  | ||||
| import com.querydsl.jpa.impl.JPAQueryFactory | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.QCreatorCommunityLike.creatorCommunityLike | ||||
| import org.springframework.data.jpa.repository.JpaRepository | ||||
|  | ||||
| interface CreatorCommunityLikeRepository : | ||||
|     JpaRepository<CreatorCommunityLike, Long>, CreatorCommunityLikeQueryRepository { | ||||
|     fun findByIdAndMemberId(id: Long, memberId: Long): CreatorCommunityLike? | ||||
| } | ||||
|  | ||||
| interface CreatorCommunityLikeQueryRepository { | ||||
|     fun totalCountCommunityPostLikeByPostId(postId: Long): Int | ||||
| } | ||||
|  | ||||
| class CreatorCommunityLikeQueryRepositoryImpl( | ||||
|     private val queryFactory: JPAQueryFactory | ||||
| ) : CreatorCommunityLikeQueryRepository { | ||||
|     override fun totalCountCommunityPostLikeByPostId(postId: Long): Int { | ||||
|         return queryFactory | ||||
|             .select(creatorCommunityLike.id) | ||||
|             .from(creatorCommunityLike) | ||||
|             .where( | ||||
|                 creatorCommunityLike.isActive.isTrue | ||||
|                     .and(creatorCommunityLike.creatorCommunity.id.eq(postId)) | ||||
|             ) | ||||
|             .fetch() | ||||
|             .size | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like | ||||
|  | ||||
| data class PostCommunityPostLikeRequest(val postId: Long) | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like | ||||
|  | ||||
| data class PostCommunityPostLikeResponse(val like: Boolean) | ||||
		Reference in New Issue
	
	Block a user