feat: add FundChartController (/api/chart/fund/{isin})
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
139
CertReports.Syncfusion/Controllers/FundChartController.cs
Normal file
139
CertReports.Syncfusion/Controllers/FundChartController.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using CertReports.Syncfusion.Services.Implementations;
|
||||
using CertReports.Syncfusion.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Syncfusion.Pdf;
|
||||
using Syncfusion.Pdf.Graphics;
|
||||
|
||||
namespace CertReports.Syncfusion.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint:
|
||||
/// GET /api/chart/fund/{isin}[?format=png|jpg|jpeg|pdf&width=&height=&save=true]
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/chart/fund")]
|
||||
public class FundChartController : ControllerBase
|
||||
{
|
||||
private readonly IFundDataService _dataService;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<FundChartController> _logger;
|
||||
|
||||
public FundChartController(
|
||||
IFundDataService dataService,
|
||||
IConfiguration configuration,
|
||||
ILogger<FundChartController> logger)
|
||||
{
|
||||
_dataService = dataService;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("{isin}")]
|
||||
public async Task<IActionResult> GetChart(
|
||||
string isin,
|
||||
[FromQuery] string format = "png",
|
||||
[FromQuery] int width = 1100,
|
||||
[FromQuery] int height = 700,
|
||||
[FromQuery] bool save = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(isin))
|
||||
return BadRequest("ISIN richiesto.");
|
||||
|
||||
width = Math.Clamp(width, 400, 2000);
|
||||
height = Math.Clamp(height, 300, 1500);
|
||||
|
||||
try
|
||||
{
|
||||
var info = await _dataService.GetFundInfoAsync(isin);
|
||||
var points = await _dataService.GetChartPricesAsync(isin);
|
||||
|
||||
if (info == null || points.Count == 0)
|
||||
return NotFound($"Nessun dato per ISIN {isin}");
|
||||
|
||||
bool isJpeg = format.Equals("jpg", StringComparison.OrdinalIgnoreCase)
|
||||
|| format.Equals("jpeg", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (format.Equals("pdf", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var pngBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height);
|
||||
var pdfBytes = WrapPngInPdf(pngBytes);
|
||||
Response.Headers.Append("Content-Disposition",
|
||||
$"inline; filename=chart_fund_{isin}.pdf");
|
||||
return File(pdfBytes, "application/pdf");
|
||||
}
|
||||
|
||||
var imgBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height, jpeg: isJpeg);
|
||||
|
||||
if (save && isJpeg)
|
||||
await SaveChartToDiskAsync(isin, imgBytes);
|
||||
|
||||
if (isJpeg)
|
||||
{
|
||||
Response.Headers.Append("Content-Disposition",
|
||||
$"inline; filename=chart_fund_{isin}.jpg");
|
||||
return File(imgBytes, "image/jpeg");
|
||||
}
|
||||
|
||||
Response.Headers.Append("Content-Disposition",
|
||||
$"inline; filename=chart_fund_{isin}.png");
|
||||
return File(imgBytes, "image/png");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore generazione chart fondo per ISIN {Isin}", isin);
|
||||
return StatusCode(500, new { status = "KO", message = "Errore nella generazione del grafico." });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveChartToDiskAsync(string isin, byte[] imgBytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
string? folder = _configuration["ChartSettings:SavePath"];
|
||||
if (string.IsNullOrEmpty(folder))
|
||||
{
|
||||
_logger.LogWarning("Salvataggio chart fondo saltato: ChartSettings:SavePath non configurato");
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(folder);
|
||||
var fullPath = Path.Combine(folder, $"{isin}_fund.jpg");
|
||||
await System.IO.File.WriteAllBytesAsync(fullPath, imgBytes);
|
||||
_logger.LogInformation("Chart fondo salvato in {Path}", fullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Salvataggio chart fondo fallito per {Isin}", isin);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] WrapPngInPdf(byte[] pngBytes)
|
||||
{
|
||||
var doc = new PdfDocument();
|
||||
doc.PageSettings.Size = PdfPageSize.A4;
|
||||
doc.PageSettings.Orientation = PdfPageOrientation.Landscape;
|
||||
doc.PageSettings.Margins.All = 30;
|
||||
|
||||
var page = doc.Pages.Add();
|
||||
var g = page.Graphics;
|
||||
float pw = page.GetClientSize().Width;
|
||||
float ph = page.GetClientSize().Height;
|
||||
|
||||
using var stream = new MemoryStream(pngBytes);
|
||||
var img = new PdfBitmap(stream);
|
||||
|
||||
float ratio = (float)img.Width / img.Height;
|
||||
float dw = pw;
|
||||
float dh = dw / ratio;
|
||||
if (dh > ph) { dh = ph; dw = dh * ratio; }
|
||||
|
||||
float x = (pw - dw) / 2;
|
||||
float y = (ph - dh) / 2;
|
||||
g.DrawImage(img, x, y, dw, dh);
|
||||
|
||||
using var output = new MemoryStream();
|
||||
doc.Save(output);
|
||||
doc.Close(true);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user