V13-FE-011: finalize search list layout slice

This commit is contained in:
2026-08-09 02:57:26 +09:00
parent 9efd202e76
commit 6422cb2b13
984 changed files with 120811 additions and 1498 deletions
@@ -0,0 +1,39 @@
using Shared.Excel;
namespace Modules.Common.Imports.Jobs;
public sealed class CommitImportJob(
ImportRepository repository,
ImportDefinitionRegistry registry,
ImportProgressPublisher progress)
{
public async Task RunAsync(Guid sessionId, string actor, CancellationToken ct)
{
try
{
var (importType, _) = await repository.GetWorkDefinitionAsync(sessionId, ct);
var definition = registry.Get(importType);
await progress.PublishAsync(new ImportProgressEvent(
sessionId, ImportStatuses.Committing, 1, 0, 0, 0, 0, 0,
"검증된 데이터를 반영하고 있습니다."), ct);
var result = await definition.CommitAsync(sessionId, actor, ct);
await repository.CompleteAsync(sessionId, result, ct);
await progress.PublishAsync(new ImportProgressEvent(
sessionId,
result.Failed > 0 ? ImportStatuses.PartiallyCompleted : ImportStatuses.Completed,
100,
result.Created + result.Updated + result.Failed,
result.Created + result.Updated + result.Failed,
result.Created + result.Updated,
result.Failed,
0,
result.Failed > 0 ? "일부 데이터가 오류로 제외되었습니다." : "반영이 완료되었습니다."), ct);
}
catch (Exception)
{
await repository.SetStatusAsync(sessionId, ImportStatuses.Failed, 100, ct);
throw;
}
}
}
@@ -0,0 +1,52 @@
using Npgsql;
using Shared.Excel;
namespace Modules.Common.Imports.Jobs;
public sealed class ValidateImportJob(
ImportRepository repository,
ImportDefinitionRegistry registry,
ClosedXmlWorkbookService workbook,
ImportProgressPublisher progress,
NpgsqlDataSource dataSource)
{
public async Task RunAsync(Guid sessionId, CancellationToken ct)
{
try
{
var (importType, mapping) = await repository.GetWorkDefinitionAsync(sessionId, ct);
var definition = registry.Get(importType);
var targetToSource = mapping
.Where(x => !string.IsNullOrWhiteSpace(x.TargetField))
.ToDictionary(x => x.TargetField!, x => x.SourceColumn, StringComparer.OrdinalIgnoreCase);
var (contentType, bytes) = await repository.GetFileAsync(sessionId, ct);
await using var stream = new MemoryStream(bytes, writable: false);
var rows = workbook.ReadRows(stream, definition.Definition.MaxRows).ToArray();
await progress.PublishAsync(new ImportProgressEvent(
sessionId, ImportStatuses.Validating, 15, rows.Length, 0, 0, 0, 0,
"참조 데이터를 확인하고 있습니다."), ct);
await using var connection = await dataSource.OpenConnectionAsync(ct);
var results = await definition.ValidateAsync(rows, targetToSource, connection, ct);
var valid = results.Count(x => x.IsValid);
var invalid = results.Count - valid;
var warnings = results.Count(x => x.Warnings.Count > 0);
await progress.PublishAsync(new ImportProgressEvent(
sessionId, ImportStatuses.Validating, 80, rows.Length, rows.Length,
valid, invalid, warnings, "검증 결과를 저장하고 있습니다."), ct);
await repository.ReplaceRowsAsync(sessionId, results, ct);
await progress.PublishAsync(new ImportProgressEvent(
sessionId, ImportStatuses.Validated, 100, rows.Length, rows.Length,
valid, invalid, warnings, "검증이 완료되었습니다."), ct);
}
catch (Exception)
{
await repository.SetStatusAsync(sessionId, ImportStatuses.Failed, 100, ct);
throw;
}
}
}