corp-app/TECHDOC/CODE_EXAMPLES.md

582 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Примеры кода
Практические примеры использования 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
// В EmployeesPage.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
// В EmployeesPage.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
// В EmployeesPage.xaml.cs
private async void OnRefreshing(object sender, EventArgs e)
{
try
{
await LoadUsersAsync();
}
finally
{
RefreshView.IsRefreshing = false;
}
}
```
```xml
<!-- В EmployeesPage.xaml -->
<RefreshView x:Name="RefreshView" Refreshing="OnRefreshing">
<CollectionView x:Name="EmployeesCollectionView">
<!-- 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
// В EmployeesPage.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