Add technical documentation for CORP_MAUI application

This commit is contained in:
Данил Ярош 2026-01-12 09:23:42 +03:00
parent bcecc24013
commit 6fe97cc8c5
6 changed files with 1764 additions and 540 deletions

615
README.md
View File

@ -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
<Project>
<PropertyGroup>
<AndroidSdkDirectory>D:\Applications\Android\SDK</AndroidSdkDirectory>
</PropertyGroup>
</Project>
```
**Важно:** Измените путь на ваш, если 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<string, string>
{
{ "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)**.

497
TECHDOC/API.md Normal file
View File

@ -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<string, string>
{
{ "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

224
TECHDOC/ARCHITECTURE.md Normal file
View File

@ -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

581
TECHDOC/CODE_EXAMPLES.md Normal file
View File

@ -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<User> _allUsers;
private ObservableCollection<User> _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<User>(_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
<!-- В UsersPage.xaml -->
<RefreshView x:Name="RefreshView" Refreshing="OnRefreshing">
<CollectionView x:Name="UsersCollectionView">
<!-- ItemTemplate -->
</CollectionView>
</RefreshView>
```
## 💬 Работа с сообщениями
### Загрузка истории сообщений
```csharp
// В ChatPage.xaml.cs
private User _recipient;
private User _currentUser;
private ObservableCollection<Message> _messages;
public ChatPage(User recipient)
{
InitializeComponent();
_recipient = recipient;
_messages = new ObservableCollection<Message>();
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<Message> _messages;
private string _messageText;
private bool _isTyping;
public ObservableCollection<Message> 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<string, string>
{
{ "Accept", "application/json" },
{ "X-Custom-Header", "value" }
}
};
var apiService = new ApiService(config);
```
---
**Последнее обновление:** 2026-01-12

50
TECHDOC/README.md Normal file
View File

@ -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

337
TECHDOC/SETUP.md Normal file
View File

@ -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
<Project>
<PropertyGroup>
<AndroidSdkDirectory>D:\Applications\Android\SDK</AndroidSdkDirectory>
</PropertyGroup>
</Project>
```
> **Важно:** Измените путь на ваш актуальный путь к 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
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.corpmaui">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
```
### Настройка 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