탐색 커뮤니티 다국어 메시지 분리

This commit is contained in:
2025-12-23 16:44:27 +09:00
parent f429ffbbbe
commit 4087d11420
6 changed files with 290 additions and 68 deletions

View File

@@ -5,6 +5,8 @@ import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest import kr.co.vividnext.sodalive.explorer.profile.PostCreatorNoticeRequest
import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.PostWriteCheersRequest
import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
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.Member
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.security.access.prepost.PreAuthorize import org.springframework.security.access.prepost.PreAuthorize
@@ -20,12 +22,16 @@ import org.springframework.web.bind.annotation.RestController
@RestController @RestController
@RequestMapping("/explorer") @RequestMapping("/explorer")
class ExplorerController(private val service: ExplorerService) { class ExplorerController(
private val service: ExplorerService,
private val messageSource: SodaMessageSource,
private val langContext: LangContext
) {
@GetMapping("/creator-rank") @GetMapping("/creator-rank")
fun getCreatorRank( fun getCreatorRank(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getCreatorRank(memberId = member.id!!)) ApiResponse.ok(service.getCreatorRank(memberId = member.id!!))
} }
@@ -34,7 +40,7 @@ class ExplorerController(private val service: ExplorerService) {
fun getExplorer( fun getExplorer(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getExplorer(member)) ApiResponse.ok(service.getExplorer(member))
} }
@@ -44,7 +50,7 @@ class ExplorerController(private val service: ExplorerService) {
@RequestParam channel: String, @RequestParam channel: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getSearchChannel(channel, member)) ApiResponse.ok(service.getSearchChannel(channel, member))
} }
@@ -55,7 +61,7 @@ class ExplorerController(private val service: ExplorerService) {
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getCreatorProfile( service.getCreatorProfile(
creatorId = creatorId, creatorId = creatorId,
@@ -72,7 +78,7 @@ class ExplorerController(private val service: ExplorerService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getCreatorProfileDonationRanking(creatorId, pageable, member)) ApiResponse.ok(service.getCreatorProfileDonationRanking(creatorId, pageable, member))
} }
@@ -81,8 +87,9 @@ class ExplorerController(private val service: ExplorerService) {
@RequestBody request: PostWriteCheersRequest, @RequestBody request: PostWriteCheersRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.writeCheers(request, member), "등록되었습니다.") val message = messageSource.getMessage("explorer.cheers.created", langContext.lang)
ApiResponse.ok(service.writeCheers(request, member), message)
} }
@PutMapping("/profile/cheers") @PutMapping("/profile/cheers")
@@ -90,8 +97,9 @@ class ExplorerController(private val service: ExplorerService) {
@RequestBody request: PutWriteCheersRequest, @RequestBody request: PutWriteCheersRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.modifyCheers(request, member), "수정되었습니다.") val message = messageSource.getMessage("explorer.cheers.updated", langContext.lang)
ApiResponse.ok(service.modifyCheers(request, member), message)
} }
@PostMapping("/profile/notice") @PostMapping("/profile/notice")
@@ -100,8 +108,9 @@ class ExplorerController(private val service: ExplorerService) {
@RequestBody request: PostCreatorNoticeRequest, @RequestBody request: PostCreatorNoticeRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.saveNotice(member, request.notice), "공지사항이 저장되었습니다.") val message = messageSource.getMessage("explorer.notice.saved", langContext.lang)
ApiResponse.ok(service.saveNotice(member, request.notice), message)
} }
@GetMapping("/profile/{id}/follower-list") @GetMapping("/profile/{id}/follower-list")
@@ -110,7 +119,7 @@ class ExplorerController(private val service: ExplorerService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getFollowerList(creatorId, member, pageable)) ApiResponse.ok(service.getFollowerList(creatorId, member, pageable))
} }
@@ -121,7 +130,7 @@ class ExplorerController(private val service: ExplorerService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.getCreatorProfileCheers(creatorId, timezone, pageable)) ApiResponse.ok(service.getCreatorProfileCheers(creatorId, timezone, pageable))
} }
} }

View File

