From 8a3d11ae59571aa6b6b4ffcdb38abab1c23b3e1f Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 19 Dec 2023 21:12:20 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreateCommunityPostRequest.kt | 7 + .../creatorCommunity/CreatorCommunity.kt | 24 ++ .../CreatorCommunityController.kt | 143 +++++++++ .../CreatorCommunityRepository.kt | 64 ++++ .../CreatorCommunityService.kt | 277 ++++++++++++++++++ .../GetCommunityPostListResponse.kt | 16 + .../ModifyCommunityPostRequest.kt | 8 + .../CreateCommunityPostCommentRequest.kt | 3 + .../comment/CreatorCommunityComment.kt | 37 +++ .../CreatorCommunityCommentRepository.kt | 91 ++++++ .../GetCommunityPostCommentListResponse.kt | 16 + .../ModifyCommunityPostCommentRequest.kt | 3 + .../like/CreatorCommunityLike.kt | 22 ++ .../like/CreatorCommunityLikeRepository.kt | 30 ++ .../like/PostCommunityPostLikeRequest.kt | 3 + .../like/PostCommunityPostLikeResponse.kt | 3 + 16 files changed, 747 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreateCommunityPostRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunity.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/GetCommunityPostListResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/ModifyCommunityPostRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreateCommunityPostCommentRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityComment.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/GetCommunityPostCommentListResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/ModifyCommunityPostCommentRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLike.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLikeRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreateCommunityPostRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreateCommunityPostRequest.kt new file mode 100644 index 0000000..d6320e4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreateCommunityPostRequest.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity + +data class CreateCommunityPostRequest( + val content: String, + val isCommentAvailable: Boolean, + val isAdult: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunity.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunity.kt new file mode 100644 index 0000000..70be7d0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunity.kt @@ -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 +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityController.kt new file mode 100644 index 0000000..4aa66a6 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityController.kt @@ -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() + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityRepository.kt new file mode 100644 index 0000000..d1e2089 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityRepository.kt @@ -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, CreatorCommunityQueryRepository + +interface CreatorCommunityQueryRepository { + fun findByIdAndMemberId(id: Long, memberId: Long): CreatorCommunity? + + fun getCommunityPostList( + creatorId: Long, + offset: Long, + limit: Long, + isAdult: Boolean + ): List + + 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 { + 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() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt new file mode 100644 index 0000000..67ada02 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt @@ -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 { + 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) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/GetCommunityPostListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/GetCommunityPostListResponse.kt new file mode 100644 index 0000000..f062e40 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/GetCommunityPostListResponse.kt @@ -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? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/ModifyCommunityPostRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/ModifyCommunityPostRequest.kt new file mode 100644 index 0000000..58ae72a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/ModifyCommunityPostRequest.kt @@ -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? +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreateCommunityPostCommentRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreateCommunityPostCommentRequest.kt new file mode 100644 index 0000000..5e833d6 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreateCommunityPostCommentRequest.kt @@ -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?) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityComment.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityComment.kt new file mode 100644 index 0000000..6e65c0a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityComment.kt @@ -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 = 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 +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt new file mode 100644 index 0000000..ccd52bf --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/CreatorCommunityCommentRepository.kt @@ -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, CreatorCommunityCommentQueryRepository + +interface CreatorCommunityCommentQueryRepository { + fun findByPostId( + cloudFrontHost: String, + timezone: String, + id: Long, + offset: Long, + limit: Long + ): List + + 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 { + 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 + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/GetCommunityPostCommentListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/GetCommunityPostCommentListResponse.kt new file mode 100644 index 0000000..58c15a4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/GetCommunityPostCommentListResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment + +data class GetCommunityPostCommentListResponse( + val totalCount: Int, + val items: List +) + +data class GetCommunityPostCommentListItem( + val id: Long, + val writerId: Long, + val nickname: String, + val profileUrl: String, + val comment: String, + val date: String, + val replyCount: Int +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/ModifyCommunityPostCommentRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/ModifyCommunityPostCommentRequest.kt new file mode 100644 index 0000000..0a363fd --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/comment/ModifyCommunityPostCommentRequest.kt @@ -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?) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLike.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLike.kt new file mode 100644 index 0000000..36ea0d0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLike.kt @@ -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 +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLikeRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLikeRepository.kt new file mode 100644 index 0000000..6d9668d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/CreatorCommunityLikeRepository.kt @@ -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, 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 + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeRequest.kt new file mode 100644 index 0000000..53a5c48 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeRequest.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like + +data class PostCommunityPostLikeRequest(val postId: Long) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeResponse.kt new file mode 100644 index 0000000..199e6b5 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/like/PostCommunityPostLikeResponse.kt @@ -0,0 +1,3 @@ +package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like + +data class PostCommunityPostLikeResponse(val like: Boolean)