222 lines
7.6 KiB
C#
222 lines
7.6 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Diagnostics;
|
|
using Microsoft.Maui.Controls;
|
|
|
|
namespace CORP_MAUI.Models;
|
|
|
|
public class User
|
|
{
|
|
public int Id { get; set; }
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Email { get; set; } = string.Empty;
|
|
|
|
[JsonPropertyName("phone_number")]
|
|
public string? PhoneNumber { get; set; }
|
|
|
|
[JsonPropertyName("first_name")]
|
|
public string FirstName { get; set; } = string.Empty;
|
|
|
|
[JsonPropertyName("last_name")]
|
|
public string LastName { get; set; } = string.Empty;
|
|
|
|
[JsonPropertyName("departments")]
|
|
public List<Department>? Departments { get; set; }
|
|
|
|
// Может быть либо объектом Department, либо строкой (для обратной совместимости)
|
|
public object? Department { get; set; }
|
|
|
|
// Может быть либо объектом Position, либо строкой (для обратной совместимости)
|
|
public object? Position { get; set; }
|
|
|
|
public List<Skill>? Skills { get; set; }
|
|
|
|
[JsonPropertyName("is_online")]
|
|
public bool IsOnline { get; set; }
|
|
|
|
[JsonPropertyName("avatar_url")]
|
|
public string? AvatarUrl { get; set; }
|
|
|
|
[JsonPropertyName("avatar")]
|
|
public string? Avatar { get; set; }
|
|
|
|
// Вспомогательные свойства для удобства
|
|
[JsonIgnore]
|
|
public string FullName => $"{FirstName} {LastName}".Trim();
|
|
|
|
[JsonIgnore]
|
|
public string? DisplayAvatarUrl
|
|
{
|
|
get
|
|
{
|
|
// Сначала проверяем Avatar (может содержать полный URL или data:image)
|
|
if (!string.IsNullOrEmpty(Avatar))
|
|
{
|
|
// Если это data:image - не возвращаем как URL
|
|
if (Avatar.StartsWith("data:image"))
|
|
return null;
|
|
|
|
// Если это полный URL
|
|
if (Avatar.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
|
return Avatar;
|
|
|
|
// Если это относительный путь
|
|
if (!Avatar.StartsWith("data:"))
|
|
{
|
|
string cleanUrl = Avatar.TrimStart('/');
|
|
return $"https://corp.robotail.pro/{cleanUrl}";
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(AvatarUrl)) return null;
|
|
|
|
// Handle protocol-relative URLs
|
|
if (AvatarUrl.StartsWith("//"))
|
|
return $"https:{AvatarUrl}";
|
|
|
|
if (AvatarUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
|
return AvatarUrl;
|
|
|
|
string cleanPath = AvatarUrl.TrimStart('/');
|
|
return $"https://corp.robotail.pro/{cleanPath}";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Кэшированный ImageSource аватара (не создаётся каждый раз)
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
private ImageSource? _cachedImageSource;
|
|
|
|
/// <summary>
|
|
/// Путь к файлу кэшированного аватара
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public string? CachedAvatarFilePath { get; set; }
|
|
|
|
/// <summary>
|
|
/// Флаг, что аватар был успешно загружен
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public bool IsAvatarLoaded { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Установить путь к файлу кэшированного аватара
|
|
/// </summary>
|
|
public void SetCachedAvatarFilePath(string filePath)
|
|
{
|
|
CachedAvatarFilePath = filePath;
|
|
_cachedImageSource = ImageSource.FromFile(filePath);
|
|
IsAvatarLoaded = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Сбросить кэш ImageSource
|
|
/// </summary>
|
|
public void ResetImageSourceCache()
|
|
{
|
|
_cachedImageSource = null;
|
|
IsAvatarLoaded = false;
|
|
CachedAvatarFilePath = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Универсальное свойство для отображения аватара
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public ImageSource? DisplayAvatarSource
|
|
{
|
|
get
|
|
{
|
|
// Если аватар уже загружен - возвращаем кэшированный
|
|
if (IsAvatarLoaded && _cachedImageSource != null)
|
|
{
|
|
return _cachedImageSource;
|
|
}
|
|
|
|
// Проверяем путь к файлу
|
|
if (!string.IsNullOrEmpty(CachedAvatarFilePath) && File.Exists(CachedAvatarFilePath))
|
|
{
|
|
_cachedImageSource = ImageSource.FromFile(CachedAvatarFilePath);
|
|
IsAvatarLoaded = true;
|
|
return _cachedImageSource;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Проверяем глобальный файловый кэш
|
|
string? url = DisplayAvatarUrl;
|
|
if (!string.IsNullOrEmpty(url))
|
|
{
|
|
string? cachedPath = Services.ImageCacheService.Instance.GetCachedFilePath(url);
|
|
if (!string.IsNullOrEmpty(cachedPath) && File.Exists(cachedPath))
|
|
{
|
|
CachedAvatarFilePath = cachedPath;
|
|
_cachedImageSource = ImageSource.FromFile(cachedPath);
|
|
IsAvatarLoaded = true;
|
|
return _cachedImageSource;
|
|
}
|
|
|
|
// Если нет в кэше - используем UriImageSource
|
|
return new UriImageSource
|
|
{
|
|
Uri = new Uri(url),
|
|
CachingEnabled = true,
|
|
CacheValidity = TimeSpan.FromDays(7)
|
|
};
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Trace.WriteLine($"[User] ⚠️ Ошибка загрузки аватара для {FullName}: {ex.Message}");
|
|
}
|
|
|
|
// Возвращаем placeholder
|
|
return ImageSource.FromFile("dotnet_bot.png");
|
|
}
|
|
}
|
|
|
|
[JsonIgnore]
|
|
public string DepartmentName
|
|
{
|
|
get
|
|
{
|
|
if (Departments != null && Departments.Count > 0)
|
|
return Departments[0].Name;
|
|
|
|
if (Department is Department dept)
|
|
return dept.Name;
|
|
if (Department is string str)
|
|
return str;
|
|
if (Department is JsonElement element)
|
|
{
|
|
if (element.ValueKind == JsonValueKind.String)
|
|
return element.GetString() ?? string.Empty;
|
|
if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("name", out JsonElement nameProp))
|
|
return nameProp.GetString() ?? string.Empty;
|
|
}
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
[JsonIgnore]
|
|
public string PositionName
|
|
{
|
|
get
|
|
{
|
|
if (Position is Position pos)
|
|
return pos.Name;
|
|
if (Position is string str)
|
|
return str;
|
|
if (Position is JsonElement element)
|
|
{
|
|
if (element.ValueKind == JsonValueKind.String)
|
|
return element.GetString() ?? string.Empty;
|
|
if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("name", out JsonElement nameProp))
|
|
return nameProp.GetString() ?? string.Empty;
|
|
}
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|