관리자 메시지 다국어 처리

This commit is contained in:
2025-12-23 17:35:38 +09:00
parent 291f9a265b
commit 7ef654e89d
12 changed files with 305 additions and 60 deletions

View File

@@ -34,7 +34,9 @@ class AdminEventBannerService(
startDateString: String,
endDateString: String
): Long {
if (detail == null && link.isNullOrBlank()) throw SodaException("상세이미지 혹은 링크를 등록하세요")
if (detail == null && link.isNullOrBlank()) {
throw SodaException(messageKey = "admin.event.banner.detail_or_link_required")
}
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
val startDate = LocalDateTime.parse(startDateString, dateTimeFormatter)
@@ -102,7 +104,7 @@ class AdminEventBannerService(
event.detailImage = detailImagePath
event.popupImage = popupImagePath
return event.id ?: throw SodaException("이벤트 등록을 하지 못했습니다.")
return event.id ?: throw SodaException(messageKey = "admin.event.banner.create_failed")
}
@Transactional
@@ -118,10 +120,10 @@ class AdminEventBannerService(
startDateString: String? = null,
endDateString: String? = null
) {
if (id <= 0) throw SodaException("잘못된 요청입니다.")
if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request")
val event = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 요청입니다.")
?: throw SodaException(messageKey = "common.error.invalid_request")
if (thumbnail != null) {
val metadata = ObjectMetadata()
@@ -190,9 +192,9 @@ class AdminEventBannerService(
@Transactional
fun delete(id: Long) {
if (id <= 0) throw SodaException("잘못된 요청입니다.")
if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request")
val event = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 요청입니다.")
?: throw SodaException(messageKey = "common.error.invalid_request")
event.isActive = false
}

View File

@@ -38,7 +38,7 @@ class AdminChargeEventService(private val repository: AdminChargeEventRepository
@Transactional
fun modifyChargeEvent(request: ModifyChargeEventRequest) {
val chargeEvent = repository.findByIdOrNull(request.id)
?: throw SodaException("해당하는 충전이벤트가 없습니다\n다시 시도해 주세요.")
?: throw SodaException(messageKey = "admin.charge_event.not_found_retry")
if (request.title != null) {
chargeEvent.title = request.title

View File

@@ -17,10 +17,10 @@ class AdminExplorerService(
) {
@Transactional
fun createExplorerSection(request: CreateExplorerSectionRequest): Long {
if (request.title.isBlank()) throw SodaException("제목을 입력하세요.")
if (request.title.isBlank()) throw SodaException(messageKey = "admin.explorer.title_required")
val findExplorerSection = repository.findByTitle(request.title)
if (findExplorerSection != null) throw SodaException("동일한 제목이 있습니다.")
if (findExplorerSection != null) throw SodaException(messageKey = "admin.explorer.title_duplicate")
val explorerSection = ExplorerSection(title = request.title, isAdult = request.isAdult)
explorerSection.coloredTitle = request.coloredTitle
@@ -37,7 +37,7 @@ class AdminExplorerService(
}
}
if (tags.size <= 0) throw SodaException("관심사를 선택하세요.")
if (tags.size <= 0) throw SodaException(messageKey = "admin.explorer.tags_required")
explorerSection.tags = tags
return repository.save(explorerSection).id!!
@@ -53,14 +53,14 @@ class AdminExplorerService(
request.coloredTitle == null &&
request.isActive == null
) {
throw SodaException("변경사항이 없습니다.")
throw SodaException(messageKey = "admin.explorer.no_changes")
}
val explorerSection = repository.findByIdOrNull(request.id)
?: throw SodaException("해당하는 섹션이 없습니다.")
?: throw SodaException(messageKey = "admin.explorer.section_not_found")
if (request.title != null) {
if (request.title.isBlank()) throw SodaException("올바른 제목을 입력하세요.")
if (request.title.isBlank()) throw SodaException(messageKey = "admin.explorer.valid_title_required")
explorerSection.title = request.title
}
@@ -97,7 +97,7 @@ class AdminExplorerService(
}
}
if (tags.size <= 0) throw SodaException("관심사를 입력하세요.")
if (tags.size <= 0) throw SodaException(messageKey = "admin.explorer.tags_input_required")
if (tags != explorerSection.tags) {
explorerSection.tags.clear()
explorerSection.tags.addAll(tags)

View File

@@ -15,6 +15,8 @@ import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.fcm.FcmEvent
import kr.co.vividnext.sodalive.fcm.FcmEventType
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBanner
import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBannerRepository
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
@@ -49,6 +51,8 @@ class AdminLiveService(
private val canRepository: CanRepository,
private val applicationEventPublisher: ApplicationEventPublisher,
private val messageSource: SodaMessageSource,
private val langContext: LangContext,
@Value("\${cloud.aws.s3.bucket}")
private val bucket: String,
@@ -118,10 +122,10 @@ class AdminLiveService(
endDateString: String,
isAdult: Boolean
): Long {
if (creatorId < 1) throw SodaException("올바른 크리에이터를 선택해 주세요.")
if (creatorId < 1) throw SodaException(messageKey = "admin.live.creator_required")
val creator = memberRepository.findCreatorByIdOrNull(memberId = creatorId)
?: throw SodaException("올바른 크리에이터를 선택해 주세요.")
?: throw SodaException(messageKey = "admin.live.creator_required")
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
val startDate = LocalDateTime.parse(startDateString, dateTimeFormatter)
@@ -134,15 +138,15 @@ class AdminLiveService(
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime()
if (startDate < nowDate) throw SodaException("노출 시작일은 현재시간 이후로 설정하셔야 합니다.")
if (startDate < nowDate) throw SodaException(messageKey = "admin.live.start_after_now")
val endDate = LocalDateTime.parse(endDateString, dateTimeFormatter)
.atZone(ZoneId.of("Asia/Seoul"))
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime()
if (endDate < nowDate) throw SodaException("노출 종료일은 현재시간 이후로 설정하셔야 합니다.")
if (endDate <= startDate) throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
if (endDate < nowDate) throw SodaException(messageKey = "admin.live.end_after_now")
if (endDate <= startDate) throw SodaException(messageKey = "admin.live.start_before_end")
val recommendCreatorBanner = RecommendLiveCreatorBanner(
startDate = startDate,
@@ -176,13 +180,13 @@ class AdminLiveService(
isAdult: Boolean?
) {
val recommendCreatorBanner = recommendCreatorBannerRepository.findByIdOrNull(recommendCreatorBannerId)
?: throw SodaException("해당하는 추천라이브가 없습니다. 다시 확인해 주세요.")
?: throw SodaException(messageKey = "admin.live.recommend_not_found_retry")
if (creatorId != null) {
if (creatorId < 1) throw SodaException("올바른 크리에이터를 선택해 주세요.")
if (creatorId < 1) throw SodaException(messageKey = "admin.live.creator_required")
val creator = memberRepository.findCreatorByIdOrNull(memberId = creatorId)
?: throw SodaException("올바른 크리에이터를 선택해 주세요.")
?: throw SodaException(messageKey = "admin.live.creator_required")
recommendCreatorBanner.creator = creator
}
@@ -218,13 +222,13 @@ class AdminLiveService(
if (endDate != null) {
if (endDate <= startDate) {
throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
throw SodaException(messageKey = "admin.live.start_before_end")
}
recommendCreatorBanner.endDate = endDate
} else {
if (recommendCreatorBanner.endDate <= startDate) {
throw SodaException("노출 시작일은 노출 종료일 이전으로 설정하셔야 합니다.")
throw SodaException(messageKey = "admin.live.start_before_end")
}
}
@@ -237,7 +241,7 @@ class AdminLiveService(
.toLocalDateTime()
if (endDate <= recommendCreatorBanner.startDate) {
throw SodaException("노출 종료일은 노출 시작일 이후로 설정하셔야 합니다.")
throw SodaException(messageKey = "admin.live.end_after_start")
}
recommendCreatorBanner.endDate = endDate
@@ -266,7 +270,10 @@ class AdminLiveService(
for (room in findRoomList) {
room.isActive = false
val roomCancel = LiveRoomCancel("관리자에 의한 취소 - 노쇼")
val cancelReason = messageSource
.getMessage("admin.live.cancel_reason.no_show", langContext.lang)
.orEmpty()
val roomCancel = LiveRoomCancel(cancelReason)
roomCancel.room = room
roomCancelRepository.save(roomCancel)
@@ -286,7 +293,10 @@ class AdminLiveService(
it.status = UseCanCalculateStatus.REFUND
val charge = Charge(0, it.can, status = ChargeStatus.REFUND_CHARGE)
charge.title = "${it.can}"
val canTitleTemplate = messageSource
.getMessage("live.room.can_title", langContext.lang)
.orEmpty()
charge.title = String.format(canTitleTemplate, it.can)
charge.useCan = useCan
when (it.paymentGateway) {
@@ -300,7 +310,9 @@ class AdminLiveService(
status = PaymentStatus.COMPLETE,
paymentGateway = it.paymentGateway
)
payment.method = "환불"
payment.method = messageSource
.getMessage("live.room.refund_method", langContext.lang)
.orEmpty()
charge.payment = payment
chargeRepository.save(charge)
@@ -313,11 +325,15 @@ class AdminLiveService(
reservationRepository.cancelReservation(roomId = room.id!!)
// 라이브 취소 푸시 발송
val cancelMessageTemplate = messageSource
.getMessage("live.room.fcm.message.canceled", langContext.lang)
.orEmpty()
val cancelMessage = String.format(cancelMessageTemplate, room.title)
applicationEventPublisher.publishEvent(
FcmEvent(
type = FcmEventType.CANCEL_LIVE,
title = room.member!!.nickname,
message = "라이브 취소 : ${room.title}",
message = cancelMessage,
recipientsMap = pushTokenListMap
)
)

View File

@@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.admin.live.signature
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.signature.SignatureCanSortType
import org.springframework.data.domain.Pageable
import org.springframework.security.access.prepost.PreAuthorize
@@ -16,7 +18,11 @@ import org.springframework.web.multipart.MultipartFile
@RestController
@PreAuthorize("hasRole('ADMIN')")
@RequestMapping("/admin/live/signature-can")
class AdminSignatureCanController(private val service: AdminSignatureCanService) {
class AdminSignatureCanController(
private val service: AdminSignatureCanService,
private val messageSource: SodaMessageSource,
private val langContext: LangContext
) {
@GetMapping
fun getSignatureCanList(
pageable: Pageable,
@@ -32,7 +38,7 @@ class AdminSignatureCanController(private val service: AdminSignatureCanService)
@RequestParam("isAdult", required = false) isAdult: Boolean = false
) = ApiResponse.ok(
service.createSignatureCan(can = can, time = time, creatorId = creatorId, image = image, isAdult = isAdult),
"등록되었습니다."
messageSource.getMessage("admin.signature_can.created", langContext.lang)
)
@PutMapping
@@ -45,7 +51,7 @@ class AdminSignatureCanController(private val service: AdminSignatureCanService)
@RequestParam("isAdult", required = false) isAdult: Boolean?
) = run {
if (can == null && time == null && image == null && isActive == null && isAdult == null) {
throw SodaException("변경사항이 없습니다.")
throw SodaException(messageKey = "admin.signature_can.no_changes")
}
ApiResponse.ok(
@@ -57,7 +63,7 @@ class AdminSignatureCanController(private val service: AdminSignatureCanService)
isActive = isActive,
isAdult = isAdult
),
"수정되었습니다."
messageSource.getMessage("admin.signature_can.updated", langContext.lang)
)
}
}

View File

@@ -43,12 +43,12 @@ class AdminSignatureCanService(
@Transactional
fun createSignatureCan(can: Int, time: Int, creatorId: Long, image: MultipartFile, isAdult: Boolean) {
if (creatorId < 1) throw SodaException("올바른 크리에이터를 선택해 주세요.")
if (can <= 0) throw SodaException("1캔 이상 설정할 수 있습니다.")
if (time < 3 || time > 20) throw SodaException("시간은 3초 이상 20초 이하로 설정할 수 있습니다.")
if (creatorId < 1) throw SodaException(messageKey = "admin.signature_can.creator_required")
if (can <= 0) throw SodaException(messageKey = "admin.signature_can.min_can")
if (time < 3 || time > 20) throw SodaException(messageKey = "admin.signature_can.time_range")
val creator = memberRepository.findCreatorByIdOrNull(memberId = creatorId)
?: throw SodaException("올바른 크리에이터를 선택해 주세요.")
?: throw SodaException(messageKey = "admin.signature_can.creator_required")
val signatureCan = SignatureCan(can = can, isAdult = isAdult)
signatureCan.creator = creator
@@ -76,15 +76,15 @@ class AdminSignatureCanService(
isAdult: Boolean?
) {
val signatureCan = repository.findByIdOrNull(id = id)
?: throw SodaException("잘못된 요청입니다.")
?: throw SodaException(messageKey = "common.error.invalid_request")
if (can != null) {
if (can <= 0) throw SodaException("1캔 이상 설정할 수 있습니다.")
if (can <= 0) throw SodaException(messageKey = "admin.signature_can.min_can")
signatureCan.can = can
}
if (time != null) {
if (time < 3 || time > 20) throw SodaException("시간은 3초 이상 20초 이하로 설정할 수 있습니다.")
if (time < 3 || time > 20) throw SodaException(messageKey = "admin.signature_can.time_range")
signatureCan.time = time
}

View File

@@ -20,7 +20,7 @@ class AdminAdMediaPartnerService(private val repository: AdMediaPartnerRepositor
@Transactional
fun updateMediaPartner(request: UpdateAdMediaPartnerRequest) {
val entity = repository.findByIdOrNull(request.id)
?: throw SodaException("잘못된 접근입니다")
?: throw SodaException(messageKey = "admin.media_partner.invalid_access")
if (request.mediaGroup != null) {
entity.mediaGroup = request.mediaGroup

View File

@@ -1,6 +1,8 @@
package kr.co.vividnext.sodalive.admin.member
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberProvider
import kr.co.vividnext.sodalive.member.MemberRole
@@ -17,6 +19,8 @@ import java.time.format.DateTimeFormatter
class AdminMemberService(
private val repository: AdminMemberRepository,
private val passwordEncoder: PasswordEncoder,
private val messageSource: SodaMessageSource,
private val langContext: LangContext,
@Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String
@@ -24,7 +28,7 @@ class AdminMemberService(
@Transactional
fun updateMember(request: UpdateMemberRequest) {
val member = repository.findByIdOrNull(request.id)
?: throw SodaException("해당 유저가 없습니다.")
?: throw SodaException(messageKey = "admin.member.not_found")
if (member.role != request.userType) {
member.role = request.userType
@@ -44,7 +48,7 @@ class AdminMemberService(
}
fun searchMember(searchWord: String, pageable: Pageable): GetAdminMemberListResponse {
if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.")
if (searchWord.length < 2) throw SodaException(messageKey = "admin.member.search_word_min_length")
val totalCount = repository.searchMemberTotalCount(searchWord = searchWord)
val memberList = processMemberListToGetAdminMemberListResponseItemList(
memberList = repository.searchMember(
@@ -71,7 +75,7 @@ class AdminMemberService(
}
fun searchCreator(searchWord: String, pageable: Pageable): GetAdminMemberListResponse {
if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.")
if (searchWord.length < 2) throw SodaException(messageKey = "admin.member.search_word_min_length")
val totalCount = repository.searchMemberTotalCount(searchWord = searchWord, role = MemberRole.CREATOR)
val creatorList = processMemberListToGetAdminMemberListResponseItemList(
memberList = repository.searchMember(
@@ -92,18 +96,18 @@ class AdminMemberService(
.asSequence()
.map {
val userType = when (it.role) {
MemberRole.ADMIN -> "관리자"
MemberRole.USER -> "일반회원"
MemberRole.CREATOR -> "크리에이터"
MemberRole.AGENT -> "에이전트"
MemberRole.BOT -> ""
MemberRole.ADMIN -> messageSource.getMessage("admin.member.role.admin", langContext.lang).orEmpty()
MemberRole.USER -> messageSource.getMessage("admin.member.role.user", langContext.lang).orEmpty()
MemberRole.CREATOR -> messageSource.getMessage("admin.member.role.creator", langContext.lang).orEmpty()
MemberRole.AGENT -> messageSource.getMessage("admin.member.role.agent", langContext.lang).orEmpty()
MemberRole.BOT -> messageSource.getMessage("admin.member.role.bot", langContext.lang).orEmpty()
}
val loginType = when (it.provider) {
MemberProvider.EMAIL -> "이메일"
MemberProvider.KAKAO -> "카카오"
MemberProvider.GOOGLE -> "구글"
MemberProvider.APPLE -> "애플"
MemberProvider.EMAIL -> messageSource.getMessage("member.provider.email", langContext.lang).orEmpty()
MemberProvider.KAKAO -> messageSource.getMessage("member.provider.kakao", langContext.lang).orEmpty()
MemberProvider.GOOGLE -> messageSource.getMessage("member.provider.google", langContext.lang).orEmpty()
MemberProvider.APPLE -> messageSource.getMessage("member.provider.apple", langContext.lang).orEmpty()
}
val signUpDate = it.createdAt!!
@@ -146,7 +150,7 @@ class AdminMemberService(
}
fun searchMemberByNickname(searchWord: String, size: Int = 20): List<AdminSimpleMemberResponse> {
if (searchWord.length < 2) throw SodaException("2글자 이상 입력하세요.")
if (searchWord.length < 2) throw SodaException(messageKey = "admin.member.search_word_min_length")
val limit = if (size <= 0) 20 else size
return repository.searchMemberByNickname(searchWord = searchWord, limit = limit.toLong())
}
@@ -154,7 +158,7 @@ class AdminMemberService(
@Transactional
fun resetPassword(request: ResetPasswordRequest) {
val member = repository.findByIdAndActive(memberId = request.memberId)
?: throw SodaException("잘못된 회원정보입니다.\n다시 시도해 주세요.")
?: throw SodaException(messageKey = "admin.member.reset_password_invalid")
member.password = passwordEncoder.encode(member.email.split("@")[0])
}

View File

@@ -35,7 +35,9 @@ class AdminMemberTagService(
}
private fun tagExistCheck(request: CreateMemberTagRequest) {
repository.findByTag(request.tag)?.let { throw SodaException("이미 등록된 태그 입니다.") }
repository.findByTag(request.tag)?.let {
throw SodaException(messageKey = "admin.member.tag.already_registered")
}
}
private fun createTag(tag: String, imagePath: String, isAdult: Boolean) {
@@ -51,7 +53,7 @@ class AdminMemberTagService(
@Transactional
fun deleteTag(id: Long) {
val creatorTag = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 요청입니다.")
?: throw SodaException(messageKey = "common.error.invalid_request")
creatorTag.tag = "${creatorTag.tag}_deleted"
creatorTag.isActive = false
@@ -60,7 +62,7 @@ class AdminMemberTagService(
@Transactional
fun modifyTag(id: Long, image: MultipartFile?, requestString: String) {
val creatorTag = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 요청입니다.")
?: throw SodaException(messageKey = "common.error.invalid_request")
val request = objectMapper.readValue(requestString, CreateMemberTagRequest::class.java)
creatorTag.tag = request.tag

View File

@@ -26,7 +26,7 @@ class PointPolicyService(private val repository: PointPolicyRepository) {
@Transactional
fun update(id: Long, request: ModifyPointRewardPolicyRequest) {
val pointPolicy = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 접근입니다.")
?: throw SodaException(messageKey = "admin.point.policy.invalid_access")
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")

View File

@@ -32,7 +32,7 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics
)
if (dateRange == null) {
throw SodaException("잘못된 접근입니다.")
throw SodaException(messageKey = "admin.member.statistics.invalid_access")
}
var startDateTime = startDate.atStartOfDay()