From 93e0411337ffdfa64cdec42090d81055db9c5a17 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 22 Dec 2025 22:51:19 +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=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=8B=A4?= =?UTF-8?q?=EA=B5=AD=EC=96=B4=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/content/AdminContentService.kt | 6 +- .../banner/AdminContentBannerService.kt | 24 ++--- .../curation/AdminContentCurationService.kt | 8 +- .../tag/AdminHashTagCurationService.kt | 6 +- .../series/AdminContentSeriesService.kt | 6 +- .../AdminContentSeriesBannerController.kt | 12 ++- .../genre/AdminContentSeriesGenreService.kt | 4 +- .../recommend/AdminRecommendSeriesService.kt | 4 +- .../content/theme/AdminContentThemeService.kt | 6 +- .../sodalive/i18n/SodaMessageSource.kt | 95 ++++++++++++++++++- 10 files changed, 137 insertions(+), 34 deletions(-) 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 9733e469..344f9b81 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 @@ -51,7 +51,9 @@ class AdminContentService( searchWord: String, pageable: Pageable ): GetAdminContentListResponse { - if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.") + if (searchWord.length < 2) { + throw SodaException(messageKey = "admin.content.search_word_min_length") + } val totalCount = repository.getAudioContentTotalCount(searchWord, status = status) val audioContentAndThemeList = repository.getAudioContentList( status = status, @@ -82,7 +84,7 @@ class AdminContentService( @Transactional fun updateAudioContent(request: UpdateAdminContentRequest) { val audioContent = repository.findByIdOrNull(id = request.id) - ?: throw SodaException("없는 콘텐츠 입니다.") + ?: throw SodaException(messageKey = "admin.content.not_found") if (request.isDefaultCoverImage) { audioContent.coverImage = "`profile/default_profile.png`" diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt index 3811632e..a6ecbf47 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt @@ -33,19 +33,19 @@ class AdminContentBannerService( fun createAudioContentMainBanner(image: MultipartFile, requestString: String) { val request = objectMapper.readValue(requestString, CreateContentBannerRequest::class.java) if (request.type == AudioContentBannerType.CREATOR && request.creatorId == null) { - throw SodaException("크리에이터를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.creator_required") } if (request.type == AudioContentBannerType.SERIES && request.seriesId == null) { - throw SodaException("시리즈를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.series_required") } if (request.type == AudioContentBannerType.LINK && request.link == null) { - throw SodaException("링크 url을 입력하세요.") + throw SodaException(messageKey = "admin.content.banner.link_required") } if (request.type == AudioContentBannerType.EVENT && request.eventId == null) { - throw SodaException("이벤트를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.event_required") } val event = if (request.eventId != null && request.eventId > 0) { @@ -94,7 +94,7 @@ class AdminContentBannerService( fun updateAudioContentMainBanner(image: MultipartFile?, requestString: String) { val request = objectMapper.readValue(requestString, UpdateContentBannerRequest::class.java) val audioContentBanner = repository.findByIdOrNull(request.id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (image != null) { val fileName = generateFileName() @@ -124,22 +124,22 @@ class AdminContentBannerService( AudioContentBannerType.EVENT -> { if (request.eventId != null) { val event = eventRepository.findByIdOrNull(request.eventId) - ?: throw SodaException("이벤트를 선택하세요.") + ?: throw SodaException(messageKey = "admin.content.banner.event_required") audioContentBanner.event = event } else { - throw SodaException("이벤트를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.event_required") } } AudioContentBannerType.CREATOR -> { if (request.creatorId != null) { val creator = memberRepository.findByIdOrNull(request.creatorId) - ?: throw SodaException("크리에이터를 선택하세요.") + ?: throw SodaException(messageKey = "admin.content.banner.creator_required") audioContentBanner.creator = creator } else { - throw SodaException("크리에이터를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.creator_required") } } @@ -147,18 +147,18 @@ class AdminContentBannerService( if (request.link != null) { audioContentBanner.link = request.link } else { - throw SodaException("링크 url을 입력하세요.") + throw SodaException(messageKey = "admin.content.banner.link_required") } } AudioContentBannerType.SERIES -> { if (request.seriesId != null) { val series = seriesRepository.findByIdOrNull(request.seriesId) - ?: throw SodaException("시리즈를 선택하세요.") + ?: throw SodaException(messageKey = "admin.content.banner.series_required") audioContentBanner.series = series } else { - throw SodaException("시리즈를 선택하세요.") + throw SodaException(messageKey = "admin.content.banner.series_required") } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt index a9745f53..d1f5b030 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/AdminContentCurationService.kt @@ -21,7 +21,7 @@ class AdminContentCurationService( @Transactional fun createContentCuration(request: CreateContentCurationRequest) { val tab = contentMainTabRepository.findByIdOrNull(request.tabId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") val curation = AudioContentCuration( title = request.title, @@ -37,7 +37,7 @@ class AdminContentCurationService( @Transactional fun updateContentCuration(request: UpdateContentCurationRequest) { val audioContentCuration = repository.findByIdOrNull(id = request.id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (request.title != null) { audioContentCuration.title = request.title @@ -85,7 +85,7 @@ class AdminContentCurationService( fun getCurationItem(curationId: Long): List { val curation = repository.findByIdOrNull(curationId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") return if (curation.isSeries) { contentCurationItemRepository.getAudioContentCurationSeriesItemList(curationId) @@ -106,7 +106,7 @@ class AdminContentCurationService( fun addItemToCuration(request: AddItemToCurationRequest) { // 큐레이션 조회 val audioContentCuration = repository.findByIdOrNull(id = request.curationId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (audioContentCuration.isSeries) { request.itemIdList.forEach { seriesId -> diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/tag/AdminHashTagCurationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/tag/AdminHashTagCurationService.kt index dc450067..9f1be258 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/tag/AdminHashTagCurationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/curation/tag/AdminHashTagCurationService.kt @@ -28,7 +28,7 @@ class AdminHashTagCurationService( val isExists = repository.isExistsTag(tag = tag) if (isExists) { - throw SodaException("이미 등록된 태그 입니다.") + throw SodaException(messageKey = "admin.content.hash_tag.already_registered") } repository.save( @@ -42,7 +42,7 @@ class AdminHashTagCurationService( @Transactional fun updateContentHashTagCuration(request: UpdateContentHashTagCurationRequest) { val hashTagCuration = repository.findByIdOrNull(id = request.id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (request.tag != null) { var tag = request.tag.trim() @@ -88,7 +88,7 @@ class AdminHashTagCurationService( @Transactional fun addItemToHashTagCuration(request: AddItemToCurationRequest) { val curation = repository.findByIdOrNull(id = request.curationId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") request.itemIdList.forEach { contentId -> val audioContent = audioContentRepository.findByIdAndActive(contentId) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt index 31505cfd..18e61d86 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/AdminContentSeriesService.kt @@ -43,12 +43,12 @@ class AdminContentSeriesService( @Transactional fun modifySeries(request: AdminModifySeriesRequest) { val series = repository.findByIdAndActiveTrue(request.seriesId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (request.publishedDaysOfWeek != null) { val days = request.publishedDaysOfWeek if (days.contains(SeriesPublishedDaysOfWeek.RANDOM) && days.size > 1) { - throw SodaException("랜덤과 연재요일 동시에 선택할 수 없습니다.") + throw SodaException(messageKey = "admin.content.series.random_days_conflict") } series.publishedDaysOfWeek.clear() series.publishedDaysOfWeek.addAll(days) @@ -56,7 +56,7 @@ class AdminContentSeriesService( if (request.genreId != null) { val genre = genreRepository.findActiveSeriesGenreById(request.genreId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") series.genre = genre } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerController.kt index 5763fe61..6f8c3d4b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerController.kt @@ -11,6 +11,8 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.series.main.banner.ContentSeriesBannerService +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.utils.generateFileName import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.PageRequest @@ -33,6 +35,8 @@ import org.springframework.web.multipart.MultipartFile class AdminContentSeriesBannerController( private val bannerService: ContentSeriesBannerService, private val s3Uploader: S3Uploader, + private val langContext: LangContext, + private val messageSource: SodaMessageSource, @Value("\${cloud.aws.s3.bucket}") private val s3Bucket: String, @@ -113,7 +117,8 @@ class AdminContentSeriesBannerController( @DeleteMapping("/{bannerId}") fun deleteBanner(@PathVariable bannerId: Long) = run { bannerService.deleteBanner(bannerId) - ApiResponse.ok("배너가 성공적으로 삭제되었습니다.") + val message = messageSource.getMessage("admin.content.series.banner.delete_success", langContext.lang) + ApiResponse.ok(message) } /** @@ -124,7 +129,8 @@ class AdminContentSeriesBannerController( @RequestBody request: UpdateBannerOrdersRequest ) = run { bannerService.updateBannerOrders(request.ids) - ApiResponse.ok(null, "배너 정렬 순서가 성공적으로 변경되었습니다.") + val message = messageSource.getMessage("admin.content.series.banner.reorder_success", langContext.lang) + ApiResponse.ok(null, message) } private fun saveImage(bannerId: Long, image: MultipartFile): String { @@ -139,7 +145,7 @@ class AdminContentSeriesBannerController( metadata = metadata ) } catch (e: Exception) { - throw SodaException("이미지 저장에 실패했습니다: ${e.message}") + throw SodaException(messageKey = "admin.content.series.banner.image_save_failed") } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt index 68eb2848..05735dea 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/genre/AdminContentSeriesGenreService.kt @@ -17,11 +17,11 @@ class AdminContentSeriesGenreService(private val repository: AdminContentSeriesG @Transactional fun modifySeriesGenre(request: ModifySeriesGenreRequest) { if (request.genre == null && request.isAdult == null && request.isActive == null) { - throw SodaException("변경사항이 없습니다.") + throw SodaException(messageKey = "admin.content.series.genre.no_changes") } val seriesGenre = repository.findByIdOrNull(id = request.id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (request.genre != null) { seriesGenre.genre = request.genre diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt index cf697e4b..fbc37ac1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/series/recommend/AdminRecommendSeriesService.kt @@ -30,7 +30,7 @@ class AdminRecommendSeriesService( fun createRecommendSeries(image: MultipartFile, requestString: String) { val request = objectMapper.readValue(requestString, CreateRecommendSeriesRequest::class.java) val series = seriesRepository.findByIdOrNull(request.seriesId) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") val recommendSeries = RecommendSeries(isFree = request.isFree) recommendSeries.series = series @@ -49,7 +49,7 @@ class AdminRecommendSeriesService( fun updateRecommendSeries(image: MultipartFile?, requestString: String) { val request = objectMapper.readValue(requestString, UpdateRecommendSeriesRequest::class.java) val recommendSeries = repository.findByIdOrNull(request.id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") if (image != null) { val fileName = generateFileName() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt index 896f636b..2c4b0aef 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt @@ -53,13 +53,15 @@ class AdminContentThemeService( } fun themeExistCheck(request: CreateContentThemeRequest) { - repository.findIdByTheme(request.theme)?.let { throw SodaException("이미 등록된 테마 입니다.") } + repository.findIdByTheme(request.theme)?.let { + throw SodaException(messageKey = "admin.content.theme.already_registered") + } } @Transactional fun deleteTheme(id: Long) { val theme = repository.findByIdOrNull(id) - ?: throw SodaException("잘못된 요청입니다.") + ?: throw SodaException(messageKey = "common.error.invalid_request") theme.theme = "${theme.theme}_deleted" theme.isActive = false diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt index 8d2ad780..dc9456d0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/SodaMessageSource.kt @@ -303,6 +303,92 @@ class SodaMessageSource { ) ) + private val adminContentMessages = mapOf( + "admin.content.search_word_min_length" to mapOf( + Lang.KO to "2글자 이상 입력하세요.", + Lang.EN to "Please enter at least 2 characters.", + Lang.JA to "2文字以上入力してください。" + ), + "admin.content.not_found" to mapOf( + Lang.KO to "없는 콘텐츠 입니다.", + Lang.EN to "Content not found.", + Lang.JA to "該当のコンテンツが見つかりません。" + ) + ) + + private val adminContentBannerMessages = mapOf( + "admin.content.banner.creator_required" to mapOf( + Lang.KO to "크리에이터를 선택하세요.", + Lang.EN to "Please select a creator.", + Lang.JA to "クリエイターを選択してください。" + ), + "admin.content.banner.series_required" to mapOf( + Lang.KO to "시리즈를 선택하세요.", + Lang.EN to "Please select a series.", + Lang.JA to "シリーズを選択してください。" + ), + "admin.content.banner.link_required" to mapOf( + Lang.KO to "링크 url을 입력하세요.", + Lang.EN to "Please enter a link URL.", + Lang.JA to "リンクURLを入力してください。" + ), + "admin.content.banner.event_required" to mapOf( + Lang.KO to "이벤트를 선택하세요.", + Lang.EN to "Please select an event.", + Lang.JA to "イベントを選択してください。" + ) + ) + + private val adminHashTagCurationMessages = mapOf( + "admin.content.hash_tag.already_registered" to mapOf( + Lang.KO to "이미 등록된 태그 입니다.", + Lang.EN to "Tag already registered.", + Lang.JA to "既に登録されたタグです。" + ) + ) + + private val adminContentSeriesMessages = mapOf( + "admin.content.series.random_days_conflict" to mapOf( + Lang.KO to "랜덤과 연재요일 동시에 선택할 수 없습니다.", + Lang.EN to "Random and published days cannot be selected together.", + Lang.JA to "ランダムと連載曜日を同時に選択できません。" + ) + ) + + private val adminContentSeriesBannerMessages = mapOf( + "admin.content.series.banner.delete_success" to mapOf( + Lang.KO to "배너가 성공적으로 삭제되었습니다.", + Lang.EN to "Banner deleted successfully.", + Lang.JA to "バナーが削除されました。" + ), + "admin.content.series.banner.reorder_success" to mapOf( + Lang.KO to "배너 정렬 순서가 성공적으로 변경되었습니다.", + Lang.EN to "Banner order updated successfully.", + Lang.JA to "バナーの並び順が変更されました。" + ), + "admin.content.series.banner.image_save_failed" to mapOf( + Lang.KO to "이미지 저장에 실패했습니다.", + Lang.EN to "Failed to save image.", + Lang.JA to "画像の保存に失敗しました。" + ) + ) + + private val adminContentSeriesGenreMessages = mapOf( + "admin.content.series.genre.no_changes" to mapOf( + Lang.KO to "변경사항이 없습니다.", + Lang.EN to "No changes to update.", + Lang.JA to "変更事項がありません。" + ) + ) + + private val adminContentThemeMessages = mapOf( + "admin.content.theme.already_registered" to mapOf( + Lang.KO to "이미 등록된 테마 입니다.", + Lang.EN to "Theme already registered.", + Lang.JA to "既に登録されたテーマです。" + ) + ) + fun getMessage(key: String, lang: Lang): String? { val messageGroups = listOf( commonMessages, @@ -317,7 +403,14 @@ class SodaMessageSource { adminChatCharacterMessages, adminChatCurationMessages, adminChatCharacterImageMessages, - adminChatOriginalWorkMessages + adminChatOriginalWorkMessages, + adminContentMessages, + adminContentBannerMessages, + adminHashTagCurationMessages, + adminContentSeriesMessages, + adminContentSeriesBannerMessages, + adminContentSeriesGenreMessages, + adminContentThemeMessages ) for (messages in messageGroups) { val translations = messages[key] ?: continue