diff --git a/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepository.kt b/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepository.kt index ca33651d4..ff6ec1d23 100644 --- a/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepository.kt +++ b/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepository.kt @@ -1,7 +1,6 @@ package com.ku_stacks.ku_ring.ai.repository import com.ku_stacks.ku_ring.domain.KuringBotMessage -import java.time.LocalDate interface KuringBotRepository { /** @@ -41,11 +40,9 @@ interface KuringBotRepository { suspend fun insertMessage(message: KuringBotMessage) /** - * 지정된 기간 동안에 전송한 질문의 수를 반환한다. + * 남은 질문 횟수를 반환한다. * - * @param from 기간의 시작 - * @param to 기간의 끝 - * @return [from]부터 [to]까지 전송한 질문의 수 (inclusive) + * @param token 사용자의 FCM 토큰 */ - suspend fun getQueryCount(from: LocalDate, to: LocalDate): Int + suspend fun getQueryCount(token: String): Int } \ No newline at end of file diff --git a/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepositoryImpl.kt b/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepositoryImpl.kt index d0efe6c90..23b176a4d 100644 --- a/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepositoryImpl.kt +++ b/data/ai/src/main/java/com/ku_stacks/ku_ring/ai/repository/KuringBotRepositoryImpl.kt @@ -5,13 +5,13 @@ import com.ku_stacks.ku_ring.ai.mapper.toEntity import com.ku_stacks.ku_ring.domain.KuringBotMessage import com.ku_stacks.ku_ring.local.room.KuringBotMessageDao import com.ku_stacks.ku_ring.remote.kuringbot.KuringBotClient -import com.ku_stacks.ku_ring.util.toEpochSecond -import java.time.LocalDate +import com.ku_stacks.ku_ring.remote.user.UserClient import javax.inject.Inject class KuringBotRepositoryImpl @Inject constructor( private val kuringBotClient: KuringBotClient, private val kuringBotMessageDao: KuringBotMessageDao, + private val userClient: UserClient, ) : KuringBotRepository { override suspend fun openKuringBotSession( @@ -38,8 +38,8 @@ class KuringBotRepositoryImpl @Inject constructor( kuringBotMessageDao.insertMessage(message.toEntity()) } - override suspend fun getQueryCount(from: LocalDate, to: LocalDate): Int { - return kuringBotMessageDao.getQueryCount(from.toEpochSecond(), to.toEpochSecond()) + override suspend fun getQueryCount(token: String): Int { + return userClient.getKuringBotQueryCount(token) } companion object { diff --git a/data/ai/src/test/java/com/ku_stacks/ku_ring/ai/KuringBotRepositoryImplTest.kt b/data/ai/src/test/java/com/ku_stacks/ku_ring/ai/KuringBotRepositoryImplTest.kt index 44ff3ee77..043156dc2 100644 --- a/data/ai/src/test/java/com/ku_stacks/ku_ring/ai/KuringBotRepositoryImplTest.kt +++ b/data/ai/src/test/java/com/ku_stacks/ku_ring/ai/KuringBotRepositoryImplTest.kt @@ -4,6 +4,7 @@ import com.ku_stacks.ku_ring.ai.repository.KuringBotRepository import com.ku_stacks.ku_ring.ai.repository.KuringBotRepositoryImpl import com.ku_stacks.ku_ring.local.room.KuringBotMessageDao import com.ku_stacks.ku_ring.remote.kuringbot.KuringBotClient +import com.ku_stacks.ku_ring.remote.user.UserClient import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -13,13 +14,14 @@ import org.mockito.exceptions.base.MockitoException class KuringBotRepositoryImplTest { private val kuringBotClient = Mockito.mock(KuringBotClient::class.java) private val dao = Mockito.mock(KuringBotMessageDao::class.java) + private val userClient = Mockito.mock(UserClient::class.java) private lateinit var repository: KuringBotRepository private val query = "교내,외 장학금 및 학자금 대출 관련 전화번호들을 안내를 해줘" @Before fun setup() { - repository = KuringBotRepositoryImpl(kuringBotClient, dao) + repository = KuringBotRepositoryImpl(kuringBotClient, dao, userClient) } @Test diff --git a/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserClient.kt b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserClient.kt index 2d986c30e..745dfbe62 100644 --- a/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserClient.kt +++ b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserClient.kt @@ -20,4 +20,10 @@ class UserClient @Inject constructor( suspend fun registerUser(token: String): DefaultResponse = userService.registerUser(RegisterUserRequest(token)) + + suspend fun getKuringBotQueryCount(token: String): Int { + return runCatching { + userService.getKuringBotQueryCount(token).data.leftAskCount + }.getOrElse { 0 } + } } \ No newline at end of file diff --git a/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserService.kt b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserService.kt index db3bd25b6..9c0a1c90a 100644 --- a/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserService.kt +++ b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/UserService.kt @@ -2,8 +2,10 @@ package com.ku_stacks.ku_ring.remote.user import com.ku_stacks.ku_ring.remote.user.request.FeedbackRequest import com.ku_stacks.ku_ring.remote.user.request.RegisterUserRequest +import com.ku_stacks.ku_ring.remote.user.response.KuringBotQueryCountResponse import com.ku_stacks.ku_ring.remote.util.DefaultResponse import retrofit2.http.Body +import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.POST @@ -16,4 +18,7 @@ interface UserService { @POST("v2/users") suspend fun registerUser(@Body registerUserRequest: RegisterUserRequest): DefaultResponse + + @GET("v2/users/ask-counts") + suspend fun getKuringBotQueryCount(@Header("User-Token") token: String): KuringBotQueryCountResponse } \ No newline at end of file diff --git a/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/response/KuringBotQueryCountResponse.kt b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/response/KuringBotQueryCountResponse.kt new file mode 100644 index 000000000..55da274cc --- /dev/null +++ b/data/remote/src/main/java/com/ku_stacks/ku_ring/remote/user/response/KuringBotQueryCountResponse.kt @@ -0,0 +1,12 @@ +package com.ku_stacks.ku_ring.remote.user.response + +data class KuringBotQueryCountResponse( + val code: Int, + val message: String, + val data: KuringBotQueryCountData, +) + +data class KuringBotQueryCountData( + val leftAskCount: Int, + val maxAskCount: Int, +) diff --git a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotMessageCounter.kt b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotMessageCounter.kt deleted file mode 100644 index 9da18d306..000000000 --- a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotMessageCounter.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.ku_stacks.ku_ring.kuringbot - -import com.ku_stacks.ku_ring.domain.KuringBotMessage -import com.ku_stacks.ku_ring.kuringbot.mapper.toUIMessage -import com.ku_stacks.ku_ring.util.yearMonth -import java.time.LocalDateTime -import java.time.YearMonth -import javax.inject.Inject - -class KuringBotMessageCounter @Inject constructor() { - - private val messageCount = mutableMapOf() - - fun convertInitialUIMessages(messages: List): List { - val uiMessages = mutableListOf() - - messages.forEachIndexed { index, message -> - uiMessages.add(message.toUIMessage()) - if (message.isQuery) { - increaseMessageCount(message) - } - // 답변이거나 - // 질문이면서 다음 메시지가 답변이 아닌 경우 (즉 답변이 오기 전에 중단된 경우) - if (!message.isQuery || !(index + 1 in messages.indices && !messages[index + 1].isQuery)) { - uiMessages.add(calculateQuestionsRemaining(message.postedDate)) - } - } - - return uiMessages - } - - fun increaseMessageCount(message: KuringBotMessage) { - messageCount[message.yearMonth] = messageCount.getOrDefault(message.yearMonth, 0) + 1 - } - - fun calculateQuestionsRemaining(dateTime: LocalDateTime): KuringBotUIMessage.QuestionsRemaining { - val questionsCount = messageCount.getOrDefault(dateTime.yearMonth, 0) - return KuringBotUIMessage.QuestionsRemaining( - (MONTHLY_LIMIT - questionsCount).coerceAtLeast(0), - dateTime.toLocalDate(), - ) - } - - private val KuringBotMessage.yearMonth: YearMonth - get() = YearMonth.of(postedDate.year, postedDate.monthValue) - - companion object { - private const val MONTHLY_LIMIT = 2 - } -} \ No newline at end of file diff --git a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotViewModel.kt b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotViewModel.kt index e35c1596c..9582118e8 100644 --- a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotViewModel.kt +++ b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/KuringBotViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ku_stacks.ku_ring.ai.repository.KuringBotRepository import com.ku_stacks.ku_ring.domain.KuringBotMessage +import com.ku_stacks.ku_ring.kuringbot.mapper.toUIMessages import com.ku_stacks.ku_ring.preferences.PreferenceUtil import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers @@ -14,12 +15,12 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.time.LocalDate import javax.inject.Inject @HiltViewModel class KuringBotViewModel @Inject constructor( private val kuringBotRepository: KuringBotRepository, - private val messageCounter: KuringBotMessageCounter, preferences: PreferenceUtil, ) : ViewModel() { private val token = preferences.fcmToken @@ -40,8 +41,7 @@ class KuringBotViewModel @Inject constructor( } private fun addInitialMessages(messages: List) { - val uiMessages = messageCounter.convertInitialUIMessages(messages) - addUiMessages(uiMessages) + addUiMessages(messages.toUIMessages()) } private fun addUiMessages(messages: List) { @@ -92,16 +92,14 @@ class KuringBotViewModel @Inject constructor( response = response.copy(message = response.message + it) updateLastMessage(response) } - addUiMessage(messageCounter.calculateQuestionsRemaining(response.postedTime)) + + addQueryCountMessage() stopKuringBotJob(response) } private fun addUiMessage(message: KuringBotUIMessage) { addUiMessages(listOf(message)) - if (message is KuringBotUIMessage.Question) { - messageCounter.increaseMessageCount(message.toDomain()) - } } private suspend fun saveMessageToLocal(message: KuringBotUIMessage.Savable) = @@ -118,6 +116,15 @@ class KuringBotViewModel @Inject constructor( updateMessages(newMessages) } + private suspend fun addQueryCountMessage() { + addUiMessage( + KuringBotUIMessage.QuestionsRemaining( + kuringBotRepository.getQueryCount(token), + LocalDate.now(), + ) + ) + } + private suspend fun stopKuringBotJob(response: KuringBotUIMessage.Response) { saveMessageToLocal(response) stopKuringBotJob() @@ -138,8 +145,8 @@ class KuringBotViewModel @Inject constructor( if (savable is KuringBotUIMessage.Response) { saveMessageToLocal(savable) } - addUiMessage(messageCounter.calculateQuestionsRemaining(savable.postedTime)) } + addQueryCountMessage() stopKuringBotJob() } diff --git a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/compose/KuringBotScreen.kt b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/compose/KuringBotScreen.kt index 7c8248091..ce484d218 100644 --- a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/compose/KuringBotScreen.kt +++ b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/compose/KuringBotScreen.kt @@ -38,6 +38,7 @@ import com.ku_stacks.ku_ring.kuringbot.R import com.ku_stacks.ku_ring.kuringbot.compose.components.* import java.time.LocalDate import java.time.LocalDateTime +import java.time.Month @Composable internal fun KuringBotScreen( @@ -357,13 +358,21 @@ private fun KuringBotScreenPreview() { } } -private val previewMessages = listOf( +private val previewMessages: List = listOf( KuringBotUIMessage.Question( message = "예시 질문".repeat(10), - postedTime = LocalDateTime.now(), + postedTime = LocalDateTime.of(2024, Month.JANUARY, 1, 12, 0), ), KuringBotUIMessage.Response( message = "예시 대답".repeat(10), + postedTime = LocalDateTime.of(2024, Month.JANUARY, 1, 12, 0), + ), + KuringBotUIMessage.Question( + message = "예시 질문", + postedTime = LocalDateTime.now(), + ), + KuringBotUIMessage.Response( + message = "예시 대답", postedTime = LocalDateTime.now(), ), KuringBotUIMessage.QuestionsRemaining( @@ -381,7 +390,7 @@ private val previewMessages = listOf( KuringBotUIMessage.QuestionsRemaining( questionsRemaining = 0, postedTime = LocalDate.now(), - ) + ), ) @LightAndDarkPreview diff --git a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/mapper/DomainToUIMessage.kt b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/mapper/DomainToUIMessage.kt index 1ca37136d..d84b6ec69 100644 --- a/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/mapper/DomainToUIMessage.kt +++ b/feature/kuringbot/src/main/java/com/ku_stacks/ku_ring/kuringbot/mapper/DomainToUIMessage.kt @@ -3,6 +3,9 @@ package com.ku_stacks.ku_ring.kuringbot.mapper import com.ku_stacks.ku_ring.domain.KuringBotMessage import com.ku_stacks.ku_ring.kuringbot.KuringBotUIMessage +internal fun List.toUIMessages(): List = + map { it.toUIMessage() } + internal fun KuringBotMessage.toUIMessage(): KuringBotUIMessage = when (isQuery) { true -> KuringBotUIMessage.Question( id = id,