From d2331f5951e1b6670466a2b9fc3abc442f7f9369 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 17:09:32 +0900 Subject: [PATCH 01/32] =?UTF-8?q?refactor:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EA=B5=AC=EC=A1=B0=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/model/chat/ChattingStreamingData.kt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt diff --git a/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt b/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt new file mode 100644 index 0000000..caef58f --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt @@ -0,0 +1,4 @@ +package com.useai.core.model.chat + +@JvmInline +value class ChattingStreamingData(val value: String) From e9bead98a52b895f326664d1f31a3f3b91a96a6f Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 12:15:19 +0900 Subject: [PATCH 02/32] =?UTF-8?q?chore:=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20ChatApi=20->=20ChattingApi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/network/api/{ChatApi.kt => ChattingApi.kt} | 2 +- .../main/kotlin/com/useai/core/network/di/ApiModule.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename core/network/src/main/kotlin/com/useai/core/network/api/{ChatApi.kt => ChattingApi.kt} (62%) diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ChatApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt similarity index 62% rename from core/network/src/main/kotlin/com/useai/core/network/api/ChatApi.kt rename to core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt index f053d4b..d0dd679 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ChatApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt @@ -1,3 +1,3 @@ package com.useai.core.network.api -interface ChatApi +interface ChattingApi diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt index cde0ee7..0da0c32 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt @@ -1,7 +1,7 @@ package com.useai.core.network.di import com.useai.core.common.qualifiers.AuthClient -import com.useai.core.network.api.ChatApi +import com.useai.core.network.api.ChattingApi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -16,9 +16,9 @@ internal object ApiModule { @Provides @ActivityScoped - fun providesChatApi( + fun providesChattingApi( @AuthClient retrofit: Retrofit - ) : ChatApi { - return retrofit.create() + ) : ChattingApi { + return retrofit.create() } } From 26b120bd1d1ed1a240226695b5fff2e6dd092153 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 13:29:14 +0900 Subject: [PATCH 03/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20Response=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/ChattingHistoryResponse.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt new file mode 100644 index 0000000..f76bb89 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt @@ -0,0 +1,23 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ChattingHistoryResponse( + @SerialName("chats") val chats: List, + @SerialName("created_at") val createdAt: String, + @SerialName("experience_ids") val experienceIds: List, + @SerialName("project_name") val projectName: String, + @SerialName("question") val questionTitle: String, + @SerialName("question_id") val questionId: Int, +) + +@Serializable +data class ChattingContentResponse( + @SerialName("id") val id: String, + @SerialName("content") val content: String, + @SerialName("is_draft") val isDraft: Boolean, + @SerialName("created_at") val createdAt: String, + @SerialName("role") val role: String, +) From 1ad416a1df40176b88f50c386ef3ff443745adcb Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:00:31 +0900 Subject: [PATCH 04/32] =?UTF-8?q?feat:=20=ED=8A=B9=EC=A0=95=20=EC=A7=88?= =?UTF-8?q?=EB=AC=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20API=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/api/ChattingApi.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt index d0dd679..e4575c9 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt @@ -1,3 +1,13 @@ package com.useai.core.network.api -interface ChattingApi +import com.useai.core.network.response.ChattingHistoryResponse +import retrofit2.http.GET +import retrofit2.http.Path + +interface ChattingApi { + + @GET("api/v1/projects/chats/{question_id}") + suspend fun getChattingHistory( + @Path("question_id") questionId: Int + ): ChattingHistoryResponse +} From ecf48986953c6eb02899d335ea763846407a28c3 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:03:13 +0900 Subject: [PATCH 05/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20API=20DataSource=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/source/ChattingRemoteDataSource.kt | 8 ++++++++ .../network/source/ChattingRemoteDataSourceImpl.kt | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt new file mode 100644 index 0000000..6943cf8 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt @@ -0,0 +1,8 @@ +package com.useai.core.network.source + +import com.useai.core.network.response.ChattingHistoryResponse + +interface ChattingRemoteDataSource { + + suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt new file mode 100644 index 0000000..66c0e37 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -0,0 +1,14 @@ +package com.useai.core.network.source + +import com.useai.core.network.api.ChattingApi +import com.useai.core.network.response.ChattingHistoryResponse +import javax.inject.Inject + +internal class ChattingRemoteDataSourceImpl @Inject constructor( + private val chattingApi: ChattingApi +) { + + suspend fun getChatHistory(projectId: Int): ChattingHistoryResponse { + return chattingApi.getChattingHistory(projectId) + } +} From 25cbc70e2bd9118a4f36bb7b868907ece1160b55 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:04:44 +0900 Subject: [PATCH 06/32] =?UTF-8?q?feat:=20RemoteDataSource=20=ED=9E=90?= =?UTF-8?q?=ED=8A=B8=20=EB=AA=A8=EB=93=88=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Chatting Source --- .../useai/core/network/di/DataSourceModule.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt new file mode 100644 index 0000000..c833972 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt @@ -0,0 +1,20 @@ +package com.useai.core.network.di + +import com.useai.core.network.source.ChattingRemoteDataSource +import com.useai.core.network.source.ChattingRemoteDataSourceImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent +import dagger.hilt.android.scopes.ActivityRetainedScoped + +@Module +@InstallIn(ActivityRetainedComponent::class) +internal interface DataSourceModule { + + @Binds + @ActivityRetainedScoped + fun bindsChattingRemoteDataSource( + chattingRemoteDataSourceImpl: ChattingRemoteDataSourceImpl + ): ChattingRemoteDataSource +} From 7e44bb66b6da14a9eaf923c0f7b962231af15830 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:17:05 +0900 Subject: [PATCH 07/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A6=AC=EB=B0=8D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20Res?= =?UTF-8?q?ponse=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/response/ChattingStreamingResponse.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt new file mode 100644 index 0000000..4beda97 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt @@ -0,0 +1,12 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ChattingStreamingResponse( + @SerialName("type") val type: String, + @SerialName("content") val data: String? = null, + @SerialName("chat_id") val chatId: String? = null, + @SerialName("is_draft") val isDraft: String? = null, +) From 9d9069641d1926ebe3951c84ded725f03354ff4a Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:19:18 +0900 Subject: [PATCH 08/32] =?UTF-8?q?feat:=20Network=20Hilt=20Module=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Json provide 메서드 - ConverterFactory 부착 - CallAdapterFactory 부착 --- .../useai/core/network/di/NetworkModule.kt | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt index 19931fc..7a907a5 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt @@ -3,38 +3,56 @@ package com.useai.core.network.di import com.useai.core.common.qualifiers.AuthClient import com.useai.core.common.qualifiers.BaseClient import com.useai.core.network.BuildConfig +import com.useai.core.network.error.RemoteErrorCallAdapterFactory import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityRetainedComponent import dagger.hilt.android.scopes.ActivityRetainedScoped +import kotlinx.serialization.json.Json import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory @Module @InstallIn(ActivityRetainedComponent::class) internal object NetworkModule { + @Provides + @ActivityRetainedScoped + fun providesJson() = Json { ignoreUnknownKeys = true } + @Provides @ActivityRetainedScoped @BaseClient - fun providesBaseRetrofit(@BaseClient client: OkHttpClient) : Retrofit { + fun providesBaseRetrofit( + @BaseClient client: OkHttpClient, + json: Json + ) : Retrofit { return Retrofit.Builder() .baseUrl(BuildConfig.BASE_URL) .client(client) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .addCallAdapterFactory(RemoteErrorCallAdapterFactory(json)) .build() } @Provides @ActivityRetainedScoped @AuthClient - fun providesAuthRetrofit(@AuthClient client: OkHttpClient) : Retrofit { + fun providesAuthRetrofit( + @AuthClient client: OkHttpClient, + json: Json + ) : Retrofit { return Retrofit.Builder() .baseUrl(BuildConfig.BASE_URL) .client(client) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .addCallAdapterFactory(RemoteErrorCallAdapterFactory(json)) .build() } From ed058bf57a720270dc9db695e5fb3427337baa27 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:28:18 +0900 Subject: [PATCH 09/32] =?UTF-8?q?fix:=20DataSource=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EA=B4=80=EA=B3=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/network/source/ChattingRemoteDataSourceImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt index 66c0e37..2dbb039 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -6,9 +6,9 @@ import javax.inject.Inject internal class ChattingRemoteDataSourceImpl @Inject constructor( private val chattingApi: ChattingApi -) { +) : ChattingRemoteDataSource { - suspend fun getChatHistory(projectId: Int): ChattingHistoryResponse { + override suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse { return chattingApi.getChattingHistory(projectId) } } From aae1e3ac98d3f046dabfeb7c79a0cc7988b029dd Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:45:52 +0900 Subject: [PATCH 10/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A6=AC=EB=B0=8D=20API=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/ChattingEventSourceFactory.kt | 9 ++++ .../useai/core/network/di/NetworkModule.kt | 27 ++++++++++ .../request/StartChattingStreamRequest.kt | 11 ++++ .../source/ChattingRemoteDataSource.kt | 4 ++ .../source/ChattingRemoteDataSourceImpl.kt | 50 ++++++++++++++++++- 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/ChattingEventSourceFactory.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/ChattingEventSourceFactory.kt b/core/network/src/main/kotlin/com/useai/core/network/ChattingEventSourceFactory.kt new file mode 100644 index 0000000..c6c4999 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/ChattingEventSourceFactory.kt @@ -0,0 +1,9 @@ +package com.useai.core.network + +import com.launchdarkly.eventsource.background.BackgroundEventHandler +import com.launchdarkly.eventsource.background.BackgroundEventSource +import com.useai.core.network.request.StartChattingStreamRequest + +internal fun interface ChattingEventSourceFactory { + fun create(handler: BackgroundEventHandler, request: StartChattingStreamRequest): BackgroundEventSource +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt index 7a907a5..9e0039d 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt @@ -1,8 +1,12 @@ package com.useai.core.network.di +import com.launchdarkly.eventsource.ConnectStrategy +import com.launchdarkly.eventsource.EventSource +import com.launchdarkly.eventsource.background.BackgroundEventSource import com.useai.core.common.qualifiers.AuthClient import com.useai.core.common.qualifiers.BaseClient import com.useai.core.network.BuildConfig +import com.useai.core.network.ChattingEventSourceFactory import com.useai.core.network.error.RemoteErrorCallAdapterFactory import dagger.Module import dagger.Provides @@ -14,14 +18,37 @@ import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.kotlinx.serialization.asConverterFactory +import java.net.URI @Module @InstallIn(ActivityRetainedComponent::class) internal object NetworkModule { + private val mediaType = "application/json; charset=utf-8".toMediaType() + + @Provides + @ActivityRetainedScoped + fun providesChattingEventSourceFactory( + @AuthClient client: OkHttpClient + ) : ChattingEventSourceFactory { + return ChattingEventSourceFactory { handler, request -> + val requestJsonString = Json.encodeToString(request) + BackgroundEventSource.Builder( + handler, + EventSource.Builder( + ConnectStrategy + .http(URI.create(BuildConfig.BASE_URL)) + .methodAndBody("POST", requestJsonString.toRequestBody(mediaType)) + .httpClient(client) + ) + ).build() + } + } + @Provides @ActivityRetainedScoped fun providesJson() = Json { ignoreUnknownKeys = true } diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt new file mode 100644 index 0000000..75d1f08 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt @@ -0,0 +1,11 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class StartChattingStreamRequest( + @SerialName("content") val sendingMessage: String, + @SerialName("experience_ids") val experienceIds: List, + @SerialName("question_id") val questionId: Int +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt index 6943cf8..530eee5 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt @@ -1,8 +1,12 @@ package com.useai.core.network.source +import com.useai.core.network.request.StartChattingStreamRequest import com.useai.core.network.response.ChattingHistoryResponse +import com.useai.core.network.response.ChattingStreamingResponse +import kotlinx.coroutines.flow.Flow interface ChattingRemoteDataSource { + fun startChattingStream(request: StartChattingStreamRequest): Flow suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt index 2dbb039..06d4d26 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -1,13 +1,61 @@ package com.useai.core.network.source +import com.launchdarkly.eventsource.MessageEvent +import com.launchdarkly.eventsource.background.BackgroundEventHandler +import com.useai.core.network.ChattingEventSourceFactory import com.useai.core.network.api.ChattingApi +import com.useai.core.network.request.StartChattingStreamRequest import com.useai.core.network.response.ChattingHistoryResponse +import com.useai.core.network.response.ChattingStreamingResponse +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.serialization.json.Json import javax.inject.Inject internal class ChattingRemoteDataSourceImpl @Inject constructor( - private val chattingApi: ChattingApi + private val chattingApi: ChattingApi, + private val chattingEventSourceFactory: ChattingEventSourceFactory ) : ChattingRemoteDataSource { + override fun startChattingStream(request: StartChattingStreamRequest): Flow { + return callbackFlow { + class ChatEventHandler : BackgroundEventHandler { + override fun onOpen() { + } + + override fun onClosed() { + close() + } + + override fun onMessage( + event: String?, + messageEvent: MessageEvent? + ) { + messageEvent?.data?.let { jsonString -> + val response = Json.decodeFromString(jsonString) + trySend(response) + } + } + + override fun onComment(comment: String?) { + + } + + override fun onError(t: Throwable?) { + close() + } + } + + val eventSource = chattingEventSourceFactory.create(ChatEventHandler(), request) + eventSource.start() + + awaitClose { + eventSource.close() + } + } + } + override suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse { return chattingApi.getChattingHistory(projectId) } From 9bfea03325870e6f60e09bd25286e5635630957c Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:57:33 +0900 Subject: [PATCH 11/32] =?UTF-8?q?feat:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20API?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/api/ChattingApi.kt | 9 +++++++++ .../useai/core/network/request/UpdateLetterRequest.kt | 10 ++++++++++ .../core/network/source/ChattingRemoteDataSource.kt | 2 ++ .../network/source/ChattingRemoteDataSourceImpl.kt | 5 +++++ 4 files changed, 26 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/UpdateLetterRequest.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt index e4575c9..c597dc8 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt @@ -1,7 +1,10 @@ package com.useai.core.network.api +import com.useai.core.network.request.UpdateLetterRequest import com.useai.core.network.response.ChattingHistoryResponse +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.PATCH import retrofit2.http.Path interface ChattingApi { @@ -10,4 +13,10 @@ interface ChattingApi { suspend fun getChattingHistory( @Path("question_id") questionId: Int ): ChattingHistoryResponse + + @PATCH("api/v1/projects/chats/{chat_id}/answer") + suspend fun updateLetter( + @Path("chat_id") chatId: String, + @Body updateLetterRequest: UpdateLetterRequest + ) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/UpdateLetterRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateLetterRequest.kt new file mode 100644 index 0000000..58a6d07 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateLetterRequest.kt @@ -0,0 +1,10 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateLetterRequest( + @SerialName("question_id") val questionId: String, + @SerialName("answer") val content: String +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt index 530eee5..ca24481 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt @@ -1,6 +1,7 @@ package com.useai.core.network.source import com.useai.core.network.request.StartChattingStreamRequest +import com.useai.core.network.request.UpdateLetterRequest import com.useai.core.network.response.ChattingHistoryResponse import com.useai.core.network.response.ChattingStreamingResponse import kotlinx.coroutines.flow.Flow @@ -9,4 +10,5 @@ interface ChattingRemoteDataSource { fun startChattingStream(request: StartChattingStreamRequest): Flow suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse + suspend fun updateLetter(chattingId: String, request: UpdateLetterRequest) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt index 06d4d26..03994b3 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -5,6 +5,7 @@ import com.launchdarkly.eventsource.background.BackgroundEventHandler import com.useai.core.network.ChattingEventSourceFactory import com.useai.core.network.api.ChattingApi import com.useai.core.network.request.StartChattingStreamRequest +import com.useai.core.network.request.UpdateLetterRequest import com.useai.core.network.response.ChattingHistoryResponse import com.useai.core.network.response.ChattingStreamingResponse import kotlinx.coroutines.channels.awaitClose @@ -59,4 +60,8 @@ internal class ChattingRemoteDataSourceImpl @Inject constructor( override suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse { return chattingApi.getChattingHistory(projectId) } + + override suspend fun updateLetter(chattingId: String, request: UpdateLetterRequest) { + chattingApi.updateLetter(chattingId, request) + } } From f99fb107a9ccdcbd3d0d05ab5a53b42b7cebeda5 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 14:58:20 +0900 Subject: [PATCH 12/32] =?UTF-8?q?fix:=20=EC=B1=84=ED=8C=85=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/useai/core/network/api/ChattingApi.kt | 2 +- .../com/useai/core/network/source/ChattingRemoteDataSource.kt | 2 +- .../useai/core/network/source/ChattingRemoteDataSourceImpl.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt index c597dc8..05bc207 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt @@ -11,7 +11,7 @@ interface ChattingApi { @GET("api/v1/projects/chats/{question_id}") suspend fun getChattingHistory( - @Path("question_id") questionId: Int + @Path("question_id") questionId: String ): ChattingHistoryResponse @PATCH("api/v1/projects/chats/{chat_id}/answer") diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt index ca24481..ec943a1 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt @@ -9,6 +9,6 @@ import kotlinx.coroutines.flow.Flow interface ChattingRemoteDataSource { fun startChattingStream(request: StartChattingStreamRequest): Flow - suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse + suspend fun getChatHistory(questionId: String): ChattingHistoryResponse suspend fun updateLetter(chattingId: String, request: UpdateLetterRequest) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt index 03994b3..e13cd72 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -57,8 +57,8 @@ internal class ChattingRemoteDataSourceImpl @Inject constructor( } } - override suspend fun getChatHistory(projectId: Int, questionId: Int): ChattingHistoryResponse { - return chattingApi.getChattingHistory(projectId) + override suspend fun getChatHistory(questionId: String): ChattingHistoryResponse { + return chattingApi.getChattingHistory(questionId) } override suspend fun updateLetter(chattingId: String, request: UpdateLetterRequest) { From 4e9480d872105fdf2109e3146dfd7fa7c90a37cd Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 16:39:49 +0900 Subject: [PATCH 13/32] =?UTF-8?q?chore:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20ru?= =?UTF-8?q?nCatching=20inline=20=ED=95=A8=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/useai/core/network/error/ErrorUtils.kt | 6 +++--- .../main/kotlin/com/useai/core/network/error/RemoteError.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/error/ErrorUtils.kt b/core/network/src/main/kotlin/com/useai/core/network/error/ErrorUtils.kt index bffb92d..b1504b1 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/error/ErrorUtils.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/error/ErrorUtils.kt @@ -3,7 +3,7 @@ package com.useai.core.network.error import com.useai.core.model.error.RootError import kotlin.coroutines.cancellation.CancellationException -fun runCatchingWith( +inline fun runCatchingWith( errorType: T, block: () -> R ): Result { @@ -20,7 +20,7 @@ fun runCatchingWith( } } -fun runCatchingWith( +inline fun runCatchingWith( block: () -> R ): Result { return try { @@ -32,4 +32,4 @@ fun runCatchingWith( } catch (e: Throwable) { Result.failure(e) } -} \ No newline at end of file +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/error/RemoteError.kt b/core/network/src/main/kotlin/com/useai/core/network/error/RemoteError.kt index f6bf5e3..3207891 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/error/RemoteError.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/error/RemoteError.kt @@ -6,7 +6,7 @@ import retrofit2.Response /** * 400, 500번대 에러 발생 시 던져지는 에러 */ -internal data class RemoteError( +data class RemoteError( val response: Response<*>, val errorCode: Int, override val message: String, From 7959ce5ac5559d52a72aa24998dabaf9ef3841bc Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 17:29:41 +0900 Subject: [PATCH 14/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20API=20?= =?UTF-8?q?=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/ChattingRepository.kt | 12 +++++ .../data/repository/ChattingRepositoryImpl.kt | 50 +++++++++++++++++++ .../core/model/chat/ChattingStreamingData.kt | 4 -- .../request/StartChattingStreamRequest.kt | 2 +- .../response/ChattingHistoryResponse.kt | 29 +++++++++++ .../response/ChattingStreamingResponse.kt | 17 ++++++- 6 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepository.kt create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepositoryImpl.kt delete mode 100644 core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepository.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepository.kt new file mode 100644 index 0000000..4bfcd1b --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepository.kt @@ -0,0 +1,12 @@ +package com.useai.core.data.repository + +import com.useai.core.model.chat.ChattingHistory +import com.useai.core.model.chat.ChattingStreaming +import kotlinx.coroutines.flow.Flow + +interface ChattingRepository { + + fun startChattingStream(questionId: String, sendingMessage: String): Flow + suspend fun getChatHistory(questionId: String): Result + suspend fun updateLetter(chattingId: String, questionId: String, content: String): Result +} diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepositoryImpl.kt new file mode 100644 index 0000000..816a0f3 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ChattingRepositoryImpl.kt @@ -0,0 +1,50 @@ +package com.useai.core.data.repository + +import com.useai.core.model.chat.ChattingHistory +import com.useai.core.model.chat.ChattingStreaming +import com.useai.core.network.error.runCatchingWith +import com.useai.core.network.request.StartChattingStreamRequest +import com.useai.core.network.request.UpdateLetterRequest +import com.useai.core.network.response.toChattingHistory +import com.useai.core.network.response.toChattingStreaming +import com.useai.core.network.source.ChattingRemoteDataSource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +internal class ChattingRepositoryImpl @Inject constructor( + private val chattingRemoteDataSource: ChattingRemoteDataSource +) : ChattingRepository { + + override fun startChattingStream(questionId: String, sendingMessage: String): Flow { + return chattingRemoteDataSource.startChattingStream( + StartChattingStreamRequest( + sendingMessage = sendingMessage, + questionId = questionId, + experienceIds = listOf() + ) + ).map { it.toChattingStreaming() } + } + + override suspend fun getChatHistory(questionId: String): Result { + return runCatchingWith { + chattingRemoteDataSource.getChatHistory(questionId).toChattingHistory() + } + } + + override suspend fun updateLetter( + chattingId: String, + questionId: String, + content: String + ): Result { + return runCatchingWith { + chattingRemoteDataSource.updateLetter( + chattingId = chattingId, + request = UpdateLetterRequest( + questionId = questionId, + content = content + ) + ) + } + } +} diff --git a/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt b/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt deleted file mode 100644 index caef58f..0000000 --- a/core/model/src/main/kotlin/com/useai/core/model/chat/ChattingStreamingData.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.useai.core.model.chat - -@JvmInline -value class ChattingStreamingData(val value: String) diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt index 75d1f08..68b8733 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt @@ -7,5 +7,5 @@ import kotlinx.serialization.Serializable data class StartChattingStreamRequest( @SerialName("content") val sendingMessage: String, @SerialName("experience_ids") val experienceIds: List, - @SerialName("question_id") val questionId: Int + @SerialName("question_id") val questionId: String ) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt index f76bb89..e852c5f 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt @@ -1,5 +1,7 @@ package com.useai.core.network.response +import com.useai.core.model.chat.ChattingContent +import com.useai.core.model.chat.ChattingHistory import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -21,3 +23,30 @@ data class ChattingContentResponse( @SerialName("created_at") val createdAt: String, @SerialName("role") val role: String, ) + +fun ChattingHistoryResponse.toChattingHistory() = ChattingHistory( + chattings = chats.map { it.toChattingContent() } +) + +fun ChattingContentResponse.toChattingContent() : ChattingContent { + return when (role) { + "user" -> { + ChattingContent.User( + id = id, + message = content, + createdAt = createdAt + ) + } + "ai" -> { + ChattingContent.AI( + id = id, + message = content, + createdAt = createdAt, + isLetter = isDraft + ) + } + else -> { + throw IllegalArgumentException("Unknown role: $role") + } + } +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt index 4beda97..7c9cb27 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt @@ -1,5 +1,6 @@ package com.useai.core.network.response +import com.useai.core.model.chat.ChattingStreaming import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -8,5 +9,19 @@ data class ChattingStreamingResponse( @SerialName("type") val type: String, @SerialName("content") val data: String? = null, @SerialName("chat_id") val chatId: String? = null, - @SerialName("is_draft") val isDraft: String? = null, + @SerialName("is_draft") val isDraft: Boolean? = null, ) + +fun ChattingStreamingResponse.toChattingStreaming() : ChattingStreaming { + return when (type) { + "content" -> { + ChattingStreaming.Streaming(data.orEmpty()) + } + "done" -> { + ChattingStreaming.Done(chatId.orEmpty(), isDraft ?: false) + } + else -> { + throw IllegalArgumentException("Unknown type: $type") + } + } +} From 358f54294fba10f9df3dc426fdef0244cc95b699 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 18:30:22 +0900 Subject: [PATCH 15/32] =?UTF-8?q?feat:=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20Hilt=20=EB=AA=A8=EB=93=88=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/data/di/RepositoryModule.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt diff --git a/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt new file mode 100644 index 0000000..9d60a16 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt @@ -0,0 +1,20 @@ +package com.useai.core.data.di + +import com.useai.core.data.repository.ChattingRepository +import com.useai.core.data.repository.ChattingRepositoryImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityRetainedComponent +import dagger.hilt.android.scopes.ActivityScoped + +@Module +@InstallIn(ActivityRetainedComponent::class) +internal interface RepositoryModule { + + @Binds + @ActivityScoped + fun providesChattingRepository( + impl: ChattingRepositoryImpl + ) : ChattingRepository +} From e3021e0d718afe609a5e3fd1f8cc87040a9800f0 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 18:57:01 +0900 Subject: [PATCH 16/32] =?UTF-8?q?feat:=20Question=20Api=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/api/QuestionApi.kt | 3 +++ .../main/kotlin/com/useai/core/network/di/ApiModule.kt | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt new file mode 100644 index 0000000..c263094 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -0,0 +1,3 @@ +package com.useai.core.network.api + +interface QuestionApi diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt index 0da0c32..be6fd0c 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt @@ -2,6 +2,7 @@ package com.useai.core.network.di import com.useai.core.common.qualifiers.AuthClient import com.useai.core.network.api.ChattingApi +import com.useai.core.network.api.QuestionApi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -21,4 +22,12 @@ internal object ApiModule { ) : ChattingApi { return retrofit.create() } + + @Provides + @ActivityScoped + fun providesQuestionApi( + @AuthClient retrofit: Retrofit + ) : QuestionApi { + return retrofit.create() + } } From 52e61e34dfa8d52130ebaad9fc64d248cf7c093c Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:41:49 +0900 Subject: [PATCH 17/32] =?UTF-8?q?feat:=20QuestionAPI=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/useai/core/network/api/QuestionApi.kt | 39 ++++++++++++++++++- .../response/CreateQuestionResponse.kt | 9 +++++ .../core/network/response/QuestionResponse.kt | 12 ++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt index c263094..bde8885 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -1,3 +1,40 @@ package com.useai.core.network.api -interface QuestionApi +import com.useai.core.network.response.CreateQuestionResponse +import com.useai.core.network.response.QuestionResponse +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.PATCH +import retrofit2.http.POST +import retrofit2.http.Path + +interface QuestionApi { + + @POST("api/v1/projects/{project_id}/questions") + suspend fun createQuestion( + @Path("project_id") projectId: String + ) : CreateQuestionResponse + + @GET("api/v1/projects/{project_id}/questions") + suspend fun getQuestions( + @Path("project_id") projectId: String + ) : List + + @GET("api/v1/projects/{project_id}/questions/{questions_id}") + suspend fun getQuestionDetail( + @Path("project_id") projectId: String, + @Path("questions_id") questionId: String + ) : QuestionResponse + + @PATCH("api/v1/projects/{project_id}/questions/{questions_id}") + suspend fun updateQuestion( + @Path("project_id") projectId: String, + @Path("questions_id") questionId: String + ) : QuestionResponse + + @DELETE("api/v1/projects/{project_id}/questions/{questions_id}") + suspend fun deleteQuestion( + @Path("project_id") projectId: String, + @Path("questions_id") questionId: String + ) +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt new file mode 100644 index 0000000..42c1f8f --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt @@ -0,0 +1,9 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CreateQuestionResponse( + @SerialName("id") val questionId: String +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt new file mode 100644 index 0000000..8569890 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt @@ -0,0 +1,12 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class QuestionResponse( + @SerialName("id") val id: String, + @SerialName("answer") val letter: String, + @SerialName("question") val title: String, + @SerialName("max_length") val maxLength: Int +) From 6fca813ce4ccb9d6a89ef68cf823918de3ed63f2 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:44:37 +0900 Subject: [PATCH 18/32] =?UTF-8?q?feat:=20Question=20DataSource=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/network/di/DataSourceModule.kt | 8 ++++ .../source/QuestionRemoteDataSource.kt | 13 +++++++ .../source/QuestionRemoteDataSourceImpl.kt | 37 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt index c833972..c506ff8 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt @@ -2,6 +2,8 @@ package com.useai.core.network.di import com.useai.core.network.source.ChattingRemoteDataSource import com.useai.core.network.source.ChattingRemoteDataSourceImpl +import com.useai.core.network.source.QuestionRemoteDataSource +import com.useai.core.network.source.QuestionRemoteDataSourceImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -17,4 +19,10 @@ internal interface DataSourceModule { fun bindsChattingRemoteDataSource( chattingRemoteDataSourceImpl: ChattingRemoteDataSourceImpl ): ChattingRemoteDataSource + + @Binds + @ActivityRetainedScoped + fun bindsQuestionRemoteDataSource( + questionRemoteDataSourceImpl: QuestionRemoteDataSourceImpl + ): QuestionRemoteDataSource } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt new file mode 100644 index 0000000..0382ecb --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt @@ -0,0 +1,13 @@ +package com.useai.core.network.source + +import com.useai.core.network.response.CreateQuestionResponse +import com.useai.core.network.response.QuestionResponse + +interface QuestionRemoteDataSource { + + suspend fun createQuestion(projectId: String): CreateQuestionResponse + suspend fun getQuestions(projectId: String): List + suspend fun getQuestionDetail(projectId: String, questionId: String): QuestionResponse + suspend fun updateQuestion(projectId: String, questionId: String): QuestionResponse + suspend fun deleteQuestion(projectId: String, questionId: String) +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt new file mode 100644 index 0000000..4e8c6d3 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt @@ -0,0 +1,37 @@ +package com.useai.core.network.source + +import com.useai.core.network.api.QuestionApi +import com.useai.core.network.response.CreateQuestionResponse +import com.useai.core.network.response.QuestionResponse +import javax.inject.Inject + +internal class QuestionRemoteDataSourceImpl @Inject constructor( + private val questionApi: QuestionApi +) : QuestionRemoteDataSource { + + override suspend fun createQuestion(projectId: String): CreateQuestionResponse { + return questionApi.createQuestion(projectId) + } + + override suspend fun getQuestions(projectId: String): List { + return questionApi.getQuestions(projectId) + } + + override suspend fun getQuestionDetail( + projectId: String, + questionId: String + ): QuestionResponse { + return questionApi.getQuestionDetail(projectId, questionId) + } + + override suspend fun updateQuestion( + projectId: String, + questionId: String + ): QuestionResponse { + return questionApi.updateQuestion(projectId, questionId) + } + + override suspend fun deleteQuestion(projectId: String, questionId: String) { + questionApi.deleteQuestion(projectId, questionId) + } +} From 8695ca2492ff15fb687dca9307d2815598dc31e9 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:47:20 +0900 Subject: [PATCH 19/32] =?UTF-8?q?refactor:=20Question=20=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=EC=97=90=20=EC=9E=90=EC=86=8C=EC=84=9C=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/useai/core/model/chat/Question.kt | 3 ++- .../com/useai/feature/chat/ui/ChatCommonStickyHeader.kt | 4 ++-- .../main/kotlin/com/useai/feature/chat/ui/QuestionTabRow.kt | 2 +- .../com/useai/feature/chat/ui/chatting/ChatChattingUI.kt | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/model/src/main/kotlin/com/useai/core/model/chat/Question.kt b/core/model/src/main/kotlin/com/useai/core/model/chat/Question.kt index f69138e..19f8077 100644 --- a/core/model/src/main/kotlin/com/useai/core/model/chat/Question.kt +++ b/core/model/src/main/kotlin/com/useai/core/model/chat/Question.kt @@ -3,5 +3,6 @@ package com.useai.core.model.chat data class Question( val id: String, val title: String, - val maxLength: Int + val maxLength: Int, + val letter: String ) diff --git a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/ChatCommonStickyHeader.kt b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/ChatCommonStickyHeader.kt index e9c792d..29b526c 100644 --- a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/ChatCommonStickyHeader.kt +++ b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/ChatCommonStickyHeader.kt @@ -60,8 +60,8 @@ internal fun LazyListScope.chatCommonStickyHeader( private fun ChatCommonStickyHeaderPreview() { LazyColumn { chatCommonStickyHeader( - questions = listOf(Question("", "", 1000), Question("", "", 1000)), - currentQuestion = Question("", "", 1000), + questions = listOf(Question("", "", 1000, ""), Question("", "", 1000, "")), + currentQuestion = Question("", "", 1000, ""), onQuestionTabChange = {}, onQuestionAdd = {}, onCategoryChange = {} diff --git a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/QuestionTabRow.kt b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/QuestionTabRow.kt index 337de74..0a94a0b 100644 --- a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/QuestionTabRow.kt +++ b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/QuestionTabRow.kt @@ -80,7 +80,7 @@ internal fun QuestionTabRow( @Preview @Composable private fun QuestionTabRowPreview(){ - val q = Question(id = "", title = "", maxLength = 1000) + val q = Question(id = "", title = "", maxLength = 1000, letter = "") QuestionTabRow( selectedQuestion = q, onTabSelect = {}, diff --git a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/chatting/ChatChattingUI.kt b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/chatting/ChatChattingUI.kt index b57b51d..536f725 100644 --- a/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/chatting/ChatChattingUI.kt +++ b/feature/chat/src/main/kotlin/com/useai/feature/chat/ui/chatting/ChatChattingUI.kt @@ -137,8 +137,8 @@ private fun ChatChattingUIPreview() { )), userInput = "제대로 써", streamingStatus = ChattingStreamingStatus.Idle, - questions = listOf(Question("","",1000), Question("","",1000)), - currentQuestion = Question("","",1000), + questions = listOf(Question("","",1000, ""), Question("","",1000, "")), + currentQuestion = Question("","",1000, ""), eventSink = {}, ), ) From 820087487ea0f0ba57cbb5d872200da35488b22c Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:47:53 +0900 Subject: [PATCH 20/32] =?UTF-8?q?feat:=20QuestionResponse=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EB=B3=80=ED=99=98=20=ED=99=95=EC=9E=A5=ED=95=A8?= =?UTF-8?q?=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 --- .../com/useai/core/network/response/QuestionResponse.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt index 8569890..7cfebf3 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt @@ -1,5 +1,6 @@ package com.useai.core.network.response +import com.useai.core.model.chat.Question import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,3 +11,10 @@ data class QuestionResponse( @SerialName("question") val title: String, @SerialName("max_length") val maxLength: Int ) + +fun QuestionResponse.toQuestion() = Question( + id = id, + title = title, + maxLength = maxLength, + letter = letter +) From ac53008dc81a287e26510a3391c1c7ae297b7c90 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:49:16 +0900 Subject: [PATCH 21/32] =?UTF-8?q?feat:=20QuestionRepository=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/data/di/RepositoryModule.kt | 8 ++++++++ .../com/useai/core/data/repository/QuestionRepository.kt | 3 +++ .../useai/core/data/repository/QuestionRepositoryImpl.kt | 9 +++++++++ 3 files changed, 20 insertions(+) create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt diff --git a/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt index 9d60a16..1349072 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt @@ -2,6 +2,8 @@ package com.useai.core.data.di import com.useai.core.data.repository.ChattingRepository import com.useai.core.data.repository.ChattingRepositoryImpl +import com.useai.core.data.repository.QuestionRepository +import com.useai.core.data.repository.QuestionRepositoryImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -17,4 +19,10 @@ internal interface RepositoryModule { fun providesChattingRepository( impl: ChattingRepositoryImpl ) : ChattingRepository + + @Binds + @ActivityScoped + fun providesQuestionRepository( + impl: QuestionRepositoryImpl + ) : QuestionRepository } diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt new file mode 100644 index 0000000..d409a28 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt @@ -0,0 +1,3 @@ +package com.useai.core.data.repository + +interface QuestionRepository diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt new file mode 100644 index 0000000..43852cf --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt @@ -0,0 +1,9 @@ +package com.useai.core.data.repository + +import com.useai.core.network.source.QuestionRemoteDataSource +import javax.inject.Inject + +internal class QuestionRepositoryImpl @Inject constructor( + private val questionRemoteDataSource: QuestionRemoteDataSource +) : QuestionRepository { +} From 3420991be64464242a9b87b6a814eef476f8d17b Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:50:58 +0900 Subject: [PATCH 22/32] =?UTF-8?q?chore:=20QuestionDetail=20->=20Question?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/useai/core/network/api/QuestionApi.kt | 2 +- .../com/useai/core/network/source/QuestionRemoteDataSource.kt | 2 +- .../useai/core/network/source/QuestionRemoteDataSourceImpl.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt index bde8885..722c82a 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -21,7 +21,7 @@ interface QuestionApi { ) : List @GET("api/v1/projects/{project_id}/questions/{questions_id}") - suspend fun getQuestionDetail( + suspend fun getQuestion( @Path("project_id") projectId: String, @Path("questions_id") questionId: String ) : QuestionResponse diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt index 0382ecb..9ec4d83 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt @@ -7,7 +7,7 @@ interface QuestionRemoteDataSource { suspend fun createQuestion(projectId: String): CreateQuestionResponse suspend fun getQuestions(projectId: String): List - suspend fun getQuestionDetail(projectId: String, questionId: String): QuestionResponse + suspend fun getQuestion(projectId: String, questionId: String): QuestionResponse suspend fun updateQuestion(projectId: String, questionId: String): QuestionResponse suspend fun deleteQuestion(projectId: String, questionId: String) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt index 4e8c6d3..1ce056a 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt @@ -17,11 +17,11 @@ internal class QuestionRemoteDataSourceImpl @Inject constructor( return questionApi.getQuestions(projectId) } - override suspend fun getQuestionDetail( + override suspend fun getQuestion( projectId: String, questionId: String ): QuestionResponse { - return questionApi.getQuestionDetail(projectId, questionId) + return questionApi.getQuestion(projectId, questionId) } override suspend fun updateQuestion( From d6e0b9367e861dad3bf6fc44b50fd98dfd7832b0 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:55:11 +0900 Subject: [PATCH 23/32] =?UTF-8?q?fix:=20API=20=ED=98=B8=EC=B6=9C=EC=97=90?= =?UTF-8?q?=20=ED=95=84=EC=9A=94=ED=95=9C=20Request=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/api/QuestionApi.kt | 9 +++++++-- .../core/network/request/CreateQuestionRequest.kt | 10 ++++++++++ .../core/network/request/UpdateQuestionRequest.kt | 11 +++++++++++ .../core/network/source/QuestionRemoteDataSource.kt | 6 ++++-- .../network/source/QuestionRemoteDataSourceImpl.kt | 11 +++++++---- 5 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/CreateQuestionRequest.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/UpdateQuestionRequest.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt index 722c82a..523cd98 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -1,7 +1,10 @@ package com.useai.core.network.api +import com.useai.core.network.request.CreateQuestionRequest +import com.useai.core.network.request.UpdateQuestionRequest import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse +import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.PATCH @@ -12,7 +15,8 @@ interface QuestionApi { @POST("api/v1/projects/{project_id}/questions") suspend fun createQuestion( - @Path("project_id") projectId: String + @Path("project_id") projectId: String, + @Body request: CreateQuestionRequest ) : CreateQuestionResponse @GET("api/v1/projects/{project_id}/questions") @@ -29,7 +33,8 @@ interface QuestionApi { @PATCH("api/v1/projects/{project_id}/questions/{questions_id}") suspend fun updateQuestion( @Path("project_id") projectId: String, - @Path("questions_id") questionId: String + @Path("questions_id") questionId: String, + @Body request: UpdateQuestionRequest ) : QuestionResponse @DELETE("api/v1/projects/{project_id}/questions/{questions_id}") diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/CreateQuestionRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/CreateQuestionRequest.kt new file mode 100644 index 0000000..7b8111c --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/CreateQuestionRequest.kt @@ -0,0 +1,10 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CreateQuestionRequest( + @SerialName("question") val title: String, + @SerialName("max_length") val maxLength: Int +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/UpdateQuestionRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateQuestionRequest.kt new file mode 100644 index 0000000..b63955c --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateQuestionRequest.kt @@ -0,0 +1,11 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateQuestionRequest( + @SerialName("question") val title: String, + @SerialName("max_length") val maxLength: Int, + @SerialName("answer") val letter: String +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt index 9ec4d83..e2cfe34 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt @@ -1,13 +1,15 @@ package com.useai.core.network.source +import com.useai.core.network.request.CreateQuestionRequest +import com.useai.core.network.request.UpdateQuestionRequest import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse interface QuestionRemoteDataSource { - suspend fun createQuestion(projectId: String): CreateQuestionResponse + suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): CreateQuestionResponse suspend fun getQuestions(projectId: String): List suspend fun getQuestion(projectId: String, questionId: String): QuestionResponse - suspend fun updateQuestion(projectId: String, questionId: String): QuestionResponse + suspend fun updateQuestion(projectId: String, questionId: String, request: UpdateQuestionRequest): QuestionResponse suspend fun deleteQuestion(projectId: String, questionId: String) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt index 1ce056a..bb2e9a1 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt @@ -1,6 +1,8 @@ package com.useai.core.network.source import com.useai.core.network.api.QuestionApi +import com.useai.core.network.request.CreateQuestionRequest +import com.useai.core.network.request.UpdateQuestionRequest import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse import javax.inject.Inject @@ -9,8 +11,8 @@ internal class QuestionRemoteDataSourceImpl @Inject constructor( private val questionApi: QuestionApi ) : QuestionRemoteDataSource { - override suspend fun createQuestion(projectId: String): CreateQuestionResponse { - return questionApi.createQuestion(projectId) + override suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): CreateQuestionResponse { + return questionApi.createQuestion(projectId, request) } override suspend fun getQuestions(projectId: String): List { @@ -26,9 +28,10 @@ internal class QuestionRemoteDataSourceImpl @Inject constructor( override suspend fun updateQuestion( projectId: String, - questionId: String + questionId: String, + request: UpdateQuestionRequest ): QuestionResponse { - return questionApi.updateQuestion(projectId, questionId) + return questionApi.updateQuestion(projectId, questionId, request) } override suspend fun deleteQuestion(projectId: String, questionId: String) { From 57b8e54cf275271de18a7c99e6aa256b4d0bda53 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 20:58:22 +0900 Subject: [PATCH 24/32] =?UTF-8?q?feat:=20QuestionRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/QuestionRepository.kt | 11 ++++- .../data/repository/QuestionRepositoryImpl.kt | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt index d409a28..ff635a8 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt @@ -1,3 +1,12 @@ package com.useai.core.data.repository -interface QuestionRepository +import com.useai.core.model.chat.Question + +interface QuestionRepository { + + suspend fun createQuestion(projectId: String, question: Question): Result + suspend fun getQuestions(projectId: String): Result> + suspend fun getQuestion(projectId: String, questionId: String): Result + suspend fun updateQuestion(projectId: String, question: Question): Result + suspend fun deleteQuestion(projectId: String, questionId: String): Result +} diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt index 43852cf..e1c84c6 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt @@ -1,9 +1,58 @@ package com.useai.core.data.repository +import com.useai.core.model.chat.Question +import com.useai.core.network.error.runCatchingWith +import com.useai.core.network.request.CreateQuestionRequest +import com.useai.core.network.request.UpdateQuestionRequest +import com.useai.core.network.response.toQuestion import com.useai.core.network.source.QuestionRemoteDataSource import javax.inject.Inject internal class QuestionRepositoryImpl @Inject constructor( private val questionRemoteDataSource: QuestionRemoteDataSource ) : QuestionRepository { + + override suspend fun createQuestion(projectId: String, question: Question): Result { + return runCatchingWith { + questionRemoteDataSource.createQuestion( + projectId, + CreateQuestionRequest( + title = question.title, + maxLength = question.maxLength + ) + ).questionId + } + } + + override suspend fun getQuestions(projectId: String): Result> { + return runCatchingWith { + questionRemoteDataSource.getQuestions(projectId).map { it.toQuestion() } + } + } + + override suspend fun getQuestion(projectId: String, questionId: String): Result { + return runCatchingWith { + questionRemoteDataSource.getQuestion(projectId, questionId).toQuestion() + } + } + + override suspend fun updateQuestion(projectId: String, question: Question): Result { + return runCatchingWith { + questionRemoteDataSource.updateQuestion( + projectId, + question.id, + UpdateQuestionRequest( + title = question.title, + maxLength = question.maxLength, + letter = question.letter + ) + ).toQuestion() + } + } + + override suspend fun deleteQuestion(projectId: String, questionId: String): Result { + return runCatchingWith { + questionRemoteDataSource.deleteQuestion(projectId, questionId) + } + } } From e53e3cac57e5e013d1757f3ed87a9def389b070a Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 21:26:20 +0900 Subject: [PATCH 25/32] =?UTF-8?q?feat:=20Experience=20=EB=A0=88=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=ED=95=8F=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/api/ExperienceApi.kt | 3 +++ .../main/kotlin/com/useai/core/network/di/ApiModule.kt | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt new file mode 100644 index 0000000..b60725f --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt @@ -0,0 +1,3 @@ +package com.useai.core.network.api + +interface ExperienceApi diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt index be6fd0c..19e281e 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt @@ -2,6 +2,7 @@ package com.useai.core.network.di import com.useai.core.common.qualifiers.AuthClient import com.useai.core.network.api.ChattingApi +import com.useai.core.network.api.ExperienceApi import com.useai.core.network.api.QuestionApi import dagger.Module import dagger.Provides @@ -30,4 +31,12 @@ internal object ApiModule { ) : QuestionApi { return retrofit.create() } + + @Provides + @ActivityScoped + fun providesExperienceApi( + @AuthClient retrofit: Retrofit + ) : ExperienceApi { + return retrofit.create() + } } From ce06e6da9b13da104a0c9858475d39ef355c57c8 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 21:31:10 +0900 Subject: [PATCH 26/32] =?UTF-8?q?refactor:=20=EB=AC=B8=ED=95=AD=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=20Response=20=EB=8B=A4=EB=A5=B8?= =?UTF-8?q?=20=EA=B2=83=EA=B3=BC=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/data/repository/QuestionRepositoryImpl.kt | 2 +- .../kotlin/com/useai/core/network/api/QuestionApi.kt | 3 +-- .../core/network/response/CreateQuestionResponse.kt | 9 --------- .../core/network/source/QuestionRemoteDataSource.kt | 3 +-- .../core/network/source/QuestionRemoteDataSourceImpl.kt | 3 +-- 5 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt index e1c84c6..8a9b6c6 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt @@ -20,7 +20,7 @@ internal class QuestionRepositoryImpl @Inject constructor( title = question.title, maxLength = question.maxLength ) - ).questionId + ).id } } diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt index 523cd98..6cc80b4 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -2,7 +2,6 @@ package com.useai.core.network.api import com.useai.core.network.request.CreateQuestionRequest import com.useai.core.network.request.UpdateQuestionRequest -import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse import retrofit2.http.Body import retrofit2.http.DELETE @@ -17,7 +16,7 @@ interface QuestionApi { suspend fun createQuestion( @Path("project_id") projectId: String, @Body request: CreateQuestionRequest - ) : CreateQuestionResponse + ) : QuestionResponse @GET("api/v1/projects/{project_id}/questions") suspend fun getQuestions( diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt deleted file mode 100644 index 42c1f8f..0000000 --- a/core/network/src/main/kotlin/com/useai/core/network/response/CreateQuestionResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.useai.core.network.response - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class CreateQuestionResponse( - @SerialName("id") val questionId: String -) diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt index e2cfe34..b115c1d 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt @@ -2,12 +2,11 @@ package com.useai.core.network.source import com.useai.core.network.request.CreateQuestionRequest import com.useai.core.network.request.UpdateQuestionRequest -import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse interface QuestionRemoteDataSource { - suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): CreateQuestionResponse + suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): QuestionResponse suspend fun getQuestions(projectId: String): List suspend fun getQuestion(projectId: String, questionId: String): QuestionResponse suspend fun updateQuestion(projectId: String, questionId: String, request: UpdateQuestionRequest): QuestionResponse diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt index bb2e9a1..38bfcbe 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt @@ -3,7 +3,6 @@ package com.useai.core.network.source import com.useai.core.network.api.QuestionApi import com.useai.core.network.request.CreateQuestionRequest import com.useai.core.network.request.UpdateQuestionRequest -import com.useai.core.network.response.CreateQuestionResponse import com.useai.core.network.response.QuestionResponse import javax.inject.Inject @@ -11,7 +10,7 @@ internal class QuestionRemoteDataSourceImpl @Inject constructor( private val questionApi: QuestionApi ) : QuestionRemoteDataSource { - override suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): CreateQuestionResponse { + override suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): QuestionResponse { return questionApi.createQuestion(projectId, request) } From 6295b10d8ba1b829ec8cb3833d5e43119d1382bf Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 21:47:34 +0900 Subject: [PATCH 27/32] =?UTF-8?q?feat:=20ExperienceAPI=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/network/api/ExperienceApi.kt | 49 ++++++++++++++++++- .../request/CreateExperienceRequest.kt | 16 ++++++ .../request/UpdateExperienceRequest.kt | 13 +++++ .../network/response/ExperienceResponse.kt | 21 ++++++++ .../network/response/ExperiencesResponse.kt | 12 +++++ .../response/MatchingExperiencesResponse.kt | 16 ++++++ 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/CreateExperienceRequest.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/ExperiencesResponse.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt index b60725f..de241b2 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt @@ -1,3 +1,50 @@ package com.useai.core.network.api -interface ExperienceApi +import androidx.annotation.IntRange +import com.useai.core.network.request.CreateExperienceRequest +import com.useai.core.network.request.UpdateExperienceRequest +import com.useai.core.network.response.ExperienceResponse +import com.useai.core.network.response.ExperiencesResponse +import com.useai.core.network.response.MatchingExperiencesResponse +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.Query + +interface ExperienceApi { + + @POST("api/v1/experiences") + suspend fun createExperience( + @Body request: CreateExperienceRequest + ): ExperienceResponse + + @GET("api/v1/experiences") + suspend fun getExperiences( + @IntRange(1,1000) @Query("limit") limit: Int = 100, + @Query("offset") offset: Int = 0 + ): ExperiencesResponse + + @GET("api/v1/experiences/search") + suspend fun searchExperience( + @Query("q") query: String, + @IntRange(1,100) @Query("limit") limit: Int = 100, + ): MatchingExperiencesResponse + + @GET("api/v1/experiences/{experience_id}") + suspend fun getExperience( + @Path("experience_id") experienceId: String + ): ExperienceResponse + + @GET("api/v1/experiences/{experience_id}") + suspend fun updateExperience( + @Path("experience_id") experienceId: String, + @Body request: UpdateExperienceRequest + ): ExperienceResponse + + @DELETE("api/v1/experiences/{experience_id}") + suspend fun deleteExperience( + @Path("experience_id") experienceId: String + ) +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/CreateExperienceRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/CreateExperienceRequest.kt new file mode 100644 index 0000000..bce78c2 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/CreateExperienceRequest.kt @@ -0,0 +1,16 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CreateExperienceRequest( + @SerialName("category") val category: String, + @SerialName("date") val date: String, + @SerialName("experience_type") val experienceType: String, + @SerialName("situation") val situation: String, + @SerialName("task") val task: String, + @SerialName("action") val action: String, + @SerialName("result") val result: String, + @SerialName("title") val title: String +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt new file mode 100644 index 0000000..54114af --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt @@ -0,0 +1,13 @@ +package com.useai.core.network.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateExperienceRequest( + @SerialName("situation") val situation: String, + @SerialName("task") val task: String, + @SerialName("action") val action: String, + @SerialName("result") val result: String, + @SerialName("title") val title: String +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt new file mode 100644 index 0000000..94730b4 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt @@ -0,0 +1,21 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ExperienceResponse( + @SerialName("id") val id: String, + @SerialName("created_at") val createdAt: String, + @SerialName("updated_at") val updatedAt: String, + @SerialName("tags") val tags: List, + @SerialName("user_id") val userId: String, + @SerialName("situation") val situation: String, + @SerialName("task") val task: String, + @SerialName("action") val action: String, + @SerialName("result") val result: String, + @SerialName("category") val category: String, + @SerialName("date") val date: String, + @SerialName("experience_type") val experienceType: String, + @SerialName("title") val title: String, +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ExperiencesResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ExperiencesResponse.kt new file mode 100644 index 0000000..995e187 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ExperiencesResponse.kt @@ -0,0 +1,12 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ExperiencesResponse( + @SerialName("experiences") val experiences: List, + @SerialName("limit") val limit: Int, + @SerialName("offset") val offset: Int, + @SerialName("total") val total: Int +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt new file mode 100644 index 0000000..0e0b232 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt @@ -0,0 +1,16 @@ +package com.useai.core.network.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MatchingExperiencesResponse( + @SerialName("total") val total: Int, + @SerialName("results") val results: List +) + +@Serializable +data class MatchingExperienceResponse( + @SerialName("experience") val experience: ExperienceResponse, + @SerialName("score") val score: Float +) From 53228a9155df3a8c5ffba66bb3ed60dd63f08827 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 21:52:21 +0900 Subject: [PATCH 28/32] =?UTF-8?q?feat:=20Experience=20DataSource=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/network/di/DataSourceModule.kt | 8 ++++ .../source/ExperienceRemoteDataSource.kt | 17 ++++++++ .../source/ExperienceRemoteDataSourceImpl.kt | 41 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSource.kt create mode 100644 core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSourceImpl.kt diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt index c506ff8..2c8790d 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt @@ -2,6 +2,8 @@ package com.useai.core.network.di import com.useai.core.network.source.ChattingRemoteDataSource import com.useai.core.network.source.ChattingRemoteDataSourceImpl +import com.useai.core.network.source.ExperienceRemoteDataSource +import com.useai.core.network.source.ExperienceRemoteDataSourceImpl import com.useai.core.network.source.QuestionRemoteDataSource import com.useai.core.network.source.QuestionRemoteDataSourceImpl import dagger.Binds @@ -25,4 +27,10 @@ internal interface DataSourceModule { fun bindsQuestionRemoteDataSource( questionRemoteDataSourceImpl: QuestionRemoteDataSourceImpl ): QuestionRemoteDataSource + + @Binds + @ActivityRetainedScoped + fun bindsExperienceRemoteDataSource( + experienceRemoteDataSourceImpl: ExperienceRemoteDataSourceImpl + ): ExperienceRemoteDataSource } diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSource.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSource.kt new file mode 100644 index 0000000..477570e --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSource.kt @@ -0,0 +1,17 @@ +package com.useai.core.network.source + +import com.useai.core.network.request.CreateExperienceRequest +import com.useai.core.network.request.UpdateExperienceRequest +import com.useai.core.network.response.ExperienceResponse +import com.useai.core.network.response.ExperiencesResponse +import com.useai.core.network.response.MatchingExperiencesResponse + +interface ExperienceRemoteDataSource { + + suspend fun createExperience(request: CreateExperienceRequest): ExperienceResponse + suspend fun getExperiences(): ExperiencesResponse + suspend fun getExperience(experienceId: String): ExperienceResponse + suspend fun searchExperience(query: String): MatchingExperiencesResponse + suspend fun updateExperience(experienceId: String, request: UpdateExperienceRequest): ExperienceResponse + suspend fun deleteExperience(experienceId: String) +} diff --git a/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSourceImpl.kt b/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSourceImpl.kt new file mode 100644 index 0000000..2c05e3a --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ExperienceRemoteDataSourceImpl.kt @@ -0,0 +1,41 @@ +package com.useai.core.network.source + +import com.useai.core.network.api.ExperienceApi +import com.useai.core.network.request.CreateExperienceRequest +import com.useai.core.network.request.UpdateExperienceRequest +import com.useai.core.network.response.ExperienceResponse +import com.useai.core.network.response.ExperiencesResponse +import com.useai.core.network.response.MatchingExperiencesResponse +import javax.inject.Inject + +internal class ExperienceRemoteDataSourceImpl @Inject constructor( + private val experienceApi: ExperienceApi +) : ExperienceRemoteDataSource { + + override suspend fun createExperience(request: CreateExperienceRequest): ExperienceResponse { + return experienceApi.createExperience(request) + } + + override suspend fun getExperiences(): ExperiencesResponse { + return experienceApi.getExperiences() + } + + override suspend fun getExperience(experienceId: String): ExperienceResponse { + return experienceApi.getExperience(experienceId) + } + + override suspend fun searchExperience(query: String): MatchingExperiencesResponse { + return experienceApi.searchExperience(query) + } + + override suspend fun updateExperience( + experienceId: String, + request: UpdateExperienceRequest + ): ExperienceResponse { + return experienceApi.updateExperience(experienceId, request) + } + + override suspend fun deleteExperience(experienceId: String) { + experienceApi.deleteExperience(experienceId) + } +} From 0e9802a4ba6480460918a29f5dd109d3179267dd Mon Sep 17 00:00:00 2001 From: Thirfir Date: Fri, 23 Jan 2026 22:22:47 +0900 Subject: [PATCH 29/32] =?UTF-8?q?feat:=20Experience=20Repository=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 매핑 로직 - Experience 도메인 모델 - Hilt 모듈 --- .../useai/core/data/di/RepositoryModule.kt | 8 +++ .../data/repository/ExperienceRepository.kt | 16 +++++ .../repository/ExperienceRepositoryImpl.kt | 67 +++++++++++++++++++ .../useai/core/model/experience/Experience.kt | 14 ++++ .../model/experience/ExperienceCategory.kt | 22 ++++++ .../core/model/experience/ExperienceParam.kt | 12 ++++ .../model/experience/MatchingExperience.kt | 6 ++ .../network/response/ExperienceResponse.kt | 25 +++++++ .../response/MatchingExperiencesResponse.kt | 6 ++ 9 files changed, 176 insertions(+) create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepository.kt create mode 100644 core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt create mode 100644 core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt create mode 100644 core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceCategory.kt create mode 100644 core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt create mode 100644 core/model/src/main/kotlin/com/useai/core/model/experience/MatchingExperience.kt diff --git a/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt index 1349072..1069fa7 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt @@ -2,6 +2,8 @@ package com.useai.core.data.di import com.useai.core.data.repository.ChattingRepository import com.useai.core.data.repository.ChattingRepositoryImpl +import com.useai.core.data.repository.ExperienceRepository +import com.useai.core.data.repository.ExperienceRepositoryImpl import com.useai.core.data.repository.QuestionRepository import com.useai.core.data.repository.QuestionRepositoryImpl import dagger.Binds @@ -25,4 +27,10 @@ internal interface RepositoryModule { fun providesQuestionRepository( impl: QuestionRepositoryImpl ) : QuestionRepository + + @Binds + @ActivityScoped + fun providesExperienceRepository( + impl: ExperienceRepositoryImpl + ) : ExperienceRepository } diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepository.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepository.kt new file mode 100644 index 0000000..f449c5e --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepository.kt @@ -0,0 +1,16 @@ +package com.useai.core.data.repository + +import com.useai.core.model.experience.Experience +import com.useai.core.model.experience.ExperienceParam +import com.useai.core.model.experience.MatchingExperience +import com.useai.core.network.request.UpdateExperienceRequest + +interface ExperienceRepository { + + suspend fun createExperience(experience: ExperienceParam): Result + suspend fun getExperiences(): Result> + suspend fun getExperience(experienceId: String): Result + suspend fun searchExperience(query: String): Result> + suspend fun updateExperience(experienceId: String, request: UpdateExperienceRequest): Result + suspend fun deleteExperience(experienceId: String): Result +} diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt new file mode 100644 index 0000000..fa66d8e --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt @@ -0,0 +1,67 @@ +package com.useai.core.data.repository + +import com.useai.core.model.experience.Experience +import com.useai.core.model.experience.ExperienceParam +import com.useai.core.model.experience.MatchingExperience +import com.useai.core.network.error.runCatchingWith +import com.useai.core.network.request.CreateExperienceRequest +import com.useai.core.network.request.UpdateExperienceRequest +import com.useai.core.network.response.toExperience +import com.useai.core.network.response.toMatchingExperience +import com.useai.core.network.source.ExperienceRemoteDataSource +import javax.inject.Inject + +internal class ExperienceRepositoryImpl @Inject constructor( + private val experienceRemoteDataSource: ExperienceRemoteDataSource +) : ExperienceRepository { + + override suspend fun createExperience(experience: ExperienceParam): Result { + return runCatchingWith { + experienceRemoteDataSource.createExperience( + CreateExperienceRequest( + category = experience.category, + date = experience.date, + experienceType = experience.experienceType, + situation = experience.situation, + task = experience.task, + action = experience.action, + result = experience.result, + title = experience.title + ) + ).toExperience() + } + } + + override suspend fun getExperiences(): Result> { + return runCatchingWith { + experienceRemoteDataSource.getExperiences().experiences.map { it.toExperience() } + } + } + + override suspend fun getExperience(experienceId: String): Result { + return runCatchingWith { + experienceRemoteDataSource.getExperience(experienceId).toExperience() + } + } + + override suspend fun searchExperience(query: String): Result> { + return runCatchingWith { + experienceRemoteDataSource.searchExperience(query).results.map { it.toMatchingExperience() } + } + } + + override suspend fun updateExperience( + experienceId: String, + request: UpdateExperienceRequest + ): Result { + return runCatchingWith { + experienceRemoteDataSource.updateExperience(experienceId, request).toExperience() + } + } + + override suspend fun deleteExperience(experienceId: String): Result { + return runCatchingWith { + experienceRemoteDataSource.deleteExperience(experienceId) + } + } +} diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt new file mode 100644 index 0000000..0f3aa91 --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt @@ -0,0 +1,14 @@ +package com.useai.core.model.experience + +data class Experience( + val id: String, + val tags: List, + val situation: String, + val task: String, + val action: String, + val result: String, + val category: ExperienceCategory, + val date: String, + val experienceType: String, + val title: String +) diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceCategory.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceCategory.kt new file mode 100644 index 0000000..8fc4741 --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceCategory.kt @@ -0,0 +1,22 @@ +package com.useai.core.model.experience + +/** + * @property PROACTIVE_EXECUTION 주도적 실행력 + * @property TECHNICAL_EXPERTISE 기술적 전문성 + * @property LOGICAL_ANALYSIS 논리적 분석력 + * @property CREATIVE_PROBLEM_SOLVING 창의적 문제해결 + * @property COLLABORATIVE_COMMUNICATION 협업적 소통 + * @property TENACIOUS_RESPONSIBILITY 끈기 있는 책임감 + * @property FLEXIBLE_ADAPTABILITY 유연한 적응력 + * @property CUSTOMER_VALUE_ORIENTATION 고객 가치 지향 + */ +enum class ExperienceCategory { + PROACTIVE_EXECUTION, + TECHNICAL_EXPERTISE, + LOGICAL_ANALYSIS, + CREATIVE_PROBLEM_SOLVING, + COLLABORATIVE_COMMUNICATION, + TENACIOUS_RESPONSIBILITY, + FLEXIBLE_ADAPTABILITY, + CUSTOMER_VALUE_ORIENTATION +} diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt new file mode 100644 index 0000000..9b2ee31 --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt @@ -0,0 +1,12 @@ +package com.useai.core.model.experience + +data class ExperienceParam( + val situation: String, + val task: String, + val action: String, + val result: String, + val category: String, + val date: String, + val experienceType: String, + val title: String +) diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/MatchingExperience.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/MatchingExperience.kt new file mode 100644 index 0000000..da3c14d --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/MatchingExperience.kt @@ -0,0 +1,6 @@ +package com.useai.core.model.experience + +data class MatchingExperience( + val experience: Experience, + val score: Float +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt index 94730b4..b13eec4 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt @@ -1,5 +1,7 @@ package com.useai.core.network.response +import com.useai.core.model.experience.Experience +import com.useai.core.model.experience.ExperienceCategory import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -19,3 +21,26 @@ data class ExperienceResponse( @SerialName("experience_type") val experienceType: String, @SerialName("title") val title: String, ) + +fun ExperienceResponse.toExperience() = Experience( + id = id, + tags = tags, + situation = situation, + task = task, + action = action, + result = result, + category = when(category) { + "주도적 실행력" -> ExperienceCategory.PROACTIVE_EXECUTION + "기술적 전문성" -> ExperienceCategory.TECHNICAL_EXPERTISE + "논리적 분석력" -> ExperienceCategory.LOGICAL_ANALYSIS + "창의적 문제해결" -> ExperienceCategory.CREATIVE_PROBLEM_SOLVING + "협업적 소통" -> ExperienceCategory.COLLABORATIVE_COMMUNICATION + "끈기 있는 책임감" -> ExperienceCategory.TENACIOUS_RESPONSIBILITY + "유연한 적응력" -> ExperienceCategory.FLEXIBLE_ADAPTABILITY + "고객 가치 지향" -> ExperienceCategory.CUSTOMER_VALUE_ORIENTATION + else -> throw IllegalArgumentException("Invalid category: $category") + }, + date = date, + experienceType = experienceType, + title = title +) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt index 0e0b232..f5660f1 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt @@ -1,5 +1,6 @@ package com.useai.core.network.response +import com.useai.core.model.experience.MatchingExperience import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -14,3 +15,8 @@ data class MatchingExperienceResponse( @SerialName("experience") val experience: ExperienceResponse, @SerialName("score") val score: Float ) + +fun MatchingExperienceResponse.toMatchingExperience() = MatchingExperience( + experience = experience.toExperience(), + score = score +) From 7abb69fad738d27bebf3eac8cf474f7be406ef51 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Sat, 24 Jan 2026 16:49:19 +0900 Subject: [PATCH 30/32] =?UTF-8?q?fix:=20=EA=B2=BD=ED=97=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20Request=20nullability=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/useai/core/network/di/NetworkModule.kt | 5 ++++- .../core/network/request/UpdateExperienceRequest.kt | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt index 9e0039d..6af752a 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/di/NetworkModule.kt @@ -51,7 +51,10 @@ internal object NetworkModule { @Provides @ActivityRetainedScoped - fun providesJson() = Json { ignoreUnknownKeys = true } + fun providesJson() = Json { + ignoreUnknownKeys = true + explicitNulls = false + } @Provides @ActivityRetainedScoped diff --git a/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt index 54114af..d6929ab 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/request/UpdateExperienceRequest.kt @@ -5,9 +5,9 @@ import kotlinx.serialization.Serializable @Serializable data class UpdateExperienceRequest( - @SerialName("situation") val situation: String, - @SerialName("task") val task: String, - @SerialName("action") val action: String, - @SerialName("result") val result: String, - @SerialName("title") val title: String + @SerialName("situation") val situation: String? = null, + @SerialName("task") val task: String? = null, + @SerialName("action") val action: String? = null, + @SerialName("result") val result: String? = null, + @SerialName("title") val title: String? = null ) From c3e30a87a7dea6e5c880123ee9a9cf39ba017894 Mon Sep 17 00:00:00 2001 From: Thirfir Date: Sun, 25 Jan 2026 12:43:14 +0900 Subject: [PATCH 31/32] =?UTF-8?q?feat:=20Time=20Format=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20=ED=99=95=EC=9E=A5=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/common/extensions/TimeExtensions.kt | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 core/common/src/main/kotlin/com/useai/core/common/extensions/TimeExtensions.kt diff --git a/core/common/src/main/kotlin/com/useai/core/common/extensions/TimeExtensions.kt b/core/common/src/main/kotlin/com/useai/core/common/extensions/TimeExtensions.kt new file mode 100644 index 0000000..be0350c --- /dev/null +++ b/core/common/src/main/kotlin/com/useai/core/common/extensions/TimeExtensions.kt @@ -0,0 +1,54 @@ +package com.useai.core.common.extensions + +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException + +fun String?.toLocalDateTime(pattern: String = "yyyy-MM-dd'T'HH:mm:ss"): LocalDateTime? { + if (this.isNullOrBlank()) return null + + return try { + val formatter = DateTimeFormatter.ofPattern(pattern) + LocalDateTime.parse(this, formatter) + } catch (e: DateTimeParseException) { + e.printStackTrace() + null + } +} + +fun String?.toLocalDate(pattern: String = "yyyy-MM-dd"): LocalDate? { + if (this.isNullOrBlank()) return null + + return try { + val formatter = DateTimeFormatter.ofPattern(pattern) + LocalDate.parse(this, formatter) + } catch (e: DateTimeParseException) { + e.printStackTrace() + null + } +} + +fun LocalDateTime?.toFormattedString(pattern: String = "yyyy-MM-dd'T'HH:mm:ss"): String? { + if (this == null) return null + + return try { + val formatter = DateTimeFormatter.ofPattern(pattern) + this.format(formatter) + } catch (e: Exception) { + e.printStackTrace() + null + } +} + +fun LocalDate?.toFormattedString(pattern: String = "yyyy-MM-dd"): String? { + if (this == null) return null + + return try { + val formatter = DateTimeFormatter.ofPattern(pattern) + this.format(formatter) + } catch (e: Exception) { + e.printStackTrace() + null + } +} From 874069823345918ddeaf5850dcd24c60cd0a26cc Mon Sep 17 00:00:00 2001 From: Thirfir Date: Sun, 25 Jan 2026 12:44:16 +0900 Subject: [PATCH 32/32] =?UTF-8?q?chore:=20=EA=B2=BD=ED=97=98=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20LocalDate=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useai/core/data/repository/ExperienceRepositoryImpl.kt | 3 ++- .../kotlin/com/useai/core/model/experience/Experience.kt | 4 +++- .../com/useai/core/model/experience/ExperienceParam.kt | 4 +++- .../useai/core/network/response/ChattingHistoryResponse.kt | 7 +++++-- .../com/useai/core/network/response/ExperienceResponse.kt | 4 +++- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt index fa66d8e..8be6354 100644 --- a/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt @@ -1,5 +1,6 @@ package com.useai.core.data.repository +import com.useai.core.common.extensions.toFormattedString import com.useai.core.model.experience.Experience import com.useai.core.model.experience.ExperienceParam import com.useai.core.model.experience.MatchingExperience @@ -20,7 +21,7 @@ internal class ExperienceRepositoryImpl @Inject constructor( experienceRemoteDataSource.createExperience( CreateExperienceRequest( category = experience.category, - date = experience.date, + date = experience.date.toFormattedString().orEmpty(), experienceType = experience.experienceType, situation = experience.situation, task = experience.task, diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt index 0f3aa91..ffaf372 100644 --- a/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt @@ -1,5 +1,7 @@ package com.useai.core.model.experience +import java.time.LocalDate + data class Experience( val id: String, val tags: List, @@ -8,7 +10,7 @@ data class Experience( val action: String, val result: String, val category: ExperienceCategory, - val date: String, + val date: LocalDate, val experienceType: String, val title: String ) diff --git a/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt index 9b2ee31..117ece8 100644 --- a/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt @@ -1,12 +1,14 @@ package com.useai.core.model.experience +import java.time.LocalDate + data class ExperienceParam( val situation: String, val task: String, val action: String, val result: String, val category: String, - val date: String, + val date: LocalDate, val experienceType: String, val title: String ) diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt index e852c5f..a3aeea3 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt @@ -1,9 +1,12 @@ package com.useai.core.network.response +import com.useai.core.common.extensions.toLocalDate +import com.useai.core.common.extensions.toLocalDateTime import com.useai.core.model.chat.ChattingContent import com.useai.core.model.chat.ChattingHistory import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.time.LocalDateTime @Serializable data class ChattingHistoryResponse( @@ -34,14 +37,14 @@ fun ChattingContentResponse.toChattingContent() : ChattingContent { ChattingContent.User( id = id, message = content, - createdAt = createdAt + createdAt = createdAt.toLocalDateTime() ?: LocalDateTime.MIN ) } "ai" -> { ChattingContent.AI( id = id, message = content, - createdAt = createdAt, + createdAt = createdAt.toLocalDateTime() ?: LocalDateTime.MIN, isLetter = isDraft ) } diff --git a/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt index b13eec4..81cd651 100644 --- a/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt @@ -1,9 +1,11 @@ package com.useai.core.network.response +import com.useai.core.common.extensions.toLocalDate import com.useai.core.model.experience.Experience import com.useai.core.model.experience.ExperienceCategory import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.time.LocalDate @Serializable data class ExperienceResponse( @@ -40,7 +42,7 @@ fun ExperienceResponse.toExperience() = Experience( "고객 가치 지향" -> ExperienceCategory.CUSTOMER_VALUE_ORIENTATION else -> throw IllegalArgumentException("Invalid category: $category") }, - date = date, + date = date.toLocalDate() ?: LocalDate.MIN, experienceType = experienceType, title = title )