크리에이터 채널 API
This commit is contained in:
parent
049e1c41de
commit
c25b105d4d
|
@ -0,0 +1,15 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class CreatorResponse(
|
||||||
|
val creatorId: Long,
|
||||||
|
val profileUrl: String,
|
||||||
|
val nickname: String,
|
||||||
|
val tags: List<String>,
|
||||||
|
val introduce: String = "",
|
||||||
|
val instagramUrl: String? = null,
|
||||||
|
val youtubeUrl: String? = null,
|
||||||
|
val websiteUrl: String? = null,
|
||||||
|
val blogUrl: String? = null,
|
||||||
|
val isNotification: Boolean,
|
||||||
|
val notificationRecipientCount: Int
|
||||||
|
)
|
|
@ -2,9 +2,18 @@ package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
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.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RequestParam
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
@ -29,4 +38,52 @@ class ExplorerController(private val service: ExplorerService) {
|
||||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
ApiResponse.ok(service.getSearchChannel(channel, member))
|
ApiResponse.ok(service.getSearchChannel(channel, member))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/profile/{id}")
|
||||||
|
fun getCreatorProfile(
|
||||||
|
@PathVariable("id") creatorId: Long,
|
||||||
|
@RequestParam timezone: String,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(service.getCreatorProfile(creatorId, timezone, member))
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/profile/cheers")
|
||||||
|
fun writeCheers(
|
||||||
|
@RequestBody request: PostWriteCheersRequest,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(service.writeCheers(request, member), "등록되었습니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/profile/cheers")
|
||||||
|
fun modifyCheers(
|
||||||
|
@RequestBody request: PutWriteCheersRequest,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(service.modifyCheers(request, member), "수정되었습니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/profile/notice")
|
||||||
|
@PreAuthorize("hasAnyRole('CREATOR')")
|
||||||
|
fun postCreatorNotice(
|
||||||
|
@RequestBody request: PostCreatorNoticeRequest,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(service.saveNotice(member, request.notice), "공지사항이 저장되었습니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/profile/{id}/follower-list")
|
||||||
|
fun getFollowerList(
|
||||||
|
@PathVariable("id") creatorId: Long,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(service.getFollowerList(creatorId, member, pageable))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,40 @@
|
||||||
package kr.co.vividnext.sodalive.explorer
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
import com.querydsl.core.types.Predicate
|
||||||
|
import com.querydsl.core.types.Projections
|
||||||
import com.querydsl.core.types.dsl.Expressions
|
import com.querydsl.core.types.dsl.Expressions
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||||
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
|
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListDto
|
||||||
|
import kr.co.vividnext.sodalive.explorer.follower.QGetFollowerListDto
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.ChannelNotice
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.QChannelNotice.channelNotice
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.QCreatorCheers.creatorCheers
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.TimeDifferenceResult
|
||||||
import kr.co.vividnext.sodalive.explorer.section.ExplorerSection
|
import kr.co.vividnext.sodalive.explorer.section.ExplorerSection
|
||||||
import kr.co.vividnext.sodalive.explorer.section.QExplorerSection.explorerSection
|
import kr.co.vividnext.sodalive.explorer.section.QExplorerSection.explorerSection
|
||||||
|
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||||
|
import kr.co.vividnext.sodalive.live.room.LiveRoomType
|
||||||
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||||
|
import kr.co.vividnext.sodalive.live.room.cancel.QLiveRoomCancel.liveRoomCancel
|
||||||
|
import kr.co.vividnext.sodalive.live.room.visit.QLiveRoomVisit.liveRoomVisit
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.QMember
|
import kr.co.vividnext.sodalive.member.QMember
|
||||||
import kr.co.vividnext.sodalive.member.QMember.member
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
|
||||||
import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing
|
import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing
|
||||||
import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag
|
import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag
|
||||||
import kr.co.vividnext.sodalive.member.tag.QMemberCreatorTag.memberCreatorTag
|
import kr.co.vividnext.sodalive.member.tag.QMemberCreatorTag.memberCreatorTag
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class ExplorerQueryRepository(
|
class ExplorerQueryRepository(
|
||||||
|
@ -141,4 +159,442 @@ class ExplorerQueryRepository(
|
||||||
)
|
)
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAccount(userId: Long): Member? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(member)
|
||||||
|
.where(member.id.eq(userId))
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserDonationRanking(
|
||||||
|
creatorId: Long,
|
||||||
|
limit: Long,
|
||||||
|
offset: Long = 0,
|
||||||
|
withDonationCan: Boolean
|
||||||
|
): List<UserDonationRankingResponse> {
|
||||||
|
val creatorMember = QMember("creator")
|
||||||
|
val userMember = QMember("user")
|
||||||
|
|
||||||
|
val donation = useCan.rewardCan.add(useCan.can).sum()
|
||||||
|
return queryFactory
|
||||||
|
.select(userMember, donation)
|
||||||
|
.from(useCan)
|
||||||
|
.join(useCan.room, liveRoom)
|
||||||
|
.join(liveRoom.member, creatorMember)
|
||||||
|
.join(useCan.member, userMember)
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.where(
|
||||||
|
useCan.canUsage.eq(CanUsage.DONATION)
|
||||||
|
.and(useCan.isRefund.isFalse)
|
||||||
|
.and(creatorMember.id.eq(creatorId))
|
||||||
|
)
|
||||||
|
.groupBy(useCan.member.id)
|
||||||
|
.orderBy(donation.desc(), userMember.id.desc())
|
||||||
|
.fetch()
|
||||||
|
.map {
|
||||||
|
val account = it.get(userMember)!!
|
||||||
|
val donationCan = it.get(donation)!!
|
||||||
|
UserDonationRankingResponse(
|
||||||
|
account.id!!,
|
||||||
|
account.nickname,
|
||||||
|
if (account.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${account.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
if (withDonationCan) donationCan else 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSimilarCreatorList(creatorId: Long): List<SimilarCreatorResponse> {
|
||||||
|
val creator = queryFactory
|
||||||
|
.selectFrom(member)
|
||||||
|
.where(member.id.eq(creatorId))
|
||||||
|
.fetchFirst() ?: throw SodaException("없는 사용자 입니다.")
|
||||||
|
|
||||||
|
val creatorTagCount = creator.tags
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.tag.isActive }
|
||||||
|
.map { it.tag }
|
||||||
|
.toList().size
|
||||||
|
|
||||||
|
if (creatorTagCount <= 0) {
|
||||||
|
val where = member.role.eq(MemberRole.CREATOR)
|
||||||
|
.and(member.id.ne(creatorId))
|
||||||
|
.and(member.tags.size().gt(0))
|
||||||
|
.and(memberCreatorTag.tag.isActive.isTrue)
|
||||||
|
|
||||||
|
return getRandomCreatorList(where = where, limit = 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cnt = memberCreatorTag.member.count()
|
||||||
|
val similarCreators = queryFactory
|
||||||
|
.select(member, cnt)
|
||||||
|
.from(memberCreatorTag)
|
||||||
|
.innerJoin(memberCreatorTag.member, member)
|
||||||
|
.innerJoin(memberCreatorTag.tag, creatorTag)
|
||||||
|
.leftJoin(member.auth, auth)
|
||||||
|
.where(
|
||||||
|
member.role.eq(MemberRole.CREATOR)
|
||||||
|
.and(member.id.ne(creatorId))
|
||||||
|
.and(
|
||||||
|
memberCreatorTag.tag.`in`(
|
||||||
|
creator.tags
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.tag.isActive }
|
||||||
|
.map { it.tag }
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.groupBy(memberCreatorTag.member.id)
|
||||||
|
.orderBy(cnt.desc())
|
||||||
|
.limit(3)
|
||||||
|
.fetch()
|
||||||
|
.map {
|
||||||
|
val account = it.get(member)!!
|
||||||
|
SimilarCreatorResponse(
|
||||||
|
account.id!!,
|
||||||
|
account.nickname,
|
||||||
|
if (account.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${account.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
account.tags
|
||||||
|
.asSequence()
|
||||||
|
.filter { accountCounselorTag -> accountCounselorTag.tag.isActive }
|
||||||
|
.map { accountCounselorTag -> accountCounselorTag.tag.tag }
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (similarCreators.size < 3) {
|
||||||
|
val userIds = similarCreators.map { it.userId }
|
||||||
|
var where = member.role.eq(MemberRole.CREATOR)
|
||||||
|
.and(member.id.ne(creatorId))
|
||||||
|
.and(member.tags.size().gt(0))
|
||||||
|
.and(memberCreatorTag.tag.isActive.isTrue)
|
||||||
|
|
||||||
|
for (userId in userIds) {
|
||||||
|
where = where.and(member.id.ne(userId))
|
||||||
|
}
|
||||||
|
|
||||||
|
val additionalCreator = getRandomCreatorList(where = where, limit = (3 - similarCreators.size).toLong())
|
||||||
|
return similarCreators + additionalCreator
|
||||||
|
} else {
|
||||||
|
return similarCreators
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRandomCreatorList(where: Predicate, limit: Long): List<SimilarCreatorResponse> {
|
||||||
|
val result = queryFactory
|
||||||
|
.select(member)
|
||||||
|
.from(memberCreatorTag)
|
||||||
|
.join(memberCreatorTag.member, member)
|
||||||
|
.innerJoin(memberCreatorTag.tag, creatorTag)
|
||||||
|
.leftJoin(member.auth, auth)
|
||||||
|
.groupBy(memberCreatorTag.member.id)
|
||||||
|
.where(where)
|
||||||
|
.limit(limit)
|
||||||
|
.orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
|
||||||
|
.fetch()
|
||||||
|
.map {
|
||||||
|
SimilarCreatorResponse(
|
||||||
|
it.id!!,
|
||||||
|
it.nickname,
|
||||||
|
if (it.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${it.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
it.tags
|
||||||
|
.asSequence()
|
||||||
|
.filter { accountCounselorTag -> accountCounselorTag.tag.isActive }
|
||||||
|
.map { accountCounselorTag -> accountCounselorTag.tag.tag }
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveRoomList(
|
||||||
|
creatorId: Long,
|
||||||
|
userMember: Member,
|
||||||
|
timezone: String,
|
||||||
|
limit: Int,
|
||||||
|
offset: Long = 0
|
||||||
|
): List<LiveRoomResponse> {
|
||||||
|
var where = liveRoom.member.id.eq(creatorId)
|
||||||
|
.and(liveRoom.cancel.id.isNull)
|
||||||
|
.and(liveRoom.isActive.isTrue)
|
||||||
|
|
||||||
|
if (userMember.auth == null) {
|
||||||
|
where = where.and(liveRoom.isAdult.isFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = mutableListOf<LiveRoom>()
|
||||||
|
|
||||||
|
if (offset == 0L) {
|
||||||
|
result.addAll(
|
||||||
|
queryFactory
|
||||||
|
.selectFrom(liveRoom)
|
||||||
|
.innerJoin(liveRoom.member, member)
|
||||||
|
.leftJoin(liveRoom.cancel, liveRoomCancel)
|
||||||
|
.where(where)
|
||||||
|
.orderBy(liveRoom.beginDateTime.asc())
|
||||||
|
.fetch()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
.map {
|
||||||
|
val reservations = it.reservations
|
||||||
|
.filter { reservation ->
|
||||||
|
reservation.member!!.id!! == userMember.id!! && reservation.isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
val beginDateTime = it.beginDateTime
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of(timezone))
|
||||||
|
|
||||||
|
val isPaid = if (it.channelName != null) {
|
||||||
|
val useCan = queryFactory
|
||||||
|
.selectFrom(useCan)
|
||||||
|
.where(
|
||||||
|
useCan.member.id.eq(member.id)
|
||||||
|
.and(useCan.room.id.eq(it.id!!))
|
||||||
|
.and(useCan.canUsage.eq(CanUsage.LIVE))
|
||||||
|
)
|
||||||
|
.orderBy(useCan.id.desc())
|
||||||
|
.fetchFirst()
|
||||||
|
|
||||||
|
useCan != null
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveRoomResponse(
|
||||||
|
roomId = it.id!!,
|
||||||
|
title = it.title,
|
||||||
|
content = it.notice,
|
||||||
|
isPaid = isPaid,
|
||||||
|
beginDateTime = beginDateTime.format(
|
||||||
|
DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")
|
||||||
|
),
|
||||||
|
isAdult = it.isAdult,
|
||||||
|
price = it.price,
|
||||||
|
channelName = it.channelName,
|
||||||
|
managerNickname = it.member!!.nickname,
|
||||||
|
coverImageUrl = if (it.coverImage!!.startsWith("https://")) {
|
||||||
|
it.coverImage!!
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/${it.coverImage!!}"
|
||||||
|
},
|
||||||
|
isReservation = reservations.isNotEmpty(),
|
||||||
|
isActive = it.isActive,
|
||||||
|
isPrivateRoom = it.type == LiveRoomType.PRIVATE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNoticeString(creatorId: Long): String {
|
||||||
|
return queryFactory
|
||||||
|
.select(channelNotice.notice)
|
||||||
|
.from(channelNotice)
|
||||||
|
.where(channelNotice.member.id.eq(creatorId))
|
||||||
|
.fetchFirst() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCheersList(creatorId: Long, timezone: String, offset: Long, limit: Long): GetCheersResponse {
|
||||||
|
val totalCount = queryFactory
|
||||||
|
.selectFrom(creatorCheers)
|
||||||
|
.where(
|
||||||
|
creatorCheers.creator.id.eq(creatorId)
|
||||||
|
.and(creatorCheers.isActive.isTrue)
|
||||||
|
.and(creatorCheers.parent.isNull)
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.count()
|
||||||
|
|
||||||
|
val cheers = queryFactory
|
||||||
|
.selectFrom(creatorCheers)
|
||||||
|
.where(
|
||||||
|
creatorCheers.creator.id.eq(creatorId)
|
||||||
|
.and(creatorCheers.isActive.isTrue)
|
||||||
|
.and(creatorCheers.parent.isNull)
|
||||||
|
)
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.orderBy(creatorCheers.id.desc())
|
||||||
|
.fetch()
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val date = it.createdAt!!
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of(timezone))
|
||||||
|
|
||||||
|
GetCheersResponseItem(
|
||||||
|
cheersId = it.id!!,
|
||||||
|
nickname = it.member!!.nickname,
|
||||||
|
profileUrl = if (it.member!!.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${it.member!!.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
content = it.cheers,
|
||||||
|
date = date.format(DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")),
|
||||||
|
replyList = it.children.asSequence()
|
||||||
|
.map { cheers ->
|
||||||
|
val replyDate = cheers.createdAt!!
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of(timezone))
|
||||||
|
|
||||||
|
GetCheersResponseItem(
|
||||||
|
cheersId = cheers.id!!,
|
||||||
|
nickname = cheers.member!!.nickname,
|
||||||
|
profileUrl = if (cheers.member!!.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${cheers.member!!.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
content = cheers.cheers,
|
||||||
|
date = replyDate.format(DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")),
|
||||||
|
replyList = listOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetCheersResponse(
|
||||||
|
totalCount = totalCount,
|
||||||
|
cheers = cheers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveCount(creatorId: Long): Long? {
|
||||||
|
return queryFactory
|
||||||
|
.select(liveRoom.id.count())
|
||||||
|
.from(liveRoom)
|
||||||
|
.where(
|
||||||
|
liveRoom.member.id.eq(creatorId)
|
||||||
|
.and(liveRoom.channelName.isNotNull)
|
||||||
|
)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveTime(creatorId: Long): Long {
|
||||||
|
val diffs = queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
TimeDifferenceResult::class.java,
|
||||||
|
liveRoom.beginDateTime,
|
||||||
|
liveRoom.updatedAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(liveRoom)
|
||||||
|
.where(
|
||||||
|
liveRoom.member.id.eq(creatorId)
|
||||||
|
.and(liveRoom.channelName.isNotNull)
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
|
||||||
|
return diffs
|
||||||
|
.asSequence()
|
||||||
|
.map { Duration.between(it.beginDateTime, it.updatedAt).toSeconds() }
|
||||||
|
.sum() / 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLiveContributorCount(creatorId: Long): Long? {
|
||||||
|
return queryFactory
|
||||||
|
.select(liveRoomVisit.member.count())
|
||||||
|
.from(liveRoomVisit)
|
||||||
|
.innerJoin(liveRoomVisit.room, liveRoom)
|
||||||
|
.where(
|
||||||
|
liveRoom.member.id.eq(creatorId)
|
||||||
|
.and(liveRoom.channelName.isNotNull)
|
||||||
|
)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFollowerListTotalCount(creatorId: Long): Int {
|
||||||
|
return queryFactory.select(creatorFollowing.id)
|
||||||
|
.from(creatorFollowing)
|
||||||
|
.innerJoin(creatorFollowing.member, member)
|
||||||
|
.where(
|
||||||
|
member.isActive.isTrue
|
||||||
|
.and(creatorFollowing.isActive.isTrue)
|
||||||
|
.and(creatorFollowing.creator.id.eq(creatorId))
|
||||||
|
.and(creatorFollowing.member.id.ne(creatorId))
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFollowerList(
|
||||||
|
creatorId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): List<GetFollowerListDto> {
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QGetFollowerListDto(
|
||||||
|
member.id,
|
||||||
|
member.profileImage.prepend("/").prepend(cloudFrontHost),
|
||||||
|
member.nickname,
|
||||||
|
member.role
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(creatorFollowing)
|
||||||
|
.innerJoin(creatorFollowing.member, member)
|
||||||
|
.where(
|
||||||
|
member.isActive.isTrue
|
||||||
|
.and(creatorFollowing.isActive.isTrue)
|
||||||
|
.and(creatorFollowing.creator.id.eq(creatorId))
|
||||||
|
.and(creatorFollowing.member.id.ne(creatorId))
|
||||||
|
)
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isFollow(creatorId: Long, memberId: Long): Boolean {
|
||||||
|
return queryFactory
|
||||||
|
.select(creatorFollowing.isActive)
|
||||||
|
.from(creatorFollowing)
|
||||||
|
.where(
|
||||||
|
creatorFollowing.creator.id.eq(creatorId)
|
||||||
|
.and(creatorFollowing.member.id.eq(memberId))
|
||||||
|
)
|
||||||
|
.fetchOne() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCreatorCheers(cheersId: Long): CreatorCheers? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(creatorCheers)
|
||||||
|
.where(creatorCheers.id.eq(cheersId))
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCheers(cheersId: Long, memberId: Long): CreatorCheers? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(creatorCheers)
|
||||||
|
.where(
|
||||||
|
creatorCheers.id.eq(cheersId)
|
||||||
|
.and(creatorCheers.member.id.eq(memberId))
|
||||||
|
)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNotice(creatorId: Long): ChannelNotice? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(channelNotice)
|
||||||
|
.where(channelNotice.member.id.eq(creatorId))
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
package kr.co.vividnext.sodalive.explorer
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponse
|
||||||
|
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListResponseItem
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.ChannelNotice
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.ChannelNoticeRepository
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.CreatorCheersRepository
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest
|
||||||
|
import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
|
||||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.MemberService
|
import kr.co.vividnext.sodalive.member.MemberService
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
|
@ -13,6 +23,8 @@ import org.springframework.transaction.annotation.Transactional
|
||||||
class ExplorerService(
|
class ExplorerService(
|
||||||
private val memberService: MemberService,
|
private val memberService: MemberService,
|
||||||
private val queryRepository: ExplorerQueryRepository,
|
private val queryRepository: ExplorerQueryRepository,
|
||||||
|
private val cheersRepository: CreatorCheersRepository,
|
||||||
|
private val noticeRepository: ChannelNoticeRepository,
|
||||||
|
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val cloudFrontHost: String
|
private val cloudFrontHost: String
|
||||||
|
@ -142,4 +154,156 @@ class ExplorerService(
|
||||||
.map { GetRoomDetailUser(it, cloudFrontHost) }
|
.map { GetRoomDetailUser(it, cloudFrontHost) }
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCreatorProfile(creatorId: Long, timezone: String, member: Member): GetCreatorProfileResponse {
|
||||||
|
// 크리에이터(유저) 정보
|
||||||
|
val creatorAccount = queryRepository.getAccount(creatorId) ?: throw SodaException("없는 사용자 입니다.")
|
||||||
|
|
||||||
|
// 차단된 사용자 체크
|
||||||
|
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = creatorId)
|
||||||
|
if (isBlocked) throw SodaException("${creatorAccount.nickname}님의 요청으로 채널 접근이 제한됩니다.")
|
||||||
|
|
||||||
|
val notificationUserIds = queryRepository.getNotificationUserIds(creatorId)
|
||||||
|
val isNotification = notificationUserIds.contains(member.id)
|
||||||
|
val notificationRecipientCount = notificationUserIds.size
|
||||||
|
|
||||||
|
// 후원랭킹
|
||||||
|
val userDonationRanking = queryRepository.getUserDonationRanking(
|
||||||
|
creatorId,
|
||||||
|
10,
|
||||||
|
withDonationCan = creatorId == member.id!!
|
||||||
|
)
|
||||||
|
|
||||||
|
// 추천 크리에이터
|
||||||
|
val similarCreatorList = queryRepository.getSimilarCreatorList(creatorId)
|
||||||
|
|
||||||
|
// 라이브
|
||||||
|
val liveRoomList = queryRepository.getLiveRoomList(
|
||||||
|
creatorId,
|
||||||
|
userMember = member,
|
||||||
|
timezone = timezone,
|
||||||
|
limit = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// 공지사항
|
||||||
|
val notice = queryRepository.getNoticeString(creatorId)
|
||||||
|
|
||||||
|
// 응원
|
||||||
|
val cheers = queryRepository.getCheersList(creatorId, timezone = timezone, offset = 0, limit = 4)
|
||||||
|
|
||||||
|
// 차단한 크리에이터 인지 체크
|
||||||
|
val isBlock = memberService.isBlocked(blockedMemberId = creatorId, memberId = member.id!!)
|
||||||
|
|
||||||
|
// 활동요약 (라이브 횟수, 라이브 시간, 라이브 참여자, 콘텐츠 수)
|
||||||
|
val liveCount = queryRepository.getLiveCount(creatorId) ?: 0
|
||||||
|
val liveTime = queryRepository.getLiveTime(creatorId)
|
||||||
|
val liveContributorCount = queryRepository.getLiveContributorCount(creatorId) ?: 0
|
||||||
|
val contentCount = 0L
|
||||||
|
|
||||||
|
return GetCreatorProfileResponse(
|
||||||
|
creator = CreatorResponse(
|
||||||
|
creatorId = creatorAccount.id!!,
|
||||||
|
profileUrl = if (creatorAccount.profileImage != null) {
|
||||||
|
"$cloudFrontHost/${creatorAccount.profileImage}"
|
||||||
|
} else {
|
||||||
|
"$cloudFrontHost/profile/default-profile.png"
|
||||||
|
},
|
||||||
|
nickname = creatorAccount.nickname,
|
||||||
|
tags = creatorAccount.tags.asSequence().filter { it.tag.isActive }.map { it.tag.tag }.toList(),
|
||||||
|
introduce = creatorAccount.introduce,
|
||||||
|
instagramUrl = creatorAccount.instagramUrl,
|
||||||
|
youtubeUrl = creatorAccount.youtubeUrl,
|
||||||
|
websiteUrl = creatorAccount.websiteUrl,
|
||||||
|
blogUrl = creatorAccount.blogUrl,
|
||||||
|
isNotification = isNotification,
|
||||||
|
notificationRecipientCount = notificationRecipientCount
|
||||||
|
),
|
||||||
|
userDonationRanking = userDonationRanking,
|
||||||
|
similarCreatorList = similarCreatorList,
|
||||||
|
liveRoomList = liveRoomList,
|
||||||
|
notice = notice,
|
||||||
|
cheers = cheers,
|
||||||
|
activitySummary = GetCreatorActivitySummary(
|
||||||
|
liveCount = liveCount,
|
||||||
|
liveTime = liveTime,
|
||||||
|
liveContributorCount = liveContributorCount,
|
||||||
|
contentCount = contentCount
|
||||||
|
),
|
||||||
|
isBlock = isBlock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFollowerList(
|
||||||
|
creatorId: Long,
|
||||||
|
member: Member,
|
||||||
|
pageable: Pageable
|
||||||
|
): GetFollowerListResponse {
|
||||||
|
val totalCount = queryRepository
|
||||||
|
.getFollowerListTotalCount(creatorId)
|
||||||
|
|
||||||
|
val followerList = queryRepository.getFollowerList(creatorId, pageable.offset, pageable.pageSize.toLong())
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val isFollow = if (it.role == MemberRole.CREATOR) {
|
||||||
|
queryRepository.isFollow(creatorId = it.userId, memberId = member.id!!)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
GetFollowerListResponseItem(
|
||||||
|
userId = it.userId,
|
||||||
|
profileImage = it.profileImage,
|
||||||
|
nickname = it.nickname,
|
||||||
|
isFollow = isFollow
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetFollowerListResponse(totalCount = totalCount, items = followerList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun writeCheers(request: PostWriteCheersRequest, member: Member) {
|
||||||
|
val creator = queryRepository.getAccount(request.creatorId) ?: throw SodaException("없는 사용자 입니다.")
|
||||||
|
|
||||||
|
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = request.creatorId)
|
||||||
|
if (isBlocked) throw SodaException("${creator.nickname}님의 요청으로 팬토크 작성이 제한됩니다.")
|
||||||
|
|
||||||
|
val cheers = CreatorCheers(cheers = request.content)
|
||||||
|
cheers.member = member
|
||||||
|
cheers.creator = creator
|
||||||
|
|
||||||
|
val parent = if (request.parentId != null) {
|
||||||
|
queryRepository.getCreatorCheers(request.parentId)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
cheers.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
cheersRepository.save(cheers)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun modifyCheers(request: PutWriteCheersRequest, member: Member) {
|
||||||
|
val cheers = queryRepository.getCheers(request.cheersId, member.id!!)
|
||||||
|
?: throw SodaException("잘못된 요청입니다.")
|
||||||
|
|
||||||
|
cheers.cheers = request.content
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun saveNotice(member: Member, notice: String) {
|
||||||
|
var channelNotice = queryRepository.getNotice(creatorId = member.id!!)
|
||||||
|
|
||||||
|
if (channelNotice == null) {
|
||||||
|
channelNotice = ChannelNotice(notice)
|
||||||
|
channelNotice.member = member
|
||||||
|
noticeRepository.save(channelNotice)
|
||||||
|
} else {
|
||||||
|
channelNotice.notice = notice
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class GetCheersResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val cheers: List<GetCheersResponseItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetCheersResponseItem(
|
||||||
|
val cheersId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val profileUrl: String,
|
||||||
|
val content: String,
|
||||||
|
val date: String,
|
||||||
|
val replyList: List<GetCheersResponseItem>
|
||||||
|
)
|
|
@ -0,0 +1,19 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class GetCreatorProfileResponse(
|
||||||
|
val creator: CreatorResponse,
|
||||||
|
val userDonationRanking: List<UserDonationRankingResponse>,
|
||||||
|
val similarCreatorList: List<SimilarCreatorResponse>,
|
||||||
|
val liveRoomList: List<LiveRoomResponse>,
|
||||||
|
val notice: String,
|
||||||
|
val cheers: GetCheersResponse,
|
||||||
|
val activitySummary: GetCreatorActivitySummary,
|
||||||
|
val isBlock: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetCreatorActivitySummary(
|
||||||
|
val liveCount: Long,
|
||||||
|
val liveTime: Long,
|
||||||
|
val liveContributorCount: Long,
|
||||||
|
val contentCount: Long
|
||||||
|
)
|
|
@ -0,0 +1,22 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class GetLiveRoomAllResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val liveRoomList: List<LiveRoomResponse>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LiveRoomResponse(
|
||||||
|
val roomId: Long,
|
||||||
|
val title: String,
|
||||||
|
val content: String,
|
||||||
|
val isPaid: Boolean,
|
||||||
|
val beginDateTime: String,
|
||||||
|
val coverImageUrl: String,
|
||||||
|
val isAdult: Boolean,
|
||||||
|
val price: Int,
|
||||||
|
val channelName: String?,
|
||||||
|
val managerNickname: String,
|
||||||
|
val isReservation: Boolean,
|
||||||
|
val isActive: Boolean,
|
||||||
|
val isPrivateRoom: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class SimilarCreatorResponse(
|
||||||
|
val userId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val profileImage: String,
|
||||||
|
val tags: List<String>
|
||||||
|
)
|
|
@ -0,0 +1,16 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
|
data class GetDonationAllResponse(
|
||||||
|
val accumulatedCansToday: Int,
|
||||||
|
val accumulatedCansLastWeek: Int,
|
||||||
|
val accumulatedCansThisMonth: Int,
|
||||||
|
val totalCount: Int,
|
||||||
|
val userDonationRanking: List<UserDonationRankingResponse>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class UserDonationRankingResponse(
|
||||||
|
val userId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val profileImage: String,
|
||||||
|
val donationCan: Int
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.follower
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
|
||||||
|
data class GetFollowerListDto @QueryProjection constructor(
|
||||||
|
val userId: Long,
|
||||||
|
val profileImage: String,
|
||||||
|
val nickname: String,
|
||||||
|
val role: MemberRole
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.follower
|
||||||
|
|
||||||
|
data class GetFollowerListResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val items: List<GetFollowerListResponseItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetFollowerListResponseItem(
|
||||||
|
val userId: Long,
|
||||||
|
val profileImage: String,
|
||||||
|
val nickname: String,
|
||||||
|
val isFollow: Boolean?
|
||||||
|
)
|
|
@ -0,0 +1,19 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
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.OneToOne
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class ChannelNotice(
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
var notice: String
|
||||||
|
) : BaseEntity() {
|
||||||
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "member_id", nullable = false)
|
||||||
|
var member: Member? = null
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface ChannelNoticeRepository : JpaRepository<ChannelNotice, Long>
|
|
@ -0,0 +1,36 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
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
|
||||||
|
import javax.persistence.OneToMany
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class CreatorCheers(
|
||||||
|
@Column(columnDefinition = "TEXT", nullable = false)
|
||||||
|
var cheers: String,
|
||||||
|
val isActive: Boolean = true
|
||||||
|
) : BaseEntity() {
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "parent_id", nullable = true)
|
||||||
|
var parent: CreatorCheers? = null
|
||||||
|
set(value) {
|
||||||
|
value?.children?.add(this)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "parent")
|
||||||
|
var children: MutableList<CreatorCheers> = mutableListOf()
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "member_id", nullable = false)
|
||||||
|
var member: Member? = null
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "creator_id", nullable = false)
|
||||||
|
var creator: Member? = null
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface CreatorCheersRepository : JpaRepository<CreatorCheers, Long>
|
|
@ -0,0 +1,3 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
data class PostCreatorNoticeRequest(val notice: String)
|
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
data class PostWriteCheersRequest(
|
||||||
|
val parentId: Long? = null,
|
||||||
|
val creatorId: Long,
|
||||||
|
val content: String
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
data class PutWriteCheersRequest(
|
||||||
|
val cheersId: Long,
|
||||||
|
val content: String
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
data class TimeDifferenceResult(
|
||||||
|
val beginDateTime: LocalDateTime,
|
||||||
|
val updatedAt: LocalDateTime
|
||||||
|
)
|
Loading…
Reference in New Issue