277 lines
10 KiB
C#
277 lines
10 KiB
C#
using System.Windows;
|
||
using System.Windows.Media;
|
||
using System.Net.Http;
|
||
using Microsoft.Extensions.Configuration;
|
||
using YY.Admin.Core.Helper;
|
||
using YY.Admin.Core.Session;
|
||
using YY.Admin.FluentValidation;
|
||
using YY.Admin.Services;
|
||
using YY.Admin.Services.Service.Auth;
|
||
using YY.Admin.Services.Service.Jeecg;
|
||
using YY.Admin.Views;
|
||
|
||
namespace YY.Admin.ViewModels
|
||
{
|
||
// ViewModels/LoginViewModel.cs
|
||
public class LoginWindowViewModel : BaseViewModel
|
||
{
|
||
private readonly ISysAuthService _authService;
|
||
private readonly IDialogService _dialogService;
|
||
private readonly IJeecgLoginLogReportService _loginLogReportService;
|
||
private readonly IConfiguration _configuration;
|
||
private readonly HttpClient _httpClient = new();
|
||
private readonly CancellationTokenSource _connectivityCts = new();
|
||
private const int ConnectivityCheckIntervalSeconds = 5;
|
||
public LoginInput LoginInput { get; set; }
|
||
private string _loginMessage = string.Empty;
|
||
public DelegateCommand LoginCommand { get; }
|
||
public DelegateCommand SyncJeecgUsersCommand { get; }
|
||
public DelegateCommand OpenServerSettingsCommand { get; }
|
||
public LoginInputValidator LoginInputValidator { get; private set; }
|
||
|
||
private bool _isSyncingJeecgUsers;
|
||
/// <summary>
|
||
/// 正在从 Jeecg 同步用户到本地库
|
||
/// </summary>
|
||
public bool IsSyncingJeecgUsers
|
||
{
|
||
get => _isSyncingJeecgUsers;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isSyncingJeecgUsers, value))
|
||
{
|
||
SyncJeecgUsersCommand.RaiseCanExecuteChanged();
|
||
LoginCommand.RaiseCanExecuteChanged();
|
||
RaisePropertyChanged(nameof(SyncJeecgUsersButtonText));
|
||
RaisePropertyChanged(nameof(CanInteractWithLogin));
|
||
}
|
||
}
|
||
}
|
||
|
||
public string SyncJeecgUsersButtonText => IsSyncingJeecgUsers ? "同步中..." : "同步 Jeecg 用户";
|
||
|
||
/// <summary>登录与同步互斥,用于禁用登录按钮</summary>
|
||
public bool CanInteractWithLogin => !IsLoading && !IsSyncingJeecgUsers;
|
||
|
||
private bool _isBackendConnected;
|
||
/// <summary>
|
||
/// 后端连接状态:true=连接中,false=已断开
|
||
/// </summary>
|
||
public bool IsBackendConnected
|
||
{
|
||
get => _isBackendConnected;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isBackendConnected, value))
|
||
{
|
||
RaisePropertyChanged(nameof(BackendConnectionStatusText));
|
||
RaisePropertyChanged(nameof(BackendConnectionStatusBrush));
|
||
}
|
||
}
|
||
}
|
||
|
||
public string BackendConnectionStatusText => IsBackendConnected ? "后端连接中" : "后端已断开";
|
||
public Brush BackendConnectionStatusBrush => IsBackendConnected ? Brushes.LimeGreen : Brushes.Red;
|
||
|
||
public LoginWindowViewModel(
|
||
ISysAuthService authService,
|
||
IDialogService dialogService,
|
||
IJeecgLoginLogReportService loginLogReportService,
|
||
IContainerExtension _container,
|
||
IRegionManager regionManager
|
||
) : base(_container, regionManager)
|
||
{
|
||
_authService = authService;
|
||
_dialogService = dialogService;
|
||
_loginLogReportService = loginLogReportService;
|
||
_configuration = _container.Resolve<IConfiguration>();
|
||
_loginLogReportService.StartBackgroundSync();
|
||
Title = "系统登录";
|
||
|
||
LoginInput = new LoginInput()
|
||
{
|
||
Username = "admin",
|
||
Password = "123456"
|
||
};
|
||
|
||
LoginInputValidator = new LoginInputValidator();
|
||
|
||
LoginCommand = new DelegateCommand(async () => await LoginAsync(), CanLogin)
|
||
.ObservesProperty(() => IsLoading)
|
||
.ObservesProperty(() => IsSyncingJeecgUsers);
|
||
|
||
SyncJeecgUsersCommand = new DelegateCommand(async () => await SyncJeecgUsersAsync(), CanSyncJeecgUsers)
|
||
.ObservesProperty(() => IsLoading)
|
||
.ObservesProperty(() => IsSyncingJeecgUsers);
|
||
|
||
OpenServerSettingsCommand = new DelegateCommand(OpenServerSettings);
|
||
|
||
_ = StartBackendConnectivityLoopAsync(_connectivityCts.Token);
|
||
}
|
||
|
||
public string LoginMessage
|
||
{
|
||
get => _loginMessage;
|
||
set => SetProperty(ref _loginMessage, value);
|
||
}
|
||
|
||
public string LoginButtonText => IsLoading ? "登录中..." : "登录";
|
||
|
||
protected override void OnIsLoadingChanged()
|
||
{
|
||
base.OnIsLoadingChanged();
|
||
RaisePropertyChanged(nameof(LoginButtonText));
|
||
RaisePropertyChanged(nameof(CanInteractWithLogin));
|
||
LoginCommand.RaiseCanExecuteChanged();
|
||
SyncJeecgUsersCommand.RaiseCanExecuteChanged();
|
||
}
|
||
|
||
private bool CanLogin()
|
||
{
|
||
return !IsLoading && !IsSyncingJeecgUsers;
|
||
}
|
||
|
||
private bool CanSyncJeecgUsers()
|
||
{
|
||
return !IsLoading && !IsSyncingJeecgUsers;
|
||
}
|
||
|
||
private async Task SyncJeecgUsersAsync()
|
||
{
|
||
LoginMessage = string.Empty;
|
||
IsSyncingJeecgUsers = true;
|
||
try
|
||
{
|
||
await UIHelper.WaitForRenderAsync();
|
||
var (success, message) = await _authService.SyncJeecgUsersToLocalFromLoginScreenAsync();
|
||
LoginMessage = message;
|
||
if (success)
|
||
{
|
||
_logger?.Information("登录页一键同步 Jeecg 用户成功");
|
||
}
|
||
else
|
||
{
|
||
_logger?.Warning($"登录页一键同步 Jeecg 用户:{message}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoginMessage = $"同步出错:{ex.Message}";
|
||
}
|
||
finally
|
||
{
|
||
IsSyncingJeecgUsers = false;
|
||
}
|
||
}
|
||
|
||
private async Task LoginAsync()
|
||
{
|
||
IsLoading = true;
|
||
//LoginMessage = string.Empty;
|
||
|
||
try
|
||
{
|
||
// 让出线程,让UI先渲染
|
||
await UIHelper.WaitForRenderAsync();
|
||
|
||
var response = await _authService.LoginAsync(LoginInput);
|
||
|
||
if (response.Success)
|
||
{
|
||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, true, "登录成功");
|
||
_connectivityCts.Cancel();
|
||
//设置会话
|
||
AppSession.CurrentUser = response.User;
|
||
// 设置用户上下文
|
||
SetUserContext(response.User!, response.Token);
|
||
// 启动定时器
|
||
StartTokenCheckTimer();
|
||
// 登录成功,打开主窗口
|
||
var loginWindow = Application.Current.MainWindow;
|
||
var mainWindow = _container.Resolve<MainWindow>();
|
||
Application.Current.MainWindow = mainWindow;
|
||
//把窗口和RegionManager 绑定一下即可
|
||
SetRegionManager(mainWindow);
|
||
mainWindow.Show();
|
||
loginWindow.Close();
|
||
|
||
// 不等待异步更新用户登录信息,不阻塞主窗口打开
|
||
_ = _authService.UpdateUserLoginInfoAsync(response.User!);
|
||
}
|
||
else
|
||
{
|
||
LoginMessage = response.Message;
|
||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, false, response.Message);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoginMessage = $"登录出错:{ex.Message}";
|
||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, false, ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
IsLoading = false;
|
||
}
|
||
}
|
||
|
||
private async Task StartBackendConnectivityLoopAsync(CancellationToken cancellationToken)
|
||
{
|
||
while (!cancellationToken.IsCancellationRequested)
|
||
{
|
||
bool connected = false;
|
||
try
|
||
{
|
||
// 每轮都重新读取配置,保存服务器设置后可即时生效
|
||
var baseUrl = _configuration.GetValue<string>("JeecgIntegration:BaseUrl")?.TrimEnd('/');
|
||
var userListPath = _configuration.GetValue<string>("JeecgIntegration:UserListPath") ?? "/sys/user/scada/queryUser";
|
||
var probeUrl = string.IsNullOrWhiteSpace(baseUrl)
|
||
? string.Empty
|
||
: $"{baseUrl}{userListPath}?pageNo=1&pageSize=1&includeDetail=false";
|
||
if (!string.IsNullOrWhiteSpace(probeUrl))
|
||
{
|
||
using var req = new HttpRequestMessage(HttpMethod.Get, probeUrl);
|
||
using var resp = await _httpClient.SendAsync(req, cancellationToken);
|
||
connected = resp.IsSuccessStatusCode;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
connected = false;
|
||
}
|
||
|
||
try
|
||
{
|
||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||
{
|
||
IsBackendConnected = connected;
|
||
});
|
||
}
|
||
catch
|
||
{
|
||
// 忽略窗口关闭后的调度异常
|
||
}
|
||
|
||
try
|
||
{
|
||
await Task.Delay(TimeSpan.FromSeconds(ConnectivityCheckIntervalSeconds), cancellationToken);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OpenServerSettings()
|
||
{
|
||
_dialogService.ShowDialog("ServerSettingsDialog", r =>
|
||
{
|
||
if (r.Result == ButtonResult.OK)
|
||
{
|
||
LoginMessage = "服务器配置已保存";
|
||
}
|
||
});
|
||
}
|
||
}
|
||
} |