test #1
|
@ -1,15 +1,58 @@
|
||||||
package kr.co.vividnext.sodalive.admin.live
|
package kr.co.vividnext.sodalive.admin.live
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.security.access.prepost.PreAuthorize
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.multipart.MultipartFile
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
|
||||||
@RequestMapping("/admin/live")
|
@RequestMapping("/admin/live")
|
||||||
class AdminLiveController(private val service: AdminLiveService) {
|
class AdminLiveController(private val service: AdminLiveService) {
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
fun getOnAirLive() = ApiResponse.ok(data = service.getLiveList())
|
fun getOnAirLive() = ApiResponse.ok(data = service.getLiveList())
|
||||||
|
|
||||||
|
@GetMapping("/recommend-creator")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
fun getRecommendCreatorBanner(pageable: Pageable) = ApiResponse.ok(service.getRecommendCreator(pageable))
|
||||||
|
|
||||||
|
@PostMapping("/recommend-creator")
|
||||||
|
fun createRecommendCreatorBanner(
|
||||||
|
@RequestParam("image") image: MultipartFile,
|
||||||
|
@RequestParam("creator_id") creatorId: Long,
|
||||||
|
@RequestParam("start_date") startDate: String,
|
||||||
|
@RequestParam("end_date") endDate: String,
|
||||||
|
@RequestParam("is_adult") isAdult: Boolean
|
||||||
|
) = ApiResponse.ok(
|
||||||
|
service.createRecommendCreatorBanner(image, creatorId, startDate, endDate, isAdult),
|
||||||
|
"등록되었습니다."
|
||||||
|
)
|
||||||
|
|
||||||
|
@PutMapping("/recommend-creator")
|
||||||
|
fun updateRecommendCreatorBanner(
|
||||||
|
@RequestParam("recommend_creator_banner_id") recommendCreatorBannerId: Long,
|
||||||
|
@RequestParam("image", required = false) image: MultipartFile?,
|
||||||
|
@RequestParam("creator_id", required = false) creatorId: Long?,
|
||||||
|
@RequestParam("start_date", required = false) startDate: String?,
|
||||||
|
@RequestParam("end_date", required = false) endDate: String?,
|
||||||
|
@RequestParam("is_adult", required = false) isAdult: Boolean?
|
||||||
|
) = ApiResponse.ok(
|
||||||
|
service.updateRecommendCreatorBanner(recommendCreatorBannerId, image, creatorId, startDate, endDate, isAdult),
|
||||||
|
"수정되었습니다."
|
||||||
|
)
|
||||||
|
|
||||||
|
@PutMapping("/recommend-creator/orders")
|
||||||
|
fun updateRecommendCreatorBannerOrders(
|
||||||
|
@RequestBody request: UpdateAdminRecommendCreatorBannerOrdersRequest
|
||||||
|
) = ApiResponse.ok(
|
||||||
|
service.updateRecommendCreatorBannerOrders(request.firstOrders, request.ids),
|
||||||
|
"수정되었습니다."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package kr.co.vividnext.sodalive.admin.live
|
package kr.co.vividnext.sodalive.admin.live
|
||||||
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.live.recommend.QRecommendLiveCreatorBanner.recommendLiveCreatorBanner
|
||||||
|
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBanner
|
||||||
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
import kr.co.vividnext.sodalive.live.room.LiveRoom
|
||||||
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 org.springframework.data.domain.Pageable
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -16,4 +19,21 @@ class AdminLiveRoomQueryRepository(private val queryFactory: JPAQueryFactory) {
|
||||||
.orderBy(liveRoom.channelName.desc(), liveRoom.beginDateTime.asc())
|
.orderBy(liveRoom.channelName.desc(), liveRoom.beginDateTime.asc())
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecommendCreatorTotalCount(): Int {
|
||||||
|
return queryFactory
|
||||||
|
.select(recommendLiveCreatorBanner.id)
|
||||||
|
.from(recommendLiveCreatorBanner)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecommendCreatorList(pageable: Pageable): List<RecommendLiveCreatorBanner> {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(recommendLiveCreatorBanner)
|
||||||
|
.offset(pageable.offset)
|
||||||
|
.limit(pageable.pageSize.toLong())
|
||||||
|
.orderBy(recommendLiveCreatorBanner.orders.asc(), recommendLiveCreatorBanner.id.desc())
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,31 @@
|
||||||
package kr.co.vividnext.sodalive.admin.live
|
package kr.co.vividnext.sodalive.admin.live
|
||||||
|
|
||||||
|
import com.amazonaws.services.s3.model.ObjectMetadata
|
||||||
|
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBanner
|
||||||
|
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBannerRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
|
import kr.co.vividnext.sodalive.utils.generateFileName
|
||||||
import org.springframework.beans.factory.annotation.Value
|
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 org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import org.springframework.web.multipart.MultipartFile
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class AdminLiveService(
|
class AdminLiveService(
|
||||||
|
private val recommendCreatorBannerRepository: RecommendLiveCreatorBannerRepository,
|
||||||
private val repository: AdminLiveRoomQueryRepository,
|
private val repository: AdminLiveRoomQueryRepository,
|
||||||
|
private val memberRepository: MemberRepository,
|
||||||
|
private val s3Uploader: S3Uploader,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.s3.bucket}")
|
||||||
|
private val bucket: String,
|
||||||
@Value("\${cloud.aws.cloud-front.host}")
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
private val coverImageHost: String
|
private val coverImageHost: String
|
||||||
) {
|
) {
|
||||||
|
@ -34,4 +53,188 @@ class AdminLiveService(
|
||||||
.toList()
|
.toList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecommendCreator(pageable: Pageable): GetAdminRecommendCreatorResponse {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||||
|
|
||||||
|
val totalCount = repository.getRecommendCreatorTotalCount()
|
||||||
|
|
||||||
|
val recommendCreatorList = repository
|
||||||
|
.getRecommendCreatorList(pageable)
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
GetAdminRecommendCreatorResponseItem(
|
||||||
|
it.id!!,
|
||||||
|
"$coverImageHost/${it.image}",
|
||||||
|
it.creator!!.id!!,
|
||||||
|
it.creator!!.nickname,
|
||||||
|
it.startDate
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("Asia/Seoul"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
.format(dateTimeFormatter),
|
||||||
|
it.endDate
|
||||||
|
.atZone(ZoneId.of("UTC"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("Asia/Seoul"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
.format(dateTimeFormatter),
|
||||||
|
it.isAdult
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return GetAdminRecommendCreatorResponse(
|
||||||
|
totalCount = totalCount,
|
||||||
|
recommendCreatorList = recommendCreatorList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun createRecommendCreatorBanner(
|
||||||
|
image: MultipartFile,
|
||||||
|
creatorId: Long,
|
||||||
|
startDateString: String,
|
||||||
|
endDateString: String,
|
||||||
|
isAdult: Boolean
|
||||||
|
): Long {
|
||||||
|
if (creatorId < 1) throw SodaException("올바른 크리에이터를 선택해 주세요.")
|
||||||
|
|
||||||
|
val creator = memberRepository.findCreatorByIdOrNull(memberId = creatorId)
|
||||||
|
?: throw SodaException("올바른 크리에이터를 선택해 주세요.")
|
||||||
|
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||||
|
val startDate = LocalDateTime.parse(startDateString, dateTimeFormatter)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
val nowDate = LocalDateTime.now()
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
if (startDate < nowDate) throw SodaException("노출 시작일은 현재시간 이후로 설정하셔야 합니다.")
|
||||||
|
|
||||||
|
val endDate = LocalDateTime.parse(endDateString, dateTimeFormatter)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
if (endDate < nowDate) throw SodaException("노출 종료일은 현재시간 이후로 설정하셔야 합니다.")
|
||||||
|
if (endDate <= startDate) throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
|
||||||
|
|
||||||
|
val recommendCreatorBanner = RecommendLiveCreatorBanner(
|
||||||
|
startDate = startDate,
|
||||||
|
endDate = endDate,
|
||||||
|
isAdult = isAdult
|
||||||
|
)
|
||||||
|
recommendCreatorBanner.creator = creator
|
||||||
|
recommendCreatorBannerRepository.save(recommendCreatorBanner)
|
||||||
|
|
||||||
|
val metadata = ObjectMetadata()
|
||||||
|
metadata.contentLength = image.size
|
||||||
|
val imagePath = s3Uploader.upload(
|
||||||
|
inputStream = image.inputStream,
|
||||||
|
bucket = bucket,
|
||||||
|
filePath = "recommend_creator_banner/${recommendCreatorBanner.id}/${generateFileName()}",
|
||||||
|
metadata = metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
recommendCreatorBanner.image = imagePath
|
||||||
|
|
||||||
|
return recommendCreatorBanner.id!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateRecommendCreatorBanner(
|
||||||
|
recommendCreatorBannerId: Long,
|
||||||
|
image: MultipartFile?,
|
||||||
|
creatorId: Long?,
|
||||||
|
startDateString: String?,
|
||||||
|
endDateString: String?,
|
||||||
|
isAdult: Boolean?
|
||||||
|
) {
|
||||||
|
val recommendCreatorBanner = recommendCreatorBannerRepository.findByIdOrNull(recommendCreatorBannerId)
|
||||||
|
?: throw SodaException("해당하는 추천라이브가 없습니다. 다시 확인해 주세요.")
|
||||||
|
|
||||||
|
if (creatorId != null) {
|
||||||
|
if (creatorId < 1) throw SodaException("올바른 크리에이터를 선택해 주세요.")
|
||||||
|
|
||||||
|
val creator = memberRepository.findCreatorByIdOrNull(memberId = creatorId)
|
||||||
|
?: throw SodaException("올바른 크리에이터를 선택해 주세요.")
|
||||||
|
|
||||||
|
recommendCreatorBanner.creator = creator
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image != null) {
|
||||||
|
val metadata = ObjectMetadata()
|
||||||
|
metadata.contentLength = image.size
|
||||||
|
val imagePath = s3Uploader.upload(
|
||||||
|
inputStream = image.inputStream,
|
||||||
|
bucket = bucket,
|
||||||
|
filePath = "recommend_creator_banner/${recommendCreatorBanner.id}/${generateFileName()}",
|
||||||
|
metadata = metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
recommendCreatorBanner.image = imagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startDateString != null) {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||||
|
val startDate = LocalDateTime.parse(startDateString, dateTimeFormatter)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
val endDate = if (endDateString != null) {
|
||||||
|
LocalDateTime.parse(endDateString, dateTimeFormatter)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDate != null) {
|
||||||
|
if (endDate <= startDate) {
|
||||||
|
throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
recommendCreatorBanner.endDate = endDate
|
||||||
|
} else {
|
||||||
|
if (recommendCreatorBanner.endDate <= startDate) {
|
||||||
|
throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recommendCreatorBanner.startDate = startDate
|
||||||
|
} else if (endDateString != null) {
|
||||||
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||||
|
val endDate = LocalDateTime.parse(endDateString, dateTimeFormatter)
|
||||||
|
.atZone(ZoneId.of("Asia/Seoul"))
|
||||||
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
||||||
|
.toLocalDateTime()
|
||||||
|
|
||||||
|
if (endDate <= recommendCreatorBanner.startDate) {
|
||||||
|
throw SodaException("노출 종료일은 노출 시작일 이후로 설정하셔야 합니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
recommendCreatorBanner.endDate = endDate
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAdult != null) {
|
||||||
|
recommendCreatorBanner.isAdult = isAdult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateRecommendCreatorBannerOrders(firstOrders: Int, ids: List<Long>) {
|
||||||
|
for (index in ids.indices) {
|
||||||
|
val recommendCreatorBanner = recommendCreatorBannerRepository.findByIdOrNull(id = ids[index])
|
||||||
|
|
||||||
|
if (recommendCreatorBanner != null) {
|
||||||
|
recommendCreatorBanner.orders = firstOrders + index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.live
|
||||||
|
|
||||||
|
data class GetAdminRecommendCreatorResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val recommendCreatorList: List<GetAdminRecommendCreatorResponseItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetAdminRecommendCreatorResponseItem(
|
||||||
|
val id: Long,
|
||||||
|
val image: String,
|
||||||
|
val creatorId: Long,
|
||||||
|
val creatorNickname: String,
|
||||||
|
val startDate: String,
|
||||||
|
val endDate: String,
|
||||||
|
val isAdult: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package kr.co.vividnext.sodalive.admin.live
|
||||||
|
|
||||||
|
data class UpdateAdminRecommendCreatorBannerOrdersRequest(
|
||||||
|
val firstOrders: Int,
|
||||||
|
val ids: List<Long>
|
||||||
|
)
|
|
@ -11,8 +11,6 @@ import javax.persistence.ManyToOne
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class RecommendLiveCreatorBanner(
|
data class RecommendLiveCreatorBanner(
|
||||||
@Column(nullable = false)
|
|
||||||
var image: String,
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var startDate: LocalDateTime,
|
var startDate: LocalDateTime,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@ -20,7 +18,9 @@ data class RecommendLiveCreatorBanner(
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var isAdult: Boolean = false,
|
var isAdult: Boolean = false,
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var orders: Int = 1
|
var orders: Int = 1,
|
||||||
|
@Column(nullable = true)
|
||||||
|
var image: String? = null
|
||||||
) : BaseEntity() {
|
) : BaseEntity() {
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "creator_id", nullable = false)
|
@JoinColumn(name = "creator_id", nullable = false)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.live.recommend
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface RecommendLiveCreatorBannerRepository : JpaRepository<RecommendLiveCreatorBanner, Long>
|
|
@ -14,6 +14,7 @@ interface MemberRepository : JpaRepository<Member, Long>, MemberQueryRepository
|
||||||
interface MemberQueryRepository {
|
interface MemberQueryRepository {
|
||||||
fun findByPushToken(pushToken: String): List<Member>
|
fun findByPushToken(pushToken: String): List<Member>
|
||||||
fun findByNicknameAndOtherCondition(nickname: String, memberId: Long): List<Member>
|
fun findByNicknameAndOtherCondition(nickname: String, memberId: Long): List<Member>
|
||||||
|
fun findCreatorByIdOrNull(memberId: Long): Member?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -36,4 +37,14 @@ class MemberQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : Mem
|
||||||
)
|
)
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findCreatorByIdOrNull(memberId: Long): Member? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(member)
|
||||||
|
.where(
|
||||||
|
member.id.eq(memberId)
|
||||||
|
.and(member.role.eq(MemberRole.CREATOR))
|
||||||
|
)
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue