앱 - 오디션 투표 API
This commit is contained in:
		| @@ -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, | ||||
|     SPIN_ROULETTE, | ||||
|     PAID_COMMUNITY_POST, | ||||
|     ALARM_SLOT | ||||
|     ALARM_SLOT, | ||||
|     AUDITION_VOTE | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user