day2_commit_10_fixed_alotofbugs_added_admin_logic

This commit is contained in:
Terebov_Maksim 2025-02-19 14:12:47 +03:00
parent 1d9207a5f5
commit 85a5c82d8c
10 changed files with 238 additions and 83 deletions

View File

@ -23,7 +23,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Default" android:theme="@style/Theme.Default"
tools:targetApi="31"> tools:targetApi="34">
<activity <activity
android:name=".ui.RootActivity" android:name=".ui.RootActivity"
android:exported="true"> android:exported="true">

View File

@ -1,5 +1,12 @@
package ru.myitschool.work package ru.myitschool.work
object SessionManager { object SessionManager {
var userLogin: String = "" var userLogin: String? = null // Логин пользователя
var userRole: String? = null // Роль пользователя
// Метод для очистки данных сессии
fun clearSession() {
userLogin = null
userRole = null
}
} }

View File

@ -4,21 +4,41 @@ import retrofit2.Response
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.PATCH import retrofit2.http.POST
import retrofit2.http.Path import retrofit2.http.Path
interface ApiService { interface ApiService {
@GET("api/{login}/auth") @GET("api/{login}/auth")
suspend fun authenticate( suspend fun authenticate(
@Path("login") login: String, @Path("login") login: String,
@Header("Authorization") authorization: String // Добавляем заголовок Authorization @Header("Authorization") authorization: String
): Response<Unit> ): Response<UserAuthResponse> // Изменяем на UserAuthResponse
@GET("api/{login}/info") @GET("api/{login}/info")
suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>> // Возвращаем Map вместо UserInfo suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>>
@PATCH("api/{login}/open") @POST("api/employee/toggleAccess")
suspend fun openDoor(@Path("login") login: String, @Body body: OpenDoorRequest): Response<Unit> suspend fun toggleAccess(@Body request: ToggleAccessRequest): Response<Unit>
// Метод для получения информации о сотруднике
@GET("api/employee/{login}")
suspend fun getEmployeeInfo(@Path("login") login: String): Response<EmployeeData>
} }
data class OpenDoorRequest(val value: String) // Модель данных для ответа аутентификации
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"
)

View File

@ -1,60 +1,108 @@
package ru.myitschool.work.ui.Main package ru.myitschool.work.ui.admin
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View 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.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 class AdminFragment : Fragment(R.layout.fragment_admin) {
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private var _binding: FragmentAdminBinding? = null
private const val ARG_PARAM1 = "param1" private val binding get() = _binding!!
private const val ARG_PARAM2 = "param2"
/** private val apiService: ApiService by lazy {
* A simple [Fragment] subclass. Retrofit.Builder()
* Use the [AdminFragment.newInstance] factory method to .baseUrl(Constants.SERVER_ADDRESS)
* create an instance of this fragment. .addConverterFactory(GsonConverterFactory.create())
*/ .build()
class AdminFragment : Fragment() { .create(ApiService::class.java)
// TODO: Rename and change types of parameters }
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) { private var isAccessBlocked: Boolean = false // Переменная для отслеживания состояния доступа
super.onCreate(savedInstanceState)
arguments?.let { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
param1 = it.getString(ARG_PARAM1) super.onViewCreated(view, savedInstanceState)
param2 = it.getString(ARG_PARAM2) _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( private fun fetchEmployeeInfo(login: String) {
inflater: LayoutInflater, container: ViewGroup?, lifecycleScope.launch {
savedInstanceState: Bundle? try {
): View? { val response = apiService.getEmployeeInfo(login)
// Inflate the layout for this fragment if (response.isSuccessful) {
return inflater.inflate(R.layout.fragment_admin, container, false) 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 { private fun toggleEmployeeAccess(login: String, action: String) {
/** val request = ToggleAccessRequest(login, action)
* Use this factory method to create a new instance of
* this fragment using the provided parameters. lifecycleScope.launch {
* try {
* @param param1 Parameter 1. val response = apiService.toggleAccess(request)
* @param param2 Parameter 2. if (response.isSuccessful) {
* @return A new instance of fragment AdminFragment. isAccessBlocked = !isAccessBlocked // Переключаем состояние доступа
*/ val message = if (action == "block") "Доступ заблокирован" else "Доступ разблокирован"
// TODO: Rename and change types and number of parameters Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
@JvmStatic } else {
fun newInstance(param1: String, param2: String) = Toast.makeText(requireContext(), "Ошибка изменения доступа", Toast.LENGTH_SHORT).show()
AdminFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
} }
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
} }
}
}
override fun onDestroyView() {
_binding = null // Освобождаем binding, когда представление уничтожается
super.onDestroyView()
} }
} }

View File

