543b327d27
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 9s
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 5s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Deploy to Production / Build & Deploy to Production (push) Failing after 2m32s
- fix CS0542: rename Users/Assets private members to _users/_assets - fix CS0246: MudDialogInstance -> IMudDialogInstance - fix AppTheme: PaletteLight/PaletteDark, string[] FontFamily, string typography values - fix DataCollectionMonitoring: @(ticker.DataPointCount)개 Korean char parsing - fix SchedulerService: add missing Hangfire namespaces, fix GetJobStatus return type - fix Program.cs: move PostgreSQL setup above Hangfire registration - fix ConfirmDialog: BackdropClick, Canceled spelling for MudBlazor v8 - fix static asset conflict: remove wwwroot/_framework from git tracking - chore: add wwwroot/_framework/ to .gitignore - ci: change DEPLOY_HOST from IP to quant.taxbaik.com domain
292 lines
11 KiB
Plaintext
292 lines
11 KiB
Plaintext
@page "/monitoring"
|
|
@attribute [Authorize]
|
|
@inject HttpClient Http
|
|
|
|
<PageTitle>QuantEngine - 데이터 수집 모니터링</PageTitle>
|
|
|
|
<!-- Page Header -->
|
|
<div class="mb-6">
|
|
<MudText Typo="Typo.h4" Class="mb-2">데이터 수집 모니터링</MudText>
|
|
<MudText Typo="Typo.body1" Class="text-muted">실시간 수집 작업 상태 및 에러 추적</MudText>
|
|
</div>
|
|
|
|
<!-- Collection Status Cards -->
|
|
<MudGrid Spacing="3" Class="mb-6">
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="0" Style="border: 1px solid var(--mud-palette-divider);">
|
|
<MudText Typo="Typo.caption" Class="text-muted mb-2">진행 중인 작업</MudText>
|
|
<MudText Typo="Typo.h5">@RunningCount</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="0" Style="border: 1px solid var(--mud-palette-divider);">
|
|
<MudText Typo="Typo.caption" Class="text-muted mb-2">완료</MudText>
|
|
<MudText Typo="Typo.h5" Class="text-success">@CompletedCount</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="0" Style="border: 1px solid var(--mud-palette-divider);">
|
|
<MudText Typo="Typo.caption" Class="text-muted mb-2">실패</MudText>
|
|
<MudText Typo="Typo.h5" Class="text-error">@FailedCount</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
<MudItem xs="12" sm="6" md="3">
|
|
<MudPaper Class="pa-4" Elevation="0" Style="border: 1px solid var(--mud-palette-divider);">
|
|
<MudText Typo="Typo.caption" Class="text-muted mb-2">대기 중</MudText>
|
|
<MudText Typo="Typo.h5" Class="text-warning">@PendingCount</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<!-- Tabs -->
|
|
<MudTabs Outlined="true" Class="mb-6">
|
|
<!-- Recent Runs -->
|
|
<MudTabPanel Text="최근 실행">
|
|
<div class="py-4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
@if (RecentRuns.Count == 0)
|
|
{
|
|
<MudAlert Severity="Severity.Info">최근 실행 기록이 없습니다.</MudAlert>
|
|
}
|
|
else
|
|
{
|
|
<MudTable Items="@RecentRuns" Dense="true" Hover="true" Striped="true">
|
|
<HeaderContent>
|
|
<MudTh>실행 ID</MudTh>
|
|
<MudTh>시작 시간</MudTh>
|
|
<MudTh>종료 시간</MudTh>
|
|
<MudTh>상태</MudTh>
|
|
<MudTh>수집된 항목</MudTh>
|
|
<MudTh>작업</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd DataLabel="Run ID">
|
|
<MudText Typo="Typo.body2" Class="font-monospace">@context.RunId</MudText>
|
|
</MudTd>
|
|
<MudTd DataLabel="Start">
|
|
<MudText Typo="Typo.body2">@context.StartTime.ToString("yyyy-MM-dd HH:mm:ss")</MudText>
|
|
</MudTd>
|
|
<MudTd DataLabel="End">
|
|
<MudText Typo="Typo.body2">@(context.EndTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")</MudText>
|
|
</MudTd>
|
|
<MudTd DataLabel="Status">
|
|
<MudChip T="string" Label="true" Size="Size.Small"
|
|
Color="@GetStatusColor(context.Status)"
|
|
Variant="Variant.Filled">
|
|
@context.Status
|
|
</MudChip>
|
|
</MudTd>
|
|
<MudTd DataLabel="Items">
|
|
<MudText Typo="Typo.body2">@context.ItemCount</MudText>
|
|
</MudTd>
|
|
<MudTd DataLabel="Actions">
|
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary"
|
|
OnClick="@(() => ViewRunDetails(context))">
|
|
상세
|
|
</MudButton>
|
|
</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
}
|
|
</MudPaper>
|
|
</div>
|
|
</MudTabPanel>
|
|
|
|
<!-- Error Logs -->
|
|
<MudTabPanel Text="에러 로그">
|
|
<div class="py-4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
@if (Errors.Count == 0)
|
|
{
|
|
<MudAlert Severity="Severity.Success">에러가 없습니다.</MudAlert>
|
|
}
|
|
else
|
|
{
|
|
<MudStack Spacing="2">
|
|
@foreach (var error in Errors)
|
|
{
|
|
<div class="pa-3" style="border-left: 3px solid #f44336; background-color: var(--mud-palette-surface);">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<MudText Typo="Typo.body2" Class="font-weight-500">@error.Message</MudText>
|
|
<MudText Typo="Typo.caption" Class="text-muted">@error.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")</MudText>
|
|
</div>
|
|
<MudText Typo="Typo.caption" Class="text-muted">Run ID: @error.RunId</MudText>
|
|
<MudText Typo="Typo.caption" Class="text-muted mt-1">@error.StackTrace</MudText>
|
|
</div>
|
|
}
|
|
</MudStack>
|
|
}
|
|
</MudPaper>
|
|
</div>
|
|
</MudTabPanel>
|
|
|
|
<!-- Collection Status -->
|
|
<MudTabPanel Text="수집 상태">
|
|
<div class="py-4">
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudStack Spacing="3">
|
|
@foreach (var ticker in CollectionStatus)
|
|
{
|
|
<div class="pa-3" style="border-bottom: 1px solid var(--mud-palette-divider);">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<MudText Typo="Typo.body2" Class="font-weight-500">@ticker.Ticker</MudText>
|
|
<MudChip T="string" Label="true" Size="Size.Small"
|
|
Color="@(ticker.IsSuccessful ? Color.Success : Color.Warning)"
|
|
Variant="Variant.Filled">
|
|
@(ticker.IsSuccessful ? "성공" : "실패")
|
|
</MudChip>
|
|
</div>
|
|
<MudText Typo="Typo.caption" Class="text-muted">
|
|
마지막 수집: @ticker.LastCollectionTime.ToString("yyyy-MM-dd HH:mm:ss")
|
|
</MudText>
|
|
<MudText Typo="Typo.caption" Class="text-muted">
|
|
데이터 포인트: @(ticker.DataPointCount)개
|
|
</MudText>
|
|
</div>
|
|
}
|
|
</MudStack>
|
|
</MudPaper>
|
|
</div>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
|
|
@code {
|
|
// Status counts
|
|
private int RunningCount = 2;
|
|
private int CompletedCount = 156;
|
|
private int FailedCount = 8;
|
|
private int PendingCount = 5;
|
|
|
|
// Recent runs
|
|
private List<RunModel> RecentRuns = new();
|
|
|
|
// Errors
|
|
private List<ErrorModel> Errors = new();
|
|
|
|
// Collection status
|
|
private List<CollectionStatusModel> CollectionStatus = new();
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadData();
|
|
}
|
|
|
|
private async Task LoadData()
|
|
{
|
|
// Load recent runs
|
|
RecentRuns = new List<RunModel>
|
|
{
|
|
new RunModel
|
|
{
|
|
RunId = "RUN-2026-07-05-001",
|
|
StartTime = DateTime.Now.AddMinutes(-45),
|
|
EndTime = DateTime.Now.AddMinutes(-40),
|
|
Status = "완료",
|
|
ItemCount = 142
|
|
},
|
|
new RunModel
|
|
{
|
|
RunId = "RUN-2026-07-05-002",
|
|
StartTime = DateTime.Now.AddMinutes(-30),
|
|
EndTime = null,
|
|
Status = "진행 중",
|
|
ItemCount = 87
|
|
},
|
|
new RunModel
|
|
{
|
|
RunId = "RUN-2026-07-04-012",
|
|
StartTime = DateTime.Now.AddHours(-8).AddMinutes(-15),
|
|
EndTime = DateTime.Now.AddHours(-8).AddMinutes(-5),
|
|
Status = "완료",
|
|
ItemCount = 189
|
|
}
|
|
};
|
|
|
|
// Load errors
|
|
Errors = new List<ErrorModel>
|
|
{
|
|
new ErrorModel
|
|
{
|
|
RunId = "RUN-2026-07-04-011",
|
|
Message = "API Rate Limit Exceeded",
|
|
StackTrace = "Exception at CollectionService.FetchData()",
|
|
Timestamp = DateTime.Now.AddHours(-2)
|
|
},
|
|
new ErrorModel
|
|
{
|
|
RunId = "RUN-2026-07-03-015",
|
|
Message = "Connection Timeout",
|
|
StackTrace = "Exception at HttpClient.GetAsync()",
|
|
Timestamp = DateTime.Now.AddHours(-5)
|
|
}
|
|
};
|
|
|
|
// Load collection status
|
|
CollectionStatus = new List<CollectionStatusModel>
|
|
{
|
|
new CollectionStatusModel
|
|
{
|
|
Ticker = "005930",
|
|
IsSuccessful = true,
|
|
LastCollectionTime = DateTime.Now.AddMinutes(-2),
|
|
DataPointCount = 1450
|
|
},
|
|
new CollectionStatusModel
|
|
{
|
|
Ticker = "000660",
|
|
IsSuccessful = true,
|
|
LastCollectionTime = DateTime.Now.AddMinutes(-5),
|
|
DataPointCount = 1203
|
|
},
|
|
new CollectionStatusModel
|
|
{
|
|
Ticker = "051910",
|
|
IsSuccessful = false,
|
|
LastCollectionTime = DateTime.Now.AddHours(-1),
|
|
DataPointCount = 945
|
|
}
|
|
};
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private Color GetStatusColor(string status) => status switch
|
|
{
|
|
"완료" => Color.Success,
|
|
"진행 중" => Color.Info,
|
|
"실패" => Color.Error,
|
|
_ => Color.Warning
|
|
};
|
|
|
|
private async Task ViewRunDetails(RunModel run)
|
|
{
|
|
// View details dialog
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private class RunModel
|
|
{
|
|
public string RunId { get; set; }
|
|
public DateTime StartTime { get; set; }
|
|
public DateTime? EndTime { get; set; }
|
|
public string Status { get; set; }
|
|
public int ItemCount { get; set; }
|
|
}
|
|
|
|
private class ErrorModel
|
|
{
|
|
public string RunId { get; set; }
|
|
public string Message { get; set; }
|
|
public string StackTrace { get; set; }
|
|
public DateTime Timestamp { get; set; }
|
|
}
|
|
|
|
private class CollectionStatusModel
|
|
{
|
|
public string Ticker { get; set; }
|
|
public bool IsSuccessful { get; set; }
|
|
public DateTime LastCollectionTime { get; set; }
|
|
public int DataPointCount { get; set; }
|
|
}
|
|
}
|