From 85a5c82d8cd908315f15633b80f97ace241cb870 Mon Sep 17 00:00:00 2001 From: Terebov_Maksim Date: Wed, 19 Feb 2025 14:12:47 +0300 Subject: [PATCH 1/4] day2_commit_10_fixed_alotofbugs_added_admin_logic --- app/src/main/AndroidManifest.xml | 2 +- .../java/ru/myitschool/work/SessionManager.kt | 9 +- .../java/ru/myitschool/work/api/ApiService.kt | 34 ++++- .../myitschool/work/ui/Main/AdminFragment.kt | 136 ++++++++++++------ .../myitschool/work/ui/Main/MainFragment.kt | 49 ++++--- .../myitschool/work/ui/login/LoginFragment.kt | 12 +- .../work/ui/login/LoginViewModel.kt | 16 ++- app/src/main/res/layout/fragment_admin.xml | 48 +++++-- app/src/main/res/layout/fragment_main.xml | 9 ++ app/src/main/res/navigation/nav_graph.xml | 6 + 10 files changed, 238 insertions(+), 83 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 000a643..2a9c5ce 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Default" - tools:targetApi="31"> + tools:targetApi="34"> diff --git a/app/src/main/java/ru/myitschool/work/SessionManager.kt b/app/src/main/java/ru/myitschool/work/SessionManager.kt index 699b478..4f43031 100644 --- a/app/src/main/java/ru/myitschool/work/SessionManager.kt +++ b/app/src/main/java/ru/myitschool/work/SessionManager.kt @@ -1,5 +1,12 @@ package ru.myitschool.work object SessionManager { - var userLogin: String = "" + var userLogin: String? = null // Логин пользователя + var userRole: String? = null // Роль пользователя + + // Метод для очистки данных сессии + fun clearSession() { + userLogin = null + userRole = null + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/ApiService.kt b/app/src/main/java/ru/myitschool/work/api/ApiService.kt index f8b780a..a4b433b 100644 --- a/app/src/main/java/ru/myitschool/work/api/ApiService.kt +++ b/app/src/main/java/ru/myitschool/work/api/ApiService.kt @@ -4,21 +4,41 @@ import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header -import retrofit2.http.PATCH +import retrofit2.http.POST import retrofit2.http.Path interface ApiService { @GET("api/{login}/auth") suspend fun authenticate( @Path("login") login: String, - @Header("Authorization") authorization: String // Добавляем заголовок Authorization - ): Response + @Header("Authorization") authorization: String + ): Response // Изменяем на UserAuthResponse @GET("api/{login}/info") - suspend fun getUserInfo(@Path("login") login: String): Response> // Возвращаем Map вместо UserInfo + suspend fun getUserInfo(@Path("login") login: String): Response> - @PATCH("api/{login}/open") - suspend fun openDoor(@Path("login") login: String, @Body body: OpenDoorRequest): Response + @POST("api/employee/toggleAccess") + suspend fun toggleAccess(@Body request: ToggleAccessRequest): Response + + // Метод для получения информации о сотруднике + @GET("api/employee/{login}") + suspend fun getEmployeeInfo(@Path("login") login: String): Response } -data class OpenDoorRequest(val value: String) \ No newline at end of file +// Модель данных для ответа аутентификации +data class UserAuthResponse( + val role: String // Добавляем поле для роли +) + +// Модель данных для информации о сотруднике +data class EmployeeData( + val name: String, + val position: String, + val lastVisit: String +) + +// Модель данных для запроса блокировки/разблокировки доступа +data class ToggleAccessRequest( + val login: String, + val action: String // "block" или "unblock" +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt b/app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt index f8ab11b..e104bb1 100644 --- a/app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt @@ -1,60 +1,108 @@ -package ru.myitschool.work.ui.Main +package ru.myitschool.work.ui.admin import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory import ru.myitschool.work.R +import ru.myitschool.work.api.ApiService +import ru.myitschool.work.api.EmployeeData +import ru.myitschool.work.api.ToggleAccessRequest +import ru.myitschool.work.core.Constants +import ru.myitschool.work.databinding.FragmentAdminBinding -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" +class AdminFragment : Fragment(R.layout.fragment_admin) { + private var _binding: FragmentAdminBinding? = null + private val binding get() = _binding!! -/** - * A simple [Fragment] subclass. - * Use the [AdminFragment.newInstance] factory method to - * create an instance of this fragment. - */ -class AdminFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null + private val apiService: ApiService by lazy { + Retrofit.Builder() + .baseUrl(Constants.SERVER_ADDRESS) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(ApiService::class.java) + } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) + private var isAccessBlocked: Boolean = false // Переменная для отслеживания состояния доступа + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + _binding = FragmentAdminBinding.bind(view) + + setupUI() + } + + private fun setupUI() { + binding.viewEmployeeInfo.setOnClickListener { + val login = binding.employeeLogin.text.toString() + if (login.isNotEmpty()) { + fetchEmployeeInfo(login) + } else { + Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show() + } + } + + binding.toggleAccess.setOnClickListener { + val login = binding.employeeLogin.text.toString() + if (login.isNotEmpty()) { + // Определяем действие на основе текущего состояния доступа + val action = if (isAccessBlocked) "unblock" else "block" + toggleEmployeeAccess(login, action) + } else { + Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show() + } } } - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_admin, container, false) + private fun fetchEmployeeInfo(login: String) { + lifecycleScope.launch { + try { + val response = apiService.getEmployeeInfo(login) + if (response.isSuccessful) { + val employeeData = response.body() + employeeData?.let { + binding.employeeInfo.text = "Имя: ${it.name}, Должность: ${it.position}, Последний визит: ${it.lastVisit}" + binding.employeeInfo.visibility = View.VISIBLE + binding.toggleAccess.visibility = View.VISIBLE + // Здесь можно установить состояние доступа, если оно доступно + isAccessBlocked = false // Предположим, что доступ не заблокирован по умолчанию + } + } else { + Toast.makeText(requireContext(), "Ошибка получения данных", Toast.LENGTH_SHORT).show() + } + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show() + } + } } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment AdminFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - AdminFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) + private fun toggleEmployeeAccess(login: String, action: String) { + val request = ToggleAccessRequest(login, action) + + lifecycleScope.launch { + try { + val response = apiService.toggleAccess(request) + if (response.isSuccessful) { + isAccessBlocked = !isAccessBlocked // Переключаем состояние доступа + val message = if (action == "block") "Доступ заблокирован" else "Доступ разблокирован" + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(requireContext(), "Ошибка изменения доступа", Toast.LENGTH_SHORT).show() } + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show() } + } + } + + override fun onDestroyView() { + _binding = null // Освобождаем binding, когда представление уничтожается + super.onDestroyView() } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/Main/MainFragment.kt b/app/src/main/java/ru/myitschool/work/ui/Main/MainFragment.kt index 351e58f..1524b9c 100644 --- a/app/src/main/java/ru/myitschool/work/ui/Main/MainFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/Main/MainFragment.kt @@ -10,7 +10,6 @@ import androidx.fragment.app.setFragmentResultListener import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -47,11 +46,24 @@ class MainFragment : Fragment(R.layout.fragment_main) { setupUI() fetchUserData() + checkAdminAccess() // Проверяем доступ администратора // Проверяем, есть ли результат QR checkQrResult() } + private fun checkAdminAccess() { + // Проверяем, является ли пользователь администратором + if (SessionManager.userRole == "admin") { + binding.adminPanel.visibility = View.VISIBLE // Показываем кнопку AdminPanel + binding.adminPanel.setOnClickListener { + findNavController().navigate(R.id.adminFragment) // Переход на экран администратора + } + } else { + binding.adminPanel.visibility = View.GONE // Скрываем кнопку для обычных пользователей + } + } + private fun checkQrResult() { // Слушаем результат QR сканирования setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle -> @@ -81,25 +93,28 @@ class MainFragment : Fragment(R.layout.fragment_main) { lifecycleScope.launch { showError(null) // Скрыть ошибку, если она была try { - val response = apiService.getUserInfo(SessionManager.userLogin) // Получаем данные пользователя - if (response.isSuccessful) { - response.body()?.let { data -> - // Извлекаем значения из Map - val fullName = data["name"] as? String ?: "Неизвестно" - val position = data["position"] as? String ?: "Неизвестно" - val lastVisit = data["lastVisit"] as? String ?: "Неизвестно" - val photoUrl = data["photo"] as? String ?: "" + val response = + SessionManager.userLogin?.let { apiService.getUserInfo(it) } // Получаем данные пользователя + if (response != null) { + if (response.isSuccessful) { + response.body()?.let { data -> + // Извлекаем значения из Map + val fullName = data["name"] as? String ?: "Неизвестно" + val position = data["position"] as? String ?: "Неизвестно" + val lastVisit = data["lastVisit"] as? String ?: "Неизвестно" + val photoUrl = data["photo"] as? String ?: "" - // Обновляем UI - updateUI(fullName, position, lastVisit, photoUrl) + // Обновляем UI + updateUI(fullName, position, lastVisit, photoUrl) - // Здесь вы можете добавить данные проходов в список - // Пример: - accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта")) - accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер + // Здесь вы можете добавить данные проходов в список + // Пример: + accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта")) + accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер + } + } else { + showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились } - } else { - showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились } } catch (e: Exception) { showError(e.localizedMessage) // Показываем ошибку при исключении diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt index bd2f510..b64393c 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt @@ -10,15 +10,17 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint import ru.myitschool.work.R +import ru.myitschool.work.SessionManager +import ru.myitschool.work.api.UserAuthResponse import ru.myitschool.work.databinding.FragmentLoginBinding import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.visibleOrGone import ru.myitschool.work.utils.AuthPreferences import kotlinx.coroutines.launch -import androidx.lifecycle.lifecycleScope @AndroidEntryPoint class LoginFragment : Fragment(R.layout.fragment_login) { @@ -76,6 +78,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) { private fun performLogin(username: String, password: String) { lifecycleScope.launch { + binding.loading.visibleOrGone(true) // Показываем индикатор загрузки viewModel.authenticate(username, password) // Передаем пароль в метод authenticate } } @@ -94,6 +97,13 @@ class LoginFragment : Fragment(R.layout.fragment_login) { binding.error.visibility = View.GONE authPreferences.saveLoginState(true) authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин + + // Сохраняем роль пользователя в SessionManager + val userAuthResponse: UserAuthResponse? = state.userAuthResponse + userAuthResponse?.let { + SessionManager.userRole = it.role // Сохраняем роль + } + Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show() navigateToMainScreen() } diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt index 6ea460d..abdcbb0 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt @@ -13,6 +13,7 @@ import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import ru.myitschool.work.SessionManager import ru.myitschool.work.api.ApiService +import ru.myitschool.work.api.UserAuthResponse import ru.myitschool.work.core.Constants import javax.inject.Inject @@ -38,8 +39,12 @@ class LoginViewModel @Inject constructor( val credentials = Credentials.basic(username, password) // Создаем Basic Auth заголовок val response = apiService.authenticate(username, credentials) // Передаем заголовок в запрос if (response.isSuccessful) { - SessionManager.userLogin = username - _state.value = LoginState(success = true) + val userAuthResponse = response.body() // Получаем ответ с ролью пользователя + userAuthResponse?.let { + SessionManager.userLogin = username // Сохраняем логин + SessionManager.userRole = it.role // Сохраняем роль + } + _state.value = LoginState(success = true, userAuthResponse = userAuthResponse) } else if (response.code() == 503) { // Пример кода для техработ _state.value = LoginState(maintenance = true) } else { @@ -60,5 +65,10 @@ class LoginViewModel @Inject constructor( return username.length >= 3 && !username.first().isDigit() && username.all { it.isLetterOrDigit() } } - data class LoginState(val success: Boolean = false, val error: String? = null, val maintenance: Boolean = false) + data class LoginState( + val success: Boolean = false, + val error: String? = null, + val maintenance: Boolean = false, + val userAuthResponse: UserAuthResponse? = null // Добавляем поле для хранения информации о роли + ) } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_admin.xml b/app/src/main/res/layout/fragment_admin.xml index c2c1852..e22daaa 100644 --- a/app/src/main/res/layout/fragment_admin.xml +++ b/app/src/main/res/layout/fragment_admin.xml @@ -1,14 +1,44 @@ - - + android:orientation="vertical" + android:padding="16dp"> - + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Введите логин сотрудника:" + android:textSize="18sp" + android:layout_marginBottom="8dp" /> - \ No newline at end of file + + +