MdpApiPushExecutor.cs 4.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. using System.Net.Http.Headers;
  2. using System.Text;
  3. using System.Text.Json;
  4. using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
  5. using Microsoft.Extensions.Logging;
  6. namespace Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
  7. /// <summary>
  8. /// 出站回写(HTTP):按 payload 调第三方写接口。批量扫描由 <see cref="MdpTargetPushDispatcher"/> 负责。
  9. /// </summary>
  10. public sealed class MdpApiPushExecutor : IMdpTargetPushExecutor, ITransient
  11. {
  12. private readonly IHttpClientFactory _httpClientFactory;
  13. private readonly MdpOutboundGate _gate;
  14. private readonly ILogger _logger;
  15. public MdpApiPushExecutor(IHttpClientFactory httpClientFactory, MdpOutboundGate gate, ILoggerFactory loggerFactory)
  16. {
  17. _httpClientFactory = httpClientFactory;
  18. _gate = gate;
  19. _logger = loggerFactory.CreateLogger(nameof(MdpApiPushExecutor));
  20. }
  21. public string SupportedType => "API";
  22. public async Task<MdpPushResult> PushAsync(MdpSource source, MdpOutbox item, CancellationToken ct = default)
  23. {
  24. if (_gate.TryHold() is { } held) return held;
  25. if (source == null || string.IsNullOrWhiteSpace(source.ApiBaseUrl))
  26. {
  27. _logger.LogWarning(
  28. "[MdpApiPushExecutor] 未配置 API outbox id={Id} idem={Idem} action={Action} source={Source} type={Type}",
  29. item.Id, item.IdemKey, item.ActionCode,
  30. source?.SourceCode ?? item.TargetSourceCode, source?.SourceType);
  31. return MdpPushResult.Fail($"目标源 {item.TargetSourceCode} 未配置 API");
  32. }
  33. using var payloadDoc = JsonDocument.Parse(string.IsNullOrWhiteSpace(item.PayloadJson) ? "{}" : item.PayloadJson!);
  34. var root = payloadDoc.RootElement;
  35. var path = root.TryGetProperty("path", out var p) ? p.GetString() : $"/outbox/{item.ActionCode}";
  36. var method = root.TryGetProperty("method", out var m) ? (m.GetString() ?? "POST") : "POST";
  37. var bodyEl = root.TryGetProperty("body", out var b) ? b : root;
  38. var client = _httpClientFactory.CreateClient("MdpApiPush");
  39. client.Timeout = TimeSpan.FromSeconds(60);
  40. var url = source.ApiBaseUrl!.TrimEnd('/') + (path!.StartsWith('/') ? path : "/" + path);
  41. using var request = new HttpRequestMessage(new HttpMethod(method), url);
  42. ApplyAuth(request, source);
  43. request.Content = new StringContent(bodyEl.GetRawText(), Encoding.UTF8, "application/json");
  44. using var response = await client.SendAsync(request, ct);
  45. var respBody = await response.Content.ReadAsStringAsync(ct);
  46. if (response.IsSuccessStatusCode)
  47. return MdpPushResult.Ok(1, Truncate(respBody, 4000));
  48. return MdpPushResult.Fail($"HTTP {(int)response.StatusCode}", Truncate(respBody, 4000));
  49. }
  50. private static void ApplyAuth(HttpRequestMessage request, MdpSource source)
  51. {
  52. var authType = (source.ApiAuthType ?? "NONE").Trim().ToUpperInvariant();
  53. if (authType is "NONE" or "") return;
  54. Dictionary<string, string>? cfg = null;
  55. if (!string.IsNullOrWhiteSpace(source.ApiAuthConfig))
  56. {
  57. try { cfg = JsonSerializer.Deserialize<Dictionary<string, string>>(source.ApiAuthConfig!); }
  58. catch { /* ignore */ }
  59. }
  60. cfg ??= new Dictionary<string, string>();
  61. if ((authType is "BEARER" or "TOKEN") && (cfg.TryGetValue("token", out var token) || cfg.TryGetValue("access_token", out token)))
  62. request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
  63. else if (authType == "BASIC" && cfg.TryGetValue("username", out var user) && cfg.TryGetValue("password", out var pwd))
  64. request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{pwd}")));
  65. else if (authType == "APIKEY")
  66. {
  67. var header = cfg.GetValueOrDefault("header") ?? "X-API-Key";
  68. if (cfg.TryGetValue("apiKey", out var key) || cfg.TryGetValue("key", out key))
  69. request.Headers.TryAddWithoutValidation(header, key);
  70. }
  71. }
  72. private static string? Truncate(string? s, int max) =>
  73. string.IsNullOrEmpty(s) ? s : (s.Length <= max ? s : s[..max]);
  74. }