refactor(dotnet): add collection audit trail
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 23s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled

This commit is contained in:
2026-07-13 00:14:05 +09:00
parent ee4ae5583d
commit 6f252162ef
4 changed files with 61 additions and 11 deletions
@@ -0,0 +1,10 @@
namespace QuantEngine.Application.Models;
public sealed record CollectionExecutionAudit(
string RunId,
string State,
DateTimeOffset StartedAt,
DateTimeOffset? FinishedAt,
int SuccessCount,
int ErrorCount,
string? Message);
@@ -1,22 +1,13 @@
using System.Text.Json;
using QuantEngine.Core.Interfaces;
using QuantEngine.Application.Interfaces;
namespace QuantEngine.Application.Services;
public class DataCollectionService
{
private readonly IKisApiClient _kisApiClient;
private readonly ICollectionRepository _repository;
private readonly ICollectionOrchestrator _orchestrator;
public DataCollectionService(
IKisApiClient kisApiClient,
ICollectionRepository repository,
ICollectionOrchestrator orchestrator)
public DataCollectionService(ICollectionOrchestrator orchestrator)
{
_kisApiClient = kisApiClient;
_repository = repository;
_orchestrator = orchestrator;
}
@@ -1,10 +1,12 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.IO;
using Microsoft.Extensions.Logging;
using QuantEngine.Core.Interfaces;
using QuantEngine.Application.Interfaces;
using QuantEngine.Application.Services;
using QuantEngine.Application.Models;
namespace QuantEngine.Application.Services;
@@ -15,6 +17,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
private readonly PriceDataNormalizer _normalizer;
private readonly SourcePriorityResolver _priorityResolver;
private readonly ILogger<KisDataCollectionOrchestrator> _logger;
private readonly string _auditRoot;
public KisDataCollectionOrchestrator(
IKisApiClient kisApiClient,
@@ -28,6 +31,28 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
_normalizer = normalizer;
_priorityResolver = priorityResolver;
_logger = logger;
_auditRoot = FindRepoTempRoot();
}
private static string FindRepoTempRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)
{
if (Directory.Exists(Path.Combine(current.FullName, ".git")))
{
return Path.Combine(current.FullName, "Temp", "collection_audit");
}
current = current.Parent;
}
return Path.Combine(Directory.GetCurrentDirectory(), "Temp", "collection_audit");
}
private void AppendAudit(CollectionExecutionAudit audit)
{
Directory.CreateDirectory(_auditRoot);
var path = Path.Combine(_auditRoot, $"{audit.RunId}.jsonl");
File.AppendAllText(path, JsonSerializer.Serialize(audit, new JsonSerializerOptions { WriteIndented = false }) + Environment.NewLine);
}
public async Task<CollectionRunResult> RunCollectionAsync(string runId, string account, List<string> tickers)
@@ -45,6 +70,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
try
{
_logger.LogInformation("Starting collection run {RunId}", runId);
AppendAudit(new CollectionExecutionAudit(runId, "RUNNING", DateTimeOffset.UtcNow, null, 0, 0, "started"));
var kisSource = new KisApiPriceSource(_kisApiClient);
var rows = new List<Dictionary<string, object>>();
@@ -157,6 +183,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
result.SourceCounts = sourceCounts;
result.Rows = rows;
result.Errors = errors;
AppendAudit(new CollectionExecutionAudit(runId, result.Status, DateTimeOffset.Parse(startedAt), DateTimeOffset.Parse(finishedAt), result.SuccessCount, result.ErrorCount, "finished"));
// Save run record
await _repository.SaveRunAsync(new CollectionRunRecord(
@@ -204,6 +231,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
result.Status = "FAILED";
result.FinishedAt = DataNormalizationHelper.KstNowIso();
result.ErrorMessage = ex.Message;
AppendAudit(new CollectionExecutionAudit(runId, result.Status, DateTimeOffset.Parse(startedAt), DateTimeOffset.Parse(result.FinishedAt), result.SuccessCount, result.ErrorCount, ex.Message));
return result;
}
}
@@ -358,4 +386,3 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
}