From 9eff82824922e049223fdad6144b979d16aaecbd Mon Sep 17 00:00:00 2001
From: Klaus <klaus@vividnext.co.kr>
Date: Mon, 2 Jun 2025 20:10:13 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?=
 =?UTF-8?q?=ED=84=B0=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=BD=98=ED=85=90?=
 =?UTF-8?q?=EC=B8=A0=20=EC=88=98=EC=A0=95=20-=20=ED=83=9C=EA=B7=B8=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95=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

---
 .../content/CreatorAdminContentRepository.kt  | 17 +++++++++
 .../content/CreatorAdminContentService.kt     | 37 +++++++++++++++++++
 .../UpdateCreatorAdminContentRequest.kt       |  1 +
 3 files changed, 55 insertions(+)

diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentRepository.kt
index 519a969..72d5cff 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentRepository.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentRepository.kt
@@ -6,6 +6,7 @@ import com.querydsl.core.types.dsl.StringTemplate
 import com.querydsl.jpa.impl.JPAQueryFactory
 import kr.co.vividnext.sodalive.content.AudioContent
 import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
+import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
 import kr.co.vividnext.sodalive.content.hashtag.QAudioContentHashTag.audioContentHashTag
 import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag
 import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
@@ -27,6 +28,8 @@ interface CreatorAdminAudioContentQueryRepository {
     fun getHashTagList(audioContentId: Long): List<String>
 
     fun getAudioContent(memberId: Long, audioContentId: Long): AudioContent?
+
+    fun findContentHashTagByContentIdAndIsActive(contentId: Long, isActive: Boolean): List<AudioContentHashTag>
 }
 
 class CreatorAdminAudioContentQueryRepositoryImpl(
@@ -127,6 +130,20 @@ class CreatorAdminAudioContentQueryRepositoryImpl(
             .fetchFirst()
     }
 
+    override fun findContentHashTagByContentIdAndIsActive(
+        contentId: Long,
+        isActive: Boolean
+    ): List<AudioContentHashTag> {
+        return queryFactory
+            .selectFrom(audioContentHashTag)
+            .innerJoin(audioContentHashTag.audioContent, audioContent)
+            .where(
+                audioContentHashTag.isActive.eq(isActive)
+                    .and(audioContent.id.eq(contentId))
+            )
+            .fetch()
+    }
+
     private fun formattedDateExpression(
         dateTime: DateTimePath<LocalDateTime>,
         format: String = "%Y-%m-%d"
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentService.kt
index c5091c8..c12c912 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentService.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/CreatorAdminContentService.kt
@@ -7,6 +7,9 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader
 import kr.co.vividnext.sodalive.common.SodaException
 import kr.co.vividnext.sodalive.content.ContentPriceChangeLog
 import kr.co.vividnext.sodalive.content.ContentPriceChangeLogRepository
+import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
+import kr.co.vividnext.sodalive.content.hashtag.HashTag
+import kr.co.vividnext.sodalive.content.hashtag.HashTagRepository
 import kr.co.vividnext.sodalive.member.Member
 import kr.co.vividnext.sodalive.utils.generateFileName
 import org.springframework.beans.factory.annotation.Value
@@ -18,6 +21,7 @@ import org.springframework.web.multipart.MultipartFile
 @Service
 class CreatorAdminContentService(
     private val repository: CreatorAdminContentRepository,
+    private val hashTagRepository: HashTagRepository,
     private val contentPriceChangeLogRepository: ContentPriceChangeLogRepository,
     private val audioContentCloudFront: AudioContentCloudFront,
     private val objectMapper: ObjectMapper,
@@ -156,5 +160,38 @@ class CreatorAdminContentService(
 
             audioContent.price = request.price
         }
+
+        if (!request.tags.isNullOrBlank()) {
+            val normalizedTags = request.tags
+                .replace("#", " #")
+                .split(" ")
+                .map { if (it.startsWith("#")) it else "#$it" }
+                .map { it.trim() }
+                .filter { it.isNotBlank() }
+                .distinct()
+
+            val currentContentTags = repository.findContentHashTagByContentIdAndIsActive(request.id, true)
+            val currentTagMap = currentContentTags.associateBy { it.hashTag!!.tag }
+
+            val tagsToAdd = normalizedTags.subtract(currentTagMap.keys)
+            val tagsToDeactivate = currentTagMap.keys.subtract(normalizedTags.toSet())
+
+            tagsToDeactivate.forEach {
+                currentTagMap[it]?.let { cht -> cht.isActive = false }
+            }
+
+            val newAudioContentHashTagList = tagsToAdd.map { tag ->
+                val hashTag = hashTagRepository.findByTag(tag)
+                    ?: hashTagRepository.save(HashTag(tag))
+
+                val audioContentHashTag = AudioContentHashTag()
+                audioContentHashTag.audioContent = audioContent
+                audioContentHashTag.hashTag = hashTag
+
+                audioContentHashTag
+            }
+
+            audioContent.audioContentHashTags.addAll(newAudioContentHashTagList)
+        }
     }
 }
diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/UpdateCreatorAdminContentRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/UpdateCreatorAdminContentRequest.kt
index 8eeda93..2df6a3a 100644
--- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/UpdateCreatorAdminContentRequest.kt
+++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/content/UpdateCreatorAdminContentRequest.kt
@@ -4,6 +4,7 @@ data class UpdateCreatorAdminContentRequest(
     val id: Long,
     val title: String?,
     val detail: String?,
+    val tags: String?,
     val price: Int?,
     val isAdult: Boolean?,
     val isActive: Boolean?,