293 lines
9.0 KiB
C#
293 lines
9.0 KiB
C#
using WorkCameraExport.Models;
|
||
using WorkCameraExport.Services.Interfaces;
|
||
|
||
namespace WorkCameraExport.Services
|
||
{
|
||
/// <summary>
|
||
/// 登录状态管理器 - 负责管理登录状态、自动登录、登录失效检测
|
||
/// </summary>
|
||
public class LoginStateManager : IDisposable
|
||
{
|
||
private readonly ApiService _apiService;
|
||
private readonly IConfigService _configService;
|
||
private readonly ILogService? _logService;
|
||
private readonly AppSettings _settings;
|
||
private System.Threading.Timer? _tokenCheckTimer;
|
||
private bool _disposed;
|
||
|
||
// Token 检查间隔(5分钟)
|
||
private const int TokenCheckIntervalMs = 5 * 60 * 1000;
|
||
// Token 即将过期阈值(10分钟)
|
||
private const int TokenExpiryThresholdMinutes = 10;
|
||
|
||
/// <summary>
|
||
/// 登录失效事件
|
||
/// </summary>
|
||
public event EventHandler<LoginExpiredEventArgs>? LoginExpired;
|
||
|
||
/// <summary>
|
||
/// Token 即将过期事件
|
||
/// </summary>
|
||
public event EventHandler? TokenExpiringSoon;
|
||
|
||
/// <summary>
|
||
/// 当前登录用户名
|
||
/// </summary>
|
||
public string? CurrentUsername { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 是否已登录
|
||
/// </summary>
|
||
public bool IsLoggedIn => _apiService.IsLoggedIn;
|
||
|
||
public LoginStateManager(
|
||
ApiService apiService,
|
||
IConfigService configService,
|
||
ILogService? logService = null)
|
||
{
|
||
_apiService = apiService;
|
||
_configService = configService;
|
||
_logService = logService;
|
||
_settings = AppSettings.Load();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试自动登录
|
||
/// </summary>
|
||
/// <returns>是否自动登录成功</returns>
|
||
public async Task<bool> TryAutoLoginAsync()
|
||
{
|
||
// 每次尝试自动登录时重新加载设置,确保获取最新的保存信息
|
||
var settings = AppSettings.Load();
|
||
|
||
_logService?.Info($"[自动登录] 检查设置: RememberMe={settings.RememberMe}, ServerUrl={settings.ServerUrl}, SavedToken长度={settings.SavedToken?.Length ?? 0}");
|
||
|
||
// 首先检查 AppSettings 中的保存信息
|
||
if (!settings.RememberMe ||
|
||
string.IsNullOrEmpty(settings.ServerUrl) ||
|
||
string.IsNullOrEmpty(settings.SavedToken))
|
||
{
|
||
_logService?.Info("未找到保存的登录信息,跳过自动登录");
|
||
return false;
|
||
}
|
||
|
||
// 检查 Token 是否已过期
|
||
if (settings.TokenExpireTime.HasValue &&
|
||
settings.TokenExpireTime.Value <= DateTime.Now)
|
||
{
|
||
_logService?.Info("保存的 Token 已过期,清除登录信息");
|
||
ClearLoginState();
|
||
return false;
|
||
}
|
||
|
||
_logService?.Info("尝试自动登录...");
|
||
|
||
try
|
||
{
|
||
_apiService.SetBaseUrl(settings.ServerUrl);
|
||
_apiService.SetToken(settings.SavedToken);
|
||
|
||
// 验证 Token 是否有效(通过获取当前用户信息)
|
||
var (success, message, userInfo) = await _apiService.GetCurrentUserAsync();
|
||
|
||
if (success && userInfo != null)
|
||
{
|
||
CurrentUsername = userInfo.UserName ?? settings.Username;
|
||
StartTokenCheckTimer();
|
||
_logService?.Info($"自动登录成功,用户: {CurrentUsername}");
|
||
return true;
|
||
}
|
||
|
||
_logService?.Warn($"Token 验证失败: {message},清除登录信息");
|
||
ClearLoginState();
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logService?.Error($"自动登录异常: {ex.Message}", ex);
|
||
ClearLoginState();
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录成功后调用,保存登录状态
|
||
/// </summary>
|
||
public void OnLoginSuccess(string serverUrl, string username, string token, bool rememberMe)
|
||
{
|
||
CurrentUsername = username;
|
||
|
||
_settings.ServerUrl = serverUrl;
|
||
_settings.Username = username;
|
||
_settings.RememberMe = rememberMe;
|
||
|
||
if (rememberMe)
|
||
{
|
||
_settings.SavedToken = token;
|
||
_settings.TokenExpireTime = DateTime.Now.AddDays(7);
|
||
_configService.SaveCredentials(serverUrl, token, username);
|
||
}
|
||
else
|
||
{
|
||
_settings.SavedToken = "";
|
||
_settings.TokenExpireTime = null;
|
||
_configService.ClearCredentials();
|
||
}
|
||
|
||
_settings.Save();
|
||
StartTokenCheckTimer();
|
||
_logService?.Info($"登录成功,用户: {username},记住登录: {rememberMe}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动登录成功后调用,仅启动定时器,不重新保存凭证
|
||
/// </summary>
|
||
public void OnAutoLoginSuccess(string username)
|
||
{
|
||
CurrentUsername = username;
|
||
StartTokenCheckTimer();
|
||
_logService?.Info($"自动登录成功,用户: {username}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出登录
|
||
/// </summary>
|
||
public void Logout()
|
||
{
|
||
_logService?.Info($"用户 {CurrentUsername} 退出登录");
|
||
StopTokenCheckTimer();
|
||
_apiService.Logout();
|
||
ClearLoginState();
|
||
CurrentUsername = null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除登录状态
|
||
/// </summary>
|
||
public void ClearLoginState()
|
||
{
|
||
_settings.ClearLoginInfo();
|
||
_configService.ClearCredentials();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查登录状态是否有效
|
||
/// </summary>
|
||
public async Task<bool> CheckLoginStateAsync()
|
||
{
|
||
if (!_apiService.IsLoggedIn)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 通过获取当前用户信息验证 Token 有效性
|
||
var (success, _, _) = await _apiService.GetCurrentUserAsync();
|
||
|
||
if (!success)
|
||
{
|
||
OnLoginExpired("登录已失效,请重新登录");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动 Token 检查定时器
|
||
/// </summary>
|
||
private void StartTokenCheckTimer()
|
||
{
|
||
StopTokenCheckTimer();
|
||
_tokenCheckTimer = new System.Threading.Timer(
|
||
CheckTokenExpiry,
|
||
null,
|
||
TokenCheckIntervalMs,
|
||
TokenCheckIntervalMs);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止 Token 检查定时器
|
||
/// </summary>
|
||
private void StopTokenCheckTimer()
|
||
{
|
||
_tokenCheckTimer?.Dispose();
|
||
_tokenCheckTimer = null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查 Token 是否即将过期
|
||
/// </summary>
|
||
private void CheckTokenExpiry(object? state)
|
||
{
|
||
if (_settings.TokenExpireTime.HasValue)
|
||
{
|
||
var timeUntilExpiry = _settings.TokenExpireTime.Value - DateTime.Now;
|
||
|
||
if (timeUntilExpiry.TotalMinutes <= 0)
|
||
{
|
||
// Token 已过期
|
||
OnLoginExpired("登录已过期,请重新登录");
|
||
}
|
||
else if (timeUntilExpiry.TotalMinutes <= TokenExpiryThresholdMinutes)
|
||
{
|
||
// Token 即将过期,尝试刷新
|
||
_logService?.Info("Token 即将过期,触发刷新事件");
|
||
TokenExpiringSoon?.Invoke(this, EventArgs.Empty);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发登录失效事件
|
||
/// </summary>
|
||
private void OnLoginExpired(string reason)
|
||
{
|
||
_logService?.Warn($"登录失效: {reason}");
|
||
StopTokenCheckTimer();
|
||
LoginExpired?.Invoke(this, new LoginExpiredEventArgs(reason));
|
||
}
|
||
|
||
#region IDisposable
|
||
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
StopTokenCheckTimer();
|
||
}
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录失效事件参数
|
||
/// </summary>
|
||
public class LoginExpiredEventArgs : EventArgs
|
||
{
|
||
public string Reason { get; }
|
||
|
||
public LoginExpiredEventArgs(string reason)
|
||
{
|
||
Reason = reason;
|
||
}
|
||
}
|
||
}
|