feat: add ChartDataServiceV2 with IChartDataServiceV2 (2-SP approach)

This commit is contained in:
2026-05-27 15:45:15 +02:00
parent 5d67ae3463
commit 494443ede3

View File

@@ -0,0 +1,105 @@
using CertReports.Syncfusion.Models;
using Microsoft.Data.SqlClient;
using System.Data;
namespace CertReports.Syncfusion.Services.Implementations;
/// <summary>
/// Recupera i dati per il grafico V2 con solo 2 round-trip al DB.
///
/// SP utilizzate:
/// - cedlab_Chart_UL1: Metadata sottostanti (1 query, N sottostanti)
/// - cedlab_Chart_AllSeriesV2: Tutte le serie CTF + UL in una query (TOP 350 per-serie)
/// </summary>
public interface IChartDataServiceV2
{
Task<ChartDataV2?> GetChartDataV2Async(string isin);
}
public class ChartDataServiceV2 : IChartDataServiceV2
{
private readonly string _connectionString;
private readonly ILogger<ChartDataServiceV2> _logger;
public ChartDataServiceV2(IConfiguration config, ILogger<ChartDataServiceV2> logger)
{
_connectionString = config.GetConnectionString("CertDb")
?? throw new InvalidOperationException("ConnectionString 'CertDb' non configurata.");
_logger = logger;
}
public async Task<ChartDataV2?> GetChartDataV2Async(string isin)
{
await using var conn = new SqlConnection(_connectionString);
await conn.OpenAsync();
// ── 1. Metadata sottostanti (cedlab_Chart_UL1) ─────────────────
var underlyings = new List<ChartUlMetadata>();
await using (var cmd = new SqlCommand("cedlab_Chart_UL1", conn)
{ CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("@isin", isin);
await using var r = await cmd.ExecuteReaderAsync();
while (await r.ReadAsync())
{
underlyings.Add(new ChartUlMetadata
{
IDCertificates = r.GetInt32(r.GetOrdinal("IDCertificates")),
IDUnderlyings = r.GetInt32(r.GetOrdinal("IDUnderlyings")),
StartDate = r.GetDateTime(r.GetOrdinal("StartDate")),
Strike = r.GetDecimal(r.GetOrdinal("Strike")),
BarrieraCouponPerc = r.GetDecimal(r.GetOrdinal("BarrieraCouponPerc")),
BarrieraCoupon = r.GetDecimal(r.GetOrdinal("BarrieraCoupon")),
BarrieraCapitalePerc = r.GetDecimal(r.GetOrdinal("BarrieraCapitalePerc")),
BarrieraCapitale = r.GetDecimal(r.GetOrdinal("BarrieraCapitale")),
Sottostante = r.GetString(r.GetOrdinal("Sottostante")),
IsWorstOf = r.GetInt32(r.GetOrdinal("IsWorstOf")),
PriceWorst = r.GetDecimal(r.GetOrdinal("PriceWorst")),
PriceWorstPerc = r.GetDecimal(r.GetOrdinal("PriceWorstPerc")),
NumPrezziCFT = r.GetInt32(r.GetOrdinal("NumPrezziCFT")),
NomeCFT = r.GetString(r.GetOrdinal("NomeCFT")),
TriggerAutocallPerc = r.GetDecimal(r.GetOrdinal("TriggerAutocallPerc")),
AutocallValue = r.GetDecimal(r.GetOrdinal("AutocallValue")),
});
}
}
if (underlyings.Count == 0)
{
_logger.LogWarning(
"Nessun sottostante trovato per il grafico V2 di {Isin} (meno di 30 prezzi EOD?)", isin);
return null;
}
var result = new ChartDataV2
{
Isin = isin,
GlobalMeta = underlyings[0], // worst-of è il primo (SP ordina IsWorstOf DESC)
Underlyings = underlyings,
};
// ── 2. Tutte le serie (cedlab_Chart_AllSeriesV2) ────────────────
await using (var cmd = new SqlCommand("cedlab_Chart_AllSeriesV2", conn)
{ CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("@isin", isin);
await using var r = await cmd.ExecuteReaderAsync();
while (await r.ReadAsync())
{
result.SeriesPoints.Add(new ChartSeriesPoint
{
IDUnderlyings = r.GetInt32(r.GetOrdinal("IDUnderlyings")),
Date = r.GetDateTime(r.GetOrdinal("Px_date")),
Performance = r.GetDecimal(r.GetOrdinal("Performance")),
});
}
}
_logger.LogInformation(
"Dati grafico V2 caricati per {Isin}: {UlCount} sottostanti, {Points} punti totali",
isin, underlyings.Count, result.SeriesPoints.Count);
return result;
}
}