using System.Text.Json; namespace Kbx.Shared.Providers; public sealed class OpenDartAdapter(HttpClient http, IKbxProviderSecretStore secrets) : IKbxExternalProviderAdapter { private static readonly Uri BaseUri = new("https://opendart.fss.or.kr/api/"); public string ProviderId => "provider.opendart"; public Task> GetDisclosuresAsync(IReadOnlyDictionary query, CancellationToken ct) => GetJsonAsync("list.json", query, ct); public Task> GetCompanyAsync(string corpCode, CancellationToken ct) => GetJsonAsync("company.json", new Dictionary { ["corp_code"] = corpCode }, ct); public async Task> DownloadCorpCodeAsync(CancellationToken ct) { var key = await secrets.GetRequiredAsync("ExternalProviders:OpenDart:ApiKey", ct); var uri = new Uri(BaseUri, $"corpCode.xml?crtfc_key={Uri.EscapeDataString(key)}"); using var response = await http.GetAsync(uri, ct); var bytes = await response.Content.ReadAsByteArrayAsync(ct); if (response.IsSuccessStatusCode) return new(KbxProviderResultKind.Success, bytes, HttpStatus:(int)response.StatusCode); return new((int)response.StatusCode >= 500 ? KbxProviderResultKind.TransientFailure : KbxProviderResultKind.PermanentFailure, Code:$"HTTP_{(int)response.StatusCode}", Message:"OPENDART 고유번호 파일 호출 실패", HttpStatus:(int)response.StatusCode); } private async Task> GetJsonAsync(string path, IReadOnlyDictionary query, CancellationToken ct) { var key = await secrets.GetRequiredAsync("ExternalProviders:OpenDart:ApiKey", ct); var pairs = new List { $"crtfc_key={Uri.EscapeDataString(key)}" }; pairs.AddRange(query.Where(x=>x.Value is not null).Select(x=>$"{Uri.EscapeDataString(x.Key)}={Uri.EscapeDataString(x.Value!)}")); var uri = new Uri(BaseUri, path + "?" + string.Join("&", pairs)); using var response = await http.GetAsync(uri, ct); var text = await response.Content.ReadAsStringAsync(ct); if (!response.IsSuccessStatusCode) return new((int)response.StatusCode >= 500 || (int)response.StatusCode == 429 ? KbxProviderResultKind.TransientFailure : KbxProviderResultKind.PermanentFailure, Code:$"HTTP_{(int)response.StatusCode}", Message:"OPENDART HTTP 호출 실패", HttpStatus:(int)response.StatusCode); JsonDocument doc; try { doc = JsonDocument.Parse(text); } catch { return new(KbxProviderResultKind.PermanentFailure, Code:"DART_INVALID_JSON", Message:"OPENDART 응답 JSON을 해석할 수 없습니다."); } var root = doc.RootElement; var status = root.TryGetProperty("status", out var s) ? s.GetString() : null; var message = root.TryGetProperty("message", out var m) ? m.GetString() : null; return status switch { "000" or null => new(KbxProviderResultKind.Success, doc), "013" => new(KbxProviderResultKind.NoData, doc, status, message), "020" or "800" or "900" => new(KbxProviderResultKind.TransientFailure, doc, status, message), _ => new(KbxProviderResultKind.PermanentFailure, doc, status, message) }; } }