S8DataSourceRowLoader.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using System.Data;
  2. using System.Text.Json;
  3. using Admin.NET.Plugin.AiDOP.Entity.S8;
  4. using Microsoft.Extensions.Logging;
  5. using SqlSugar;
  6. namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules;
  7. /// <summary>
  8. /// S8 规则取数:SQL 走 SqlSugarScope;API 走 HTTP GET(endpoint=完整 URL)。
  9. /// </summary>
  10. public class S8DataSourceRowLoader : ITransient
  11. {
  12. public const string SqlType = "SQL";
  13. public const string ApiType = "API";
  14. private readonly S8SqlSugarScopeFactory _scopeFactory;
  15. private readonly ILogger<S8DataSourceRowLoader> _logger;
  16. public S8DataSourceRowLoader(S8SqlSugarScopeFactory scopeFactory, ILogger<S8DataSourceRowLoader> logger)
  17. {
  18. _scopeFactory = scopeFactory;
  19. _logger = logger;
  20. }
  21. public async Task<DataTable> LoadAsync(
  22. AdoS8DataSource dataSource,
  23. string expression,
  24. SqlSugar.DbType dbType,
  25. int timeoutSeconds,
  26. CancellationToken cancellationToken = default)
  27. {
  28. var type = dataSource.Type?.Trim() ?? SqlType;
  29. if (string.Equals(type, SqlType, StringComparison.OrdinalIgnoreCase))
  30. {
  31. using var db = _scopeFactory.CreateScope(dataSource.Endpoint!, dbType, timeoutSeconds);
  32. return await db.Ado.GetDataTableAsync(expression);
  33. }
  34. if (string.Equals(type, ApiType, StringComparison.OrdinalIgnoreCase))
  35. return await LoadFromApiAsync(dataSource, expression, timeoutSeconds, cancellationToken);
  36. throw new InvalidOperationException($"不支持的数据源类型:{type}");
  37. }
  38. public static bool IsSupportedType(string? type) =>
  39. string.Equals(type?.Trim(), SqlType, StringComparison.OrdinalIgnoreCase)
  40. || string.Equals(type?.Trim(), ApiType, StringComparison.OrdinalIgnoreCase);
  41. private async Task<DataTable> LoadFromApiAsync(
  42. AdoS8DataSource dataSource,
  43. string expression,
  44. int timeoutSeconds,
  45. CancellationToken cancellationToken)
  46. {
  47. if (string.IsNullOrWhiteSpace(dataSource.Endpoint))
  48. throw new InvalidOperationException("API 数据源 endpoint 为空");
  49. var url = dataSource.Endpoint.Trim();
  50. if (!string.IsNullOrWhiteSpace(expression)
  51. && !expression.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
  52. {
  53. if (expression.StartsWith('?'))
  54. url = url.Contains('?') ? url + "&" + expression.TrimStart('?') : url + expression;
  55. else if (expression.StartsWith('/'))
  56. url = url.TrimEnd('/') + expression;
  57. }
  58. _logger.LogDebug("S8 API 取数 url={Url} authType={AuthType}", url, dataSource.AuthType);
  59. using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds)) };
  60. using var resp = await http.GetAsync(url, cancellationToken);
  61. resp.EnsureSuccessStatusCode();
  62. var json = await resp.Content.ReadAsStringAsync(cancellationToken);
  63. return JsonToDataTable(json);
  64. }
  65. /// <summary>支持 {data:{list:[...]}} / {list:[...]} / [...] 。</summary>
  66. internal static DataTable JsonToDataTable(string json)
  67. {
  68. using var doc = JsonDocument.Parse(json);
  69. JsonElement arr;
  70. var root = doc.RootElement;
  71. if (root.ValueKind == JsonValueKind.Array)
  72. arr = root;
  73. else if (root.TryGetProperty("data", out var data) && data.TryGetProperty("list", out var list) && list.ValueKind == JsonValueKind.Array)
  74. arr = list;
  75. else if (root.TryGetProperty("list", out var list2) && list2.ValueKind == JsonValueKind.Array)
  76. arr = list2;
  77. else
  78. throw new InvalidOperationException("API 响应不是可识别的行数组(期望 data.list / list / [])");
  79. var table = new DataTable();
  80. foreach (var item in arr.EnumerateArray())
  81. {
  82. if (item.ValueKind != JsonValueKind.Object) continue;
  83. foreach (var prop in item.EnumerateObject())
  84. {
  85. if (!table.Columns.Contains(prop.Name))
  86. table.Columns.Add(prop.Name, typeof(string));
  87. }
  88. }
  89. foreach (var item in arr.EnumerateArray())
  90. {
  91. if (item.ValueKind != JsonValueKind.Object) continue;
  92. var row = table.NewRow();
  93. foreach (DataColumn col in table.Columns)
  94. {
  95. if (item.TryGetProperty(col.ColumnName, out var v))
  96. row[col.ColumnName] = v.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined
  97. ? DBNull.Value
  98. : v.ToString();
  99. }
  100. table.Rows.Add(row);
  101. }
  102. return table;
  103. }
  104. }