From d1512f418fca4e04c7ae7a3f5a8fa7c787372404 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 6 Feb 2026 14:40:14 +0900 Subject: [PATCH 1/5] =?UTF-8?q?GetRoomInfoResponse=EC=97=90=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=20=EA=B4=80=EC=8B=AC=EC=82=AC=20tags=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/vividnext/sodalive/live/room/LiveRoomService.kt | 10 ++++++++-- .../sodalive/live/room/info/GetRoomInfoResponse.kt | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt index 691d6567..8e9c16cb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt @@ -522,10 +522,9 @@ class LiveRoomService( title = room.title, notice = room.notice, price = room.price, - tags = room.tags.asSequence() + tags = room.tags .filter { it.tag.isActive } .map { it.tag.tag } - .toList() .let { tags -> applyLanguageTagToRoomTags(room.member?.id, tags, languageTagByMemberId) }, numberOfParticipantsTotal = room.numberOfPeople, numberOfParticipants = 0, @@ -956,6 +955,12 @@ class LiveRoomService( } val menuPan = menuService.getLiveMenu(creatorId = room.member!!.id!!) + val languageTagByMemberId = buildLanguageTagMap(listOfNotNull(room.member?.id)) + + val tags = room.tags + .filter { it.tag.isActive } + .map { it.tag.tag } + .let { tags -> applyLanguageTagToRoomTags(room.member?.id, tags, languageTagByMemberId) } return GetRoomInfoResponse( roomId = roomId, @@ -993,6 +998,7 @@ class LiveRoomService( managerList = roomInfo.managerList, donationRankingTop3UserIds = donationRankingTop3UserIds, menuPan = menuPan?.menu ?: "", + tags = tags, isPrivateRoom = room.type == LiveRoomType.PRIVATE, password = room.password, isActiveRoulette = isActiveRoulette diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt index d4d536e8..f8f8b6d1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt @@ -20,6 +20,7 @@ data class GetRoomInfoResponse( val managerList: List, val donationRankingTop3UserIds: List, val menuPan: String, + val tags: List, val isPrivateRoom: Boolean = false, val password: String? = null, val isActiveRoulette: Boolean = false From 4a2a3cbbf8e02af9fe542d56b6782438bc251f6c Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 6 Feb 2026 19:46:57 +0900 Subject: [PATCH 2/5] =?UTF-8?q?GetRoomInfoResponse=EC=97=90=20v2v=20worker?= =?UTF-8?q?=EC=9A=A9=20rtm=20=ED=86=A0=ED=81=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/live/room/LiveRoomService.kt | 8 ++++++++ .../sodalive/live/room/info/GetRoomInfoResponse.kt | 1 + 2 files changed, 9 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt index 8e9c16cb..74cf8fa6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt @@ -928,6 +928,13 @@ class LiveRoomService( expireTimestamp.toInt() ) + val v2vWorkerRtmToken = rtmTokenBuilder.buildToken( + agoraAppId, + agoraAppCertificate, + "v2v-agent-${member.id!!}", + expireTimestamp.toInt() + ) + val isFollowing = explorerQueryRepository .getNotificationUserIds(room.member!!.id!!) .contains(member.id) @@ -982,6 +989,7 @@ class LiveRoomService( channelName = room.channelName!!, rtcToken = rtcToken, rtmToken = rtmToken, + v2vWorkerRtmToken = v2vWorkerRtmToken, creatorId = room.member!!.id!!, creatorNickname = room.member!!.nickname, creatorProfileUrl = if (room.member!!.profileImage != null) { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt index f8f8b6d1..b455b6ec 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt @@ -8,6 +8,7 @@ data class GetRoomInfoResponse( val channelName: String, val rtcToken: String, val rtmToken: String, + val v2vWorkerRtmToken: String, val creatorId: Long, val creatorNickname: String, val creatorProfileUrl: String, From 23c219c67226bf9a8c019f2a2793dadcb8e14883 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sun, 8 Feb 2026 16:02:32 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=98=95=EC=9A=A9=EC=82=AC=20=EB=B0=8F=20=EB=AA=85?= =?UTF-8?q?=EC=82=AC=20=EB=8B=A8=EC=96=B4=20=EB=AA=A9=EB=A1=9D=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NicknameGenerateService의 adjectives, nouns 리스트를 새로운 단어 목록으로 전체 교체한다. 형용사 140개, 명사 160개를 신규 단어로 구성한다. --- .../nickname/NicknameGenerateService.kt | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt index 77002bca..963a8d40 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt @@ -8,40 +8,40 @@ import kotlin.random.Random @Service class NicknameGenerateService(private val repository: MemberRepository) { private val adjectives = listOf( - "따뜻한", "은은한", "고요한", "푸른", "맑은", "강한", "평온한", "깊은", "고독한", - "거친", "빛바랜", "차가운", "꿈꾸는", "숨겨진", "고귀한", "깨어난", "끝없는", "청명한", - "어두운", "희미한", "선명한", "눈부신", "불타는", "차분한", "아련한", "선선한", "상쾌한", "온화한", - "포근한", "황금빛", "청량한", "시원한", "서늘한", "우아한", "단단한", "투명한", "가벼운", "조용한", - "화려한", "찬란한", "순수한", "흐릿한", "고결한", "달콤한", "무한한", "아득한", "화사한", "평안한", - "웅장한", "황홀한", "빛나는", "쓸쓸한", "청순한", "흐르는", "미묘한", "그윽한", "몽롱한", "청아한", - "섬세한", "촉촉한", "강렬한", "싱싱한", "부드런", "아늑한", "매운듯", "고운듯", "느린듯", "밝은듯", - "짧은듯", "달큰한", "깊은듯", "기쁜듯", "쌀쌀한", "무거운", "연한듯", "편안한", "깨끗한", "말끔한", - "뽀얀듯", "푸르른", "붉은듯", "노오란", "무딘듯", "살짝한", "상큼한", "시큰한", "고소한", "나긋한", - "화끈한", "중후한", "정겨운", "날렵한", "기묘한", "참신한", "담백한", "퉁명한", "꾸밈없", "소박한", - "뾰족한", "무심한", "도도한", "따끔한", "무난한", "단호한", "냉정한", "따스한", "유연한", "묵직한", - "나른한", "몽환적", "정돈된", "쾌활한", "날카론", "묘한듯", "예쁜듯", "뽀얗게", "다정한", "푸근한", - "애틋한", "낭만적", "건조한", "훈훈한", "섹시한", "정적인", "유쾌한", "멍한듯", "혼란한", "상냥한", - "뚜렷한", "신비한", "허전한", "그리운", "들뜬듯", "절실한", "반듯한", "반가운", "새하얀", "흐린듯", - "엄숙한", "깊잖은", "산뜻한", "낯선듯" + "활기찬", "명랑한", "씩씩한", "용감한", "지혜론", "슬기론", "넉넉한", "든든한", "알찬듯", + "빠른듯", "느긋한", "당당한", "솔직한", "진실한", "겸손한", "성실한", "꼼꼼한", "야무진", + "재빠른", "영리한", "총명한", "현명한", "착실한", "올곧은", "바른듯", "곧은듯", "힘찬듯", "굳센듯", + "의젓한", "점잖은", "듬직한", "너그런", "관대한", "인자한", "자애론", "헌신적", "열정적", "적극적", + "능숙한", "탁월한", "뛰어난", "출중한", "비범한", "특별한", "독특한", "개성적", "창의적", "혁신적", + "진취적", "도전적", "패기찬", "호쾌한", "시원찬", "통쾌한", "유능한", "민첩한", "기민한", "재치론", + "센스찬", "감각적", "세련된", "품격찬", "격조찬", "기품찬", "위풍찬", "늠름한", "씩씩찬", "호탕한", + "대범한", "거뜬한", "가뿐한", "홀가분", "산뜻찬", "깔끔찬", "정갈한", "단정한", "반짝찬", "영롱한", + "찬란찬", "눈부찬", "환한듯", "밝은찬", "빛깔찬", "색다른", "새로운", "신선찬", "풋풋한", "싱그런", + "생기찬", "발랄한", "경쾌한", "리듬찬", "율동적", "역동적", "활발한", "생동찬", "약동찬", "힘있는", + "건장한", "튼튼한", "건강한", "탄탄한", "단련된", "숙련된", "노련한", "원숙한", "성숙한", "완숙한", + "정확한", "치밀한", "정밀한", "철저한", "완벽찬", "흠없는", "나무랄", "빈틈없", "알뜰한", "꾸준한", + "한결찬", "변함없", "굳건한", "확고한", "견고한", "탄탄찬", "안정적", "평화론", "온유한", "자비론", + "배려찬", "사려찬", "깊숙한", "심오한", "오묘한", "현묘한", "신묘한", "경이론", "놀라운", "대단한", + "훌륭한", "멋스런", "근사한", "기특한" ) private val nouns = listOf( - "소리", "울림", "공명", "음색", "감성", "리듬", "바람", "늑대", "태양", "대지", - "강", "하늘", "불꽃", "별빛", "나무", "산", "달빛", "폭풍", "눈", "밤", - "노을", "물결", "노래", "파도", "구름", "사슴", "신비", "영혼", "선율", "평원", - "빛", "고래", "모래", "사자", "표범", "여우", "곰", "수달", "판다", "들소", - "까치", "매", "솔개", "물총새", "철새", "황새", "은어", "붕어", "산양", "담비", - "설표", "물개", "자라", "나비", "노루", "해마", "백조", "청어", "호수", "샘물", - "쿼카", "상어", "무드", "나노", "루프", "네온", "모아", "아토", "플로", "루미", - "도트", "비트", "토브", "온기", "클리", "위드", "제로", "베이", "미오", "시그", - "쿠나", "오로", "폴라", "바움", "포잇", "누아", "오브", "파인", "조이", "아뜰", - "티노", "소마", "하루", "밀크", "아린", "토로", "벨로", "위시", "뮤즈", "노블", - "카노", "미카", "하라", "엘로", "피오", "라임", "노이", "루다", "이브", "마리", - "블루", "시온", "레아", "도르", "하노", "네리", "키노", "쿠키", "라노", "수이", - "우노", "파루", "크리", "포유", "코코", "아라", "토리", "누리", "보노", "페어", - "리아", "모리", "세리", "리브", "헤이", "모카", "아이", "르네", "이로", "미노", - "다라", "노바", "디노", "오미", "카라", "니아", "루아", "네오", "하이", "레인", - "피카", "유카", "제니", "이든", "라비", "아벨", "솔라", "쿠로", "시라", "리코" + "다람쥐", "청설모", "두루미", "기러기", "올빼미", "부엉이", "딱따구", "꾀꼬리", "직박구", "동박새", + "참새", "종달새", "제비", "뻐꾸기", "앵무새", "공작새", "원앙", "두더지", "고슴도", "족제비", + "오소리", "수리부", "해오라", "갈매기", "펭귄", "코알라", "알파카", "카멜레", "이구아", "플라밍", + "돌고래", "해달", "라쿤", "미어캣", "친칠라", "햄스터", "기니피", "토끼", "강아지", "고양이", + "망아지", "송아지", "병아리", "올챙이", "개구리", "도롱뇽", "거북이", "앵무", "카나리", "비둘기", + "참매", "독수리", "콘도르", "벌새", "홍학", "타조", "키위새", "투칸", "앵콩이", "물까치", + "루비", "사파이", "에메랄", "자수정", "진주", "산호", "호박", "비취", "오팔", "토파즈", + "다이아", "크리스", "아쿠아", "코발트", "인디고", "라벤더", "마젠타", "터콰이", "세룰리", "버밀리", + "카푸치", "에스프", "아메리", "마키아", "바닐라", "캐러멜", "시나몬", "민트", "자스민", "캐모마", + "히비스", "라일락", "프리지", "튤립", "수선화", "동백", "매화", "목련", "벚꽃", "진달래", + "철쭉", "개나리", "무궁화", "해바라", "코스모", "달리아", "작약", "모란", "연꽃", "수련", + "클로버", "민들레", "제비꽃", "은방울", "안개꽃", "라넌큘", "아네모", "델피니", "글라디", "프로테", + "유칼립", "로즈마", "바질", "타임", "오레가", "세이지", "딜", "파슬리", "고수", "루꼴라", + "아보카", "블루베", "라즈베", "크랜베", "아사이", "망고", "파파야", "리치", "패션후", "구아바", + "석류", "무화과", "살구", "자두", "체리", "복숭아", "포도", "감귤", "유자", "한라봉", + "천혜향", "레드향", "금귤", "모과", "비파", "대추", "밤", "호두", "잣", "은행" ) private fun generateRandomNickname(): String { From 9779c1b50b83b31edc9f0b668bd265f4e6613891 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sun, 8 Feb 2026 16:15:51 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=EC=9D=BC=EB=B3=B8=EC=96=B4=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generateUniqueNickname에 Lang 파라미터를 추가하여 언어 설정이 일본어일 때 일본어 단어 조합으로 닉네임을 생성한다. --- .../sodalive/member/MemberService.kt | 10 +-- .../nickname/NicknameGenerateService.kt | 89 ++++++++++++++++--- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberService.kt index e92fdc3a..d6868bd0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/MemberService.kt @@ -130,7 +130,7 @@ class MemberService( duplicateCheckEmail(request.email) validatePassword(request.password) - val nickname = nicknameGenerateService.generateUniqueNickname() + val nickname = nicknameGenerateService.generateUniqueNickname(langContext.lang) val member = Member( email = request.email, password = passwordEncoder.encode(request.password), @@ -850,7 +850,7 @@ class MemberService( val email = googleUserInfo.email checkEmail(email) - val nickname = nicknameGenerateService.generateUniqueNickname() + val nickname = nicknameGenerateService.generateUniqueNickname(langContext.lang) val member = Member( googleId = googleUserInfo.sub, email = email, @@ -907,7 +907,7 @@ class MemberService( val email = kakaoUserInfo.email checkEmail(email) - val nickname = nicknameGenerateService.generateUniqueNickname() + val nickname = nicknameGenerateService.generateUniqueNickname(langContext.lang) val member = Member( kakaoId = kakaoUserInfo.id, email = email, @@ -964,7 +964,7 @@ class MemberService( val email = appleUserInfo.email checkEmail(email) - val nickname = nicknameGenerateService.generateUniqueNickname() + val nickname = nicknameGenerateService.generateUniqueNickname(langContext.lang) val member = Member( appleId = appleUserInfo.sub, email = email, @@ -1021,7 +1021,7 @@ class MemberService( val email = lineUserInfo.email checkEmail(email) - val nickname = nicknameGenerateService.generateUniqueNickname() + val nickname = nicknameGenerateService.generateUniqueNickname(langContext.lang) val member = Member( lineId = lineUserInfo.sub, email = email, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt index 963a8d40..1a0e90e1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/nickname/NicknameGenerateService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.member.nickname import kr.co.vividnext.sodalive.common.SodaException +import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.member.MemberRepository import org.springframework.stereotype.Service import kotlin.random.Random @@ -44,23 +45,85 @@ class NicknameGenerateService(private val repository: MemberRepository) { "천혜향", "레드향", "금귤", "모과", "비파", "대추", "밤", "호두", "잣", "은행" ) - private fun generateRandomNickname(): String { + private val jaAdjectives = listOf( + "元気な", "明るい", "優しい", "強い", "賢い", "穏やかな", "爽やかな", "楽しい", + "勇敢な", "素敵な", "可愛い", "美しい", "清らかな", "温かい", "輝く", "華やかな", + "凛とした", "朗らかな", "逞しい", "麗しい", "雅な", "粋な", "健やかな", "晴れやかな", + "鮮やかな", "煌めく", "微笑む", "誠実な", "丁寧な", "真っ直ぐな", "気高い", "聡明な", + "快活な", "軽やかな", "しなやかな", "伸びやかな", "瑞々しい", "初々しい", "艶やかな", "柔らかな", + "澄んだ", "静かな", "豊かな", "深い", "広い", "高い", "清い", "涼しい", + "眩しい", "暖かな", "和やかな", "安らかな", "のどかな", "ほがらかな", "すこやかな", "たくましい", + "ひたむきな", "まめな", "きらきらな", "ふわふわな", "にこにこな", "わくわくな", "すくすくな", "のびのびな", + "きりっとした", "はきはきな", "てきぱきな", "しっかりな", "どっしりな", "ゆったりな", "さっぱりな", "すっきりな", + "ぴかぴかな", "つやつやな", "さらさらな", "もちもちな", "ぷるぷるな", "ころころな", "ぽかぽかな", "そよそよな", + "堅実な", "勤勉な", "忠実な", "素直な", "謙虚な", "大胆な", "情熱的な", "積極的な", + "独創的な", "繊細な", "壮大な", "格調高い", "品のある", "風格ある", "趣のある", "奥深い", + "颯爽とした", "堂々とした", "悠々とした", "泰然とした", "毅然とした", "端正な", "清楚な", "典雅な", + "俊敏な", "機敏な", "敏捷な", "軽快な", "活発な", "溌剌とした", "生き生きな", "伸び伸びな", + "揺るぎない", "確かな", "頼もしい", "心強い", "力強い", "逞しき", "雄々しい", "凜々しい", + "慈しみの", "思いやりの", "気配りの", "心優しい", "情け深い", "懐の深い", "器の大きい", "包容力の" + ) + + private val jaNouns = listOf( + "うさぎ", "ねこ", "いぬ", "たぬき", "きつね", "しか", "りす", "ふくろう", + "つばめ", "すずめ", "ひばり", "うぐいす", "めじろ", "つる", "はと", "かもめ", + "いるか", "くじら", "らっこ", "ペンギン", "コアラ", "パンダ", "アルパカ", "ハムスター", + "かめ", "かえる", "ほたる", "ちょう", "とんぼ", "てんとう", "こねこ", "こいぬ", + "ひよこ", "こじか", "こぐま", "こうさぎ", "こりす", "こだぬき", "こぎつね", "こばと", + "さくら", "うめ", "もみじ", "つばき", "すみれ", "たんぽぽ", "ひまわり", "あじさい", + "コスモス", "ラベンダー", "チューリップ", "カーネーション", "バラ", "ユリ", "ダリア", "マーガレット", + "なでしこ", "あやめ", "ききょう", "はぎ", "ふじ", "ぼたん", "しゃくやく", "れんげ", + "ルビー", "サファイア", "エメラルド", "アメジスト", "パール", "オパール", "トパーズ", "ガーネット", + "ひかり", "そら", "うみ", "かぜ", "つき", "ほし", "にじ", "ゆめ", + "あかね", "みずき", "はるか", "あおい", "ひなた", "こはる", "いろは", "かなで", + "しずく", "つゆ", "あられ", "みぞれ", "こはく", "あかり", "ともしび", "かがやき", + "やまと", "みやび", "まこと", "ちはや", "あさひ", "ゆうひ", "あけぼの", "たそがれ", + "わかば", "あおば", "もえぎ", "ときわ", "さつき", "やよい", "きさらぎ", "むつき", + "抹茶", "桜餅", "団子", "大福", "最中", "羊羹", "煎餅", "饅頭", + "柚子", "梅干し", "味噌", "醤油", "わさび", "生姜", "山椒", "昆布", + "風鈴", "提灯", "扇子", "千鶴", "折鶴", "手毬", "万華鏡", "花火", + "雪うさぎ", "だるま", "こけし", "招き猫", "風車", "独楽", "竹とんぼ", "紙風船", + "朝露", "夕凪", "木漏れ日", "花吹雪", "月明かり", "星空", "天の川", "春風", + "小春日和", "花曇り", "薄紅", "若草", "深緑", "紺碧", "茜色", "藤色" + ) + + private fun getAdjectives(lang: Lang): List = when (lang) { + Lang.JA -> jaAdjectives + else -> adjectives + } + + private fun getNouns(lang: Lang): List = when (lang) { + Lang.JA -> jaNouns + else -> nouns + } + + private fun getParticle(lang: Lang): String = when (lang) { + Lang.JA -> "の" + else -> "의" + } + + private fun generateRandomNickname(lang: Lang): String { + val adj = getAdjectives(lang) + val noun = getNouns(lang) + val particle = getParticle(lang) val formatType = Random.nextInt(3) return when (formatType) { - 0 -> "${adjectives.random()}${nouns.random()}" - 1 -> "${nouns.random()}의${nouns.random()}" - else -> "${adjectives.random()}${nouns.random()}의${nouns.random()}" + 0 -> "${adj.random()}${noun.random()}" + 1 -> "${noun.random()}${particle}${noun.random()}" + else -> "${adj.random()}${noun.random()}${particle}${noun.random()}" } } - private fun generateNonConflictingNickname(usedNicknames: Set): String { - val usedNicknameSet = HashSet(usedNicknames) // 해시셋으로 변환 (O(1) 조회 가능) + private fun generateNonConflictingNickname(usedNicknames: Set, lang: Lang): String { + val usedNicknameSet = HashSet(usedNicknames) val availableNumbers = (1000..9999).shuffled() + val adj = getAdjectives(lang) + val noun = getNouns(lang) - for (num in availableNumbers) { // 숫자를 먼저 결정 (무작위) - for (adj in adjectives.shuffled()) { // 형용사 순서 랜덤화 - for (noun in nouns.shuffled()) { // 명사 순서 랜덤화 - val candidate = "$adj$noun$num" + for (num in availableNumbers) { + for (a in adj.shuffled()) { + for (n in noun.shuffled()) { + val candidate = "$a$n$num" if (!usedNicknameSet.contains(candidate)) { return candidate } @@ -70,13 +133,13 @@ class NicknameGenerateService(private val repository: MemberRepository) { throw SodaException(messageKey = "member.signup.failed_retry") } - fun generateUniqueNickname(): String { + fun generateUniqueNickname(lang: Lang = Lang.KO): String { repeat(5) { - val candidates = (1..10).map { generateRandomNickname() } + val candidates = (1..10).map { generateRandomNickname(lang) } val available = candidates.firstOrNull { !repository.existsByNickname(it) } if (available != null) return available } - return generateNonConflictingNickname(repository.findNicknamesWithPrefix("").toSet()) + return generateNonConflictingNickname(repository.findNicknamesWithPrefix("").toSet(), lang) } } From 37d2e0de73d32d603e1e8dca8ce7baee44ac73ed Mon Sep 17 00:00:00 2001 From: Klaus Date: Sun, 8 Feb 2026 16:26:28 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EC=9D=BC=EB=B3=84=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EC=88=98=EC=97=90=20=EC=95=A0=ED=94=8C=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=EC=9C=BC=EB=A1=9C=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/AdminMemberStatisticsRepository.kt | 32 +++++++++++++++++++ .../member/AdminMemberStatisticsService.kt | 11 +++++++ .../member/GetMemberStatisticsResponse.kt | 2 ++ 3 files changed, 45 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsRepository.kt index 66650292..0cb7868d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsRepository.kt @@ -68,6 +68,19 @@ class AdminMemberStatisticsRepository(private val queryFactory: JPAQueryFactory) .size } + fun getTotalSignUpAppleCount(startDate: LocalDateTime, endDate: LocalDateTime): Int { + return queryFactory + .select(member.id) + .from(member) + .where( + member.createdAt.goe(startDate), + member.createdAt.loe(endDate), + member.provider.eq(MemberProvider.APPLE) + ) + .fetch() + .size + } + fun getTotalSignUpLineCount(startDate: LocalDateTime, endDate: LocalDateTime): Int { return queryFactory .select(member.id) @@ -202,6 +215,25 @@ class AdminMemberStatisticsRepository(private val queryFactory: JPAQueryFactory) .fetch() } + fun getSignUpAppleCountInRange(startDate: LocalDateTime, endDate: LocalDateTime): List { + return queryFactory + .select( + QDateAndMemberCount( + getFormattedDate(member.createdAt), + member.id.countDistinct().castToNum(Int::class.java) + ) + ) + .from(member) + .where( + member.createdAt.goe(startDate), + member.createdAt.loe(endDate), + member.provider.eq(MemberProvider.APPLE) + ) + .groupBy(getFormattedDate(member.createdAt)) + .orderBy(getFormattedDate(member.createdAt).desc()) + .fetch() + } + fun getSignUpLineCountInRange(startDate: LocalDateTime, endDate: LocalDateTime): List { return queryFactory .select( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsService.kt index 67906d64..9353d65f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/AdminMemberStatisticsService.kt @@ -58,6 +58,10 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics startDate = startDateTime, endDate = endDateTime ) + val totalSignUpAppleCount = repository.getTotalSignUpAppleCount( + startDate = startDateTime, + endDate = endDateTime + ) val totalSignUpLineCount = repository.getTotalSignUpLineCount( startDate = startDateTime, endDate = endDateTime @@ -96,6 +100,11 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics endDate = endDateTime ).associateBy({ it.date }, { it.memberCount }) + val signUpAppleCountInRange = repository.getSignUpAppleCountInRange( + startDate = startDateTime, + endDate = endDateTime + ).associateBy({ it.date }, { it.memberCount }) + val signUpLineCountInRange = repository.getSignUpLineCountInRange( startDate = startDateTime, endDate = endDateTime @@ -130,6 +139,7 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics signUpEmailCount = signUpEmailCountInRange[date] ?: 0, signUpKakaoCount = signUpKakaoCountInRange[date] ?: 0, signUpGoogleCount = signUpGoogleCountInRange[date] ?: 0, + signUpAppleCount = signUpAppleCountInRange[date] ?: 0, signUpLineCount = signUpLineCountInRange[date] ?: 0, signOutCount = signOutCountInRange[date] ?: 0, paymentMemberCount = paymentMemberCountInRangeMap[date] ?: 0 @@ -144,6 +154,7 @@ class AdminMemberStatisticsService(private val repository: AdminMemberStatistics totalSignUpEmailCount = totalSignUpEmailCount, totalSignUpKakaoCount = totalSignUpKakaoCount, totalSignUpGoogleCount = totalSignUpGoogleCount, + totalSignUpAppleCount = totalSignUpAppleCount, totalSignUpLineCount = totalSignUpLineCount, totalSignOutCount = totalSignOutCount, totalPaymentMemberCount = totalPaymentMemberCount, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/GetMemberStatisticsResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/GetMemberStatisticsResponse.kt index 1558d86f..3286c47b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/GetMemberStatisticsResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/statistics/member/GetMemberStatisticsResponse.kt @@ -7,6 +7,7 @@ data class GetMemberStatisticsResponse( val totalSignUpEmailCount: Int, val totalSignUpKakaoCount: Int, val totalSignUpGoogleCount: Int, + val totalSignUpAppleCount: Int, val totalSignUpLineCount: Int, val totalSignOutCount: Int, val totalPaymentMemberCount: Int, @@ -20,6 +21,7 @@ data class GetMemberStatisticsItem( val signUpEmailCount: Int, val signUpKakaoCount: Int, val signUpGoogleCount: Int, + val signUpAppleCount: Int, val signUpLineCount: Int, val signOutCount: Int, val paymentMemberCount: Int