-
Notifications
You must be signed in to change notification settings - Fork 1
경험/문항/채팅 API 데이터 레이어 구현 #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
d2331f5
e9bead9
26b120b
1ad416a
ecf4898
25cbc70
7e44bb6
9d90696
ed058bf
aae1e3a
9bfea03
f99fb10
4e9480d
7959ce5
358f542
e3021e0
52e61e3
6fca813
8695ca2
8200874
ac53008
3420991
d6e0b93
57b8e54
e53e3ca
ce06e6d
6295b10
53228a9
0e9802a
7abb69f
c3e30a8
8740698
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<ChattingStreaming> | ||
| suspend fun getChatHistory(questionId: String): Result<ChattingHistory> | ||
| suspend fun updateLetter(chattingId: String, questionId: String, content: String): Result<Unit> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 이게 자소서 업데이트하는 함수라면 updatePaper로 이름 지으면 어떨까요?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이게 백엔드에서는 Draft로 표현하는거 같더라구요 ... |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<ChattingStreaming> { | ||
| return chattingRemoteDataSource.startChattingStream( | ||
| StartChattingStreamRequest( | ||
| sendingMessage = sendingMessage, | ||
| questionId = questionId, | ||
| experienceIds = listOf() | ||
| ) | ||
| ).map { it.toChattingStreaming() } | ||
| } | ||
|
|
||
| override suspend fun getChatHistory(questionId: String): Result<ChattingHistory> { | ||
| return runCatchingWith { | ||
| chattingRemoteDataSource.getChatHistory(questionId).toChattingHistory() | ||
| } | ||
| } | ||
|
|
||
| override suspend fun updateLetter( | ||
| chattingId: String, | ||
| questionId: String, | ||
| content: String | ||
| ): Result<Unit> { | ||
| return runCatchingWith { | ||
| chattingRemoteDataSource.updateLetter( | ||
| chattingId = chattingId, | ||
| request = UpdateLetterRequest( | ||
| questionId = questionId, | ||
| content = content | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Experience> | ||
| suspend fun getExperiences(): Result<List<Experience>> | ||
| suspend fun getExperience(experienceId: String): Result<Experience> | ||
| suspend fun searchExperience(query: String): Result<List<MatchingExperience>> | ||
| suspend fun updateExperience(experienceId: String, request: UpdateExperienceRequest): Result<Experience> | ||
| suspend fun deleteExperience(experienceId: String): Result<Unit> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Experience> { | ||
| 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<List<Experience>> { | ||
| return runCatchingWith { | ||
| experienceRemoteDataSource.getExperiences().experiences.map { it.toExperience() } | ||
| } | ||
| } | ||
|
|
||
| override suspend fun getExperience(experienceId: String): Result<Experience> { | ||
| return runCatchingWith { | ||
| experienceRemoteDataSource.getExperience(experienceId).toExperience() | ||
| } | ||
| } | ||
|
|
||
| override suspend fun searchExperience(query: String): Result<List<MatchingExperience>> { | ||
| return runCatchingWith { | ||
| experienceRemoteDataSource.searchExperience(query).results.map { it.toMatchingExperience() } | ||
| } | ||
| } | ||
|
|
||
| override suspend fun updateExperience( | ||
| experienceId: String, | ||
| request: UpdateExperienceRequest | ||
| ): Result<Experience> { | ||
| return runCatchingWith { | ||
| experienceRemoteDataSource.updateExperience(experienceId, request).toExperience() | ||
| } | ||
| } | ||
|
|
||
| override suspend fun deleteExperience(experienceId: String): Result<Unit> { | ||
| return runCatchingWith { | ||
| experienceRemoteDataSource.deleteExperience(experienceId) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<String> | ||
| suspend fun getQuestions(projectId: String): Result<List<Question>> | ||
| suspend fun getQuestion(projectId: String, questionId: String): Result<Question> | ||
| suspend fun updateQuestion(projectId: String, question: Question): Result<Question> | ||
| suspend fun deleteQuestion(projectId: String, questionId: String): Result<Unit> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<String> { | ||
| return runCatchingWith { | ||
| questionRemoteDataSource.createQuestion( | ||
| projectId, | ||
| CreateQuestionRequest( | ||
| title = question.title, | ||
| maxLength = question.maxLength | ||
| ) | ||
| ).id | ||
| } | ||
| } | ||
|
|
||
| override suspend fun getQuestions(projectId: String): Result<List<Question>> { | ||
| return runCatchingWith { | ||
| questionRemoteDataSource.getQuestions(projectId).map { it.toQuestion() } | ||
| } | ||
| } | ||
|
|
||
| override suspend fun getQuestion(projectId: String, questionId: String): Result<Question> { | ||
| return runCatchingWith { | ||
| questionRemoteDataSource.getQuestion(projectId, questionId).toQuestion() | ||
| } | ||
| } | ||
|
|
||
| override suspend fun updateQuestion(projectId: String, question: Question): Result<Question> { | ||
| 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<Unit> { | ||
| return runCatchingWith { | ||
| questionRemoteDataSource.deleteQuestion(projectId, questionId) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.useai.core.model.experience | ||
|
|
||
| import java.time.LocalDate | ||
|
|
||
| data class Experience( | ||
| val id: String, | ||
| val tags: List<String>, | ||
| val situation: String, | ||
| val task: String, | ||
| val action: String, | ||
| val result: String, | ||
| val category: ExperienceCategory, | ||
| val date: LocalDate, | ||
| val experienceType: String, | ||
| val title: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.useai.core.model.experience | ||
|
|
||
| data class MatchingExperience( | ||
| val experience: Experience, | ||
| val score: Float | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ActivityRetainedComponent는 화면 회전 시에도 유지되게 하는 설정값이고 ActivityScoped는 무조건 Activity 생명주기에 따라 소멸하는 설정값인 것 같은데(잘 모름)
이거 혹시 @ActivityRetainedScoped를 써야 의도대로 동작하지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 이런!! 수정할게여