@@ -68,6 +68,19 @@ class AdminMemberStatisticsRepository(private val queryFactory: JPAQueryFactory)
|
|||||||
.size
|
.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getTotalSignUpLineCount(startDate: LocalDateTime, endDate: LocalDateTime): Int {
|
||||||
|
return queryFactory
|
||||||
|
.select(member.id)
|
||||||
|
.from(member)
|
||||||
|
.where(
|
||||||
|
member.createdAt.goe(startDate),
|
||||||
|
member.createdAt.loe(endDate),
|
||||||
|
member.provider.eq(MemberProvider.LINE)
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
fun getTotalAuthCount(startDate: LocalDateTime, endDate: LocalDateTime): Int {
|
fun getTotalAuthCount(startDate: LocalDateTime, endDate: LocalDateTime): Int {
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(auth.id)
|
.select(auth.id)
|
||||||
@@ -189,6 +202,25 @@ class AdminMemberStatisticsRepository(private val queryFactory: JPAQueryFactory)
|
|||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSignUpLineCountInRange(startDate: LocalDateTime, endDate: LocalDateTime): List<DateAndMemberCount> {
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QDateAndMemberCount(
|
||||||
|
getFormattedDate(member.createdAt),
|
||||||
|
member.id.countDistinct().castToNum(Int::class.java)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(member)
|
||||||
|
.where(
|
||||||
|
member.createdAt.goe(startDate),
|
||||||
|
member.createdAt.loe(endDate),
|
||||||
|
member.provider.eq(MemberProvider.LINE)
|
||||||
|
)
|
||||||
|
.groupBy(getFormattedDate(member.createdAt))
|
||||||
|
.orderBy(getFormattedDate(member.createdAt).desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
fun getAuthCountInRange(startDate: LocalDateTime, endDate: LocalDateTime): List<DateAndMemberCount> {
|
fun getAuthCountInRange(startDate: LocalDateTime, endDate: LocalDateTime): List<DateAndMemberCount> {
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics
|
|||||||
startDate = startDateTime,
|
startDate = startDateTime,
|
||||||
endDate = endDateTime
|
endDate = endDateTime
|
||||||
)
|
)
|
||||||
|
val totalSignUpLineCount = repository.getTotalSignUpLineCount(
|
||||||
|
startDate = startDateTime,
|
||||||
|
endDate = endDateTime
|
||||||
|
)
|
||||||
val totalAuthCount = repository.getTotalAuthCount(startDate = startDateTime, endDate = endDateTime)
|
val totalAuthCount = repository.getTotalAuthCount(startDate = startDateTime, endDate = endDateTime)
|
||||||
val totalSignOutCount = repository.getTotalSignOutCount(startDate = startDateTime, endDate = endDateTime)
|
val totalSignOutCount = repository.getTotalSignOutCount(startDate = startDateTime, endDate = endDateTime)
|
||||||
val totalPaymentMemberCount = repository.getPaymentMemberCount(startDate = startDateTime, endDate = endDateTime)
|
val totalPaymentMemberCount = repository.getPaymentMemberCount(startDate = startDateTime, endDate = endDateTime)
|
||||||
@@ -92,6 +96,11 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics
|
|||||||
endDate = endDateTime
|
endDate = endDateTime
|
||||||
).associateBy({ it.date }, { it.memberCount })
|
).associateBy({ it.date }, { it.memberCount })
|
||||||
|
|
||||||
|
val signUpLineCountInRange = repository.getSignUpLineCountInRange(
|
||||||
|
startDate = startDateTime,
|
||||||
|
endDate = endDateTime
|
||||||
|
).associateBy({ it.date }, { it.memberCount })
|
||||||
|
|
||||||
val authCountInRange = repository.getAuthCountInRange(
|
val authCountInRange = repository.getAuthCountInRange(
|
||||||
startDate = startDateTime,
|
startDate = startDateTime,
|
||||||
endDate = endDateTime
|
endDate = endDateTime
|
||||||
@@ -121,6 +130,7 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics
|
|||||||
signUpEmailCount = signUpEmailCountInRange[date] ?: 0,
|
signUpEmailCount = signUpEmailCountInRange[date] ?: 0,
|
||||||
signUpKakaoCount = signUpKakaoCountInRange[date] ?: 0,
|
signUpKakaoCount = signUpKakaoCountInRange[date] ?: 0,
|
||||||
signUpGoogleCount = signUpGoogleCountInRange[date] ?: 0,
|
signUpGoogleCount = signUpGoogleCountInRange[date] ?: 0,
|
||||||
|
signUpLineCount = signUpLineCountInRange[date] ?: 0,
|
||||||
signOutCount = signOutCountInRange[date] ?: 0,
|
signOutCount = signOutCountInRange[date] ?: 0,
|
||||||
paymentMemberCount = paymentMemberCountInRangeMap[date] ?: 0
|
paymentMemberCount = paymentMemberCountInRangeMap[date] ?: 0
|
||||||
)
|
)
|
||||||
@@ -134,6 +144,7 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics
|
|||||||
totalSignUpEmailCount = totalSignUpEmailCount,
|
totalSignUpEmailCount = totalSignUpEmailCount,
|
||||||
totalSignUpKakaoCount = totalSignUpKakaoCount,
|
totalSignUpKakaoCount = totalSignUpKakaoCount,
|
||||||
totalSignUpGoogleCount = totalSignUpGoogleCount,
|
totalSignUpGoogleCount = totalSignUpGoogleCount,
|
||||||
|
totalSignUpLineCount = totalSignUpLineCount,
|
||||||
totalSignOutCount = totalSignOutCount,
|
totalSignOutCount = totalSignOutCount,
|
||||||
totalPaymentMemberCount = totalPaymentMemberCount,
|
totalPaymentMemberCount = totalPaymentMemberCount,
|
||||||
items = items
|
items = items
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ data class GetMemberStatisticsResponse(
|
|||||||
val totalSignUpEmailCount: Int,
|
val totalSignUpEmailCount: Int,
|
||||||
val totalSignUpKakaoCount: Int,
|
val totalSignUpKakaoCount: Int,
|
||||||
val totalSignUpGoogleCount: Int,
|
val totalSignUpGoogleCount: Int,
|
||||||
|
val totalSignUpLineCount: Int,
|
||||||
val totalSignOutCount: Int,
|
val totalSignOutCount: Int,
|
||||||
val totalPaymentMemberCount: Int,
|
val totalPaymentMemberCount: Int,
|
||||||
val items: List<GetMemberStatisticsItem>
|
val items: List<GetMemberStatisticsItem>
|
||||||
@@ -19,6 +20,7 @@ data class GetMemberStatisticsItem(
|
|||||||
val signUpEmailCount: Int,
|
val signUpEmailCount: Int,
|
||||||
val signUpKakaoCount: Int,
|
val signUpKakaoCount: Int,
|
||||||
val signUpGoogleCount: Int,
|
val signUpGoogleCount: Int,
|
||||||
|
val signUpLineCount: Int,
|
||||||
val signOutCount: Int,
|
val signOutCount: Int,
|
||||||
val paymentMemberCount: Int
|
val paymentMemberCount: Int
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -317,8 +317,9 @@ class HomeService(
|
|||||||
val themeList = if (theme.isBlank()) {
|
val themeList = if (theme.isBlank()) {
|
||||||
contentThemeService.getActiveThemeOfContent(
|
contentThemeService.getActiveThemeOfContent(
|
||||||
isAdult = isAdult,
|
isAdult = isAdult,
|
||||||
isFree = true,
|
isFree = false,
|
||||||
contentType = contentType
|
contentType = contentType,
|
||||||
|
excludeThemes = listOf("다시듣기")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
listOf(theme)
|
listOf(theme)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest
|
|||||||
import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
|
import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
|
||||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
|
import kr.co.vividnext.sodalive.member.DonationRankingPeriod
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.security.access.prepost.PreAuthorize
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
@@ -75,11 +76,12 @@ class ExplorerController(
|
|||||||
@GetMapping("/profile/{id}/donation-rank")
|
@GetMapping("/profile/{id}/donation-rank")
|
||||||
fun getCreatorProfileDonationRanking(
|
fun getCreatorProfileDonationRanking(
|
||||||
@PathVariable("id") creatorId: Long,
|
@PathVariable("id") creatorId: Long,
|
||||||
|
@RequestParam("period", required = false) period: DonationRankingPeriod? = null,
|
||||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
pageable: Pageable
|
pageable: Pageable
|
||||||
) = run {
|
) = run {
|
||||||
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
|
if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
ApiResponse.ok(service.getCreatorProfileDonationRanking(creatorId, pageable, member))
|
ApiResponse.ok(service.getCreatorProfileDonationRanking(creatorId, period, pageable, member))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/profile/cheers")
|
@PostMapping("/profile/cheers")
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ import kr.co.vividnext.sodalive.explorer.profile.TimeDifferenceResult
|
|||||||
import kr.co.vividnext.sodalive.i18n.Lang
|
import kr.co.vividnext.sodalive.i18n.Lang
|
||||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoomType
|
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.cancel.QLiveRoomCancel.liveRoomCancel
|
||||||
import kr.co.vividnext.sodalive.live.room.visit.QLiveRoomVisit.liveRoomVisit
|
import kr.co.vividnext.sodalive.live.room.visit.QLiveRoomVisit.liveRoomVisit
|
||||||
|
import kr.co.vividnext.sodalive.member.Gender
|
||||||
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
|
||||||
@@ -342,6 +344,21 @@ class ExplorerQueryRepository(
|
|||||||
.and(liveRoom.cancel.id.isNull)
|
.and(liveRoom.cancel.id.isNull)
|
||||||
.and(liveRoom.isActive.isTrue)
|
.and(liveRoom.isActive.isTrue)
|
||||||
|
|
||||||
|
val effectiveGender = if (userMember.auth != null) {
|
||||||
|
if (userMember.auth!!.gender == 1) Gender.MALE else Gender.FEMALE
|
||||||
|
} else {
|
||||||
|
userMember.gender
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectiveGender != Gender.NONE) {
|
||||||
|
val genderCondition = when (effectiveGender) {
|
||||||
|
Gender.MALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.MALE_ONLY)
|
||||||
|
Gender.FEMALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.FEMALE_ONLY)
|
||||||
|
Gender.NONE -> liveRoom.genderRestriction.isNotNull
|
||||||
|
}
|
||||||
|
where = where.and(genderCondition.or(liveRoom.member.id.eq(userMember.id)))
|
||||||
|
}
|
||||||
|
|
||||||
if (userMember.auth == null) {
|
if (userMember.auth == null) {
|
||||||
where = where.and(liveRoom.isAdult.isFalse)
|
where = where.and(liveRoom.isAdult.isFalse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import kr.co.vividnext.sodalive.i18n.Lang
|
|||||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||||
|
import kr.co.vividnext.sodalive.member.DonationRankingPeriod
|
||||||
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.MemberService
|
import kr.co.vividnext.sodalive.member.MemberService
|
||||||
@@ -215,6 +216,7 @@ class ExplorerService(
|
|||||||
val notificationUserIds = queryRepository.getNotificationUserIds(creatorId)
|
val notificationUserIds = queryRepository.getNotificationUserIds(creatorId)
|
||||||
val creatorFollowing = queryRepository.getCreatorFollowing(creatorId = creatorId, memberId = member.id!!)
|
val creatorFollowing = queryRepository.getCreatorFollowing(creatorId = creatorId, memberId = member.id!!)
|
||||||
val notificationRecipientCount = notificationUserIds.size
|
val notificationRecipientCount = notificationUserIds.size
|
||||||
|
val donationRankingPeriod = creatorAccount.donationRankingPeriod ?: DonationRankingPeriod.CUMULATIVE
|
||||||
|
|
||||||
// 후원랭킹
|
// 후원랭킹
|
||||||
val memberDonationRanking = if (
|
val memberDonationRanking = if (
|
||||||
@@ -223,7 +225,8 @@ class ExplorerService(
|
|||||||
donationRankingService.getMemberDonationRanking(
|
donationRankingService.getMemberDonationRanking(
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
limit = 10,
|
limit = 10,
|
||||||
withDonationCan = creatorId == member.id!!
|
withDonationCan = creatorId == member.id!!,
|
||||||
|
period = donationRankingPeriod
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
listOf()
|
listOf()
|
||||||
@@ -396,23 +399,37 @@ class ExplorerService(
|
|||||||
|
|
||||||
fun getCreatorProfileDonationRanking(
|
fun getCreatorProfileDonationRanking(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
|
period: DonationRankingPeriod?,
|
||||||
pageable: Pageable,
|
pageable: Pageable,
|
||||||
member: Member
|
member: Member
|
||||||
): GetDonationAllResponse {
|
): GetDonationAllResponse {
|
||||||
|
val creatorAccount = queryRepository.getMember(creatorId)
|
||||||
|
?: throw SodaException(messageKey = "member.validation.user_not_found")
|
||||||
|
val donationRankingPeriod = creatorAccount.donationRankingPeriod ?: DonationRankingPeriod.CUMULATIVE
|
||||||
|
val isCreatorSelf = creatorId == member.id!!
|
||||||
|
val effectivePeriod = if (isCreatorSelf && period != null) {
|
||||||
|
period
|
||||||
|
} else {
|
||||||
|
donationRankingPeriod
|
||||||
|
}
|
||||||
val currentDate = LocalDate.now().atTime(0, 0, 0)
|
val currentDate = LocalDate.now().atTime(0, 0, 0)
|
||||||
val firstDayOfLastWeek = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7)
|
val firstDayOfLastWeek = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7)
|
||||||
val firstDayOfMonth = currentDate.with(TemporalAdjusters.firstDayOfMonth())
|
val firstDayOfMonth = currentDate.with(TemporalAdjusters.firstDayOfMonth())
|
||||||
|
|
||||||
val donationMemberTotal = donationRankingService.getMemberDonationRankingTotal(creatorId)
|
val donationMemberTotal = donationRankingService.getMemberDonationRankingTotal(
|
||||||
|
creatorId,
|
||||||
|
effectivePeriod
|
||||||
|
)
|
||||||
val donationRanking = donationRankingService.getMemberDonationRanking(
|
val donationRanking = donationRankingService.getMemberDonationRanking(
|
||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
offset = pageable.offset,
|
offset = pageable.offset,
|
||||||
limit = pageable.pageSize.toLong(),
|
limit = pageable.pageSize.toLong(),
|
||||||
withDonationCan = creatorId == member.id!!
|
withDonationCan = isCreatorSelf,
|
||||||
|
period = effectivePeriod
|
||||||
)
|
)
|
||||||
|
|
||||||
return GetDonationAllResponse(
|
return GetDonationAllResponse(
|
||||||
accumulatedCansToday = if (creatorId == member.id!!) {
|
accumulatedCansToday = if (isCreatorSelf) {
|
||||||
queryRepository.getDonationCoinsDateRange(
|
queryRepository.getDonationCoinsDateRange(
|
||||||
creatorId,
|
creatorId,
|
||||||
currentDate,
|
currentDate,
|
||||||
@@ -421,7 +438,7 @@ class ExplorerService(
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
accumulatedCansLastWeek = if (creatorId == member.id!!) {
|
accumulatedCansLastWeek = if (isCreatorSelf) {
|
||||||
queryRepository.getDonationCoinsDateRange(
|
queryRepository.getDonationCoinsDateRange(
|
||||||
creatorId,
|
creatorId,
|
||||||
firstDayOfLastWeek,
|
firstDayOfLastWeek,
|
||||||
@@ -430,7 +447,7 @@ class ExplorerService(
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
accumulatedCansThisMonth = if (creatorId == member.id!!) {
|
accumulatedCansThisMonth = if (isCreatorSelf) {
|
||||||
queryRepository.getDonationCoinsDateRange(
|
queryRepository.getDonationCoinsDateRange(
|
||||||
creatorId,
|
creatorId,
|
||||||
firstDayOfMonth,
|
firstDayOfMonth,
|
||||||
@@ -439,11 +456,16 @@ class ExplorerService(
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
isVisibleDonationRank = if (creatorId == member.id!!) {
|
isVisibleDonationRank = if (isCreatorSelf) {
|
||||||
queryRepository.getVisibleDonationRank(creatorId)
|
queryRepository.getVisibleDonationRank(creatorId)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
|
donationRankingPeriod = if (isCreatorSelf) {
|
||||||
|
donationRankingPeriod
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
totalCount = donationMemberTotal,
|
totalCount = donationMemberTotal,
|
||||||
userDonationRanking = donationRanking
|
userDonationRanking = donationRanking
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kr.co.vividnext.sodalive.explorer
|
package kr.co.vividnext.sodalive.explorer
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import kr.co.vividnext.sodalive.member.DonationRankingPeriod
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
data class GetDonationAllResponse(
|
data class GetDonationAllResponse(
|
||||||
@@ -8,6 +9,7 @@ data class GetDonationAllResponse(
|
|||||||
val accumulatedCansLastWeek: Int,
|
val accumulatedCansLastWeek: Int,
|
||||||
val accumulatedCansThisMonth: Int,
|
val accumulatedCansThisMonth: Int,
|
||||||
val isVisibleDonationRank: Boolean,
|
val isVisibleDonationRank: Boolean,
|
||||||
|
val donationRankingPeriod: DonationRankingPeriod?,
|
||||||
val totalCount: Int,
|
val totalCount: Int,
|
||||||
val userDonationRanking: List<MemberDonationRankingResponse>
|
val userDonationRanking: List<MemberDonationRankingResponse>
|
||||||
) : Serializable
|
) : Serializable
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kr.co.vividnext.sodalive.explorer.profile
|
package kr.co.vividnext.sodalive.explorer.profile
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.querydsl.core.BooleanBuilder
|
||||||
import com.querydsl.core.annotations.QueryProjection
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
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
|
||||||
@@ -8,13 +9,17 @@ import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
|
|||||||
import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
|
import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
|
||||||
import kr.co.vividnext.sodalive.member.QMember.member
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFactory) {
|
class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
fun getMemberDonationRanking(
|
fun getMemberDonationRanking(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
offset: Long,
|
offset: Long,
|
||||||
limit: Long
|
limit: Long,
|
||||||
|
startDate: LocalDateTime? = null,
|
||||||
|
endDate: LocalDateTime? = null
|
||||||
): List<DonationRankingProjection> {
|
): List<DonationRankingProjection> {
|
||||||
val donationCan = useCan.rewardCan.add(useCan.can).sum()
|
val donationCan = useCan.rewardCan.add(useCan.can).sum()
|
||||||
return queryFactory
|
return queryFactory
|
||||||
@@ -38,6 +43,7 @@ class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFa
|
|||||||
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
|
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
|
||||||
.or(useCan.canUsage.eq(CanUsage.LIVE))
|
.or(useCan.canUsage.eq(CanUsage.LIVE))
|
||||||
)
|
)
|
||||||
|
.and(buildDateRangeCondition(startDate, endDate))
|
||||||
)
|
)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
@@ -46,7 +52,11 @@ class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFa
|
|||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMemberDonationRankingTotal(creatorId: Long): Int {
|
fun getMemberDonationRankingTotal(
|
||||||
|
creatorId: Long,
|
||||||
|
startDate: LocalDateTime? = null,
|
||||||
|
endDate: LocalDateTime? = null
|
||||||
|
): Int {
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(member.id)
|
.select(member.id)
|
||||||
.from(useCanCalculate)
|
.from(useCanCalculate)
|
||||||
@@ -61,11 +71,32 @@ class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFa
|
|||||||
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
|
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
|
||||||
.or(useCan.canUsage.eq(CanUsage.LIVE))
|
.or(useCan.canUsage.eq(CanUsage.LIVE))
|
||||||
)
|
)
|
||||||
|
.and(buildDateRangeCondition(startDate, endDate))
|
||||||
)
|
)
|
||||||
.groupBy(member.id)
|
.groupBy(member.id)
|
||||||
.fetch()
|
.fetch()
|
||||||
.size
|
.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildDateRangeCondition(
|
||||||
|
startDate: LocalDateTime?,
|
||||||
|
endDate: LocalDateTime?
|
||||||
|
): BooleanBuilder {
|
||||||
|
val condition = BooleanBuilder()
|
||||||
|
if (startDate != null && endDate != null) {
|
||||||
|
val startUtc = startDate
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
val endUtc = endDate
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
condition.and(useCanCalculate.createdAt.goe(startUtc))
|
||||||
|
condition.and(useCanCalculate.createdAt.lt(endUtc))
|
||||||
|
}
|
||||||
|
return condition
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DonationRankingProjection @QueryProjection constructor(
|
data class DonationRankingProjection @QueryProjection constructor(
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package kr.co.vividnext.sodalive.explorer.profile
|
|||||||
|
|
||||||
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingListResponse
|
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingListResponse
|
||||||
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingResponse
|
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingResponse
|
||||||
|
import kr.co.vividnext.sodalive.member.DonationRankingPeriod
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.data.redis.core.RedisTemplate
|
import org.springframework.data.redis.core.RedisTemplate
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.time.DayOfWeek
|
import java.time.DayOfWeek
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
@@ -20,14 +22,22 @@ class CreatorDonationRankingService(
|
|||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val imageHost: String
|
private val imageHost: String
|
||||||
) {
|
) {
|
||||||
fun getMemberDonationRankingTotal(creatorId: Long): Int {
|
fun getMemberDonationRankingTotal(
|
||||||
val cacheKey = "creator_donation_ranking_member_total_v2:$creatorId"
|
creatorId: Long,
|
||||||
|
period: DonationRankingPeriod = DonationRankingPeriod.CUMULATIVE
|
||||||
|
): Int {
|
||||||
|
val cacheKey = "creator_donation_ranking_member_total_v2:$creatorId:$period"
|
||||||
val cachedTotal = redisTemplate.opsForValue().get(cacheKey) as? Int
|
val cachedTotal = redisTemplate.opsForValue().get(cacheKey) as? Int
|
||||||
if (cachedTotal != null) {
|
if (cachedTotal != null) {
|
||||||
return cachedTotal
|
return cachedTotal
|
||||||
}
|
}
|
||||||
|
|
||||||
val total = repository.getMemberDonationRankingTotal(creatorId)
|
val weeklyDateRange = getWeeklyDateRange(period)
|
||||||
|
val total = if (weeklyDateRange == null) {
|
||||||
|
repository.getMemberDonationRankingTotal(creatorId)
|
||||||
|
} else {
|
||||||
|
repository.getMemberDonationRankingTotal(creatorId, weeklyDateRange.first, weeklyDateRange.second)
|
||||||
|
}
|
||||||
|
|
||||||
val now = LocalDateTime.now()
|
val now = LocalDateTime.now()
|
||||||
val nextMonday = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).with(LocalTime.MIN)
|
val nextMonday = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).with(LocalTime.MIN)
|
||||||
@@ -46,15 +56,27 @@ class CreatorDonationRankingService(
|
|||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
offset: Long = 0,
|
offset: Long = 0,
|
||||||
limit: Long = 10,
|
limit: Long = 10,
|
||||||
withDonationCan: Boolean
|
withDonationCan: Boolean,
|
||||||
|
period: DonationRankingPeriod = DonationRankingPeriod.CUMULATIVE
|
||||||
): List<MemberDonationRankingResponse> {
|
): List<MemberDonationRankingResponse> {
|
||||||
val cacheKey = "creator_donation_ranking_v2:$creatorId:$offset:$limit:$withDonationCan"
|
val cacheKey = "creator_donation_ranking_v2:$creatorId:$period:$offset:$limit:$withDonationCan"
|
||||||
val cachedData = redisTemplate.opsForValue().get(cacheKey) as? MemberDonationRankingListResponse
|
val cachedData = redisTemplate.opsForValue().get(cacheKey) as? MemberDonationRankingListResponse
|
||||||
if (cachedData != null) {
|
if (cachedData != null) {
|
||||||
return cachedData.rankings
|
return cachedData.rankings
|
||||||
}
|
}
|
||||||
|
|
||||||
val memberDonationRanking = repository.getMemberDonationRanking(creatorId, offset, limit)
|
val weeklyDateRange = getWeeklyDateRange(period)
|
||||||
|
val memberDonationRanking = if (weeklyDateRange == null) {
|
||||||
|
repository.getMemberDonationRanking(creatorId, offset, limit)
|
||||||
|
} else {
|
||||||
|
repository.getMemberDonationRanking(
|
||||||
|
creatorId,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
weeklyDateRange.first,
|
||||||
|
weeklyDateRange.second
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val result = memberDonationRanking.map {
|
val result = memberDonationRanking.map {
|
||||||
MemberDonationRankingResponse(
|
MemberDonationRankingResponse(
|
||||||
@@ -77,4 +99,17 @@ class CreatorDonationRankingService(
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getWeeklyDateRange(period: DonationRankingPeriod): Pair<LocalDateTime, LocalDateTime>? {
|
||||||
|
if (period != DonationRankingPeriod.WEEKLY) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentDate = LocalDate.now()
|
||||||
|
val lastWeekMonday = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1)
|
||||||
|
val startDate = lastWeekMonday.atStartOfDay()
|
||||||
|
val endDate = startDate.plusDays(7)
|
||||||
|
|
||||||
|
return startDate to endDate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.fcm
|
|||||||
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
||||||
import kr.co.vividnext.sodalive.i18n.Lang
|
import kr.co.vividnext.sodalive.i18n.Lang
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
import org.springframework.scheduling.annotation.Async
|
import org.springframework.scheduling.annotation.Async
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
@@ -33,7 +34,8 @@ class FcmEvent(
|
|||||||
val auditionId: Long? = null,
|
val auditionId: Long? = null,
|
||||||
val commentParentId: Long? = null,
|
val commentParentId: Long? = null,
|
||||||
val myMemberId: Long? = null,
|
val myMemberId: Long? = null,
|
||||||
val isAvailableJoinCreator: Boolean? = null
|
val isAvailableJoinCreator: Boolean? = null,
|
||||||
|
val genderRestriction: GenderRestriction? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@@ -69,7 +71,8 @@ class FcmSendListener(
|
|||||||
val pushTokens = memberRepository.getCreateLiveRoomNotificationRecipientPushTokens(
|
val pushTokens = memberRepository.getCreateLiveRoomNotificationRecipientPushTokens(
|
||||||
creatorId = fcmEvent.creatorId!!,
|
creatorId = fcmEvent.creatorId!!,
|
||||||
isAuth = fcmEvent.isAuth ?: false,
|
isAuth = fcmEvent.isAuth ?: false,
|
||||||
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false
|
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false,
|
||||||
|
genderRestriction = fcmEvent.genderRestriction
|
||||||
)
|
)
|
||||||
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
|
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
|
||||||
}
|
}
|
||||||
@@ -79,7 +82,8 @@ class FcmSendListener(
|
|||||||
creatorId = fcmEvent.creatorId!!,
|
creatorId = fcmEvent.creatorId!!,
|
||||||
roomId = fcmEvent.roomId!!,
|
roomId = fcmEvent.roomId!!,
|
||||||
isAuth = fcmEvent.isAuth ?: false,
|
isAuth = fcmEvent.isAuth ?: false,
|
||||||
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false
|
isAvailableJoinCreator = fcmEvent.isAvailableJoinCreator ?: false,
|
||||||
|
genderRestriction = fcmEvent.genderRestriction
|
||||||
)
|
)
|
||||||
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
|
sendPush(pushTokens, fcmEvent, roomId = fcmEvent.roomId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface PushTokenRepository : JpaRepository<PushToken, Long>, PushTokenQueryRe
|
|||||||
interface PushTokenQueryRepository {
|
interface PushTokenQueryRepository {
|
||||||
fun findByToken(token: String): PushToken?
|
fun findByToken(token: String): PushToken?
|
||||||
fun findByMemberId(memberId: Long): List<PushToken>
|
fun findByMemberId(memberId: Long): List<PushToken>
|
||||||
|
fun findByMemberIds(memberIds: List<Long>): List<PushToken>
|
||||||
}
|
}
|
||||||
|
|
||||||
class PushTokenQueryRepositoryImpl(
|
class PushTokenQueryRepositoryImpl(
|
||||||
@@ -27,4 +28,12 @@ class PushTokenQueryRepositoryImpl(
|
|||||||
.where(pushToken.member.id.eq(memberId))
|
.where(pushToken.member.id.eq(memberId))
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findByMemberIds(memberIds: List<Long>): List<PushToken> {
|
||||||
|
if (memberIds.isEmpty()) return emptyList()
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(pushToken)
|
||||||
|
.where(pushToken.member.id.`in`(memberIds))
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1448,6 +1448,11 @@ class SodaMessageSource {
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val liveRoomMessages = mapOf(
|
private val liveRoomMessages = mapOf(
|
||||||
|
"live.room.gender_restricted" to mapOf(
|
||||||
|
Lang.KO to "입장 가능한 성별이 아닙니다.",
|
||||||
|
Lang.EN to "Your gender is not allowed to enter this room.",
|
||||||
|
Lang.JA to "入場可能な性別ではありません。"
|
||||||
|
),
|
||||||
"live.room.max_reservations" to mapOf(
|
"live.room.max_reservations" to mapOf(
|
||||||
Lang.KO to "예약 라이브는 최대 3개까지 가능합니다.",
|
Lang.KO to "예약 라이브는 최대 3개까지 가능합니다.",
|
||||||
Lang.EN to "You can reserve up to 3 live sessions.",
|
Lang.EN to "You can reserve up to 3 live sessions.",
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ data class CreateLiveRoomRequest(
|
|||||||
val menuPanId: Long = 0,
|
val menuPanId: Long = 0,
|
||||||
val menuPan: String = "",
|
val menuPan: String = "",
|
||||||
val isActiveMenuPan: Boolean = false,
|
val isActiveMenuPan: Boolean = false,
|
||||||
val isAvailableJoinCreator: Boolean = true
|
val isAvailableJoinCreator: Boolean = true,
|
||||||
|
val genderRestriction: GenderRestriction = GenderRestriction.ALL
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ data class EditLiveRoomInfoRequest(
|
|||||||
val menuPanId: Long = 0,
|
val menuPanId: Long = 0,
|
||||||
val menuPan: String = "",
|
val menuPan: String = "",
|
||||||
val isActiveMenuPan: Boolean? = null,
|
val isActiveMenuPan: Boolean? = null,
|
||||||
val isAdult: Boolean? = null
|
val isAdult: Boolean? = null,
|
||||||
|
val genderRestriction: GenderRestriction? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ data class GetRecentRoomInfoResponse(
|
|||||||
val notice: String,
|
val notice: String,
|
||||||
var coverImageUrl: String,
|
var coverImageUrl: String,
|
||||||
val coverImagePath: String,
|
val coverImagePath: String,
|
||||||
val numberOfPeople: Int
|
val numberOfPeople: Int,
|
||||||
|
val genderRestriction: GenderRestriction
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ data class LiveRoom(
|
|||||||
@Enumerated(value = EnumType.STRING)
|
@Enumerated(value = EnumType.STRING)
|
||||||
val type: LiveRoomType = LiveRoomType.OPEN,
|
val type: LiveRoomType = LiveRoomType.OPEN,
|
||||||
@Column(nullable = true)
|
@Column(nullable = true)
|
||||||
var password: String? = null
|
var password: String? = null,
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
var genderRestriction: GenderRestriction = GenderRestriction.ALL
|
||||||
) : BaseEntity() {
|
) : BaseEntity() {
|
||||||
@OneToOne(fetch = FetchType.LAZY)
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "member_id", nullable = false)
|
@JoinColumn(name = "member_id", nullable = false)
|
||||||
@@ -67,3 +69,7 @@ enum class LiveRoomType {
|
|||||||
enum class LiveRoomStatus {
|
enum class LiveRoomStatus {
|
||||||
NOW, RESERVATION
|
NOW, RESERVATION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class GenderRestriction {
|
||||||
|
ALL, MALE_ONLY, FEMALE_ONLY
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationItem
|
|||||||
import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem
|
import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem
|
||||||
import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListItem
|
import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListItem
|
||||||
import kr.co.vividnext.sodalive.live.room.like.QGetLiveRoomHeartListItem
|
import kr.co.vividnext.sodalive.live.room.like.QGetLiveRoomHeartListItem
|
||||||
|
import kr.co.vividnext.sodalive.member.Gender
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.member.QMember.member
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
@@ -32,7 +34,8 @@ interface LiveRoomQueryRepository {
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom>
|
): List<LiveRoom>
|
||||||
|
|
||||||
fun getLiveRoomListReservationWithDate(
|
fun getLiveRoomListReservationWithDate(
|
||||||
@@ -41,14 +44,16 @@ interface LiveRoomQueryRepository {
|
|||||||
limit: Long,
|
limit: Long,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom>
|
): List<LiveRoom>
|
||||||
|
|
||||||
fun getLiveRoomListReservationWithoutDate(
|
fun getLiveRoomListReservationWithoutDate(
|
||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom>
|
): List<LiveRoom>
|
||||||
|
|
||||||
fun getLiveRoom(id: Long): LiveRoom?
|
fun getLiveRoom(id: Long): LiveRoom?
|
||||||
@@ -76,7 +81,8 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
var where = liveRoom.channelName.isNotNull
|
var where = liveRoom.channelName.isNotNull
|
||||||
.and(liveRoom.channelName.isNotEmpty)
|
.and(liveRoom.channelName.isNotEmpty)
|
||||||
@@ -94,10 +100,34 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryFactory
|
if (effectiveGender != null && effectiveGender != Gender.NONE) {
|
||||||
|
val genderCondition = when (effectiveGender) {
|
||||||
|
Gender.MALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.MALE_ONLY)
|
||||||
|
Gender.FEMALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.FEMALE_ONLY)
|
||||||
|
Gender.NONE -> liveRoom.genderRestriction.isNotNull
|
||||||
|
}
|
||||||
|
where = if (memberId != null) {
|
||||||
|
where.and(genderCondition.or(liveRoom.member.id.eq(memberId)))
|
||||||
|
} else {
|
||||||
|
where.and(genderCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var select = queryFactory
|
||||||
.selectFrom(liveRoom)
|
.selectFrom(liveRoom)
|
||||||
.innerJoin(liveRoom.member, member)
|
.innerJoin(liveRoom.member, member)
|
||||||
.leftJoin(quarterLiveRankings).on(liveRoom.id.eq(quarterLiveRankings.roomId))
|
.leftJoin(quarterLiveRankings).on(liveRoom.id.eq(quarterLiveRankings.roomId))
|
||||||
|
|
||||||
|
if (memberId != null) {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
|
||||||
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
where = where.and(blockMember.id.isNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
return select
|
||||||
.where(where)
|
.where(where)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
@@ -116,7 +146,8 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
limit: Long,
|
limit: Long,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
var where = liveRoom.beginDateTime.goe(date)
|
var where = liveRoom.beginDateTime.goe(date)
|
||||||
.and(liveRoom.beginDateTime.lt(date.plusDays(1)))
|
.and(liveRoom.beginDateTime.lt(date.plusDays(1)))
|
||||||
@@ -138,9 +169,33 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryFactory
|
if (effectiveGender != null && effectiveGender != Gender.NONE) {
|
||||||
|
val genderCondition = when (effectiveGender) {
|
||||||
|
Gender.MALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.MALE_ONLY)
|
||||||
|
Gender.FEMALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.FEMALE_ONLY)
|
||||||
|
Gender.NONE -> liveRoom.genderRestriction.isNotNull
|
||||||
|
}
|
||||||
|
where = if (memberId != null) {
|
||||||
|
where.and(genderCondition.or(liveRoom.member.id.eq(memberId)))
|
||||||
|
} else {
|
||||||
|
where.and(genderCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var select = queryFactory
|
||||||
.selectFrom(liveRoom)
|
.selectFrom(liveRoom)
|
||||||
.innerJoin(liveRoom.member, member)
|
.innerJoin(liveRoom.member, member)
|
||||||
|
|
||||||
|
if (memberId != null) {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
|
||||||
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
where = where.and(blockMember.id.isNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
return select
|
||||||
.where(where)
|
.where(where)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
@@ -152,7 +207,8 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
var where = liveRoom.beginDateTime.gt(
|
var where = liveRoom.beginDateTime.gt(
|
||||||
LocalDateTime.now()
|
LocalDateTime.now()
|
||||||
@@ -178,6 +234,19 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (effectiveGender != null && effectiveGender != Gender.NONE) {
|
||||||
|
val genderCondition = when (effectiveGender) {
|
||||||
|
Gender.MALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.MALE_ONLY)
|
||||||
|
Gender.FEMALE -> liveRoom.genderRestriction.`in`(GenderRestriction.ALL, GenderRestriction.FEMALE_ONLY)
|
||||||
|
Gender.NONE -> liveRoom.genderRestriction.isNotNull
|
||||||
|
}
|
||||||
|
where = if (memberId != null) {
|
||||||
|
where.and(genderCondition.or(liveRoom.member.id.eq(memberId)))
|
||||||
|
} else {
|
||||||
|
where.and(genderCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val orderBy = if (memberId != null) {
|
val orderBy = if (memberId != null) {
|
||||||
listOf(
|
listOf(
|
||||||
CaseBuilder()
|
CaseBuilder()
|
||||||
@@ -190,10 +259,21 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
listOf(liveRoom.beginDateTime.asc())
|
listOf(liveRoom.beginDateTime.asc())
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryFactory
|
var select = queryFactory
|
||||||
.selectFrom(liveRoom)
|
.selectFrom(liveRoom)
|
||||||
.innerJoin(liveRoom.member, member)
|
.innerJoin(liveRoom.member, member)
|
||||||
.limit(10)
|
.limit(10)
|
||||||
|
|
||||||
|
if (memberId != null) {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
|
||||||
|
select = select.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
where = where.and(blockMember.id.isNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
return select
|
||||||
.where(where)
|
.where(where)
|
||||||
.orderBy(*orderBy.toTypedArray())
|
.orderBy(*orderBy.toTypedArray())
|
||||||
.fetch()
|
.fetch()
|
||||||
@@ -231,7 +311,8 @@ class LiveRoomQueryRepositoryImpl(
|
|||||||
liveRoom.notice,
|
liveRoom.notice,
|
||||||
liveRoom.coverImage.prepend("/").prepend(cloudFrontHost),
|
liveRoom.coverImage.prepend("/").prepend(cloudFrontHost),
|
||||||
liveRoom.coverImage,
|
liveRoom.coverImage,
|
||||||
liveRoom.numberOfPeople
|
liveRoom.numberOfPeople,
|
||||||
|
liveRoom.genderRestriction
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.from(liveRoom)
|
.from(liveRoom)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
|||||||
import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
|
import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
|
||||||
import kr.co.vividnext.sodalive.fcm.FcmEvent
|
import kr.co.vividnext.sodalive.fcm.FcmEvent
|
||||||
import kr.co.vividnext.sodalive.fcm.FcmEventType
|
import kr.co.vividnext.sodalive.fcm.FcmEventType
|
||||||
|
import kr.co.vividnext.sodalive.fcm.PushTokenRepository
|
||||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
||||||
@@ -95,6 +96,7 @@ class LiveRoomService(
|
|||||||
private val roomVisitService: LiveRoomVisitService,
|
private val roomVisitService: LiveRoomVisitService,
|
||||||
private val canPaymentService: CanPaymentService,
|
private val canPaymentService: CanPaymentService,
|
||||||
private val chargeRepository: ChargeRepository,
|
private val chargeRepository: ChargeRepository,
|
||||||
|
private val pushTokenRepository: PushTokenRepository,
|
||||||
private val memberRepository: MemberRepository,
|
private val memberRepository: MemberRepository,
|
||||||
private val tagRepository: LiveTagRepository,
|
private val tagRepository: LiveTagRepository,
|
||||||
private val canRepository: CanRepository,
|
private val canRepository: CanRepository,
|
||||||
@@ -126,6 +128,62 @@ class LiveRoomService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyLanguageTagToRoomTags(
|
||||||
|
memberId: Long?,
|
||||||
|
tags: List<String>,
|
||||||
|
languageTagByMemberId: Map<Long, String?>? = null
|
||||||
|
): List<String> {
|
||||||
|
val randomizedTags = tags.shuffled()
|
||||||
|
val languageTag = getCreatorLanguageTag(memberId, languageTagByMemberId) ?: return randomizedTags
|
||||||
|
val filteredTags = randomizedTags.filterNot { it == languageTag }
|
||||||
|
return listOf(languageTag) + filteredTags
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCreatorLanguageTag(
|
||||||
|
memberId: Long?,
|
||||||
|
languageTagByMemberId: Map<Long, String?>? = null
|
||||||
|
): String? {
|
||||||
|
if (memberId == null) return null
|
||||||
|
if (languageTagByMemberId != null && languageTagByMemberId.containsKey(memberId)) {
|
||||||
|
return languageTagByMemberId[memberId]
|
||||||
|
}
|
||||||
|
|
||||||
|
val tokens = pushTokenRepository.findByMemberId(memberId)
|
||||||
|
val languageCode = tokens
|
||||||
|
.filterNot { it.languageCode.isNullOrBlank() }
|
||||||
|
.maxByOrNull { it.updatedAt ?: LocalDateTime.MIN }
|
||||||
|
?.languageCode
|
||||||
|
|
||||||
|
val languageTag = when (languageCode?.lowercase()?.take(2)) {
|
||||||
|
"ko" -> "한국어"
|
||||||
|
"ja" -> "일본어"
|
||||||
|
"en" -> "영어"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return languageTag
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildLanguageTagMap(memberIds: List<Long>): Map<Long, String?> {
|
||||||
|
val tokens = pushTokenRepository.findByMemberIds(memberIds)
|
||||||
|
if (tokens.isEmpty()) return emptyMap()
|
||||||
|
|
||||||
|
val latestTokenByMemberId = tokens
|
||||||
|
.filter { it.member?.id != null }
|
||||||
|
.groupBy { it.member!!.id!! }
|
||||||
|
.mapValues { (_, memberTokens) ->
|
||||||
|
memberTokens.maxByOrNull { it.updatedAt ?: LocalDateTime.MIN }
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestTokenByMemberId.mapValues { (_, token) ->
|
||||||
|
when (token?.languageCode?.lowercase()?.take(2)) {
|
||||||
|
"ko" -> "한국어"
|
||||||
|
"ja" -> "일본어"
|
||||||
|
"en" -> "영어"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun getRoomList(
|
fun getRoomList(
|
||||||
dateString: String?,
|
dateString: String?,
|
||||||
@@ -135,13 +193,21 @@ class LiveRoomService(
|
|||||||
member: Member?,
|
member: Member?,
|
||||||
timezone: String
|
timezone: String
|
||||||
): List<GetRoomListResponse> {
|
): List<GetRoomListResponse> {
|
||||||
|
val effectiveGender = member?.let {
|
||||||
|
if (it.auth != null) {
|
||||||
|
if (it.auth!!.gender == 1) Gender.MALE else Gender.FEMALE
|
||||||
|
} else {
|
||||||
|
it.gender
|
||||||
|
}
|
||||||
|
}
|
||||||
val roomList = if (status == LiveRoomStatus.NOW) {
|
val roomList = if (status == LiveRoomStatus.NOW) {
|
||||||
getLiveRoomListNow(
|
getLiveRoomListNow(
|
||||||
pageable,
|
pageable,
|
||||||
timezone,
|
timezone,
|
||||||
memberId = member?.id,
|
memberId = member?.id,
|
||||||
isCreator = member?.role == MemberRole.CREATOR,
|
isCreator = member?.role == MemberRole.CREATOR,
|
||||||
isAdult = member?.auth != null && isAdultContentVisible
|
isAdult = member?.auth != null && isAdultContentVisible,
|
||||||
|
effectiveGender = effectiveGender
|
||||||
)
|
)
|
||||||
} else if (dateString != null) {
|
} else if (dateString != null) {
|
||||||
getLiveRoomListReservationWithDate(
|
getLiveRoomListReservationWithDate(
|
||||||
@@ -150,25 +216,23 @@ class LiveRoomService(
|
|||||||
timezone,
|
timezone,
|
||||||
memberId = member?.id,
|
memberId = member?.id,
|
||||||
isCreator = member?.role == MemberRole.CREATOR,
|
isCreator = member?.role == MemberRole.CREATOR,
|
||||||
isAdult = member?.auth != null && isAdultContentVisible
|
isAdult = member?.auth != null && isAdultContentVisible,
|
||||||
|
effectiveGender = effectiveGender
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
getLiveRoomListReservationWithoutDate(
|
getLiveRoomListReservationWithoutDate(
|
||||||
timezone,
|
timezone,
|
||||||
isCreator = member?.role == MemberRole.CREATOR,
|
isCreator = member?.role == MemberRole.CREATOR,
|
||||||
memberId = member?.id,
|
memberId = member?.id,
|
||||||
isAdult = member?.auth != null && isAdultContentVisible
|
isAdult = member?.auth != null && isAdultContentVisible,
|
||||||
|
effectiveGender = effectiveGender
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val creatorIds = roomList.mapNotNull { it.member?.id }.distinct()
|
||||||
|
val languageTagByMemberId = buildLanguageTagMap(creatorIds)
|
||||||
|
|
||||||
return roomList
|
return roomList
|
||||||
.filter {
|
|
||||||
if (member?.id != null) {
|
|
||||||
!blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.member!!.id!!)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.map {
|
.map {
|
||||||
val roomInfo = roomInfoRepository.findByIdOrNull(it.id!!)
|
val roomInfo = roomInfoRepository.findByIdOrNull(it.id!!)
|
||||||
|
|
||||||
@@ -193,6 +257,11 @@ class LiveRoomService(
|
|||||||
val beginDateTimeUtc = it.beginDateTime
|
val beginDateTimeUtc = it.beginDateTime
|
||||||
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||||
|
|
||||||
|
val tags = it.tags
|
||||||
|
.filter { tag -> tag.tag.isActive }
|
||||||
|
.map { tag -> tag.tag.tag }
|
||||||
|
.let { list -> applyLanguageTagToRoomTags(it.member?.id, list, languageTagByMemberId) }
|
||||||
|
|
||||||
GetRoomListResponse(
|
GetRoomListResponse(
|
||||||
roomId = it.id!!,
|
roomId = it.id!!,
|
||||||
title = it.title,
|
title = it.title,
|
||||||
@@ -213,11 +282,7 @@ class LiveRoomService(
|
|||||||
},
|
},
|
||||||
creatorNickname = it.member!!.nickname,
|
creatorNickname = it.member!!.nickname,
|
||||||
creatorId = it.member!!.id!!,
|
creatorId = it.member!!.id!!,
|
||||||
tags = it.tags
|
tags = tags,
|
||||||
.asSequence()
|
|
||||||
.filter { tag -> tag.tag.isActive }
|
|
||||||
.map { tag -> tag.tag.tag }
|
|
||||||
.toList(),
|
|
||||||
coverImageUrl = if (it.coverImage!!.startsWith("https://")) {
|
coverImageUrl = if (it.coverImage!!.startsWith("https://")) {
|
||||||
it.coverImage!!
|
it.coverImage!!
|
||||||
} else {
|
} else {
|
||||||
@@ -234,7 +299,8 @@ class LiveRoomService(
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
return repository.getLiveRoomListNow(
|
return repository.getLiveRoomListNow(
|
||||||
offset = pageable.offset,
|
offset = pageable.offset,
|
||||||
@@ -242,7 +308,8 @@ class LiveRoomService(
|
|||||||
timezone = timezone,
|
timezone = timezone,
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
isCreator = isCreator,
|
isCreator = isCreator,
|
||||||
isAdult = isAdult
|
isAdult = isAdult,
|
||||||
|
effectiveGender = effectiveGender
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +319,8 @@ class LiveRoomService(
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
val date = LocalDate.parse(dateString, dateTimeFormatter).atStartOfDay()
|
val date = LocalDate.parse(dateString, dateTimeFormatter).atStartOfDay()
|
||||||
@@ -266,7 +334,8 @@ class LiveRoomService(
|
|||||||
limit = pageable.pageSize.toLong(),
|
limit = pageable.pageSize.toLong(),
|
||||||
memberId = memberId,
|
memberId = memberId,
|
||||||
isCreator = isCreator,
|
isCreator = isCreator,
|
||||||
isAdult = isAdult
|
isAdult = isAdult,
|
||||||
|
effectiveGender = effectiveGender
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,9 +343,10 @@ class LiveRoomService(
|
|||||||
timezone: String,
|
timezone: String,
|
||||||
memberId: Long?,
|
memberId: Long?,
|
||||||
isCreator: Boolean,
|
isCreator: Boolean,
|
||||||
isAdult: Boolean
|
isAdult: Boolean,
|
||||||
|
effectiveGender: Gender?
|
||||||
): List<LiveRoom> {
|
): List<LiveRoom> {
|
||||||
return repository.getLiveRoomListReservationWithoutDate(timezone, memberId, isCreator, isAdult)
|
return repository.getLiveRoomListReservationWithoutDate(timezone, memberId, isCreator, isAdult, effectiveGender)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -338,7 +408,8 @@ class LiveRoomService(
|
|||||||
},
|
},
|
||||||
type = request.type,
|
type = request.type,
|
||||||
password = request.password,
|
password = request.password,
|
||||||
isAvailableJoinCreator = request.isAvailableJoinCreator
|
isAvailableJoinCreator = request.isAvailableJoinCreator,
|
||||||
|
genderRestriction = request.genderRestriction
|
||||||
)
|
)
|
||||||
room.member = member
|
room.member = member
|
||||||
|
|
||||||
@@ -417,7 +488,8 @@ class LiveRoomService(
|
|||||||
isAuth = createdRoom.isAdult,
|
isAuth = createdRoom.isAdult,
|
||||||
isAvailableJoinCreator = createdRoom.isAvailableJoinCreator,
|
isAvailableJoinCreator = createdRoom.isAvailableJoinCreator,
|
||||||
roomId = createdRoom.id,
|
roomId = createdRoom.id,
|
||||||
creatorId = createdRoom.member!!.id
|
creatorId = createdRoom.member!!.id,
|
||||||
|
genderRestriction = createdRoom.genderRestriction
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -432,6 +504,10 @@ class LiveRoomService(
|
|||||||
throw SodaException(messageKey = "live.room.adult_verification_required")
|
throw SodaException(messageKey = "live.room.adult_verification_required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!member.canEnter(room.genderRestriction) && room.member!!.id!! != member.id!!) {
|
||||||
|
throw SodaException(messageKey = "live.room.gender_restricted")
|
||||||
|
}
|
||||||
|
|
||||||
val beginDateTime = room.beginDateTime
|
val beginDateTime = room.beginDateTime
|
||||||
.atZone(ZoneId.of("UTC"))
|
.atZone(ZoneId.of("UTC"))
|
||||||
.withZoneSameInstant(ZoneId.of(timezone))
|
.withZoneSameInstant(ZoneId.of(timezone))
|
||||||
@@ -440,12 +516,17 @@ class LiveRoomService(
|
|||||||
val beginDateTimeUtc = room.beginDateTime
|
val beginDateTimeUtc = room.beginDateTime
|
||||||
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||||
|
|
||||||
|
val languageTagByMemberId = buildLanguageTagMap(listOfNotNull(room.member?.id))
|
||||||
val response = GetRoomDetailResponse(
|
val response = GetRoomDetailResponse(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
title = room.title,
|
title = room.title,
|
||||||
notice = room.notice,
|
notice = room.notice,
|
||||||
price = room.price,
|
price = room.price,
|
||||||
tags = room.tags.asSequence().filter { it.tag.isActive }.map { it.tag.tag }.toList(),
|
tags = room.tags.asSequence()
|
||||||
|
.filter { it.tag.isActive }
|
||||||
|
.map { it.tag.tag }
|
||||||
|
.toList()
|
||||||
|
.let { tags -> applyLanguageTagToRoomTags(room.member?.id, tags, languageTagByMemberId) },
|
||||||
numberOfParticipantsTotal = room.numberOfPeople,
|
numberOfParticipantsTotal = room.numberOfPeople,
|
||||||
numberOfParticipants = 0,
|
numberOfParticipants = 0,
|
||||||
channelName = room.channelName,
|
channelName = room.channelName,
|
||||||
@@ -454,6 +535,7 @@ class LiveRoomService(
|
|||||||
isPaid = false,
|
isPaid = false,
|
||||||
isAdult = room.isAdult,
|
isAdult = room.isAdult,
|
||||||
isPrivateRoom = room.type == LiveRoomType.PRIVATE,
|
isPrivateRoom = room.type == LiveRoomType.PRIVATE,
|
||||||
|
genderRestriction = room.genderRestriction,
|
||||||
password = room.password
|
password = room.password
|
||||||
)
|
)
|
||||||
response.manager = GetRoomDetailManager(room.member!!, cloudFrontHost = cloudFrontHost)
|
response.manager = GetRoomDetailManager(room.member!!, cloudFrontHost = cloudFrontHost)
|
||||||
@@ -573,7 +655,8 @@ class LiveRoomService(
|
|||||||
isAuth = room.isAdult,
|
isAuth = room.isAdult,
|
||||||
isAvailableJoinCreator = room.isAvailableJoinCreator,
|
isAvailableJoinCreator = room.isAvailableJoinCreator,
|
||||||
roomId = room.id,
|
roomId = room.id,
|
||||||
creatorId = room.member!!.id
|
creatorId = room.member!!.id,
|
||||||
|
genderRestriction = room.genderRestriction
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -672,6 +755,10 @@ class LiveRoomService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (room.member!!.id!! != member.id!! && !member.canEnter(room.genderRestriction)) {
|
||||||
|
throw SodaException(messageKey = "live.room.gender_restricted")
|
||||||
|
}
|
||||||
|
|
||||||
val lock = getOrCreateLock(memberId = member.id!!)
|
val lock = getOrCreateLock(memberId = member.id!!)
|
||||||
lock.write {
|
lock.write {
|
||||||
var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||||
@@ -786,6 +873,10 @@ class LiveRoomService(
|
|||||||
room.isAdult = request.isAdult
|
room.isAdult = request.isAdult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.genderRestriction != null) {
|
||||||
|
room.genderRestriction = request.genderRestriction
|
||||||
|
}
|
||||||
|
|
||||||
if (request.isActiveMenuPan != null) {
|
if (request.isActiveMenuPan != null) {
|
||||||
if (request.isActiveMenuPan) {
|
if (request.isActiveMenuPan) {
|
||||||
if (request.menuPanId > 0) {
|
if (request.menuPanId > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.live.room.detail
|
package kr.co.vividnext.sodalive.live.room.detail
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
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
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ data class GetRoomDetailResponse(
|
|||||||
var isPaid: Boolean,
|
var isPaid: Boolean,
|
||||||
val isAdult: Boolean,
|
val isAdult: Boolean,
|
||||||
val isPrivateRoom: Boolean,
|
val isPrivateRoom: Boolean,
|
||||||
|
val genderRestriction: GenderRestriction,
|
||||||
val password: String?,
|
val password: String?,
|
||||||
val tags: List<String>,
|
val tags: List<String>,
|
||||||
val channelName: String?,
|
val channelName: String?,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.member
|
|||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.BaseEntity
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse
|
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
import kr.co.vividnext.sodalive.member.auth.Auth
|
import kr.co.vividnext.sodalive.member.auth.Auth
|
||||||
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
import kr.co.vividnext.sodalive.member.following.CreatorFollowing
|
||||||
import kr.co.vividnext.sodalive.member.notification.MemberNotification
|
import kr.co.vividnext.sodalive.member.notification.MemberNotification
|
||||||
@@ -46,6 +47,9 @@ data class Member(
|
|||||||
|
|
||||||
var isVisibleDonationRank: Boolean = true,
|
var isVisibleDonationRank: Boolean = true,
|
||||||
|
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
var donationRankingPeriod: DonationRankingPeriod? = DonationRankingPeriod.CUMULATIVE,
|
||||||
|
|
||||||
var isActive: Boolean = true,
|
var isActive: Boolean = true,
|
||||||
|
|
||||||
var container: String = "web",
|
var container: String = "web",
|
||||||
@@ -148,6 +152,22 @@ data class Member(
|
|||||||
follow = follow
|
follow = follow
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canEnter(restriction: GenderRestriction): Boolean {
|
||||||
|
val effectiveGender = if (auth != null) {
|
||||||
|
if (auth!!.gender == 1) Gender.MALE else Gender.FEMALE
|
||||||
|
} else {
|
||||||
|
gender
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectiveGender == Gender.NONE) return true
|
||||||
|
|
||||||
|
return when (restriction) {
|
||||||
|
GenderRestriction.ALL -> true
|
||||||
|
GenderRestriction.MALE_ONLY -> effectiveGender == Gender.MALE
|
||||||
|
GenderRestriction.FEMALE_ONLY -> effectiveGender == Gender.FEMALE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Gender {
|
enum class Gender {
|
||||||
@@ -161,3 +181,7 @@ enum class MemberRole {
|
|||||||
enum class MemberProvider {
|
enum class MemberProvider {
|
||||||
EMAIL, KAKAO, GOOGLE, APPLE, LINE
|
EMAIL, KAKAO, GOOGLE, APPLE, LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class DonationRankingPeriod {
|
||||||
|
WEEKLY, CUMULATIVE
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.fcm.PushTokenInfo
|
|||||||
import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken
|
import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken
|
||||||
import kr.co.vividnext.sodalive.fcm.QPushTokenInfo
|
import kr.co.vividnext.sodalive.fcm.QPushTokenInfo
|
||||||
import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
|
import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
|
||||||
|
import kr.co.vividnext.sodalive.live.room.GenderRestriction
|
||||||
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||||
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.auth.QAuth.auth
|
||||||
@@ -35,14 +36,16 @@ interface MemberQueryRepository {
|
|||||||
fun getCreateLiveRoomNotificationRecipientPushTokens(
|
fun getCreateLiveRoomNotificationRecipientPushTokens(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
isAuth: Boolean,
|
isAuth: Boolean,
|
||||||
isAvailableJoinCreator: Boolean
|
isAvailableJoinCreator: Boolean,
|
||||||
|
genderRestriction: GenderRestriction? = null
|
||||||
): List<PushTokenInfo>
|
): List<PushTokenInfo>
|
||||||
|
|
||||||
fun getStartLiveRoomNotificationRecipientPushTokens(
|
fun getStartLiveRoomNotificationRecipientPushTokens(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
roomId: Long,
|
roomId: Long,
|
||||||
isAuth: Boolean,
|
isAuth: Boolean,
|
||||||
isAvailableJoinCreator: Boolean
|
isAvailableJoinCreator: Boolean,
|
||||||
|
genderRestriction: GenderRestriction? = null
|
||||||
): List<PushTokenInfo>
|
): List<PushTokenInfo>
|
||||||
|
|
||||||
fun getUploadContentNotificationRecipientPushTokens(
|
fun getUploadContentNotificationRecipientPushTokens(
|
||||||
@@ -132,7 +135,8 @@ class MemberQueryRepositoryImpl(
|
|||||||
override fun getCreateLiveRoomNotificationRecipientPushTokens(
|
override fun getCreateLiveRoomNotificationRecipientPushTokens(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
isAuth: Boolean,
|
isAuth: Boolean,
|
||||||
isAvailableJoinCreator: Boolean
|
isAvailableJoinCreator: Boolean,
|
||||||
|
genderRestriction: GenderRestriction?
|
||||||
): List<PushTokenInfo> {
|
): List<PushTokenInfo> {
|
||||||
val member = QMember.member
|
val member = QMember.member
|
||||||
val creator = QMember.member
|
val creator = QMember.member
|
||||||
@@ -158,6 +162,10 @@ class MemberQueryRepositoryImpl(
|
|||||||
where = where.and(creatorFollowing.member.role.ne(MemberRole.CREATOR))
|
where = where.and(creatorFollowing.member.role.ne(MemberRole.CREATOR))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (genderRestriction != null && genderRestriction != GenderRestriction.ALL) {
|
||||||
|
where = where.and(getGenderCondition(genderRestriction))
|
||||||
|
}
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.select(
|
.select(
|
||||||
QPushTokenInfo(
|
QPushTokenInfo(
|
||||||
@@ -180,7 +188,8 @@ class MemberQueryRepositoryImpl(
|
|||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
roomId: Long,
|
roomId: Long,
|
||||||
isAuth: Boolean,
|
isAuth: Boolean,
|
||||||
isAvailableJoinCreator: Boolean
|
isAvailableJoinCreator: Boolean,
|
||||||
|
genderRestriction: GenderRestriction?
|
||||||
): List<PushTokenInfo> {
|
): List<PushTokenInfo> {
|
||||||
val member = QMember.member
|
val member = QMember.member
|
||||||
val creator = QMember.member
|
val creator = QMember.member
|
||||||
@@ -206,6 +215,10 @@ class MemberQueryRepositoryImpl(
|
|||||||
where = where.and(creatorFollowing.member.role.ne(MemberRole.CREATOR))
|
where = where.and(creatorFollowing.member.role.ne(MemberRole.CREATOR))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (genderRestriction != null && genderRestriction != GenderRestriction.ALL) {
|
||||||
|
where = where.and(getGenderCondition(genderRestriction))
|
||||||
|
}
|
||||||
|
|
||||||
val followingMemberPushToken = queryFactory
|
val followingMemberPushToken = queryFactory
|
||||||
.select(
|
.select(
|
||||||
QPushTokenInfo(
|
QPushTokenInfo(
|
||||||
@@ -237,6 +250,10 @@ class MemberQueryRepositoryImpl(
|
|||||||
where = where.and(auth.isNotNull)
|
where = where.and(auth.isNotNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (genderRestriction != null && genderRestriction != GenderRestriction.ALL) {
|
||||||
|
where = where.and(getGenderCondition(genderRestriction, liveReservation.member))
|
||||||
|
}
|
||||||
|
|
||||||
val reservationMemberPushToken = queryFactory
|
val reservationMemberPushToken = queryFactory
|
||||||
.select(
|
.select(
|
||||||
QPushTokenInfo(
|
QPushTokenInfo(
|
||||||
@@ -256,6 +273,33 @@ class MemberQueryRepositoryImpl(
|
|||||||
return (followingMemberPushToken + reservationMemberPushToken).distinctBy { it.token }
|
return (followingMemberPushToken + reservationMemberPushToken).distinctBy { it.token }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getGenderCondition(
|
||||||
|
genderRestriction: GenderRestriction,
|
||||||
|
qMember: QMember = member
|
||||||
|
) = when (genderRestriction) {
|
||||||
|
GenderRestriction.MALE_ONLY -> {
|
||||||
|
auth.isNotNull.and(auth.gender.eq(1))
|
||||||
|
.or(
|
||||||
|
auth.isNull.and(
|
||||||
|
qMember.gender.eq(Gender.MALE)
|
||||||
|
.or(qMember.gender.eq(Gender.NONE))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
GenderRestriction.FEMALE_ONLY -> {
|
||||||
|
auth.isNotNull.and(auth.gender.eq(0))
|
||||||
|
.or(
|
||||||
|
auth.isNull.and(
|
||||||
|
qMember.gender.eq(Gender.FEMALE)
|
||||||
|
.or(qMember.gender.eq(Gender.NONE))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
override fun getUploadContentNotificationRecipientPushTokens(
|
override fun getUploadContentNotificationRecipientPushTokens(
|
||||||
creatorId: Long,
|
creatorId: Long,
|
||||||
isAuth: Boolean
|
isAuth: Boolean
|
||||||
|
|||||||
@@ -726,6 +726,10 @@ class MemberService(
|
|||||||
member.isVisibleDonationRank = profileUpdateRequest.isVisibleDonationRank
|
member.isVisibleDonationRank = profileUpdateRequest.isVisibleDonationRank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (profileUpdateRequest.donationRankingPeriod != null) {
|
||||||
|
member.donationRankingPeriod = profileUpdateRequest.donationRankingPeriod
|
||||||
|
}
|
||||||
|
|
||||||
return ProfileResponse(member, cloudFrontHost, profileUpdateRequest.container)
|
return ProfileResponse(member, cloudFrontHost, profileUpdateRequest.container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ data class ProfileUpdateRequest(
|
|||||||
val websiteUrl: String? = null,
|
val websiteUrl: String? = null,
|
||||||
val blogUrl: String? = null,
|
val blogUrl: String? = null,
|
||||||
val isVisibleDonationRank: Boolean? = null,
|
val isVisibleDonationRank: Boolean? = null,
|
||||||
|
val donationRankingPeriod: DonationRankingPeriod? = null,
|
||||||
val container: String
|
val container: String
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user