DataSourceAdapter-Pattern + SiteExportService schlanker + Page-Services Scoped

- IDataSourceAdapter mit 3 Implementierungen (HANA, SAP_GATEWAY, MANUAL_EXCEL)
  und DataSourceAdapterResolver ersetzen das if/else auf ConnectionKind.
- SiteExportService von 338 auf 187 Zeilen reduziert: Pipeline
  Resolve -> Fetch -> Transform -> Excel -> Central -> SharePoint.
- Page-Services auf Scoped (per Blazor-Circuit); Orchestrator bleibt Singleton
  fuer geteilten Export-Status.
This commit is contained in:
Claude
2026-04-17 12:11:35 +00:00
parent 2a56ba53ba
commit 82ac7df0ec
11 changed files with 431 additions and 231 deletions
@@ -0,0 +1,93 @@
using TrafagSalesExporter.Models;
namespace TrafagSalesExporter.Services.DataSources;
public sealed class ManualExcelDataSourceAdapter : IDataSourceAdapter
{
private readonly ISharePointUploadService _sharePointService;
private readonly IManualExcelImportService _manualExcelImportService;
private readonly IAppEventLogService _appEventLogService;
public ManualExcelDataSourceAdapter(
ISharePointUploadService sharePointService,
IManualExcelImportService manualExcelImportService,
IAppEventLogService appEventLogService)
{
_sharePointService = sharePointService;
_manualExcelImportService = manualExcelImportService;
_appEventLogService = appEventLogService;
}
public string ConnectionKind => SourceSystemConnectionKinds.ManualExcel;
public async Task<DataSourceFetchResult> FetchAsync(DataSourceFetchContext context)
{
var site = context.Site;
if (string.IsNullOrWhiteSpace(site.ManualImportFilePath))
throw new InvalidOperationException($"Standort '{site.Land}' hat keine manuelle Excel-Datei.");
var manualImportPath = site.ManualImportFilePath.Trim();
string filePath;
string? tempManualImportPath = null;
try
{
if (File.Exists(manualImportPath))
{
filePath = manualImportPath;
}
else if (LooksLikeSharePointReference(manualImportPath))
{
var spConfig = context.SharePointConfig
?? throw new InvalidOperationException(
"Fuer SharePoint-Manuellimport fehlt eine vollstaendige SharePoint-Konfiguration in Settings.");
if (string.IsNullOrWhiteSpace(spConfig.TenantId) ||
string.IsNullOrWhiteSpace(spConfig.ClientId) ||
string.IsNullOrWhiteSpace(spConfig.ClientSecret) ||
string.IsNullOrWhiteSpace(spConfig.SiteUrl))
{
throw new InvalidOperationException(
"Fuer SharePoint-Manuellimport fehlt eine vollstaendige SharePoint-Konfiguration in Settings.");
}
context.UpdateStatus?.Invoke("Manuelle Excel von SharePoint laden...");
await _appEventLogService.WriteAsync("Export", "Manuelle Excel von SharePoint laden",
siteId: site.Id, land: site.Land, details: manualImportPath);
tempManualImportPath = await _sharePointService.DownloadToTempFileAsync(
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret,
spConfig.SiteUrl, manualImportPath);
filePath = manualImportPath;
}
else
{
throw new InvalidOperationException(
$"Die manuelle Excel-Datei wurde nicht gefunden: {manualImportPath}");
}
var readPath = tempManualImportPath ?? filePath;
context.UpdateStatus?.Invoke("Manuelle Excel lesen...");
await _appEventLogService.WriteAsync("Export", "Manuelle Excel lesen",
siteId: site.Id, land: site.Land, details: filePath);
var records = await _manualExcelImportService.ReadSalesRecordsAsync(readPath, site);
return new DataSourceFetchResult
{
Records = records,
ReferenceFilePath = filePath
};
}
finally
{
if (!string.IsNullOrWhiteSpace(tempManualImportPath) && File.Exists(tempManualImportPath))
File.Delete(tempManualImportPath);
}
}
private static bool LooksLikeSharePointReference(string path)
=> path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("/Shared Documents/", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("Shared Documents/", StringComparison.OrdinalIgnoreCase);
}