using MBN_STOCK_WEBVIEW.Infrastructure; using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW; public sealed partial class MainWindow { private LegacyNamedForeignStockRestoreService? _namedForeignStockRestoreService; private async Task PostNamedForeignStockRestoreAsync( string requestId, IReadOnlyList items, CancellationToken cancellationToken) { var candidates = items .Select(static item => new LegacyNamedForeignStockRestoreRow( item.ItemIndex, item.IsEnabled, item.Selection.GroupCode, item.Selection.Subject, item.Selection.GraphicType, item.Selection.Subtype, item.Page?.ToString() ?? string.Empty, item.Selection.DataCode)) .Where(LegacyNamedForeignStockRestoreService.IsCandidate) .ToArray(); if (candidates.Length == 0) { return; } try { var runtime = _databaseRuntime; if (runtime is null) { PostNamedForeignStockRestoreError( requestId, "DATABASE_UNAVAILABLE", "현재 해외종목 master DB를 사용할 수 없어 저장 행을 재검증하지 못했습니다."); return; } var service = _namedForeignStockRestoreService ??= new LegacyNamedForeignStockRestoreService(runtime.Executor); var result = await service.ResolveAsync(candidates, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); PostMessage("named-foreign-stock-restore-results", new { requestId, result.RetrievedAt, totalRowCount = result.Rows.Count, rows = result.Rows.Select(ToNamedForeignStockRestoreWireRow) }); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { if (Volatile.Read(ref _playoutCommandInFlight) != 0 && !_lifetimeCancellation.IsCancellationRequested) { PostNamedForeignStockRestoreError( requestId, "PLAYOUT_PRIORITY", "송출 명령 우선 처리로 해외종목 재검증을 취소했습니다. 자동 재시도하지 않습니다."); } throw; } catch (DatabaseOperationException) { PostNamedForeignStockRestoreError( requestId, "DATABASE_UNAVAILABLE", "현재 해외종목 master DB에서 저장 행을 재검증하지 못했습니다."); _ = PostDatabaseStatusAsync(_lifetimeCancellation.Token); } catch { PostNamedForeignStockRestoreError( requestId, "RESTORE_FAILED", "저장된 해외종목 행을 현재의 안전한 종목 선택으로 복원하지 못했습니다."); } } private static object ToNamedForeignStockRestoreWireRow( LegacyNamedForeignStockRestoreOutcome outcome) { if (outcome.IsResolved) { var identity = outcome.Identity!; return new { outcome.ItemIndex, status = "resolved", identity.ActionId, identity.InputName, identity.Symbol, identity.NationCode, identity.ForeignDomesticCode, identity.BuilderKey, identity.CutCode, identity.ValueType, identity.PeriodDays }; } return new { outcome.ItemIndex, status = "failed", failure = outcome.Failure switch { LegacyNamedForeignStockRestoreFailure.InvalidRow => "INVALID_ROW", LegacyNamedForeignStockRestoreFailure.IdentityNotFound => "IDENTITY_NOT_FOUND", LegacyNamedForeignStockRestoreFailure.IdentityAmbiguous => "IDENTITY_AMBIGUOUS", LegacyNamedForeignStockRestoreFailure.DatabaseDataInvalid => "DATABASE_DATA_INVALID", _ => "RESTORE_FAILED" } }; } private void PostNamedForeignStockRestoreError( string requestId, string code, string message) { PostMessage("named-foreign-stock-restore-error", new { requestId, code, message, retryable = false }); } }