From dfee4cd1d8b4f7be840ede8253f0d6d279bb2720 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Tue, 16 Dec 2025 14:19:59 +0100 Subject: [PATCH] Aggiunta gestione salvataggio range statistiche --- .../Controllers/LuxController.cs | 95 ++++++++++++++++++- EgwCoreLib.Lux.Data/DataLayerContext.cs | 5 +- .../Services/CalcRuidService.cs | 91 ++++++++++++++++-- 3 files changed, 179 insertions(+), 12 deletions(-) diff --git a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs index 438ab67..69fef0f 100644 --- a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs +++ b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs @@ -4,6 +4,7 @@ using EgwCoreLib.Lux.Data.DbModel.Cost; using EgwCoreLib.Lux.Data.DbModel.Items; using EgwCoreLib.Lux.Data.DbModel.Production; using EgwCoreLib.Lux.Data.DbModel.Sales; +using EgwCoreLib.Lux.Data.DbModel.Stats; using EgwCoreLib.Lux.Data.DbModel.Task; using EgwCoreLib.Lux.Data.DbModel.Utils; using Microsoft.EntityFrameworkCore; @@ -2519,7 +2520,7 @@ namespace EgwCoreLib.Lux.Data.Controllers /// internal async Task OrderFromOffer(OfferModel rec2clone) { - OrderModel? newRec = null; + OrderModel? newRec = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { @@ -2610,7 +2611,7 @@ namespace EgwCoreLib.Lux.Data.Controllers if (numSave > 0 && newRec != null) { #if false - orderId = newRec.OrderID; + orderId = newRec.OrderID; #endif // sistemo UID... foreach (var item in newRec.OrderRowNav) @@ -2660,7 +2661,7 @@ namespace EgwCoreLib.Lux.Data.Controllers } return newRec; #if false - return orderId; + return orderId; #endif } @@ -3282,6 +3283,94 @@ namespace EgwCoreLib.Lux.Data.Controllers } #endif + /// + /// Esegue insert statistiche aggregate sul DB + /// + /// Elenco dei record da inserire + /// Se true preventivamente elimina record nel periodo richiesto + /// + internal async Task StatsAggrUpsertAsync(List listRecords, bool removeOld) + { + int answ = 0; + //using (DataLayerContext dbCtx = new DataLayerContext(_config)) + using (DataLayerContext dbCtx = new DataLayerContext()) + { + // in primis se richiesto calcolo range periodo e svuoto... + if (removeOld) + { + var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); + var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); + + if (firstRec != null && lastRec != null) + { + DateTime startDate = firstRec.Hour; + DateTime endDate = lastRec.Hour; + // uso direttamente ExecuteDelete + await dbCtx + .DbSetStatsAggr + .Where(x => x.Hour >= startDate && x.Hour < endDate) + .ExecuteDeleteAsync(); + } + } + + // ora preparo inserimento massivo + await dbCtx + .DbSetStatsAggr + .AddRangeAsync(listRecords); + + // salvo! + answ = await dbCtx.SaveChangesAsync(); + + // libero memoria del changeTracker + dbCtx.ChangeTracker.Clear(); + } + return answ; + } + + /// + /// Esegue insert statistiche di dettaglio sul DB + /// + /// Elenco dei record da inserire + /// Se true preventivamente elimina record nel periodo richiesto + /// + internal async Task StatsDetailUpsertAsync(List listRecords, bool removeOld) + { + int answ = 0; + //using (DataLayerContext dbCtx = new DataLayerContext(_config)) + using (DataLayerContext dbCtx = new DataLayerContext()) + { + // in primis se richiesto calcolo range periodo e svuoto... + if (removeOld) + { + var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); + var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); + + if (firstRec != null && lastRec != null) + { + DateTime startDate = firstRec.Hour; + DateTime endDate = lastRec.Hour; + // uso direttamente ExecuteDelete + await dbCtx + .DbSetStatsDet + .Where(x => x.Hour >= startDate && x.Hour < endDate) + .ExecuteDeleteAsync(); + } + } + + // ora preparo inserimento massivo + await dbCtx + .DbSetStatsDet + .AddRangeAsync(listRecords); + + // salvo! + answ = await dbCtx.SaveChangesAsync(); + + // libero memoria del changeTracker + dbCtx.ChangeTracker.Clear(); + } + return answ; + } + /// /// Elenco completo Tags /// diff --git a/EgwCoreLib.Lux.Data/DataLayerContext.cs b/EgwCoreLib.Lux.Data/DataLayerContext.cs index 0ae8606..30ecb1e 100644 --- a/EgwCoreLib.Lux.Data/DataLayerContext.cs +++ b/EgwCoreLib.Lux.Data/DataLayerContext.cs @@ -3,6 +3,7 @@ using EgwCoreLib.Lux.Data.DbModel.Cost; using EgwCoreLib.Lux.Data.DbModel.Items; using EgwCoreLib.Lux.Data.DbModel.Production; using EgwCoreLib.Lux.Data.DbModel.Sales; +using EgwCoreLib.Lux.Data.DbModel.Stats; using EgwCoreLib.Lux.Data.DbModel.Stock; using EgwCoreLib.Lux.Data.DbModel.Task; using EgwCoreLib.Lux.Data.DbModel.Utils; @@ -77,8 +78,8 @@ namespace EgwCoreLib.Lux.Data public virtual DbSet DbSetStockMov { get; set; } public virtual DbSet DbSetGenClass { get; set; } public virtual DbSet DbSetGenVal { get; set; } - - + public virtual DbSet DbSetStatsDet { get; set; } + public virtual DbSet DbSetStatsAggr { get; set; } partial void OnModelCreatingPartial(ModelBuilder modelBuilder); diff --git a/EgwCoreLib.Lux.Data/Services/CalcRuidService.cs b/EgwCoreLib.Lux.Data/Services/CalcRuidService.cs index 15ab203..2c4c78a 100644 --- a/EgwCoreLib.Lux.Data/Services/CalcRuidService.cs +++ b/EgwCoreLib.Lux.Data/Services/CalcRuidService.cs @@ -1,8 +1,14 @@ using EgwCoreLib.Lux.Core.Stats; +using EgwCoreLib.Lux.Data.Controllers; +using EgwCoreLib.Lux.Data.DbModel.Stats; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace EgwCoreLib.Lux.Data.Services @@ -10,16 +16,30 @@ namespace EgwCoreLib.Lux.Data.Services /// /// Gestione servizio indice richieste /// - public class CalcRuidService + public class CalcRuidService : BaseServ { #region Public Constructors - public CalcRuidService(ConnectionMultiplexer redis, TimeSpan retention, string redisBaseKey) + public CalcRuidService(IConfiguration configuration, IConnectionMultiplexer redisConn, string redisBaseKey, TimeSpan retention, TimeSpan archivePeriod) : base(configuration, redisConn) { - _db = redis.GetDatabase(); - _retention = retention; + _db = redisConn.GetDatabase(); _base = redisBaseKey.TrimEnd(':'); - //_base = redisBaseKey.EndsWith(":") ? redisBaseKey : redisBaseKey + ":"; + _retention = retention; + _archivePeriod = archivePeriod; + // conf DB + string connStr = BaseServ._config.GetConnectionString("Lux.All") ?? ""; + if (string.IsNullOrEmpty(connStr)) + { + Log.Error("ConnString empty!"); + } + else + { + //dbController = new Controllers.LuxController(_config); + dbController = new LuxController(); + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"CalcRuidService | LuxController OK"); + Log.Info(sb.ToString()); + } } #endregion Public Constructors @@ -75,7 +95,6 @@ namespace EgwCoreLib.Lux.Data.Services return ruid; } - /// /// Metodo di Cleanup periodico /// @@ -126,6 +145,57 @@ namespace EgwCoreLib.Lux.Data.Services await Task.WhenAll(tasks); } + /// + /// Metodo di Cleanup dato periodo limite prima del quale va eliminato l'elenco dei dati + /// + /// Environment calcolo + /// Tipologia richiesta + /// Limite temporale dati da eliminare + /// + public async Task CleanupOldRequestsAsync(string environment, string tipo, TimeSpan timeLimit) + { + var cutoff = DateTimeOffset.UtcNow.Add(-timeLimit).ToUnixTimeMilliseconds(); + var setKey = GetSortedSetKey(environment, tipo); + + var oldIds = await _db.SortedSetRangeByScoreAsync(setKey, stop: cutoff); + if (oldIds.Length == 0) return; + + var batch = _db.CreateBatch(); + var tasks = new List(); + + foreach (var id in oldIds) + { + var ruid = id.ToString(); + var hashKey = GetRequestKey(ruid); + + var uid = await _db.HashGetAsync(hashKey, "UID"); + if (!uid.IsNull) + { + var uidKey = GetUidSetKey(uid); + tasks.Add(batch.SetRemoveAsync(uidKey, ruid)); + + tasks.Add(batch.SetLengthAsync(uidKey).ContinueWith(t => + { + if (t.Result == 0) + _db.KeyDelete(uidKey); + })); + } + + tasks.Add(batch.KeyDeleteAsync(hashKey)); + } + + tasks.Add(batch.SortedSetRemoveRangeByScoreAsync(setKey, double.NegativeInfinity, cutoff)); + + tasks.Add(batch.SortedSetLengthAsync(setKey).ContinueWith(t => + { + if (t.Result == 0) + _db.KeyDelete(setKey); + })); + + batch.Execute(); + await Task.WhenAll(tasks); + } + /// /// Metodo di Aggiornamento richiesta esistente /// @@ -174,7 +244,6 @@ namespace EgwCoreLib.Lux.Data.Services await Task.WhenAll(t1, t2, t3); } - /// /// Metodo Recupero combinazioni envir/tipo /// @@ -290,6 +359,8 @@ namespace EgwCoreLib.Lux.Data.Services #region Private Fields + private static Logger Log = LogManager.GetCurrentClassLogger(); + private readonly TimeSpan _archivePeriod; private readonly string _base; private readonly IDatabase _db; private readonly TimeSpan _retention; @@ -297,6 +368,12 @@ namespace EgwCoreLib.Lux.Data.Services #endregion Private Fields + #region Private Properties + + private static LuxController dbController { get; set; } = null!; + + #endregion Private Properties + #region Private Methods ///