라이브 메인 - 추천라이브, 추천채널, 예약중인 라이브, 진행중인 라이브, 이벤트 배너 API 추가
This commit is contained in:
parent
ee124e258e
commit
ee99dd3147
|
@ -0,0 +1,23 @@
|
||||||
|
package kr.co.vividnext.sodalive.event
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
|
import javax.persistence.Column
|
||||||
|
import javax.persistence.Entity
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class Event(
|
||||||
|
@Column(nullable = false)
|
||||||
|
var thumbnailImage: String,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var detailImage: String?,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var popupImage: String?,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var link: String?,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var title: String?,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var isPopup: Boolean = false,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var isActive: Boolean = true
|
||||||
|
) : BaseEntity()
|
|
@ -0,0 +1,13 @@
|
||||||
|
package kr.co.vividnext.sodalive.event
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/event")
|
||||||
|
class EventController(private val service: EventService) {
|
||||||
|
@GetMapping
|
||||||
|
fun getEventList() = ApiResponse.ok(service.getEventList())
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package kr.co.vividnext.sodalive.event
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.event.QEvent.event
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface EventRepository : JpaRepository<Event, Long>, EventQueryRepository
|
||||||
|
|
||||||
|
interface EventQueryRepository {
|
||||||
|
fun getEventList(): List<EventItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class EventQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : EventQueryRepository {
|
||||||
|
override fun getEventList(): List<EventItem> {
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QEventItem(
|
||||||
|
event.id,
|
||||||
|
event.title,
|
||||||
|
event.thumbnailImage,
|
||||||
|
event.detailImage,
|
||||||
|
event.popupImage,
|
||||||
|
event.link,
|
||||||
|
event.isPopup
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(event)
|
||||||
|
.where(event.isActive.isTrue)
|
||||||
|
.orderBy(event.id.desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package kr.co.vividnext.sodalive.event
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class EventService(
|
||||||
|
private val repository: EventRepository,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val cloudFrontHost: String
|
||||||
|
) {
|
||||||
|
fun getEventList(): GetEventResponse {
|
||||||
|
val eventList = repository.getEventList()
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
if (!it.thumbnailImageUrl.startsWith("https://")) {
|
||||||
|
it.thumbnailImageUrl = "$cloudFrontHost/${it.thumbnailImageUrl}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.detailImageUrl != null && !it.detailImageUrl!!.startsWith("https://")) {
|
||||||
|
it.detailImageUrl = "$cloudFrontHost/${it.detailImageUrl}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.popupImageUrl != null && !it.popupImageUrl!!.startsWith("https://")) {
|
||||||
|
it.popupImageUrl = "$cloudFrontHost/${it.popupImageUrl}"
|
||||||
|
}
|
||||||
|
|
||||||
|
it
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetEventResponse(0, eventList)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package kr.co.vividnext.sodalive.event
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
|
||||||
|
data class GetEventResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val eventList: List<EventItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class EventItem @QueryProjection constructor(
|
||||||
|
val id: Long,
|
||||||
|
val title: String? = null,
|
||||||
|
var thumbnailImageUrl: String,
|
||||||
|
var detailImageUrl: String? = null,
|
||||||
|
var popupImageUrl: String? = null,
|
||||||
|
val link: String? = null,
|
||||||
|
val isPopup: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
data class GetRecommendChannelResponse(
|
||||||
|
val creatorId: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val profileImageUrl: String,
|
||||||
|
val isOnAir: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
data class GetRecommendLiveResponse(
|
||||||
|
val imageUrl: String,
|
||||||
|
val creatorId: Long
|
||||||
|
)
|
|
@ -0,0 +1,29 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
class LiveRecommendController(private val service: LiveRecommendService) {
|
||||||
|
@GetMapping("/live/recommend")
|
||||||
|
fun getRecommendLive(
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.getRecommendLive(member))
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/live/recommend/channel")
|
||||||
|
fun getRecommendChannelList(
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.getRecommendChannelList(member))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
import com.querydsl.core.types.Projections
|
||||||
|
import com.querydsl.core.types.dsl.Expressions
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.live.recommend.QRecommendLiveCreatorBanner.recommendLiveCreatorBanner
|
||||||
|
import kr.co.vividnext.sodalive.live.room.LiveRoomType
|
||||||
|
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class LiveRecommendRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
|
fun getRecommendLive(memberId: Long, isAdult: Boolean): List<GetRecommendLiveResponse> {
|
||||||
|
val dateNow = LocalDateTime.now()
|
||||||
|
|
||||||
|
var where = recommendLiveCreatorBanner.startDate.loe(dateNow)
|
||||||
|
.and(recommendLiveCreatorBanner.endDate.goe(dateNow))
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where.and(recommendLiveCreatorBanner.isAdult.isFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
GetRecommendLiveResponse::class.java,
|
||||||
|
recommendLiveCreatorBanner.image,
|
||||||
|
recommendLiveCreatorBanner.creator.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(recommendLiveCreatorBanner)
|
||||||
|
.where(where)
|
||||||
|
.orderBy(recommendLiveCreatorBanner.orders.asc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOnAirRecommendChannelList(
|
||||||
|
memberId: Long,
|
||||||
|
isAdult: Boolean
|
||||||
|
): List<GetRecommendChannelResponse> {
|
||||||
|
var where = member.role.eq(MemberRole.CREATOR)
|
||||||
|
.and(member.isActive.isTrue)
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where.and(liveRoom.isAdult.isFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
GetRecommendChannelResponse::class.java,
|
||||||
|
member.id,
|
||||||
|
member.nickname,
|
||||||
|
member.profileImage,
|
||||||
|
Expressions.asBoolean(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(liveRoom)
|
||||||
|
.rightJoin(liveRoom.member, member)
|
||||||
|
.where(
|
||||||
|
where
|
||||||
|
.and(liveRoom.isActive.isTrue)
|
||||||
|
.and(liveRoom.type.ne(LiveRoomType.SECRET))
|
||||||
|
.and(liveRoom.channelName.isNotNull)
|
||||||
|
.and(liveRoom.channelName.isNotEmpty)
|
||||||
|
)
|
||||||
|
.groupBy(member.id)
|
||||||
|
.orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
|
||||||
|
.limit(20)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecommendChannelList(
|
||||||
|
memberId: Long,
|
||||||
|
withOutCreatorList: List<Long>,
|
||||||
|
limit: Long
|
||||||
|
): List<GetRecommendChannelResponse> {
|
||||||
|
val where = member.role.eq(MemberRole.CREATOR)
|
||||||
|
.and(member.isActive.isTrue)
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
Projections.constructor(
|
||||||
|
GetRecommendChannelResponse::class.java,
|
||||||
|
member.id,
|
||||||
|
member.nickname,
|
||||||
|
member.profileImage,
|
||||||
|
Expressions.asBoolean(false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(member)
|
||||||
|
.where(where.and(member.id.notIn(withOutCreatorList)))
|
||||||
|
.orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
|
||||||
|
.limit(limit)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class LiveRecommendService(private val repository: LiveRecommendRepository) {
|
||||||
|
|
||||||
|
fun getRecommendLive(member: Member): List<GetRecommendLiveResponse> {
|
||||||
|
return repository.getRecommendLive(
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = member.auth != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecommendChannelList(member: Member): List<GetRecommendChannelResponse> {
|
||||||
|
val onAirChannelList = repository.getOnAirRecommendChannelList(member.id!!, isAdult = member.auth != null)
|
||||||
|
|
||||||
|
if (onAirChannelList.size >= 20) {
|
||||||
|
return onAirChannelList
|
||||||
|
}
|
||||||
|
|
||||||
|
val onAirCreatorIdList = onAirChannelList.asSequence()
|
||||||
|
.map { it.creatorId }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val notOnAirCreatorList = repository.getRecommendChannelList(
|
||||||
|
member.id!!,
|
||||||
|
withOutCreatorList = onAirCreatorIdList,
|
||||||
|
limit = (20 - onAirChannelList.size).toLong()
|
||||||
|
)
|
||||||
|
|
||||||
|
return onAirChannelList + notOnAirCreatorList
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.Column
|
||||||
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.FetchType
|
||||||
|
import javax.persistence.JoinColumn
|
||||||
|
import javax.persistence.ManyToOne
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class RecommendLiveCreatorBanner(
|
||||||
|
@Column(nullable = false)
|
||||||
|
var image: String,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var startDate: LocalDateTime,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var endDate: LocalDateTime,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var isAdult: Boolean = false,
|
||||||
|
@Column(nullable = false)
|
||||||
|
var orders: Int = 1
|
||||||
|
) : BaseEntity() {
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "creator_id", nullable = false)
|
||||||
|
var creator: Member? = null
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
|
data class GetRoomListResponse(
|
||||||
|
val roomId: Long,
|
||||||
|
val title: String,
|
||||||
|
val content: String,
|
||||||
|
val beginDateTime: String,
|
||||||
|
val numberOfParticipate: Int,
|
||||||
|
val numberOfPeople: Int,
|
||||||
|
val coverImageUrl: String,
|
||||||
|
val isAdult: Boolean,
|
||||||
|
val price: Int,
|
||||||
|
val tags: List<String>,
|
||||||
|
val channelName: String?,
|
||||||
|
val managerNickname: String,
|
||||||
|
val managerId: Long,
|
||||||
|
val isReservation: Boolean,
|
||||||
|
val isPrivateRoom: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,51 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.BaseEntity
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.Column
|
||||||
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.EnumType
|
||||||
|
import javax.persistence.Enumerated
|
||||||
|
import javax.persistence.FetchType
|
||||||
|
import javax.persistence.JoinColumn
|
||||||
|
import javax.persistence.OneToOne
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class LiveRoom(
|
||||||
|
var title: String,
|
||||||
|
@Column(columnDefinition = "TEXT", nullable = false)
|
||||||
|
var notice: String,
|
||||||
|
var beginDateTime: LocalDateTime,
|
||||||
|
var numberOfPeople: Int,
|
||||||
|
var coverImage: String? = null,
|
||||||
|
var bgImage: String? = null,
|
||||||
|
var isAdult: Boolean,
|
||||||
|
val price: Int = 0,
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
val type: LiveRoomType = LiveRoomType.OPEN,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var password: String? = null
|
||||||
|
) : BaseEntity() {
|
||||||
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "member_id", nullable = false)
|
||||||
|
var member: Member? = null
|
||||||
|
|
||||||
|
var channelName: String? = null
|
||||||
|
var isActive: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LiveRoomType {
|
||||||
|
// 공개
|
||||||
|
OPEN,
|
||||||
|
|
||||||
|
// 비공개
|
||||||
|
PRIVATE,
|
||||||
|
|
||||||
|
// 비밀방
|
||||||
|
SECRET
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LiveRoomStatus {
|
||||||
|
NOW, RESERVATION
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/live/room")
|
||||||
|
class LiveRoomController(private val service: LiveRoomService) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
fun getRoomList(
|
||||||
|
@RequestParam timezone: String,
|
||||||
|
@RequestParam dateString: String? = null,
|
||||||
|
@RequestParam status: LiveRoomStatus,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(service.getRoomList(dateString, status, pageable, member, timezone))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
|
import com.querydsl.core.types.OrderSpecifier
|
||||||
|
import com.querydsl.core.types.Predicate
|
||||||
|
import com.querydsl.core.types.dsl.CaseBuilder
|
||||||
|
import com.querydsl.core.types.dsl.Expressions
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface LiveRoomRepository : JpaRepository<LiveRoom, Long>, LiveRoomQueryRepository
|
||||||
|
|
||||||
|
interface LiveRoomQueryRepository {
|
||||||
|
fun getLiveRoomList(
|
||||||
|
dateString: String?,
|
||||||
|
status: LiveRoomStatus,
|
||||||
|
pageable: Pageable,
|
||||||
|
member: Member,
|
||||||
|
timezone: String,
|
||||||
|
isAdult: Boolean
|
||||||
|
): List<LiveRoom>
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveRoomQueryRepository {
|
||||||
|
override fun getLiveRoomList(
|
||||||
|
dateString: String?,
|
||||||
|
status: LiveRoomStatus,
|
||||||
|
pageable: Pageable,
|
||||||
|
member: Member,
|
||||||
|
timezone: String,
|
||||||
|
isAdult: Boolean
|
||||||
|
): List<LiveRoom> {
|
||||||
|
var where: Predicate
|
||||||
|
|
||||||
|
if (status == LiveRoomStatus.NOW) {
|
||||||
|
where = liveRoom.channelName.isNotNull
|
||||||
|
.and(liveRoom.channelName.isNotEmpty)
|
||||||
|
} else {
|
||||||
|
where = if (dateString != null) {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
|
val date = LocalDate.parse(dateString, dateTimeFormatter).atStartOfDay()
|
||||||
|
.atZone(ZoneId.of(timezone))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
liveRoom.beginDateTime.goe(date)
|
||||||
|
.and(liveRoom.beginDateTime.lt(date.plusDays(1)))
|
||||||
|
.and(
|
||||||
|
liveRoom.channelName.isNull
|
||||||
|
.or(liveRoom.channelName.isEmpty)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
liveRoom.beginDateTime.gt(
|
||||||
|
LocalDateTime.now()
|
||||||
|
.atZone(ZoneId.of(timezone))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
)
|
||||||
|
.and(
|
||||||
|
liveRoom.channelName.isNull
|
||||||
|
.or(liveRoom.channelName.isEmpty)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where.and(liveRoom.isAdult.isFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
where = where.and(liveRoom.isActive.isTrue)
|
||||||
|
.and(liveRoom.member.isNotNull)
|
||||||
|
.and(liveRoom.type.ne(LiveRoomType.SECRET))
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(liveRoom)
|
||||||
|
.offset(pageable.offset)
|
||||||
|
.limit(pageable.pageSize.toLong())
|
||||||
|
.where(where)
|
||||||
|
.orderBy(
|
||||||
|
*orderByFieldAccountId(
|
||||||
|
memberId = member.id!!,
|
||||||
|
status = status,
|
||||||
|
offset = pageable.offset,
|
||||||
|
dateString = dateString
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun orderByFieldAccountId(
|
||||||
|
memberId: Long,
|
||||||
|
status: LiveRoomStatus,
|
||||||
|
offset: Long,
|
||||||
|
dateString: String?
|
||||||
|
): Array<out OrderSpecifier<*>> {
|
||||||
|
return if (status == LiveRoomStatus.NOW) {
|
||||||
|
arrayOf(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
|
||||||
|
} else if (status == LiveRoomStatus.RESERVATION && offset == 0L && dateString == null) {
|
||||||
|
arrayOf(
|
||||||
|
CaseBuilder()
|
||||||
|
.`when`(member.id.eq(memberId)).then(1)
|
||||||
|
.otherwise(2)
|
||||||
|
.asc(),
|
||||||
|
liveRoom.beginDateTime.asc()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
arrayOf(liveRoom.beginDateTime.asc())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class LiveRoomService(
|
||||||
|
private val repository: LiveRoomRepository,
|
||||||
|
private val roomInfoRepository: LiveRoomInfoRedisRepository,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val coverImageHost: String
|
||||||
|
) {
|
||||||
|
fun getRoomList(
|
||||||
|
dateString: String?,
|
||||||
|
status: LiveRoomStatus,
|
||||||
|
pageable: Pageable,
|
||||||
|
member: Member,
|
||||||
|
timezone: String
|
||||||
|
): List<GetRoomListResponse> {
|
||||||
|
return repository
|
||||||
|
.getLiveRoomList(
|
||||||
|
dateString = dateString,
|
||||||
|
status = status,
|
||||||
|
pageable = pageable,
|
||||||
|
member = member,
|
||||||
|
timezone = timezone,
|
||||||
|
isAdult = member.auth != null
|
||||||
|
)
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val roomInfo = roomInfoRepository.findByIdOrNull(it.id!!)
|
||||||
|
|
||||||
|
val beginDateTime = it.beginDateTime
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of(timezone))
|
||||||
|
|
||||||
|
GetRoomListResponse(
|
||||||
|
roomId = it.id!!,
|
||||||
|
title = it.title,
|
||||||
|
content = it.notice,
|
||||||
|
beginDateTime = beginDateTime.format(
|
||||||
|
DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")
|
||||||
|
),
|
||||||
|
numberOfParticipate = (roomInfo?.listenerCount ?: 0) +
|
||||||
|
(roomInfo?.speakerCount ?: 0) +
|
||||||
|
(roomInfo?.managerCount ?: 0),
|
||||||
|
numberOfPeople = it.numberOfPeople,
|
||||||
|
isAdult = it.isAdult,
|
||||||
|
price = it.price,
|
||||||
|
channelName = it.channelName,
|
||||||
|
managerNickname = it.member!!.nickname,
|
||||||
|
managerId = it.member!!.id!!,
|
||||||
|
tags = listOf(),
|
||||||
|
coverImageUrl = if (it.coverImage!!.startsWith("https://")) {
|
||||||
|
it.coverImage!!
|
||||||
|
} else {
|
||||||
|
"$coverImageHost/${it.coverImage!!}"
|
||||||
|
},
|
||||||
|
isReservation = false,
|
||||||
|
isPrivateRoom = it.type == LiveRoomType.PRIVATE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room.donation
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class LiveRoomDonationMessage(
|
||||||
|
val uuid: String = UUID.randomUUID().toString(),
|
||||||
|
val nickname: String,
|
||||||
|
val coinMessage: String,
|
||||||
|
val donationMessage: String
|
||||||
|
)
|
|
@ -0,0 +1,101 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room.info
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessage
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.data.annotation.Id
|
||||||
|
import org.springframework.data.redis.core.RedisHash
|
||||||
|
|
||||||
|
@RedisHash("live_room_info")
|
||||||
|
data class LiveRoomInfo(
|
||||||
|
@Id
|
||||||
|
val roomId: Long,
|
||||||
|
var speakerList: List<LiveRoomMember> = mutableListOf(),
|
||||||
|
var listenerList: List<LiveRoomMember> = mutableListOf(),
|
||||||
|
var managerList: List<LiveRoomMember> = mutableListOf(),
|
||||||
|
var donationMessageList: List<LiveRoomDonationMessage> = mutableListOf()
|
||||||
|
) {
|
||||||
|
var speakerCount = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var listenerCount = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var managerCount = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun addSpeaker(member: Member) {
|
||||||
|
val liveRoomMember = LiveRoomMember(member)
|
||||||
|
liveRoomMember.role = LiveRoomMemberRole.SPEAKER
|
||||||
|
|
||||||
|
val speakerSet = speakerList.toMutableSet()
|
||||||
|
speakerSet.add(liveRoomMember)
|
||||||
|
speakerList = speakerSet.toList()
|
||||||
|
|
||||||
|
setSpeakerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSpeaker(member: Member) {
|
||||||
|
(speakerList as MutableList).removeIf { it.id == member.id!! }
|
||||||
|
setSpeakerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSpeakerCount() {
|
||||||
|
speakerCount = speakerList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addListener(member: Member) {
|
||||||
|
val liveRoomMember = LiveRoomMember(member)
|
||||||
|
liveRoomMember.role = LiveRoomMemberRole.LISTENER
|
||||||
|
|
||||||
|
val listenerSet = listenerList.toMutableSet()
|
||||||
|
listenerSet.add(liveRoomMember)
|
||||||
|
listenerList = listenerSet.toList()
|
||||||
|
|
||||||
|
setListenerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeListener(member: Member) {
|
||||||
|
(listenerList as MutableList).removeIf { it.id == member.id!! }
|
||||||
|
setListenerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setListenerCount() {
|
||||||
|
listenerCount = listenerList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addManager(member: Member) {
|
||||||
|
val liveRoomMember = LiveRoomMember(member)
|
||||||
|
liveRoomMember.role = LiveRoomMemberRole.MANAGER
|
||||||
|
|
||||||
|
val managerSet = managerList.toMutableSet()
|
||||||
|
managerSet.add(liveRoomMember)
|
||||||
|
managerList = managerSet.toList()
|
||||||
|
|
||||||
|
setManagerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeManager(member: Member) {
|
||||||
|
(managerList as MutableList).removeIf { it.id == member.id!! }
|
||||||
|
setManagerCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setManagerCount() {
|
||||||
|
managerCount = managerList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDonationMessage(nickname: String, coin: Int, donationMessage: String) {
|
||||||
|
val donationMessageSet = donationMessageList.toMutableSet()
|
||||||
|
donationMessageSet.add(
|
||||||
|
LiveRoomDonationMessage(
|
||||||
|
nickname = nickname,
|
||||||
|
coinMessage = "${coin}코인을 후원하셨습니다.",
|
||||||
|
donationMessage = donationMessage
|
||||||
|
)
|
||||||
|
)
|
||||||
|
donationMessageList = donationMessageSet.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDonationMessage(uuid: String) {
|
||||||
|
(donationMessageList as MutableList).removeIf { it.uuid == uuid }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room.info
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository
|
||||||
|
|
||||||
|
interface LiveRoomInfoRedisRepository : CrudRepository<LiveRoomInfo, Long>
|
|
@ -0,0 +1,21 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.room.info
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
|
||||||
|
data class LiveRoomMember(
|
||||||
|
val id: Long,
|
||||||
|
val nickname: String,
|
||||||
|
val profileImage: String
|
||||||
|
) {
|
||||||
|
var role = LiveRoomMemberRole.LISTENER
|
||||||
|
|
||||||
|
constructor(member: Member) : this(
|
||||||
|
id = member.id!!,
|
||||||
|
nickname = member.nickname,
|
||||||
|
profileImage = member.profileImage ?: "profile/default-profile.png"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LiveRoomMemberRole {
|
||||||
|
LISTENER, SPEAKER, MANAGER
|
||||||
|
}
|
Loading…
Reference in New Issue