250 lines
7.4 KiB
C#
250 lines
7.4 KiB
C#
using LiveForum.IService.Others;
|
||
using LiveForum.Model.Dto.Others;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using SKIT.FlurlHttpClient.Wechat.Api;
|
||
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace LiveForum.Service.Others
|
||
{
|
||
/// <summary>
|
||
/// 微信 API 客户端管理器实现(支持配置热更新)
|
||
/// </summary>
|
||
public class WechatApiClientManager : IWechatApiClientManager
|
||
{
|
||
private readonly IOptionsMonitor<WechatConfig> _configMonitor;
|
||
private readonly ILogger<WechatApiClientManager> _logger;
|
||
private readonly object _lockObject = new object();
|
||
|
||
private WechatApiClient _client;
|
||
private string _currentAppId;
|
||
|
||
public WechatApiClientManager(
|
||
IOptionsMonitor<WechatConfig> configMonitor,
|
||
ILogger<WechatApiClientManager> logger)
|
||
{
|
||
_configMonitor = configMonitor;
|
||
_logger = logger;
|
||
|
||
// 监听配置变更
|
||
_configMonitor.OnChange(OnConfigChanged);
|
||
|
||
// 初始化客户端
|
||
InitializeClient();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化客户端
|
||
/// </summary>
|
||
private void InitializeClient()
|
||
{
|
||
try
|
||
{
|
||
var config = _configMonitor.CurrentValue;
|
||
|
||
// 验证配置
|
||
if (!ValidateConfig(config))
|
||
{
|
||
_logger.LogWarning("微信配置验证失败,无法创建客户端");
|
||
_client = null;
|
||
_currentAppId = null;
|
||
return;
|
||
}
|
||
|
||
var options = new WechatApiClientOptions()
|
||
{
|
||
AppId = config.AppId,
|
||
AppSecret = config.AppSecret
|
||
};
|
||
|
||
lock (_lockObject)
|
||
{
|
||
_client = new WechatApiClient(options);
|
||
_currentAppId = config.AppId;
|
||
}
|
||
|
||
var appIdDisplay = GetMaskedAppId(config.AppId);
|
||
_logger.LogInformation("微信 API 客户端已初始化 - AppId: {AppId}", appIdDisplay);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "初始化微信 API 客户端失败");
|
||
_client = null;
|
||
_currentAppId = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证配置
|
||
/// </summary>
|
||
private bool ValidateConfig(WechatConfig config)
|
||
{
|
||
if (config == null)
|
||
{
|
||
_logger.LogError("微信配置为空");
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(config.AppId))
|
||
{
|
||
_logger.LogError("微信 AppId 为空");
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(config.AppSecret))
|
||
{
|
||
_logger.LogError("微信 AppSecret 为空");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取微信 API 客户端实例
|
||
/// </summary>
|
||
public WechatApiClient GetClient()
|
||
{
|
||
lock (_lockObject)
|
||
{
|
||
if (_client == null)
|
||
{
|
||
_logger.LogWarning("微信 API 客户端不可用,尝试重新初始化");
|
||
InitializeClient();
|
||
}
|
||
return _client;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重新加载客户端(配置变更时调用)
|
||
/// </summary>
|
||
public bool Reload()
|
||
{
|
||
try
|
||
{
|
||
_logger.LogInformation("开始重新加载微信 API 客户端...");
|
||
|
||
var newConfig = _configMonitor.CurrentValue;
|
||
|
||
// 验证新配置
|
||
if (!ValidateConfig(newConfig))
|
||
{
|
||
_logger.LogWarning("新配置验证失败,无法重新加载客户端");
|
||
return false;
|
||
}
|
||
|
||
WechatApiClient oldClient = null;
|
||
|
||
lock (_lockObject)
|
||
{
|
||
// 保存旧客户端引用(用于延迟 Dispose)
|
||
oldClient = _client;
|
||
|
||
// 创建新客户端
|
||
var options = new WechatApiClientOptions()
|
||
{
|
||
AppId = newConfig.AppId,
|
||
AppSecret = newConfig.AppSecret
|
||
};
|
||
|
||
_client = new WechatApiClient(options);
|
||
_currentAppId = newConfig.AppId;
|
||
|
||
var appIdDisplay = GetMaskedAppId(newConfig.AppId);
|
||
_logger.LogInformation("微信 API 客户端已重新加载 - 新 AppId: {AppId}", appIdDisplay);
|
||
}
|
||
|
||
// 延迟释放旧客户端(确保正在进行的请求完成)
|
||
//ScheduleDispose(oldClient);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "重新加载微信 API 客户端失败");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查客户端是否可用
|
||
/// </summary>
|
||
public bool IsAvailable()
|
||
{
|
||
lock (_lockObject)
|
||
{
|
||
return _client != null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前配置的 AppId
|
||
/// </summary>
|
||
public string GetCurrentAppId()
|
||
{
|
||
lock (_lockObject)
|
||
{
|
||
return _currentAppId;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置变更回调
|
||
/// </summary>
|
||
private void OnConfigChanged(WechatConfig newConfig)
|
||
{
|
||
var newAppIdDisplay = GetMaskedAppId(newConfig?.AppId);
|
||
_logger.LogInformation("检测到微信配置变更 - 新 AppId: {AppId}", newAppIdDisplay);
|
||
|
||
// 自动重新加载(可选,也可以手动调用)
|
||
// Reload();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 延迟释放旧客户端
|
||
/// </summary>
|
||
private void ScheduleDispose(WechatApiClient oldClient)
|
||
{
|
||
if (oldClient == null) return;
|
||
|
||
// 延迟30秒后释放,确保所有正在进行的请求完成
|
||
Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
await Task.Delay(TimeSpan.FromSeconds(30));
|
||
|
||
// 检查是否实现了 IDisposable
|
||
if (oldClient is IDisposable disposable)
|
||
{
|
||
disposable.Dispose();
|
||
_logger.LogInformation("旧的微信 API 客户端已释放");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "释放旧的微信 API 客户端时发生错误");
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取脱敏的 AppId(用于日志)
|
||
/// </summary>
|
||
private string GetMaskedAppId(string appId)
|
||
{
|
||
if (string.IsNullOrEmpty(appId))
|
||
return "未配置";
|
||
|
||
if (appId.Length <= 8)
|
||
return appId;
|
||
|
||
return $"{appId.Substring(0, 8)}***";
|
||
}
|
||
}
|
||
}
|
||
|