diff --git a/README.md b/README.md index 610b14b..dcfe90d 100644 --- a/README.md +++ b/README.md @@ -2,589 +2,124 @@ Кроссплатформенное корпоративное приложение для общения между сотрудниками компании, построенное на .NET MAUI. -## 📱 Возможности - -- ✅ **Аутентификация** - безопасный вход в систему -- ✅ **Список сотрудников** - просмотр всех пользователей с фильтрацией и поиском -- ✅ **Личные сообщения** - обмен сообщениями между сотрудниками в реальном времени -- ✅ **WebSocket подключение** - мгновенная доставка сообщений -- ✅ **Статусы пользователей** - отображение онлайн/оффлайн статуса -- ✅ **Индикатор набора текста** - видно, когда собеседник печатает -- ✅ **Профиль пользователя** - просмотр информации о себе -- ✅ **Поддержка Android, iOS, Windows, MacCatalyst** - -## 🏗️ Архитектура - -### Структура проекта - -``` -CORP-MAUI/ -├── Models/ # Модели данных -│ ├── User.cs -│ ├── Message.cs -│ └── AuthResponse.cs -├── Services/ # Бизнес-логика -│ ├── ApiService.cs # REST API клиент -│ └── WebSocketService.cs # WebSocket клиент -├── Pages/ # Страницы приложения -│ ├── LoginPage.xaml -│ ├── UsersPage.xaml -│ ├── ChatPage.xaml -│ └── ProfilePage.xaml -├── ServiceProvider.cs # Singleton для сервисов -├── App.xaml.cs # Точка входа -└── AppShell.xaml # Навигация -``` - -### Компоненты - -#### 1. **ApiService** - REST API клиент -Отвечает за HTTP запросы к серверу: -- Аутентификация (`POST /auth/login`) -- Получение списка пользователей (`GET /users`) -- Получение сообщений (`GET /messages`) -- Отправка сообщений (`POST /messages`) -- Поиск пользователей (`GET /users/search`) -- Обновление статуса (`PUT /users/status`) - -#### 2. **WebSocketService** - WebSocket клиент -Обеспечивает real-time коммуникацию: -- Подключение к WebSocket серверу -- Отправка/получение сообщений -- Индикаторы набора текста -- Обновление статусов пользователей -- Автоматическое переподключение - -#### 3. **ServiceProvider** - Singleton провайдер -Централизованное управление сервисами приложения. - -## 🔌 API Endpoints - -Базовый URL: `https://corp.robotail.pro/api/v1/` - -### REST API - -```http -# Аутентификация -POST /auth/login -Content-Type: application/json -{ - "username": "user", - "password": "pass" -} - -# Получить список пользователей -GET /users -Authorization: Bearer {token} - -# Получить текущего пользователя -GET /users/me -Authorization: Bearer {token} - -# Поиск пользователей -GET /users/search?q={query} -Authorization: Bearer {token} - -# Получить сообщения -GET /messages?userId={id} -Authorization: Bearer {token} - -# Отправить сообщение -POST /messages -Authorization: Bearer {token} -Content-Type: application/json -{ - "receiverId": 123, - "content": "Текст сообщения" -} - -# Обновить статус -PUT /users/status -Authorization: Bearer {token} -Content-Type: application/json -{ - "isOnline": true -} -``` - -### WebSocket - -``` -URL: wss://corp.robotail.pro/api/v1/ws -Headers: Authorization: Bearer {token} - -# Отправка сообщения -{ - "type": "message", - "data": { - "receiverId": 123, - "content": "Текст сообщения" - } -} - -# Индикатор набора текста -{ - "type": "typing", - "data": { - "recipientId": 123, - "isTyping": true - } -} - -# Получение сообщения (от сервера) -{ - "type": "message", - "data": { - "id": 456, - "senderId": 789, - "senderName": "Иван Иванов", - "content": "Привет!", - "timestamp": "2026-01-08T10:30:00Z" - } -} - -# Обновление статуса пользователя (от сервера) -{ - "type": "user_status", - "data": { - "id": 123, - "isOnline": true - } -} -``` - -## 🚀 Установка и запуск - -### Требования - -- .NET 9.0 SDK -- Visual Studio 2022 / JetBrains Rider -- Для Android: Android SDK (см. [ANDROID_SETUP.md](ANDROID_SETUP.md)) -- Для iOS: Xcode и Mac - -### ⚙️ Настройка Android SDK - -Если Android SDK установлен по нестандартному пути, создан файл `Directory.Build.props` в корне решения: - -```xml - - - D:\Applications\Android\SDK - - -``` - -**Важно:** Измените путь на ваш, если SDK установлен в другом месте. - -Подробная инструкция: [ANDROID_SETUP.md](ANDROID_SETUP.md) - -### Скрипты сборки для Android - -Для удобной сборки Android используйте готовые скрипты: - -```powershell -# PowerShell (рекомендуется) -.\build-android.ps1 - -# Batch -.\build-android.bat -``` - -Скрипты автоматически настроят переменные окружения и соберут проект. - -### Установка зависимостей - -```powershell -# CORP-MAUI - Корпоративное приложение для сотрудников - -Мобильное приложение для внутрикорпоративного общения между сотрудниками компании. - -## 🚀 Технологии - -- **.NET MAUI 9.0** - Кросс-платформенный фреймворк -- **C# 12** - Язык программирования -- **WebSocket** - Обмен сообщениями в реальном времени -- **REST API** - Взаимодействие с сервером -- **JWT Authentication** - Безопасная аутентификация -- **SecureStorage** - Безопасное хранение токенов +> 📖 **Для разработчиков:** Техническая документация, API, примеры кода и инструкции по установке доступны в [папке TECHDOC](TECHDOC/README.md) ## 📱 Возможности ### Аутентификация -- Вход по email и паролю -- Автоматическое сохранение JWT токенов -- Автоматическое обновление access токена при истечении -- Безопасное хранение токенов в SecureStorage +- Безопасный вход в систему по email и паролю +- Автоматическое сохранение сессии +- Быстрый выход из системы ### Профиль пользователя - Просмотр личной информации - Отображение статуса (в сети/не в сети) -- Выход из системы +- Информация об отделе и должности ### Список сотрудников - Просмотр всех сотрудников компании -- Поиск по имени, отделу, должности -- Отображение статуса онлайн/офлайн +- Удобный поиск по имени, отделу или должности +- Отображение статуса онлайн/оффлайн - Pull-to-refresh для обновления списка ### Чат -- Обмен личными сообщениями -- Обмен в реальном времени через WebSocket -- Отображение истории сообщений -- Индикатор набора текста -- Резервная отправка через REST API +- Обмен личными сообщениями с коллегами +- Мгновенная доставка сообщений в реальном времени +- История переписки с каждым сотрудником +- Индикатор "печатает..." когда собеседник набирает текст +- Автоматическая прокрутка к новым сообщениям -## 🏗️ Архитектура +## 🎯 Как использовать -``` -CORP-MAUI/ -├── Models/ # Модели данных -│ ├── User.cs # Модель пользователя -│ ├── Message.cs # Модель сообщения -│ └── AuthResponse.cs # Ответ аутентификации -├── Services/ # Сервисы -│ ├── ApiService.cs # HTTP-клиент для REST API -│ └── WebSocketService.cs # WebSocket-клиент -├── Pages/ # Страницы приложения -│ ├── LoginPage.xaml # Страница входа -│ ├── ProfilePage.xaml # Профиль пользователя -│ ├── UsersPage.xaml # Список сотрудников -│ └── ChatPage.xaml # Чат с сотрудником -└── ServiceProvider.cs # Глобальный провайдер сервисов -``` +### Вход в систему -## 🔌 API Endpoints +1. Откройте приложение +2. Введите ваш корпоративный email +3. Введите пароль +4. Нажмите кнопку "Войти" -**Базовый URL:** `https://corp.robotail.pro` +При первом входе сессия будет сохранена, и при следующем запуске вы автоматически войдете в систему. -### Аутентификация -```http -POST /api/auth/token/ -Body: { "email": "user@example.com", "password": "password" } -Response: { "access": "token", "refresh": "token" } +### Поиск коллег -POST /api/auth/token/refresh/ -Body: { "refresh": "token" } -Response: { "access": "new_token" } -``` +1. Перейдите на вкладку "Сотрудники" +2. Используйте строку поиска для фильтрации по имени, отделу или должности +3. Потяните список вниз для обновления информации -### Пользователи -```http -GET /api/v1/employees/ -Response: [{ "id": 1, "username": "user", ... }] +### Отправка сообщений -GET /api/v1/employees/me/ -Response: { "id": 1, "username": "user", ... } +1. В списке сотрудников нажмите на коллегу, с которым хотите пообщаться +2. Введите текст сообщения в поле внизу экрана +3. Нажмите кнопку отправки -GET /api/v1/users/search/?q=query -Response: [{ "id": 1, "username": "user", ... }] +Сообщения доставляются мгновенно. Вы увидите, когда собеседник начнет печатать ответ. -PUT /api/v1/users/status/ -Body: { "isOnline": true } -``` +### Просмотр профиля -### Сообщения -```http -GET /api/v1/messages/ -Response: [{ "id": 1, "content": "Hello", ... }] +1. Перейдите на вкладку "Профиль" +2. Здесь отображается ваша информация: + - Имя и фамилия + - Email + - Отдел + - Должность + - Текущий статус -GET /api/v1/messages/?user_id=1 -Response: [{ "id": 1, "content": "Hello", ... }] +### Выход из системы -POST /api/v1/messages/ -Body: { "senderId": 1, "receiverId": 2, "content": "Hello" } -``` +1. Перейдите на вкладку "Профиль" +2. Нажмите кнопку "Выйти" -### WebSocket -``` -Поддерживаются следующие варианты URL (автоматический подбор): -- wss://corp.robotail.pro/ws (основной) -- ws://corp.robotail.pro/ws (без SSL) -- wss://corp.robotail.pro/ws/ (со слешем) -- и другие варианты... +## 💡 Советы по использованию -ВАЖНО! Обязательный заголовок при подключении: -Authorization: Bearer {access_token} +- **Статусы онлайн/оффлайн** обновляются в реальном времени. Зеленый индикатор означает, что коллега сейчас в сети. +- **Pull-to-refresh** - потяните список сотрудников вниз для обновления информации. +- **История сообщений** сохраняется на сервере, вы можете просмотреть предыдущую переписку в любое время. +- **Автоматическое переподключение** - если интернет-соединение прервется, приложение автоматически переподключится при восстановлении связи. -Пример подключения: -var ws = new ClientWebSocket(); -ws.Options.SetRequestHeader("Authorization", $"Bearer {token}"); -await ws.ConnectAsync(new Uri("wss://corp.robotail.pro/ws"), cancellationToken); +## 🔐 Безопасность -Типы сообщений: -- message: Новое сообщение -- user_status: Изменение статуса пользователя -- typing: Индикатор набора текста +- Все данные передаются по защищенному соединению (HTTPS/WSS) +- Ваш пароль никогда не сохраняется на устройстве +- Токен аутентификации хранится в защищенном хранилище вашего устройства +- При выходе все данные сессии удаляются -Формат сообщений: -{ - "type": "message", - "data": { - "senderId": 1, - "receiverId": 2, - "content": "Текст сообщения", - "timestamp": "2026-01-09T12:00:00Z" - } -} -``` +## 📱 Поддерживаемые платформы -**Примечание**: -- WebSocketService автоматически пробует разные варианты URL и подключается к первому работающему -- Заголовок Authorization устанавливается автоматически при вызове `ConnectAsync(token)` -- Токен передается в формате JWT Bearer token +- **Android** - версия 5.0 и выше +- **iOS** - версия 11.0 и выше +- **Windows** - Windows 10 и выше +- **macOS** - macOS 10.15 и выше -## 🔐 Система управления токенами +## ❓ Часто задаваемые вопросы -ApiService автоматически управляет JWT токенами: +**Почему я не могу войти в систему?** +- Проверьте правильность email и пароля +- Убедитесь, что у вас есть подключение к интернету +- Свяжитесь с IT-отделом для проверки вашей учетной записи -1. **При входе:** - - Получает access и refresh токены - - Сохраняет их в SecureStorage - - Устанавливает в HTTP заголовки +**Сообщения не доставляются** +- Проверьте подключение к интернету +- Перезапустите приложение +- Если проблема сохраняется, обратитесь в техподдержку -2. **При запросах:** - - Автоматически добавляет Bearer токен - - При 401/403 пытается обновить токен - - Повторяет запрос после обновления +**Не вижу некоторых коллег в списке** +- Используйте поиск для быстрого нахождения нужного человека +- Обновите список, потянув его вниз +- Возможно, коллега еще не зарегистрирован в системе -3. **При выходе:** - - Очищает токены из SecureStorage - - Удаляет заголовки авторизации +**Как удалить сообщения?** +- В текущей версии удаление отдельных сообщений не поддерживается +- Вся история переписки хранится на сервере -## 📝 Использование +## 📞 Поддержка -### Пример авторизации -```csharp -var result = await ServiceProvider.ApiService.LoginAsync(email, password); -if (result.Success) -{ - // Токены автоматически сохранены - var user = result.User; - await ServiceProvider.WebSocketService.ConnectAsync(result.Token); - Application.Current.MainPage = new AppShell(); -} -``` - -### Пример получения данных -```csharp -// Автоматически обновит токен при необходимости -var employees = await ServiceProvider.ApiService.GetEmployeesAsync(); -var currentUser = await ServiceProvider.ApiService.GetCurrentUserAsync(); -``` - -### Пример отправки сообщения -```csharp -var message = new Message -{ - SenderId = currentUser.Id, - ReceiverId = recipient.Id, - Content = "Hello!", - Timestamp = DateTime.Now, - Type = MessageType.Personal -}; - -// Через WebSocket (приоритет) -if (ServiceProvider.WebSocketService.IsConnected) -{ - await ServiceProvider.WebSocketService.SendMessageAsync(message); -} -else -{ - // Резервный метод через REST API - await ServiceProvider.ApiService.SendMessageAsync(message); -} -``` - -### Пример выхода -```csharp -await ServiceProvider.ApiService.UpdateStatusAsync(false); -await ServiceProvider.WebSocketService.DisconnectAsync(); -ServiceProvider.ApiService.Logout(); // Очищает токены -Application.Current.MainPage = new NavigationPage(new LoginPage()); -``` - -## 🔧 Конфигурация - -Вы можете настроить ApiService при создании: - -```csharp -var config = new ApiConfig -{ - BaseUrl = "https://your-api.com/api", - TokenObtainPath = "/auth/token/", - TokenRefreshPath = "/auth/token/refresh/", - Timeout = 30, - DefaultHeaders = new Dictionary - { - { "Accept", "application/json" }, - { "X-Custom-Header", "value" } - } -}; - -var apiService = new ApiService(config); -``` - -## 🛠️ Сборка и запуск - -### Требования -- Visual Studio 2022 или JetBrains Rider -- .NET 9.0 SDK -- Android SDK (для Android) -- Xcode (для iOS/macOS) - -### Запуск -```powershell -# Android -dotnet build -t:Run -f net9.0-android - -# iOS -dotnet build -t:Run -f net9.0-ios - -# Windows -dotnet build -t:Run -f net9.0-windows10.0.19041.0 -``` - -## 📚 Документация - -- [API_REFACTORING.md](API_REFACTORING.md) - Подробное описание рефакторинга ApiService -- [.NET MAUI Documentation](https://docs.microsoft.com/dotnet/maui/) - -## ⚠️ Важные замечания - -1. **SecureStorage** - Токены хранятся в защищенном хранилище платформы -2. **Автоматический Refresh** - Access токен обновляется прозрачно при 401/403 -3. **WebSocket Reconnect** - При разрыве соединения нужно реализовать переподключение -4. **Offline Mode** - Можно добавить локальное кэширование сообщений - -## 🤝 Вклад - -Проект находится в разработке. Приветствуются pull requests и issue reports. - -## 📄 Лицензия - -Корпоративное приложение для внутреннего использования. +При возникновении проблем или вопросов обращайтесь в IT-отдел. --- **Версия:** 1.0.0 -**Дата обновления:** 2026-01-09 -dotnet restore -``` +**Последнее обновление:** 2026-01-12 -### Сборка проекта - -```powershell -# Для Android -dotnet build -f net9.0-android - -# Для Windows -dotnet build -f net9.0-windows10.0.19041.0 - -# Для iOS -dotnet build -f net9.0-ios - -# Для MacCatalyst -dotnet build -f net9.0-maccatalyst -``` - -### Запуск приложения - -```powershell -# Android -dotnet run -f net9.0-android - -# Windows -dotnet run -f net9.0-windows10.0.19041.0 -``` - -## 🔧 Конфигурация - -### Изменение API URL - -В файле `Services/ApiService.cs`: -```csharp -private const string BaseUrl = "https://corp.robotail.pro/api/v1/"; -``` - -В файле `Services/WebSocketService.cs`: -```csharp -private const string WebSocketUrl = "wss://corp.robotail.pro/api/v1/ws"; -``` - -## 📚 Используемые технологии - -- **.NET 9.0** - фреймворк -- **.NET MAUI** - UI фреймворк -- **Newtonsoft.Json** - JSON сериализация -- **System.Net.WebSockets** - WebSocket клиент -- **SecureStorage** - безопасное хранение данных - -## 🎨 UI/UX особенности - -- Material Design стиль -- Адаптивный дизайн для разных экранов -- Плавные анимации и переходы -- Индикаторы загрузки -- Обработка ошибок с user-friendly сообщениями -- Pull-to-refresh для обновления данных - -## 🔐 Безопасность - -- Токены хранятся в SecureStorage -- HTTPS для REST API -- WSS (WebSocket Secure) для real-time коммуникации -- Автоматический выход при истечении токена - -## 📝 Примеры использования - -### Отправка сообщения через WebSocket - -```csharp -var message = new Message -{ - SenderId = currentUser.Id, - ReceiverId = recipient.Id, - Content = "Привет!", - Timestamp = DateTime.Now -}; - -await ServiceProvider.WebSocketService.SendMessageAsync(message); -``` - -### Поиск пользователей - -```csharp -var users = await ServiceProvider.ApiService.SearchUsersAsync("Иван"); -``` - -### Подписка на новые сообщения - -```csharp -ServiceProvider.WebSocketService.MessageReceived += (sender, message) => -{ - // Обработка нового сообщения - Console.WriteLine($"Новое сообщение от {message.SenderName}: {message.Content}"); -}; -``` - -## 🐛 Известные проблемы - -- WebSocket может разрываться при неактивности (реализовано автоматическое переподключение) -- На iOS требуются дополнительные разрешения для фоновой работы - -## 🤝 Вклад в проект - -Пул-реквесты приветствуются! Для крупных изменений, пожалуйста, сначала откройте issue. - -## 📄 Лицензия - -[MIT](https://choosealicense.com/licenses/mit/) - -## 👥 Автор - -Создано для корпоративного использования в компании. - -## 📞 Поддержка - -При возникновении проблем создайте issue в репозитории проекта. +## 🔧 Техническая информация +Для разработчиков, системных администраторов и всех, кто хочет узнать больше о технических аспектах приложения, перейдите к **[технической документации](TECHDOC/README.md)**. diff --git a/TECHDOC/API.md b/TECHDOC/API.md new file mode 100644 index 0000000..c241592 --- /dev/null +++ b/TECHDOC/API.md @@ -0,0 +1,497 @@ +# API и интеграция + +## 🌐 Базовая информация + +**Базовый URL:** `https://corp.robotail.pro` + +**API версия:** v1 + +**Протоколы:** +- REST API - `https://corp.robotail.pro/api/v1/` +- WebSocket - `wss://corp.robotail.pro/ws` + +## 🔐 Аутентификация + +### Получение токена + +```http +POST /api/auth/token/ +Content-Type: application/json + +{ + "email": "user@example.com", + "password": "password" +} +``` + +**Ответ:** +```json +{ + "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} +``` + +### Обновление токена + +```http +POST /api/auth/token/refresh/ +Content-Type: application/json + +{ + "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} +``` + +**Ответ:** +```json +{ + "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} +``` + +### Использование токена + +Все защищенные endpoints требуют Bearer токен в заголовке: + +```http +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... +``` + +## 👥 REST API - Пользователи + +### Получить список всех сотрудников + +```http +GET /api/v1/employees/ +Authorization: Bearer {access_token} +``` + +**Ответ:** +```json +[ + { + "id": 1, + "username": "ivan_ivanov", + "email": "ivan@company.com", + "firstName": "Иван", + "lastName": "Иванов", + "department": "IT", + "position": "Разработчик", + "isOnline": true, + "avatarUrl": null + }, + { + "id": 2, + "username": "maria_petrova", + "email": "maria@company.com", + "firstName": "Мария", + "lastName": "Петрова", + "department": "HR", + "position": "Менеджер", + "isOnline": false, + "avatarUrl": null + } +] +``` + +### Получить текущего пользователя + +```http +GET /api/v1/employees/me/ +Authorization: Bearer {access_token} +``` + +**Ответ:** +```json +{ + "id": 1, + "username": "ivan_ivanov", + "email": "ivan@company.com", + "firstName": "Иван", + "lastName": "Иванов", + "department": "IT", + "position": "Разработчик", + "isOnline": true, + "avatarUrl": null +} +``` + +### Поиск пользователей + +```http +GET /api/v1/users/search/?q={query} +Authorization: Bearer {access_token} +``` + +**Параметры:** +- `q` (string, required) - поисковый запрос (имя, отдел, должность) + +**Пример:** +```http +GET /api/v1/users/search/?q=Иван +``` + +**Ответ:** +```json +[ + { + "id": 1, + "username": "ivan_ivanov", + "firstName": "Иван", + "lastName": "Иванов", + "department": "IT", + "position": "Разработчик", + "isOnline": true + } +] +``` + +### Обновить статус пользователя + +```http +PUT /api/v1/users/status/ +Authorization: Bearer {access_token} +Content-Type: application/json + +{ + "isOnline": true +} +``` + +**Ответ:** +```json +{ + "success": true, + "message": "Статус обновлен" +} +``` + +## 💬 REST API - Сообщения + +### Получить все сообщения + +```http +GET /api/v1/messages/ +Authorization: Bearer {access_token} +``` + +**Ответ:** +```json +[ + { + "id": 1, + "senderId": 2, + "senderName": "Мария Петрова", + "receiverId": 1, + "content": "Привет! Как дела?", + "timestamp": "2026-01-12T10:30:00Z", + "isRead": false + } +] +``` + +### Получить сообщения с конкретным пользователем + +```http +GET /api/v1/messages/?user_id={userId} +Authorization: Bearer {access_token} +``` + +**Параметры:** +- `user_id` (int, required) - ID пользователя для фильтрации + +**Пример:** +```http +GET /api/v1/messages/?user_id=2 +``` + +**Ответ:** +```json +[ + { + "id": 1, + "senderId": 2, + "senderName": "Мария Петрова", + "receiverId": 1, + "content": "Привет!", + "timestamp": "2026-01-12T10:30:00Z", + "isRead": true + }, + { + "id": 2, + "senderId": 1, + "senderName": "Иван Иванов", + "receiverId": 2, + "content": "Привет! Как дела?", + "timestamp": "2026-01-12T10:31:00Z", + "isRead": true + } +] +``` + +### Отправить сообщение (REST fallback) + +```http +POST /api/v1/messages/ +Authorization: Bearer {access_token} +Content-Type: application/json + +{ + "senderId": 1, + "receiverId": 2, + "content": "Текст сообщения" +} +``` + +**Ответ:** +```json +{ + "id": 3, + "senderId": 1, + "senderName": "Иван Иванов", + "receiverId": 2, + "content": "Текст сообщения", + "timestamp": "2026-01-12T10:35:00Z", + "isRead": false +} +``` + +## 🔌 WebSocket API + +### Подключение + +**URL:** `wss://corp.robotail.pro/ws` + +**Поддерживаемые варианты URL:** +- `wss://corp.robotail.pro/ws` (основной) +- `ws://corp.robotail.pro/ws` (без SSL) +- `wss://corp.robotail.pro/ws/` (со слешем) +- `wss://corp.robotail.pro/api/v1/ws` +- `ws://corp.robotail.pro/api/v1/ws` + +> WebSocketService автоматически пробует разные варианты и подключается к первому работающему + +**Обязательный заголовок:** +```http +Authorization: Bearer {access_token} +``` + +**Пример подключения (C#):** +```csharp +var ws = new ClientWebSocket(); +ws.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}"); +await ws.ConnectAsync(new Uri("wss://corp.robotail.pro/ws"), CancellationToken.None); +``` + +### Формат сообщений + +Все WebSocket сообщения имеют единый формат: + +```json +{ + "type": "message_type", + "data": { + // данные специфичные для типа + } +} +``` + +### Типы сообщений + +#### 1. Отправка сообщения (Client → Server) + +```json +{ + "type": "message", + "data": { + "receiverId": 2, + "content": "Текст сообщения" + } +} +``` + +#### 2. Получение сообщения (Server → Client) + +```json +{ + "type": "message", + "data": { + "id": 456, + "senderId": 2, + "senderName": "Мария Петрова", + "receiverId": 1, + "content": "Текст сообщения", + "timestamp": "2026-01-12T10:30:00Z" + } +} +``` + +#### 3. Индикатор набора текста (Client → Server) + +```json +{ + "type": "typing", + "data": { + "recipientId": 2, + "isTyping": true + } +} +``` + +#### 4. Индикатор набора текста (Server → Client) + +```json +{ + "type": "typing", + "data": { + "senderId": 2, + "senderName": "Мария Петрова", + "isTyping": true + } +} +``` + +#### 5. Обновление статуса пользователя (Server → Client) + +```json +{ + "type": "user_status", + "data": { + "userId": 2, + "isOnline": true + } +} +``` + +### Обработка событий (C#) + +```csharp +// Подписка на события +ServiceProvider.WebSocketService.MessageReceived += OnMessageReceived; +ServiceProvider.WebSocketService.UserStatusChanged += OnUserStatusChanged; +ServiceProvider.WebSocketService.TypingIndicatorReceived += OnTypingIndicator; + +// Обработчики +private void OnMessageReceived(object sender, Message message) +{ + // Обработка нового сообщения +} + +private void OnUserStatusChanged(object sender, (int userId, bool isOnline) status) +{ + // Обновление статуса пользователя в UI +} + +private void OnTypingIndicator(object sender, (int userId, bool isTyping) typing) +{ + // Показать/скрыть индикатор "печатает..." +} +``` + +## 🔄 Система управления токенами + +### Автоматическое обновление + +ApiService автоматически управляет токенами: + +1. **При получении 401/403 ошибки:** + - Пытается обновить access токен используя refresh токен + - Повторяет оригинальный запрос с новым токеном + - При неудаче перенаправляет на страницу входа + +2. **При успешном обновлении:** + - Сохраняет новый access токен в SecureStorage + - Обновляет HTTP заголовки + - Возвращает результат оригинального запроса + +3. **При неудаче обновления:** + - Очищает все токены + - Очищает SecureStorage + - Требует повторную аутентификацию + +### Хранение токенов + +Токены хранятся в `SecureStorage`: + +```csharp +// Сохранение +await SecureStorage.SetAsync("access_token", accessToken); +await SecureStorage.SetAsync("refresh_token", refreshToken); + +// Получение +var accessToken = await SecureStorage.GetAsync("access_token"); +var refreshToken = await SecureStorage.GetAsync("refresh_token"); + +// Удаление +SecureStorage.Remove("access_token"); +SecureStorage.Remove("refresh_token"); +``` + +## ⚠️ Обработка ошибок + +### HTTP статус коды + +- **200 OK** - успешный запрос +- **201 Created** - ресурс создан +- **400 Bad Request** - неверные параметры запроса +- **401 Unauthorized** - токен отсутствует или недействителен +- **403 Forbidden** - доступ запрещен +- **404 Not Found** - ресурс не найден +- **500 Internal Server Error** - ошибка сервера + +### Формат ошибок + +```json +{ + "error": "Описание ошибки", + "detail": "Детальная информация", + "code": "ERROR_CODE" +} +``` + +### WebSocket коды закрытия + +- **1000** - нормальное закрытие +- **1001** - сервер недоступен +- **1006** - аномальное закрытие (например, потеря сети) +- **1008** - нарушение политики (например, недействительный токен) + +## 🔧 Конфигурация ApiService + +```csharp +var config = new ApiConfig +{ + BaseUrl = "https://corp.robotail.pro/api", + TokenObtainPath = "/auth/token/", + TokenRefreshPath = "/auth/token/refresh/", + Timeout = 30, + DefaultHeaders = new Dictionary + { + { "Accept", "application/json" }, + { "Content-Type", "application/json" } + } +}; + +var apiService = new ApiService(config); +``` + +## 📝 Примечания + +### Лимиты и ограничения + +- Максимальная длина сообщения: 4000 символов +- Rate limiting: 100 запросов в минуту на пользователя +- WebSocket idle timeout: 5 минут без активности + +### Рекомендации + +1. **Приоритет WebSocket** - используйте WebSocket для отправки сообщений, REST API только как fallback +2. **Переподключение** - реализуйте автоматическое переподключение WebSocket при разрыве +3. **Кэширование** - кэшируйте список пользователей для offline режима +4. **Optimistic UI** - обновляйте UI сразу, не дожидаясь ответа сервера + +--- + +**Последнее обновление:** 2026-01-12 + diff --git a/TECHDOC/ARCHITECTURE.md b/TECHDOC/ARCHITECTURE.md new file mode 100644 index 0000000..7c2bb1e --- /dev/null +++ b/TECHDOC/ARCHITECTURE.md @@ -0,0 +1,224 @@ +# Архитектура приложения + +## 📋 Структура проекта + +``` +CORP-MAUI/ +├── Models/ # Модели данных +│ ├── User.cs # Модель пользователя +│ ├── Message.cs # Модель сообщения +│ └── AuthResponse.cs # Ответ аутентификации +├── Services/ # Сервисы +│ ├── ApiService.cs # HTTP-клиент для REST API +│ └── WebSocketService.cs # WebSocket-клиент +├── Pages/ # Страницы приложения +│ ├── LoginPage.xaml # Страница входа +│ ├── ProfilePage.xaml # Профиль пользователя +│ ├── UsersPage.xaml # Список сотрудников +│ └── ChatPage.xaml # Чат с сотрудником +├── Platforms/ # Платформенно-специфичный код +│ ├── Android/ +│ ├── iOS/ +│ ├── MacCatalyst/ +│ ├── Windows/ +│ └── Tizen/ +├── Resources/ # Ресурсы приложения +│ ├── AppIcon/ +│ ├── Fonts/ +│ ├── Images/ +│ ├── Splash/ +│ └── Styles/ +├── ServiceProvider.cs # Глобальный провайдер сервисов +├── App.xaml.cs # Точка входа приложения +└── AppShell.xaml # Навигационная структура +``` + +## 🏛️ Ключевые компоненты + +### 1. ApiService - REST API клиент + +**Назначение:** HTTP-клиент для взаимодействия с REST API сервера. + +**Основные функции:** +- Аутентификация пользователей (`POST /auth/login`) +- Получение списка пользователей (`GET /users`) +- Получение сообщений (`GET /messages`) +- Отправка сообщений (`POST /messages`) +- Поиск пользователей (`GET /users/search`) +- Обновление статуса (`PUT /users/status`) + +**Особенности:** +- Автоматическое управление JWT токенами (access/refresh) +- Автоматическое обновление токена при истечении (401/403) +- Безопасное хранение токенов в SecureStorage +- Повторная отправка запроса после обновления токена +- Настраиваемая конфигурация (BaseUrl, Timeout, Headers) + +**Используемые технологии:** +- `HttpClient` для HTTP запросов +- `SecureStorage` для хранения токенов +- `Newtonsoft.Json` для сериализации/десериализации + +### 2. WebSocketService - WebSocket клиент + +**Назначение:** Real-time коммуникация через WebSocket. + +**Основные функции:** +- Подключение к WebSocket серверу +- Отправка и получение сообщений в реальном времени +- Индикаторы набора текста ("печатает...") +- Обновление статусов пользователей (онлайн/оффлайн) +- Автоматическое переподключение при разрыве соединения + +**Особенности:** +- Автоматический подбор рабочего WebSocket URL +- Поддержка WSS (WebSocket Secure) +- Передача Bearer токена при подключении +- Event-based архитектура для обработки сообщений + +**События:** +- `MessageReceived` - получено новое сообщение +- `UserStatusChanged` - изменился статус пользователя +- `TypingIndicatorReceived` - пользователь печатает + +**Используемые технологии:** +- `System.Net.WebSockets.ClientWebSocket` +- `Newtonsoft.Json` для работы с JSON + +### 3. ServiceProvider - Singleton провайдер + +**Назначение:** Централизованный доступ к сервисам приложения. + +**Преимущества:** +- Единая точка доступа к ApiService и WebSocketService +- Гарантия единственного экземпляра сервисов +- Упрощение dependency injection + +**Использование:** +```csharp +var apiService = ServiceProvider.ApiService; +var wsService = ServiceProvider.WebSocketService; +``` + +## 🎯 Паттерны проектирования + +### Singleton +Используется для ServiceProvider, ApiService и WebSocketService для обеспечения единственного экземпляра в приложении. + +### Repository Pattern +ApiService действует как репозиторий для работы с данными сервера. + +### Observer Pattern +WebSocketService использует события (Events) для уведомления UI о новых сообщениях и изменениях статусов. + +### MVVM (Model-View-ViewModel) +Архитектура страниц следует паттерну MVVM: +- **Model**: классы в папке Models +- **View**: XAML файлы в папке Pages +- **ViewModel**: code-behind файлы (.xaml.cs) + +## 🔧 Используемые технологии + +### Фреймворки +- **.NET 9.0** - платформа разработки +- **.NET MAUI** - кроссплатформенный UI фреймворк + +### Библиотеки +- **Newtonsoft.Json** - JSON сериализация/десериализация +- **System.Net.WebSockets** - WebSocket клиент +- **Microsoft.Maui.Essentials.SecureStorage** - безопасное хранение данных + +### Языки и стандарты +- **C# 12** - язык программирования +- **XAML** - разметка пользовательского интерфейса +- **JSON** - формат обмена данными +- **JWT** - токены аутентификации +- **WebSocket Protocol** - real-time коммуникация + +## 🔐 Безопасность + +### Хранение токенов +- JWT токены (access/refresh) хранятся в `SecureStorage` +- `SecureStorage` использует платформенные механизмы шифрования: + - **Android**: KeyStore + - **iOS**: Keychain + - **Windows**: Data Protection API + +### Коммуникация +- **HTTPS** для REST API (TLS/SSL) +- **WSS** для WebSocket (WebSocket Secure) +- **Bearer Authentication** для всех защищенных endpoints + +### Автоматический выход +- При истечении refresh токена пользователь перенаправляется на страницу входа +- Токены очищаются при выходе из системы + +## 🎨 UI/UX архитектура + +### Навигация +- **AppShell** - основная навигационная структура +- **FlyoutMenu** - боковое меню +- **TabBar** - нижняя панель вкладок + +### Страницы +1. **LoginPage** - аутентификация +2. **UsersPage** - список сотрудников с поиском +3. **ChatPage** - личные сообщения +4. **ProfilePage** - профиль пользователя + +### UI особенности +- Material Design стиль +- Адаптивный дизайн для разных экранов +- Плавные анимации и переходы +- Индикаторы загрузки +- Pull-to-refresh для обновления данных +- User-friendly сообщения об ошибках + +## 📱 Платформенная специфика + +### Android +- Минимальная версия: API 21 (Android 5.0) +- Target версия: API 34 (Android 14) +- Разрешения: INTERNET, ACCESS_NETWORK_STATE + +### iOS +- Минимальная версия: iOS 11.0 +- Требуется Xcode и Mac для сборки +- Разрешения для фоновой работы + +### Windows +- Минимальная версия: Windows 10.0.19041.0 +- UWP приложение + +### MacCatalyst +- Минимальная версия: macOS 10.15 + +## 🔄 Жизненный цикл приложения + +1. **Запуск** (`App.xaml.cs`) + - Инициализация сервисов + - Проверка сохраненных токенов + - Определение стартовой страницы (Login/Main) + +2. **Аутентификация** (`LoginPage`) + - Ввод учетных данных + - Получение JWT токенов + - Сохранение в SecureStorage + - Подключение к WebSocket + - Переход к главному экрану + +3. **Работа с данными** + - REST API для запросов данных + - WebSocket для real-time обновлений + - Автоматическое обновление токенов + +4. **Выход** + - Обновление статуса на "офлайн" + - Отключение WebSocket + - Очистка токенов + - Возврат к LoginPage + +--- + +**Последнее обновление:** 2026-01-12 + diff --git a/TECHDOC/CODE_EXAMPLES.md b/TECHDOC/CODE_EXAMPLES.md new file mode 100644 index 0000000..39612bc --- /dev/null +++ b/TECHDOC/CODE_EXAMPLES.md @@ -0,0 +1,581 @@ +# Примеры кода + +Практические примеры использования API и компонентов приложения. + +## 🔐 Аутентификация + +### Вход в систему + +```csharp +using CORP_MAUI.Services; + +// В LoginPage.xaml.cs +private async void OnLoginClicked(object sender, EventArgs e) +{ + var email = EmailEntry.Text; + var password = PasswordEntry.Text; + + // Показать индикатор загрузки + LoadingIndicator.IsVisible = true; + LoginButton.IsEnabled = false; + + try + { + // Выполнить вход + var result = await ServiceProvider.ApiService.LoginAsync(email, password); + + if (result.Success) + { + // Токены автоматически сохранены в SecureStorage + var user = result.User; + + // Подключиться к WebSocket + await ServiceProvider.WebSocketService.ConnectAsync(result.Token); + + // Перейти к главному экрану + Application.Current.MainPage = new AppShell(); + } + else + { + await DisplayAlert("Ошибка", result.Message, "OK"); + } + } + catch (Exception ex) + { + await DisplayAlert("Ошибка", $"Не удалось войти: {ex.Message}", "OK"); + } + finally + { + LoadingIndicator.IsVisible = false; + LoginButton.IsEnabled = true; + } +} +``` + +### Проверка сохраненной сессии + +```csharp +// В App.xaml.cs +public App() +{ + InitializeComponent(); + + // Проверить наличие сохраненного токена + Task.Run(async () => + { + var accessToken = await SecureStorage.GetAsync("access_token"); + + if (!string.IsNullOrEmpty(accessToken)) + { + try + { + // Попытаться получить текущего пользователя + var user = await ServiceProvider.ApiService.GetCurrentUserAsync(); + + if (user != null) + { + // Подключиться к WebSocket + await ServiceProvider.WebSocketService.ConnectAsync(accessToken); + + // Перейти к главному экрану + MainPage = new AppShell(); + return; + } + } + catch + { + // Токен недействителен + } + } + + // Показать страницу входа + MainPage = new NavigationPage(new LoginPage()); + }); +} +``` + +### Выход из системы + +```csharp +// В ProfilePage.xaml.cs +private async void OnLogoutClicked(object sender, EventArgs e) +{ + try + { + // Обновить статус на offline + await ServiceProvider.ApiService.UpdateStatusAsync(false); + + // Отключиться от WebSocket + await ServiceProvider.WebSocketService.DisconnectAsync(); + + // Очистить токены + ServiceProvider.ApiService.Logout(); + + // Вернуться на страницу входа + Application.Current.MainPage = new NavigationPage(new LoginPage()); + } + catch (Exception ex) + { + await DisplayAlert("Ошибка", $"Не удалось выйти: {ex.Message}", "OK"); + } +} +``` + +## 👥 Работа с пользователями + +### Получение списка сотрудников + +```csharp +// В UsersPage.xaml.cs +private List _allUsers; +private ObservableCollection _displayedUsers; + +protected override async void OnAppearing() +{ + base.OnAppearing(); + await LoadUsersAsync(); +} + +private async Task LoadUsersAsync() +{ + try + { + LoadingIndicator.IsVisible = true; + + // Получить список сотрудников + _allUsers = await ServiceProvider.ApiService.GetEmployeesAsync(); + _displayedUsers = new ObservableCollection(_allUsers); + + UsersCollectionView.ItemsSource = _displayedUsers; + } + catch (Exception ex) + { + await DisplayAlert("Ошибка", $"Не удалось загрузить пользователей: {ex.Message}", "OK"); + } + finally + { + LoadingIndicator.IsVisible = false; + } +} +``` + +### Поиск пользователей + +```csharp +// В UsersPage.xaml.cs +private async void OnSearchTextChanged(object sender, TextChangedEventArgs e) +{ + var searchText = e.NewTextValue?.Trim(); + + if (string.IsNullOrEmpty(searchText)) + { + // Показать всех пользователей + _displayedUsers.Clear(); + foreach (var user in _allUsers) + { + _displayedUsers.Add(user); + } + return; + } + + try + { + // Поиск на сервере + var results = await ServiceProvider.ApiService.SearchUsersAsync(searchText); + + _displayedUsers.Clear(); + foreach (var user in results) + { + _displayedUsers.Add(user); + } + } + catch (Exception ex) + { + // Локальная фильтрация в случае ошибки + var filtered = _allUsers.Where(u => + u.FirstName.Contains(searchText, StringComparison.OrdinalIgnoreCase) || + u.LastName.Contains(searchText, StringComparison.OrdinalIgnoreCase) || + u.Department?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true || + u.Position?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true + ).ToList(); + + _displayedUsers.Clear(); + foreach (var user in filtered) + { + _displayedUsers.Add(user); + } + } +} +``` + +### Pull-to-Refresh + +```csharp +// В UsersPage.xaml.cs +private async void OnRefreshing(object sender, EventArgs e) +{ + try + { + await LoadUsersAsync(); + } + finally + { + RefreshView.IsRefreshing = false; + } +} +``` + +```xml + + + + + + +``` + +## 💬 Работа с сообщениями + +### Загрузка истории сообщений + +```csharp +// В ChatPage.xaml.cs +private User _recipient; +private User _currentUser; +private ObservableCollection _messages; + +public ChatPage(User recipient) +{ + InitializeComponent(); + _recipient = recipient; + _messages = new ObservableCollection(); + MessagesCollectionView.ItemsSource = _messages; +} + +protected override async void OnAppearing() +{ + base.OnAppearing(); + + // Получить текущего пользователя + _currentUser = await ServiceProvider.ApiService.GetCurrentUserAsync(); + + // Загрузить историю сообщений + await LoadMessagesAsync(); + + // Подписаться на новые сообщения + ServiceProvider.WebSocketService.MessageReceived += OnMessageReceived; + ServiceProvider.WebSocketService.TypingIndicatorReceived += OnTypingIndicator; +} + +protected override void OnDisappearing() +{ + base.OnDisappearing(); + + // Отписаться от событий + ServiceProvider.WebSocketService.MessageReceived -= OnMessageReceived; + ServiceProvider.WebSocketService.TypingIndicatorReceived -= OnTypingIndicator; +} + +private async Task LoadMessagesAsync() +{ + try + { + var messages = await ServiceProvider.ApiService.GetMessagesAsync(_recipient.Id); + + _messages.Clear(); + foreach (var message in messages.OrderBy(m => m.Timestamp)) + { + _messages.Add(message); + } + + // Прокрутить к последнему сообщению + if (_messages.Any()) + { + MessagesCollectionView.ScrollTo(_messages.Last(), position: ScrollToPosition.End, animate: false); + } + } + catch (Exception ex) + { + await DisplayAlert("Ошибка", $"Не удалось загрузить сообщения: {ex.Message}", "OK"); + } +} +``` + +### Отправка сообщения + +```csharp +// В ChatPage.xaml.cs +private async void OnSendClicked(object sender, EventArgs e) +{ + var content = MessageEntry.Text?.Trim(); + + if (string.IsNullOrEmpty(content)) + return; + + var message = new Message + { + SenderId = _currentUser.Id, + SenderName = $"{_currentUser.FirstName} {_currentUser.LastName}", + ReceiverId = _recipient.Id, + Content = content, + Timestamp = DateTime.Now + }; + + // Очистить поле ввода + MessageEntry.Text = string.Empty; + + // Добавить в UI оптимистично + _messages.Add(message); + MessagesCollectionView.ScrollTo(message, position: ScrollToPosition.End, animate: true); + + try + { + // Попытаться отправить через WebSocket + if (ServiceProvider.WebSocketService.IsConnected) + { + await ServiceProvider.WebSocketService.SendMessageAsync(message); + } + else + { + // Fallback на REST API + var sentMessage = await ServiceProvider.ApiService.SendMessageAsync(message); + + // Обновить ID сообщения + message.Id = sentMessage.Id; + } + } + catch (Exception ex) + { + await DisplayAlert("Ошибка", $"Не удалось отправить сообщение: {ex.Message}", "OK"); + + // Удалить сообщение из UI + _messages.Remove(message); + } +} +``` + +### Получение новых сообщений + +```csharp +// В ChatPage.xaml.cs +private void OnMessageReceived(object sender, Message message) +{ + // Проверить, что сообщение от текущего собеседника + if (message.SenderId == _recipient.Id && message.ReceiverId == _currentUser.Id) + { + MainThread.BeginInvokeOnMainThread(() => + { + _messages.Add(message); + MessagesCollectionView.ScrollTo(message, position: ScrollToPosition.End, animate: true); + }); + } +} +``` + +### Индикатор "печатает..." + +```csharp +// В ChatPage.xaml.cs +private CancellationTokenSource _typingCts; + +private void OnMessageEntryTextChanged(object sender, TextChangedEventArgs e) +{ + // Отменить предыдущий таймер + _typingCts?.Cancel(); + _typingCts = new CancellationTokenSource(); + + var isTyping = !string.IsNullOrEmpty(e.NewTextValue); + + // Отправить индикатор "печатает" + Task.Run(async () => + { + try + { + await ServiceProvider.WebSocketService.SendTypingIndicatorAsync(_recipient.Id, isTyping); + + if (isTyping) + { + // Через 3 секунды отправить "перестал печатать" + await Task.Delay(3000, _typingCts.Token); + await ServiceProvider.WebSocketService.SendTypingIndicatorAsync(_recipient.Id, false); + } + } + catch (TaskCanceledException) + { + // Таймер был отменен + } + }, _typingCts.Token); +} + +private void OnTypingIndicator(object sender, (int userId, string userName, bool isTyping) typing) +{ + if (typing.userId == _recipient.Id) + { + MainThread.BeginInvokeOnMainThread(() => + { + TypingIndicator.IsVisible = typing.isTyping; + TypingLabel.Text = typing.isTyping ? $"{typing.userName} печатает..." : string.Empty; + }); + } +} +``` + +## 🔌 WebSocket + +### Подключение и переподключение + +```csharp +// В App.xaml.cs или AppShell.xaml.cs +private async Task EnsureWebSocketConnection() +{ + if (!ServiceProvider.WebSocketService.IsConnected) + { + var token = await SecureStorage.GetAsync("access_token"); + + if (!string.IsNullOrEmpty(token)) + { + try + { + await ServiceProvider.WebSocketService.ConnectAsync(token); + } + catch (Exception ex) + { + Console.WriteLine($"WebSocket connection failed: {ex.Message}"); + + // Попытаться переподключиться через 5 секунд + await Task.Delay(5000); + await EnsureWebSocketConnection(); + } + } + } +} +``` + +### Обработка изменения статуса пользователя + +```csharp +// В UsersPage.xaml.cs +protected override void OnAppearing() +{ + base.OnAppearing(); + ServiceProvider.WebSocketService.UserStatusChanged += OnUserStatusChanged; +} + +protected override void OnDisappearing() +{ + base.OnDisappearing(); + ServiceProvider.WebSocketService.UserStatusChanged -= OnUserStatusChanged; +} + +private void OnUserStatusChanged(object sender, (int userId, bool isOnline) status) +{ + MainThread.BeginInvokeOnMainThread(() => + { + var user = _displayedUsers.FirstOrDefault(u => u.Id == status.userId); + if (user != null) + { + user.IsOnline = status.isOnline; + + // Обновить UI (если используется INotifyPropertyChanged) + var index = _displayedUsers.IndexOf(user); + _displayedUsers[index] = user; + } + }); +} +``` + +## 🔄 Обновление токенов + +Автоматическое обновление токенов происходит внутри `ApiService`, но вы можете вручную проверить: + +```csharp +try +{ + var users = await ServiceProvider.ApiService.GetEmployeesAsync(); + // ApiService автоматически обновит токен если получит 401/403 +} +catch (UnauthorizedAccessException) +{ + // Токен истек и не удалось обновить + // Перенаправить на страницу входа + Application.Current.MainPage = new NavigationPage(new LoginPage()); +} +``` + +## 📝 MVVM Pattern + +### ViewModel с INotifyPropertyChanged + +```csharp +using System.ComponentModel; +using System.Runtime.CompilerServices; + +public class ChatViewModel : INotifyPropertyChanged +{ + private ObservableCollection _messages; + private string _messageText; + private bool _isTyping; + + public ObservableCollection Messages + { + get => _messages; + set + { + _messages = value; + OnPropertyChanged(); + } + } + + public string MessageText + { + get => _messageText; + set + { + _messageText = value; + OnPropertyChanged(); + } + } + + public bool IsTyping + { + get => _isTyping; + set + { + _isTyping = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} +``` + +## 🔧 Конфигурация при инициализации + +```csharp +// Создание ApiService с кастомной конфигурацией +var config = new ApiConfig +{ + BaseUrl = "https://your-api.com/api", + TokenObtainPath = "/auth/token/", + TokenRefreshPath = "/auth/token/refresh/", + Timeout = 30, + DefaultHeaders = new Dictionary + { + { "Accept", "application/json" }, + { "X-Custom-Header", "value" } + } +}; + +var apiService = new ApiService(config); +``` + +--- + +**Последнее обновление:** 2026-01-12 + diff --git a/TECHDOC/README.md b/TECHDOC/README.md new file mode 100644 index 0000000..a5c7b19 --- /dev/null +++ b/TECHDOC/README.md @@ -0,0 +1,50 @@ +# Техническая документация CORP-MAUI + +Добро пожаловать в техническую документацию корпоративного MAUI приложения. + +## 📚 Содержание + +### [Архитектура приложения](ARCHITECTURE.md) +Подробное описание архитектуры проекта, структуры кода и ключевых компонентов: +- Структура проекта +- Описание компонентов (ApiService, WebSocketService, ServiceProvider) +- Паттерны проектирования +- Используемые технологии + +### [API и интеграция](API.md) +Полная документация по REST API и WebSocket протоколу: +- REST API endpoints +- WebSocket сообщения +- Система аутентификации и управления токенами +- Форматы запросов и ответов + +### [Установка и настройка](SETUP.md) +Инструкции по установке, настройке и сборке проекта: +- Системные требования +- Настройка окружения разработки +- Настройка Android SDK +- Сборка и запуск на разных платформах +- Конфигурация приложения + +### [Примеры кода](CODE_EXAMPLES.md) +Практические примеры использования API и компонентов: +- Аутентификация и работа с токенами +- Отправка и получение сообщений +- Работа с WebSocket +- Поиск пользователей +- Управление статусами + +## 🔗 Дополнительные ресурсы + +- [Главный README](../README.md) - Информация для конечных пользователей +- [ANDROID_SETUP.md](../ANDROID_SETUP.md) - Детальная настройка Android SDK +- [.NET MAUI Documentation](https://docs.microsoft.com/dotnet/maui/) + +## 📝 Примечания + +Эта документация предназначена для разработчиков, работающих над проектом. Если вы конечный пользователь приложения, обратитесь к [основному README](../README.md). + +--- + +**Последнее обновление:** 2026-01-12 + diff --git a/TECHDOC/SETUP.md b/TECHDOC/SETUP.md new file mode 100644 index 0000000..d1e00e9 --- /dev/null +++ b/TECHDOC/SETUP.md @@ -0,0 +1,337 @@ +# Установка и настройка + +## 📋 Системные требования + +### Общие требования + +- **.NET 9.0 SDK** - [Скачать](https://dotnet.microsoft.com/download/dotnet/9.0) +- **Visual Studio 2022 17.8+** или **JetBrains Rider 2023.3+** +- **Git** - для клонирования репозитория + +### Требования по платформам + +#### Android +- **Android SDK** - API Level 21 (Android 5.0) минимум, API 34 (Android 14) рекомендуется +- **Java Development Kit (JDK) 17** или выше +- **Android Emulator** или физическое устройство для тестирования + +#### iOS +- **macOS** - требуется для сборки +- **Xcode 15.0+** - [Скачать из Mac App Store](https://apps.apple.com/app/xcode/id497799835) +- **iOS Simulator** или физическое устройство + +#### Windows +- **Windows 10.0.19041.0** или выше +- **Windows SDK** - устанавливается с Visual Studio + +#### MacCatalyst +- **macOS 10.15** или выше +- **Xcode 15.0+** + +## 🚀 Установка + +### 1. Клонирование репозитория + +```powershell +git clone https://github.com/your-company/corp-maui.git +cd corp-maui +``` + +### 2. Установка .NET MAUI workload + +```powershell +dotnet workload install maui +``` + +### 3. Восстановление NuGet пакетов + +```powershell +dotnet restore +``` + +## ⚙️ Настройка Android SDK + +### Автоматическое определение + +.NET MAUI автоматически ищет Android SDK в стандартных путях: +- Windows: `%LOCALAPPDATA%\Android\Sdk` +- macOS/Linux: `~/Library/Android/sdk` или `~/Android/Sdk` + +### Нестандартный путь + +Если Android SDK установлен в другом месте, создайте файл `Directory.Build.props` в корне решения: + +```xml + + + D:\Applications\Android\SDK + + +``` + +> **Важно:** Измените путь на ваш актуальный путь к Android SDK + +### Переменные окружения (альтернатива) + +```powershell +# PowerShell +$env:ANDROID_HOME = "D:\Applications\Android\SDK" + +# Постоянная установка (System Properties) +[Environment]::SetEnvironmentVariable("ANDROID_HOME", "D:\Applications\Android\SDK", "User") +``` + +### Проверка установки + +```powershell +# Список установленных SDK +dotnet build -t:GetAndroidSdkPaths +``` + +## 🔨 Сборка проекта + +### Сборка для всех платформ + +```powershell +dotnet build CORP-MAUI.sln +``` + +### Сборка для конкретной платформы + +```powershell +# Android +dotnet build CORP-MAUI/CORP-MAUI.csproj -f net9.0-android + +# iOS +dotnet build CORP-MAUI/CORP-MAUI.csproj -f net9.0-ios + +# Windows +dotnet build CORP-MAUI/CORP-MAUI.csproj -f net9.0-windows10.0.19041.0 + +# MacCatalyst +dotnet build CORP-MAUI/CORP-MAUI.csproj -f net9.0-maccatalyst +``` + +### Использование скриптов (Android) + +Для удобной сборки Android созданы готовые скрипты: + +```powershell +# PowerShell (рекомендуется) +.\build-android.ps1 + +# Batch +.\build-android.bat +``` + +Скрипты автоматически: +- Устанавливают переменную `ANDROID_HOME` +- Выполняют сборку проекта +- Выводят результат сборки + +## 🏃 Запуск приложения + +### Запуск в режиме разработки + +```powershell +# Android +dotnet run -f net9.0-android --project CORP-MAUI/CORP-MAUI.csproj + +# iOS (требуется macOS) +dotnet run -f net9.0-ios --project CORP-MAUI/CORP-MAUI.csproj + +# Windows +dotnet run -f net9.0-windows10.0.19041.0 --project CORP-MAUI/CORP-MAUI.csproj +``` + +### Запуск через IDE + +#### Visual Studio 2022 +1. Откройте `CORP-MAUI.sln` +2. Выберите целевую платформу в выпадающем списке +3. Выберите устройство/эмулятор +4. Нажмите F5 или кнопку "Start Debugging" + +#### JetBrains Rider +1. Откройте `CORP-MAUI.sln` +2. В конфигурации запуска выберите Target Framework +3. Выберите устройство +4. Нажмите Shift+F10 или кнопку запуска + +## 📦 Создание релизной сборки + +### Android APK + +```powershell +dotnet publish CORP-MAUI/CORP-MAUI.csproj -f net9.0-android -c Release +``` + +APK файл будет создан в: +``` +CORP-MAUI/bin/Release/net9.0-android/publish/ +``` + +### Android AAB (для Google Play) + +```powershell +dotnet publish CORP-MAUI/CORP-MAUI.csproj -f net9.0-android -c Release -p:AndroidPackageFormat=aab +``` + +### iOS IPA + +```powershell +dotnet publish CORP-MAUI/CORP-MAUI.csproj -f net9.0-ios -c Release +``` + +### Windows MSIX + +```powershell +dotnet publish CORP-MAUI/CORP-MAUI.csproj -f net9.0-windows10.0.19041.0 -c Release +``` + +## 🔧 Конфигурация приложения + +### Изменение API URL + +Откройте `Services/ApiService.cs` и измените константу: + +```csharp +private const string BaseUrl = "https://your-api-server.com/api/v1/"; +``` + +### Изменение WebSocket URL + +Откройте `Services/WebSocketService.cs` и измените массив URLs: + +```csharp +private readonly string[] _possibleWebSocketUrls = new[] +{ + "wss://your-api-server.com/ws", + "ws://your-api-server.com/ws", + // ... другие варианты +}; +``` + +### Настройка AndroidManifest.xml + +Откройте `Platforms/Android/AndroidManifest.xml` для изменения: +- Имени пакета +- Разрешений +- Минимальной версии Android + +```xml + + + + +``` + +### Настройка Info.plist (iOS) + +Откройте `Platforms/iOS/Info.plist` для изменения: +- Bundle Identifier +- Версии приложения +- Разрешений + +## 🐛 Решение проблем + +### Android SDK не найден + +**Проблема:** Ошибка "Android SDK not found" + +**Решение:** +1. Установите переменную окружения `ANDROID_HOME` +2. Создайте `Directory.Build.props` с путем к SDK +3. Перезапустите IDE + +### Ошибки при сборке Android + +**Проблема:** Ошибки Java или Android build tools + +**Решение:** +1. Проверьте установку JDK 17+ +2. Убедитесь, что установлены Android Build Tools +3. Очистите проект: `dotnet clean` +4. Удалите папки `bin` и `obj` + +### Проблемы с подключением к API + +**Проблема:** Не удается подключиться к серверу + +**Решение:** +1. Проверьте URL в `ApiService.cs` и `WebSocketService.cs` +2. На Android эмуляторе используйте `10.0.2.2` для localhost +3. Убедитесь, что добавлено разрешение INTERNET + +### Ошибки сертификатов SSL + +**Проблема:** SSL certificate validation failed + +**Решение для разработки:** + +Добавьте в `Platforms/Android/MainApplication.cs`: +```csharp +#if DEBUG + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; +#endif +``` + +> **Внимание:** Только для разработки! Не используйте в продакшене! + +## 📚 Дополнительные ресурсы + +### Документация +- [.NET MAUI Documentation](https://docs.microsoft.com/dotnet/maui/) +- [Android Developer Guide](https://developer.android.com/) +- [iOS Developer Documentation](https://developer.apple.com/documentation/) + +### Настройка Android +Подробная инструкция по настройке Android SDK: [ANDROID_SETUP.md](../ANDROID_SETUP.md) + +### API Документация +Описание API endpoints: [API.md](API.md) + +## 🔄 Обновление зависимостей + +### Обновление .NET MAUI + +```powershell +dotnet workload update +``` + +### Обновление NuGet пакетов + +```powershell +dotnet restore --force +dotnet build +``` + +### Проверка устаревших пакетов + +```powershell +dotnet list package --outdated +``` + +## ✅ Проверка окружения + +Используйте команду для проверки установки всех необходимых компонентов: + +```powershell +dotnet workload list +``` + +Должны быть установлены: +- `maui` +- `maui-android` +- `maui-ios` (только на macOS) +- `maui-maccatalyst` (только на macOS) +- `maui-windows` + +--- + +**Последнее обновление:** 2026-01-12 +