161 lines
6.6 KiB
C#
161 lines
6.6 KiB
C#
using System.Text.Json.Serialization;
|
||
|
||
namespace KronaStaffTerminal.Models;
|
||
|
||
public sealed class EmployeeRecord
|
||
{
|
||
public string Id { get; set; } = UlidGenerator.New();
|
||
public string PersonnelNumber { get; set; } = "";
|
||
public string LastName { get; set; } = "";
|
||
public string FirstName { get; set; } = "";
|
||
public string Patronymic { get; set; } = "";
|
||
private string _displayName = "";
|
||
public string DisplayName
|
||
{
|
||
get
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(_displayName)) return _displayName;
|
||
var parts = new[] { LastName, FirstName, Patronymic }.Where(p => !string.IsNullOrWhiteSpace(p));
|
||
var combined = string.Join(" ", parts);
|
||
return string.IsNullOrWhiteSpace(combined) ? "" : combined;
|
||
}
|
||
set => _displayName = value;
|
||
}
|
||
public string Position { get; set; } = "";
|
||
public string Gender { get; set; } = "";
|
||
public bool IsAdministrator { get; set; }
|
||
public bool IsBlocked { get; set; }
|
||
public DateTimeOffset? BlockedAtUtc { get; set; }
|
||
public List<FingerprintRecord> Fingerprints { get; set; } = [];
|
||
public string TelegramChatId { get; set; } = "";
|
||
public string VkPeerId { get; set; } = "";
|
||
public string MaxUserId { get; set; } = "";
|
||
|
||
public void SetFullName(string lastName, string firstName, string patronymic)
|
||
{
|
||
LastName = lastName?.Trim() ?? "";
|
||
FirstName = firstName?.Trim() ?? "";
|
||
Patronymic = patronymic?.Trim() ?? "";
|
||
var parts = new[] { LastName, FirstName, Patronymic }.Where(p => !string.IsNullOrWhiteSpace(p));
|
||
_displayName = string.Join(" ", parts);
|
||
}
|
||
|
||
// Legacy single-fingerprint fields are kept for one-way migration of existing encrypted state.
|
||
public int FingerprintId { get; set; }
|
||
public string? FingerprintTemplate { get; set; }
|
||
|
||
[JsonIgnore]
|
||
public bool HasFingerprint => Fingerprints.Count > 0;
|
||
|
||
[JsonIgnore]
|
||
public string FingerprintStatus => Fingerprints.Count switch
|
||
{
|
||
0 => "Отпечатков нет",
|
||
1 => "1 отпечаток",
|
||
2 or 3 or 4 => $"{Fingerprints.Count} отпечатка",
|
||
_ => $"{Fingerprints.Count} отпечатков"
|
||
};
|
||
|
||
[JsonIgnore]
|
||
public string AccessStatus => IsBlocked ? "Заблокирован" : IsAdministrator ? "Администратор" : "Сотрудник";
|
||
|
||
[JsonIgnore]
|
||
public string NotificationStatus
|
||
{
|
||
get
|
||
{
|
||
if (IsBlocked) return "Отключены";
|
||
var channels = new List<string>();
|
||
if (!string.IsNullOrWhiteSpace(TelegramChatId)) channels.Add("Telegram");
|
||
if (!string.IsNullOrWhiteSpace(VkPeerId)) channels.Add("VK");
|
||
if (!string.IsNullOrWhiteSpace(MaxUserId)) channels.Add("MAX");
|
||
return channels.Count == 0 ? "Не подключены" : string.Join(", ", channels);
|
||
}
|
||
}
|
||
|
||
[JsonIgnore]
|
||
public string GenderDisplay => string.IsNullOrWhiteSpace(Gender) ? "—" : Gender;
|
||
}
|
||
|
||
public sealed class FingerprintRecord
|
||
{
|
||
public int Id { get; set; }
|
||
public string Template { get; set; } = "";
|
||
public string Label { get; set; } = "";
|
||
public DateTimeOffset EnrolledAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||
}
|
||
|
||
public sealed class AttendanceRecord
|
||
{
|
||
public string Id { get; set; } = UlidGenerator.New();
|
||
public string EmployeeId { get; set; } = "";
|
||
public string EmployeeName { get; set; } = "";
|
||
public DateTimeOffset OccurredAtUtc { get; set; }
|
||
public int ReaderIndex { get; set; }
|
||
public string EventType { get; set; } = "Приход";
|
||
public bool IsAutomatic { get; set; }
|
||
public bool NotificationDelivered { get; set; }
|
||
public string? NotificationError { get; set; }
|
||
public bool ServerSynced { get; set; }
|
||
public string? ServerSyncError { get; set; }
|
||
|
||
[JsonIgnore]
|
||
public string LocalTime => OccurredAtUtc.ToLocalTime().ToString("dd.MM.yyyy HH:mm:ss");
|
||
|
||
[JsonIgnore]
|
||
public string DeliveryStatus => NotificationDelivered ? "Отправлено" : string.IsNullOrWhiteSpace(NotificationError) ? "Не настроено" : "Ошибка отправки";
|
||
}
|
||
|
||
public sealed class TerminalSettings
|
||
{
|
||
public int ReaderIndex { get; set; }
|
||
public bool StartWithWindows { get; set; }
|
||
public string AdminPinSalt { get; set; } = "";
|
||
public string AdminPinHash { get; set; } = "";
|
||
public int AdminPinIterations { get; set; } = 120_000;
|
||
public int FailedAdminPinAttempts { get; set; }
|
||
public DateTimeOffset AdminPinBlockedUntilUtc { get; set; }
|
||
public int DuplicateScanGuardSeconds { get; set; } = 120;
|
||
public int MaximumShiftHours { get; set; } = 20;
|
||
public bool AutomaticCheckoutEnabled { get; set; }
|
||
public int AutomaticCheckoutHour { get; set; } = 2;
|
||
public int AutomaticCheckoutMinute { get; set; }
|
||
public DateTimeOffset LastAutomaticCheckoutUtc { get; set; }
|
||
public bool ShowEmployeeNamesOnTerminal { get; set; }
|
||
public bool TelegramEnabled { get; set; }
|
||
public string TelegramBotToken { get; set; } = "";
|
||
// Legacy global chat IDs are retained only so existing encrypted state remains readable.
|
||
public string TelegramChatId { get; set; } = "";
|
||
public bool VkEnabled { get; set; }
|
||
public string VkGroupToken { get; set; } = "";
|
||
public bool MaxEnabled { get; set; }
|
||
public string MaxBotToken { get; set; } = "";
|
||
public string MaxChatId { get; set; } = "";
|
||
public bool UpdateChecksEnabled { get; set; } = true;
|
||
public string UpdateFeedUrl { get; set; } = "https://git.ochenstarik.ru/api/v1/repos/ochenstarik/krona-staff/releases/latest";
|
||
public string UpdateAccessToken { get; set; } = "";
|
||
public string UpdateTrustedSignerThumbprint { get; set; } = "";
|
||
public string StaffServerUrl { get; set; } = "https://192.168.1.81:8453";
|
||
public string StaffServerToken { get; set; } = "";
|
||
public string StaffServerCertificateSha256 { get; set; } = "";
|
||
public string TerminalDeviceId { get; set; } = UlidGenerator.New();
|
||
public DateTimeOffset LastUpdateCheckUtc { get; set; }
|
||
}
|
||
|
||
public sealed class TerminalState
|
||
{
|
||
public List<EmployeeRecord> Employees { get; set; } = [];
|
||
public List<AttendanceRecord> Attendance { get; set; } = [];
|
||
public List<TerminalAuditRecord> SecurityAudit { get; set; } = [];
|
||
public TerminalSettings Settings { get; set; } = new();
|
||
}
|
||
|
||
public sealed class TerminalAuditRecord
|
||
{
|
||
public string Id { get; set; } = UlidGenerator.New();
|
||
public DateTimeOffset OccurredAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||
public string Action { get; set; } = "";
|
||
public string Actor { get; set; } = "";
|
||
public string Outcome { get; set; } = "";
|
||
public string Detail { get; set; } = "";
|
||
}
|