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 + } +} 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..1069fa7 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/di/RepositoryModule.kt @@ -0,0 +1,36 @@ +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 +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 + + @Binds + @ActivityScoped + 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/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/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..8be6354 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/ExperienceRepositoryImpl.kt @@ -0,0 +1,68 @@ +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 +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.toFormattedString().orEmpty(), + 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/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..ff635a8 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepository.kt @@ -0,0 +1,12 @@ +package com.useai.core.data.repository + +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 new file mode 100644 index 0000000..8a9b6c6 --- /dev/null +++ b/core/data/src/main/kotlin/com/useai/core/data/repository/QuestionRepositoryImpl.kt @@ -0,0 +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 + ) + ).id + } + } + + 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) + } + } +} 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/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..ffaf372 --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/Experience.kt @@ -0,0 +1,16 @@ +package com.useai.core.model.experience + +import java.time.LocalDate + +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: LocalDate, + 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..117ece8 --- /dev/null +++ b/core/model/src/main/kotlin/com/useai/core/model/experience/ExperienceParam.kt @@ -0,0 +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: LocalDate, + 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/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/api/ChatApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ChatApi.kt deleted file mode 100644 index f053d4b..0000000 --- a/core/network/src/main/kotlin/com/useai/core/network/api/ChatApi.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.useai.core.network.api - -interface ChatApi 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 new file mode 100644 index 0000000..05bc207 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ChattingApi.kt @@ -0,0 +1,22 @@ +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 { + + @GET("api/v1/projects/chats/{question_id}") + suspend fun getChattingHistory( + @Path("question_id") questionId: String + ): 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/api/ExperienceApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt new file mode 100644 index 0000000..de241b2 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/api/ExperienceApi.kt @@ -0,0 +1,50 @@ +package com.useai.core.network.api + +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/api/QuestionApi.kt b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt new file mode 100644 index 0000000..6cc80b4 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/api/QuestionApi.kt @@ -0,0 +1,44 @@ +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.QuestionResponse +import retrofit2.http.Body +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, + @Body request: CreateQuestionRequest + ) : QuestionResponse + + @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 getQuestion( + @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, + @Body request: UpdateQuestionRequest + ) : 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/di/ApiModule.kt b/core/network/src/main/kotlin/com/useai/core/network/di/ApiModule.kt index cde0ee7..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 @@ -1,7 +1,9 @@ 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 com.useai.core.network.api.ExperienceApi +import com.useai.core.network.api.QuestionApi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -16,9 +18,25 @@ internal object ApiModule { @Provides @ActivityScoped - fun providesChatApi( + fun providesChattingApi( @AuthClient retrofit: Retrofit - ) : ChatApi { - return retrofit.create() + ) : ChattingApi { + return retrofit.create() + } + + @Provides + @ActivityScoped + fun providesQuestionApi( + @AuthClient retrofit: Retrofit + ) : QuestionApi { + return retrofit.create() + } + + @Provides + @ActivityScoped + fun providesExperienceApi( + @AuthClient retrofit: Retrofit + ) : ExperienceApi { + return retrofit.create() } } 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..2c8790d --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/di/DataSourceModule.kt @@ -0,0 +1,36 @@ +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 +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 + + @Binds + @ActivityRetainedScoped + fun bindsQuestionRemoteDataSource( + questionRemoteDataSourceImpl: QuestionRemoteDataSourceImpl + ): QuestionRemoteDataSource + + @Binds + @ActivityRetainedScoped + fun bindsExperienceRemoteDataSource( + experienceRemoteDataSourceImpl: ExperienceRemoteDataSourceImpl + ): ExperienceRemoteDataSource +} 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..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 @@ -1,40 +1,88 @@ 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 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.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 + explicitNulls = false + } + @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() } 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, 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/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/StartChattingStreamRequest.kt b/core/network/src/main/kotlin/com/useai/core/network/request/StartChattingStreamRequest.kt new file mode 100644 index 0000000..68b8733 --- /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: 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..d6929ab --- /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? = 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 +) 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/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/response/ChattingHistoryResponse.kt b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt new file mode 100644 index 0000000..a3aeea3 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingHistoryResponse.kt @@ -0,0 +1,55 @@ +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( + @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, +) + +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.toLocalDateTime() ?: LocalDateTime.MIN + ) + } + "ai" -> { + ChattingContent.AI( + id = id, + message = content, + createdAt = createdAt.toLocalDateTime() ?: LocalDateTime.MIN, + 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 new file mode 100644 index 0000000..7c9cb27 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ChattingStreamingResponse.kt @@ -0,0 +1,27 @@ +package com.useai.core.network.response + +import com.useai.core.model.chat.ChattingStreaming +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: 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") + } + } +} 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..81cd651 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/ExperienceResponse.kt @@ -0,0 +1,48 @@ +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( + @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, +) + +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.toLocalDate() ?: LocalDate.MIN, + experienceType = experienceType, + title = title +) 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..f5660f1 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/MatchingExperiencesResponse.kt @@ -0,0 +1,22 @@ +package com.useai.core.network.response + +import com.useai.core.model.experience.MatchingExperience +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 +) + +fun MatchingExperienceResponse.toMatchingExperience() = MatchingExperience( + experience = experience.toExperience(), + score = score +) 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..7cfebf3 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/response/QuestionResponse.kt @@ -0,0 +1,20 @@ +package com.useai.core.network.response + +import com.useai.core.model.chat.Question +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 +) + +fun QuestionResponse.toQuestion() = Question( + id = id, + title = title, + maxLength = maxLength, + letter = letter +) 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..ec943a1 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSource.kt @@ -0,0 +1,14 @@ +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 + +interface ChattingRemoteDataSource { + + fun startChattingStream(request: StartChattingStreamRequest): Flow + 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 new file mode 100644 index 0000000..e13cd72 --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/ChattingRemoteDataSourceImpl.kt @@ -0,0 +1,67 @@ +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.request.UpdateLetterRequest +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 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(questionId: String): ChattingHistoryResponse { + return chattingApi.getChattingHistory(questionId) + } + + override suspend fun updateLetter(chattingId: String, request: UpdateLetterRequest) { + chattingApi.updateLetter(chattingId, request) + } +} 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) + } +} 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..b115c1d --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSource.kt @@ -0,0 +1,14 @@ +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.QuestionResponse + +interface QuestionRemoteDataSource { + + 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 + 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..38bfcbe --- /dev/null +++ b/core/network/src/main/kotlin/com/useai/core/network/source/QuestionRemoteDataSourceImpl.kt @@ -0,0 +1,39 @@ +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.QuestionResponse +import javax.inject.Inject + +internal class QuestionRemoteDataSourceImpl @Inject constructor( + private val questionApi: QuestionApi +) : QuestionRemoteDataSource { + + override suspend fun createQuestion(projectId: String, request: CreateQuestionRequest): QuestionResponse { + return questionApi.createQuestion(projectId, request) + } + + override suspend fun getQuestions(projectId: String): List { + return questionApi.getQuestions(projectId) + } + + override suspend fun getQuestion( + projectId: String, + questionId: String + ): QuestionResponse { + return questionApi.getQuestion(projectId, questionId) + } + + override suspend fun updateQuestion( + projectId: String, + questionId: String, + request: UpdateQuestionRequest + ): QuestionResponse { + return questionApi.updateQuestion(projectId, questionId, request) + } + + override suspend fun deleteQuestion(projectId: String, questionId: String) { + questionApi.deleteQuestion(projectId, questionId) + } +} 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 = {}, ), )