2026-03-14 06:13:06 +08:00
|
|
|
|
using System.Net.Http.Json;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Polly;
|
|
|
|
|
|
using Polly.Retry;
|
|
|
|
|
|
using YD_Event.Core.Options;
|
|
|
|
|
|
using Furion.DependencyInjection;
|
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Furion.FriendlyException;
|
|
|
|
|
|
|
2026-03-14 09:06:43 +08:00
|
|
|
|
namespace YD_Event.Core.Infrastructure.WeChat;
|
2026-03-14 06:13:06 +08:00
|
|
|
|
|
2026-03-14 09:06:43 +08:00
|
|
|
|
public class WeChatService : ITransient
|
2026-03-14 06:13:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
2026-03-14 09:06:43 +08:00
|
|
|
|
private readonly ILogger<WeChatService> _logger;
|
2026-03-14 06:13:06 +08:00
|
|
|
|
private readonly WeChatOptions _options;
|
|
|
|
|
|
|
2026-03-14 09:06:43 +08:00
|
|
|
|
public WeChatService(IHttpClientFactory httpClientFactory, ILogger<WeChatService> logger, IOptions<WeChatOptions> options)
|
2026-03-14 06:13:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
_httpClientFactory = httpClientFactory;
|
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
_options = options.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// code换取session_key
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="code"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public async Task<WeChatCode2SessionResponse> Code2Session(string code)
|
|
|
|
|
|
{
|
|
|
|
|
|
var url = $"https://api.weixin.qq.com/sns/jscode2session?appid={_options.AppId}&secret={_options.Secret}&js_code={code}&grant_type=authorization_code";
|
|
|
|
|
|
|
|
|
|
|
|
// 定义重试策略:3秒超时,重试2次
|
|
|
|
|
|
var retryPolicy = Policy
|
|
|
|
|
|
.Handle<HttpRequestException>()
|
|
|
|
|
|
.Or<TaskCanceledException>()
|
|
|
|
|
|
.WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(1), (exception, timeSpan, retryCount, context) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogWarning($"WeChat API Request Failed. Retry {retryCount}. Error: {exception.Message}");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var timeoutPolicy = Policy.TimeoutAsync(3); // 3秒超时
|
|
|
|
|
|
|
|
|
|
|
|
var policyWrap = Policy.WrapAsync(retryPolicy, timeoutPolicy);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return await policyWrap.ExecuteAsync(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var client = _httpClientFactory.CreateClient();
|
|
|
|
|
|
var response = await client.GetAsync(url);
|
|
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
|
|
var content = await response.Content.ReadAsStringAsync();
|
|
|
|
|
|
var result = JsonConvert.DeserializeObject<WeChatCode2SessionResponse>(content);
|
|
|
|
|
|
|
|
|
|
|
|
if (result == null || result.ErrCode != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"WeChat Code2Session Error: {content}");
|
|
|
|
|
|
throw Oops.Bah($"微信接口调用失败: {result?.ErrMsg ?? "未知错误"}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "WeChat Code2Session Exception");
|
|
|
|
|
|
throw Oops.Bah("微信服务暂不可用,请稍后重试");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class WeChatCode2SessionResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("openid")]
|
|
|
|
|
|
public string OpenId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("session_key")]
|
|
|
|
|
|
public string SessionKey { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("unionid")]
|
|
|
|
|
|
public string UnionId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("errcode")]
|
|
|
|
|
|
public int ErrCode { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("errmsg")]
|
|
|
|
|
|
public string ErrMsg { get; set; }
|
|
|
|
|
|
}
|