From f05f146c89f79b93e55ae435c7fae8828b19e05e Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 11 Sep 2025 12:35:16 +0900 Subject: [PATCH] =?UTF-8?q?fix(chat-quota):=20=EC=9C=A0=EB=A3=8C=20?= =?UTF-8?q?=EC=B0=A8=EA=B0=90=20=ED=9B=84=20=EB=AC=B4=EB=A3=8C=C2=B7?= =?UTF-8?q?=EC=9C=A0=EB=A3=8C=20=EB=8F=99=EC=8B=9C=200=EC=9D=BC=20?= =?UTF-8?q?=EB=95=8C=20next=5Frecharge=5Fat=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 문제: 유료 잔여가 있을 때 유료 우선 차감 경로에서 `next_recharge_at` 설정 분기가 없어, 무료/유료가 동시에 0이 되는 경우 다음 무료 충전 시점이 노출되지 않음 - 수정: `ChatRoomQuotaService.consumeOneForSend`의 유료 차감 분기에 `remainingPaid==0 && remainingFree==0 && nextRechargeAt==null` 조건에서 `now + 6h`로 `next_recharge_at`을 설정하도록 로직 추가 - 참고: 무료 차감 경로의 `next_recharge_at` 설정 및 입장 시 lazy refill 동작은 기존과 동일 --- .../chat/quota/room/ChatRoomQuotaService.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/quota/room/ChatRoomQuotaService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/quota/room/ChatRoomQuotaService.kt index db24ea6..f3db435 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/quota/room/ChatRoomQuotaService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/quota/room/ChatRoomQuotaService.kt @@ -86,6 +86,10 @@ class ChatRoomQuotaService( // 1) 유료 우선 사용: 글로벌에 영향 없음 if (quota.remainingPaid > 0) { quota.remainingPaid -= 1 + // 유료 차감 후, 무료와 유료가 모두 0이 되는 시점이면 다음 무료 충전을 예약한다. + if (quota.remainingPaid == 0 && quota.remainingFree == 0 && quota.nextRechargeAt == null) { + quota.nextRechargeAt = now.plus(Duration.ofHours(6)).toEpochMilli() + } val total = calculateAvailableForRoom(globalFreeProvider(), quota.remainingFree, quota.remainingPaid) return RoomQuotaStatus(total, quota.nextRechargeAt, quota.remainingFree, quota.remainingPaid) } @@ -94,16 +98,16 @@ class ChatRoomQuotaService( val globalFree = globalFreeProvider() if (globalFree <= 0) { // 전송 차단: 글로벌 무료가 0이며 유료도 0 → 전송 불가 - throw SodaException("무료 쿼터가 소진되었습니다. 글로벌 무료 충전 이후 이용해 주세요.") + throw SodaException("오늘의 무료 채팅이 모두 소진되었습니다. 내일 다시 이용해 주세요.") } if (quota.remainingFree <= 0) { // 전송 차단: 룸 무료가 0이며 유료도 0 → 전송 불가 val waitMillis = quota.nextRechargeAt - if (waitMillis != null && waitMillis > nowMillis) { - throw SodaException("채팅방 무료 쿼터가 소진되었습니다. 무료 충전 이후 이용해 주세요.") - } else { - throw SodaException("채팅방 무료 쿼터가 소진되었습니다. 잠시 후 다시 시도해 주세요.") + if (waitMillis == null) { + quota.nextRechargeAt = now.plus(Duration.ofHours(6)).toEpochMilli() } + + throw SodaException("무료 채팅이 모두 소진되었습니다.") } // 둘 다 가능 → 차감