라이브 - 시작, 취소, 입장, 수정, 예약 API 추가

This commit is contained in:
2023-07-31 17:09:45 +09:00
parent 197cca1f1b
commit f393c7630e
29 changed files with 1044 additions and 19 deletions

View File

@@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.live.reservation
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.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/live/reservation")
class LiveReservationController(private val service: LiveReservationService) {
@PostMapping
fun makeReservation(
@RequestBody request: MakeLiveReservationRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(service.makeReservation(request, member.id!!))
}
}

View File

@@ -0,0 +1,70 @@
package kr.co.vividnext.sodalive.live.reservation
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation
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.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface LiveReservationRepository : JpaRepository<LiveReservation, Long>, LiveReservationQueryRepository
interface LiveReservationQueryRepository {
fun getReservationList(roomId: Long): List<LiveReservation>
fun cancelReservation(roomId: Long)
fun getReservationBookerList(roomId: Long): List<Member>
fun isExistsReservation(roomId: Long, memberId: Long): Boolean
}
@Repository
class LiveReservationQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveReservationQueryRepository {
override fun getReservationList(roomId: Long): List<LiveReservation> {
return queryFactory
.selectFrom(liveReservation)
.innerJoin(liveReservation.room, liveRoom)
.where(
liveRoom.id.eq(roomId)
.and(liveReservation.isActive.isTrue)
)
.fetch()
}
override fun cancelReservation(roomId: Long) {
queryFactory
.update(liveReservation)
.set(liveReservation.isActive, false)
.where(liveReservation.room.id.eq(roomId))
.execute()
}
override fun getReservationBookerList(roomId: Long): List<Member> {
return queryFactory
.select(member)
.from(liveReservation)
.innerJoin(liveReservation.member, member)
.innerJoin(liveReservation.room, liveRoom)
.where(
liveRoom.id.eq(roomId)
.and(liveReservation.isActive.isTrue)
)
.fetch()
}
override fun isExistsReservation(roomId: Long, memberId: Long): Boolean {
return queryFactory
.selectFrom(liveReservation)
.innerJoin(liveReservation.member, member)
.innerJoin(liveReservation.room, liveRoom)
.where(
liveReservation.isActive.isTrue
.and(liveReservation.room.id.eq(roomId))
.and(liveReservation.member.id.eq(memberId))
)
.fetchFirst() != null
}
}

View File

@@ -0,0 +1,79 @@
package kr.co.vividnext.sodalive.live.reservation
import kr.co.vividnext.sodalive.can.payment.CanPaymentService
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.live.room.LiveRoomRepository
import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.member.MemberRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.time.ZoneId
import java.time.format.DateTimeFormatter
@Service
class LiveReservationService(
private val repository: LiveReservationRepository,
private val liveRoomRepository: LiveRoomRepository,
private val memberRepository: MemberRepository,
private val canPaymentService: CanPaymentService
) {
fun makeReservation(request: MakeLiveReservationRequest, memberId: Long): MakeLiveReservationResponse {
val room = liveRoomRepository.findByIdOrNull(id = request.roomId)
?: throw SodaException(message = "잘못된 요청입니다.\n다시 시도해 주세요.")
val member = memberRepository.findByIdOrNull(id = memberId)
?: throw SodaException(message = "로그인 정보를 확인해주세요.")
if (
room.member!!.id!! != memberId &&
room.type == LiveRoomType.PRIVATE &&
(request.password == null || request.password != room.password)
) {
throw SodaException("비밀번호가 일치하지 않습니다.\n다시 확인 후 입력해주세요.")
}
if (repository.isExistsReservation(roomId = request.roomId, memberId = memberId)) {
throw SodaException("이미 예약한 라이브 입니다.")
}
val haveCan = member.getChargeCan(request.container) + member.getRewardCan(request.container)
if (haveCan < room.price) {
throw SodaException("${room.price - haveCan}캔이 부족합니다. 충전 후 이용해 주세요.")
}
if (room.price > 0) {
canPaymentService.spendCan(
memberId = member.id!!,
needCan = room.price,
canUsage = CanUsage.LIVE,
liveRoom = room,
container = request.container
)
}
val reservation = LiveReservation()
reservation.room = room
reservation.member = member
repository.save(reservation)
val beginDateTime = room.beginDateTime
.atZone(ZoneId.of("UTC"))
.withZoneSameInstant(ZoneId.of(request.timezone))
return MakeLiveReservationResponse(
reservationId = reservation.id!!,
nickname = room.member!!.nickname,
title = room.title,
beginDateString = beginDateTime.format(DateTimeFormatter.ofPattern("yyyy년 M월 d일 (E), a hh:mm")),
price = if (room.price > 0) {
"${room.price}"
} else {
"무료"
},
haveCan = haveCan,
useCan = room.price,
remainingCan = haveCan - room.price
)
}
}

View File

@@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.live.reservation
data class MakeLiveReservationRequest(
val roomId: Long,
val container: String,
val timezone: String = "Asia/Seoul",
val password: String? = null
)

View File

@@ -0,0 +1,12 @@
package kr.co.vividnext.sodalive.live.reservation
data class MakeLiveReservationResponse(
val reservationId: Long,
val nickname: String,
val title: String,
val beginDateString: String,
val price: String,
val haveCan: Int,
val useCan: Int,
val remainingCan: Int
)