diff --git a/core/common/src/main/java/com/example/common/util/Event.kt b/core/common/src/main/java/com/example/common/util/Event.kt new file mode 100644 index 00000000..5b31e6d6 --- /dev/null +++ b/core/common/src/main/java/com/example/common/util/Event.kt @@ -0,0 +1,59 @@ +package com.example.common.util + +sealed class Event( + val data: T? = null +) { + + object Loading : Event() + + /** + * 성공 + */ + class Success(data: T? = null) : Event(data = data) + + /** + * 400번 요청이 올바르지 않은 경우 + */ + object BadRequest : Event() + + /** + * 401번 비인증 요청 + */ + object Unauthorized : Event() + + /** + * 403번 권한이 없음 + */ + object ForBidden : Event() + + /** + * 404 찾을 수 없는 경우 + */ + object NotFound : Event() + + /** + * 406 맞는 규격이 없는 경우 + */ + object NotAcceptable : Event() + + /** + * 408 요청이 너무 오래 걸리는 경우 + */ + object TimeOut : Event() + + /** + * 409 권한이 없을 때 + */ + object Conflict : Event() + + /** + * 50X 서버에러 + */ + object Server : Event() + + /** + * 예상치 못한 에러 + */ + object UnKnown : Event() + +} \ No newline at end of file diff --git a/core/common/src/main/java/com/example/common/util/errorHandling.kt b/core/common/src/main/java/com/example/common/util/errorHandling.kt new file mode 100644 index 00000000..afef3565 --- /dev/null +++ b/core/common/src/main/java/com/example/common/util/errorHandling.kt @@ -0,0 +1,67 @@ +package com.example.common.util + +import android.util.Log +import com.example.common.exception.* + +suspend fun Throwable.errorHandling( + badRequestAction: suspend () -> Unit = {}, + unauthorizedAction: suspend () -> Unit = {}, + forBiddenAction: suspend () -> Unit = {}, + notFoundAction: suspend () -> Unit = {}, + notAcceptableAction: suspend () -> Unit = {}, + timeOutAction: suspend () -> Unit = {}, + conflictAction: suspend () -> Unit = {}, + serverAction: suspend () -> Unit = {}, + unknownAction: suspend () -> Unit = {}, +): Event = + when (this) { + is BadRequestException -> { + errorLog("BadRequestException", message) + badRequestAction() + Event.BadRequest + } + is UnauthorizedException, is NeedLoginException -> { + errorLog("UnauthorizedException", message) + unauthorizedAction() + Event.Unauthorized + } + is ForBiddenException -> { + errorLog("ForBiddenException", message) + forBiddenAction() + Event.ForBidden + } + is NotFoundException -> { + errorLog("NotFoundException", message) + notFoundAction() + Event.NotFound + } + is NotAcceptableException -> { + errorLog("NotAcceptableException", message) + notAcceptableAction() + Event.NotAcceptable + } + is TimeOutException -> { + errorLog("TimeOutException", message) + timeOutAction() + Event.TimeOut + } + is ConflictException -> { + errorLog("ConflictException", message) + conflictAction() + Event.Conflict + } + is ServerException -> { + errorLog("ServerException", message) + serverAction() + Event.Server + } + else -> { + errorLog("UnKnownException", message) + unknownAction() + Event.UnKnown + } + } + +private fun errorLog(tag: String, msg: String?) { + Log.d("ErrorHandling-$tag", msg ?: "알 수 없는 오류") +} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt index b1e2fcc6..fa183c26 100644 --- a/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt @@ -1,5 +1,7 @@ package com.stackknowledge.di +import com.stackknowledge.repository.mission.MissionRepository +import com.stackknowledge.repository.mission.MissionRepositoryImpl import com.stackknowledge.repository.student.StudentRepository import com.stackknowledge.repository.student.StudentRepositoryImpl import dagger.Binds @@ -10,6 +12,11 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) abstract class RepositoryModule { + @Binds + abstract fun bindMissionRepository( + missionRepositoryImpl: MissionRepositoryImpl + ): MissionRepository + @Binds abstract fun bindStudentRepository( studentRepositoryImpl: StudentRepositoryImpl diff --git a/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepository.kt b/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepository.kt new file mode 100644 index 00000000..50a95050 --- /dev/null +++ b/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepository.kt @@ -0,0 +1,15 @@ +package com.stackknowledge.repository.mission + +import kotlinx.coroutines.flow.Flow +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel + +interface MissionRepository { + fun getMission(): Flow + + fun detailMission(missionId: DetailMissionRequestModel): Flow + + fun createMission(body: CreateMissionRequestModel): Flow +} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepositoryImpl.kt b/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepositoryImpl.kt new file mode 100644 index 00000000..1ea4a7b3 --- /dev/null +++ b/core/data/src/main/kotlin/com/stackknowledge/repository/mission/MissionRepositoryImpl.kt @@ -0,0 +1,25 @@ +package com.stackknowledge.repository.mission + +import com.stackknowledge.datasource.mission.MissionDataSource +import kotlinx.coroutines.flow.Flow +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel +import javax.inject.Inject + +class MissionRepositoryImpl @Inject constructor( + private val missionDataSource: MissionDataSource +): MissionRepository { + override fun getMission(): Flow { + return missionDataSource.getMission() + } + + override fun detailMission(missionId: DetailMissionRequestModel): Flow { + return missionDataSource.detailMission(missionId = missionId) + } + + override fun createMission(body: CreateMissionRequestModel): Flow { + return missionDataSource.createMission(body = body) + } +} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/stackknowledge/repository/user/UserRepositoryImpl.kt b/core/data/src/main/kotlin/com/stackknowledge/repository/user/UserRepositoryImpl.kt index aca260cc..04315372 100644 --- a/core/data/src/main/kotlin/com/stackknowledge/repository/user/UserRepositoryImpl.kt +++ b/core/data/src/main/kotlin/com/stackknowledge/repository/user/UserRepositoryImpl.kt @@ -1,13 +1,8 @@ package com.stackknowledge.repository.user import com.stackknowledge.datasource.user.UserDataSource -import com.stackknowledge.dto.request.user.ApproveRequest -import com.stackknowledge.dto.request.user.ScoreRequest -import com.stackknowledge.dto.response.user.DetailSolveMissionResponse -import com.stackknowledge.dto.response.user.GetRequestSignUpTeacherResponse -import com.stackknowledge.dto.response.user.GetSolveMissionResponse import com.stackknowledge.mapper.request.user.toDto -import com.stackknowledge.mapper.response.toModel +import com.stackknowledge.mapper.response.user.toModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import remote.request.user.ApproveRequestModel diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/CreateMissionUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/CreateMissionUseCase.kt new file mode 100644 index 00000000..3c1e2f2f --- /dev/null +++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/CreateMissionUseCase.kt @@ -0,0 +1,13 @@ +package com.stackknowledge.usecase.misson + +import com.stackknowledge.repository.mission.MissionRepository +import remote.request.mission.CreateMissionRequestModel +import javax.inject.Inject + +class CreateMissionUseCase @Inject constructor( + private val missionRepository: MissionRepository +) { + suspend operator fun invoke(body: CreateMissionRequestModel) = runCatching { + missionRepository.createMission(body = body) + } +} \ No newline at end of file diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/DetailMissionUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/DetailMissionUseCase.kt new file mode 100644 index 00000000..cadc992d --- /dev/null +++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/DetailMissionUseCase.kt @@ -0,0 +1,13 @@ +package com.stackknowledge.usecase.misson + +import com.stackknowledge.repository.mission.MissionRepository +import remote.request.mission.DetailMissionRequestModel +import javax.inject.Inject + +class DetailMissionUseCase @Inject constructor( + private val missionRepository: MissionRepository +) { + suspend operator fun invoke(missionId: DetailMissionRequestModel) = runCatching { + missionRepository.detailMission(missionId = missionId) + } +} \ No newline at end of file diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/GetMissionUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/GetMissionUseCase.kt new file mode 100644 index 00000000..d9b15987 --- /dev/null +++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/misson/GetMissionUseCase.kt @@ -0,0 +1,12 @@ +package com.stackknowledge.usecase.misson + +import com.stackknowledge.repository.mission.MissionRepository +import javax.inject.Inject + +class GetMissionUseCase @Inject constructor( + private val missionRepository: MissionRepository +) { + suspend operator fun invoke() = runCatching { + missionRepository.getMission() + } +} \ No newline at end of file diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index fa854b45..beb0ab02 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -6,4 +6,5 @@ plugins { dependencies { implementation(libs.kotlinx.datetime) implementation(libs.retrofit.core) + implementation(libs.moshi) } \ No newline at end of file diff --git a/core/model/src/main/kotlin/enumdata/MissionStatus.kt b/core/model/src/main/kotlin/enumdata/MissionStatus.kt new file mode 100644 index 00000000..9fe37c1b --- /dev/null +++ b/core/model/src/main/kotlin/enumdata/MissionStatus.kt @@ -0,0 +1,7 @@ +package enumdata + +enum class MissionStatus { + CLOSED, // Mission을 풀 수 없는 상태 + OPENED, // Mission을 풀 수 있는 상태 + AVAILABLE_OPEN, // Mission을 내일 풀 수 있는 상태 +} \ No newline at end of file diff --git a/core/model/src/main/kotlin/enumdatatype/MissionStatus.kt b/core/model/src/main/kotlin/enumdatatype/MissionStatus.kt new file mode 100644 index 00000000..842feaed --- /dev/null +++ b/core/model/src/main/kotlin/enumdatatype/MissionStatus.kt @@ -0,0 +1,7 @@ +package enumdatatype + +enum class MissionStatus { + CLOSED, + OPENED, + AVAILABLE_OPEN, +} \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/request/mission/CreateMissionRequestModel.kt b/core/model/src/main/kotlin/remote/request/mission/CreateMissionRequestModel.kt new file mode 100644 index 00000000..863202f7 --- /dev/null +++ b/core/model/src/main/kotlin/remote/request/mission/CreateMissionRequestModel.kt @@ -0,0 +1,11 @@ +package remote.request.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class CreateMissionRequestModel( + @Json(name = "title") val title: String, + @Json(name = "content") val content: String, + @Json(name = "timeLimit") val timeLimit: Int, +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/request/mission/DetailMissionRequestModel.kt b/core/model/src/main/kotlin/remote/request/mission/DetailMissionRequestModel.kt new file mode 100644 index 00000000..ee29b629 --- /dev/null +++ b/core/model/src/main/kotlin/remote/request/mission/DetailMissionRequestModel.kt @@ -0,0 +1,10 @@ +package remote.request.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import java.util.UUID + +@JsonClass(generateAdapter = true) +data class DetailMissionRequestModel( + @Json(name = "mission_id") val missionId: UUID, +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/request/student/UploadProfileImageRequest.kt b/core/model/src/main/kotlin/remote/request/student/UploadProfileImageRequest.kt index bf8b5334..ff2727bd 100644 --- a/core/model/src/main/kotlin/remote/request/student/UploadProfileImageRequest.kt +++ b/core/model/src/main/kotlin/remote/request/student/UploadProfileImageRequest.kt @@ -4,4 +4,4 @@ import okhttp3.MultipartBody data class UploadProfileImageRequest( val image: MultipartBody.Part -) +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/response/mission/DetailMissionResponseModel.kt b/core/model/src/main/kotlin/remote/response/mission/DetailMissionResponseModel.kt new file mode 100644 index 00000000..b135e1e8 --- /dev/null +++ b/core/model/src/main/kotlin/remote/response/mission/DetailMissionResponseModel.kt @@ -0,0 +1,11 @@ +package remote.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class DetailMissionResponseModel( + @Json(name = "title") val title: String, + @Json(name = "content") val content: String, + @Json(name = "timeLimit") val timeLimit: Int, +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt b/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt new file mode 100644 index 00000000..81338719 --- /dev/null +++ b/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt @@ -0,0 +1,12 @@ +package remote.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import enumdatatype.MissionStatus +import remote.user.UserModel +import java.util.UUID + +@JsonClass(generateAdapter = true) +data class MissionResponseModel( + @Json(name = "body") val missions: MissionsModel, +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt b/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt new file mode 100644 index 00000000..390b7b55 --- /dev/null +++ b/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt @@ -0,0 +1,16 @@ +package remote.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import enumdatatype.MissionStatus +import remote.user.UserModel +import java.util.UUID + +@JsonClass(generateAdapter = true) +data class MissionsModel( + @Json(name = "id") val id: UUID, + @Json(name = "title") val title: String, + @Json(name = "point") val point: Int, + @Json(name = "missionStatus") val missionStatus: MissionStatus, + @Json(name = "user") val user: UserModel, +) diff --git a/core/model/src/main/kotlin/remote/response/student/UploadProfileImageResponse.kt b/core/model/src/main/kotlin/remote/response/student/UploadProfileImageResponse.kt index 812d948a..95252c25 100644 --- a/core/model/src/main/kotlin/remote/response/student/UploadProfileImageResponse.kt +++ b/core/model/src/main/kotlin/remote/response/student/UploadProfileImageResponse.kt @@ -2,4 +2,4 @@ package remote.response.student data class UploadProfileImageResponse( val fileName: String, -) +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/response/user/DetailSolveMissionResponseModel.kt b/core/model/src/main/kotlin/remote/response/user/DetailSolveMissionResponseModel.kt index 6e7231a3..014d7524 100644 --- a/core/model/src/main/kotlin/remote/response/user/DetailSolveMissionResponseModel.kt +++ b/core/model/src/main/kotlin/remote/response/user/DetailSolveMissionResponseModel.kt @@ -5,4 +5,4 @@ data class DetailSolveMissionResponseModel( val solveId: UUID, val title: String, val solution: String, -) +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/remote/response/user/GetRequestSignUpTeacherResponseModel.kt b/core/model/src/main/kotlin/remote/response/user/GetRequestSignUpTeacherResponseModel.kt index f75edc12..2a00a603 100644 --- a/core/model/src/main/kotlin/remote/response/user/GetRequestSignUpTeacherResponseModel.kt +++ b/core/model/src/main/kotlin/remote/response/user/GetRequestSignUpTeacherResponseModel.kt @@ -7,4 +7,4 @@ data class GetRequestSignUpTeacherResponseModel( val userId: UUID, val name: String, val createdAt: LocalDateTime, -) +) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/api/MissionAPI.kt b/core/network/src/main/kotlin/com/stackknowledge/api/MissionAPI.kt new file mode 100644 index 00000000..85b151c9 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/api/MissionAPI.kt @@ -0,0 +1,27 @@ +package com.stackknowledge.api + +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel +import com.stackknowledge.dto.request.mission.CreateMissionRequest +import com.stackknowledge.dto.response.mission.GetMissionListResponse +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path + +interface MissionAPI { + @GET("/mission") + suspend fun getMission(): MissionResponseModel + + @GET("/mission/{mission_id}") + suspend fun getDetailMission( + @Path("mission_id") missionId: DetailMissionRequestModel, + ): DetailMissionResponseModel + + @POST("/mission") + suspend fun createMission( + @Body body: CreateMissionRequestModel, + ) +} \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSource.kt b/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSource.kt new file mode 100644 index 00000000..4306a767 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSource.kt @@ -0,0 +1,15 @@ +package com.stackknowledge.datasource.mission + +import kotlinx.coroutines.flow.Flow +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel + +interface MissionDataSource { + fun getMission(): Flow + + fun detailMission(missionId: DetailMissionRequestModel): Flow + + fun createMission(body: CreateMissionRequestModel): Flow +} \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSourceImpl.kt b/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSourceImpl.kt new file mode 100644 index 00000000..ac5cb314 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/datasource/mission/MissionDataSourceImpl.kt @@ -0,0 +1,41 @@ +package com.stackknowledge.datasource.mission + +import com.stackknowledge.api.MissionAPI +import com.stackknowledge.util.StackKnowledgeApiHandler +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel +import javax.inject.Inject + +class MissionDataSourceImpl @Inject constructor( + private val missionAPI: MissionAPI +) : MissionDataSource { + override fun getMission(): Flow = flow { + emit( + StackKnowledgeApiHandler() + .httpRequest { missionAPI.getMission() } + .sendRequest() + ) + }.flowOn(Dispatchers.IO) + + override fun detailMission(missionId: DetailMissionRequestModel): Flow = flow { + emit( + StackKnowledgeApiHandler() + .httpRequest { missionAPI.getDetailMission(missionId = missionId) } + .sendRequest() + ) + }.flowOn(Dispatchers.IO) + + override fun createMission(body: CreateMissionRequestModel): Flow = flow { + emit( + StackKnowledgeApiHandler() + .httpRequest { missionAPI.createMission(body = body) } + .sendRequest() + ) + }.flowOn(Dispatchers.IO) +} \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt b/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt index 91b5ee5d..69535588 100644 --- a/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt @@ -1,16 +1,78 @@ package com.stackknowledge.di +import com.msg.network.BuildConfig +import com.squareup.moshi.Moshi +import com.stackknowledge.api.MissionAPI import com.stackknowledge.api.StudentAPI import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import okhttp3.CookieJar +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object NetworkModule { + + @Provides + @Singleton + fun provideOkhttpClient( + httpLoggingInterceptor: HttpLoggingInterceptor, + ): OkHttpClient { + return OkHttpClient.Builder() + .cookieJar(CookieJar.NO_COOKIES) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .addInterceptor(httpLoggingInterceptor) + .build() + } + + @Provides + @Singleton + fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { + return HttpLoggingInterceptor().apply { + level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE + } + } + + @Provides + @Singleton + fun provideMoshiInstance(): Moshi { + return Moshi.Builder().build() + } + + @Provides + @Singleton + fun provideConverterFactory(moshi: Moshi): MoshiConverterFactory { + return MoshiConverterFactory.create(moshi) + } + + @Provides + @Singleton + fun provideRetrofitInstance( + okHttpClient: OkHttpClient, + moshiConverterFactory: MoshiConverterFactory, + ): Retrofit { + return Retrofit.Builder() + .baseUrl(BuildConfig.BASE_URL) + .client(okHttpClient) + .addConverterFactory(moshiConverterFactory) + .build() + } + @Provides + @Singleton + fun provideMissionAPI(retrofit: Retrofit): MissionAPI = + retrofit.create(MissionAPI::class.java) + @Provides + @Singleton fun provideStudentAPI(retrofit: Retrofit): StudentAPI = retrofit.create(StudentAPI::class.java) } \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt b/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt index b72feced..ae473068 100644 --- a/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt +++ b/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt @@ -1,16 +1,26 @@ package com.stackknowledge.di +import com.stackknowledge.datasource.mission.MissionDataSource +import com.stackknowledge.datasource.mission.MissionDataSourceImpl import com.stackknowledge.datasource.student.StudentDataSource import com.stackknowledge.datasource.student.StudentDataSourceImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) abstract class RemoteDataSourceModule { @Binds + @Singleton + abstract fun bindMissionDataSource( + missionDataSourceImpl: MissionDataSourceImpl + ): MissionDataSource + + @Binds + @Singleton abstract fun bindStudentDataSource( studentDataSourceImpl: StudentDataSourceImpl ): StudentDataSource diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/CreateMissionRequest.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/CreateMissionRequest.kt new file mode 100644 index 00000000..b9594c18 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/CreateMissionRequest.kt @@ -0,0 +1,11 @@ +package com.stackknowledge.dto.request.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class CreateMissionRequest ( + @Json(name = "title") val title: String, + @Json(name = "content") val content: String, + @Json(name = "timeLimit") val timeLimit: Int, +) diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/DetailMissionRequest.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/DetailMissionRequest.kt new file mode 100644 index 00000000..97d3dcf9 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/request/mission/DetailMissionRequest.kt @@ -0,0 +1,10 @@ +package com.stackknowledge.dto.request.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import java.util.UUID + +@JsonClass(generateAdapter = true) +data class DetailMissionRequest( + @Json(name = "mission_id") val missionId: UUID, +) diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/DetailMissionResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/DetailMissionResponse.kt new file mode 100644 index 00000000..db57b3e8 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/DetailMissionResponse.kt @@ -0,0 +1,11 @@ +package com.stackknowledge.dto.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class DetailMissionResponse( + @Json(name = "title") val title: String, + @Json(name = "content") val content: String, + @Json(name = "timeLimit") val timeLimit: Int, +) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt new file mode 100644 index 00000000..0935e52a --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt @@ -0,0 +1,19 @@ +package com.stackknowledge.dto.response.mission + +import com.stackknowledge.dto.user.User +import enumdata.MissionStatus +import java.util.UUID + +data class GetMissionListResponse( + val id: UUID, + val title: String, + val point: Int, + val missionStatus: MissionStatus, + val user: User, +) { + data class User( + val id: UUID, + val name: String, + val profileImage: String?, + ) +} diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt new file mode 100644 index 00000000..e41cded1 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt @@ -0,0 +1,9 @@ +package com.stackknowledge.dto.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class MissionResponse( + @Json(name = "body") val missions: Missions, +) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt new file mode 100644 index 00000000..af28f99c --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt @@ -0,0 +1,16 @@ +package com.stackknowledge.dto.response.mission + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import enumdatatype.MissionStatus +import remote.user.UserModel +import java.util.UUID + +@JsonClass(generateAdapter = true) +data class Missions( + @Json(name = "id") val id: UUID, + @Json(name = "title") val title: String, + @Json(name = "point") val point: Int, + @Json(name = "missionStatus") val missionStatus: MissionStatus, + @Json(name = "user") val user: UserModel, +) diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/CreateMissionRequestMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/CreateMissionRequestMapper.kt new file mode 100644 index 00000000..d1337a38 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/CreateMissionRequestMapper.kt @@ -0,0 +1,11 @@ +package com.stackknowledge.mapper.request.mission + +import com.stackknowledge.dto.request.mission.CreateMissionRequest +import remote.request.mission.CreateMissionRequestModel + +fun CreateMissionRequestModel.toDto(): CreateMissionRequest = + CreateMissionRequest( + title = this.title, + content = this.content, + timeLimit = this.timeLimit, + ) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/DetailMissionRequestMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/DetailMissionRequestMapper.kt new file mode 100644 index 00000000..4087af98 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/mission/DetailMissionRequestMapper.kt @@ -0,0 +1,9 @@ +package com.stackknowledge.mapper.request.mission + +import com.stackknowledge.dto.request.mission.DetailMissionRequest +import remote.request.mission.DetailMissionRequestModel + +fun DetailMissionRequestModel.toDto(): DetailMissionRequest = + DetailMissionRequest( + missionId = this.missionId + ) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/DetailMissionResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/DetailMissionResponseMapper.kt new file mode 100644 index 00000000..7933bb86 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/DetailMissionResponseMapper.kt @@ -0,0 +1,11 @@ +package com.stackknowledge.mapper.response.mission + +import com.stackknowledge.dto.response.mission.DetailMissionResponse +import remote.response.mission.DetailMissionResponseModel + +fun DetailMissionResponse.toModel(): DetailMissionResponseModel = + DetailMissionResponseModel( + title = this.title, + content = this.content, + timeLimit = this.timeLimit, + ) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionResponseMapper.kt new file mode 100644 index 00000000..cc34eb65 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionResponseMapper.kt @@ -0,0 +1,9 @@ +package com.stackknowledge.mapper.response.mission + +import com.stackknowledge.dto.response.mission.MissionResponse +import remote.response.mission.MissionResponseModel + +fun MissionResponse.toModel(): MissionResponseModel = + MissionResponseModel( + missions = this.missions.toModel() + ) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionsMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionsMapper.kt new file mode 100644 index 00000000..238827f6 --- /dev/null +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/mission/MissionsMapper.kt @@ -0,0 +1,13 @@ +package com.stackknowledge.mapper.response.mission + +import com.stackknowledge.dto.response.mission.Missions +import remote.response.mission.MissionsModel + +fun Missions.toModel(): MissionsModel = + MissionsModel( + id = this.id, + title = this.title, + point = this.point, + missionStatus = this.missionStatus, + user = this.user, + ) \ No newline at end of file diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/DetailSolveMissionResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/DetailSolveMissionResponseMapper.kt similarity index 87% rename from core/network/src/main/kotlin/com/stackknowledge/mapper/response/DetailSolveMissionResponseMapper.kt rename to core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/DetailSolveMissionResponseMapper.kt index 14ba2153..e6095532 100644 --- a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/DetailSolveMissionResponseMapper.kt +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/DetailSolveMissionResponseMapper.kt @@ -1,4 +1,4 @@ -package com.stackknowledge.mapper.response +package com.stackknowledge.mapper.response.user import com.stackknowledge.dto.response.user.DetailSolveMissionResponse import remote.response.user.DetailSolveMissionResponseModel diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetRequestSignUpTeacherResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetRequestSignUpTeacherResponseMapper.kt similarity index 88% rename from core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetRequestSignUpTeacherResponseMapper.kt rename to core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetRequestSignUpTeacherResponseMapper.kt index 77589b09..72a200f2 100644 --- a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetRequestSignUpTeacherResponseMapper.kt +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetRequestSignUpTeacherResponseMapper.kt @@ -1,4 +1,4 @@ -package com.stackknowledge.mapper.response +package com.stackknowledge.mapper.response.user import com.stackknowledge.dto.response.user.GetRequestSignUpTeacherResponse import remote.response.user.GetRequestSignUpTeacherResponseModel diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetSolveMissionResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetSolveMissionResponseMapper.kt similarity index 90% rename from core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetSolveMissionResponseMapper.kt rename to core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetSolveMissionResponseMapper.kt index 6d3c15f3..5b888d08 100644 --- a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/GetSolveMissionResponseMapper.kt +++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/user/GetSolveMissionResponseMapper.kt @@ -1,4 +1,4 @@ -package com.stackknowledge.mapper.response +package com.stackknowledge.mapper.response.user import com.stackknowledge.dto.response.user.GetSolveMissionResponse import com.stackknowledge.mapper.user.toModel diff --git a/feature/mission/build.gradle.kts b/feature/mission/build.gradle.kts index 3aaac10b..44e6c2a9 100644 --- a/feature/mission/build.gradle.kts +++ b/feature/mission/build.gradle.kts @@ -6,4 +6,7 @@ plugins { android { namespace = "com.stackknowledge.mission" -} \ No newline at end of file +} +dependencies { + implementation(project(":core:common")) +} diff --git a/feature/mission/src/main/java/com/stackkowledge/mission/viewmodel/MissionViewModel.kt b/feature/mission/src/main/java/com/stackkowledge/mission/viewmodel/MissionViewModel.kt new file mode 100644 index 00000000..fa95e551 --- /dev/null +++ b/feature/mission/src/main/java/com/stackkowledge/mission/viewmodel/MissionViewModel.kt @@ -0,0 +1,75 @@ +package com.stackkowledge.mission.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.common.util.Event +import com.example.common.util.errorHandling +import com.stackknowledge.usecase.misson.CreateMissionUseCase +import com.stackknowledge.usecase.misson.DetailMissionUseCase +import com.stackknowledge.usecase.misson.GetMissionUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import remote.request.mission.CreateMissionRequestModel +import remote.request.mission.DetailMissionRequestModel +import remote.response.mission.DetailMissionResponseModel +import remote.response.mission.MissionResponseModel +import javax.inject.Inject + +@HiltViewModel +class MissionViewModel @Inject constructor( + private val getMissionUseCase: GetMissionUseCase, + private val detailMissionUseCase: DetailMissionUseCase, + private val createMissionUseCase: CreateMissionUseCase, +) : ViewModel() { + private val _missionRequest = MutableStateFlow>(Event.Loading) + val missionRequest = _missionRequest.asStateFlow() + + private val _detailMissionRequest = + MutableStateFlow>(Event.Loading) + val detailMissionRequest = _detailMissionRequest.asStateFlow() + + private val _createMissionRequest = MutableStateFlow>(Event.Loading) + val createMissionRequest = _createMissionRequest.asStateFlow() + + internal fun getMission() = viewModelScope.launch { + getMissionUseCase() + .onSuccess { + it.catch { remoteError -> + _missionRequest.value = remoteError.errorHandling() + }.collect { response -> + _missionRequest.value = Event.Success(data = response) + } + } + .onFailure { + _missionRequest.value = it.errorHandling() + } + } + + internal fun detailMission(missionId: DetailMissionRequestModel) = viewModelScope.launch { + detailMissionUseCase(missionId = missionId) + .onSuccess { + it.catch { remoteError -> + _detailMissionRequest.value = remoteError.errorHandling() + }.collect { response -> + _detailMissionRequest.value = Event.Success(data = response) + } + } + .onFailure { + _detailMissionRequest.value = it.errorHandling() + } + } + + internal fun createMission(body: CreateMissionRequestModel) = viewModelScope.launch { + createMissionUseCase(body = body) + .onSuccess { + _createMissionRequest.value = Event.Success() + } + .onFailure { + _createMissionRequest.value = it.errorHandling() + } + } +} \ No newline at end of file