Skip to content
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

[TNT-114] 회원가입 데이터 취합 및 API 연동 #54

Merged
merged 14 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ sealed interface Route {
data object Login : Route

@Serializable
data object TrainerSignUp : Route
data class TrainerSignUp(
val authId: String,
val authType: String,
val email: String,
) : Route

@Serializable
data object TraineeSignUp : Route
data class TraineeSignUp(
val authId: String,
val authType: String,
val email: String,
) : Route

@Serializable
data class TrainerConnect(val isFromMyPage: Boolean) : Route
Expand Down
29 changes: 29 additions & 0 deletions core/ui/src/main/java/co/kr/tnt/ui/util/FileUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package co.kr.tnt.ui.util

import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import java.io.File

fun Uri.toFile(context: Context): File? {
return getRealPathFromUri(this, context)?.let { filePath ->
File(filePath)
} ?: run {
Log.e("toFile", "Error creating file for URI: $this")
null
}
}

fun getRealPathFromUri(uri: Uri, context: Context): String? {
val projection = arrayOf(MediaStore.Images.Media.DATA)
val cursor: Cursor? = context.contentResolver.query(uri, projection, null, null, null)
cursor?.use {
if (it.moveToFirst()) {
val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
return it.getString(columnIndex)
}
}
return null
}
2 changes: 2 additions & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<string name="close">닫기</string>
<string name="ok">확인</string>

<string name="error_server_request_failed">서버 요청에 실패했어요</string>

<string name="trainee">트레이니</string>
<string name="trainer">트레이너</string>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package co.kr.data.network.model

import co.kr.tnt.domain.model.UserType
import kotlinx.serialization.Serializable

@Serializable
data class SignUpRequest(
val socialType: String,
val socialId: String,
val fcmToken: String,
val serviceAgreement: Boolean,
val collectionAgreement: Boolean,
val advertisementAgreement: Boolean,
val socialEmail: String,
val memberType: String,
val name: String,
val birthday: String? = null,
val height: Double? = null,
val weight: Double? = null,
val goalContents: List<String>? = null,
val cautionNote: String? = "",
)

fun UserType.toSignUpRequest(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요 클래스에서 UserType 에 대한 확장 함수를 가지고 있는건 너무 어색해보입니다..!

SignUpRequest.fromDomain 이나, SignUpRequest.fromUserType 과 같은 형태가 맞는 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵..! 다시 수정해보겠습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 제 코드는 UserType을 기반으로 새로운 SignUpRequest 객체를 생성하는 방식이라 SignUpRequest에 대한 확장 함수로 구현하기는 어려울 것 같습니다..!

companion object를 이용해 fromUserType을 구현하는건 안 될까요?😨

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얽 이거 Request 인데 제가 놓쳤네요 '-' ;;

혼동드려서 죄송합니다 ㅜㅜ

수정해주신대로 진행해도 될 것 같습니다ㅠㅠㅠ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼 지금 코드대로 머지하겠습니다!!

socialId: String,
socialType: String,
email: String,
fcmToken: String,
): SignUpRequest {
return when (this) {
is UserType.Trainer -> SignUpRequest(
memberType = "trainer",
name = name,
birthday = null,
height = null,
weight = null,
goalContents = null,
cautionNote = null,
socialType = socialType,
socialId = socialId,
socialEmail = email,
fcmToken = fcmToken,
serviceAgreement = true,
collectionAgreement = true,
advertisementAgreement = true,
)

is UserType.Trainee -> SignUpRequest(
memberType = "trainee",
name = name,
birthday = birthday?.toString(),
height = height.toDouble(),
weight = weight,
goalContents = ptPurpose,
cautionNote = caution?.ifBlank { null },
socialType = socialType,
socialId = socialId,
socialEmail = email,
fcmToken = fcmToken,
serviceAgreement = true,
collectionAgreement = true,
advertisementAgreement = true,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package co.kr.data.network.model

import co.kr.tnt.domain.model.SignUpResult
import kotlinx.serialization.Serializable

@Serializable
data class SignUpResponse(
val memberType: String,
val sessionId: String,
val name: String,
val profileImageUrl: String,
)

fun SignUpResponse.toDomain(): SignUpResult {
return SignUpResult(
memberType = memberType,
sessionId = sessionId,
name = name,
profileImageUrl = profileImageUrl,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ package co.kr.data.network.service

import co.kr.data.network.model.LoginRequest
import co.kr.data.network.model.LoginResponse
import co.kr.data.network.model.SignUpResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part

interface ApiService {
@POST("/login")
suspend fun postLogin(
@Body request: LoginRequest,
): LoginResponse

@Multipart
@POST("/members/sign-up")
suspend fun postSignUp(
@Part profileImage: MultipartBody.Part?,
@Part("request") request: RequestBody,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 @Body 로는 불가능한가요?

Copy link
Contributor Author

@SeonJeongk SeonJeongk Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multipart/form-data요청에서는 @Body 대신 @Part 또는@PartMap을 사용해야 한다고 해서 @Part로 구현했습니다!

@Body로 수정해서 호출해보면 아래와 같은 오류가 뜹니다

java.lang.IllegalArgumentException: @Body parameters cannot be used with form or multi-part encoding. (parameter #2)

): SignUpResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package co.kr.data.network.source

import co.kr.data.network.model.SignUpResponse
import co.kr.data.network.service.ApiService
import co.kr.data.network.util.networkHandler
import okhttp3.MultipartBody
import okhttp3.RequestBody
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class SignUpRemoteDataSource @Inject constructor(
private val apiService: ApiService,
) {
suspend fun postSignUp(
profileImage: MultipartBody.Part?,
request: RequestBody,
): SignUpResponse = networkHandler {
apiService.postSignUp(profileImage, request)
}
}
2 changes: 2 additions & 0 deletions data/repository/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ dependencies {
implementation(projects.domain)
implementation(projects.data.network)
implementation(projects.data.storage)
implementation(libs.okhttp.logging)
implementation(libs.kotlinx.serialization.json)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package co.kr.data.repository

import co.kr.data.network.model.SignUpRequest
import co.kr.data.network.model.toDomain
import co.kr.data.network.model.toSignUpRequest
import co.kr.data.network.source.SignUpRemoteDataSource
import co.kr.data.storage.source.SessionLocalDataSource
import co.kr.tnt.domain.model.SignUpResult
import co.kr.tnt.domain.model.UserType
import co.kr.tnt.domain.repository.SignUpRepository
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import javax.inject.Inject

class SignUpRepositoryImpl @Inject constructor(
private val signupRemoteDataSource: SignUpRemoteDataSource,
private val sessionLocalDataSource: SessionLocalDataSource,
private val json: Json,
) : SignUpRepository {
override suspend fun signUp(
profileImage: File?,
userType: UserType,
socialId: String,
socialType: String,
email: String,
): SignUpResult {
val profileImagePart = profileImage?.let {
val requestFile = it.asRequestBody("image/*".toMediaTypeOrNull())
MultipartBody.Part.createFormData("profileImage", it.name, requestFile)
}

// TODO FCM token
val signUpRequest = userType.toSignUpRequest(
socialId = socialId,
socialType = socialType,
email = email,
fcmToken = "EMPTY",
)
val requestBody = signUpRequest.toRequestBody(Json)

val response = signupRemoteDataSource.postSignUp(
profileImage = profileImagePart,
request = requestBody,
)

sessionLocalDataSource.updateSessionId(response.sessionId)

return response.toDomain()
}

private fun SignUpRequest.toRequestBody(json: Json): RequestBody {
val jsonString = json.encodeToString(this)
return jsonString.toRequestBody("application/json".toMediaTypeOrNull())
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package co.kr.data.repository.di

import co.kr.data.repository.LoginRepositoryImpl
import co.kr.data.repository.SignUpRepositoryImpl
import co.kr.tnt.domain.repository.LoginRepository
import co.kr.tnt.domain.repository.SignUpRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -14,4 +16,9 @@ internal abstract class RepositoryModule {
abstract fun bindsLoginRepository(
repository: LoginRepositoryImpl,
): LoginRepository

@Binds
abstract fun bindsSignUpRepository(
repository: SignUpRepositoryImpl,
): SignUpRepository
}
8 changes: 8 additions & 0 deletions domain/src/main/java/co/kr/tnt/domain/model/SignUpResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package co.kr.tnt.domain.model

data class SignUpResult(
val memberType: String,
val sessionId: String,
val name: String,
val profileImageUrl: String,
)
25 changes: 14 additions & 11 deletions domain/src/main/java/co/kr/tnt/domain/model/UserType.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package co.kr.tnt.domain.model

import java.time.LocalDate

sealed class UserType {
abstract val id: String
abstract val name: String
abstract val image: String?

data class Trainer(
override val id: String = "",
override val name: String = "",
override val image: String? = null,
override val id: String,
override val name: String,
override val image: String?,
) : UserType()

data class Trainee(
override val id: String = "",
override val name: String = "",
override val image: String? = null,
val age: Int = 0,
val weight: Float = 0f,
val height: Int = 0,
val ptPurpose: List<String> = emptyList(),
val caution: String? = null,
override val id: String,
override val name: String,
override val image: String?,
val birthday: LocalDate?,
val age: Int?,
val weight: Double,
val height: Int,
val ptPurpose: List<String>,
val caution: String?,
) : UserType()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package co.kr.tnt.domain.repository

import co.kr.tnt.domain.model.SignUpResult
import co.kr.tnt.domain.model.UserType
import java.io.File

interface SignUpRepository {
suspend fun signUp(
profileImage: File?,
userType: UserType,
socialId: String,
socialType: String,
email: String,
): SignUpResult
}
19 changes: 18 additions & 1 deletion feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import co.kr.tnt.roleselect.navigateToRoleSelection
import co.kr.tnt.roleselect.roleSelectionScreen
import co.kr.tnt.trainee.connect.navigation.navigateToTraineeConnect
import co.kr.tnt.trainee.connect.navigation.traineeConnectScreen
import co.kr.tnt.trainee.signup.navigation.navigateToTraineeSignUp
import co.kr.tnt.trainee.signup.navigation.traineeSignUpScreen
import co.kr.tnt.trainer.connect.navigation.navigateToTrainerConnect
import co.kr.tnt.trainer.connect.navigation.trainerConnectScreen
import co.kr.tnt.trainer.signup.navigation.navigateToTrainerSignUp
import co.kr.tnt.trainer.signup.navigation.trainerSignUpScreen

@Composable
Expand Down Expand Up @@ -50,6 +52,22 @@ fun TnTNavHost(
)
},
)
roleSelectionScreen(
navigateToTraineeSignUp = { authId, authType, email ->
navController.navigateToTraineeSignUp(
authId = authId,
authType = authType,
email = email,
)
},
navigateToTrainerSignUp = { authId, authType, email ->
navController.navigateToTrainerSignUp(
authId = authId,
authType = authType,
email = email,
)
},
)
trainerSignUpScreen(
navigateToPrevious = { navController.popBackStack() },
navigateToConnect = { navController.navigateToTrainerConnect(isFromMyPage = false) },
Expand All @@ -70,7 +88,6 @@ fun TnTNavHost(
navController.navigateToHome(isTrainer = false, clearBackStack = true)
},
)
roleSelectionScreen()
homeNavGraph()
}
}
Expand Down
Loading