앱 - 오디션 투표 API
This commit is contained in:
parent
7a395a9906
commit
cd0c066978
|
@ -0,0 +1,33 @@
|
||||||
|
package kr.co.vividnext.sodalive.audition.vote
|
||||||
|
|
||||||
|
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("/audition/vote")
|
||||||
|
class AuditionVoteController(
|
||||||
|
private val service: AuditionVoteService
|
||||||
|
) {
|
||||||
|
@PostMapping
|
||||||
|
fun voteAuditionApplicant(
|
||||||
|
@RequestBody request: VoteAuditionApplicantRequest,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
|
||||||
|
ApiResponse.ok(
|
||||||
|
service.voteAuditionApplicant(
|
||||||
|
applicantId = request.applicantId,
|
||||||
|
timezone = request.timezone,
|
||||||
|
container = request.container,
|
||||||
|
member = member
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package kr.co.vividnext.sodalive.audition.vote
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.audition.AuditionVote
|
||||||
|
import kr.co.vividnext.sodalive.audition.QAuditionVote.auditionVote
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface AuditionVoteRepository : JpaRepository<AuditionVote, Long>, AuditionVoteQueryRepository
|
||||||
|
|
||||||
|
interface AuditionVoteQueryRepository {
|
||||||
|
fun countByMemberIdAndApplicantIdAndVoteDateRange(
|
||||||
|
memberId: Long,
|
||||||
|
applicantId: Long,
|
||||||
|
startDate: LocalDateTime,
|
||||||
|
endDate: LocalDateTime
|
||||||
|
): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuditionVoteQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : AuditionVoteQueryRepository {
|
||||||
|
override fun countByMemberIdAndApplicantIdAndVoteDateRange(
|
||||||
|
memberId: Long,
|
||||||
|
applicantId: Long,
|
||||||
|
startDate: LocalDateTime,
|
||||||
|
endDate: LocalDateTime
|
||||||
|
): Int {
|
||||||
|
return queryFactory
|
||||||
|
.select(auditionVote.id)
|
||||||
|
.from(auditionVote)
|
||||||
|
.where(
|
||||||
|
auditionVote.member.id.eq(memberId)
|
||||||
|
.and(auditionVote.applicant.id.eq(applicantId))
|
||||||
|
.and(auditionVote.createdAt.between(startDate, endDate))
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package kr.co.vividnext.sodalive.audition.vote
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.audition.AuditionVote
|
||||||
|
import kr.co.vividnext.sodalive.audition.applicant.AuditionApplicantRepository
|
||||||
|
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.member.Member
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AuditionVoteService(
|
||||||
|
private val repository: AuditionVoteRepository,
|
||||||
|
private val applicantRepository: AuditionApplicantRepository,
|
||||||
|
private val canPaymentService: CanPaymentService
|
||||||
|
) {
|
||||||
|
fun voteAuditionApplicant(applicantId: Long, timezone: String, container: String, member: Member) {
|
||||||
|
val applicant = applicantRepository.findByIdOrNull(applicantId)
|
||||||
|
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
|
||||||
|
|
||||||
|
val defaultZoneId = ZoneId.of("Asia/Seoul")
|
||||||
|
val clientZoneId = try {
|
||||||
|
ZoneId.of(timezone)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
defaultZoneId
|
||||||
|
}
|
||||||
|
|
||||||
|
val nowInClientZone = ZonedDateTime.now(clientZoneId)
|
||||||
|
val startOfDayClient = nowInClientZone.toLocalDate().atStartOfDay(clientZoneId)
|
||||||
|
val endOfDayClient = startOfDayClient.plusDays(1).minusSeconds(1)
|
||||||
|
|
||||||
|
val startDate = startOfDayClient.withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime()
|
||||||
|
val endDate = endOfDayClient.withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime()
|
||||||
|
|
||||||
|
val voteCount = repository.countByMemberIdAndApplicantIdAndVoteDateRange(
|
||||||
|
memberId = member.id!!,
|
||||||
|
applicantId = applicantId,
|
||||||
|
startDate = startDate,
|
||||||
|
endDate = endDate
|
||||||
|
)
|
||||||
|
|
||||||
|
if (voteCount > 10) {
|
||||||
|
throw SodaException("오늘 해당 지원자에게 할 수 있는 최대 투표수를 초과하였습니다.\n내일 다시 투표해 주세요.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voteCount > 0) {
|
||||||
|
canPaymentService.spendCan(
|
||||||
|
memberId = member.id!!,
|
||||||
|
needCan = 1,
|
||||||
|
canUsage = CanUsage.AUDITION_VOTE,
|
||||||
|
container = container
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val auditionVote = AuditionVote()
|
||||||
|
auditionVote.applicant = applicant
|
||||||
|
auditionVote.member = member
|
||||||
|
repository.save(auditionVote)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package kr.co.vividnext.sodalive.audition.vote
|
||||||
|
|
||||||
|
data class VoteAuditionApplicantRequest(
|
||||||
|
val applicantId: Long,
|
||||||
|
val timezone: String,
|
||||||
|
val container: String
|
||||||
|
)
|
|
@ -8,5 +8,6 @@ enum class CanUsage {
|
||||||
ORDER_CONTENT,
|
ORDER_CONTENT,
|
||||||
SPIN_ROULETTE,
|
SPIN_ROULETTE,
|
||||||
PAID_COMMUNITY_POST,
|
PAID_COMMUNITY_POST,
|
||||||
ALARM_SLOT
|
ALARM_SLOT,
|
||||||
|
AUDITION_VOTE
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue