Add technical documentation for CORP_MAUI application
This commit is contained in:
parent
bcecc24013
commit
6fe97cc8c5
615
README.md
615
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
|
||||
<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
497
TECHDOC/API.md
Normal 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
224
TECHDOC/ARCHITECTURE.md
Normal 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
581
TECHDOC/CODE_EXAMPLES.md
Normal 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
50
TECHDOC/README.md
Normal 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
337
TECHDOC/SETUP.md
Normal 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user