From e29e71b8bdc7a249e17526f488ae6c7931aaf8e5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 24 Dec 2024 03:26:20 +0900 Subject: [PATCH 01/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20-=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=9E=91=EC=84=B1=20-=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20-=20=EC=98=A4=EB=94=94=EC=85=98?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1,=20=EC=88=98=EC=A0=95,=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/AdminAuditionController.kt | 37 ++++++++ .../admin/audition/AdminAuditionRepository.kt | 70 +++++++++++++++ .../admin/audition/AdminAuditionService.kt | 90 +++++++++++++++++++ .../admin/audition/CreateAuditionRequest.kt | 45 ++++++++++ .../admin/audition/GetAuditionListResponse.kt | 19 ++++ .../admin/audition/UpdateAuditionRequest.kt | 11 +++ .../vividnext/sodalive/audition/Audition.kt | 18 ++++ .../sodalive/audition/AuditionApplicant.kt | 23 +++++ .../sodalive/audition/AuditionRole.kt | 20 +++++ .../sodalive/audition/AuditionVote.kt | 18 ++++ 10 files changed, 351 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionVote.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt new file mode 100644 index 0000000..ab1e9ef --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt @@ -0,0 +1,37 @@ +package kr.co.vividnext.sodalive.admin.audition + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize +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.RequestMapping +import org.springframework.web.bind.annotation.RequestPart +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@PreAuthorize("hasRole('ADMIN')") +@RequestMapping("/admin/audition") +class AdminAuditionController(private val service: AdminAuditionService) { + @PostMapping + fun createAudition( + @RequestPart("image") image: MultipartFile, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.createAudition(image, requestString), "등록되었습니다.") + + @PutMapping + fun updateAudition( + @RequestPart("image", required = false) image: MultipartFile? = null, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.updateAudition(image, requestString), "수정되었습니다.") + + @GetMapping + fun getAuditionList(pageable: Pageable) = ApiResponse.ok( + service.getAuditionList( + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt new file mode 100644 index 0000000..f548802 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -0,0 +1,70 @@ +package kr.co.vividnext.sodalive.admin.audition + +import com.querydsl.core.types.dsl.DateTimePath +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.core.types.dsl.StringTemplate +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.audition.Audition +import kr.co.vividnext.sodalive.audition.QAudition.audition +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +interface AdminAuditionRepository : JpaRepository, AdminAuditionQueryRepository + +interface AdminAuditionQueryRepository { + fun getAuditionList(offset: Long, limit: Long): List + fun getAuditionListCount(): Int +} + +class AdminAuditionQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val coverImageHost: String +) : AdminAuditionQueryRepository { + override fun getAuditionList(offset: Long, limit: Long): List { + return queryFactory + .select( + QGetAuditionListItem( + audition.id, + audition.title, + getFormattedDate(audition.endDate), + audition.imagePath.prepend("/").prepend(coverImageHost), + audition.isAdult, + audition.isActive, + audition.information, + audition.originalWorkUrl + ) + ) + .from(audition) + .offset(offset) + .limit(limit) + .orderBy(audition.isActive.desc(), audition.id.desc()) + .fetch() + } + + override fun getAuditionListCount(): Int { + return queryFactory + .select(audition.id) + .from(audition) + .fetch() + .size + } + + private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { + return Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + dateTimePath, + "UTC", + "Asia/Seoul" + ), + "%Y-%m-%d %H:%i:%s" + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt new file mode 100644 index 0000000..bfec948 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt @@ -0,0 +1,90 @@ +package kr.co.vividnext.sodalive.admin.audition + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.common.SodaException +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.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@Service +class AdminAuditionService( + private val s3Uploader: S3Uploader, + private val objectMapper: ObjectMapper, + private val repository: AdminAuditionRepository, + + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String +) { + @Transactional + fun createAudition(image: MultipartFile, requestString: String) { + val request = objectMapper.readValue(requestString, CreateAuditionRequest::class.java) + val audition = repository.save(request.toAudition()) + + val fileName = generateFileName("audition") + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "audition/production/${audition.id}/$fileName" + ) + audition.imagePath = imagePath + } + + @Transactional + fun updateAudition(image: MultipartFile?, requestString: String) { + val request = objectMapper.readValue(requestString, UpdateAuditionRequest::class.java) + val audition = repository.findByIdOrNull(id = request.id) + ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + + if (request.title != null) { + audition.title = request.title + } + + if (request.information != null) { + audition.information = request.information + } + + if (request.isAdult != null) { + audition.isAdult = request.isAdult + } + + if (request.endDateString != null) { + val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + val endDate = LocalDateTime.parse(request.endDateString, dateTimeFormatter) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime() + audition.endDate = endDate + } + + if (request.originalWorkUrl != null) { + audition.originalWorkUrl = request.originalWorkUrl + } + + if (image != null) { + val fileName = generateFileName("audition") + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "audition/production/${audition.id}/$fileName" + ) + audition.imagePath = imagePath + } + + if (request.isActive != null) { + audition.isActive = request.isActive + } + } + + fun getAuditionList(offset: Long, limit: Long): GetAuditionListResponse { + val totalCount = repository.getAuditionListCount() + val items = repository.getAuditionList(offset = offset, limit = limit) + return GetAuditionListResponse(totalCount, items) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt new file mode 100644 index 0000000..647fa34 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt @@ -0,0 +1,45 @@ +package kr.co.vividnext.sodalive.admin.audition + +import kr.co.vividnext.sodalive.audition.Audition +import kr.co.vividnext.sodalive.common.SodaException +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +data class CreateAuditionRequest( + val title: String, + val information: String, + val isAdult: Boolean = false, + val endDateString: String? = null, + val originalWorkUrl: String? = null +) { + init { + if (title.isBlank()) { + throw SodaException("오디션 제목을 입력하세요") + } + + if (information.isBlank() || information.length < 10) { + throw SodaException("오디션 정보는 최소 10글자 입니다") + } + } + + fun toAudition(): Audition { + val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + val endDate = if (endDateString != null) { + LocalDateTime.parse(endDateString, dateTimeFormatter) + .atZone(ZoneId.of("Asia/Seoul")) + .withZoneSameInstant(ZoneId.of("UTC")) + .toLocalDateTime() + } else { + null + } + + return Audition( + title = title, + information = information, + isAdult = isAdult, + endDate = endDate, + originalWorkUrl = originalWorkUrl + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt new file mode 100644 index 0000000..10f2db4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt @@ -0,0 +1,19 @@ +package kr.co.vividnext.sodalive.admin.audition + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionListResponse( + val totalCount: Int, + val items: List +) + +data class GetAuditionListItem @QueryProjection constructor( + val id: Long, + val title: String, + val endDate: String, + val imageUrl: String, + val isAdult: Boolean, + val isActive: Boolean, + val information: String, + val originalWorkUrl: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt new file mode 100644 index 0000000..5b1f16f --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt @@ -0,0 +1,11 @@ +package kr.co.vividnext.sodalive.admin.audition + +data class UpdateAuditionRequest( + val id: Long, + val title: String? = null, + val information: String? = null, + val isAdult: Boolean? = null, + val endDateString: String? = null, + val originalWorkUrl: String? = null, + val isActive: Boolean? = null +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt new file mode 100644 index 0000000..5970923 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.audition + +import kr.co.vividnext.sodalive.common.BaseEntity +import java.time.LocalDateTime +import javax.persistence.Entity + +@Entity +data class Audition( + var title: String, + var information: String, + var isAdult: Boolean = false, + var endDate: LocalDateTime? = null, + // 원작 URL + var originalWorkUrl: String? = null +) : BaseEntity() { + var isActive: Boolean = true + var imagePath: String? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt new file mode 100644 index 0000000..e485407 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt @@ -0,0 +1,23 @@ +package kr.co.vividnext.sodalive.audition + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.member.Member +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +@Entity +data class AuditionApplicant( + val voicePath: String, + val phoneNumber: String, + val isActive: Boolean = true +) : BaseEntity() { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "role_id", nullable = false) + var role: AuditionRole? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + var member: Member? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt new file mode 100644 index 0000000..4f7250e --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.audition + +import kr.co.vividnext.sodalive.common.BaseEntity +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +@Entity +data class AuditionRole( + val name: String, + val imagePath: String, + // 오디션 대본 URL + val auditionScriptUrl: String, + val isActive: Boolean +) : BaseEntity() { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "audition_id", nullable = false) + var audition: Audition? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionVote.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionVote.kt new file mode 100644 index 0000000..8e37808 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionVote.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.audition + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.member.Member +import javax.persistence.Entity +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +@Entity +class AuditionVote : BaseEntity() { + @ManyToOne + @JoinColumn(name = "member_id", nullable = false) + var member: Member? = null + + @ManyToOne + @JoinColumn(name = "applicant_id", nullable = false) + var applicant: AuditionApplicant? = null +} -- 2.40.1 From bb3263dd68f5d01a3a4efda24ae463392165e531 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 24 Dec 2024 16:28:11 +0900 Subject: [PATCH 02/40] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=98=A4?= =?UTF-8?q?=EB=94=94=EC=85=98=20=EC=83=81=EC=84=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/AdminAuditionController.kt | 6 +++++ .../admin/audition/AdminAuditionRepository.kt | 27 +++++++++++++++++++ .../admin/audition/AdminAuditionService.kt | 4 +++ .../audition/GetAuditionDetailResponse.kt | 18 +++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt index ab1e9ef..ebba99c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionController.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.data.domain.Pageable import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestMapping @@ -34,4 +35,9 @@ class AdminAuditionController(private val service: AdminAuditionService) { limit = pageable.pageSize.toLong() ) ) + + @GetMapping("/{id}") + fun getAuditionDetail(@PathVariable id: Long) = ApiResponse.ok( + service.getAuditionDetail(auditionId = id) + ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index f548802..6e0ad41 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -1,11 +1,13 @@ package kr.co.vividnext.sodalive.admin.audition +import com.querydsl.core.group.GroupBy.list import com.querydsl.core.types.dsl.DateTimePath import com.querydsl.core.types.dsl.Expressions import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.audition.Audition import kr.co.vividnext.sodalive.audition.QAudition.audition +import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole import org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository @@ -17,6 +19,7 @@ interface AdminAuditionRepository : JpaRepository, AdminAudition interface AdminAuditionQueryRepository { fun getAuditionList(offset: Long, limit: Long): List fun getAuditionListCount(): Int + fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse } class AdminAuditionQueryRepositoryImpl( @@ -54,6 +57,30 @@ class AdminAuditionQueryRepositoryImpl( .size } + override fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse { + return queryFactory + .select( + QGetAuditionDetailResponse( + audition.id, + audition.title, + audition.imagePath.prepend("/").prepend(coverImageHost), + audition.information, + audition.originalWorkUrl, + list( + QGetAuditionDetailRole( + auditionRole.id, + auditionRole.name, + auditionRole.imagePath.prepend("/").prepend(coverImageHost) + ) + ) + ) + ) + .from(audition) + .leftJoin(auditionRole).on(auditionRole.audition.id.eq(audition.id)) + .where(audition.id.eq(auditionId)) + .fetchFirst() + } + private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { return Expressions.stringTemplate( "DATE_FORMAT({0}, {1})", diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt index bfec948..5127a00 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt @@ -87,4 +87,8 @@ class AdminAuditionService( val items = repository.getAuditionList(offset = offset, limit = limit) return GetAuditionListResponse(totalCount, items) } + + fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse { + return repository.getAuditionDetail(auditionId = auditionId) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt new file mode 100644 index 0000000..803aca6 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.admin.audition + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionDetailResponse @QueryProjection constructor( + val id: Long, + val title: String, + val imageUrl: String, + val information: String, + val originalWorkUrl: String, + val roleList: List = listOf() +) + +data class GetAuditionDetailRole @QueryProjection constructor( + val id: Long, + val name: String, + val imageUrl: String +) -- 2.40.1 From 99fdf473ae84df6cf348067582fae0d091dcc248 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 25 Dec 2024 02:39:21 +0900 Subject: [PATCH 03/40] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=98=A4?= =?UTF-8?q?=EB=94=94=EC=85=98=20=EC=BA=90=EB=A6=AD=ED=84=B0=20-=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D,=20=EC=88=98=EC=A0=95,=20=EC=83=81=EC=84=B8,?= =?UTF-8?q?=20=EC=A7=80=EC=9B=90=EB=A6=AC=EC=8A=A4=ED=8A=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../role/AdminAuditionRoleController.kt | 45 +++++++++++ .../role/AdminAuditionRoleRepository.kt | 73 +++++++++++++++++ .../audition/role/AdminAuditionRoleService.kt | 81 +++++++++++++++++++ .../role/CreateAuditionRoleRequest.kt | 23 ++++++ .../role/GetAuditionRoleApplicantResponse.kt | 16 ++++ .../role/GetAuditionRoleDetailResponse.kt | 9 +++ .../role/UpdateAuditionRoleRequest.kt | 16 ++++ .../sodalive/audition/AuditionRole.kt | 9 ++- 8 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleApplicantResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt new file mode 100644 index 0000000..f13a248 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt @@ -0,0 +1,45 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.data.domain.Pageable +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestPart +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile + +@RestController +@RequestMapping("/admin/audition/role") +class AdminAuditionRoleController(private val service: AdminAuditionRoleService) { + @PostMapping + fun createAuditionRole( + @RequestPart("image") image: MultipartFile, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.createAuditionRole(image, requestString), "등록되었습니다.") + + @PutMapping + fun updateAuditionRole( + @RequestPart("image", required = false) image: MultipartFile? = null, + @RequestPart("request") requestString: String + ) = ApiResponse.ok(service.updateAuditionRole(image, requestString), "수정되었습니다.") + + @GetMapping("/{id}") + fun getAuditionRoleDetail(@PathVariable id: Long) = ApiResponse.ok( + service.getAuditionRoleDetail(auditionRoleId = id) + ) + + @GetMapping("/{id}/applicant") + fun getAuditionApplicantList( + @PathVariable id: Long, + pageable: Pageable + ) = ApiResponse.ok( + service.getAuditionApplicantList( + auditionRoleId = id, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt new file mode 100644 index 0000000..c4fdbac --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -0,0 +1,73 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.audition.AuditionRole +import kr.co.vividnext.sodalive.audition.QAuditionApplicant.auditionApplicant +import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole +import kr.co.vividnext.sodalive.audition.QAuditionVote.auditionVote +import kr.co.vividnext.sodalive.member.QMember.member +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository + +interface AdminAuditionRoleRepository : JpaRepository, AdminAuditionRoleQueryRepository + +interface AdminAuditionRoleQueryRepository { + fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailResponse + fun getAuditionApplicantList(auditionRoleId: Long, offset: Long, limit: Long): List + fun getAuditionApplicantTotalCount(auditionRoleId: Long): Int +} + +class AdminAuditionRoleQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudfrontHost: String +) : AdminAuditionRoleQueryRepository { + override fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailResponse { + return queryFactory + .select( + QGetAuditionRoleDetailResponse( + auditionRole.name, + auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.auditionScriptUrl + ) + ) + .from(auditionRole) + .where(auditionRole.id.eq(auditionRoleId)) + .fetchFirst() + } + + override fun getAuditionApplicantList( + auditionRoleId: Long, + offset: Long, + limit: Long + ): List { + return queryFactory + .select( + QGetAuditionRoleApplicantItem( + auditionApplicant.id, + member.nickname, + member.profileImage.prepend("/").prepend(cloudfrontHost), + auditionApplicant.voicePath.prepend("/").prepend(cloudfrontHost), + auditionVote.id.count() + ) + ) + .from(auditionApplicant) + .innerJoin(auditionApplicant.member, member) + .innerJoin(auditionApplicant.role, auditionRole) + .leftJoin(auditionVote).on(auditionApplicant.id.eq(auditionVote.applicant.id)) + .where(auditionRole.id.eq(auditionRoleId)) + .groupBy(auditionApplicant.id) + .fetch() + } + + override fun getAuditionApplicantTotalCount(auditionRoleId: Long): Int { + return queryFactory + .select(auditionApplicant.id) + .from(auditionApplicant) + .innerJoin(auditionApplicant.role, auditionRole) + .where(auditionRole.id.eq(auditionRoleId)) + .fetch() + .size + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt new file mode 100644 index 0000000..371c10c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt @@ -0,0 +1,81 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.admin.audition.AdminAuditionRepository +import kr.co.vividnext.sodalive.audition.AuditionRole +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.common.SodaException +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.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile + +@Service +class AdminAuditionRoleService( + private val s3Uploader: S3Uploader, + private val objectMapper: ObjectMapper, + private val repository: AdminAuditionRoleRepository, + private val auditionRepository: AdminAuditionRepository, + + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String +) { + @Transactional + fun createAuditionRole(image: MultipartFile, requestString: String) { + val request = objectMapper.readValue(requestString, CreateAuditionRoleRequest::class.java) + val auditionRole = AuditionRole(name = request.name, auditionScriptUrl = request.auditionScriptUrl) + val audition = auditionRepository.findByIdOrNull(id = request.auditionId) + ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + auditionRole.audition = audition + repository.save(auditionRole) + + val fileName = generateFileName("audition_role") + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "audition/role/${auditionRole.id}/$fileName" + ) + auditionRole.imagePath = imagePath + } + + @Transactional + fun updateAuditionRole(image: MultipartFile?, requestString: String) { + val request = objectMapper.readValue(requestString, UpdateAuditionRoleRequest::class.java) + val auditionRole = repository.findByIdOrNull(id = request.id) + ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + + if (!request.name.isNullOrBlank() && request.name.length > 2) { + auditionRole.name = request.name + } + + if (!request.auditionScriptUrl.isNullOrBlank()) { + auditionRole.auditionScriptUrl = request.auditionScriptUrl + } + + if (request.isActive != null) { + auditionRole.isActive = request.isActive + } + + if (image != null) { + val fileName = generateFileName("audition_role") + val imagePath = s3Uploader.upload( + inputStream = image.inputStream, + bucket = bucket, + filePath = "audition/role/${auditionRole.id}/$fileName" + ) + auditionRole.imagePath = imagePath + } + } + + fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailResponse { + return repository.getAuditionRoleDetail(auditionRoleId = auditionRoleId) + } + + fun getAuditionApplicantList(auditionRoleId: Long, offset: Long, limit: Long): GetAuditionRoleApplicantResponse { + val totalCount = repository.getAuditionApplicantTotalCount(auditionRoleId = auditionRoleId) + val items = repository.getAuditionApplicantList(auditionRoleId = auditionRoleId, offset = offset, limit = limit) + return GetAuditionRoleApplicantResponse(totalCount, items) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt new file mode 100644 index 0000000..71f13d7 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt @@ -0,0 +1,23 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import kr.co.vividnext.sodalive.common.SodaException + +data class CreateAuditionRoleRequest( + val auditionId: Long, + val name: String, + val auditionScriptUrl: String +) { + init { + if (auditionId < 0) { + throw SodaException("캐릭터가 등록될 오디션을 선택하세요") + } + + if (name.isBlank() || name.length < 2) { + throw SodaException("캐릭터명을 입력하세요") + } + + if (auditionScriptUrl.isBlank() || auditionScriptUrl.length < 10) { + throw SodaException("오디션 대본 URL을 입력하세요") + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleApplicantResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleApplicantResponse.kt new file mode 100644 index 0000000..8a10251 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleApplicantResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionRoleApplicantResponse( + val totalCount: Int, + val items: List +) + +data class GetAuditionRoleApplicantItem @QueryProjection constructor( + val applicantId: Long, + val nickname: String, + val profileImageUrl: String, + val voiceUrl: String, + val voteCount: Long +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt new file mode 100644 index 0000000..41e014c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt @@ -0,0 +1,9 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionRoleDetailResponse @QueryProjection constructor( + val name: String, + val imageUrl: String, + val auditionScriptUrl: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt new file mode 100644 index 0000000..82e6ab8 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.admin.audition.role + +import kr.co.vividnext.sodalive.common.SodaException + +data class UpdateAuditionRoleRequest( + val id: Long, + val name: String? = null, + val auditionScriptUrl: String? = null, + val isActive: Boolean? = null +) { + init { + if (id < 0) { + throw SodaException("잘못된 요청입니다.") + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt index 4f7250e..bd511c0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt @@ -8,12 +8,13 @@ import javax.persistence.ManyToOne @Entity data class AuditionRole( - val name: String, - val imagePath: String, + var name: String, // 오디션 대본 URL - val auditionScriptUrl: String, - val isActive: Boolean + var auditionScriptUrl: String? = null ) : BaseEntity() { + var isActive: Boolean = true + var imagePath: String? = null + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "audition_id", nullable = false) var audition: Audition? = null -- 2.40.1 From d940b3092f4c39159ca73841a71416066802b826 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 25 Dec 2024 02:46:55 +0900 Subject: [PATCH 04/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20-=20information=20column=20type=EC=9D=84?= =?UTF-8?q?=20TEXT=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt index 5970923..9de0835 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt @@ -2,11 +2,13 @@ package kr.co.vividnext.sodalive.audition import kr.co.vividnext.sodalive.common.BaseEntity import java.time.LocalDateTime +import javax.persistence.Column import javax.persistence.Entity @Entity data class Audition( var title: String, + @Column(columnDefinition = "TEXT") var information: String, var isAdult: Boolean = false, var endDate: LocalDateTime? = null, -- 2.40.1 From 86450533cf791a4ff69a15adb7027e489b15ecea Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 26 Dec 2024 22:43:15 +0900 Subject: [PATCH 05/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20API=20-=20endDate=EC=99=80=20=EC=9B=90?= =?UTF-8?q?=EC=9E=91=EB=A7=81=ED=81=AC=EA=B0=80=20null=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EB=B9=88=20=EC=B9=B8=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/AdminAuditionRepository.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index 6e0ad41..29b3a95 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.audition import com.querydsl.core.group.GroupBy.list +import com.querydsl.core.types.dsl.CaseBuilder import com.querydsl.core.types.dsl.DateTimePath import com.querydsl.core.types.dsl.Expressions import com.querydsl.core.types.dsl.StringTemplate @@ -34,12 +35,15 @@ class AdminAuditionQueryRepositoryImpl( QGetAuditionListItem( audition.id, audition.title, - getFormattedDate(audition.endDate), + CaseBuilder() + .`when`(audition.endDate.isNotNull) + .then(getFormattedDate(audition.endDate)) + .otherwise(""), audition.imagePath.prepend("/").prepend(coverImageHost), audition.isAdult, audition.isActive, audition.information, - audition.originalWorkUrl + audition.originalWorkUrl.coalesce("") ) ) .from(audition) -- 2.40.1 From 22c302efa0afd5e0dfe27b132820e4f7c378927a Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 01:56:45 +0900 Subject: [PATCH 06/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20-=20status(=EB=AA=A8=EC=A7=91=EC=83=81?= =?UTF-8?q?=ED=83=9C)=20=EC=B6=94=EA=B0=80=20-=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20api=20:=20=EC=9D=91=EB=8B=B5=EA=B0=92=EC=97=90=20st?= =?UTF-8?q?atus=20=EC=B6=94=EA=B0=80,=20=ED=99=9C=EC=84=B1=ED=99=94=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A7=8C=20=EC=A1=B0=ED=9A=8C=20-?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20api=20:=20status=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/AdminAuditionRepository.kt | 3 ++- .../sodalive/admin/audition/AdminAuditionService.kt | 12 ++++++++++++ .../admin/audition/GetAuditionListResponse.kt | 3 ++- .../sodalive/admin/audition/UpdateAuditionRequest.kt | 3 +++ .../kr/co/vividnext/sodalive/audition/Audition.kt | 6 +++++- .../co/vividnext/sodalive/audition/AuditionStatus.kt | 7 +++++++ 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionStatus.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index 29b3a95..dc2e8e1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -41,12 +41,13 @@ class AdminAuditionQueryRepositoryImpl( .otherwise(""), audition.imagePath.prepend("/").prepend(coverImageHost), audition.isAdult, - audition.isActive, audition.information, + audition.status, audition.originalWorkUrl.coalesce("") ) ) .from(audition) + .where(audition.isActive.isTrue) .offset(offset) .limit(limit) .orderBy(audition.isActive.desc(), audition.id.desc()) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt index 5127a00..6358a23 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.audition import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.audition.AuditionStatus import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.utils.generateFileName @@ -54,6 +55,17 @@ class AdminAuditionService( audition.isAdult = request.isAdult } + if (request.status != null) { + if ( + (audition.status == AuditionStatus.COMPLETED || audition.status == AuditionStatus.IN_PROGRESS) && + request.status == AuditionStatus.NOT_STARTED + ) { + throw SodaException("모집전 상태로 변경할 수 없습니다.") + } + + audition.status = request.status + } + if (request.endDateString != null) { val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") val endDate = LocalDateTime.parse(request.endDateString, dateTimeFormatter) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt index 10f2db4..4858790 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.audition import com.querydsl.core.annotations.QueryProjection +import kr.co.vividnext.sodalive.audition.AuditionStatus data class GetAuditionListResponse( val totalCount: Int, @@ -13,7 +14,7 @@ data class GetAuditionListItem @QueryProjection constructor( val endDate: String, val imageUrl: String, val isAdult: Boolean, - val isActive: Boolean, val information: String, + val status: AuditionStatus, val originalWorkUrl: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt index 5b1f16f..540e446 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt @@ -1,11 +1,14 @@ package kr.co.vividnext.sodalive.admin.audition +import kr.co.vividnext.sodalive.audition.AuditionStatus + data class UpdateAuditionRequest( val id: Long, val title: String? = null, val information: String? = null, val isAdult: Boolean? = null, val endDateString: String? = null, + val status: AuditionStatus? = null, val originalWorkUrl: String? = null, val isActive: Boolean? = null ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt index 9de0835..24e2727 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt @@ -4,6 +4,8 @@ import kr.co.vividnext.sodalive.common.BaseEntity import java.time.LocalDateTime import javax.persistence.Column import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated @Entity data class Audition( @@ -13,7 +15,9 @@ data class Audition( var isAdult: Boolean = false, var endDate: LocalDateTime? = null, // 원작 URL - var originalWorkUrl: String? = null + var originalWorkUrl: String? = null, + @Enumerated(value = EnumType.STRING) + var status: AuditionStatus = AuditionStatus.NOT_STARTED ) : BaseEntity() { var isActive: Boolean = true var imagePath: String? = null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionStatus.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionStatus.kt new file mode 100644 index 0000000..58a5c03 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionStatus.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.audition + +enum class AuditionStatus { + NOT_STARTED, + IN_PROGRESS, + COMPLETED +} -- 2.40.1 From 8b10e0e7703655dff68f3c8594642a13b891af6e Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 02:00:15 +0900 Subject: [PATCH 07/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD(=EC=BA=90=EB=A6=AD=ED=84=B0)=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20-=20status(=EB=AA=A8=EC=A7=91=EC=83=81=ED=83=9C)=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/AdminAuditionRepository.kt | 3 ++- .../sodalive/admin/audition/GetAuditionDetailResponse.kt | 4 +++- .../kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index dc2e8e1..c2b7827 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -75,7 +75,8 @@ class AdminAuditionQueryRepositoryImpl( QGetAuditionDetailRole( auditionRole.id, auditionRole.name, - auditionRole.imagePath.prepend("/").prepend(coverImageHost) + auditionRole.imagePath.prepend("/").prepend(coverImageHost), + auditionRole.status ) ) ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt index 803aca6..2cdc85a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.audition import com.querydsl.core.annotations.QueryProjection +import kr.co.vividnext.sodalive.audition.AuditionStatus data class GetAuditionDetailResponse @QueryProjection constructor( val id: Long, @@ -14,5 +15,6 @@ data class GetAuditionDetailResponse @QueryProjection constructor( data class GetAuditionDetailRole @QueryProjection constructor( val id: Long, val name: String, - val imageUrl: String + val imageUrl: String, + val status: AuditionStatus ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt index bd511c0..fb43371 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt @@ -10,7 +10,8 @@ import javax.persistence.ManyToOne data class AuditionRole( var name: String, // 오디션 대본 URL - var auditionScriptUrl: String? = null + var auditionScriptUrl: String? = null, + var status: AuditionStatus = AuditionStatus.IN_PROGRESS ) : BaseEntity() { var isActive: Boolean = true var imagePath: String? = null -- 2.40.1 From a3e717f2f7032cbb05adda0bfaca820254c4d2d8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 02:07:35 +0900 Subject: [PATCH 08/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD(=EC=BA=90=EB=A6=AD=ED=84=B0)=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20-=20status(=EB=AA=A8=EC=A7=91=EC=83=81=ED=83=9C)=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt index fb43371..40e8089 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt @@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.audition import kr.co.vividnext.sodalive.common.BaseEntity import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated import javax.persistence.FetchType import javax.persistence.JoinColumn import javax.persistence.ManyToOne @@ -11,6 +13,7 @@ data class AuditionRole( var name: String, // 오디션 대본 URL var auditionScriptUrl: String? = null, + @Enumerated(value = EnumType.STRING) var status: AuditionStatus = AuditionStatus.IN_PROGRESS ) : BaseEntity() { var isActive: Boolean = true -- 2.40.1 From a35b602f1a52c0ba02e1ca3ec2786d4b5cbc618a Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 21:53:37 +0900 Subject: [PATCH 09/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20roleList=EC=9D=98=20=EC=A1=B0=ED=9A=8C=EA=B0=92?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20emptyList?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A0=EC=96=B8=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/AdminAuditionRepository.kt | 3 ++- .../sodalive/admin/audition/GetAuditionDetailResponse.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index c2b7827..5c9a753 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -70,7 +70,7 @@ class AdminAuditionQueryRepositoryImpl( audition.title, audition.imagePath.prepend("/").prepend(coverImageHost), audition.information, - audition.originalWorkUrl, + audition.originalWorkUrl.coalesce(""), list( QGetAuditionDetailRole( auditionRole.id, @@ -84,6 +84,7 @@ class AdminAuditionQueryRepositoryImpl( .from(audition) .leftJoin(auditionRole).on(auditionRole.audition.id.eq(audition.id)) .where(audition.id.eq(auditionId)) + .groupBy(audition.id) .fetchFirst() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt index 2cdc85a..9ac5a2b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -9,7 +9,7 @@ data class GetAuditionDetailResponse @QueryProjection constructor( val imageUrl: String, val information: String, val originalWorkUrl: String, - val roleList: List = listOf() + val roleList: List = emptyList() ) data class GetAuditionDetailRole @QueryProjection constructor( -- 2.40.1 From 4f0a882b9e6deaf3b1c82148953ee374eacdee70 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 22:05:32 +0900 Subject: [PATCH 10/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20groupBy=EC=97=90=20=EB=B9=84=EC=A7=91=EA=B3=84?= =?UTF-8?q?=EC=97=B4=20=EB=AA=A8=EB=91=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/AdminAuditionRepository.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index 5c9a753..7bf3b17 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -84,7 +84,13 @@ class AdminAuditionQueryRepositoryImpl( .from(audition) .leftJoin(auditionRole).on(auditionRole.audition.id.eq(audition.id)) .where(audition.id.eq(auditionId)) - .groupBy(audition.id) + .groupBy( + audition.id, + audition.title, + audition.imagePath, + audition.information, + audition.originalWorkUrl + ) .fetchFirst() } -- 2.40.1 From c9e90974bdcc58398f776318f957d18cf493b0e6 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Dec 2024 23:19:21 +0900 Subject: [PATCH 11/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20=EC=98=A4=EB=94=94=EC=85=98=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EC=99=80=20=EC=98=A4=EB=94=94=EC=85=98=20?= =?UTF-8?q?=EB=B0=B0=EC=97=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=98=B8=EC=B6=9C=EC=9D=84=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/AdminAuditionRepository.kt | 26 +++---------------- .../admin/audition/AdminAuditionService.kt | 14 +++++++++- .../audition/GetAuditionDetailResponse.kt | 14 +++++++--- .../role/AdminAuditionRoleRepository.kt | 20 ++++++++++++++ 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index 7bf3b17..a457e59 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -1,6 +1,5 @@ package kr.co.vividnext.sodalive.admin.audition -import com.querydsl.core.group.GroupBy.list import com.querydsl.core.types.dsl.CaseBuilder import com.querydsl.core.types.dsl.DateTimePath import com.querydsl.core.types.dsl.Expressions @@ -8,7 +7,6 @@ import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.audition.Audition import kr.co.vividnext.sodalive.audition.QAudition.audition -import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole import org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository @@ -20,7 +18,7 @@ interface AdminAuditionRepository : JpaRepository, AdminAudition interface AdminAuditionQueryRepository { fun getAuditionList(offset: Long, limit: Long): List fun getAuditionListCount(): Int - fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse + fun getAuditionDetail(auditionId: Long): GetAuditionDetailRawData } class AdminAuditionQueryRepositoryImpl( @@ -62,35 +60,19 @@ class AdminAuditionQueryRepositoryImpl( .size } - override fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse { + override fun getAuditionDetail(auditionId: Long): GetAuditionDetailRawData { return queryFactory .select( - QGetAuditionDetailResponse( + QGetAuditionDetailRawData( audition.id, audition.title, audition.imagePath.prepend("/").prepend(coverImageHost), audition.information, - audition.originalWorkUrl.coalesce(""), - list( - QGetAuditionDetailRole( - auditionRole.id, - auditionRole.name, - auditionRole.imagePath.prepend("/").prepend(coverImageHost), - auditionRole.status - ) - ) + audition.originalWorkUrl.coalesce("") ) ) .from(audition) - .leftJoin(auditionRole).on(auditionRole.audition.id.eq(audition.id)) .where(audition.id.eq(auditionId)) - .groupBy( - audition.id, - audition.title, - audition.imagePath, - audition.information, - audition.originalWorkUrl - ) .fetchFirst() } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt index 6358a23..774b5b8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.admin.audition import com.fasterxml.jackson.databind.ObjectMapper +import kr.co.vividnext.sodalive.admin.audition.role.AdminAuditionRoleRepository import kr.co.vividnext.sodalive.audition.AuditionStatus import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException @@ -19,6 +20,7 @@ class AdminAuditionService( private val s3Uploader: S3Uploader, private val objectMapper: ObjectMapper, private val repository: AdminAuditionRepository, + private val roleRepository: AdminAuditionRoleRepository, @Value("\${cloud.aws.s3.bucket}") private val bucket: String @@ -101,6 +103,16 @@ class AdminAuditionService( } fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse { - return repository.getAuditionDetail(auditionId = auditionId) + val auditionDetail = repository.getAuditionDetail(auditionId = auditionId) + val roleList = roleRepository.getAuditionRoleListByAuditionId(auditionId = auditionId) + + return GetAuditionDetailResponse( + id = auditionDetail.id, + title = auditionDetail.title, + imageUrl = auditionDetail.imageUrl, + information = auditionDetail.information, + originalWorkUrl = auditionDetail.originalWorkUrl, + roleList = roleList + ) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt index 9ac5a2b..0071dd2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -3,16 +3,24 @@ package kr.co.vividnext.sodalive.admin.audition import com.querydsl.core.annotations.QueryProjection import kr.co.vividnext.sodalive.audition.AuditionStatus -data class GetAuditionDetailResponse @QueryProjection constructor( +data class GetAuditionDetailRawData @QueryProjection constructor( + val id: Long, + val title: String, + val imageUrl: String, + val information: String, + val originalWorkUrl: String +) + +data class GetAuditionDetailResponse( val id: Long, val title: String, val imageUrl: String, val information: String, val originalWorkUrl: String, - val roleList: List = emptyList() + val roleList: List ) -data class GetAuditionDetailRole @QueryProjection constructor( +data class GetAuditionRoleListData @QueryProjection constructor( val id: Long, val name: String, val imageUrl: String, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt index c4fdbac..0c71c7f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -1,7 +1,10 @@ package kr.co.vividnext.sodalive.admin.audition.role import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.admin.audition.GetAuditionRoleListData +import kr.co.vividnext.sodalive.admin.audition.QGetAuditionRoleListData import kr.co.vividnext.sodalive.audition.AuditionRole +import kr.co.vividnext.sodalive.audition.QAudition.audition import kr.co.vividnext.sodalive.audition.QAuditionApplicant.auditionApplicant import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole import kr.co.vividnext.sodalive.audition.QAuditionVote.auditionVote @@ -12,6 +15,7 @@ import org.springframework.data.jpa.repository.JpaRepository interface AdminAuditionRoleRepository : JpaRepository, AdminAuditionRoleQueryRepository interface AdminAuditionRoleQueryRepository { + fun getAuditionRoleListByAuditionId(auditionId: Long): List fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailResponse fun getAuditionApplicantList(auditionRoleId: Long, offset: Long, limit: Long): List fun getAuditionApplicantTotalCount(auditionRoleId: Long): Int @@ -23,6 +27,22 @@ class AdminAuditionRoleQueryRepositoryImpl( @Value("\${cloud.aws.cloud-front.host}") private val cloudfrontHost: String ) : AdminAuditionRoleQueryRepository { + override fun getAuditionRoleListByAuditionId(auditionId: Long): List { + return queryFactory + .select( + QGetAuditionRoleListData( + auditionRole.id, + auditionRole.name, + auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.status + ) + ) + .from(auditionRole) + .innerJoin(auditionRole.audition, audition) + .where(auditionRole.audition.id.eq(auditionId)) + .fetch() + } + override fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailResponse { return queryFactory .select( -- 2.40.1 From 1dba0a3d959e0927eb460ee47e651743d5dc811c Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 01:11:26 +0900 Subject: [PATCH 12/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=97=90=20=EB=8C=80=EB=B3=B8=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/GetAuditionDetailResponse.kt | 1 + .../sodalive/admin/audition/role/AdminAuditionRoleRepository.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt index 0071dd2..8c0ddcd 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -24,5 +24,6 @@ data class GetAuditionRoleListData @QueryProjection constructor( val id: Long, val name: String, val imageUrl: String, + val auditionScriptUrl: String, val status: AuditionStatus ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt index 0c71c7f..a830406 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -34,6 +34,7 @@ class AdminAuditionRoleQueryRepositoryImpl( auditionRole.id, auditionRole.name, auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.auditionScriptUrl, auditionRole.status ) ) -- 2.40.1 From 2e66b5fa457397af7e7fe697a8083975c2ab11dd Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 01:40:16 +0900 Subject: [PATCH 13/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=EB=90=9C=20=EB=B0=B0=EC=97=AD=EB=A7=8C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/role/AdminAuditionRoleRepository.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt index a830406..92c3844 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -40,7 +40,10 @@ class AdminAuditionRoleQueryRepositoryImpl( ) .from(auditionRole) .innerJoin(auditionRole.audition, audition) - .where(auditionRole.audition.id.eq(auditionId)) + .where( + auditionRole.audition.id.eq(auditionId), + auditionRole.isActive.isTrue + ) .fetch() } -- 2.40.1 From df3a00f8c0ed240bd6aa8f7875cf7db1058de496 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 01:51:46 +0900 Subject: [PATCH 14/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EC=88=98=EC=A0=95=20-=20=EB=AA=A8=EC=A7=91=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=B3=80=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt index 82e6ab8..51ac6e2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt @@ -1,11 +1,13 @@ package kr.co.vividnext.sodalive.admin.audition.role +import kr.co.vividnext.sodalive.audition.AuditionStatus import kr.co.vividnext.sodalive.common.SodaException data class UpdateAuditionRoleRequest( val id: Long, val name: String? = null, val auditionScriptUrl: String? = null, + val status: AuditionStatus? = null, val isActive: Boolean? = null ) { init { -- 2.40.1 From 6e6b27bb65b4fe635ac5a2819cf7e62513ccdba5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 02:04:04 +0900 Subject: [PATCH 15/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EC=88=98=EC=A0=95=20-=20=EB=AA=A8=EC=A7=91=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=B3=80=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/role/AdminAuditionRoleService.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt index 371c10c..6c6ecc6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt @@ -54,6 +54,10 @@ class AdminAuditionRoleService( auditionRole.auditionScriptUrl = request.auditionScriptUrl } + if (request.status != null) { + auditionRole.status = request.status + } + if (request.isActive != null) { auditionRole.isActive = request.isActive } -- 2.40.1 From 8cfe9ade9a5ebcefab34a932f7f61b944e7115a4 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 03:29:11 +0900 Subject: [PATCH 16/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EB=93=B1=EB=A1=9D/=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0=EC=97=AD=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/role/AdminAuditionRoleRepository.kt | 1 + .../admin/audition/role/AdminAuditionRoleService.kt | 10 +++++++++- .../admin/audition/role/CreateAuditionRoleRequest.kt | 5 +++++ .../audition/role/GetAuditionRoleDetailResponse.kt | 1 + .../admin/audition/role/UpdateAuditionRoleRequest.kt | 1 + .../kr/co/vividnext/sodalive/audition/AuditionRole.kt | 3 +++ 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt index 92c3844..7b6fb50 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -53,6 +53,7 @@ class AdminAuditionRoleQueryRepositoryImpl( QGetAuditionRoleDetailResponse( auditionRole.name, auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.information, auditionRole.auditionScriptUrl ) ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt index 6c6ecc6..37870ae 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt @@ -25,7 +25,11 @@ class AdminAuditionRoleService( @Transactional fun createAuditionRole(image: MultipartFile, requestString: String) { val request = objectMapper.readValue(requestString, CreateAuditionRoleRequest::class.java) - val auditionRole = AuditionRole(name = request.name, auditionScriptUrl = request.auditionScriptUrl) + val auditionRole = AuditionRole( + name = request.name, + information = request.information, + auditionScriptUrl = request.auditionScriptUrl + ) val audition = auditionRepository.findByIdOrNull(id = request.auditionId) ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") auditionRole.audition = audition @@ -50,6 +54,10 @@ class AdminAuditionRoleService( auditionRole.name = request.name } + if (!request.information.isNullOrBlank() && request.information.length >= 10) { + auditionRole.information = request.information + } + if (!request.auditionScriptUrl.isNullOrBlank()) { auditionRole.auditionScriptUrl = request.auditionScriptUrl } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt index 71f13d7..1332e8f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/CreateAuditionRoleRequest.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.common.SodaException data class CreateAuditionRoleRequest( val auditionId: Long, val name: String, + val information: String, val auditionScriptUrl: String ) { init { @@ -19,5 +20,9 @@ data class CreateAuditionRoleRequest( if (auditionScriptUrl.isBlank() || auditionScriptUrl.length < 10) { throw SodaException("오디션 대본 URL을 입력하세요") } + + if (information.isBlank() || information.length < 10) { + throw SodaException("오디션 캐릭터 정보는 최소 10글자 입니다") + } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt index 41e014c..2c3232c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/GetAuditionRoleDetailResponse.kt @@ -5,5 +5,6 @@ import com.querydsl.core.annotations.QueryProjection data class GetAuditionRoleDetailResponse @QueryProjection constructor( val name: String, val imageUrl: String, + val information: String, val auditionScriptUrl: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt index 51ac6e2..6454bf0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/UpdateAuditionRoleRequest.kt @@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.common.SodaException data class UpdateAuditionRoleRequest( val id: Long, val name: String? = null, + val information: String? = null, val auditionScriptUrl: String? = null, val status: AuditionStatus? = null, val isActive: Boolean? = null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt index 40e8089..b482787 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRole.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.audition import kr.co.vividnext.sodalive.common.BaseEntity +import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType import javax.persistence.Enumerated @@ -11,6 +12,8 @@ import javax.persistence.ManyToOne @Entity data class AuditionRole( var name: String, + @Column(columnDefinition = "TEXT") + var information: String, // 오디션 대본 URL var auditionScriptUrl: String? = null, @Enumerated(value = EnumType.STRING) -- 2.40.1 From 44dfa45ca8b4f603fed514e78d7f80ffbb45630e Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 03:40:04 +0900 Subject: [PATCH 17/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EB=B0=B0=EC=97=AD=20=EC=A0=95=EB=B3=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/GetAuditionDetailResponse.kt | 1 + .../sodalive/admin/audition/role/AdminAuditionRoleRepository.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt index 8c0ddcd..7a692a1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionDetailResponse.kt @@ -24,6 +24,7 @@ data class GetAuditionRoleListData @QueryProjection constructor( val id: Long, val name: String, val imageUrl: String, + val information: String, val auditionScriptUrl: String, val status: AuditionStatus ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt index 7b6fb50..3503306 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleRepository.kt @@ -34,6 +34,7 @@ class AdminAuditionRoleQueryRepositoryImpl( auditionRole.id, auditionRole.name, auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.information, auditionRole.auditionScriptUrl, auditionRole.status ) -- 2.40.1 From bb41a81eb1cb4977087002b5ec99b9c8e012f23c Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 28 Dec 2024 03:50:43 +0900 Subject: [PATCH 18/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EB=B0=B0?= =?UTF-8?q?=EC=97=AD=20=EC=88=98=EC=A0=95=20-=20=EB=B0=B0=EC=97=AD=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EA=B3=BC=20=EB=B0=B0=EC=97=AD=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/role/AdminAuditionRoleService.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt index 37870ae..5e5b24a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleService.kt @@ -50,11 +50,13 @@ class AdminAuditionRoleService( val auditionRole = repository.findByIdOrNull(id = request.id) ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") - if (!request.name.isNullOrBlank() && request.name.length > 2) { + if (!request.name.isNullOrBlank()) { + if (request.name.length < 2) throw SodaException("배역 이름은 최소 2글자 입니다") auditionRole.name = request.name } - if (!request.information.isNullOrBlank() && request.information.length >= 10) { + if (!request.information.isNullOrBlank()) { + if (request.information.length < 10) throw SodaException("오디션 배역 정보는 최소 10글자 입니다") auditionRole.information = request.information } -- 2.40.1 From f77b5f67d07773d5a7c220b965461a3561a6fe02 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 30 Dec 2024 20:29:28 +0900 Subject: [PATCH 19/40] =?UTF-8?q?=EC=95=B1=20API=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/AuditionController.kt | 30 +++++++ .../sodalive/audition/AuditionRepository.kt | 78 +++++++++++++++++++ .../sodalive/audition/AuditionService.kt | 14 ++++ .../audition/GetAuditionListResponse.kt | 16 ++++ 4 files changed, 138 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt new file mode 100644 index 0000000..ee3e7e4 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt @@ -0,0 +1,30 @@ +package kr.co.vividnext.sodalive.audition + +import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import org.springframework.data.domain.Pageable +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/audition") +class AuditionController(private val service: AuditionService) { + @GetMapping + fun getAuditionList( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + pageable: Pageable + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getAuditionList( + offset = pageable.offset, + limit = pageable.pageSize.toLong(), + isAdult = member.auth != null + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt new file mode 100644 index 0000000..9fa812d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -0,0 +1,78 @@ +package kr.co.vividnext.sodalive.audition + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.audition.QAudition.audition +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository + +interface AuditionRepository : JpaRepository, AuditionQueryRepository + +interface AuditionQueryRepository { + fun getInProgressAuditionCount(isAdult: Boolean): Int + fun getCompletedAuditionCount(isAdult: Boolean): Int + fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): List +} + +class AuditionQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudFrontHost: String +) : AuditionQueryRepository { + override fun getInProgressAuditionCount(isAdult: Boolean): Int { + var where = audition.isActive.isTrue + .and(audition.status.eq(AuditionStatus.IN_PROGRESS)) + + if (!isAdult) { + where = where.and(audition.isAdult.isFalse) + } + + return queryFactory + .select(audition.id) + .from(audition) + .where(where) + .fetch() + .size + } + + override fun getCompletedAuditionCount(isAdult: Boolean): Int { + var where = audition.isActive.isTrue + .and(audition.status.eq(AuditionStatus.COMPLETED)) + + if (!isAdult) { + where = where.and(audition.isAdult.isFalse) + } + + return queryFactory + .select(audition.id) + .from(audition) + .where(where) + .fetch() + .size + } + + override fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): List { + var where = audition.isActive.isTrue + .and( + audition.status.eq(AuditionStatus.COMPLETED) + .and(audition.status.eq(AuditionStatus.IN_PROGRESS)) + ) + + if (!isAdult) { + where = where.and(audition.isAdult.isFalse) + } + + return queryFactory + .select( + QGetAuditionListItem( + audition.id, + audition.title, + audition.imagePath.prepend("/").prepend(cloudFrontHost), + audition.status.eq(AuditionStatus.COMPLETED) + ) + ) + .from(audition) + .where(where) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt new file mode 100644 index 0000000..cde6ad5 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt @@ -0,0 +1,14 @@ +package kr.co.vividnext.sodalive.audition + +import org.springframework.stereotype.Service + +@Service +class AuditionService(private val repository: AuditionRepository) { + fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): GetAuditionListResponse { + val inProgressCount = repository.getInProgressAuditionCount(isAdult = isAdult) + val completedCount = repository.getCompletedAuditionCount(isAdult = isAdult) + val items = repository.getAuditionList(offset = offset, limit = limit, isAdult = isAdult) + + return GetAuditionListResponse(inProgressCount, completedCount, items) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt new file mode 100644 index 0000000..67452cd --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.audition + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionListResponse( + val inProgressCount: Int, + val completedCount: Int, + val items: List +) + +data class GetAuditionListItem @QueryProjection constructor( + val id: Long, + val title: String, + val imageUrl: String, + val isOff: Boolean +) -- 2.40.1 From b56b2e15aff644dfb8f91709a7aa0df010c18d55 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 30 Dec 2024 20:49:03 +0900 Subject: [PATCH 20/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=BA=90?= =?UTF-8?q?=EB=A6=AD=ED=84=B0(=EB=B0=B0=EC=97=AD)=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=20API=20-=20=EA=B4=80=EB=A6=AC=EC=9E=90=EB=A7=8C=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/audition/role/AdminAuditionRoleController.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt index f13a248..947f132 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/role/AdminAuditionRoleController.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.audition.role import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController import org.springframework.web.multipart.MultipartFile @RestController +@PreAuthorize("hasRole('ADMIN')") @RequestMapping("/admin/audition/role") class AdminAuditionRoleController(private val service: AdminAuditionRoleService) { @PostMapping -- 2.40.1 From ddd552deb4353effd998dcd683a9ba23d6a12276 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 30 Dec 2024 23:42:25 +0900 Subject: [PATCH 21/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EC=83=81=EC=84=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/AuditionController.kt | 11 +++++ .../sodalive/audition/AuditionRepository.kt | 16 +++++++ .../sodalive/audition/AuditionService.kt | 19 +++++++- .../audition/GetAuditionDetailResponse.kt | 25 +++++++++++ .../audition/role/AuditionRoleRepository.kt | 43 +++++++++++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionDetailResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt index ee3e7e4..ce4593c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionController.kt @@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -27,4 +28,14 @@ class AuditionController(private val service: AuditionService) { ) ) } + + @GetMapping("/{id}") + fun getAuditionDetail( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.getAuditionDetail(auditionId = id)) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt index 9fa812d..8bb8360 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -11,6 +11,7 @@ interface AuditionQueryRepository { fun getInProgressAuditionCount(isAdult: Boolean): Int fun getCompletedAuditionCount(isAdult: Boolean): Int fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): List + fun getAuditionDetail(auditionId: Long): GetAuditionDetailRawData } class AuditionQueryRepositoryImpl( @@ -75,4 +76,19 @@ class AuditionQueryRepositoryImpl( .where(where) .fetch() } + + override fun getAuditionDetail(auditionId: Long): GetAuditionDetailRawData { + return queryFactory + .select( + QGetAuditionDetailRawData( + audition.id, + audition.title, + audition.imagePath.prepend("/").prepend(cloudFrontHost), + audition.information + ) + ) + .from(audition) + .where(audition.id.eq(auditionId)) + .fetchFirst() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt index cde6ad5..2f6286c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt @@ -1,9 +1,13 @@ package kr.co.vividnext.sodalive.audition +import kr.co.vividnext.sodalive.audition.role.AuditionRoleRepository import org.springframework.stereotype.Service @Service -class AuditionService(private val repository: AuditionRepository) { +class AuditionService( + private val repository: AuditionRepository, + private val roleRepository: AuditionRoleRepository +) { fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): GetAuditionListResponse { val inProgressCount = repository.getInProgressAuditionCount(isAdult = isAdult) val completedCount = repository.getCompletedAuditionCount(isAdult = isAdult) @@ -11,4 +15,17 @@ class AuditionService(private val repository: AuditionRepository) { return GetAuditionListResponse(inProgressCount, completedCount, items) } + + fun getAuditionDetail(auditionId: Long): GetAuditionDetailResponse { + val auditionDetail = repository.getAuditionDetail(auditionId = auditionId) + val roleList = roleRepository.getAuditionRoleListByAuditionId(auditionId = auditionId) + + return GetAuditionDetailResponse( + auditionId = auditionId, + title = auditionDetail.title, + imageUrl = auditionDetail.imageUrl, + information = auditionDetail.information, + roleList = roleList + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionDetailResponse.kt new file mode 100644 index 0000000..4a99d8c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/GetAuditionDetailResponse.kt @@ -0,0 +1,25 @@ +package kr.co.vividnext.sodalive.audition + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionDetailRawData @QueryProjection constructor( + val auditionId: Long, + val title: String, + val imageUrl: String, + val information: String +) + +data class GetAuditionDetailResponse( + val auditionId: Long, + val title: String, + val imageUrl: String, + val information: String, + val roleList: List +) + +data class GetAuditionRoleListData @QueryProjection constructor( + val roleId: Long, + val name: String, + val imageUrl: String, + val isComplete: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt new file mode 100644 index 0000000..b46f4df --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt @@ -0,0 +1,43 @@ +package kr.co.vividnext.sodalive.audition.role + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.audition.AuditionRole +import kr.co.vividnext.sodalive.audition.AuditionStatus +import kr.co.vividnext.sodalive.audition.GetAuditionRoleListData +import kr.co.vividnext.sodalive.audition.QAudition.audition +import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole +import kr.co.vividnext.sodalive.audition.QGetAuditionRoleListData +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository + +interface AuditionRoleRepository : JpaRepository, AuditionRoleQueryRepository + +interface AuditionRoleQueryRepository { + fun getAuditionRoleListByAuditionId(auditionId: Long): List +} + +class AuditionRoleQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudfrontHost: String +) : AuditionRoleQueryRepository { + override fun getAuditionRoleListByAuditionId(auditionId: Long): List { + return queryFactory + .select( + QGetAuditionRoleListData( + auditionRole.id, + auditionRole.name, + auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.status.eq(AuditionStatus.COMPLETED) + ) + ) + .from(auditionRole) + .innerJoin(auditionRole.audition, audition) + .where( + audition.id.eq(auditionId), + auditionRole.isActive.isTrue + ) + .fetch() + } +} -- 2.40.1 From affbb3eba323f06effadbc7b8d018a4928fc6e22 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 31 Dec 2024 02:46:19 +0900 Subject: [PATCH 22/40] =?UTF-8?q?=EC=95=B1=20-=20=EB=B0=B0=EC=97=AD=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20API,=20=EC=A7=80=EC=9B=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../applicant/AuditionApplicantController.kt | 34 +++++++ .../applicant/AuditionApplicantRepository.kt | 93 +++++++++++++++++++ .../applicant/AuditionApplicantService.kt | 26 ++++++ .../applicant/AuditionApplicantSortType.kt | 5 + .../GetAuditionApplicantListResponse.kt | 16 ++++ .../audition/role/AuditionRoleController.kt | 29 ++++++ .../audition/role/AuditionRoleRepository.kt | 22 +++++ .../audition/role/AuditionRoleService.kt | 23 +++++ .../role/GetAuditionRoleDetailResponse.kt | 32 +++++++ 9 files changed, 280 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantSortType.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/role/GetAuditionRoleDetailResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt new file mode 100644 index 0000000..ff03e54 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt @@ -0,0 +1,34 @@ +package kr.co.vividnext.sodalive.audition.applicant + +import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.member.Member +import org.springframework.data.domain.Pageable +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/audition/applicant") +class AuditionApplicantController(private val service: AuditionApplicantService) { + @GetMapping + fun getAuditionApplicantList( + @RequestParam auditionRoleId: Long, + @RequestParam sortType: AuditionApplicantSortType, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, + pageable: Pageable + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getAuditionApplicantList( + auditionRoleId = auditionRoleId, + sortType = sortType, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt new file mode 100644 index 0000000..23f88ac --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt @@ -0,0 +1,93 @@ +package kr.co.vividnext.sodalive.audition.applicant + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.audition.AuditionApplicant +import kr.co.vividnext.sodalive.audition.QAuditionApplicant.auditionApplicant +import kr.co.vividnext.sodalive.audition.QAuditionRole.auditionRole +import kr.co.vividnext.sodalive.audition.QAuditionVote.auditionVote +import kr.co.vividnext.sodalive.member.QMember.member +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.jpa.repository.JpaRepository + +interface AuditionApplicantRepository : JpaRepository, AuditionApplicantQueryRepository + +interface AuditionApplicantQueryRepository { + fun isAlreadyApplicant(auditionRoleId: Long, memberId: Long): Boolean + fun getAuditionApplicantTotalCount(auditionRoleId: Long): Int + fun getAuditionApplicantList( + auditionRoleId: Long, + sortType: AuditionApplicantSortType, + offset: Long, + limit: Long + ): List +} + +class AuditionApplicantQueryRepositoryImpl( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val cloudFrontHost: String +) : AuditionApplicantQueryRepository { + override fun isAlreadyApplicant(auditionRoleId: Long, memberId: Long): Boolean { + return queryFactory + .select(auditionApplicant.id) + .from(auditionApplicant) + .innerJoin(auditionApplicant.role, auditionRole) + .innerJoin(auditionApplicant.member, member) + .where( + auditionRole.id.eq(auditionRoleId), + member.id.eq(memberId), + auditionApplicant.isActive.isTrue + ) + .fetch() + .size > 0 + } + + override fun getAuditionApplicantTotalCount(auditionRoleId: Long): Int { + return queryFactory + .select(auditionApplicant.id) + .from(auditionApplicant) + .innerJoin(auditionApplicant.role, auditionRole) + .where( + auditionRole.id.eq(auditionRoleId), + auditionRole.isActive.isTrue + ) + .fetch() + .size + } + + override fun getAuditionApplicantList( + auditionRoleId: Long, + sortType: AuditionApplicantSortType, + offset: Long, + limit: Long + ): List { + val orderBy = if (sortType == AuditionApplicantSortType.LIKES) { + auditionVote.id.count().desc() + } else { + auditionApplicant.id.desc() + } + + return queryFactory + .select( + QGetAuditionRoleApplicantItem( + auditionApplicant.id, + member.nickname, + member.profileImage.prepend("/").prepend(cloudFrontHost), + auditionApplicant.voicePath.prepend("/").prepend(cloudFrontHost), + auditionVote.id.count() + ) + ) + .from(auditionApplicant) + .innerJoin(auditionApplicant.member, member) + .innerJoin(auditionApplicant.role, auditionRole) + .leftJoin(auditionVote).on(auditionApplicant.id.eq(auditionVote.applicant.id)) + .where( + auditionRole.id.eq(auditionRoleId), + auditionRole.isActive.isTrue + ) + .groupBy(auditionApplicant.id) + .orderBy(orderBy) + .fetch() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt new file mode 100644 index 0000000..edc1c94 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt @@ -0,0 +1,26 @@ +package kr.co.vividnext.sodalive.audition.applicant + +import org.springframework.stereotype.Service + +@Service +class AuditionApplicantService(private val repository: AuditionApplicantRepository) { + fun getAuditionApplicantList( + auditionRoleId: Long, + sortType: AuditionApplicantSortType, + offset: Long, + limit: Long + ): GetAuditionApplicantListResponse { + val totalCount = repository.getAuditionApplicantTotalCount(auditionRoleId = auditionRoleId) + val items = repository.getAuditionApplicantList( + auditionRoleId = auditionRoleId, + sortType = sortType, + offset = offset, + limit = limit + ) + + return GetAuditionApplicantListResponse( + totalCount = totalCount, + items = items + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantSortType.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantSortType.kt new file mode 100644 index 0000000..649dc1e --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantSortType.kt @@ -0,0 +1,5 @@ +package kr.co.vividnext.sodalive.audition.applicant + +enum class AuditionApplicantSortType { + NEWEST, LIKES +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt new file mode 100644 index 0000000..d86ff49 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.audition.applicant + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionApplicantListResponse( + val totalCount: Int, + val items: List +) + +data class GetAuditionRoleApplicantItem @QueryProjection constructor( + val applicantId: Long, + val nickname: String, + val profileImageUrl: String, + val voiceUrl: String, + val voteCount: Long +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleController.kt new file mode 100644 index 0000000..5f7a48b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleController.kt @@ -0,0 +1,29 @@ +package kr.co.vividnext.sodalive.audition.role + +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.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/audition/role") +class AuditionRoleController(private val service: AuditionRoleService) { + @GetMapping("/{id}") + fun getAuditionRoleDetail( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok( + service.getAuditionRoleDetail( + auditionRoleId = id, + memberId = member.id!! + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt index b46f4df..735dffc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt @@ -14,6 +14,7 @@ interface AuditionRoleRepository : JpaRepository, AuditionRo interface AuditionRoleQueryRepository { fun getAuditionRoleListByAuditionId(auditionId: Long): List + fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailData? } class AuditionRoleQueryRepositoryImpl( @@ -40,4 +41,25 @@ class AuditionRoleQueryRepositoryImpl( ) .fetch() } + + override fun getAuditionRoleDetail(auditionRoleId: Long): GetAuditionRoleDetailData? { + return queryFactory + .select( + QGetAuditionRoleDetailData( + auditionRole.id, + auditionRole.name, + auditionRole.imagePath.prepend("/").prepend(cloudfrontHost), + auditionRole.information, + audition.originalWorkUrl, + auditionRole.auditionScriptUrl + ) + ) + .from(auditionRole) + .innerJoin(auditionRole.audition, audition) + .where( + auditionRole.id.eq(auditionRoleId), + auditionRole.isActive.isTrue + ) + .fetchFirst() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleService.kt new file mode 100644 index 0000000..c1e05d3 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleService.kt @@ -0,0 +1,23 @@ +package kr.co.vividnext.sodalive.audition.role + +import kr.co.vividnext.sodalive.audition.applicant.AuditionApplicantRepository +import kr.co.vividnext.sodalive.common.SodaException +import org.springframework.stereotype.Service + +@Service +class AuditionRoleService( + private val repository: AuditionRoleRepository, + private val applicantRepository: AuditionApplicantRepository +) { + fun getAuditionRoleDetail(auditionRoleId: Long, memberId: Long): GetAuditionRoleDetailResponse { + val roleDetailData = repository.getAuditionRoleDetail(auditionRoleId = auditionRoleId) + ?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") + + val isAlreadyApplicant = applicantRepository.isAlreadyApplicant( + auditionRoleId = auditionRoleId, + memberId = memberId + ) + + return roleDetailData.toDetailResponse(isAlreadyApplicant = isAlreadyApplicant) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/GetAuditionRoleDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/GetAuditionRoleDetailResponse.kt new file mode 100644 index 0000000..c49287c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/GetAuditionRoleDetailResponse.kt @@ -0,0 +1,32 @@ +package kr.co.vividnext.sodalive.audition.role + +import com.querydsl.core.annotations.QueryProjection + +data class GetAuditionRoleDetailResponse( + val auditionRoleId: Long, + val name: String, + val imageUrl: String, + val information: String, + val originalWorkUrl: String, + val auditionScriptUrl: String, + val isAlreadyApplicant: Boolean = false +) + +data class GetAuditionRoleDetailData @QueryProjection constructor( + val auditionRoleId: Long, + val name: String, + val imageUrl: String, + val information: String, + val originalWorkUrl: String, + val auditionScriptUrl: String +) { + fun toDetailResponse(isAlreadyApplicant: Boolean) = GetAuditionRoleDetailResponse( + auditionRoleId = auditionRoleId, + name = name, + imageUrl = imageUrl, + information = information, + originalWorkUrl = originalWorkUrl, + auditionScriptUrl = auditionScriptUrl, + isAlreadyApplicant = isAlreadyApplicant + ) +} -- 2.40.1 From 00c306475cba85de6d46ce2eb038583008d6f11c Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 31 Dec 2024 03:37:00 +0900 Subject: [PATCH 23/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20API=20-=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=A0=95=EB=A0=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/audition/AuditionRepository.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt index 8bb8360..8602388 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -74,6 +74,7 @@ class AuditionQueryRepositoryImpl( ) .from(audition) .where(where) + .orderBy(audition.status.desc()) .fetch() } -- 2.40.1 From 9315447618fd83b567f17578a8f93c0a32712549 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 31 Dec 2024 07:26:25 +0900 Subject: [PATCH 24/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20API=20-=20offset,=20l?= =?UTF-8?q?imit=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/audition/AuditionRepository.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt index 8602388..1c63e6f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -56,7 +56,7 @@ class AuditionQueryRepositoryImpl( var where = audition.isActive.isTrue .and( audition.status.eq(AuditionStatus.COMPLETED) - .and(audition.status.eq(AuditionStatus.IN_PROGRESS)) + .or(audition.status.eq(AuditionStatus.IN_PROGRESS)) ) if (!isAdult) { @@ -74,6 +74,8 @@ class AuditionQueryRepositoryImpl( ) .from(audition) .where(where) + .offset(offset) + .limit(limit) .orderBy(audition.status.desc()) .fetch() } -- 2.40.1 From 8385800e48ece098e8abfe0c17b0af0c528e4e8e Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 31 Dec 2024 09:12:26 +0900 Subject: [PATCH 25/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EC=83=81=EC=84=B8=20=EC=BA=90=EB=A6=AD=ED=84=B0=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20status=20=EB=82=B4=EB=A6=BC?= =?UTF-8?q?=EC=B0=A8=EC=88=9C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/audition/role/AuditionRoleRepository.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt index 735dffc..b914f58 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/role/AuditionRoleRepository.kt @@ -39,6 +39,7 @@ class AuditionRoleQueryRepositoryImpl( audition.id.eq(auditionId), auditionRole.isActive.isTrue ) + .orderBy(auditionRole.status.desc(), auditionRole.id.desc()) .fetch() } -- 2.40.1 From 96f571e0c439a7e79399a71c04ed50b94ae7b471 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Jan 2025 19:32:31 +0900 Subject: [PATCH 26/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EC=A7=80=EC=9B=90=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/AuditionApplicant.kt | 2 +- .../applicant/ApplyAuditionRoleRequest.kt | 6 +++ .../applicant/AuditionApplicantController.kt | 21 ++++++++ .../applicant/AuditionApplicantService.kt | 50 ++++++++++++++++++- 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/ApplyAuditionRoleRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt index e485407..21dc568 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt @@ -9,8 +9,8 @@ import javax.persistence.ManyToOne @Entity data class AuditionApplicant( - val voicePath: String, val phoneNumber: String, + var voicePath: String? = null, val isActive: Boolean = true ) : BaseEntity() { @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/ApplyAuditionRoleRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/ApplyAuditionRoleRequest.kt new file mode 100644 index 0000000..c5b7d4e --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/ApplyAuditionRoleRequest.kt @@ -0,0 +1,6 @@ +package kr.co.vividnext.sodalive.audition.applicant + +data class ApplyAuditionRoleRequest( + val roleId: Long, + val phoneNumber: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt index ff03e54..9caeb2d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantController.kt @@ -6,9 +6,12 @@ import kr.co.vividnext.sodalive.member.Member import org.springframework.data.domain.Pageable import org.springframework.security.core.annotation.AuthenticationPrincipal 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.RequestParam +import org.springframework.web.bind.annotation.RequestPart import org.springframework.web.bind.annotation.RestController +import org.springframework.web.multipart.MultipartFile @RestController @RequestMapping("/audition/applicant") @@ -31,4 +34,22 @@ 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 + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt index edc1c94..d27f7ca 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt @@ -1,9 +1,30 @@ 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.transaction.annotation.Transactional +import org.springframework.web.multipart.MultipartFile @Service -class AuditionApplicantService(private val repository: AuditionApplicantRepository) { +class AuditionApplicantService( + 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( auditionRoleId: Long, sortType: AuditionApplicantSortType, @@ -23,4 +44,31 @@ class AuditionApplicantService(private val repository: AuditionApplicantReposito 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 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 + } } -- 2.40.1 From 7a395a9906dcc9dbfc5a9d90090207d6109c1874 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Jan 2025 22:46:57 +0900 Subject: [PATCH 27/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=EC=A7=80=EC=9B=90=20API=20-=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=97=90=20=EC=A7=80=EC=9B=90=ED=95=9C=20=EB=82=B4=EC=97=AD?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=88=EC=9C=BC=EB=A9=B4=20false=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=ED=9B=84=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/AuditionApplicant.kt | 2 +- .../applicant/AuditionApplicantRepository.kt | 13 +++++++++++++ .../audition/applicant/AuditionApplicantService.kt | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt index 21dc568..fcba813 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionApplicant.kt @@ -11,7 +11,7 @@ import javax.persistence.ManyToOne data class AuditionApplicant( val phoneNumber: String, var voicePath: String? = null, - val isActive: Boolean = true + var isActive: Boolean = true ) : BaseEntity() { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "role_id", nullable = false) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt index 23f88ac..c20599f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt @@ -20,6 +20,8 @@ interface AuditionApplicantQueryRepository { offset: Long, limit: Long ): List + + fun findActiveApplicantByMemberIdAndRoleId(memberId: Long, roleId: Long): AuditionApplicant? } class AuditionApplicantQueryRepositoryImpl( @@ -90,4 +92,15 @@ class AuditionApplicantQueryRepositoryImpl( .orderBy(orderBy) .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() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt index d27f7ca..971bd01 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt @@ -53,6 +53,16 @@ class AuditionApplicantService( 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 -- 2.40.1 From cd0c066978f8e84275129782e5598455726ccb63 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 00:09:53 +0900 Subject: [PATCH 28/40] =?UTF-8?q?=EC=95=B1=20-=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=85=98=20=ED=88=AC=ED=91=9C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../audition/vote/AuditionVoteController.kt | 33 ++++++++++ .../audition/vote/AuditionVoteRepository.kt | 40 ++++++++++++ .../audition/vote/AuditionVoteService.kt | 64 +++++++++++++++++++ .../vote/VoteAuditionApplicantRequest.kt | 7 ++ .../co/vividnext/sodalive/can/use/CanUsage.kt | 3 +- 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/VoteAuditionApplicantRequest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteController.kt new file mode 100644 index 0000000..434783b --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteController.kt @@ -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 + ) + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt new file mode 100644 index 0000000..2a5d61a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt @@ -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, 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 + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt new file mode 100644 index 0000000..4496ead --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt @@ -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) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/VoteAuditionApplicantRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/VoteAuditionApplicantRequest.kt new file mode 100644 index 0000000..2fb1957 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/VoteAuditionApplicantRequest.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.audition.vote + +data class VoteAuditionApplicantRequest( + val applicantId: Long, + val timezone: String, + val container: String +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/CanUsage.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/CanUsage.kt index 6677b8f..0b26698 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/CanUsage.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/CanUsage.kt @@ -8,5 +8,6 @@ enum class CanUsage { ORDER_CONTENT, SPIN_ROULETTE, PAID_COMMUNITY_POST, - ALARM_SLOT + ALARM_SLOT, + AUDITION_VOTE } -- 2.40.1 From 82b109e3bd52c8158b71586addc957095e99a2e1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 00:42:54 +0900 Subject: [PATCH 29/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20API=20-=20=ED=88=AC=ED=91=9C=EC=8B=9C=20=EC=96=B4?= =?UTF-8?q?=EB=96=A4=20=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=97=90=20=ED=88=AC=ED=91=9C=ED=96=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20-=20=EC=BA=94=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=82=B4=EC=97=AD=EC=97=90=20"[=EC=98=A4=EB=94=94=EC=85=98=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C]=20=EB=8B=89=EB=84=A4=EC=9E=84"=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/audition/vote/AuditionVoteService.kt | 1 + src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt | 1 + .../co/vividnext/sodalive/can/payment/CanPaymentService.kt | 4 ++++ src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt | 5 +++++ 4 files changed, 11 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt index 4496ead..a897527 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt @@ -52,6 +52,7 @@ class AuditionVoteService( memberId = member.id!!, needCan = 1, canUsage = CanUsage.AUDITION_VOTE, + auditionApplicant = applicant, container = container ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index 87025af..988cee3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -71,6 +71,7 @@ class CanService(private val repository: CanRepository) { CanUsage.ALARM_SLOT -> "알람 슬롯 구매" CanUsage.ORDER_CONTENT -> "[콘텐츠 구매] ${it.audioContent!!.title}" CanUsage.PAID_COMMUNITY_POST -> "[게시글 보기] ${it.communityPost?.member?.nickname ?: ""}" + CanUsage.AUDITION_VOTE -> "[오디션 투표] ${it.auditionApplicant?.member?.nickname ?: ""}" } val createdAt = it.createdAt!! diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt index c50f823..a90f74a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.can.payment +import kr.co.vividnext.sodalive.audition.AuditionApplicant import kr.co.vividnext.sodalive.can.CanRepository import kr.co.vividnext.sodalive.can.charge.Charge import kr.co.vividnext.sodalive.can.charge.ChargeRepository @@ -41,6 +42,7 @@ class CanPaymentService( order: Order? = null, audioContent: AudioContent? = null, communityPost: CreatorCommunity? = null, + auditionApplicant: AuditionApplicant? = null, container: String ) { val member = memberRepository.findByIdOrNull(id = memberId) @@ -100,6 +102,8 @@ class CanPaymentService( useCan.member = member } else if (canUsage == CanUsage.ALARM_SLOT) { useCan.member = member + } else if (canUsage == CanUsage.AUDITION_VOTE && auditionApplicant != null) { + useCan.member = member } else if (canUsage == CanUsage.HEART && liveRoom != null) { recipientId = liveRoom.member!!.id!! useCan.room = liveRoom diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt index 31ac687..5a879f4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/use/UseCan.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.can.use +import kr.co.vividnext.sodalive.audition.AuditionApplicant import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.content.AudioContent import kr.co.vividnext.sodalive.content.order.Order @@ -53,6 +54,10 @@ data class UseCan( @JoinColumn(name = "creator_community_id", nullable = true) var communityPost: CreatorCommunity? = null + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "audition_applicant_id", nullable = true) + var auditionApplicant: AuditionApplicant? = null + @OneToMany(mappedBy = "useCan", cascade = [CascadeType.ALL]) val useCanCalculates: MutableList = mutableListOf() } -- 2.40.1 From b10c102f9401d1c2837e3eaa8b48be18611a3b29 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 01:03:38 +0900 Subject: [PATCH 30/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20API=20-=20=ED=88=AC=ED=91=9C=EC=8B=9C=20=EC=96=B4?= =?UTF-8?q?=EB=96=A4=20=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=97=90=20=ED=88=AC=ED=91=9C=ED=96=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20-=20=EC=BA=94=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=82=B4=EC=97=AD=EC=97=90=20"[=EC=98=A4=EB=94=94=EC=85=98=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C]=20=EB=8B=89=EB=84=A4=EC=9E=84"=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt index a90f74a..5f60109 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/payment/CanPaymentService.kt @@ -103,6 +103,7 @@ class CanPaymentService( } else if (canUsage == CanUsage.ALARM_SLOT) { useCan.member = member } else if (canUsage == CanUsage.AUDITION_VOTE && auditionApplicant != null) { + useCan.auditionApplicant = auditionApplicant useCan.member = member } else if (canUsage == CanUsage.HEART && liveRoom != null) { recipientId = liveRoom.member!!.id!! -- 2.40.1 From c8f96a10f0a4083d5583936f5a6341a5dff9ebde Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 01:29:42 +0900 Subject: [PATCH 31/40] =?UTF-8?q?=EC=BA=94=20=EC=82=AC=EC=9A=A9=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20-=20=EC=98=A4=EB=94=94=EC=85=98=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20-=20"[=EC=98=A4=EB=94=94=EC=85=98=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C]=20=EC=98=A4=EB=94=94=EC=85=98=EB=AA=85"=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt index 988cee3..33466a4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/can/CanService.kt @@ -71,7 +71,7 @@ class CanService(private val repository: CanRepository) { CanUsage.ALARM_SLOT -> "알람 슬롯 구매" CanUsage.ORDER_CONTENT -> "[콘텐츠 구매] ${it.audioContent!!.title}" CanUsage.PAID_COMMUNITY_POST -> "[게시글 보기] ${it.communityPost?.member?.nickname ?: ""}" - CanUsage.AUDITION_VOTE -> "[오디션 투표] ${it.auditionApplicant?.member?.nickname ?: ""}" + CanUsage.AUDITION_VOTE -> "[오디션 투표] ${it.auditionApplicant?.role?.audition?.title ?: ""}" } val createdAt = it.createdAt!! -- 2.40.1 From 80841fe5439005574849a6510f14e0b5c310a69a Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 07:53:16 +0900 Subject: [PATCH 32/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20-=20=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=ED=8C=8C=EC=9D=BC=20=EC=A0=80=EC=9E=A5=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/applicant/AuditionApplicantService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt index 971bd01..92c410e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantService.kt @@ -76,7 +76,7 @@ class AuditionApplicantService( val contentPath = s3Uploader.upload( inputStream = contentFile.inputStream, bucket = bucket, - filePath = "audition/${applicant.id}/$contentFileName", + filePath = "audition/applicant/${applicant.id}/$contentFileName", metadata = metadata ) applicant.voicePath = contentPath -- 2.40.1 From 460196dc4ded83571a871af66a9fe1ad7c65a780 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 08:04:58 +0900 Subject: [PATCH 33/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/applicant/AuditionApplicantRepository.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt index c20599f..3c83256 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt @@ -90,6 +90,8 @@ class AuditionApplicantQueryRepositoryImpl( ) .groupBy(auditionApplicant.id) .orderBy(orderBy) + .offset(offset) + .limit(limit) .fetch() } -- 2.40.1 From 64d9f3e36264ce71f68d76d83e1db671b822d322 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 08:33:52 +0900 Subject: [PATCH 34/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20=EB=B9=84?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=20=EB=90=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=8A=94=20=EC=A1=B0=ED=9A=8C=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../audition/applicant/AuditionApplicantRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt index 3c83256..d2f766c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt @@ -52,7 +52,7 @@ class AuditionApplicantQueryRepositoryImpl( .innerJoin(auditionApplicant.role, auditionRole) .where( auditionRole.id.eq(auditionRoleId), - auditionRole.isActive.isTrue + auditionApplicant.isActive.isTrue ) .fetch() .size @@ -86,7 +86,7 @@ class AuditionApplicantQueryRepositoryImpl( .leftJoin(auditionVote).on(auditionApplicant.id.eq(auditionVote.applicant.id)) .where( auditionRole.id.eq(auditionRoleId), - auditionRole.isActive.isTrue + auditionApplicant.isActive.isTrue ) .groupBy(auditionApplicant.id) .orderBy(orderBy) -- 2.40.1 From 1ddd40948e3b68ba7ff796f3c8ef624d8f04b1a5 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 13:00:02 +0900 Subject: [PATCH 35/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20-=20=ED=9A=9F=EC=88=98=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95=20-=20=EC=98=A4?= =?UTF-8?q?=EB=94=94=EC=85=98=20=EC=A7=80=EC=9B=90=EC=9E=90=EB=B3=84=20?= =?UTF-8?q?=ED=95=98=EB=A3=A8=2010=EA=B0=9C=20->=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=ED=9A=9F=EC=88=98=20=ED=95=98=EB=A3=A8=20?= =?UTF-8?q?10=EA=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/vote/AuditionVoteRepository.kt | 7 ++----- .../sodalive/audition/vote/AuditionVoteService.kt | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt index 2a5d61a..584ed3d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteRepository.kt @@ -9,9 +9,8 @@ import java.time.LocalDateTime interface AuditionVoteRepository : JpaRepository, AuditionVoteQueryRepository interface AuditionVoteQueryRepository { - fun countByMemberIdAndApplicantIdAndVoteDateRange( + fun countByMemberIdAndVoteDateRange( memberId: Long, - applicantId: Long, startDate: LocalDateTime, endDate: LocalDateTime ): Int @@ -20,9 +19,8 @@ interface AuditionVoteQueryRepository { class AuditionVoteQueryRepositoryImpl( private val queryFactory: JPAQueryFactory ) : AuditionVoteQueryRepository { - override fun countByMemberIdAndApplicantIdAndVoteDateRange( + override fun countByMemberIdAndVoteDateRange( memberId: Long, - applicantId: Long, startDate: LocalDateTime, endDate: LocalDateTime ): Int { @@ -31,7 +29,6 @@ class AuditionVoteQueryRepositoryImpl( .from(auditionVote) .where( auditionVote.member.id.eq(memberId) - .and(auditionVote.applicant.id.eq(applicantId)) .and(auditionVote.createdAt.between(startDate, endDate)) ) .fetch() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt index a897527..34261ea 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/vote/AuditionVoteService.kt @@ -36,15 +36,14 @@ class AuditionVoteService( val startDate = startOfDayClient.withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime() val endDate = endOfDayClient.withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime() - val voteCount = repository.countByMemberIdAndApplicantIdAndVoteDateRange( + val voteCount = repository.countByMemberIdAndVoteDateRange( memberId = member.id!!, - applicantId = applicantId, startDate = startDate, endDate = endDate ) if (voteCount > 10) { - throw SodaException("오늘 해당 지원자에게 할 수 있는 최대 투표수를 초과하였습니다.\n내일 다시 투표해 주세요.") + throw SodaException("오늘 응원은 여기까지!\n하루 최대 10회까지 응원이 가능합니다.\n내일 다시 이용해주세요.") } if (voteCount > 0) { -- 2.40.1 From 354fbf7e29a5bdf1e6df7f2a4253add79e47dbe4 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 3 Jan 2025 23:40:26 +0900 Subject: [PATCH 36/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20-=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=9E=90=20memberId=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/applicant/AuditionApplicantRepository.kt | 1 + .../audition/applicant/GetAuditionApplicantListResponse.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt index d2f766c..92af3f3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/AuditionApplicantRepository.kt @@ -74,6 +74,7 @@ class AuditionApplicantQueryRepositoryImpl( .select( QGetAuditionRoleApplicantItem( auditionApplicant.id, + member.id, member.nickname, member.profileImage.prepend("/").prepend(cloudFrontHost), auditionApplicant.voicePath.prepend("/").prepend(cloudFrontHost), diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt index d86ff49..188e17b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/applicant/GetAuditionApplicantListResponse.kt @@ -9,6 +9,7 @@ data class GetAuditionApplicantListResponse( data class GetAuditionRoleApplicantItem @QueryProjection constructor( val applicantId: Long, + val memberId: Long, val nickname: String, val profileImageUrl: String, val voiceUrl: String, -- 2.40.1 From eb36313c9b057b2eab77a2d591cbbe30b1a75be0 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 4 Jan 2025 00:43:10 +0900 Subject: [PATCH 37/40] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=94=84=EB=A1=9C=ED=95=84=20API=20-=20=EB=9E=AD?= =?UTF-8?q?=ED=82=B9,=20=EC=B6=94=EC=B2=9C=20=ED=81=AC=EB=A6=AC,=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C,=20=EC=BD=98=ED=85=90=EC=B8=A0,=20?= =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88,=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0,=20=ED=99=9C=EB=8F=99=EC=9A=94=EC=95=BD=EC=9D=84=20?= =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4=ED=84=B0=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/explorer/ExplorerService.kt | 112 ++++++++++++------ .../explorer/GetCreatorProfileResponse.kt | 3 +- 2 files changed, 75 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt index 5286adf..a7c3a0e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt @@ -144,12 +144,16 @@ class ExplorerService( val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = creatorId) if (isBlocked) throw SodaException("${creatorAccount.nickname}님의 요청으로 채널 접근이 제한됩니다.") + val isCreator = creatorAccount.role == MemberRole.CREATOR + val notificationUserIds = queryRepository.getNotificationUserIds(creatorId) val creatorFollowing = queryRepository.getCreatorFollowing(creatorId = creatorId, memberId = member.id!!) val notificationRecipientCount = notificationUserIds.size // 후원랭킹 - val memberDonationRanking = if (creatorId == member.id!! || creatorAccount.isVisibleDonationRank) { + val memberDonationRanking = if ( + isCreator && (creatorId == member.id!! || creatorAccount.isVisibleDonationRank) + ) { queryRepository.getMemberDonationRanking( creatorId, 10, @@ -160,37 +164,57 @@ class ExplorerService( } // 추천 크리에이터 - val similarCreatorList = queryRepository.getSimilarCreatorList(creatorId) + val similarCreatorList = if (isCreator) { + queryRepository.getSimilarCreatorList(creatorId) + } else { + listOf() + } // 라이브 - val liveRoomList = queryRepository.getLiveRoomList( - creatorId, - userMember = member, - timezone = timezone, - limit = 3 - ) + val liveRoomList = if (isCreator) { + queryRepository.getLiveRoomList( + creatorId, + userMember = member, + timezone = timezone, + limit = 3 + ) + } else { + listOf() + } // 오디오 콘텐츠 - val contentList = audioContentService.getAudioContentList( - creatorId = creatorId, - sortType = SortType.NEWEST, - member = member, - offset = 0, - limit = 3 - ).items + val contentList = if (isCreator) { + audioContentService.getAudioContentList( + creatorId = creatorId, + sortType = SortType.NEWEST, + member = member, + offset = 0, + limit = 3 + ).items + } else { + listOf() + } // 공지사항 - val notice = queryRepository.getNoticeString(creatorId) + val notice = if (isCreator) { + queryRepository.getNoticeString(creatorId) + } else { + "" + } // 커뮤니티 - val communityPostList = communityService.getCommunityPostList( - creatorId = creatorId, - memberId = member.id!!, - timezone = timezone, - offset = 0, - limit = 3, - isAdult = member.auth != null - ) + val communityPostList = if (isCreator) { + communityService.getCommunityPostList( + creatorId = creatorId, + memberId = member.id!!, + timezone = timezone, + offset = 0, + limit = 3, + isAdult = member.auth != null + ) + } else { + listOf() + } // 응원 val cheers = queryRepository.getCheersList(creatorId, timezone = timezone, offset = 0, limit = 4) @@ -198,15 +222,29 @@ class ExplorerService( // 차단한 크리에이터 인지 체크 val isBlock = memberService.isBlocked(blockedMemberId = creatorId, memberId = member.id!!) - // 활동요약 (라이브 횟수, 라이브 시간, 라이브 참여자, 콘텐츠 수) - val liveCount = queryRepository.getLiveCount(creatorId) ?: 0 - val liveTime = queryRepository.getLiveTime(creatorId) - val liveContributorCount = queryRepository.getLiveContributorCount(creatorId) ?: 0 - val contentCount = queryRepository.getContentCount(creatorId) ?: 0 + val activitySummary = if (isCreator) { + // 활동요약 (라이브 횟수, 라이브 시간, 라이브 참여자, 콘텐츠 수) + val liveCount = queryRepository.getLiveCount(creatorId) ?: 0 + val liveTime = queryRepository.getLiveTime(creatorId) + val liveContributorCount = queryRepository.getLiveContributorCount(creatorId) ?: 0 + val contentCount = queryRepository.getContentCount(creatorId) ?: 0 + GetCreatorActivitySummary( + liveCount = liveCount, + liveTime = liveTime, + liveContributorCount = liveContributorCount, + contentCount = contentCount + ) + } else { + GetCreatorActivitySummary(0, 0, 0, 0) + } - val seriesList = seriesService - .getSeriesList(creatorId = creatorId, member = member) - .items + val seriesList = if (isCreator) { + seriesService + .getSeriesList(creatorId = creatorId, member = member) + .items + } else { + listOf() + } return GetCreatorProfileResponse( creator = CreatorResponse( @@ -235,14 +273,10 @@ class ExplorerService( notice = notice, communityPostList = communityPostList, cheers = cheers, - activitySummary = GetCreatorActivitySummary( - liveCount = liveCount, - liveTime = liveTime, - liveContributorCount = liveContributorCount, - contentCount = contentCount - ), + activitySummary = activitySummary, seriesList = seriesList, - isBlock = isBlock + isBlock = isBlock, + isCreator = isCreator ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt index bfdd016..5ff9ed5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt @@ -15,7 +15,8 @@ data class GetCreatorProfileResponse( val cheers: GetCheersResponse, val activitySummary: GetCreatorActivitySummary, val seriesList: List, - val isBlock: Boolean + val isBlock: Boolean, + val isCreator: Boolean ) data class GetCreatorActivitySummary( -- 2.40.1 From 47dfaec2262b515e91745eac9e7bfb8fb429eeda Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 4 Jan 2025 01:07:37 +0900 Subject: [PATCH 38/40] =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=94=84=EB=A1=9C=ED=95=84=20API=20-=20isCreator?= =?UTF-8?q?=EB=A5=BC=20isCreatorRole=EC=9D=B4=EB=9D=BC=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-?= =?UTF-8?q?=20=EB=82=98=ED=83=80=EB=82=B4=EB=8A=94=20=EA=B0=92=EC=9D=80=20?= =?UTF-8?q?=EB=8F=99=EC=9D=BC=ED=95=98=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt | 2 +- .../co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt index a7c3a0e..1ce31f5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt @@ -276,7 +276,7 @@ class ExplorerService( activitySummary = activitySummary, seriesList = seriesList, isBlock = isBlock, - isCreator = isCreator + isCreatorRole = isCreator ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt index 5ff9ed5..5e80a77 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetCreatorProfileResponse.kt @@ -16,7 +16,7 @@ data class GetCreatorProfileResponse( val activitySummary: GetCreatorActivitySummary, val seriesList: List, val isBlock: Boolean, - val isCreator: Boolean + val isCreatorRole: Boolean ) data class GetCreatorActivitySummary( -- 2.40.1 From 824cd2f3ea4f7a43fbe3e161ee74770a88fa7dc2 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sun, 5 Jan 2025 15:27:03 +0900 Subject: [PATCH 39/40] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20-=20endDate?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/audition/AdminAuditionRepository.kt | 23 ------------------- .../admin/audition/AdminAuditionService.kt | 12 ---------- .../admin/audition/CreateAuditionRequest.kt | 15 ------------ .../admin/audition/GetAuditionListResponse.kt | 1 - .../admin/audition/UpdateAuditionRequest.kt | 1 - .../vividnext/sodalive/audition/Audition.kt | 2 -- 6 files changed, 54 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt index a457e59..dbdbe16 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionRepository.kt @@ -1,16 +1,11 @@ package kr.co.vividnext.sodalive.admin.audition -import com.querydsl.core.types.dsl.CaseBuilder -import com.querydsl.core.types.dsl.DateTimePath -import com.querydsl.core.types.dsl.Expressions -import com.querydsl.core.types.dsl.StringTemplate import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.audition.Audition import kr.co.vividnext.sodalive.audition.QAudition.audition import org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository -import java.time.LocalDateTime @Repository interface AdminAuditionRepository : JpaRepository, AdminAuditionQueryRepository @@ -33,10 +28,6 @@ class AdminAuditionQueryRepositoryImpl( QGetAuditionListItem( audition.id, audition.title, - CaseBuilder() - .`when`(audition.endDate.isNotNull) - .then(getFormattedDate(audition.endDate)) - .otherwise(""), audition.imagePath.prepend("/").prepend(coverImageHost), audition.isAdult, audition.information, @@ -75,18 +66,4 @@ class AdminAuditionQueryRepositoryImpl( .where(audition.id.eq(auditionId)) .fetchFirst() } - - private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { - return Expressions.stringTemplate( - "DATE_FORMAT({0}, {1})", - Expressions.dateTimeTemplate( - LocalDateTime::class.java, - "CONVERT_TZ({0},{1},{2})", - dateTimePath, - "UTC", - "Asia/Seoul" - ), - "%Y-%m-%d %H:%i:%s" - ) - } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt index 774b5b8..b0ad818 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/AdminAuditionService.kt @@ -11,9 +11,6 @@ import org.springframework.data.repository.findByIdOrNull 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 class AdminAuditionService( @@ -68,15 +65,6 @@ class AdminAuditionService( audition.status = request.status } - if (request.endDateString != null) { - val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - val endDate = LocalDateTime.parse(request.endDateString, dateTimeFormatter) - .atZone(ZoneId.of("Asia/Seoul")) - .withZoneSameInstant(ZoneId.of("UTC")) - .toLocalDateTime() - audition.endDate = endDate - } - if (request.originalWorkUrl != null) { audition.originalWorkUrl = request.originalWorkUrl } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt index 647fa34..ccf7a10 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/CreateAuditionRequest.kt @@ -2,15 +2,11 @@ package kr.co.vividnext.sodalive.admin.audition import kr.co.vividnext.sodalive.audition.Audition import kr.co.vividnext.sodalive.common.SodaException -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter data class CreateAuditionRequest( val title: String, val information: String, val isAdult: Boolean = false, - val endDateString: String? = null, val originalWorkUrl: String? = null ) { init { @@ -24,21 +20,10 @@ data class CreateAuditionRequest( } fun toAudition(): Audition { - val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - val endDate = if (endDateString != null) { - LocalDateTime.parse(endDateString, dateTimeFormatter) - .atZone(ZoneId.of("Asia/Seoul")) - .withZoneSameInstant(ZoneId.of("UTC")) - .toLocalDateTime() - } else { - null - } - return Audition( title = title, information = information, isAdult = isAdult, - endDate = endDate, originalWorkUrl = originalWorkUrl ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt index 4858790..8d63ef2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/GetAuditionListResponse.kt @@ -11,7 +11,6 @@ data class GetAuditionListResponse( data class GetAuditionListItem @QueryProjection constructor( val id: Long, val title: String, - val endDate: String, val imageUrl: String, val isAdult: Boolean, val information: String, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt index 540e446..a90be2d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/audition/UpdateAuditionRequest.kt @@ -7,7 +7,6 @@ data class UpdateAuditionRequest( val title: String? = null, val information: String? = null, val isAdult: Boolean? = null, - val endDateString: String? = null, val status: AuditionStatus? = null, val originalWorkUrl: String? = null, val isActive: Boolean? = null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt index 24e2727..ce29dd6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/Audition.kt @@ -1,7 +1,6 @@ package kr.co.vividnext.sodalive.audition import kr.co.vividnext.sodalive.common.BaseEntity -import java.time.LocalDateTime import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType @@ -13,7 +12,6 @@ data class Audition( @Column(columnDefinition = "TEXT") var information: String, var isAdult: Boolean = false, - var endDate: LocalDateTime? = null, // 원작 URL var originalWorkUrl: String? = null, @Enumerated(value = EnumType.STRING) -- 2.40.1 From c4d9d503ac604129fdee12c75923fadb2c263fec Mon Sep 17 00:00:00 2001 From: Klaus Date: Sun, 5 Jan 2025 15:35:26 +0900 Subject: [PATCH 40/40] =?UTF-8?q?=ED=83=90=EC=83=89=EC=97=90=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=EA=B3=BC=20=EB=8F=99=EC=9D=BC=ED=95=9C=20?= =?UTF-8?q?=EB=B3=84=EB=8F=84=EC=9D=98=20API=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/explorer/ExplorerController.kt | 9 ++++++ .../sodalive/explorer/ExplorerService.kt | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerController.kt index 8b66af3..feb7daa 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerController.kt @@ -21,6 +21,15 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/explorer") class ExplorerController(private val service: ExplorerService) { + @GetMapping("/creator-rank") + fun getCreatorRank( + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.getCreatorRank(memberId = member.id!!)) + } + @GetMapping fun getExplorer( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt index 1ce31f5..7c3c8ce 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt @@ -47,6 +47,34 @@ class ExplorerService( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { + fun getCreatorRank(memberId: Long): GetExplorerSectionResponse { + val creatorRankings = queryRepository + .getCreatorRankings() + .filter { !memberService.isBlocked(blockedMemberId = memberId, memberId = it.id!!) } + .map { it.toExplorerSectionCreator(cloudFrontHost) } + + val currentDateTime = LocalDateTime.now() + val lastMonday = currentDateTime + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val lastSunday = lastMonday + .plusDays(6) + + val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") + val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") + + val formattedLastMonday = lastMonday.format(startDateFormatter) + val formattedLastSunday = lastSunday.format(endDateFormatter) + + return GetExplorerSectionResponse( + title = "인기 크리에이터", + coloredTitle = "인기", + color = "FF5C49", + desc = "$formattedLastMonday ~ $formattedLastSunday", + creators = creatorRankings + ) + } + fun getExplorer(member: Member, growthRankingCreatorsLimit: Long = 20): GetExplorerResponse { val sections = mutableListOf() -- 2.40.1