회원 메시지 다국어 처리
회원/인증 API 응답 메시지를 다국어 키로 분리함.
This commit is contained in:
@@ -471,6 +471,197 @@ class SodaMessageSource {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val memberAuthMessages = mapOf(
|
||||||
|
"member.auth.blocked_policy" to mapOf(
|
||||||
|
Lang.KO to "운영정책을 위반하여 이용을 제한합니다.",
|
||||||
|
Lang.EN to "Your access is restricted due to policy violations.",
|
||||||
|
Lang.JA to "運営ポリシー違反のため利用が制限されています。"
|
||||||
|
),
|
||||||
|
"member.auth.already_verified" to mapOf(
|
||||||
|
Lang.KO to "이미 인증된 계정입니다.",
|
||||||
|
Lang.EN to "This account is already verified.",
|
||||||
|
Lang.JA to "既に認証済みのアカウントです。"
|
||||||
|
),
|
||||||
|
"member.auth.certificate_invalid_retry" to mapOf(
|
||||||
|
Lang.KO to "인증정보에 오류가 있습니다.\n다시 시도해 주세요.",
|
||||||
|
Lang.EN to "There is an error with the verification information.\nPlease try again.",
|
||||||
|
Lang.JA to "認証情報にエラーがあります。\nもう一度お試しください。"
|
||||||
|
),
|
||||||
|
"member.auth.max_accounts" to mapOf(
|
||||||
|
Lang.KO to "이미 본인인증한 계정 %s개 이용중입니다.\n" +
|
||||||
|
"소다라이브의 본인인증은 최대 3개의 계정만 이용할 수 있습니다.",
|
||||||
|
Lang.EN to "You are already using %s verified account(s).\n" +
|
||||||
|
"Identity verification is limited to up to 3 accounts on Sodalive.",
|
||||||
|
Lang.JA to "本人認証済みのアカウントを%s件利用中です。\n" +
|
||||||
|
"ソダライブの本人認証は最大3アカウントまでです。"
|
||||||
|
),
|
||||||
|
"member.auth.age_limit" to mapOf(
|
||||||
|
Lang.KO to "%s년 1월 1일 이전 출생자만 본인인증이 가능합니다.",
|
||||||
|
Lang.EN to "Only users born on or before January 1, %s can be verified.",
|
||||||
|
Lang.JA to "%s年1月1日以前に生まれた方のみ本人認証が可能です。"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val memberMessages = mapOf(
|
||||||
|
"member.signup.failed_retry" to mapOf(
|
||||||
|
Lang.KO to "회원가입을 하지 못했습니다.\n다시 시도해 주세요.",
|
||||||
|
Lang.EN to "Sign up failed.\nPlease try again.",
|
||||||
|
Lang.JA to "会員登録に失敗しました。\nもう一度お試しください。"
|
||||||
|
),
|
||||||
|
"member.signup.success" to mapOf(
|
||||||
|
Lang.KO to "회원가입을 축하드립니다.",
|
||||||
|
Lang.EN to "Congratulations on your sign up.",
|
||||||
|
Lang.JA to "ご登録おめでとうございます。"
|
||||||
|
),
|
||||||
|
"member.login.success" to mapOf(
|
||||||
|
Lang.KO to "로그인 되었습니다.",
|
||||||
|
Lang.EN to "You are logged in.",
|
||||||
|
Lang.JA to "ログインしました。"
|
||||||
|
),
|
||||||
|
"member.signout.success" to mapOf(
|
||||||
|
Lang.KO to "정상적으로 탈퇴 처리되었습니다.",
|
||||||
|
Lang.EN to "Your account has been successfully deleted.",
|
||||||
|
Lang.JA to "正常に退会処理されました。"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val memberValidationMessages = mapOf(
|
||||||
|
"member.validation.invalid_request_retry" to mapOf(
|
||||||
|
Lang.KO to "잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.",
|
||||||
|
Lang.EN to "Invalid request.\nPlease close the app and try again.",
|
||||||
|
Lang.JA to "不正なリクエストです。\nアプリを終了して再度お試しください。"
|
||||||
|
),
|
||||||
|
"member.validation.agree_required" to mapOf(
|
||||||
|
Lang.KO to "약관에 동의하셔야 회원가입이 가능합니다.",
|
||||||
|
Lang.EN to "You must agree to the terms to sign up.",
|
||||||
|
Lang.JA to "会員登録には規約への同意が必要です。"
|
||||||
|
),
|
||||||
|
"member.validation.user_not_found" to mapOf(
|
||||||
|
Lang.KO to "없는 사용자 입니다.",
|
||||||
|
Lang.EN to "User not found.",
|
||||||
|
Lang.JA to "ユーザーが見つかりません。"
|
||||||
|
),
|
||||||
|
"member.validation.account_not_found" to mapOf(
|
||||||
|
Lang.KO to "없는 계정입니다.",
|
||||||
|
Lang.EN to "Account not found.",
|
||||||
|
Lang.JA to "アカウントが見つかりません。"
|
||||||
|
),
|
||||||
|
"member.validation.inactive_account" to mapOf(
|
||||||
|
Lang.KO to "탈퇴한 계정입니다.\n고객센터로 문의해 주시기 바랍니다.",
|
||||||
|
Lang.EN to "This account has been deleted.\nPlease contact customer support.",
|
||||||
|
Lang.JA to "退会したアカウントです。\nカスタマーサポートへお問い合わせください。"
|
||||||
|
),
|
||||||
|
"member.validation.creator_not_found" to mapOf(
|
||||||
|
Lang.KO to "크리에이터 정보를 확인해주세요.",
|
||||||
|
Lang.EN to "Please check the creator information.",
|
||||||
|
Lang.JA to "クリエイター情報を確認してください。"
|
||||||
|
),
|
||||||
|
"member.validation.nickname_min_length" to mapOf(
|
||||||
|
Lang.KO to "두 글자 이상 입력 하셔야 합니다.",
|
||||||
|
Lang.EN to "Please enter at least 2 characters.",
|
||||||
|
Lang.JA to "2文字以上入力してください。"
|
||||||
|
),
|
||||||
|
"member.validation.password_mismatch" to mapOf(
|
||||||
|
Lang.KO to "비밀번호가 일치하지 않습니다.",
|
||||||
|
Lang.EN to "Password does not match.",
|
||||||
|
Lang.JA to "パスワードが一致しません。"
|
||||||
|
),
|
||||||
|
"member.validation.signout_reason_required" to mapOf(
|
||||||
|
Lang.KO to "탈퇴하려는 이유를 입력해 주세요.",
|
||||||
|
Lang.EN to "Please enter a reason for deleting your account.",
|
||||||
|
Lang.JA to "退会理由を入力してください。"
|
||||||
|
),
|
||||||
|
"member.validation.email_available" to mapOf(
|
||||||
|
Lang.KO to "사용 가능한 이메일 입니다.",
|
||||||
|
Lang.EN to "This email is available.",
|
||||||
|
Lang.JA to "使用可能なメールアドレスです。"
|
||||||
|
),
|
||||||
|
"member.validation.nickname_available" to mapOf(
|
||||||
|
Lang.KO to "사용 가능한 닉네임 입니다.",
|
||||||
|
Lang.EN to "This nickname is available.",
|
||||||
|
Lang.JA to "使用可能なニックネームです。"
|
||||||
|
),
|
||||||
|
"member.validation.email_in_use" to mapOf(
|
||||||
|
Lang.KO to "이미 사용중인 이메일 입니다.",
|
||||||
|
Lang.EN to "This email is already in use.",
|
||||||
|
Lang.JA to "このメールアドレスは既に使用されています。"
|
||||||
|
),
|
||||||
|
"member.validation.nickname_in_use" to mapOf(
|
||||||
|
Lang.KO to "이미 사용중인 닉네임 입니다.",
|
||||||
|
Lang.EN to "This nickname is already in use.",
|
||||||
|
Lang.JA to "このニックネームは既に使用されています。"
|
||||||
|
),
|
||||||
|
"member.validation.email_registered_with_provider" to mapOf(
|
||||||
|
Lang.KO to "해당 이메일은 %s 계정으로 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.",
|
||||||
|
Lang.EN to "This email is registered with a %s account. Please use that social login.",
|
||||||
|
Lang.JA to "このメールアドレスは%sアカウントで登録されています。該当のソーシャルログインをご利用ください。"
|
||||||
|
),
|
||||||
|
"member.validation.email_registered_with_provider_already" to mapOf(
|
||||||
|
Lang.KO to "해당 이메일은 %s 계정으로 이미 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.",
|
||||||
|
Lang.EN to "This email is already registered with a %s account. Please use that social login.",
|
||||||
|
Lang.JA to "このメールアドレスは既に%sアカウントで登録されています。該当のソーシャルログインをご利用ください。"
|
||||||
|
),
|
||||||
|
"member.validation.unregistered_account_retry" to mapOf(
|
||||||
|
Lang.KO to "등록되지 않은 계정입니다.\n확인 후 다시 시도해 주세요.",
|
||||||
|
Lang.EN to "This account is not registered.\nPlease check and try again.",
|
||||||
|
Lang.JA to "登録されていないアカウントです。\n確認してもう一度お試しください。"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val memberSocialMessages = mapOf(
|
||||||
|
"member.social.google_login_failed" to mapOf(
|
||||||
|
Lang.KO to "구글 로그인을 하지 못했습니다. 다시 시도해 주세요",
|
||||||
|
Lang.EN to "Google login failed. Please try again.",
|
||||||
|
Lang.JA to "Googleログインに失敗しました。もう一度お試しください。"
|
||||||
|
),
|
||||||
|
"member.social.kakao_login_failed" to mapOf(
|
||||||
|
Lang.KO to "카카오 로그인을 하지 못했습니다. 다시 시도해 주세요",
|
||||||
|
Lang.EN to "Kakao login failed. Please try again.",
|
||||||
|
Lang.JA to "カカオログインに失敗しました。もう一度お試しください。"
|
||||||
|
),
|
||||||
|
"member.social.email_consent_required" to mapOf(
|
||||||
|
Lang.KO to "이메일 제공에 동의하셔야 서비스 이용이 가능합니다.",
|
||||||
|
Lang.EN to "You must agree to provide your email to use the service.",
|
||||||
|
Lang.JA to "サービス利用にはメール提供への同意が必要です。"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val memberProviderMessages = mapOf(
|
||||||
|
"member.provider.email" to mapOf(
|
||||||
|
Lang.KO to "이메일",
|
||||||
|
Lang.EN to "Email",
|
||||||
|
Lang.JA to "メール"
|
||||||
|
),
|
||||||
|
"member.provider.kakao" to mapOf(
|
||||||
|
Lang.KO to "카카오",
|
||||||
|
Lang.EN to "Kakao",
|
||||||
|
Lang.JA to "カカオ"
|
||||||
|
),
|
||||||
|
"member.provider.google" to mapOf(
|
||||||
|
Lang.KO to "구글",
|
||||||
|
Lang.EN to "Google",
|
||||||
|
Lang.JA to "Google"
|
||||||
|
),
|
||||||
|
"member.provider.apple" to mapOf(
|
||||||
|
Lang.KO to "애플",
|
||||||
|
Lang.EN to "Apple",
|
||||||
|
Lang.JA to "Apple"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val memberGenderMessages = mapOf(
|
||||||
|
"member.gender.male" to mapOf(
|
||||||
|
Lang.KO to "남",
|
||||||
|
Lang.EN to "Male",
|
||||||
|
Lang.JA to "男性"
|
||||||
|
),
|
||||||
|
"member.gender.female" to mapOf(
|
||||||
|
Lang.KO to "여",
|
||||||
|
Lang.EN to "Female",
|
||||||
|
Lang.JA to "女性"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
fun getMessage(key: String, lang: Lang): String? {
|
fun getMessage(key: String, lang: Lang): String? {
|
||||||
val messageGroups = listOf(
|
val messageGroups = listOf(
|
||||||
commonMessages,
|
commonMessages,
|
||||||
@@ -496,7 +687,13 @@ class SodaMessageSource {
|
|||||||
messageMessages,
|
messageMessages,
|
||||||
noticeMessages,
|
noticeMessages,
|
||||||
reportMessages,
|
reportMessages,
|
||||||
imageValidationMessages
|
imageValidationMessages,
|
||||||
|
memberAuthMessages,
|
||||||
|
memberMessages,
|
||||||
|
memberValidationMessages,
|
||||||
|
memberSocialMessages,
|
||||||
|
memberProviderMessages,
|
||||||
|
memberGenderMessages
|
||||||
)
|
)
|
||||||
for (messages in messageGroups) {
|
for (messages in messageGroups) {
|
||||||
val translations = messages[key] ?: continue
|
val translations = messages[key] ?: continue
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.member
|
|||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
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.marketing.AdTrackingHistoryType
|
import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType
|
||||||
import kr.co.vividnext.sodalive.marketing.AdTrackingService
|
import kr.co.vividnext.sodalive.marketing.AdTrackingService
|
||||||
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
import kr.co.vividnext.sodalive.member.block.MemberBlockRequest
|
||||||
@@ -37,7 +39,9 @@ class MemberController(
|
|||||||
private val kakaoAuthService: KakaoAuthService,
|
private val kakaoAuthService: KakaoAuthService,
|
||||||
private val googleAuthService: GoogleAuthService,
|
private val googleAuthService: GoogleAuthService,
|
||||||
private val trackingService: AdTrackingService,
|
private val trackingService: AdTrackingService,
|
||||||
private val userActionService: UserActionService
|
private val userActionService: UserActionService,
|
||||||
|
private val messageSource: SodaMessageSource,
|
||||||
|
private val langContext: LangContext
|
||||||
) {
|
) {
|
||||||
@GetMapping("/check/email")
|
@GetMapping("/check/email")
|
||||||
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
||||||
@@ -69,7 +73,8 @@ class MemberController(
|
|||||||
actionType = ActionType.SIGN_UP
|
actionType = ActionType.SIGN_UP
|
||||||
)
|
)
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
val message = messageSource.getMessage("member.signup.success", langContext.lang)
|
||||||
|
return ApiResponse.ok(message = message, data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/signup")
|
@PostMapping("/signup")
|
||||||
@@ -87,7 +92,8 @@ class MemberController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
val message = messageSource.getMessage("member.signup.success", langContext.lang)
|
||||||
|
return ApiResponse.ok(message = message, data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
@@ -230,7 +236,7 @@ class MemberController(
|
|||||||
@RequestBody request: CreatorFollowRequest,
|
@RequestBody request: CreatorFollowRequest,
|
||||||
@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.creatorUnFollow(creatorId = request.creatorId, memberId = member.id!!))
|
ApiResponse.ok(service.creatorUnFollow(creatorId = request.creatorId, memberId = member.id!!))
|
||||||
}
|
}
|
||||||
@@ -240,7 +246,7 @@ class MemberController(
|
|||||||
@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.getBlockedMemberIdList(member.id!!))
|
ApiResponse.ok(service.getBlockedMemberIdList(member.id!!))
|
||||||
}
|
}
|
||||||
@@ -250,7 +256,7 @@ class MemberController(
|
|||||||
@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.getBlockedMemberList(
|
service.getBlockedMemberList(
|
||||||
@@ -266,7 +272,7 @@ class MemberController(
|
|||||||
@RequestBody request: MemberBlockRequest,
|
@RequestBody request: MemberBlockRequest,
|
||||||
@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.memberBlock(request = request, memberId = member.id!!))
|
ApiResponse.ok(service.memberBlock(request = request, memberId = member.id!!))
|
||||||
}
|
}
|
||||||
@@ -276,7 +282,7 @@ class MemberController(
|
|||||||
@RequestBody request: MemberBlockRequest,
|
@RequestBody request: MemberBlockRequest,
|
||||||
@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.memberUnBlock(request = request, memberId = member.id!!))
|
ApiResponse.ok(service.memberUnBlock(request = request, memberId = member.id!!))
|
||||||
}
|
}
|
||||||
@@ -286,7 +292,7 @@ class MemberController(
|
|||||||
@RequestParam nickname: String,
|
@RequestParam nickname: 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.searchMember(nickname = nickname, member = member))
|
ApiResponse.ok(service.searchMember(nickname = nickname, member = member))
|
||||||
}
|
}
|
||||||
@@ -295,13 +301,16 @@ class MemberController(
|
|||||||
fun signOut(
|
fun signOut(
|
||||||
@RequestBody signOutRequest: SignOutRequest,
|
@RequestBody signOutRequest: SignOutRequest,
|
||||||
@AuthenticationPrincipal user: User
|
@AuthenticationPrincipal user: User
|
||||||
) = ApiResponse.ok(service.signOut(signOutRequest, user), "정상적으로 탈퇴 처리되었습니다.")
|
) = ApiResponse.ok(
|
||||||
|
service.signOut(signOutRequest, user),
|
||||||
|
messageSource.getMessage("member.signout.success", langContext.lang)
|
||||||
|
)
|
||||||
|
|
||||||
@GetMapping("/change/nickname/price")
|
@GetMapping("/change/nickname/price")
|
||||||
fun getChangeNicknamePrice(
|
fun getChangeNicknamePrice(
|
||||||
@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.getChangeNicknamePrice(memberId = member.id!!))
|
ApiResponse.ok(service.getChangeNicknamePrice(memberId = member.id!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +336,7 @@ class MemberController(
|
|||||||
@PathVariable id: Long,
|
@PathVariable id: Long,
|
||||||
@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.getMemberProfile(memberId = id, myMemberId = member.id!!))
|
ApiResponse.ok(service.getMemberProfile(memberId = id, myMemberId = member.id!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +346,7 @@ class MemberController(
|
|||||||
@RequestBody request: SocialLoginRequest
|
@RequestBody request: SocialLoginRequest
|
||||||
): ApiResponse<LoginResponse> {
|
): ApiResponse<LoginResponse> {
|
||||||
if (!authHeader.startsWith("Bearer ")) {
|
if (!authHeader.startsWith("Bearer ")) {
|
||||||
throw SodaException("구글 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
throw SodaException(messageKey = "member.social.google_login_failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
val token = authHeader.substring(7)
|
val token = authHeader.substring(7)
|
||||||
@@ -359,7 +368,8 @@ class MemberController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
val message = messageSource.getMessage("member.signup.success", langContext.lang)
|
||||||
|
return ApiResponse.ok(message = message, data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/login/kakao")
|
@PostMapping("/login/kakao")
|
||||||
@@ -368,7 +378,7 @@ class MemberController(
|
|||||||
@RequestBody request: SocialLoginRequest
|
@RequestBody request: SocialLoginRequest
|
||||||
): ApiResponse<LoginResponse> {
|
): ApiResponse<LoginResponse> {
|
||||||
if (!authHeader.startsWith("Bearer ")) {
|
if (!authHeader.startsWith("Bearer ")) {
|
||||||
throw SodaException("카카오 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
throw SodaException(messageKey = "member.social.kakao_login_failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
val token = authHeader.substring(7)
|
val token = authHeader.substring(7)
|
||||||
@@ -390,6 +400,7 @@ class MemberController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
val message = messageSource.getMessage("member.signup.success", langContext.lang)
|
||||||
|
return ApiResponse.ok(message = message, data = response.loginResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import kr.co.vividnext.sodalive.common.SodaException
|
|||||||
import kr.co.vividnext.sodalive.content.order.OrderService
|
import kr.co.vividnext.sodalive.content.order.OrderService
|
||||||
import kr.co.vividnext.sodalive.email.SendEmailService
|
import kr.co.vividnext.sodalive.email.SendEmailService
|
||||||
import kr.co.vividnext.sodalive.fcm.PushTokenService
|
import kr.co.vividnext.sodalive.fcm.PushTokenService
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
|
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||||
import kr.co.vividnext.sodalive.jwt.TokenProvider
|
import kr.co.vividnext.sodalive.jwt.TokenProvider
|
||||||
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
||||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||||
@@ -95,6 +97,9 @@ class MemberService(
|
|||||||
private val passwordEncoder: PasswordEncoder,
|
private val passwordEncoder: PasswordEncoder,
|
||||||
private val authenticationManagerBuilder: AuthenticationManagerBuilder,
|
private val authenticationManagerBuilder: AuthenticationManagerBuilder,
|
||||||
|
|
||||||
|
private val messageSource: SodaMessageSource,
|
||||||
|
private val langContext: LangContext,
|
||||||
|
|
||||||
private val objectMapper: ObjectMapper,
|
private val objectMapper: ObjectMapper,
|
||||||
|
|
||||||
@Value("\${cloud.aws.s3.bucket}")
|
@Value("\${cloud.aws.s3.bucket}")
|
||||||
@@ -109,13 +114,13 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun signUpV2(request: SignUpRequestV2): SignUpResponse {
|
fun signUpV2(request: SignUpRequestV2): SignUpResponse {
|
||||||
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
if (!request.isAgreePrivacyPolicy || !request.isAgreeTermsOfService) {
|
if (!request.isAgreePrivacyPolicy || !request.isAgreeTermsOfService) {
|
||||||
throw SodaException("약관에 동의하셔야 회원가입이 가능합니다.")
|
throw SodaException(messageKey = "member.validation.agree_required")
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicateCheckEmail(request.email)
|
duplicateCheckEmail(request.email)
|
||||||
@@ -160,14 +165,14 @@ class MemberService(
|
|||||||
requestString: String
|
requestString: String
|
||||||
): SignUpResponse {
|
): SignUpResponse {
|
||||||
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val request = objectMapper.readValue(requestString, SignUpRequest::class.java)
|
val request = objectMapper.readValue(requestString, SignUpRequest::class.java)
|
||||||
if (!request.isAgreePrivacyPolicy || !request.isAgreeTermsOfService) {
|
if (!request.isAgreePrivacyPolicy || !request.isAgreeTermsOfService) {
|
||||||
throw SodaException("약관에 동의하셔야 회원가입이 가능합니다.")
|
throw SodaException(messageKey = "member.validation.agree_required")
|
||||||
}
|
}
|
||||||
|
|
||||||
validatePassword(request.password)
|
validatePassword(request.password)
|
||||||
@@ -187,14 +192,14 @@ class MemberService(
|
|||||||
|
|
||||||
fun login(request: LoginRequest): ApiResponse<LoginResponse> {
|
fun login(request: LoginRequest): ApiResponse<LoginResponse> {
|
||||||
return ApiResponse.ok(
|
return ApiResponse.ok(
|
||||||
message = "로그인 되었습니다.",
|
message = messageSource.getMessage("member.login.success", langContext.lang),
|
||||||
data = login(request.email, request.password, request.isAdmin, request.isCreator)
|
data = login(request.email, request.password, request.isAdmin, request.isCreator)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMember(id: Long, container: String): ProfileResponse {
|
fun getMember(id: Long, container: String): ProfileResponse {
|
||||||
val member = repository.findByIdOrNull(id)
|
val member = repository.findByIdOrNull(id)
|
||||||
?: throw SodaException("없는 사용자 입니다.")
|
?: throw SodaException(messageKey = "member.validation.user_not_found")
|
||||||
|
|
||||||
return ProfileResponse(member, cloudFrontHost, container)
|
return ProfileResponse(member, cloudFrontHost, container)
|
||||||
}
|
}
|
||||||
@@ -202,9 +207,9 @@ class MemberService(
|
|||||||
fun getMemberInfo(member: Member, container: String): GetMemberInfoResponse {
|
fun getMemberInfo(member: Member, container: String): GetMemberInfoResponse {
|
||||||
val gender = if (member.auth != null) {
|
val gender = if (member.auth != null) {
|
||||||
if (member.auth!!.gender == 1) {
|
if (member.auth!!.gender == 1) {
|
||||||
"남"
|
messageSource.getMessage("member.gender.male", langContext.lang)
|
||||||
} else {
|
} else {
|
||||||
"여"
|
messageSource.getMessage("member.gender.female", langContext.lang)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
@@ -260,7 +265,7 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun updateAdid(memberId: Long, adid: String) {
|
fun updateAdid(memberId: Long, adid: String) {
|
||||||
val member = repository.findByIdOrNull(id = memberId)
|
val member = repository.findByIdOrNull(id = memberId)
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
member.adid = adid
|
member.adid = adid
|
||||||
}
|
}
|
||||||
@@ -305,28 +310,24 @@ class MemberService(
|
|||||||
isAdmin: Boolean = false,
|
isAdmin: Boolean = false,
|
||||||
isCreator: Boolean = false
|
isCreator: Boolean = false
|
||||||
): LoginResponse {
|
): LoginResponse {
|
||||||
val member = repository.findByEmail(email = email) ?: throw SodaException("없는 계정입니다.")
|
val member = repository.findByEmail(email = email)
|
||||||
|
?: throw SodaException(messageKey = "member.validation.account_not_found")
|
||||||
|
|
||||||
if (!member.isActive) {
|
if (!member.isActive) {
|
||||||
throw SodaException("탈퇴한 계정입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
throw SodaException(messageKey = "member.validation.inactive_account")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member.provider != MemberProvider.EMAIL) {
|
if (member.provider != MemberProvider.EMAIL) {
|
||||||
val provider = when (member.provider) {
|
val provider = resolveProviderLabel(member.provider)
|
||||||
MemberProvider.APPLE -> "애플"
|
throw SodaException(message = formatMessage("member.validation.email_registered_with_provider", provider))
|
||||||
MemberProvider.GOOGLE -> "구글"
|
|
||||||
else -> "카카오"
|
|
||||||
}
|
|
||||||
|
|
||||||
throw SodaException("해당 이메일은 $provider 계정으로 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCreator && member.role != MemberRole.CREATOR) {
|
if (isCreator && member.role != MemberRole.CREATOR) {
|
||||||
throw SodaException("로그인 정보를 확인해주세요.")
|
throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdmin && member.role != MemberRole.ADMIN) {
|
if (isAdmin && member.role != MemberRole.ADMIN) {
|
||||||
throw SodaException("로그인 정보를 확인해주세요.")
|
throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
val authenticationToken = UsernamePasswordAuthenticationToken(email, password)
|
val authenticationToken = UsernamePasswordAuthenticationToken(email, password)
|
||||||
@@ -414,22 +415,17 @@ class MemberService(
|
|||||||
|
|
||||||
if (findMember != null) {
|
if (findMember != null) {
|
||||||
if (findMember.provider == MemberProvider.EMAIL) {
|
if (findMember.provider == MemberProvider.EMAIL) {
|
||||||
throw SodaException("이미 사용중인 이메일 입니다.", "email")
|
throw SodaException(messageKey = "member.validation.email_in_use", errorProperty = "email")
|
||||||
} else {
|
} else {
|
||||||
val provider = when (findMember.provider) {
|
val provider = resolveProviderLabel(findMember.provider)
|
||||||
MemberProvider.APPLE -> "애플"
|
|
||||||
MemberProvider.GOOGLE -> "구글"
|
|
||||||
else -> "카카오"
|
|
||||||
}
|
|
||||||
|
|
||||||
throw SodaException(
|
throw SodaException(
|
||||||
"해당 이메일은 $provider 계정으로 이미 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.",
|
message = formatMessage("member.validation.email_registered_with_provider_already", provider),
|
||||||
"email"
|
errorProperty = "email"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApiResponse.ok(message = "사용 가능한 이메일 입니다.")
|
return ApiResponse.ok(message = messageSource.getMessage("member.validation.email_available", langContext.lang))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateEmail(email: String) {
|
private fun validateEmail(email: String) {
|
||||||
@@ -441,8 +437,9 @@ class MemberService(
|
|||||||
|
|
||||||
fun duplicateCheckNickname(nickname: String): ApiResponse<Any> {
|
fun duplicateCheckNickname(nickname: String): ApiResponse<Any> {
|
||||||
validateNickname(nickname)
|
validateNickname(nickname)
|
||||||
repository.findByNickname(nickname)?.let { throw SodaException("이미 사용중인 닉네임 입니다.", "nickname") }
|
repository.findByNickname(nickname)
|
||||||
return ApiResponse.ok(message = "사용 가능한 닉네임 입니다.")
|
?.let { throw SodaException(messageKey = "member.validation.nickname_in_use", errorProperty = "nickname") }
|
||||||
|
return ApiResponse.ok(message = messageSource.getMessage("member.validation.nickname_available", langContext.lang))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateNickname(nickname: String) {
|
private fun validateNickname(nickname: String) {
|
||||||
@@ -469,8 +466,10 @@ class MemberService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (creatorFollowing == null) {
|
if (creatorFollowing == null) {
|
||||||
val creator = repository.findByIdOrNull(creatorId) ?: throw SodaException("크리에이터 정보를 확인해주세요.")
|
val creator = repository.findByIdOrNull(creatorId)
|
||||||
val member = repository.findByIdOrNull(memberId) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "member.validation.creator_not_found")
|
||||||
|
val member = repository.findByIdOrNull(memberId)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
val newCreatorFollowing = CreatorFollowing()
|
val newCreatorFollowing = CreatorFollowing()
|
||||||
newCreatorFollowing.member = member
|
newCreatorFollowing.member = member
|
||||||
@@ -514,10 +513,10 @@ class MemberService(
|
|||||||
|
|
||||||
if (blockMember == null) {
|
if (blockMember == null) {
|
||||||
val blockedMember = repository.findByIdOrNull(id = request.blockMemberId)
|
val blockedMember = repository.findByIdOrNull(id = request.blockMemberId)
|
||||||
?: throw SodaException("잘못된 요청입니다.")
|
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
|
||||||
val member = repository.findByIdOrNull(id = memberId)
|
val member = repository.findByIdOrNull(id = memberId)
|
||||||
?: throw SodaException("잘못된 요청입니다.")
|
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
|
||||||
blockMember = BlockMember()
|
blockMember = BlockMember()
|
||||||
blockMember.member = member
|
blockMember.member = member
|
||||||
@@ -545,7 +544,7 @@ class MemberService(
|
|||||||
|
|
||||||
fun searchMember(nickname: String, member: Member): List<GetRoomDetailUser> {
|
fun searchMember(nickname: String, member: Member): List<GetRoomDetailUser> {
|
||||||
if (nickname.length < 2) {
|
if (nickname.length < 2) {
|
||||||
throw SodaException("두 글자 이상 입력 하셔야 합니다.")
|
throw SodaException(messageKey = "member.validation.nickname_min_length")
|
||||||
}
|
}
|
||||||
|
|
||||||
return repository.findByNicknameAndOtherCondition(nickname, member)
|
return repository.findByNicknameAndOtherCondition(nickname, member)
|
||||||
@@ -560,7 +559,7 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun logout(token: String, memberId: Long) {
|
fun logout(token: String, memberId: Long) {
|
||||||
val member = repository.findByIdOrNull(memberId)
|
val member = repository.findByIdOrNull(memberId)
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
member.pushToken = null
|
member.pushToken = null
|
||||||
pushTokenService.logout(memberId = memberId)
|
pushTokenService.logout(memberId = memberId)
|
||||||
@@ -568,7 +567,7 @@ class MemberService(
|
|||||||
val lock = getOrCreateLock(memberId = memberId)
|
val lock = getOrCreateLock(memberId = memberId)
|
||||||
lock.write {
|
lock.write {
|
||||||
val memberToken = tokenRepository.findByIdOrNull(memberId)
|
val memberToken = tokenRepository.findByIdOrNull(memberId)
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
memberToken.tokenSet.remove(token)
|
memberToken.tokenSet.remove(token)
|
||||||
tokenRepository.save(memberToken)
|
tokenRepository.save(memberToken)
|
||||||
@@ -578,7 +577,7 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun logoutAll(memberId: Long) {
|
fun logoutAll(memberId: Long) {
|
||||||
val member = repository.findByIdOrNull(memberId)
|
val member = repository.findByIdOrNull(memberId)
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
member.pushToken = null
|
member.pushToken = null
|
||||||
pushTokenService.logout(memberId = memberId)
|
pushTokenService.logout(memberId = memberId)
|
||||||
@@ -589,16 +588,17 @@ class MemberService(
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun signOut(signOutRequest: SignOutRequest, user: User) {
|
fun signOut(signOutRequest: SignOutRequest, user: User) {
|
||||||
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = repository.findByEmail(user.username)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
if (
|
if (
|
||||||
member.provider == MemberProvider.EMAIL &&
|
member.provider == MemberProvider.EMAIL &&
|
||||||
!passwordEncoder.matches(signOutRequest.password, member.password)
|
!passwordEncoder.matches(signOutRequest.password, member.password)
|
||||||
) {
|
) {
|
||||||
throw SodaException("비밀번호가 일치하지 않습니다.")
|
throw SodaException(messageKey = "member.validation.password_mismatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signOutRequest.reason.isBlank()) {
|
if (signOutRequest.reason.isBlank()) {
|
||||||
throw SodaException("탈퇴하려는 이유를 입력해 주세요.")
|
throw SodaException(messageKey = "member.validation.signout_reason_required")
|
||||||
}
|
}
|
||||||
|
|
||||||
logoutAll(memberId = member.id!!)
|
logoutAll(memberId = member.id!!)
|
||||||
@@ -617,15 +617,16 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun updateNickname(profileUpdateRequest: ProfileUpdateRequest, user: User) {
|
fun updateNickname(profileUpdateRequest: ProfileUpdateRequest, user: User) {
|
||||||
if (profileUpdateRequest.email != user.username) {
|
if (profileUpdateRequest.email != user.username) {
|
||||||
throw SodaException("로그인 정보를 확인해 주세요.")
|
throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = repository.findByEmail(user.username)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
if (profileUpdateRequest.nickname != null) {
|
if (profileUpdateRequest.nickname != null) {
|
||||||
validateNickname(profileUpdateRequest.nickname)
|
validateNickname(profileUpdateRequest.nickname)
|
||||||
repository.findByNickname(profileUpdateRequest.nickname)
|
repository.findByNickname(profileUpdateRequest.nickname)
|
||||||
?.let { throw SodaException("이미 사용중인 닉네임 입니다.") }
|
?.let { throw SodaException(messageKey = "member.validation.nickname_in_use") }
|
||||||
|
|
||||||
val price = repository.getChangeNicknamePrice(memberId = member.id!!).price
|
val price = repository.getChangeNicknamePrice(memberId = member.id!!).price
|
||||||
if (price > 0) {
|
if (price > 0) {
|
||||||
@@ -648,17 +649,18 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun profileUpdate(profileUpdateRequest: ProfileUpdateRequest, user: User): ProfileResponse {
|
fun profileUpdate(profileUpdateRequest: ProfileUpdateRequest, user: User): ProfileResponse {
|
||||||
if (profileUpdateRequest.email != user.username) {
|
if (profileUpdateRequest.email != user.username) {
|
||||||
throw SodaException("로그인 정보를 확인해 주세요.")
|
throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = repository.findByEmail(user.username)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
if (profileUpdateRequest.modifyPassword != null) {
|
if (profileUpdateRequest.modifyPassword != null) {
|
||||||
if (passwordEncoder.matches(profileUpdateRequest.password, member.password)) {
|
if (passwordEncoder.matches(profileUpdateRequest.password, member.password)) {
|
||||||
validatePassword(profileUpdateRequest.modifyPassword)
|
validatePassword(profileUpdateRequest.modifyPassword)
|
||||||
member.password = passwordEncoder.encode(profileUpdateRequest.modifyPassword)
|
member.password = passwordEncoder.encode(profileUpdateRequest.modifyPassword)
|
||||||
} else {
|
} else {
|
||||||
throw SodaException("비밀번호가 일치하지 않습니다.")
|
throw SodaException(messageKey = "member.validation.password_mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,7 +671,7 @@ class MemberService(
|
|||||||
if (profileUpdateRequest.nickname != null) {
|
if (profileUpdateRequest.nickname != null) {
|
||||||
validateNickname(profileUpdateRequest.nickname)
|
validateNickname(profileUpdateRequest.nickname)
|
||||||
repository.findByNickname(profileUpdateRequest.nickname)
|
repository.findByNickname(profileUpdateRequest.nickname)
|
||||||
?.let { throw SodaException("이미 사용중인 닉네임 입니다.") }
|
?.let { throw SodaException(messageKey = "member.validation.nickname_in_use") }
|
||||||
member.nickname = profileUpdateRequest.nickname
|
member.nickname = profileUpdateRequest.nickname
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +725,8 @@ class MemberService(
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun profileImageUpdate(multipartFile: MultipartFile, user: User): String {
|
fun profileImageUpdate(multipartFile: MultipartFile, user: User): String {
|
||||||
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = repository.findByEmail(user.username)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
val metadata = ObjectMetadata()
|
val metadata = ObjectMetadata()
|
||||||
metadata.contentLength = multipartFile.size
|
metadata.contentLength = multipartFile.size
|
||||||
@@ -741,17 +744,11 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun forgotPassword(request: ForgotPasswordRequest) {
|
fun forgotPassword(request: ForgotPasswordRequest) {
|
||||||
val member = repository.getMemberByEmail(email = request.email)
|
val member = repository.getMemberByEmail(email = request.email)
|
||||||
?: throw SodaException("등록되지 않은 계정입니다.\n확인 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.unregistered_account_retry")
|
||||||
|
|
||||||
val provider = when (member.provider) {
|
if (member.provider != MemberProvider.EMAIL) {
|
||||||
MemberProvider.EMAIL -> "이메일"
|
val provider = resolveProviderLabel(member.provider)
|
||||||
MemberProvider.KAKAO -> "카카오"
|
throw SodaException(message = formatMessage("member.validation.email_registered_with_provider", provider))
|
||||||
MemberProvider.GOOGLE -> "구글"
|
|
||||||
MemberProvider.APPLE -> "애플"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (provider != "이메일") {
|
|
||||||
throw SodaException("해당 계정은 $provider 계정으로 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val password = generatePassword(12)
|
val password = generatePassword(12)
|
||||||
@@ -779,6 +776,21 @@ class MemberService(
|
|||||||
return repository.getMemberProfile(memberId, myMemberId)
|
return repository.getMemberProfile(memberId, myMemberId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun formatMessage(key: String, vararg args: Any): String {
|
||||||
|
val template = messageSource.getMessage(key, langContext.lang) ?: ""
|
||||||
|
return if (args.isEmpty()) template else String.format(template, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveProviderLabel(provider: MemberProvider): String {
|
||||||
|
val key = when (provider) {
|
||||||
|
MemberProvider.EMAIL -> "member.provider.email"
|
||||||
|
MemberProvider.KAKAO -> "member.provider.kakao"
|
||||||
|
MemberProvider.GOOGLE -> "member.provider.google"
|
||||||
|
MemberProvider.APPLE -> "member.provider.apple"
|
||||||
|
}
|
||||||
|
return messageSource.getMessage(key, langContext.lang) ?: provider.name
|
||||||
|
}
|
||||||
|
|
||||||
private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock {
|
private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock {
|
||||||
return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() }
|
return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() }
|
||||||
}
|
}
|
||||||
@@ -786,7 +798,7 @@ class MemberService(
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun updateMarketingInfo(memberId: Long, adid: String, pid: String): String? {
|
fun updateMarketingInfo(memberId: Long, adid: String, pid: String): String? {
|
||||||
val member = repository.findByIdOrNull(id = memberId)
|
val member = repository.findByIdOrNull(id = memberId)
|
||||||
?: throw SodaException("로그인 정보를 확인해주세요.")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
if (adid != member.adid) {
|
if (adid != member.adid) {
|
||||||
member.adid = adid
|
member.adid = adid
|
||||||
@@ -814,15 +826,15 @@ class MemberService(
|
|||||||
if (findMember.isActive) {
|
if (findMember.isActive) {
|
||||||
return MemberResolveResult(member = findMember, isNew = false)
|
return MemberResolveResult(member = findMember, isNew = false)
|
||||||
} else {
|
} else {
|
||||||
throw SodaException("탈퇴한 계정입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
throw SodaException(messageKey = "member.validation.inactive_account")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val email = googleUserInfo.email
|
val email = googleUserInfo.email
|
||||||
checkEmail(email)
|
checkEmail(email)
|
||||||
@@ -870,15 +882,15 @@ class MemberService(
|
|||||||
if (findMember.isActive) {
|
if (findMember.isActive) {
|
||||||
return MemberResolveResult(member = findMember, isNew = false)
|
return MemberResolveResult(member = findMember, isNew = false)
|
||||||
} else {
|
} else {
|
||||||
throw SodaException("탈퇴한 계정입니다.\n고객센터로 문의해 주시기 바랍니다.")
|
throw SodaException(messageKey = "member.validation.inactive_account")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
val stipulationTermsOfService = stipulationRepository.findByIdOrNull(StipulationIds.TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
val stipulationPrivacyPolicy = stipulationRepository.findByIdOrNull(StipulationIds.PRIVACY_POLICY_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
val email = kakaoUserInfo.email
|
val email = kakaoUserInfo.email
|
||||||
checkEmail(email)
|
checkEmail(email)
|
||||||
@@ -918,13 +930,8 @@ class MemberService(
|
|||||||
val member = repository.findByEmail(email)
|
val member = repository.findByEmail(email)
|
||||||
|
|
||||||
if (member != null) {
|
if (member != null) {
|
||||||
val provider = when (member.provider) {
|
val provider = resolveProviderLabel(member.provider)
|
||||||
MemberProvider.APPLE -> "애플"
|
throw SodaException(message = formatMessage("member.validation.email_registered_with_provider", provider))
|
||||||
MemberProvider.GOOGLE -> "구글"
|
|
||||||
else -> "카카오"
|
|
||||||
}
|
|
||||||
|
|
||||||
throw SodaException("해당 이메일은 $provider 계정으로 가입되어 있습니다. 해당 소셜 로그인을 사용해 주세요.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ class AuthController(
|
|||||||
@RequestBody request: AuthVerifyRequest,
|
@RequestBody request: AuthVerifyRequest,
|
||||||
@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")
|
||||||
|
|
||||||
val authenticateData = service.certificate(request, memberId = member.id!!)
|
val authenticateData = service.certificate(request, memberId = member.id!!)
|
||||||
|
|
||||||
if (service.isBlockAuth(authenticateData)) {
|
if (service.isBlockAuth(authenticateData)) {
|
||||||
service.signOut(member.id!!)
|
service.signOut(member.id!!)
|
||||||
throw SodaException("운영정책을 위반하여 이용을 제한합니다.")
|
throw SodaException(messageKey = "member.auth.blocked_policy")
|
||||||
}
|
}
|
||||||
|
|
||||||
val authResponse = service.authenticate(authenticateData, member.id!!)
|
val authResponse = service.authenticate(authenticateData, member.id!!)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package kr.co.vividnext.sodalive.member.auth
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import kr.co.bootpay.Bootpay
|
import kr.co.bootpay.Bootpay
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
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.MemberRepository
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
import kr.co.vividnext.sodalive.member.MemberService
|
import kr.co.vividnext.sodalive.member.MemberService
|
||||||
import kr.co.vividnext.sodalive.member.SignOut
|
import kr.co.vividnext.sodalive.member.SignOut
|
||||||
@@ -22,6 +24,8 @@ class AuthService(
|
|||||||
private val memberService: MemberService,
|
private val memberService: MemberService,
|
||||||
private val memberRepository: MemberRepository,
|
private val memberRepository: MemberRepository,
|
||||||
private val signOutRepository: SignOutRepository,
|
private val signOutRepository: SignOutRepository,
|
||||||
|
private val messageSource: SodaMessageSource,
|
||||||
|
private val langContext: LangContext,
|
||||||
|
|
||||||
@Value("\${bootpay.application-id}")
|
@Value("\${bootpay.application-id}")
|
||||||
private val bootpayApplicationId: String,
|
private val bootpayApplicationId: String,
|
||||||
@@ -32,16 +36,16 @@ class AuthService(
|
|||||||
val bootpay = Bootpay(bootpayApplicationId, bootpayPrivateKey)
|
val bootpay = Bootpay(bootpayApplicationId, bootpayPrivateKey)
|
||||||
|
|
||||||
val authId = repository.getAuthIdByMemberId(memberId = memberId)
|
val authId = repository.getAuthIdByMemberId(memberId = memberId)
|
||||||
if (authId != null) throw SodaException("이미 인증된 계정입니다.")
|
if (authId != null) throw SodaException(messageKey = "member.auth.already_verified")
|
||||||
|
|
||||||
val certificateResult: AuthCertificateResult = try {
|
val certificateResult: AuthCertificateResult = try {
|
||||||
val token = bootpay.accessToken
|
val token = bootpay.accessToken
|
||||||
if (token["error_code"] != null) throw SodaException("인증정보에 오류가 있습니다.\n다시 시도해 주세요.")
|
if (token["error_code"] != null) throw SodaException(messageKey = "member.auth.certificate_invalid_retry")
|
||||||
|
|
||||||
val res = bootpay.certificate(request.receiptId)
|
val res = bootpay.certificate(request.receiptId)
|
||||||
objectMapper.convertValue(res, AuthCertificateResult::class.java)
|
objectMapper.convertValue(res, AuthCertificateResult::class.java)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw SodaException(e.message ?: "인증정보에 오류가 있습니다.\n다시 시도해 주세요.")
|
throw SodaException(messageKey = "member.auth.certificate_invalid_retry")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -51,7 +55,7 @@ class AuthService(
|
|||||||
) {
|
) {
|
||||||
return certificateResult.authenticateData
|
return certificateResult.authenticateData
|
||||||
} else {
|
} else {
|
||||||
throw SodaException("인증정보에 오류가 있습니다.\n다시 시도해 주세요.")
|
throw SodaException(messageKey = "member.auth.certificate_invalid_retry")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,11 +66,13 @@ class AuthService(
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun signOut(memberId: Long) {
|
fun signOut(memberId: Long) {
|
||||||
val member = memberRepository.findByIdOrNull(memberId) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = memberRepository.findByIdOrNull(memberId)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
member.isActive = false
|
member.isActive = false
|
||||||
member.nickname = "deleted_${member.nickname}"
|
member.nickname = "deleted_${member.nickname}"
|
||||||
|
|
||||||
val signOut = SignOut(reason = "운영정책을 위반하여 이용을 제한합니다.")
|
val signOutReason = messageSource.getMessage("member.auth.blocked_policy", langContext.lang) ?: ""
|
||||||
|
val signOut = SignOut(reason = signOutReason)
|
||||||
signOut.member = member
|
signOut.member = member
|
||||||
signOutRepository.save(signOut)
|
signOutRepository.save(signOut)
|
||||||
|
|
||||||
@@ -77,13 +83,14 @@ class AuthService(
|
|||||||
fun authenticate(certificate: AuthVerifyCertificate, memberId: Long): AuthResponse {
|
fun authenticate(certificate: AuthVerifyCertificate, memberId: Long): AuthResponse {
|
||||||
val memberIds = repository.getActiveMemberIdsByDi(di = certificate.di)
|
val memberIds = repository.getActiveMemberIdsByDi(di = certificate.di)
|
||||||
if (memberIds.size >= 3) {
|
if (memberIds.size >= 3) {
|
||||||
|
val message = messageSource.getMessage("member.auth.max_accounts", langContext.lang) ?: ""
|
||||||
throw SodaException(
|
throw SodaException(
|
||||||
"이미 본인인증한 계정 ${memberIds.size}개 이용중입니다.\n" +
|
message = String.format(message, memberIds.size)
|
||||||
"소다라이브의 본인인증은 최대 3개의 계정만 이용할 수 있습니다."
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val member = memberRepository.findByIdOrNull(memberId) ?: throw SodaException("로그인 정보를 확인해주세요.")
|
val member = memberRepository.findByIdOrNull(memberId)
|
||||||
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
val nowYear = LocalDate.now().year
|
val nowYear = LocalDate.now().year
|
||||||
val certificateYear = certificate.birth.substring(0, 4).toInt()
|
val certificateYear = certificate.birth.substring(0, 4).toInt()
|
||||||
if (nowYear - certificateYear >= 19) {
|
if (nowYear - certificateYear >= 19) {
|
||||||
@@ -99,7 +106,8 @@ class AuthService(
|
|||||||
repository.save(auth)
|
repository.save(auth)
|
||||||
return AuthResponse(gender = certificate.gender)
|
return AuthResponse(gender = certificate.gender)
|
||||||
} else {
|
} else {
|
||||||
throw SodaException("${nowYear - 19}년 1월 1일 이전 출생자만 본인인증이 가능합니다.")
|
val message = messageSource.getMessage("member.auth.age_limit", langContext.lang) ?: ""
|
||||||
|
throw SodaException(message = String.format(message, nowYear - 19))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class NicknameGenerateService(private val repository: MemberRepository) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw SodaException("회원가입을 하지 못했습니다.\n다시 시도해 주세요.")
|
throw SodaException(messageKey = "member.signup.failed_retry")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateUniqueNickname(): String {
|
fun generateUniqueNickname(): String {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class GoogleAuthService(
|
|||||||
pushToken: String?
|
pushToken: String?
|
||||||
): SocialLoginResponse {
|
): SocialLoginResponse {
|
||||||
val googleUserInfo = googleService.getUserInfo(idToken)
|
val googleUserInfo = googleService.getUserInfo(idToken)
|
||||||
?: throw SodaException("구글 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
?: throw SodaException(messageKey = "member.social.google_login_failed")
|
||||||
val memberResolveResult = memberService.findOrRegister(googleUserInfo, container, marketingPid, pushToken)
|
val memberResolveResult = memberService.findOrRegister(googleUserInfo, container, marketingPid, pushToken)
|
||||||
val member = memberResolveResult.member
|
val member = memberResolveResult.member
|
||||||
val principal = MemberAdapter(member)
|
val principal = MemberAdapter(member)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class GoogleService(
|
|||||||
|
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
val payload = token.payload
|
val payload = token.payload
|
||||||
val email = payload.email ?: throw SodaException("이메일 제공에 동의하셔야 서비스 이용이 가능합니다.")
|
val email = payload.email ?: throw SodaException(messageKey = "member.social.email_consent_required")
|
||||||
|
|
||||||
GoogleUserInfo(
|
GoogleUserInfo(
|
||||||
sub = payload.subject,
|
sub = payload.subject,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class KakaoAuthService(
|
|||||||
pushToken: String?
|
pushToken: String?
|
||||||
): SocialLoginResponse {
|
): SocialLoginResponse {
|
||||||
val kakaoUserInfo = kakaoService.getUserInfo(accessToken)
|
val kakaoUserInfo = kakaoService.getUserInfo(accessToken)
|
||||||
?: throw SodaException("카카오 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
?: throw SodaException(messageKey = "member.social.kakao_login_failed")
|
||||||
val memberResolveResult = memberService.findOrRegister(kakaoUserInfo, container, marketingPid, pushToken)
|
val memberResolveResult = memberService.findOrRegister(kakaoUserInfo, container, marketingPid, pushToken)
|
||||||
val member = memberResolveResult.member
|
val member = memberResolveResult.member
|
||||||
val principal = MemberAdapter(member)
|
val principal = MemberAdapter(member)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class KakaoService(
|
|||||||
val id = jsonNode.get("id").asLong()
|
val id = jsonNode.get("id").asLong()
|
||||||
val kakaoAccount = jsonNode.get("kakao_account")
|
val kakaoAccount = jsonNode.get("kakao_account")
|
||||||
val email = kakaoAccount?.get("email")?.asText()
|
val email = kakaoAccount?.get("email")?.asText()
|
||||||
?: throw SodaException("카카오 로그인을 하지 못했습니다. 다시 시도해 주세요")
|
?: throw SodaException(messageKey = "member.social.kakao_login_failed")
|
||||||
val properties = jsonNode.get("properties")
|
val properties = jsonNode.get("properties")
|
||||||
val nickname = properties?.get("nickname")?.asText()
|
val nickname = properties?.get("nickname")?.asText()
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ class StipulationService(private val repository: StipulationRepository) {
|
|||||||
|
|
||||||
fun getTermsOfService(): StipulationDto {
|
fun getTermsOfService(): StipulationDto {
|
||||||
val stipulation = repository.findByIdOrNull(TERMS_OF_SERVICE_ID)
|
val stipulation = repository.findByIdOrNull(TERMS_OF_SERVICE_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
return StipulationDto(stipulation)
|
return StipulationDto(stipulation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPrivacyPolicy(): StipulationDto {
|
fun getPrivacyPolicy(): StipulationDto {
|
||||||
val stipulation = repository.findByIdOrNull(PRIVACY_POLICY_ID)
|
val stipulation = repository.findByIdOrNull(PRIVACY_POLICY_ID)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
return StipulationDto(stipulation)
|
return StipulationDto(stipulation)
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ class StipulationService(private val repository: StipulationRepository) {
|
|||||||
@Transactional
|
@Transactional
|
||||||
fun modify(request: StipulationModifyRequest) {
|
fun modify(request: StipulationModifyRequest) {
|
||||||
val stipulation = repository.findByIdOrNull(request.id)
|
val stipulation = repository.findByIdOrNull(request.id)
|
||||||
?: throw SodaException("잘못된 요청입니다\n앱 종료 후 다시 시도해 주세요.")
|
?: throw SodaException(messageKey = "member.validation.invalid_request_retry")
|
||||||
|
|
||||||
stipulation.description = request.description
|
stipulation.description = request.description
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class MemberTagController(private val service: MemberTagService) {
|
|||||||
fun getTags(
|
fun getTags(
|
||||||
@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.getTags(member))
|
ApiResponse.ok(service.getTags(member))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user