Compare commits

..

No commits in common. "cd0c066978f8e84275129782e5598455726ccb63" and "8385800e48ece098e8abfe0c17b0af0c528e4e8e" have entirely different histories.

10 changed files with 4 additions and 247 deletions

View File

@ -9,9 +9,9 @@ import javax.persistence.ManyToOne
@Entity @Entity
data class AuditionApplicant( data class AuditionApplicant(
val voicePath: String,
val phoneNumber: String, val phoneNumber: String,
var voicePath: String? = null, val isActive: Boolean = true
var isActive: Boolean = true
) : BaseEntity() { ) : BaseEntity() {
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id", nullable = false) @JoinColumn(name = "role_id", nullable = false)

View File

@ -1,6 +0,0 @@
package kr.co.vividnext.sodalive.audition.applicant
data class ApplyAuditionRoleRequest(
val roleId: Long,
val phoneNumber: String
)

View File

@ -6,12 +6,9 @@ import kr.co.vividnext.sodalive.member.Member
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.security.core.annotation.AuthenticationPrincipal
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.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
@RestController @RestController
@RequestMapping("/audition/applicant") @RequestMapping("/audition/applicant")
@ -34,22 +31,4 @@ class AuditionApplicantController(private val service: AuditionApplicantService)
) )
) )
} }
@PostMapping
fun applyAuditionRole(
@RequestPart("contentFile")
contentFile: MultipartFile?,
@RequestPart("request") requestString: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(
service.applyAuditionRole(
contentFile = contentFile,
requestString = requestString,
member = member
)
)
}
} }

View File

@ -20,8 +20,6 @@ interface AuditionApplicantQueryRepository {
offset: Long, offset: Long,
limit: Long limit: Long
): List<GetAuditionRoleApplicantItem> ): List<GetAuditionRoleApplicantItem>
fun findActiveApplicantByMemberIdAndRoleId(memberId: Long, roleId: Long): AuditionApplicant?
} }
class AuditionApplicantQueryRepositoryImpl( class AuditionApplicantQueryRepositoryImpl(
@ -92,15 +90,4 @@ class AuditionApplicantQueryRepositoryImpl(
.orderBy(orderBy) .orderBy(orderBy)
.fetch() .fetch()
} }
override fun findActiveApplicantByMemberIdAndRoleId(memberId: Long, roleId: Long): AuditionApplicant? {
return queryFactory
.selectFrom(auditionApplicant)
.where(
auditionApplicant.isActive.isTrue
.and(auditionApplicant.member.id.eq(memberId))
.and(auditionApplicant.role.id.eq(roleId))
)
.fetchFirst()
}
} }

View File

@ -1,30 +1,9 @@
package kr.co.vividnext.sodalive.audition.applicant package kr.co.vividnext.sodalive.audition.applicant
import com.amazonaws.services.s3.model.ObjectMetadata
import com.fasterxml.jackson.databind.ObjectMapper
import kr.co.vividnext.sodalive.audition.AuditionApplicant
import kr.co.vividnext.sodalive.audition.role.AuditionRoleRepository
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value
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
@Service @Service
class AuditionApplicantService( class AuditionApplicantService(private val repository: AuditionApplicantRepository) {
private val repository: AuditionApplicantRepository,
private val roleRepository: AuditionRoleRepository,
private val s3Uploader: S3Uploader,
private val objectMapper: ObjectMapper,
@Value("\${cloud.aws.s3.bucket}")
private val bucket: String
) {
fun getAuditionApplicantList( fun getAuditionApplicantList(
auditionRoleId: Long, auditionRoleId: Long,
sortType: AuditionApplicantSortType, sortType: AuditionApplicantSortType,
@ -44,41 +23,4 @@ class AuditionApplicantService(
items = items items = items
) )
} }
@Transactional
fun applyAuditionRole(contentFile: MultipartFile?, requestString: String, member: Member) {
if (contentFile == null) throw SodaException("녹음 파일을 확인해 주세요.")
val request = objectMapper.readValue(requestString, ApplyAuditionRoleRequest::class.java)
val auditionRole = roleRepository.findByIdOrNull(id = request.roleId)
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.")
val existingApplicant = repository.findActiveApplicantByMemberIdAndRoleId(
memberId = member.id!!,
roleId = auditionRole.id!!
)
if (existingApplicant != null) {
existingApplicant.isActive = false
repository.save(existingApplicant)
}
val applicant = AuditionApplicant(phoneNumber = request.phoneNumber)
applicant.role = auditionRole
applicant.member = member
repository.save(applicant)
val contentFileName = generateFileName(prefix = "${applicant.id}-applicant")
val metadata = ObjectMetadata()
metadata.contentLength = contentFile.size
val contentPath = s3Uploader.upload(
inputStream = contentFile.inputStream,
bucket = bucket,
filePath = "audition/${applicant.id}/$contentFileName",
metadata = metadata
)
applicant.voicePath = contentPath
}
} }

View File

@ -1,33 +0,0 @@
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
)
)
}
}

View File

@ -1,40 +0,0 @@
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
}
}

View File

@ -1,64 +0,0 @@
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)
}
}

View File

@ -1,7 +0,0 @@
package kr.co.vividnext.sodalive.audition.vote
data class VoteAuditionApplicantRequest(
val applicantId: Long,
val timezone: String,
val container: String
)

View File

@ -8,6 +8,5 @@ enum class CanUsage {
ORDER_CONTENT, ORDER_CONTENT,
SPIN_ROULETTE, SPIN_ROULETTE,
PAID_COMMUNITY_POST, PAID_COMMUNITY_POST,
ALARM_SLOT, ALARM_SLOT
AUDITION_VOTE
} }