From 3e41e763e34150dbd6f6abdae60e049da933f0eb Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 5 Feb 2026 17:16:54 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=BD=98?= =?UTF-8?q?=ED=85=90=EC=B8=A0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/content/AdminContentController.kt | 11 ++++--- .../admin/content/AdminContentService.kt | 31 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt index 65ebb64c..7e303c80 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentController.kt @@ -2,13 +2,15 @@ package kr.co.vividnext.sodalive.admin.content import kr.co.vividnext.sodalive.common.ApiResponse import org.springframework.data.domain.Pageable +import org.springframework.http.MediaType import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PutMapping -import org.springframework.web.bind.annotation.RequestBody 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 @PreAuthorize("hasRole('ADMIN')") @@ -38,10 +40,11 @@ class AdminContentController(private val service: AdminContentService) { ) ) - @PutMapping + @PutMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) fun modifyAudioContent( - @RequestBody request: UpdateAdminContentRequest - ) = ApiResponse.ok(service.updateAudioContent(request)) + @RequestPart("request") requestString: String, + @RequestPart("coverImage", required = false) coverImage: MultipartFile? = null + ) = ApiResponse.ok(service.updateAudioContent(coverImage, requestString)) @GetMapping("/main/tab") fun getContentMainTabList() = ApiResponse.ok(service.getContentMainTabList()) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt index 344f9b81..14f591b5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/AdminContentService.kt @@ -1,15 +1,21 @@ package kr.co.vividnext.sodalive.admin.content +import com.amazonaws.services.s3.model.ObjectMetadata +import com.fasterxml.jackson.databind.ObjectMapper import kr.co.vividnext.sodalive.admin.content.curation.AdminContentCurationRepository import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository import kr.co.vividnext.sodalive.admin.content.theme.AdminContentThemeRepository import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront +import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.main.tab.GetContentMainTabItem +import kr.co.vividnext.sodalive.utils.generateFileName +import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.Pageable 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 AdminContentService( @@ -17,7 +23,11 @@ class AdminContentService( private val themeRepository: AdminContentThemeRepository, private val audioContentCloudFront: AudioContentCloudFront, private val curationRepository: AdminContentCurationRepository, - private val contentMainTabRepository: AdminContentMainTabRepository + private val contentMainTabRepository: AdminContentMainTabRepository, + private val objectMapper: ObjectMapper, + private val s3Uploader: S3Uploader, + @Value("\${cloud.aws.s3.bucket}") + private val bucket: String ) { fun getAudioContentList(status: ContentReleaseStatus, pageable: Pageable): GetAdminContentListResponse { val totalCount = repository.getAudioContentTotalCount(status = status) @@ -82,12 +92,25 @@ class AdminContentService( } @Transactional - fun updateAudioContent(request: UpdateAdminContentRequest) { + fun updateAudioContent(coverImage: MultipartFile?, requestString: String) { + val request = objectMapper.readValue(requestString, UpdateAdminContentRequest::class.java) val audioContent = repository.findByIdOrNull(id = request.id) ?: throw SodaException(messageKey = "admin.content.not_found") - if (request.isDefaultCoverImage) { - audioContent.coverImage = "`profile/default_profile.png`" + if (coverImage != null) { + val metadata = ObjectMetadata() + metadata.contentLength = coverImage.size + + val fileName = generateFileName() + val imagePath = s3Uploader.upload( + inputStream = coverImage.inputStream, + bucket = bucket, + filePath = "audio_content_cover/${request.id}/$fileName", + metadata = metadata + ) + audioContent.coverImage = imagePath + } else if (request.isDefaultCoverImage) { + audioContent.coverImage = "profile/default_profile.png" } if (request.isActive != null) {