using GPW.CORE.Comp; using GPW.CORE.Data.DbModels; using GPW.CORE.Data.DTO; using Microsoft.AspNetCore.Identity.UI.Services; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Diagnostics; using System.Linq; namespace GPW.CORE.Smart.Data { public class CoreSmartDataService : IDisposable { #region Public Fields public static CORE.Data.Controllers.GPWController dbController = null!; public bool VetoInsert = false; #endregion Public Fields #region Public Constructors public CoreSmartDataService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult, IEmailSender emailSender) { _logger = logger; _configuration = configuration; _emailSender = emailSender; // Conf cache redisConn = redisConnMult;// ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis")); //redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis")); redisDb = this.redisConn.GetDatabase(); // json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ JSSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // cod app CodApp = _configuration["CodApp"]; var strVeto = _configuration["VetoIns"]; _ = bool.TryParse(strVeto, out VetoInsert); // Conf DB string connStr = _configuration.GetConnectionString("GPW.DB"); if (string.IsNullOrEmpty(connStr)) { _logger.LogError("ConnString empty!"); } else { dbController = new CORE.Data.Controllers.GPWController(configuration); } _logger.LogInformation("Avviata classe CoreSmartDataService"); } #endregion Public Constructors #region Public Properties public string CodApp { get; set; } = ""; #endregion Public Properties #region Public Methods /// /// Recupera l'elenco dei controlli VC19 nel periodo indicato x dipendente /// /// Dipendente interessato /// Data di riferimento (ultima/corrente) /// NUm settimane precedenti da recuperare /// public async Task> CheckVC19List(int idxDipendente, DateTime dtInizio, DateTime dtFine) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyVC19}:{dtInizio:yyyyMMdd}:{dtFine:yyyMMdd}"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.CheckVC19List(idxDipendente, dtInizio, dtFine); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, FastCache); } if (dbResult == null) { dbResult = new List(); } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"CheckVC19List | {source} in: {ts.TotalMilliseconds} ms"); return dbResult; } /// /// Recupera l'elenco dei dettagli giornalieri attività di un dipendente, dato periodo riferimento /// /// Dipendente interessato /// Data di riferimento (ultima/corrente) /// NUm settimane precedenti da recuperare /// public async Task> DailyDetails(int idxDipendente, DateTime dtInizio, DateTime dtFine) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyDailyData}:{idxDipendente}:{dtInizio.ToString("yyyy-MM-dd")}:{dtFine.ToString("yyyy-MM-dd")}"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.DailyDetails(idxDipendente, dtInizio, dtFine); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, FastCache); } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"DailyDetails | {source} in: {ts.TotalMilliseconds} ms"); return dbResult; } public string DeriptData(string encData) { return SteamCrypto.DecryptString(encData, passPhrase); } public async Task DeviceBySecret(string devSecret) { string source = "DB"; AnagDeviceModel? dbResult = new AnagDeviceModel(); List? cachedResult = new List(); string currKey = $"{rKeyUserSecrets}"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { cachedResult = new List(); } else { cachedResult = tempResult; } } // controllo se c'è in cache... dbResult = cachedResult.FirstOrDefault(x => x.DeviceSecret == devSecret); if (dbResult == null) { source = "REDIS + DB"; //altrimenti cerco nel DB ed aggiungo dbResult = dbController.AnagDeviceByKey(devSecret); if (dbResult != null) { cachedResult.Add(dbResult); rawData = JsonConvert.SerializeObject(cachedResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, LongCache); } } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"DeviceBySecret | {source} in: {ts.TotalMilliseconds} ms"); return dbResult; } /// /// Crea un record device /// /// /// public async Task DeviceInsert(AnagDeviceModel newRecord) { string source = "DB"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); bool fatto = dbController.AnagDeviceInsert(newRecord); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"DeviceInsert | {source} in: {ts.TotalMilliseconds} ms"); await Task.Delay(1); return fatto; } public async Task> DipendentiGetAll() { string source = "DB"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); List? dbResult = new List(); string? rawData = await redisDb.StringGetAsync(rKeyDipendenti); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.DipendentiGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(rKeyDipendenti, rawData, FastCache); } if (dbResult == null) { dbResult = new List(); } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"DipendentiGetAll | {source} in: {ts.TotalMilliseconds} ms"); return dbResult; } public void Dispose() { // Clear database controller dbController.Dispose(); } public string EncriptData(string rawData) { return SteamCrypto.EncryptString(rawData, passPhrase); } public async Task FlushRedisCache() { await Task.Delay(1); RedisValue pattern = new RedisValue($"{redisBaseAddr}:*"); bool answ = await ExecFlushRedisPattern(pattern); return answ; } /// /// Recupera l'elenco dei Rilievi temperatura nel periodo indicato x dipendente /// /// Dipendente interessato /// Data di riferimento (ultima/corrente) /// NUm settimane precedenti da recuperare /// public async Task> RilTempList(int idxDipendente, DateTime dtInizio, DateTime dtFine) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyRilTemp}:{dtInizio:yyyyMMdd}:{dtFine:yyyMMdd}"; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { dbResult = dbController.RilTempList(idxDipendente, dtInizio, dtFine); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, FastCache); } if (dbResult == null) { dbResult = new List(); } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Debug($"RilTempList | {source} in: {ts.TotalMilliseconds} ms"); return dbResult; } /// /// Registra update valore temp rilevata /// /// /// public async Task RilTempUpdate(RilievoTempModel currItem) { bool answ = false; try { dbController.RilTempUpdate(currItem); // invalido la cache... await ExecFlushRedisPattern($"{rKeyRilTemp}:*"); await ExecFlushRedisPattern($"{rKeyDailyData}:*"); answ = true; } catch { } return answ; } public void rollBackEdit(object item) { dbController.rollBackEntity(item); } /// /// Elenco timbrature di un giorno /// /// /// /// public async Task> TimbratureDay(DateTime dateRif, int IdxDip) { await Task.Delay(1); List? dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.TimbratureDay(dateRif, IdxDip); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB + caching per TimbratureDay: {ts.TotalMilliseconds} ms"); return dbResult; } public async Task TimbratureDelete(TimbratureModel currItem) { bool answ = false; try { dbController.TimbratureDelete(currItem); // invalido la cache... await FlushRedisCache(); answ = true; } catch { } return answ; } public async Task TimbratureUpdate(TimbratureModel currItem) { bool answ = false; try { dbController.TimbratureUpdate(currItem); // invalido la cache... await FlushRedisCache(); answ = true; } catch { } return answ; } #endregion Public Methods #region Protected Fields protected const string passPhrase = "EB6BD8BE-638F-481E-85E4-F89012DA95BB"; protected const string redisBaseAddr = "GPW:Smart"; protected const string rKeyAKV = $"{redisBaseAddr}:Cache:AKV"; protected const string rKeyAnOrDip = $"{redisBaseAddr}:Cache:AnOrariDip"; protected const string rKeyCalcOreFase = $"{redisBaseAddr}:Cache:CalcOreFase"; protected const string rKeyCalcOreProj = $"{redisBaseAddr}:Cache:CalcOreProj"; protected const string rKeyCalendari = $"{redisBaseAddr}:Cache:Calendari"; protected const string rKeyCliAll = $"{redisBaseAddr}:Cache:CliAll"; protected const string rKeyConfig = $"{redisBaseAddr}:Cache:Config"; protected const string rKeyDailyData = $"{redisBaseAddr}:Cache:DailyData"; protected const string rKeyUserSecrets = $"{redisBaseAddr}:Cache:DevSecrets"; protected const string rKeyDipendenti = $"{redisBaseAddr}:Cache:Dipendenti"; protected const string rKeyFasiAll = $"{redisBaseAddr}:Cache:FasiAll"; protected const string rKeyFasiByProj = $"{redisBaseAddr}:Cache:FasiByProj"; protected const string rKeyFasiSearch = $"{redisBaseAddr}:Cache:FasiSearch"; protected const string rKeyGiust = $"{redisBaseAddr}:Cache:Giustif"; protected const string rKeyGrpAll = $"{redisBaseAddr}:Cache:GrpAll"; protected const string rKeyGrpUser = $"{redisBaseAddr}:Cache:GrpUser"; protected const string rKeyParetoRegAtt = $"{redisBaseAddr}:Cache:ParetoRegAtt"; protected const string rKeyProjAll = $"{redisBaseAddr}:Cache:ProjAll"; protected const string rKeyRilTemp = $"{redisBaseAddr}:Cache:RilTemp"; protected const string rKeyVC19 = $"{redisBaseAddr}:Cache:VC19"; protected const string rKeyVetoIns = $"{redisBaseAddr}:Cache:VetoInsert"; protected const string rKeyWeekStats = $"{redisBaseAddr}:Cache:WeekStats"; protected static string connStringBBM = ""; protected Random rnd = new Random(); #endregion Protected Fields #region Protected Methods /// /// Invio email tramite libreria /// /// /// /// /// protected async Task sendEmail(string destList, string subject, string message) { List emailDestList = destList.Split(",").ToList(); foreach (var dest in emailDestList) { try { await _emailSender.SendEmailAsync(dest, subject, message); } catch (Exception exc) { Log.Error($"Eccezione durante invio email:{Environment.NewLine}dest: {dest} | subject {subject}{Environment.NewLine}{exc}"); } } } #endregion Protected Methods #region Private Fields private static IConfiguration _configuration = null!; private static ILogger _logger = null!; private static JsonSerializerSettings? JSSettings; private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); private readonly IEmailSender _emailSender; private List cachedDataList = new List(); /// /// Durata cache lunga IN SECONDI /// private int cacheTtlLong = 60 * 5; /// /// Durata cache breve IN SECONDI /// private int cacheTtlShort = 60 * 1; /// /// Oggetto per connessione a REDIS /// private IConnectionMultiplexer redisConn; //ISubscriber sub = redis.GetSubscriber(); /// /// Oggetto DB redis da impiegare x chiamate R/W /// private IDatabase redisDb = null!; #endregion Private Fields #region Private Properties /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan FastCache { get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan LongCache { get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan UltraLongCache { get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); } #endregion Private Properties #region Private Methods /// /// Esegue flush memoria redis dato pattern /// /// /// private async Task ExecFlushRedisPattern(RedisValue pattern) { bool answ = false; var listEndpoints = redisConn.GetEndPoints(); foreach (var endPoint in listEndpoints) { //var server = redisConnAdmin.GetServer(listEndpoints[0]); var server = redisConn.GetServer(endPoint); if (server != null) { var keyList = server.Keys(redisDb.Database, pattern); foreach (var item in keyList) { await redisDb.KeyDeleteAsync(item); } answ = true; } } return answ; } #endregion Private Methods } }