/
kochkatech
/
Queue
Обзор
Документация
Войти
/
kochkatech
/
Queue
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
src/Queue.Admin/ViewModels/OneCIntegrationViewModel.cs
169 строк
6 KB
kochkareal
init: Начальная структура проекта и панель администратора
24 июл 2026, 21:42
24 июл 2026, 21:42
cd5152a
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; using Queue.Core.Models; using Queue.Data; using Queue.Integration.OneC; namespace Queue.Admin.ViewModels; public record ChannelOption(IntegrationChannel Value, string Display); public partial class OneCIntegrationViewModel : ObservableObject { private readonly QueueDbContextFactory _dbContextFactory; private readonly IOneCIntegrationService _integrationService; private int _settingsId; public ObservableCollection<IntegrationLogEntry> LogEntries { get; } = new(); public IReadOnlyList<ChannelOption> Channels { get; } = new List<ChannelOption> { new(IntegrationChannel.HttpService, "HTTP-сервис 1С"), new(IntegrationChannel.FileExchange, "Файловый обмен") }; [ObservableProperty] private IntegrationChannel channel = IntegrationChannel.HttpService; [ObservableProperty] private string endpoint = string.Empty; [ObservableProperty] private string? login; [ObservableProperty] private string syncScheduleCron = string.Empty; [ObservableProperty] private string statusMessage = string.Empty; public OneCIntegrationViewModel(QueueDbContextFactory dbContextFactory) { _dbContextFactory = dbContextFactory; // Пока единственная реализация — заглушка HTTP-канала (см. Queue.Integration.OneC/README.md). _integrationService = new HttpServiceOneCIntegration(); _ = LoadAsync(); } [RelayCommand] private async Task LoadAsync() { using var db = _dbContextFactory.CreateDbContext(); var settings = await db.IntegrationSettings.FirstOrDefaultAsync(); if (settings is null) { settings = new IntegrationSetting { Channel = IntegrationChannel.HttpService, Endpoint = string.Empty }; db.IntegrationSettings.Add(settings); await db.SaveChangesAsync(); } _settingsId = settings.Id; Channel = settings.Channel; Endpoint = settings.Endpoint; Login = settings.Login; SyncScheduleCron = settings.SyncScheduleCron ?? string.Empty; var entries = await db.IntegrationLogEntries.OrderByDescending(l => l.Timestamp).Take(20).ToListAsync(); LogEntries.Clear(); foreach (var entry in entries) { LogEntries.Add(entry); } } // password передаётся из code-behind (PasswordBox.Password не поддерживает биндинг). // Пустое значение — оставить сохранённый пароль без изменений. [RelayCommand] private async Task SaveAsync(string? password) { using var db = _dbContextFactory.CreateDbContext(); var settings = await db.IntegrationSettings.FindAsync(_settingsId); if (settings is null) { return; } settings.Channel = Channel; settings.Endpoint = Endpoint.Trim(); settings.Login = string.IsNullOrWhiteSpace(Login) ? null : Login.Trim(); settings.SyncScheduleCron = string.IsNullOrWhiteSpace(SyncScheduleCron) ? null : SyncScheduleCron.Trim(); if (!string.IsNullOrEmpty(password)) { settings.EncryptedCredentials = Protect(password); } await db.SaveChangesAsync(); StatusMessage = "Настройки сохранены."; } [RelayCommand] private async Task TestConnectionAsync() { await RunAndLogAsync("Проверка соединения", () => _integrationService.TestConnectionAsync()); } [RelayCommand] private async Task SyncNowAsync() { await RunAndLogAsync("Синхронизация", async () => { await _integrationService.SyncAsync(); return true; }); } private async Task RunAndLogAsync(string action, Func<Task<bool>> operation) { var entry = new IntegrationLogEntry { Timestamp = DateTime.Now }; try { var success = await operation(); entry.Success = success; entry.Message = success ? $"{action}: успешно" : $"{action}: неуспешно"; } catch (NotImplementedException) { entry.Success = false; entry.Message = $"{action}: канал обмена ещё не реализован (см. Queue.Integration.OneC/README.md)"; } catch (Exception ex) { entry.Success = false; entry.Message = $"{action}: ошибка — {ex.Message}"; } using (var db = _dbContextFactory.CreateDbContext()) { db.IntegrationLogEntries.Add(entry); await db.SaveChangesAsync(); } LogEntries.Insert(0, entry); StatusMessage = entry.Message; } // DPAPI: реальное локальное шифрование, привязанное к пользователю Windows. // Годится для standalone-инсталляции; не подходит, если БД потом переносят на другую машину/пользователя. private static string? Protect(string? plainText) { if (string.IsNullOrEmpty(plainText)) { return null; } var bytes = Encoding.UTF8.GetBytes(plainText); var protectedBytes = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser); return Convert.ToBase64String(protectedBytes); } }