feat: complete legacy UI and playout parity migration
This commit is contained in:
232
src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.ManualLists.cs
Normal file
232
src/MBN_STOCK_WEBVIEW.LegacyParityApp/MainWindow.ManualLists.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
||||
using MBN_STOCK_WEBVIEW.Infrastructure;
|
||||
using MBN_STOCK_WEBVIEW.LegacyApplication;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.LegacyParityApp;
|
||||
|
||||
public sealed partial class MainWindow
|
||||
{
|
||||
private const string OperatorDataDirectoryEnvironmentVariable =
|
||||
"MBN_STOCK_OPERATOR_DATA_DIRECTORY";
|
||||
|
||||
private S5025TrustedManualFileStore? _manualNetSellStore;
|
||||
private ViTrustedManualFileStore? _manualViStore;
|
||||
private LegacyManualOperatorDataImporter? _manualDataImporter;
|
||||
|
||||
private LegacyManualListsWorkflow CreateManualListsWorkflow(
|
||||
ILegacyStockLookup stockLookup)
|
||||
{
|
||||
ILegacyManualListStore store;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(
|
||||
OperatorDataDirectoryEnvironmentVariable)))
|
||||
{
|
||||
store = new UnavailableManualListStore(
|
||||
"UNSAFE_CONFIGURATION",
|
||||
"외부 경로 설정은 사용하지 않습니다. 앱 전용 로컬 저장소만 사용할 수 있습니다.");
|
||||
return new LegacyManualListsWorkflow(store, stockLookup);
|
||||
}
|
||||
|
||||
var directory = TrustedManualDirectory.PreparePrivateWritableDirectory(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"MBN_STOCK_WEBVIEW",
|
||||
"OperatorData"));
|
||||
var inspection = LegacyManualOperatorDataRuntimeGuard.Inspect(directory);
|
||||
if (!inspection.IsReady)
|
||||
{
|
||||
var interrupted = inspection.State ==
|
||||
LegacyManualOperatorDataRuntimeState.InterruptedImport;
|
||||
store = new UnavailableManualListStore(
|
||||
interrupted ? "IMPORT_INTERRUPTED" : "IMPORTED_STATE_INVALID",
|
||||
interrupted
|
||||
? "이전 원본 데이터 가져오기가 중단되어 수동 파일을 열지 않았습니다."
|
||||
: "수동 데이터 가져오기 표식과 파일 상태가 일치하지 않습니다.");
|
||||
return new LegacyManualListsWorkflow(store, stockLookup);
|
||||
}
|
||||
|
||||
_manualNetSellStore = new S5025TrustedManualFileStore(directory);
|
||||
_manualViStore = new ViTrustedManualFileStore(directory);
|
||||
_manualDataImporter = new LegacyManualOperatorDataImporter(
|
||||
directory,
|
||||
new FixedLegacyManualOperatorDataSource());
|
||||
store = new TrustedManualListStore(
|
||||
_manualNetSellStore,
|
||||
_manualViStore,
|
||||
_manualDataImporter);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_manualDataImporter?.Dispose();
|
||||
_manualViStore?.Dispose();
|
||||
_manualNetSellStore?.Dispose();
|
||||
_manualDataImporter = null;
|
||||
_manualViStore = null;
|
||||
_manualNetSellStore = null;
|
||||
store = new UnavailableManualListStore(
|
||||
"INITIALIZATION_FAILED",
|
||||
"수동 순매도/VI 로컬 저장소를 초기화하지 못했습니다.");
|
||||
}
|
||||
|
||||
return new LegacyManualListsWorkflow(store, stockLookup);
|
||||
}
|
||||
|
||||
private void ShutdownManualListsRuntime()
|
||||
{
|
||||
var importer = Interlocked.Exchange(ref _manualDataImporter, null);
|
||||
var viStore = Interlocked.Exchange(ref _manualViStore, null);
|
||||
var netStore = Interlocked.Exchange(ref _manualNetSellStore, null);
|
||||
if (importer is null && viStore is null && netStore is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await _intentGate.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
importer?.Dispose();
|
||||
viStore?.Dispose();
|
||||
netStore?.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_intentGate.Release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private sealed class TrustedManualListStore : ILegacyManualListStore
|
||||
{
|
||||
private readonly S5025TrustedManualFileStore _netStore;
|
||||
private readonly ViTrustedManualFileStore _viStore;
|
||||
private readonly LegacyManualOperatorDataImporter _importer;
|
||||
|
||||
public TrustedManualListStore(
|
||||
S5025TrustedManualFileStore netStore,
|
||||
ViTrustedManualFileStore viStore,
|
||||
LegacyManualOperatorDataImporter importer)
|
||||
{
|
||||
_netStore = netStore;
|
||||
_viStore = viStore;
|
||||
_importer = importer;
|
||||
}
|
||||
|
||||
public LegacyManualListStoreAvailability Availability { get; } =
|
||||
new(true, "READY", null);
|
||||
|
||||
public Task<IReadOnlyList<S5025TrustedManualRow>> ReadNetSellAsync(
|
||||
S5025ManualAudience audience,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
_netStore.ReadAsync(audience, cancellationToken);
|
||||
|
||||
public Task WriteNetSellAsync(
|
||||
S5025ManualAudience audience,
|
||||
IReadOnlyList<S5025TrustedManualRow> rows,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
_netStore.WriteAsync(audience, rows, cancellationToken);
|
||||
|
||||
public async Task<LegacyManualViStoreSnapshot> ReadViAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var snapshot = await _viStore.ReadSnapshotAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return Map(snapshot);
|
||||
}
|
||||
|
||||
public async Task<LegacyManualViWriteReceipt> WriteViAsync(
|
||||
IReadOnlyList<LegacyManualViStoredItem> items,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = await _viStore.WriteAsync(
|
||||
items.Select(item => new ViTrustedManualItem(item.Code, item.Name)).ToArray(),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
return new LegacyManualViWriteReceipt(Map(result.Snapshot), result.Persisted);
|
||||
}
|
||||
|
||||
public async Task<LegacyManualImportReceipt> ImportAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _importer.ImportAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return new LegacyManualImportReceipt(
|
||||
result.MarkerVersion,
|
||||
result.SourceSha256,
|
||||
result.ImportedAt,
|
||||
result.NetSellRowCount,
|
||||
result.ViItemCount);
|
||||
}
|
||||
catch (LegacyManualOperatorImportException exception)
|
||||
{
|
||||
throw new LegacyManualImportStoreException(
|
||||
ImportFailureCode(exception.Failure),
|
||||
exception.OutcomeUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
private static LegacyManualViStoreSnapshot Map(ViTrustedManualSnapshot snapshot) =>
|
||||
new(
|
||||
Array.AsReadOnly(snapshot.Items.Select(item =>
|
||||
new LegacyManualViStoredItem(item.Code, item.Name)).ToArray()),
|
||||
snapshot.RowVersion);
|
||||
|
||||
private static string ImportFailureCode(LegacyManualOperatorImportFailure failure) =>
|
||||
failure switch
|
||||
{
|
||||
LegacyManualOperatorImportFailure.SourceUnavailable => "SOURCE_UNAVAILABLE",
|
||||
LegacyManualOperatorImportFailure.SourceInvalid => "SOURCE_INVALID",
|
||||
LegacyManualOperatorImportFailure.AlreadyImported => "ALREADY_IMPORTED",
|
||||
LegacyManualOperatorImportFailure.DestinationNotEmpty =>
|
||||
"DESTINATION_NOT_EMPTY",
|
||||
LegacyManualOperatorImportFailure.InterruptedImport => "INTERRUPTED_IMPORT",
|
||||
LegacyManualOperatorImportFailure.ImportFailed => "IMPORT_FAILED",
|
||||
LegacyManualOperatorImportFailure.RollbackFailed => "ROLLBACK_FAILED",
|
||||
_ => "IMPORT_FAILED"
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class UnavailableManualListStore : ILegacyManualListStore
|
||||
{
|
||||
private readonly string _message;
|
||||
|
||||
public UnavailableManualListStore(string code, string message)
|
||||
{
|
||||
_message = message;
|
||||
Availability = new LegacyManualListStoreAvailability(false, code, message);
|
||||
}
|
||||
|
||||
public LegacyManualListStoreAvailability Availability { get; }
|
||||
|
||||
public Task<IReadOnlyList<S5025TrustedManualRow>> ReadNetSellAsync(
|
||||
S5025ManualAudience audience,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromException<IReadOnlyList<S5025TrustedManualRow>>(
|
||||
new InvalidOperationException(_message));
|
||||
|
||||
public Task WriteNetSellAsync(
|
||||
S5025ManualAudience audience,
|
||||
IReadOnlyList<S5025TrustedManualRow> rows,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromException(new InvalidOperationException(_message));
|
||||
|
||||
public Task<LegacyManualViStoreSnapshot> ReadViAsync(
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromException<LegacyManualViStoreSnapshot>(
|
||||
new InvalidOperationException(_message));
|
||||
|
||||
public Task<LegacyManualViWriteReceipt> WriteViAsync(
|
||||
IReadOnlyList<LegacyManualViStoredItem> items,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromException<LegacyManualViWriteReceipt>(
|
||||
new InvalidOperationException(_message));
|
||||
|
||||
public Task<LegacyManualImportReceipt> ImportAsync(
|
||||
CancellationToken cancellationToken = default) =>
|
||||
Task.FromException<LegacyManualImportReceipt>(
|
||||
new LegacyManualImportStoreException("UNAVAILABLE", false));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user