커뮤니티 API 추가

This commit is contained in:
Klaus 2023-12-19 21:12:20 +09:00
parent 7e02acd22c
commit 8a3d11ae59
16 changed files with 747 additions and 0 deletions

View File

@ -0,0 +1,7 @@
package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity
data class CreateCommunityPostRequest(
val content: String,
val isCommentAvailable: Boolean,
val isAdult: Boolean
)

View File

@ -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
}

View File

@ -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()
)
)
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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?
)

View File

@ -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?
)

View File

@ -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?)

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
)

View File

@ -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?)

View File

@ -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
}

View File

@ -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
}
}

View File

@ -0,0 +1,3 @@
package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like
data class PostCommunityPostLikeRequest(val postId: Long)

View File

@ -0,0 +1,3 @@
package kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like
data class PostCommunityPostLikeResponse(val like: Boolean)