앱 - 오디션 투표 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, |     ORDER_CONTENT, | ||||||
|     SPIN_ROULETTE, |     SPIN_ROULETTE, | ||||||
|     PAID_COMMUNITY_POST, |     PAID_COMMUNITY_POST, | ||||||
|     ALARM_SLOT |     ALARM_SLOT, | ||||||
|  |     AUDITION_VOTE | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user