@ -10,7 +10,6 @@ import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -47,11 +46,24 @@ class MainFragment : Fragment(R.layout.fragment_main) {
setupUI() setupUI()
fetchUserData() fetchUserData()
checkAdminAccess() // Проверяем доступ администратора
// Проверяем, есть ли результат QR // Проверяем, есть ли результат QR
checkQrResult() 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() { private fun checkQrResult() {
// Слушаем результат QR сканирования // Слушаем результат QR сканирования
setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle -> setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
@ -81,25 +93,28 @@ class MainFragment : Fragment(R.layout.fragment_main) {
lifecycleScope.launch { lifecycleScope.launch {
showError(null) // Скрыть ошибку, если она была showError(null) // Скрыть ошибку, если она была
try { try {
val response = apiService.getUserInfo(SessionManager.userLogin) // Получаем данные пользователя val response =
if (response.isSuccessful) { SessionManager.userLogin?.let { apiService.getUserInfo(it) } // Получаем данные пользователя
response.body()?.let { data -> if (response != null) {
// Извлекаем значения из Map if (response.isSuccessful) {
val fullName = data["name"] as? String ?: "Неизвестно" response.body()?.let { data ->
val position = data["position"] as? String ?: "Неизвестно" // Извлекаем значения из Map
val lastVisit = data["lastVisit"] as? String ?: "Неизвестно" val fullName = data["name"] as? String ?: "Неизвестно"
val photoUrl = data["photo"] as? String ?: "" val position = data["position"] as? String ?: "Неизвестно"
val lastVisit = data["lastVisit"] as? String ?: "Неизвестно"
val photoUrl = data["photo"] as? String ?: ""
// Обновляем UI // Обновляем UI
updateUI(fullName, position, lastVisit, photoUrl) updateUI(fullName, position, lastVisit, photoUrl)
// Здесь вы можете добавить данные проходов в список // Здесь вы можете добавить данные проходов в список
// Пример: // Пример:
accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта")) accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта"))
accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер
}
} else {
showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
} }
} else {
showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
} }
} catch (e: Exception) { } catch (e: Exception) {
showError(e.localizedMessage) // Показываем ошибку при исключении showError(e.localizedMessage) // Показываем ошибку при исключении

View File

@ -10,15 +10,17 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R 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.databinding.FragmentLoginBinding
import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.visibleOrGone import ru.myitschool.work.utils.visibleOrGone
import ru.myitschool.work.utils.AuthPreferences import ru.myitschool.work.utils.AuthPreferences
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope
@AndroidEntryPoint @AndroidEntryPoint
class LoginFragment : Fragment(R.layout.fragment_login) { 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) { private fun performLogin(username: String, password: String) {
lifecycleScope.launch { lifecycleScope.launch {
binding.loading.visibleOrGone(true) // Показываем индикатор загрузки
viewModel.authenticate(username, password) // Передаем пароль в метод authenticate viewModel.authenticate(username, password) // Передаем пароль в метод authenticate
} }
} }
@ -94,6 +97,13 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
binding.error.visibility = View.GONE binding.error.visibility = View.GONE
authPreferences.saveLoginState(true) authPreferences.saveLoginState(true)
authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин 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() Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
navigateToMainScreen() navigateToMainScreen()
} }

View File

@ -13,6 +13,7 @@ import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.SessionManager import ru.myitschool.work.SessionManager
import ru.myitschool.work.api.ApiService import ru.myitschool.work.api.ApiService
import ru.myitschool.work.api.UserAuthResponse
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
import javax.inject.Inject import javax.inject.Inject
@ -38,8 +39,12 @@ class LoginViewModel @Inject constructor(
val credentials = Credentials.basic(username, password) // Создаем Basic Auth заголовок val credentials = Credentials.basic(username, password) // Создаем Basic Auth заголовок
val response = apiService.authenticate(username, credentials) // Передаем заголовок в запрос val response = apiService.authenticate(username, credentials) // Передаем заголовок в запрос
if (response.isSuccessful) { if (response.isSuccessful) {
SessionManager.userLogin = username val userAuthResponse = response.body() // Получаем ответ с ролью пользователя
_state.value = LoginState(success = true) userAuthResponse?.let {
SessionManager.userLogin = username // Сохраняем логин
SessionManager.userRole = it.role // Сохраняем роль
}
_state.value = LoginState(success = true, userAuthResponse = userAuthResponse)
} else if (response.code() == 503) { // Пример кода для техработ } else if (response.code() == 503) { // Пример кода для техработ
_state.value = LoginState(maintenance = true) _state.value = LoginState(maintenance = true)
} else { } else {
@ -60,5 +65,10 @@ class LoginViewModel @Inject constructor(
return username.length >= 3 && !username.first().isDigit() && username.all { it.isLetterOrDigit() } 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 // Добавляем поле для хранения информации о роли
)
} }

View File

@ -1,14 +1,44 @@
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.Main.AdminFragment"> android:orientation="vertical"
android:padding="16dp">
<!-- TODO: Update blank fragment layout -->
<TextView <TextView
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment" /> android:text="Введите логин сотрудника:"
android:textSize="18sp"
android:layout_marginBottom="8dp" />
</FrameLayout> <EditText
android:id="@+id/employee_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Логин сотрудника"
android:inputType="text" />
<Button
android:id="@+id/view_employee_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Просмотреть информацию о сотруднике"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/employee_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textSize="16sp"
android:visibility="gone" />
<Button
android:id="@+id/toggle_access"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Блокировать/Разблокировать доступ"
android:layout_marginTop="16dp"
android:visibility="gone" />
</LinearLayout>

View File

@ -93,4 +93,13 @@
android:backgroundTint="@color/colorPrimary" android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:visibility="gone" /> android:visibility="gone" />
<Button
android:id="@+id/admin_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Admin Panel"
android:layout_marginTop="16dp"
android:visibility="gone" />
</LinearLayout> </LinearLayout>

View File

@ -22,6 +22,12 @@
android:label="Main Fragment" android:label="Main Fragment"
tools:layout="@layout/fragment_main" /> tools:layout="@layout/fragment_main" />
<fragment
android:id="@+id/adminFragment"
android:name="ru.myitschool.work.ui.admin.AdminFragment"
android:label="Admin Fragment"
tools:layout="@layout/fragment_admin" />
<fragment <fragment
android:id="@+id/qrResultFragment" android:id="@+id/qrResultFragment"
android:name="ru.myitschool.work.ui.qr.result.QrResult" android:name="ru.myitschool.work.ui.qr.result.QrResult"