@@ -19,6 +19,9 @@ import kr.co.vividnext.sodalive.explorer.profile.CreatorCheers
import kr.co.vividnext.sodalive.explorer.profile.QChannelNotice.channelNotice import kr.co.vividnext.sodalive.explorer.profile.QChannelNotice.channelNotice
import kr.co.vividnext.sodalive.explorer.profile.QCreatorCheers.creatorCheers import kr.co.vividnext.sodalive.explorer.profile.QCreatorCheers.creatorCheers
import kr.co.vividnext.sodalive.explorer.profile.TimeDifferenceResult import kr.co.vividnext.sodalive.explorer.profile.TimeDifferenceResult
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.room.LiveRoom import kr.co.vividnext.sodalive.live.room.LiveRoom
import kr.co.vividnext.sodalive.live.room.LiveRoomType import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
@@ -40,11 +43,12 @@ import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.Locale
@Repository @Repository
class ExplorerQueryRepository( class ExplorerQueryRepository(
private val queryFactory: JPAQueryFactory, private val queryFactory: JPAQueryFactory,
private val messageSource: SodaMessageSource,
private val langContext: LangContext,
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String private val cloudFrontHost: String
@@ -243,7 +247,7 @@ class ExplorerQueryRepository(
val creator = queryFactory val creator = queryFactory
.selectFrom(member) .selectFrom(member)
.where(member.id.eq(creatorId)) .where(member.id.eq(creatorId))
.fetchFirst() ?: throw SodaException("없는 사용자 입니다.") .fetchFirst() ?: throw SodaException(messageKey = "member.validation.user_not_found")
val creatorTagCount = creator.tags val creatorTagCount = creator.tags
.asSequence() .asSequence()
@@ -383,6 +387,12 @@ class ExplorerQueryRepository(
) )
} }
val dateTimePattern = messageSource
.getMessage("explorer.date.live_room.datetime_format", langContext.lang)
?: messageSource.getMessage("explorer.date.live_room.datetime_format", Lang.KO).orEmpty()
val dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimePattern)
.withLocale(langContext.lang.locale)
return result return result
.map { .map {
val reservations = it.reservations val reservations = it.reservations
@@ -393,11 +403,7 @@ class ExplorerQueryRepository(
val beginDateTime = it.beginDateTime val beginDateTime = it.beginDateTime
.atZone(ZoneId.of("UTC")) .atZone(ZoneId.of("UTC"))
.withZoneSameInstant(ZoneId.of(timezone)) .withZoneSameInstant(ZoneId.of(timezone))
.format( .format(dateTimeFormatter)
DateTimeFormatter
.ofPattern("yyyy년 MM월 dd일 (E) a hh시 mm분")
.withLocale(Locale.KOREAN)
)
val beginDateTimeUtc = it.beginDateTime val beginDateTimeUtc = it.beginDateTime
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
@@ -451,6 +457,12 @@ class ExplorerQueryRepository(
} }
fun getCheersList(creatorId: Long, timezone: String, offset: Long, limit: Long): GetCheersResponse { fun getCheersList(creatorId: Long, timezone: String, offset: Long, limit: Long): GetCheersResponse {
val cheersDatePattern = messageSource
.getMessage("explorer.date.cheers.format", langContext.lang)
?: messageSource.getMessage("explorer.date.cheers.format", Lang.KO).orEmpty()
val cheersDateFormatter = DateTimeFormatter.ofPattern(cheersDatePattern)
.withLocale(langContext.lang.locale)
val totalCount = queryFactory val totalCount = queryFactory
.selectFrom(creatorCheers) .selectFrom(creatorCheers)
.where( .where(
@@ -489,7 +501,7 @@ class ExplorerQueryRepository(
}, },
content = it.cheers, content = it.cheers,
languageCode = it.languageCode, languageCode = it.languageCode,
date = date.format(DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")), date = date.format(cheersDateFormatter),
replyList = it.children.asSequence() replyList = it.children.asSequence()
.map { cheers -> .map { cheers ->
val replyDate = cheers.createdAt!! val replyDate = cheers.createdAt!!
@@ -507,7 +519,7 @@ class ExplorerQueryRepository(
}, },
content = cheers.cheers, content = cheers.cheers,
languageCode = cheers.languageCode, languageCode = cheers.languageCode,
date = replyDate.format(DateTimeFormatter.ofPattern("yyyy.MM.dd E hh:mm a")), date = replyDate.format(cheersDateFormatter),
replyList = listOf() replyList = listOf()
) )
} }

View File

@@ -19,7 +19,9 @@ import kr.co.vividnext.sodalive.explorer.profile.PutWriteCheersRequest
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityService import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityService
import kr.co.vividnext.sodalive.fcm.FcmEvent import kr.co.vividnext.sodalive.fcm.FcmEvent
import kr.co.vividnext.sodalive.fcm.FcmEventType import kr.co.vividnext.sodalive.fcm.FcmEventType
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.MemberRole
@@ -50,6 +52,7 @@ class ExplorerService(
private val applicationEventPublisher: ApplicationEventPublisher, private val applicationEventPublisher: ApplicationEventPublisher,
private val contentTranslationRepository: ContentTranslationRepository, private val contentTranslationRepository: ContentTranslationRepository,
private val messageSource: SodaMessageSource,
private val langContext: LangContext, private val langContext: LangContext,
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
@@ -68,15 +71,20 @@ class ExplorerService(
val lastSunday = lastMonday val lastSunday = lastMonday
.plusDays(6) .plusDays(6)
val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") val startDatePattern = messageSource.getMessage("explorer.date.creator_rank.start_format", langContext.lang)
val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") ?: messageSource.getMessage("explorer.date.creator_rank.start_format", Lang.KO).orEmpty()
val endDatePattern = messageSource.getMessage("explorer.date.creator_rank.end_format", langContext.lang)
?: messageSource.getMessage("explorer.date.creator_rank.end_format", Lang.KO).orEmpty()
val startDateFormatter = DateTimeFormatter.ofPattern(startDatePattern).withLocale(langContext.lang.locale)
val endDateFormatter = DateTimeFormatter.ofPattern(endDatePattern).withLocale(langContext.lang.locale)
val formattedLastMonday = lastMonday.format(startDateFormatter) val formattedLastMonday = lastMonday.format(startDateFormatter)
val formattedLastSunday = lastSunday.format(endDateFormatter) val formattedLastSunday = lastSunday.format(endDateFormatter)
return GetExplorerSectionResponse( return GetExplorerSectionResponse(
title = "인기 크리에이터", title = messageSource.getMessage("explorer.section.creator_rank.title", langContext.lang).orEmpty(),
coloredTitle = "인기", coloredTitle = messageSource.getMessage("explorer.section.creator_rank.colored_title", langContext.lang)
.orEmpty(),
color = "FF5C49", color = "FF5C49",
desc = "$formattedLastMonday ~ $formattedLastSunday", desc = "$formattedLastMonday ~ $formattedLastSunday",
creators = creatorRankings creators = creatorRankings
@@ -99,15 +107,20 @@ class ExplorerService(
val lastSunday = lastMonday val lastSunday = lastMonday
.plusDays(6) .plusDays(6)
val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") val startDatePattern = messageSource.getMessage("explorer.date.creator_rank.start_format", langContext.lang)
val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") ?: messageSource.getMessage("explorer.date.creator_rank.start_format", Lang.KO).orEmpty()
val endDatePattern = messageSource.getMessage("explorer.date.creator_rank.end_format", langContext.lang)
?: messageSource.getMessage("explorer.date.creator_rank.end_format", Lang.KO).orEmpty()
val startDateFormatter = DateTimeFormatter.ofPattern(startDatePattern).withLocale(langContext.lang.locale)
val endDateFormatter = DateTimeFormatter.ofPattern(endDatePattern).withLocale(langContext.lang.locale)
val formattedLastMonday = lastMonday.format(startDateFormatter) val formattedLastMonday = lastMonday.format(startDateFormatter)
val formattedLastSunday = lastSunday.format(endDateFormatter) val formattedLastSunday = lastSunday.format(endDateFormatter)
val creatorRankingSection = GetExplorerSectionResponse( val creatorRankingSection = GetExplorerSectionResponse(
title = "인기 크리에이터", title = messageSource.getMessage("explorer.section.creator_rank.title", langContext.lang).orEmpty(),
coloredTitle = "인기", coloredTitle = messageSource.getMessage("explorer.section.creator_rank.colored_title", langContext.lang)
.orEmpty(),
color = "FF5C49", color = "FF5C49",
desc = "$formattedLastMonday ~ $formattedLastSunday", desc = "$formattedLastMonday ~ $formattedLastSunday",
creators = creatorRankings creators = creatorRankings
@@ -121,16 +134,18 @@ class ExplorerService(
.map { it.toExplorerSectionCreator(cloudFrontHost) } .map { it.toExplorerSectionCreator(cloudFrontHost) }
val newCreatorsSection = GetExplorerSectionResponse( val newCreatorsSection = GetExplorerSectionResponse(
title = "새로 시작", title = messageSource.getMessage("explorer.section.new_creators.title", langContext.lang).orEmpty(),
coloredTitle = "새로", coloredTitle = messageSource.getMessage("explorer.section.new_creators.colored_title", langContext.lang)
.orEmpty(),
color = "5FD28F", color = "5FD28F",
creators = newCreators creators = newCreators
) )
sections.add(newCreatorsSection) sections.add(newCreatorsSection)
val maleCreatorSection = GetExplorerSectionResponse( val maleCreatorSection = GetExplorerSectionResponse(
title = "남자 크리에이터", title = messageSource.getMessage("explorer.section.male_creators.title", langContext.lang).orEmpty(),
coloredTitle = "남자", coloredTitle = messageSource.getMessage("explorer.section.male_creators.colored_title", langContext.lang)
.orEmpty(),
color = "39abde", color = "39abde",
creators = queryRepository creators = queryRepository
.findCreatorByGender(1) .findCreatorByGender(1)
@@ -139,8 +154,9 @@ class ExplorerService(
) )
val femaleCreatorSection = GetExplorerSectionResponse( val femaleCreatorSection = GetExplorerSectionResponse(
title = "여자 크리에이터", title = messageSource.getMessage("explorer.section.female_creators.title", langContext.lang).orEmpty(),
coloredTitle = "여자", coloredTitle = messageSource.getMessage("explorer.section.female_creators.colored_title", langContext.lang)
.orEmpty(),
color = "ffa517", color = "ffa517",
creators = queryRepository creators = queryRepository
.findCreatorByGender(0) .findCreatorByGender(0)
@@ -162,7 +178,7 @@ class ExplorerService(
fun getSearchChannel(channel: String, member: Member): List<GetRoomDetailUser> { fun getSearchChannel(channel: String, member: Member): List<GetRoomDetailUser> {
if (channel.length < 2) { if (channel.length < 2) {
throw SodaException("두 글자 이상 입력 하셔야 합니다.") throw SodaException(messageKey = "explorer.search.channel.min_length")
} }
return queryRepository.getSearchChannel(channel, member.id!!) return queryRepository.getSearchChannel(channel, member.id!!)
@@ -179,11 +195,17 @@ class ExplorerService(
member: Member member: Member
): GetCreatorProfileResponse { ): GetCreatorProfileResponse {
// 크리에이터(유저) 정보 // 크리에이터(유저) 정보
val creatorAccount = queryRepository.getMember(creatorId) ?: throw SodaException("없는 사용자 입니다.") val creatorAccount = queryRepository.getMember(creatorId)
?: throw SodaException(messageKey = "member.validation.user_not_found")
// 차단된 사용자 체크 // 차단된 사용자 체크
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = creatorId) val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = creatorId)
if (isBlocked) throw SodaException("${creatorAccount.nickname}님의 요청으로 채널 접근이 제한됩니다.") if (isBlocked) {
val messageTemplate = messageSource
.getMessage("explorer.creator.blocked_access", langContext.lang)
.orEmpty()
throw SodaException(message = String.format(messageTemplate, creatorAccount.nickname))
}
val isCreator = creatorAccount.role == MemberRole.CREATOR val isCreator = creatorAccount.role == MemberRole.CREATOR
@@ -461,10 +483,16 @@ class ExplorerService(
@Transactional @Transactional
fun writeCheers(request: PostWriteCheersRequest, member: Member) { fun writeCheers(request: PostWriteCheersRequest, member: Member) {
val creator = queryRepository.getMember(request.creatorId) ?: throw SodaException("없는 사용자 입니다.") val creator = queryRepository.getMember(request.creatorId)
?: throw SodaException(messageKey = "member.validation.user_not_found")
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = request.creatorId) val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = request.creatorId)
if (isBlocked) throw SodaException("${creator.nickname}님의 요청으로 팬토크 작성이 제한됩니다.") if (isBlocked) {
val messageTemplate = messageSource
.getMessage("explorer.creator.blocked_cheers", langContext.lang)
.orEmpty()
throw SodaException(message = String.format(messageTemplate, creator.nickname))
}
val cheers = CreatorCheers(cheers = request.content, languageCode = request.languageCode) val cheers = CreatorCheers(cheers = request.content, languageCode = request.languageCode)
cheers.member = member cheers.member = member
@@ -510,7 +538,7 @@ class ExplorerService(
@Transactional @Transactional
fun modifyCheers(request: PutWriteCheersRequest, member: Member) { fun modifyCheers(request: PutWriteCheersRequest, member: Member) {
val cheers = queryRepository.getCheers(request.cheersId) val cheers = queryRepository.getCheers(request.cheersId)
?: throw SodaException("잘못된 요청입니다.") ?: throw SodaException(messageKey = "common.error.invalid_request")
if (cheers.member!!.id!! == member.id!!) { if (cheers.member!!.id!! == member.id!!) {
if (request.content != null) { if (request.content != null) {
@@ -544,7 +572,7 @@ class ExplorerService(
FcmEvent( FcmEvent(
type = FcmEventType.CHANGE_NOTICE, type = FcmEventType.CHANGE_NOTICE,
title = member.nickname, title = member.nickname,
message = "새 글이 등록되었습니다.", message = messageSource.getMessage("explorer.notice.fcm.message", langContext.lang).orEmpty(),
creatorId = member.id!! creatorId = member.id!!
) )
) )

View File

@@ -36,7 +36,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestPart("request") requestString: String, @RequestPart("request") requestString: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.createCommunityPost( service.createCommunityPost(
@@ -57,7 +57,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestPart("request") requestString: String, @RequestPart("request") requestString: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.modifyCommunityPost( service.modifyCommunityPost(
@@ -75,7 +75,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getCommunityPostList( service.getCommunityPostList(
@@ -95,7 +95,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestParam timezone: String, @RequestParam timezone: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getCommunityPostDetail( service.getCommunityPostDetail(
@@ -112,7 +112,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestBody request: PostCommunityPostLikeRequest, @RequestBody request: PostCommunityPostLikeRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok(service.communityPostLike(request, member)) ApiResponse.ok(service.communityPostLike(request, member))
} }
@@ -122,7 +122,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestBody request: CreateCommunityPostCommentRequest, @RequestBody request: CreateCommunityPostCommentRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.createCommunityPostComment( service.createCommunityPostComment(
@@ -140,7 +140,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestBody request: ModifyCommunityPostCommentRequest, @RequestBody request: ModifyCommunityPostCommentRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.modifyCommunityPostComment(request = request, member = member) service.modifyCommunityPostComment(request = request, member = member)
@@ -154,7 +154,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getCommunityPostCommentList( service.getCommunityPostCommentList(
@@ -174,7 +174,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getCommentReplyList( service.getCommentReplyList(
@@ -191,7 +191,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestParam timezone: String, @RequestParam timezone: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.getLatestPostListFromCreatorsYouFollow( service.getLatestPostListFromCreatorsYouFollow(
@@ -207,7 +207,7 @@ class CreatorCommunityController(private val service: CreatorCommunityService) {
@RequestBody request: PurchasePostRequest, @RequestBody request: PurchasePostRequest,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException(messageKey = "common.error.bad_credentials")
ApiResponse.ok( ApiResponse.ok(
service.purchasePost( service.purchasePost(

View File

@@ -19,6 +19,8 @@ import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommu
import kr.co.vividnext.sodalive.extensions.getTimeAgoString import kr.co.vividnext.sodalive.extensions.getTimeAgoString
import kr.co.vividnext.sodalive.fcm.FcmEvent import kr.co.vividnext.sodalive.fcm.FcmEvent
import kr.co.vividnext.sodalive.fcm.FcmEventType 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.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import kr.co.vividnext.sodalive.utils.generateFileName import kr.co.vividnext.sodalive.utils.generateFileName
@@ -45,6 +47,8 @@ class CreatorCommunityService(
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
private val audioContentCloudFront: AudioContentCloudFront, private val audioContentCloudFront: AudioContentCloudFront,
private val applicationEventPublisher: ApplicationEventPublisher, private val applicationEventPublisher: ApplicationEventPublisher,
private val messageSource: SodaMessageSource,
private val langContext: LangContext,
@Value("\${cloud.aws.s3.bucket}") @Value("\${cloud.aws.s3.bucket}")
private val imageBucket: String, private val imageBucket: String,
@@ -65,11 +69,11 @@ class CreatorCommunityService(
val request = objectMapper.readValue(requestString, CreateCommunityPostRequest::class.java) val request = objectMapper.readValue(requestString, CreateCommunityPostRequest::class.java)
if (request.price > 0 && postImage == null) { if (request.price > 0 && postImage == null) {
throw SodaException("유료 게시글 등록을 위해서는 이미지가 필요합니다.") throw SodaException(messageKey = "creator.community.paid_post_image_required")
} }
if (audioFile != null && postImage == null) { if (audioFile != null && postImage == null) {
throw SodaException("오디오 등록을 위해서는 이미지가 필요합니다.") throw SodaException(messageKey = "creator.community.audio_post_image_required")
} }
postImage?.let { validateImage(it, request.price > 0) } postImage?.let { validateImage(it, request.price > 0) }
@@ -119,7 +123,7 @@ class CreatorCommunityService(
FcmEvent( FcmEvent(
type = FcmEventType.CHANGE_NOTICE, type = FcmEventType.CHANGE_NOTICE,
title = member.nickname, title = member.nickname,
message = "새 글이 등록되었습니다.", message = messageSource.getMessage("creator.community.fcm.new_post", langContext.lang).orEmpty(),
creatorId = member.id!! creatorId = member.id!!
) )
) )
@@ -130,7 +134,7 @@ class CreatorCommunityService(
val request = objectMapper.readValue(requestString, ModifyCommunityPostRequest::class.java) val request = objectMapper.readValue(requestString, ModifyCommunityPostRequest::class.java)
val post = repository.findByIdAndMemberId(id = request.creatorCommunityId, memberId = member.id!!) val post = repository.findByIdAndMemberId(id = request.creatorCommunityId, memberId = member.id!!)
?: throw SodaException("잘못된 요청입니다.") ?: throw SodaException(messageKey = "common.error.invalid_request")
postImage?.let { validateImage(it, post.price > 0) } postImage?.let { validateImage(it, post.price > 0) }
@@ -269,10 +273,15 @@ class CreatorCommunityService(
isAdult: Boolean isAdult: Boolean
): GetCommunityPostListResponse { ): GetCommunityPostListResponse {
val post = repository.getCommunityPost(postId, isAdult = isAdult) val post = repository.getCommunityPost(postId, isAdult = isAdult)
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") ?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = post.creatorId) val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = post.creatorId)
if (isBlocked) throw SodaException("${post.creatorNickname}님의 요청으로 접근이 제한됩니다.") if (isBlocked) {
val messageTemplate = messageSource
.getMessage("creator.community.blocked_access", langContext.lang)
.orEmpty()
throw SodaException(message = String.format(messageTemplate, post.creatorNickname))
}
val isLike = likeRepository.findByPostIdAndMemberId(postId = post.id, memberId = memberId)?.isActive ?: false val isLike = likeRepository.findByPostIdAndMemberId(postId = post.id, memberId = memberId)?.isActive ?: false
val likeCount = likeRepository.totalCountCommunityPostLikeByPostId(post.id) val likeCount = likeRepository.totalCountCommunityPostLikeByPostId(post.id)
@@ -345,7 +354,7 @@ class CreatorCommunityService(
postLike.member = member postLike.member = member
val post = repository.findByIdAndActive(request.postId, isAdult = member.auth != null) val post = repository.findByIdAndActive(request.postId, isAdult = member.auth != null)
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") ?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
postLike.creatorCommunity = post postLike.creatorCommunity = post
likeRepository.save(postLike) likeRepository.save(postLike)
@@ -365,11 +374,11 @@ class CreatorCommunityService(
isSecret: Boolean = false isSecret: Boolean = false
) { ) {
val post = repository.findByIdOrNull(id = postId) val post = repository.findByIdOrNull(id = postId)
?: throw SodaException("잘못된 게시물 입니다.\n다시 시도해 주세요.") ?: throw SodaException(messageKey = "creator.community.invalid_post_retry")
val isExistOrdered = useCanRepository.isExistCommunityPostOrdered(postId = postId, memberId = member.id!!) val isExistOrdered = useCanRepository.isExistCommunityPostOrdered(postId = postId, memberId = member.id!!)
if (isSecret && !isExistOrdered) { if (isSecret && !isExistOrdered) {
throw SodaException("게시글을 구매 후 비밀댓글을 등록할 수 있습니다.") throw SodaException(messageKey = "creator.community.secret_comment_purchase_required")
} }
val postComment = CreatorCommunityComment(comment = comment, isSecret = isSecret) val postComment = CreatorCommunityComment(comment = comment, isSecret = isSecret)
@@ -392,7 +401,7 @@ class CreatorCommunityService(
@Transactional @Transactional
fun modifyCommunityPostComment(request: ModifyCommunityPostCommentRequest, member: Member) { fun modifyCommunityPostComment(request: ModifyCommunityPostCommentRequest, member: Member) {
val postComment = commentRepository.findByIdOrNull(id = request.commentId) val postComment = commentRepository.findByIdOrNull(id = request.commentId)
?: throw SodaException("잘못된 접근 입니다.\n확인 후 다시 시도해 주세요.") ?: throw SodaException(messageKey = "creator.community.invalid_access_retry")
if (request.comment != null && postComment.member!!.id!! == member.id!!) { if (request.comment != null && postComment.member!!.id!! == member.id!!) {
postComment.comment = request.comment postComment.comment = request.comment
@@ -530,10 +539,15 @@ class CreatorCommunityService(
container: String container: String
): GetCommunityPostListResponse { ): GetCommunityPostListResponse {
val post = repository.findByIdAndActive(postId, isAdult) val post = repository.findByIdAndActive(postId, isAdult)
?: throw SodaException("잘못된 요청입니다.\n다시 시도해 주세요.") ?: throw SodaException(messageKey = "creator.community.invalid_request_retry")
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = post.member!!.id!!) val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = post.member!!.id!!)
if (isBlocked) throw SodaException("${post.member!!.nickname}님의 요청으로 접근이 제한됩니다.") if (isBlocked) {
val messageTemplate = messageSource
.getMessage("creator.community.blocked_access", langContext.lang)
.orEmpty()
throw SodaException(message = String.format(messageTemplate, post.member!!.nickname))
}
val existOrdered = useCanRepository.isExistCommunityPostOrdered(postId = postId, memberId = memberId) val existOrdered = useCanRepository.isExistCommunityPostOrdered(postId = postId, memberId = memberId)

View File

@@ -988,6 +988,159 @@ class SodaMessageSource {
) )
) )
private val explorerSectionMessages = mapOf(
"explorer.section.creator_rank.title" to mapOf(
Lang.KO to "인기 크리에이터",
Lang.EN to "Top creators",
Lang.JA to "人気クリエイター"
),
"explorer.section.creator_rank.colored_title" to mapOf(
Lang.KO to "인기",
Lang.EN to "Top",
Lang.JA to "人気"
),
"explorer.section.new_creators.title" to mapOf(
Lang.KO to "새로 시작",
Lang.EN to "New creators",
Lang.JA to "新規クリエイター"
),
"explorer.section.new_creators.colored_title" to mapOf(
Lang.KO to "새로",
Lang.EN to "New",
Lang.JA to "新規"
),
"explorer.section.male_creators.title" to mapOf(
Lang.KO to "남자 크리에이터",
Lang.EN to "Male creators",
Lang.JA to "男性クリエイター"
),
"explorer.section.male_creators.colored_title" to mapOf(
Lang.KO to "남자",
Lang.EN to "Male",
Lang.JA to "男性"
),
"explorer.section.female_creators.title" to mapOf(
Lang.KO to "여자 크리에이터",
Lang.EN to "Female creators",
Lang.JA to "女性クリエイター"
),
"explorer.section.female_creators.colored_title" to mapOf(
Lang.KO to "여자",
Lang.EN to "Female",
Lang.JA to "女性"
)
)
private val explorerDateMessages = mapOf(
"explorer.date.creator_rank.start_format" to mapOf(
Lang.KO to "yyyy년 MM월 dd일",
Lang.EN to "MMM dd, yyyy",
Lang.JA to "yyyy年MM月dd日"
),
"explorer.date.creator_rank.end_format" to mapOf(
Lang.KO to "MM월 dd일",
Lang.EN to "MMM dd",
Lang.JA to "MM月dd日"
),
"explorer.date.live_room.datetime_format" to mapOf(
Lang.KO to "yyyy년 MM월 dd일 (E) a hh시 mm분",
Lang.EN to "EEE, MMM dd, yyyy h:mm a",
Lang.JA to "yyyy年MM月dd日(E) a hh時 mm分"
),
"explorer.date.cheers.format" to mapOf(
Lang.KO to "yyyy.MM.dd E hh:mm a",
Lang.EN to "MMM dd, yyyy E hh:mm a",
Lang.JA to "yyyy.MM.dd E hh:mm a"
)
)
private val explorerResponseMessages = mapOf(
"explorer.cheers.created" to mapOf(
Lang.KO to "등록되었습니다.",
Lang.EN to "Registered.",
Lang.JA to "登録されました。"
),
"explorer.cheers.updated" to mapOf(
Lang.KO to "수정되었습니다.",
Lang.EN to "Updated.",
Lang.JA to "更新されました。"
),
"explorer.notice.saved" to mapOf(
Lang.KO to "공지사항이 저장되었습니다.",
Lang.EN to "Notice has been saved.",
Lang.JA to "お知らせが保存されました。"
),
"explorer.notice.fcm.message" to mapOf(
Lang.KO to "새 글이 등록되었습니다.",
Lang.EN to "A new post has been added.",
Lang.JA to "新しい投稿が登録されました。"
)
)
private val explorerValidationMessages = mapOf(
"explorer.search.channel.min_length" to mapOf(
Lang.KO to "두 글자 이상 입력 하셔야 합니다.",
Lang.EN to "Please enter at least 2 characters.",
Lang.JA to "2文字以上入力してください。"
)
)
private val explorerAccessMessages = mapOf(
"explorer.creator.blocked_access" to mapOf(
Lang.KO to "%s님의 요청으로 채널 접근이 제한됩니다.",
Lang.EN to "Channel access is restricted at %s's request.",
Lang.JA to "%sさんの要請によりチャンネルへのアクセスが制限されています。"
),
"explorer.creator.blocked_cheers" to mapOf(
Lang.KO to "%s님의 요청으로 팬토크 작성이 제한됩니다.",
Lang.EN to "Fan talk posting is restricted at %s's request.",
Lang.JA to "%sさんの要請によりファントークの投稿が制限されています。"
)
)
private val creatorCommunityMessages = mapOf(
"creator.community.paid_post_image_required" to mapOf(
Lang.KO to "유료 게시글 등록을 위해서는 이미지가 필요합니다.",
Lang.EN to "An image is required to post paid content.",
Lang.JA to "有料投稿を登録するには画像が必要です。"
),
"creator.community.audio_post_image_required" to mapOf(
Lang.KO to "오디오 등록을 위해서는 이미지가 필요합니다.",
Lang.EN to "An image is required to upload audio.",
Lang.JA to "オーディオを登録するには画像が必要です。"
),
"creator.community.fcm.new_post" to mapOf(
Lang.KO to "새 글이 등록되었습니다.",
Lang.EN to "A new post has been added.",
Lang.JA to "新しい投稿が登録されました。"
),
"creator.community.invalid_request_retry" to mapOf(
Lang.KO to "잘못된 요청입니다.\n다시 시도해 주세요.",
Lang.EN to "Invalid request.\nPlease try again.",
Lang.JA to "不正なリクエストです。\nもう一度お試しください。"
),
"creator.community.invalid_post_retry" to mapOf(
Lang.KO to "잘못된 게시물 입니다.\n다시 시도해 주세요.",
Lang.EN to "Invalid post.\nPlease try again.",
Lang.JA to "不正な投稿です。\nもう一度お試しください。"
),
"creator.community.secret_comment_purchase_required" to mapOf(
Lang.KO to "게시글을 구매 후 비밀댓글을 등록할 수 있습니다.",
Lang.EN to "You can post a secret comment after purchasing the post.",
Lang.JA to "投稿を購入した後に秘密コメントを登録できます。"
),
"creator.community.invalid_access_retry" to mapOf(
Lang.KO to "잘못된 접근 입니다.\n확인 후 다시 시도해 주세요.",
Lang.EN to "Invalid access.\nPlease check and try again.",
Lang.JA to "不正なアクセスです。\n確認して再度お試しください。"
),
"creator.community.blocked_access" to mapOf(
Lang.KO to "%s님의 요청으로 접근이 제한됩니다.",
Lang.EN to "Access is restricted at %s's request.",
Lang.JA to "%sさんの要請によりアクセスが制限されています。"
)
)
fun getMessage(key: String, lang: Lang): String? { fun getMessage(key: String, lang: Lang): String? {
val messageGroups = listOf( val messageGroups = listOf(
commonMessages, commonMessages,
@@ -1026,7 +1179,13 @@ class SodaMessageSource {
liveRoomMessages, liveRoomMessages,
liveRoomMenuMessages, liveRoomMenuMessages,
memberProviderMessages, memberProviderMessages,
memberGenderMessages memberGenderMessages,
explorerSectionMessages,
explorerDateMessages,
explorerResponseMessages,
explorerValidationMessages,
explorerAccessMessages,
creatorCommunityMessages
) )
for (messages in messageGroups) { for (messages in messageGroups) {
val translations = messages[key] ?: continue val translations = messages[key] ?: continue