This commit is contained in:
zaccaria.majid
2023-01-18 11:57:52 +01:00
10 changed files with 1344 additions and 15 deletions
+100
View File
@@ -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
/// <summary>
/// Verifica informazioni condivise applicazione
/// </summary>
/// <returns></returns>
protected async Task<bool> 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;
}
/// <summary>
/// Verifica informazioni condivise applicazione
/// </summary>
/// <returns></returns>
protected async Task<bool> 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<AnagKeyValueModel>? rawAkvList = await CDService.AKVList();
if (rawAkvList != null)
{
LicServ.AKVList = rawAkvList;
allOk = LicServ.InitAkv();
}
}
await Task.Delay(1);
return allOk;
}
/// <summary>
/// Verifica info specifiche utente
/// </summary>
/// <returns></returns>
protected async Task<bool> 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
+14 -4
View File
@@ -1,4 +1,4 @@
<ProjectsList ListRA="ListProgetti" EnableAdmin="false" EnableEdit="true" ReqUpdate="DoUpdate" ItemCloned="SaveCloned" ActionPaste="ExecPaste"></ProjectsList>
<ProjectsList ListRA="ListProgetti" EnableAdmin="false" EnableEdit="true" ReqUpdate="DoUpdate" ItemCloned="SaveCloned" ItemSelected="EditItem" ActionPaste="ExecPaste"></ProjectsList>
<div class="toast-container py-2 top-0 start-50 translate-middle-x">
<div class="toast bg-warning fade @toastCss" id="LiveToast2" role="alert">
@@ -9,10 +9,20 @@
<button type="button" class="btn-close" aria-label="Close" @onclick="() => resetClone()"></button>
</div>
<div class="toast-body text-dark small">
@if (currRec != null)
@if (ClonedRA != null)
{
<SingleRaDisplay CurrItem="@currRec" EnableAction="false"></SingleRaDisplay>
<SingleRaDisplay CurrItem="@ClonedRA" EnableAction="false"></SingleRaDisplay>
}
</div>
</div>
</div>
</div>
@if (CurrRA != null)
{
<dialog class="modal fade show p-0" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" aria-modal="true" role="dialog">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<RegAttEditor currRecord="@CurrRA" ItemReset="UpdRegAttData" ItemUpdated="UpdRegAttData"></RegAttEditor>
</div>
</div>
</dialog>
}
+42 -10
View File
@@ -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
/// </summary>
/// <param name="clonedRec"></param>
/// <returns></returns>
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);
}
@@ -0,0 +1,132 @@
<EditForm Model="@currRecord">
<div class="card">
<div class="card-header bg-info">
<div class="row">
<div class="col-12">
<h4>
Modifica #
@if (currRecord != null)
{
<b>@currRecord.IdxRa</b>
}
</h4>
</div>
<div class="col-12 text-end">
<div class="input-group input-group-sm">
<span class="input-group-text">
<i class="far fa-object-group"></i>
</span>
<InputSelect @bind-Value="@gruppoSel" class="form-select" title="Gruppo">
@foreach (var item in gruppiList)
{
<option value="@item.Gruppo">@item.DescrGruppo</option>
}
</InputSelect>
</div>
</div>
</div>
</div>
<div class="card-body">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row">
<div class="col-12 col-xl-6 text-end">
<label class="small">Progetto</label>
<div class="input-group input-group-sm" title="Progetto">
<span class="input-group-text" style="width:3em;">
<i class="fas fa-project-diagram"></i>
</span>
<InputSelect @bind-Value="@idxProj" class="form-select bg-light text-start" title="Progetto">
@foreach (var item in projList)
{
<option value="@item.IdxProgetto">@item.ClienteNav.RagSociale | @item.NomeProj</option>
}
</InputSelect>
</div>
<label class="small">Fase</label>
<div class="input-group input-group-sm" title="Fase">
<span class="input-group-text" style="width:3em;">
<i class="fas fa-tasks"></i>
</span>
<InputSelect @bind-Value="@idxFase" class="form-select bg-light text-start" title="Fase">
@foreach (var item in fasiList)
{
if (@item.EnableTime)
{
if (item.Attivo)
{
<option value="@item.IdxFase">@item.NomeFase</option>
}
else
{
<option value="@item.IdxFase" disabled="@VetoInsert" title="Fase Disattivata, non selezionabile">@item.NomeFase / DISATTIVA</option>
}
}
else
{
if (item.Attivo)
{
<option value="@item.IdxFase" disabled="true" class="bg-dark text-warning">[@item.NomeFase]</option>
}
else
{
<option value="@item.IdxFase" disabled="true" class="bg-dark text-warning" title="Fase Disattivata, non selezionabile">[@item.NomeFase / DISATTIVA]</option>
}
}
}
</InputSelect>
</div>
</div>
@if (currRecord != null)
{
<div class="col-12 col-xl-6 text-start">
<label class="small">Periodo (arr 5 min)</label>
<div class="d-flex justify-content-between">
<div class="px-0">
<div class="input-group input-group-sm">
<span class="input-group-text">
<i class="fas fa-play fa-xs"></i>
</span>
<input type="datetime-local" @bind="@currRecord.Inizio"></input>
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<div class="px-0">
<div class="input-group input-group-sm">
<span class="input-group-text">
<i class="fas fa-stop fa-xs"></i>
</span>
<input type="datetime-local" @bind="@currRecord.Fine"></input>
</div>
</div>
</div>
</div>
<div class="col-12 text-start">
<label class="small">Descrizione</label>
<div class="input-group input-group-sm">
<span class="input-group-text">
<i class="far fa-comment-alt"></i>
</span>
<InputTextArea id="idxRa" @bind-Value="@currRecord.Descrizione" class="form-control" title="Descrizione attività"></InputTextArea>
</div>
</div>
}
</div>
</div>
<div class="card-footer">
<div class="row">
<div class="col-6">
@if (AppMServ.PayloadOk)
{
<button type="button" class="btn w-100 btn-success" @onclick="DoUpdate"><i class="fas fa-check-circle"></i> Save</button>
}
</div>
<div class="col-6">
<button type="button" class="btn w-100 btn-warning" @onclick="DoReset"><i class="fas fa-ban"></i> Cancel</button>
</div>
</div>
</div>
</div>
</EditForm>
@@ -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<AnagFasiModel> fasiList { get; set; } = new List<AnagFasiModel>();
[Inject]
protected CoreSmartDataService CDService { get; set; } = null!;
protected List<AnagGruppiModel> gruppiList { get; set; } = new List<AnagGruppiModel>();
protected List<AnagProgettiModel> projList { get; set; } = new List<AnagProgettiModel>();
#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<bool> ItemReset { get; set; }
[Parameter]
public EventCallback<bool> 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);
}
}
/// <summary>
/// Verifico coerenza date (inizio < fine)
/// </summary>
/// <param name="modInizio">indica se la data modificata sia l'inizio</param>
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
/// <summary>
/// Salvo in MService record clonato
/// </summary>
protected async void DoClone()
{
if (currRecord != null)
{
var newData = currRecord.Clone();
newData.IdxRa = 0;
AppMServ.clonedRA = newData;
await ItemReset.InvokeAsync(true);
}
}
/// <summary>
/// Elimino record
/// </summary>
protected async void DoDelete()
{
// chiedo verifica
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare il record selezionato??"))
return;
if (currRecord != null)
{
// aggiorno
await CDService.RegAttDelete(currRecord);
// registro fatto
await ItemUpdated.InvokeAsync(true);
}
}
/// <summary>
/// Indico item selezionato
/// </summary>
protected async void DoReset()
{
await ItemReset.InvokeAsync(true);
}
/// <summary>
/// Aggiorno e riporto update
/// </summary>
protected async void DoUpdate()
{
arrotondaMinuti();
// aggiorno
if (currRecord != null)
{
// controllo IdxDipendente
if (currRecord.IdxDipendente == 0)
{
currRecord.IdxDipendente = AppMServ.IdxDipendente;
}
// prearo lista inserimento
List<RegAttivitaModel> data2ins = new List<RegAttivitaModel>();
// 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<bool>("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<AnagFasiModel>();
}
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
}
}
+373
View File
@@ -62,6 +62,379 @@ namespace GPW.CORE.Smart.Data
#region Public Methods
/// <summary>
/// Elenco AKV
/// </summary>
public async Task<List<AnagKeyValueModel>?> AKVList()
{
string source = "DB";
List<AnagKeyValueModel>? dbResult = new List<AnagKeyValueModel>();
// 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<List<AnagKeyValueModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagKeyValueModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagKeyValGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new List<AnagKeyValueModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AKVList | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco fasi (tutte)
/// </summary>
/// <returns></returns>
public async Task<List<AnagClientiModel>> AnagClientiAll()
{
string source = "DB";
List<AnagClientiModel>? dbResult = new List<AnagClientiModel>();
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<List<AnagClientiModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagClientiModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagClientiAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (dbResult == null)
{
dbResult = new List<AnagClientiModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagClientiAll | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco fasi (tutte)
/// </summary>
/// <returns></returns>
public async Task<List<AnagFasiModel>> AnagFasiAll()
{
string source = "DB";
List<AnagFasiModel>? dbResult = new List<AnagFasiModel>();
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<List<AnagFasiModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagFasiModel>();
}
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<AnagFasiModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagFasiAll | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera info x una specifica FASE
/// </summary>
/// <returns></returns>
public async Task<AnagFasiModel?> 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<AnagFasiModel>(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;
}
/// <summary>
/// Elenco fasi dato progetto
/// </summary>
/// <param name="idxProj"></param>
/// <returns></returns>
public async Task<List<AnagFasiModel>?> AnagFasiByProj(int idxProj)
{
string source = "DB";
List<AnagFasiModel>? dbResult = new List<AnagFasiModel>();
// 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<List<AnagFasiModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagFasiModel>();
}
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<AnagFasiModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagFasiByProj | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Elenco Giustificativi
/// </summary>
/// <returns></returns>
public async Task<List<AnagGiustModel>?> AnagGiust()
{
string source = "DB";
List<AnagGiustModel>? dbResult = new List<AnagGiustModel>();
// 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<List<AnagGiustModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagGiustModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagGiust();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (dbResult == null)
{
dbResult = new List<AnagGiustModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagGiust | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco gruppi abilitati x il dipendente
/// </summary>
/// <param name="idxDipendente"></param>
/// <returns></returns>
public async Task<List<AnagGruppiModel>> AnagGruppiUser(int idxDipendente)
{
string source = "DB";
List<AnagGruppiModel>? dbResult = new List<AnagGruppiModel>();
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<List<AnagGruppiModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagGruppiModel>();
}
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<AnagGruppiModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagGruppiUser | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Dati orario del dipendente indicato
/// </summary>
/// <param name="idxProj"></param>
/// <returns></returns>
public async Task<AnagOrariModel> 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<AnagOrariModel>(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;
}
/// <summary>
/// Recupera l'elenco progetti (tutti)
/// </summary>
/// <returns></returns>
public async Task<List<AnagProgettiModel>> AnagProjAll()
{
string source = "DB";
List<AnagProgettiModel>? dbResult = new List<AnagProgettiModel>();
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<List<AnagProgettiModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagProgettiModel>();
}
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<AnagProgettiModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagProjAll | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco dei controlli VC19 nel periodo indicato x dipendente
/// </summary>
+296
View File
@@ -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
{
/// <summary>
/// Servizi e dati condivisi a livello applicazione
/// </summary>
public class LicenseService
{
#region Public Constructors
/// <summary>
/// Init classe
/// </summary>
/// <param name="configuration"></param>
/// <param name="logger"></param>
/// <param name="distributedCache"></param>
public LicenseService(IConfiguration configuration, ILogger<LicenseService> 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<LiManObj.AttivazioneDTO> ActivList { get; set; } = new List<LiManObj.AttivazioneDTO>();
public List<AnagKeyValueModel> AKVList { get; set; } = new List<AnagKeyValueModel>();
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<List<LiManObj.AttivazioneDTO>> ActivListCache()
{
List<LiManObj.AttivazioneDTO> dbResult = new List<LiManObj.AttivazioneDTO>();
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
string rawData = await getRSV(cacheKey);
if (!string.IsNullOrEmpty(rawData))
{
var cacheRes = JsonConvert.DeserializeObject<List<LiManObj.AttivazioneDTO>?>(rawData);
if (cacheRes != null)
{
dbResult = cacheRes;
}
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// </summary>
public bool InitAkv()
{
bool fatto = false;
Installazione = getAVKStr("installazione");
MasterKey = getAVKStr(Installazione);
fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}");
return fatto;
}
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// </summary>
public async Task<bool> 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<bool> setActivList(List<LiManObj.AttivazioneDTO> 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
/// <summary>
/// Chiave redis x attivazioni della licenza
/// </summary>
protected const string rKeyAttByLic = "LongCache:AttByLic";
protected Random rnd = new Random();
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Cerca di recuperare valore string da elenco AKV
/// </summary>
/// <param name="varReq">Chiave AKV richiesta</param>
/// <returns></returns>
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;
}
/// <summary>
/// Recupero chiave da redis
/// </summary>
/// <param name="rKey"></param>
/// <returns></returns>
protected async Task<string> getRSV(string rKey)
{
string answ = "";
string? rawData = await redisDb.StringGetAsync(rKey);
if (!string.IsNullOrEmpty(rawData))
{
answ = rawData;
}
return answ;
}
/// <summary>
/// Salvataggio chiave in redis
/// </summary>
/// <param name="rKey"></param>
/// <param name="rVal"></param>
/// <param name="cacheMult"></param>
/// <returns></returns>
protected async Task<bool> setRSV(string rKey, string rVal, int cacheMult)
{
bool fatto = false;
fatto = await redisDb.StringSetAsync(rKey, rVal, getCache(cacheMult));
return fatto;
}
/// <summary>
/// Salvataggio chiave INT in redis
/// </summary>
/// <param name="rKey"></param>
/// <param name="rValInt"></param>
/// <param name="cacheMult"></param>
/// <returns></returns>
protected async Task<bool> 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<LicenseService>? _logger;
/// <summary>
/// URL dell'API x chiamate gestione licenze
/// </summary>
private static string apiUrl = "https://liman.egalware.com/ELM.API/";
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
private IConnectionMultiplexer redisConn;
//ISubscriber sub = redis.GetSubscriber();
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// </summary>
private IDatabase redisDb = null!;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Durata cache da moltiplicatore (orario) + perturbazione percentuale (+/-10%)
/// </summary>
private TimeSpan getCache(int cacheMult)
{
return TimeSpan.FromHours((double)cacheMult * rnd.Next(900, 1100) / 1000);
}
/// <summary>
/// Elenco attivazioni attuali
/// </summary>
private async Task<List<LiManObj.AttivazioneDTO>?> OnlineActivationList()
{
List<LiManObj.AttivazioneDTO>? answ = new List<LiManObj.AttivazioneDTO>();
// 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<List<LiManObj.AttivazioneDTO>?>(rawData);
}
return await Task.FromResult(answ);
}
private void ReportUpdated()
{
if (EA_InfoUpdated != null)
{
EA_InfoUpdated?.Invoke();
}
}
#endregion Private Methods
}
}
+1
View File
@@ -21,6 +21,7 @@
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.2.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
<PackageReference Include="RestSharp" Version="108.0.1" />
<PackageReference Include="StackExchange.Redis" Version="2.6.86" />
</ItemGroup>
+1
View File
@@ -34,6 +34,7 @@ builder.Services.Configure<MailKitEmailSenderOptions>(options =>
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<CoreSmartDataService>();
builder.Services.AddSingleton<LicenseService>();
builder.Services.AddSingleton<IConnectionMultiplexer>(redisMultiplexer);
builder.Services.AddScoped<MessageService>();
+3 -1
View File
@@ -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"
}