From 59560f927a0bee6aecf69a7ba0c164ffc614dd79 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Wed, 18 Jan 2023 11:51:43 +0100 Subject: [PATCH] Update gestione editing RA --- GPW.CORE.SMART/Components/CmpTop.razor.cs | 100 +++++ GPW.CORE.SMART/Components/CompProj.razor | 18 +- GPW.CORE.SMART/Components/CompProj.razor.cs | 52 ++- GPW.CORE.SMART/Components/RegAttEditor.razor | 132 ++++++ .../Components/RegAttEditor.razor.cs | 382 ++++++++++++++++++ GPW.CORE.SMART/Data/CoreSmartDataService.cs | 373 +++++++++++++++++ GPW.CORE.SMART/Data/LicenseService.cs | 296 ++++++++++++++ GPW.CORE.SMART/GPW.CORE.SMART.csproj | 1 + GPW.CORE.SMART/Program.cs | 1 + GPW.CORE.SMART/appsettings.json | 4 +- 10 files changed, 1344 insertions(+), 15 deletions(-) create mode 100644 GPW.CORE.SMART/Components/RegAttEditor.razor create mode 100644 GPW.CORE.SMART/Components/RegAttEditor.razor.cs create mode 100644 GPW.CORE.SMART/Data/LicenseService.cs diff --git a/GPW.CORE.SMART/Components/CmpTop.razor.cs b/GPW.CORE.SMART/Components/CmpTop.razor.cs index 6454754..5afb835 100644 --- a/GPW.CORE.SMART/Components/CmpTop.razor.cs +++ b/GPW.CORE.SMART/Components/CmpTop.razor.cs @@ -1,3 +1,4 @@ +using GPW.CORE.Data.DbModels; using GPW.CORE.Smart.Data; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; @@ -23,6 +24,9 @@ namespace GPW.CORE.Smart.Components [Inject] protected IJSRuntime JSRuntime { get; set; } = null!; + [Inject] + protected LicenseService LicServ { get; set; } = null!; + [Inject] protected MessageService MService { get; set; } = null!; @@ -35,10 +39,88 @@ namespace GPW.CORE.Smart.Components #region Protected Methods + /// + /// Verifica informazioni condivise applicazione + /// + /// + protected async Task checkActivations() + { + bool allOk = false; + if (LicServ.ValidData) + { + // se non fosse tutto ok + if (!LicServ.HasActivData) + { + // chiamo refresh licenze da remoto + allOk = await LicServ.RefreshLicense(); + } + } + + await Task.Delay(1); + return allOk; + } + + /// + /// Verifica informazioni condivise applicazione + /// + /// + protected async Task checkSharedInfo() + { + bool allOk = false; + if (!LicServ.ValidData) + { + // salvo cod app da Conf... + LicServ.Applicazione = CDService.CodApp; + // effettuo lettura dati preliminari da AKV (eventualmente in cache...) + List? rawAkvList = await CDService.AKVList(); + if (rawAkvList != null) + { + LicServ.AKVList = rawAkvList; + allOk = LicServ.InitAkv(); + } + } + + await Task.Delay(1); + return allOk; + } + + /// + /// Verifica info specifiche utente + /// + /// + protected async Task checkUserLicense() + { + bool answ = false; + // verifico utente attivo + answ = MService.IsActive; + if (answ) + { + var rawActList = LicServ.ActivList; + // recupero hash utente + string hashImpiego = MService.HashDip; + // confronto con elenco attivazioni dello shared service... + var foundRec = rawActList.Where(x => x.CodImpiego == hashImpiego); + answ = foundRec.Count() > 0; + // salvo status payloadOk + MService.PayloadOk = answ; + } + + await Task.Delay(1); + return answ; + } + + protected async Task DoVerifyActiv() + { + MService.PayloadOk = false; + await Task.Delay(250); + await VerifyActiv(); + } + protected async Task ForceReset() { string nextPage = navManager.ToBaseRelativePath(navManager.Uri); await Task.Delay(1); + await DoVerifyActiv(); navManager.NavigateTo($"ForceReset?nextPage={nextPage}", true); } @@ -50,15 +132,33 @@ namespace GPW.CORE.Smart.Components if (!navManager.Uri.Contains("jumper")) { await checkUser(); + // verifico attivazione dipendente... + await checkUserLicense(); } } } + protected override async Task OnInitializedAsync() + { + // verifica attivazione + await VerifyActiv(); + } + protected void ReturnHome() { navManager.NavigateTo("Home", true); } + protected async Task VerifyActiv() + { + // preliminarmente verifica shared info + await checkSharedInfo(); + // refresh attivazioni se necessario + await checkActivations(); + // verifico e salvo dati attivazione dipendente... + await checkUserLicense(); + } + #endregion Protected Methods #region Private Properties diff --git a/GPW.CORE.SMART/Components/CompProj.razor b/GPW.CORE.SMART/Components/CompProj.razor index e027bd2..22283ce 100644 --- a/GPW.CORE.SMART/Components/CompProj.razor +++ b/GPW.CORE.SMART/Components/CompProj.razor @@ -1,4 +1,4 @@ - +
- @if (currRec != null) + @if (ClonedRA != null) { - + }
- \ No newline at end of file + +@if (CurrRA != null) +{ + + + +} \ No newline at end of file diff --git a/GPW.CORE.SMART/Components/CompProj.razor.cs b/GPW.CORE.SMART/Components/CompProj.razor.cs index 40fe635..5684607 100644 --- a/GPW.CORE.SMART/Components/CompProj.razor.cs +++ b/GPW.CORE.SMART/Components/CompProj.razor.cs @@ -58,21 +58,21 @@ namespace GPW.CORE.Smart.Components protected async Task ExecPaste(RegAttivitaModel pasteRec) { // usa i dati del record ricevuto x indicare inizio... - currRec.Inizio = pasteRec.Inizio; + ClonedRA.Inizio = pasteRec.Inizio; // fine e durata calcolate... - if (pasteRec.OreTot < currRec.OreTot) + if (pasteRec.OreTot < ClonedRA.OreTot) { - currRec.Fine = pasteRec.Fine; - currRec.OreTot = pasteRec.OreTot; + ClonedRA.Fine = pasteRec.Fine; + ClonedRA.OreTot = pasteRec.OreTot; } else { - currRec.Fine = pasteRec.Inizio.AddHours((double)currRec.OreTot); + ClonedRA.Fine = pasteRec.Inizio.AddHours((double)ClonedRA.OreTot); } // chiama insert ed invalida cache... - await CDService.RegAttUpdate(currRec); + await CDService.RegAttUpdate(ClonedRA); // svuota cache x copy/paste - currRec = null; + ClonedRA = null; await Task.Delay(1); // richiesta rilettura dati... await ReqReload.InvokeAsync(true); @@ -85,14 +85,46 @@ namespace GPW.CORE.Smart.Components protected async Task SaveCloned(RegAttivitaModel clonedRec) { - currRec = clonedRec; + ClonedRA = clonedRec; textDtAct = $"{DateTime.Now:ddd dd.MM.yyyy}"; lastAction = $"Clonato Attività"; await Task.Delay(1); } + protected async Task EditItem(RegAttivitaModel selRec) + { + CurrRA = selRec; + await Task.Delay(1); + } + + protected async Task UpdRegAttData() + { + isLoading = true; + CurrRA = null; + // richiesta rilettura dati... + await ReqReload.InvokeAsync(true); + +#if false + ListTimbr = null; + await Task.Delay(100); + await ReloadData(); + // ricarico giornata... + if (SelDayDTO != null) + { + DateTime dtRif = SelDayDTO.DtRif; + if (ListRecords != null) + { + SelDayDTO = ListRecords.Where(x => x.DtRif == dtRif).FirstOrDefault(); + showDay = true; + } + } +#endif + } + #endregion Protected Methods + private RegAttivitaModel? CurrRA { get; set; } = null; + #region Private Fields private string lastAction = ""; @@ -111,7 +143,7 @@ namespace GPW.CORE.Smart.Components /// /// /// - private RegAttivitaModel? currRec + private RegAttivitaModel? ClonedRA { get => MService.clonedRA; set => MService.clonedRA = value; @@ -212,7 +244,7 @@ namespace GPW.CORE.Smart.Components private async Task resetClone() { - currRec = null; + ClonedRA = null; await Task.Delay(1); } diff --git a/GPW.CORE.SMART/Components/RegAttEditor.razor b/GPW.CORE.SMART/Components/RegAttEditor.razor new file mode 100644 index 0000000..aa33d73 --- /dev/null +++ b/GPW.CORE.SMART/Components/RegAttEditor.razor @@ -0,0 +1,132 @@ + +
+
+
+
+

+ Modifica # + @if (currRecord != null) + { + @currRecord.IdxRa + } +

+
+
+
+ + + + + @foreach (var item in gruppiList) + { + + } + +
+
+
+
+
+ + +
+
+ +
+ + + + + @foreach (var item in projList) + { + + } + +
+ +
+ + + + + @foreach (var item in fasiList) + { + if (@item.EnableTime) + { + if (item.Attivo) + { + + } + else + { + + } + } + else + { + if (item.Attivo) + { + + } + else + { + + } + } + } + +
+
+ @if (currRecord != null) + { +
+ +
+
+
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+
+
+ +
+ + + + +
+
+ } +
+
+ +
+
+ diff --git a/GPW.CORE.SMART/Components/RegAttEditor.razor.cs b/GPW.CORE.SMART/Components/RegAttEditor.razor.cs new file mode 100644 index 0000000..9b729a1 --- /dev/null +++ b/GPW.CORE.SMART/Components/RegAttEditor.razor.cs @@ -0,0 +1,382 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using System.Net.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.Web.Virtualization; +using Microsoft.JSInterop; +using GPW.CORE.Comp; +using GPW.CORE.Data.DbModels; +using GPW.CORE.Smart; +using GPW.CORE.Smart.Components; +using GPW.CORE.Smart.Shared; +using GPW.CORE.Smart.Data; +using NLog; +using System.Diagnostics; + +namespace GPW.CORE.Smart.Components +{ + public partial class RegAttEditor + { + #region Protected Fields + + protected string _gruppoSel = ""; + protected int _idxProj = 0; + protected bool vetoUpd = false; + + #endregion Protected Fields + + #region Private Properties + + [Inject] + private MessageService AppMServ { get; set; } = null!; + + [Inject] + private IJSRuntime JSRuntime { get; set; } = null!; + + private bool VetoInsert + { + get => CDService.VetoInsert; + } + + #endregion Private Properties + + #region Protected Properties + + protected List fasiList { get; set; } = new List(); + + [Inject] + protected CoreSmartDataService CDService { get; set; } = null!; + + protected List gruppiList { get; set; } = new List(); + protected List projList { get; set; } = new List(); + + #endregion Protected Properties + + #region Public Properties + + + private static Logger Log = LogManager.GetCurrentClassLogger(); + + [Parameter] + public RegAttivitaModel? currRecord + { + get + { + return AppMServ.recordRA; + } + set + { + AppMServ.recordRA = value; + vetoUpd = true; + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + + var updFase = Task.Run(async () => + { + if (value != null) + { + if (value.FasiNav != null && value.FasiNav.ProgettoNav != null) + { + gruppoSel = value.FasiNav.ProgettoNav.Gruppo; + idxProj = value.FasiNav.IdxProgetto != null ? (int)value.FasiNav.IdxProgetto : 0; + } + else + { + + // effettuo selezione manuale... + var currFase = await CDService.AnagFasiByKey(value.IdxFase); + if (currFase != null) + { + if (currFase.ProgettoNav != null) + { + gruppoSel = currFase.ProgettoNav.Gruppo; + } + idxProj = currFase.IdxProgetto != null ? (int)currFase.IdxProgetto : 0; + } + value.IdxFase = 0; + } + } + await ReloadData(false, false); + }); + updFase.Wait(); + + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + + Log.Trace($"Test esecuzione sel cascata: {ts.TotalMilliseconds} ms"); + vetoUpd = false; + } + } + + public string gruppoSel + { + get + { + return _gruppoSel; + } + set + { + _gruppoSel = value; + if (!vetoUpd) + { + var pUpd = Task.Run(async () => await ReloadData(true, true)); + pUpd.Wait(); + } + } + } + + protected int idxFase + { + get + { + int answ = 0; + if (currRecord != null) + { + answ = currRecord.IdxFase; + } + return answ; + } + set + { + if (currRecord != null) + { + currRecord.IdxFase = value; + } + } + } + + public int idxProj + { + get + { + return _idxProj; + } + set + { + _idxProj = value; + if (!vetoUpd) + { + var pUpd = Task.Run(async () => await ReloadData(false, true)); + pUpd.Wait(); + } + } + } + + [Parameter] + public EventCallback ItemReset { get; set; } + + [Parameter] + public EventCallback ItemUpdated { get; set; } + + #endregion Public Properties + + #region Private Methods + + private void arrotondaMinuti() + { + if (currRecord != null) + { + // arrotondo ai 5 min... + currRecord.Inizio = CORE.Data.Utils.DateRounded(currRecord.Inizio.AddMinutes(2), 5, true); + // arrotondo ai 5 min... + currRecord.Fine = CORE.Data.Utils.DateRounded(currRecord.Fine.AddMinutes(2), 5, true); + checkCoerenzaDate(true); + } + } + + /// + /// Verifico coerenza date (inizio < fine) + /// + /// indica se la data modificata sia l'inizio + private void checkCoerenzaDate(bool modInizio) + { + bool inError = false; + // verifica presenza errori + if (currRecord != null) + { + inError = currRecord.Inizio >= currRecord.Fine; + if (inError) + { + // se ho mod inizio --> tengo ferma fine, inizio 1/2 h prima + if (modInizio) + { + currRecord.Inizio = currRecord.Fine.AddMinutes(-30); + } + else + { + currRecord.Fine = currRecord.Inizio.AddMinutes(30); + } + } + } + } + + #endregion Private Methods + + #region Protected Methods + + /// + /// Salvo in MService record clonato + /// + protected async void DoClone() + { + if (currRecord != null) + { + var newData = currRecord.Clone(); + newData.IdxRa = 0; + AppMServ.clonedRA = newData; + await ItemReset.InvokeAsync(true); + } + } + + /// + /// Elimino record + /// + protected async void DoDelete() + { + // chiedo verifica + if (!await JSRuntime.InvokeAsync("confirm", "Sicuro di voler eliminare il record selezionato??")) + return; + + if (currRecord != null) + { + // aggiorno + await CDService.RegAttDelete(currRecord); + // registro fatto + await ItemUpdated.InvokeAsync(true); + } + } + + /// + /// Indico item selezionato + /// + protected async void DoReset() + { + await ItemReset.InvokeAsync(true); + } + + /// + /// Aggiorno e riporto update + /// + protected async void DoUpdate() + { + arrotondaMinuti(); + // aggiorno + if (currRecord != null) + { + // controllo IdxDipendente + if (currRecord.IdxDipendente == 0) + { + currRecord.IdxDipendente = AppMServ.IdxDipendente; + } + + // prearo lista inserimento + List data2ins = new List(); + + // se inizio/fine stesso giorno processo + if (currRecord.Fine.Date.Equals(currRecord.Inizio.Date)) + { + data2ins.Add(currRecord); + } + // in caso di eventuale supero della mezzanotte --> chiedo se si vuole split su 2 attività... + else + { + bool doSplit = await JSRuntime.InvokeAsync("confirm", "L'attività si protrae su più giorni, vuoi suddividere in record per ogni giornata?"); + // chiedo verifica x splittare in N+1 periodi (N = giorni) + if (doSplit) + { + RegAttivitaModel currRA = currRecord.Clone() as RegAttivitaModel; + // primo record è inizio --> mezzanote + DateTime lastInizio = currRA.Inizio; + DateTime lastFine = currRA.Inizio.Date.AddDays(1); + currRA.Fine = lastFine; + data2ins.Add(currRA); + // calcolo i gg + int numGG = currRecord.Fine.Date.Subtract(currRecord.Inizio.Date).Days; + // ciclo + for (int i = 0; i < numGG; i++) + { + // splitto... inizio = fine + 1 sec... + currRA = currRecord.Clone() as RegAttivitaModel; + currRA.Inizio = lastFine; + // porto avanti ultima fine... + lastFine = lastFine.AddDays(1); + currRA.Fine = currRA.Fine > lastFine ? lastFine : currRA.Fine; + data2ins.Add(currRA); + } + } + else + { + data2ins.Add(currRecord); + } + } + // ciclo x inserire + foreach (var item in data2ins) + { + await CDService.RegAttUpdate(item); + } + } + // resetto clone e record corrente... + AppMServ.clonedRA = null; + currRecord = null; + // registro fatto + await ItemUpdated.InvokeAsync(true); + } + + protected async Task ReloadData(bool resetProj, bool resetFase) + { + gruppiList = await CDService.AnagGruppiUser(AppMServ.IdxDipendente); + var allProj = await CDService.AnagProjAll(); + projList = allProj + .Where(x => x.Attivo == true && x.Gruppo == gruppoSel) + .OrderBy(x => x.ClienteNav.RagSociale) + .ThenBy(x => x.NomeProj) + .ToList(); + if (resetProj) + { + if (projList.Count > 0) + { + idxProj = projList[0].IdxProgetto; + } + } + var fasiProj = await CDService.AnagFasiByProj(idxProj); + if (fasiProj != null) + { + fasiList = fasiProj; + } + else + { + fasiList = new List(); + } + if (resetFase) + { + // se ho 2+ risultati --> il primo è titolo faccio il secondo + if (fasiList.Count > 1) + { + var currData = fasiList.Skip(1).FirstOrDefault(); + if (currData != null) + { + idxFase = currData.IdxFase; + } + } + // altrimenti primo... + else + { + var currData = fasiList.FirstOrDefault(); + if (currData != null) + { + idxFase = currData.IdxFase; + } + } + } + } + + #endregion Protected Methods + } +} \ No newline at end of file diff --git a/GPW.CORE.SMART/Data/CoreSmartDataService.cs b/GPW.CORE.SMART/Data/CoreSmartDataService.cs index c9d10ea..dd76c4e 100644 --- a/GPW.CORE.SMART/Data/CoreSmartDataService.cs +++ b/GPW.CORE.SMART/Data/CoreSmartDataService.cs @@ -62,6 +62,379 @@ namespace GPW.CORE.Smart.Data #region Public Methods + /// + /// Elenco AKV + /// + public async Task?> AKVList() + { + string source = "DB"; + + List? dbResult = new List(); + // cerco da cache + string currKey = rKeyAKV; + 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.AnagKeyValGetAll(); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AKVList | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Recupera l'elenco fasi (tutte) + /// + /// + public async Task> AnagClientiAll() + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyCliAll}"; + + 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.AnagClientiAll(); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagClientiAll | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Recupera l'elenco fasi (tutte) + /// + /// + public async Task> AnagFasiAll() + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyFasiAll}"; + 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.AnagFasiAll(false); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagFasiAll | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Recupera info x una specifica FASE + /// + /// + public async Task AnagFasiByKey(int idxFase) + { + string source = "DB"; + AnagFasiModel? dbResult = new AnagFasiModel(); + // cerco "secca "in redis... + string currKey = $"{rKeyFasiSearch}:{idxFase}"; + 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 AnagFasiModel(); + } + else + { + dbResult = tempResult; + } + } + else + { + dbResult = dbController.AnagFasiByKey(idxFase); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagFasiSearch | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Elenco fasi dato progetto + /// + /// + /// + public async Task?> AnagFasiByProj(int idxProj) + { + string source = "DB"; + List? dbResult = new List(); + // cerco "secca "in redis... + string currKey = $"{rKeyFasiByProj}:{idxProj}"; + 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.AnagFasiByProj(idxProj); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagFasiByProj | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Elenco Giustificativi + /// + /// + public async Task?> AnagGiust() + { + string source = "DB"; + List? dbResult = new List(); + // cerco "secca "in redis... + string currKey = $"{rKeyGiust}"; + 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.AnagGiust(); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagGiust | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Recupera l'elenco gruppi abilitati x il dipendente + /// + /// + /// + public async Task> AnagGruppiUser(int idxDipendente) + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyGrpUser}:{idxDipendente}"; + 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.AnagGruppiUser(idxDipendente); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagGruppiUser | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Dati orario del dipendente indicato + /// + /// + /// + public async Task AnagOrarioByDip(int idxDip) + { + string source = "DB"; + AnagOrariModel? dbResult = new AnagOrariModel(); + string currKey = $"{rKeyAnOrDip}:{idxDip}"; + 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 AnagOrariModel(); + } + else + { + dbResult = tempResult; + } + } + else + { + dbResult = dbController.AnagOrarioByDip(idxDip); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new AnagOrariModel(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagOrarioByDip | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + + /// + /// Recupera l'elenco progetti (tutti) + /// + /// + public async Task> AnagProjAll() + { + string source = "DB"; + List? dbResult = new List(); + string currKey = $"{rKeyProjAll}"; + 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.AnagProjAll(false); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"AnagProjAll | {source} in: {ts.TotalMilliseconds} ms"); + return dbResult; + } + /// /// Recupera l'elenco dei controlli VC19 nel periodo indicato x dipendente /// diff --git a/GPW.CORE.SMART/Data/LicenseService.cs b/GPW.CORE.SMART/Data/LicenseService.cs new file mode 100644 index 0000000..01de50f --- /dev/null +++ b/GPW.CORE.SMART/Data/LicenseService.cs @@ -0,0 +1,296 @@ +using GPW.CORE.Data; +using GPW.CORE.Data.DbModels; +using Newtonsoft.Json; +using RestSharp; +using StackExchange.Redis; +using System.Web; + +namespace GPW.CORE.Smart.Data +{ + /// + /// Servizi e dati condivisi a livello applicazione + /// + public class LicenseService + { + #region Public Constructors + + /// + /// Init classe + /// + /// + /// + /// + public LicenseService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult) + { + _logger = logger; + _configuration = configuration; + // Conf cache + redisConn = redisConnMult;// ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis")); + redisDb = this.redisConn.GetDatabase(); + } + + #endregion Public Constructors + + #region Public Events + + public event Action EA_InfoUpdated = null!; + + #endregion Public Events + + #region Public Properties + + public List ActivList { get; set; } = new List(); + public List AKVList { get; set; } = new List(); + public string Applicazione { get; set; } = ""; + + public bool HasActivData + { + get + { + bool answ = ValidData; + // se ok controllo che ci siano attivazioni + if (answ) + { + // provo classe locale... + answ = ActivList != null && ActivList.Count > 0; + // se non le avessi carico! + if (!answ) + { + var pUpd = Task.Run(async () => + { + ActivList = await ActivListCache(); + }); + pUpd.Wait(); + } + } + return answ; + } + } + + public DateTime infoExpiry { get; set; } = DateTime.Today.AddDays(1); + public string Installazione { get; set; } = ""; + public string MasterKey { get; set; } = ""; + + public bool ValidData + { + get + { + // controllo valori string base + bool checkData = !string.IsNullOrEmpty(Installazione) && !string.IsNullOrEmpty(Applicazione) && !string.IsNullOrEmpty(MasterKey); + return checkData; + } + } + + #endregion Public Properties + + #region Public Methods + + public async Task> ActivListCache() + { + List dbResult = new List(); + string cacheKey = $"{rKeyAttByLic}:{MasterKey}"; + string rawData = await getRSV(cacheKey); + if (!string.IsNullOrEmpty(rawData)) + { + var cacheRes = JsonConvert.DeserializeObject?>(rawData); + if (cacheRes != null) + { + dbResult = cacheRes; + } + } + + return await Task.FromResult(dbResult); + } + + /// + /// Init della classe con variabili di base da Redis/DB + /// + public bool InitAkv() + { + bool fatto = false; + Installazione = getAVKStr("installazione"); + MasterKey = getAVKStr(Installazione); + fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}"); + return fatto; + } + + /// + /// Init della classe con variabili di base da Redis/DB + /// + public async Task RefreshLicense() + { + bool fatto = false; + var onlineAct = await OnlineActivationList(); + if (onlineAct != null) + { + if (onlineAct.Count > 0) + { + // scadenza info a 15 gg... + int numDays = 15; + infoExpiry = DateTime.Now.AddDays(numDays); + ActivList = onlineAct; + fatto = await setActivList(onlineAct, numDays); + } + } + await Task.Delay(1); + return fatto; + } + + public async Task setActivList(List newActList, int numDays) + { + bool fatto = false; + string cacheKey = $"{rKeyAttByLic}:{MasterKey}"; + var rawData = JsonConvert.SerializeObject(newActList); + await setRSV(cacheKey, rawData, numDays * 24); + fatto = true; + if (EA_InfoUpdated != null) + { + EA_InfoUpdated?.Invoke(); + } + return fatto; + } + + #endregion Public Methods + + #region Protected Fields + + /// + /// Chiave redis x attivazioni della licenza + /// + protected const string rKeyAttByLic = "LongCache:AttByLic"; + + protected Random rnd = new Random(); + + #endregion Protected Fields + + #region Protected Methods + + /// + /// Cerca di recuperare valore string da elenco AKV + /// + /// Chiave AKV richiesta + /// + protected string getAVKStr(string varReq) + { + string answ = ""; + if (AKVList != null && AKVList.Count > 0) + { + var currRec = AKVList.Where(x => x.NomeVar == varReq).FirstOrDefault(); + if (currRec != null) + { + answ = $"{currRec.ValString}"; + } + } + return answ; + } + + /// + /// Recupero chiave da redis + /// + /// + /// + protected async Task getRSV(string rKey) + { + string answ = ""; + string? rawData = await redisDb.StringGetAsync(rKey); + if (!string.IsNullOrEmpty(rawData)) + { + answ = rawData; + } + return answ; + } + + /// + /// Salvataggio chiave in redis + /// + /// + /// + /// + /// + protected async Task setRSV(string rKey, string rVal, int cacheMult) + { + bool fatto = false; + fatto = await redisDb.StringSetAsync(rKey, rVal, getCache(cacheMult)); + return fatto; + } + + /// + /// Salvataggio chiave INT in redis + /// + /// + /// + /// + /// + protected async Task setRSV(string rKey, int rValInt, int cacheMult) + { + return await setRSV(rKey, $"{rValInt}", cacheMult); + } + + #endregion Protected Methods + + #region Private Fields + + private static IConfiguration? _configuration; + private static ILogger? _logger; + + /// + /// URL dell'API x chiamate gestione licenze + /// + private static string apiUrl = "https://liman.egalware.com/ELM.API/"; + + /// + /// 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 Methods + + /// + /// Durata cache da moltiplicatore (orario) + perturbazione percentuale (+/-10%) + /// + private TimeSpan getCache(int cacheMult) + { + return TimeSpan.FromHours((double)cacheMult * rnd.Next(900, 1100) / 1000); + } + + /// + /// Elenco attivazioni attuali + /// + private async Task?> OnlineActivationList() + { + List? answ = new List(); + // cerco online + RestClient client = new RestClient(apiUrl); + //client.Authenticator = new HttpBasicAuthenticator("username", "password"); + string MKeyEnc = HttpUtility.UrlEncode(MasterKey); + var request = new RestRequest($"/api/attivazioni/?chiave={MKeyEnc}", Method.Get); + var response = await client.GetAsync(request); + // controllo risposta + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + // salvo in redis contenuto serializzato + string rawData = $"{response.Content}"; + answ = JsonConvert.DeserializeObject?>(rawData); + } + return await Task.FromResult(answ); + } + + private void ReportUpdated() + { + if (EA_InfoUpdated != null) + { + EA_InfoUpdated?.Invoke(); + } + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/GPW.CORE.SMART/GPW.CORE.SMART.csproj b/GPW.CORE.SMART/GPW.CORE.SMART.csproj index 35eb1d1..6af9a23 100644 --- a/GPW.CORE.SMART/GPW.CORE.SMART.csproj +++ b/GPW.CORE.SMART/GPW.CORE.SMART.csproj @@ -21,6 +21,7 @@ + diff --git a/GPW.CORE.SMART/Program.cs b/GPW.CORE.SMART/Program.cs index a9c8726..2c047c6 100644 --- a/GPW.CORE.SMART/Program.cs +++ b/GPW.CORE.SMART/Program.cs @@ -34,6 +34,7 @@ builder.Services.Configure(options => builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(redisMultiplexer); builder.Services.AddScoped(); diff --git a/GPW.CORE.SMART/appsettings.json b/GPW.CORE.SMART/appsettings.json index ef0e3ac..d39c0f7 100644 --- a/GPW.CORE.SMART/appsettings.json +++ b/GPW.CORE.SMART/appsettings.json @@ -6,6 +6,7 @@ } }, "AllowedHosts": "*", + "CodApp": "GPW", "ConnectionStrings": { "Redis": "localhost:6379,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false", "GPW.DB": "Server=SQLSTEAM;Database=GPW; User ID=sa;Password=keyhammer; integrated security=False; MultipleActiveResultSets=True; App=GPW.CORE.WRKLOG;" @@ -26,5 +27,6 @@ "UrlLinkInt": "https://iis01.egalware.com/GPW/CORE.Smart/", //"LinkInt": "https://iis03.egalware.com/GPW/CORE.Smart/", "UrlLinkExt": "https://iis03.egalware.com/GPW/CORE.Smart/" - } + }, + "VetoIns": "true" }