This commit is contained in:
imglmd
2025-12-01 16:53:39 +03:00
parent 0c5c97d2cd
commit fd8309521d
10 changed files with 139 additions and 55 deletions

View File

@@ -0,0 +1,14 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class AvailableBookingResponse(
val dates: Map<String, List<AvailablePlace>> = emptyMap()
)
@Serializable
data class AvailablePlace(
val id: Int,
val place: String
)

View File

@@ -0,0 +1,9 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class BookRequest(
val date: String,
val placeID: Int
)

View File

@@ -0,0 +1,16 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class UserInfoResponse(
val name: String,
val photoUrl: String,
val booking: Map<String, BookingItem> = emptyMap()
)
@Serializable
data class BookingItem(
val id: Int,
val place: String
)

View File

@@ -1,16 +1,13 @@
package ru.myitschool.work.data.repo package ru.myitschool.work.data.repo
import ru.myitschool.work.data.source.NetworkDataSource import ru.myitschool.work.data.source.NetworkDataSource
import ru.myitschool.work.domain.MyResult
object AuthRepository { object AuthRepository {
private var codeCache: String? = null private var codeCache: String? = null
suspend fun checkAndSave(text: String): Result<Boolean> { suspend fun checkAndSave(code: String): MyResult<Boolean> {
return NetworkDataSource.checkAuth(text).onSuccess { success -> return NetworkDataSource.checkAuth(code)
if (success) {
codeCache = text
}
}
} }
} }

View File

@@ -1,16 +1,18 @@
package ru.myitschool.work.data.source package ru.myitschool.work.data.source
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
import ru.myitschool.work.data.dto.UserInfoResponse
import ru.myitschool.work.domain.MyResult
object NetworkDataSource { object NetworkDataSource {
private val client by lazy { private val client by lazy {
@@ -28,15 +30,35 @@ object NetworkDataSource {
} }
} }
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) { suspend fun checkAuth(code: String): MyResult<Boolean> = withContext(Dispatchers.IO) {
return@withContext runCatching { try {
val response = client.get(getUrl(code, Constants.AUTH_URL)) val response = client.get(getUrl(code, Constants.AUTH_URL))
when (response.status) { when (response.status) {
HttpStatusCode.OK -> true HttpStatusCode.OK -> MyResult.Success(true)
else -> error(response.bodyAsText()) else -> {
MyResult.Error("Неверный код или ошибка сервера")
} }
} }
} catch (e: Exception) {
MyResult.Error("Сетевая ошибка")
}
} }
suspend fun getUserInfo(code: String): MyResult<UserInfoResponse> = withContext(Dispatchers.IO){
try {
val response = client.get(getUrl(code, Constants.INFO_URL))
when (response.status) {
HttpStatusCode.OK -> {
val userInfo: UserInfoResponse = response.body()
MyResult.Success(userInfo)
}
else -> {
MyResult.Error("Ошибка получения данных пользователя")
}
}
} catch (e: Exception){
MyResult.Error("Сетевая ошибка")
}
}
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
} }

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.domain
sealed class MyResult<out T> {
data class Success<T>(val data: T): MyResult<T>()
data class Error(val error: String): MyResult<Nothing>()
}

View File

@@ -1,23 +1,25 @@
package ru.myitschool.work.domain.auth package ru.myitschool.work.domain.auth
import androidx.datastore.dataStore
import ru.myitschool.work.data.repo.AuthRepository import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.MyResult
import ru.myitschool.work.util.DataStoreManager import ru.myitschool.work.util.DataStoreManager
class CheckAndSaveAuthCodeUseCase( class CheckAndSaveAuthCodeUseCase(
private val repository: AuthRepository, private val repository: AuthRepository,
) { ) {
suspend operator fun invoke( suspend operator fun invoke(code: String): MyResult<Unit> {
code: String return when (val repoResult = repository.checkAndSave(code)) {
): Result<Unit> = try { is MyResult.Success -> {
val response = repository.checkAndSave(code) if (repoResult.data) {
if (response.isSuccess) {
DataStoreManager.saveAuthCode(code) DataStoreManager.saveAuthCode(code)
Result.success(Unit) MyResult.Success(Unit)
} else { } else {
Result.failure(Exception("Неверный код")) MyResult.Error("Неверный код")
} }
} catch (e: Exception){ }
Result.failure(e) is MyResult.Error -> {
MyResult.Error(repoResult.error)
}
}
} }
} }

View File

@@ -0,0 +1,4 @@
package ru.myitschool.work.domain.user
class GetUserUseCase {
}

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.domain.user
data class User(
val name: String,
val imageUrl: String
)

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.MyResult
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
class AuthViewModel : ViewModel() { class AuthViewModel : ViewModel() {
@@ -17,42 +18,49 @@ class AuthViewModel : ViewModel() {
fun onIntent(intent: AuthIntent) { fun onIntent(intent: AuthIntent) {
when (intent) { when (intent) {
is AuthIntent.Send -> { AuthIntent.Send -> sendCode()
viewModelScope.launch { is AuthIntent.TextInput -> updateCode(intent.text)
_uiState.update { }
it.copy(isLoading = true)
} }
checkAndSaveAuthCodeUseCase(_uiState.value.code).fold(
onSuccess = {
_uiState.update {
it.copy(
isLoading = false,
isAuthenticated = true
)
}
}, private fun updateCode(newCode: String) {
onFailure = { error -> val trimmed = newCode.trim()
val isValid = trimmed.length == 4 && trimmed.matches(Regex("^[a-zA-Z0-9]{4}$"))
_uiState.update { _uiState.update {
it.copy( it.copy(
isLoading = false, code = trimmed,
error = error.message ?: "Ошибка", isButtonEnabled = isValid,
isAuthenticated = false
)
}
}
)
}
}
is AuthIntent.TextInput -> _uiState.update {
it.copy(
code = intent.text,
isButtonEnabled = intent.text.length == 4 &&
uiState.value.code.matches(Regex("^[a-zA-Z0-9]+$")),
error = null error = null
) )
} }
} }
private fun sendCode() {
val currentCode = _uiState.value.code
if (currentCode.length != 4 || !currentCode.matches(Regex("^[a-zA-Z0-9]{4}$"))) return
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true, error = null) }
when (val result = checkAndSaveAuthCodeUseCase(currentCode)){
is MyResult.Success<*> -> {
_uiState.update {
it.copy(isLoading = false, isAuthenticated = true)
}
}
is MyResult.Error -> {
_uiState.update {
it.copy(
isLoading = false,
error = result.error,
isAuthenticated = false
)
}
}
}
}
} }
} }