125 lines
4.1 KiB
C#
125 lines
4.1 KiB
C#
using System.Text;
|
||
using System.Text.Json;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using HtmlToPdfService.Core.Models;
|
||
using HtmlToPdfService.Core.Options;
|
||
|
||
namespace HtmlToPdfService.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 回调服务实现
|
||
/// </summary>
|
||
public class CallbackService : ICallbackService
|
||
{
|
||
private readonly CallbackOptions _options;
|
||
private readonly ILogger<CallbackService> _logger;
|
||
private readonly IHttpClientFactory _httpClientFactory;
|
||
|
||
public CallbackService(
|
||
IOptions<PdfServiceOptions> options,
|
||
ILogger<CallbackService> logger,
|
||
IHttpClientFactory httpClientFactory)
|
||
{
|
||
_options = options.Value.Callback;
|
||
_logger = logger;
|
||
_httpClientFactory = httpClientFactory;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送回调(Fire-and-Forget 模式)
|
||
/// </summary>
|
||
public async Task SendCallbackAsync(
|
||
string callbackUrl,
|
||
CallbackPayload payload,
|
||
Dictionary<string, string>? customHeaders = null,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrEmpty(callbackUrl))
|
||
{
|
||
_logger.LogDebug("回调 URL 为空,跳过回调");
|
||
return;
|
||
}
|
||
|
||
if (!_options.Enabled)
|
||
{
|
||
_logger.LogDebug("回调功能已禁用,跳过回调");
|
||
return;
|
||
}
|
||
|
||
// Fire-and-Forget:不等待回调完成
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
await SendCallbackInternalAsync(callbackUrl, payload, customHeaders, cancellationToken);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 只记录日志,不抛出异常
|
||
_logger.LogError(ex, "回调发送失败: {Url}, RequestId: {RequestId}",
|
||
callbackUrl, payload.RequestId);
|
||
}
|
||
}, cancellationToken);
|
||
|
||
_logger.LogInformation("回调已触发(异步): {Url}, RequestId: {RequestId}",
|
||
callbackUrl, payload.RequestId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实际发送回调
|
||
/// </summary>
|
||
private async Task SendCallbackInternalAsync(
|
||
string callbackUrl,
|
||
CallbackPayload payload,
|
||
Dictionary<string, string>? customHeaders,
|
||
CancellationToken cancellationToken)
|
||
{
|
||
var httpClient = _httpClientFactory.CreateClient("CallbackClient");
|
||
httpClient.Timeout = TimeSpan.FromMilliseconds(_options.Timeout);
|
||
|
||
var request = new HttpRequestMessage(HttpMethod.Post, callbackUrl);
|
||
|
||
// 添加全局配置的 Headers
|
||
foreach (var header in _options.CustomHeaders)
|
||
{
|
||
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||
}
|
||
|
||
// 添加请求级的自定义 Headers(优先级更高)
|
||
if (customHeaders != null)
|
||
{
|
||
foreach (var header in customHeaders)
|
||
{
|
||
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||
}
|
||
}
|
||
|
||
// 序列化 Payload
|
||
var jsonContent = JsonSerializer.Serialize(payload, new JsonSerializerOptions
|
||
{
|
||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||
WriteIndented = false
|
||
});
|
||
|
||
request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||
|
||
_logger.LogDebug("发送回调请求: {Url}, Payload: {Payload}", callbackUrl, jsonContent);
|
||
|
||
var response = await httpClient.SendAsync(request, cancellationToken);
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
_logger.LogInformation("回调发送成功: {Url}, StatusCode: {StatusCode}, RequestId: {RequestId}",
|
||
callbackUrl, (int)response.StatusCode, payload.RequestId);
|
||
}
|
||
else
|
||
{
|
||
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||
_logger.LogWarning("回调返回非成功状态码: {Url}, StatusCode: {StatusCode}, Response: {Response}, RequestId: {RequestId}",
|
||
callbackUrl, (int)response.StatusCode, responseBody, payload.RequestId);
|
||
}
|
||
}
|
||
}
|
||
|