diff --git a/EgwCoreLib.Lux.Data/Services/ReqIndexService.cs b/EgwCoreLib.Lux.Data/Services/ReqIndexService.cs index c6d670c2..06ad7f2a 100644 --- a/EgwCoreLib.Lux.Data/Services/ReqIndexService.cs +++ b/EgwCoreLib.Lux.Data/Services/ReqIndexService.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; + namespace EgwCoreLib.Lux.Data.Services { @@ -12,36 +14,50 @@ namespace EgwCoreLib.Lux.Data.Services { private readonly IDatabase _db; private readonly TimeSpan _retention; + private readonly Random _rnd = new Random(); + private readonly string _base; - /// - /// Init classe gestione - /// - /// - /// - public ReqIndexService(ConnectionMultiplexer redis, TimeSpan retention) + public ReqIndexService(IConnectionMultiplexer redis, TimeSpan retention, string redisBaseKey) { _db = redis.GetDatabase(); - _retention = retention; // es. TimeSpan.FromDays(90) + _retention = retention; + _base = redisBaseKey.EndsWith(":") ? redisBaseKey : redisBaseKey + ":"; } - private string GetRequestKey(string id) => $"request:{id}"; - private string GetSortedSetKey(string environment, string tipo) => $"requests:{environment}:{tipo}"; - private string GetUidSetKey(string uid) => $"uid:{uid}:requests"; + private string Key(string suffix) => _base + suffix; + + private string GetRequestKey(string id) => Key($"request:{id}"); + private string GetSortedSetKey(string env, string tipo) => Key($"requests:{env}:{tipo}"); + private string GetUidSetKey(string uid) => Key($"uid:{uid}:requests"); + private string GetCombinationsKey() => Key("req:combinations"); + + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } // --------------------------------------------------------- // ✅ 1. Creazione nuova richiesta // --------------------------------------------------------- - public string AddRequest(string environment, string uid, string tipo) + public async Task AddRequestAsync(string environment, string uid, string tipo) { - var id = Guid.NewGuid().ToString(); + var id = GenerateId(); var processStart = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var hashKey = GetRequestKey(id); var setKey = GetSortedSetKey(environment, tipo); var uidKey = GetUidSetKey(uid); + var combKey = GetCombinationsKey(); - // Hash con i dettagli - _db.HashSet(hashKey, new HashEntry[] + var batch = _db.CreateBatch(); + + // Hash + batch.HashSetAsync(hashKey, new HashEntry[] { new HashEntry("processStart", processStart), new HashEntry("UID", uid), @@ -49,11 +65,24 @@ namespace EgwCoreLib.Lux.Data.Services new HashEntry("environment", environment) }); - // SortedSet per ordinamento temporale - _db.SortedSetAdd(setKey, id, processStart); + // SortedSet + batch.SortedSetAddAsync(setKey, id, processStart); - // Set per indicizzazione per UID - _db.SetAdd(uidKey, id); + // UID index + batch.SetAddAsync(uidKey, id); + + // Registra combinazione environment|tipo + string comb = $"{environment}|{tipo}"; + batch.SetAddAsync(combKey, comb); + + // Statistiche richieste + string minuteKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHHmm}"); + string hourKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHH}"); + + batch.StringIncrementAsync(minuteKey); + batch.StringIncrementAsync(hourKey); + + batch.Execute(); return id; } @@ -61,71 +90,136 @@ namespace EgwCoreLib.Lux.Data.Services // --------------------------------------------------------- // ✅ 2. Aggiornamento richiesta esistente // --------------------------------------------------------- - public void CompleteRequest(string id) + public async Task CompleteRequestAsync(string id) { var hashKey = GetRequestKey(id); var processEnd = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - var processStartValue = _db.HashGet(hashKey, "processStart"); - if (processStartValue.IsNull) return; // richiesta inesistente + var processStartValue = await _db.HashGetAsync(hashKey, "processStart"); + if (processStartValue.IsNull) return; var processStart = (long)processStartValue; var elapsed = processEnd - processStart; - _db.HashSet(hashKey, new HashEntry[] + var batch = _db.CreateBatch(); + + batch.HashSetAsync(hashKey, new HashEntry[] { new HashEntry("processEnd", processEnd), new HashEntry("processElapsed", elapsed) }); + + // Statistiche processing + string hourKeySum = Key($"stats:processing:sum:{DateTime.UtcNow:yyyyMMddHH}"); + string hourKeyMax = Key($"stats:processing:max:{DateTime.UtcNow:yyyyMMddHH}"); + + batch.StringIncrementAsync(hourKeySum, elapsed); + + // Max non atomico, ma sufficiente per statistiche + var currentMax = await _db.StringGetAsync(hourKeyMax); + if (currentMax.IsNull || long.Parse(currentMax) < elapsed) + batch.StringSetAsync(hourKeyMax, elapsed); + + batch.Execute(); } // --------------------------------------------------------- // ✅ 3. Recupero richieste per UID // --------------------------------------------------------- - public IEnumerable GetRequestsByUid(string uid) + public async Task> GetRequestsByUidAsync(string uid) { var uidKey = GetUidSetKey(uid); - return _db.SetMembers(uidKey).Select(x => x.ToString()); + var members = await _db.SetMembersAsync(uidKey); + return members.Select(x => x.ToString()); } // --------------------------------------------------------- - // ✅ 4. Pulizia periodica + // ✅ 4. Recupero combinazioni environment/tipo // --------------------------------------------------------- - public void CleanupOldRequests(string environment, string tipo) + public async Task> GetCombinationsAsync() + { + var members = await _db.SetMembersAsync(GetCombinationsKey()); + return members + .Select(x => x.ToString().Split('|')) + .Select(a => (a[0], a[1])); + } + + // --------------------------------------------------------- + // ✅ 5. Cleanup periodico + // --------------------------------------------------------- + public async Task CleanupOldRequestsAsync(string environment, string tipo) { var cutoff = DateTimeOffset.UtcNow.Add(-_retention).ToUnixTimeSeconds(); var setKey = GetSortedSetKey(environment, tipo); - // Recupera gli ID troppo vecchi - var oldIds = _db.SortedSetRangeByScore(setKey, stop: cutoff); + var oldIds = await _db.SortedSetRangeByScoreAsync(setKey, stop: cutoff); + if (oldIds.Length == 0) return; + + var batch = _db.CreateBatch(); foreach (var id in oldIds) { var idStr = id.ToString(); var hashKey = GetRequestKey(idStr); - // Recupera UID per rimuovere l'indice secondario - var uid = _db.HashGet(hashKey, "UID"); + var uid = await _db.HashGetAsync(hashKey, "UID"); if (!uid.IsNull) { var uidKey = GetUidSetKey(uid); - _db.SetRemove(uidKey, idStr); + batch.SetRemoveAsync(uidKey, idStr); - // Se il Set UID è vuoto → elimina la chiave - if (_db.SetLength(uidKey) == 0) - _db.KeyDelete(uidKey); + batch.SetLengthAsync(uidKey).ContinueWith(t => + { + if (t.Result == 0) + _db.KeyDelete(uidKey); + }); } - // Elimina l'hash della richiesta - _db.KeyDelete(hashKey); + batch.KeyDeleteAsync(hashKey); } - // Rimuove gli ID dal SortedSet - _db.SortedSetRemoveRangeByScore(setKey, double.NegativeInfinity, cutoff); + batch.SortedSetRemoveRangeByScoreAsync(setKey, double.NegativeInfinity, cutoff); - // Se il SortedSet è vuoto → elimina la chiave - if (_db.SortedSetLength(setKey) == 0) - _db.KeyDelete(setKey); + batch.SortedSetLengthAsync(setKey).ContinueWith(t => + { + if (t.Result == 0) + _db.KeyDelete(setKey); + }); + + batch.Execute(); + } + + // --------------------------------------------------------- + // ✅ 6. Statistiche aggregate + // --------------------------------------------------------- + public async Task GetStatsAsync() + { + string minuteKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHHmm}"); + string hourKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHH}"); + string hourSumKey = Key($"stats:processing:sum:{DateTime.UtcNow:yyyyMMddHH}"); + string hourMaxKey = Key($"stats:processing:max:{DateTime.UtcNow:yyyyMMddHH}"); + + var minuteCount = await _db.StringGetAsync(minuteKey); + var hourCount = await _db.StringGetAsync(hourKey); + var sum = await _db.StringGetAsync(hourSumKey); + var max = await _db.StringGetAsync(hourMaxKey); + + long minCnt = minuteCount.IsNull ? 0 : (long)minuteCount; + long hourCnt = hourCount.IsNull ? 0 : (long)hourCount; + long sumVal = sum.IsNull ? 0 : (long)sum; + long maxVal = max.IsNull ? 0 : (long)max; + + double avg = hourCnt > 0 ? (double)sumVal / hourCnt : 0; + + return new + { + RequestsLastMinute = minCnt, + RequestsLastHour = hourCnt, + ProcessingAvgLastHour = avg, + ProcessingMaxLastHour = maxVal + }; } } + + } \ No newline at end of file diff --git a/Lux.API/Controllers/GenericController.cs b/Lux.API/Controllers/GenericController.cs index 447f33d6..bf605cb1 100644 --- a/Lux.API/Controllers/GenericController.cs +++ b/Lux.API/Controllers/GenericController.cs @@ -56,6 +56,11 @@ namespace Lux.API.Controllers { DictExec.Add("UID", id); } + if (!DictExec.ContainsKey("RUID")) + { + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + } int nId = 1; // da modificare con tipo richiesta... QuestionDTO currArgs = new QuestionDTO(nId, currReq.EnvType, DictExec); @@ -68,6 +73,17 @@ namespace Lux.API.Controllers return Ok(retVal); } + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + /// /// Chiamata POST: riceve Json in formato JWD serializzato, invia richiesta calcolo modo 2 (BOM) /// PUT: api/window/bom/00000000-0000-0000-0000-000000000000 @@ -92,6 +108,10 @@ namespace Lux.API.Controllers DictExec.Add("Mode", $"{(int)Enums.QuestionModes.BOM}"); // UID cablato x ora... DictExec.Add("UID", id); + + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + DictExec.Add("SerializedData", currSer); int nId = 1; // da modificare con tipo richiesta... diff --git a/Lux.API/Controllers/WindowController.cs b/Lux.API/Controllers/WindowController.cs index f2e048a9..7fbf3b2f 100644 --- a/Lux.API/Controllers/WindowController.cs +++ b/Lux.API/Controllers/WindowController.cs @@ -64,6 +64,9 @@ namespace Lux.API.Controllers DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}"); // UID cablato x ora... DictExec.Add("UID", id); + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + DictExec.Add("SerializedData", currJwd); int nId = 1; // da modificare con tipo richiesta... @@ -77,6 +80,17 @@ namespace Lux.API.Controllers return Ok(svgContent); } + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + /// /// Chiamata GET: invia richiesta modo 3 (HardwareModelList) che sarà poi salvata /// GET: api/window/hwlist @@ -106,6 +120,8 @@ namespace Lux.API.Controllers DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.HARDWARE}"); // da rivedere? DictExec.Add("UID", "HW.AGB"); + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionHwSubModes.LIST}"); DictExec.Add("Manufacturer", $"{(int)Egw.Window.Data.Enums.HardwareManufacturers.AGB}"); int nId = 1; @@ -192,7 +208,7 @@ namespace Lux.API.Controllers id = id.Replace(".svg", ""); } // se contiene i caratteri casuali x forzare reload --> li levo - if(id.Contains("-")) + if (id.Contains("-")) { id = id.Substring(0, id.IndexOf("-")); } @@ -242,6 +258,8 @@ namespace Lux.API.Controllers DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}"); // UID cablato x ora... DictExec.Add("UID", id); + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); DictExec.Add("SerializedData", currJwd); int nId = 1; // da modificare con tipo richiesta... diff --git a/Lux.API/Lux.API.csproj b/Lux.API/Lux.API.csproj index 4e5192b5..c7542124 100644 --- a/Lux.API/Lux.API.csproj +++ b/Lux.API/Lux.API.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 0.9.2512.1212 + 0.9.2512.1216 diff --git a/Lux.UI/Components/Compo/Config/HardwareMan.razor.cs b/Lux.UI/Components/Compo/Config/HardwareMan.razor.cs index 98252058..95263f04 100644 --- a/Lux.UI/Components/Compo/Config/HardwareMan.razor.cs +++ b/Lux.UI/Components/Compo/Config/HardwareMan.razor.cs @@ -1,4 +1,4 @@ -using EgwCoreLib.Lux.Core.RestPayload; +using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.DbModel.Config; using EgwCoreLib.Lux.Data.DbModel.Sales; using EgwCoreLib.Lux.Data.Services; @@ -151,6 +151,8 @@ namespace Lux.UI.Components.Compo.Config DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.HARDWARE}"); // da rivedere? DictExec.Add("UID", reqUid); + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionHwSubModes.LIST}"); DictExec.Add("Manufacturer", $"{(int)Egw.Window.Data.Enums.HardwareManufacturers.AGB}"); CalcRequestDTO req = new CalcRequestDTO() @@ -166,6 +168,17 @@ namespace Lux.UI.Components.Compo.Config await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req); } + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + /// /// Ricevuto update da Calc x elenco HW: aggiorno! /// diff --git a/Lux.UI/Components/Compo/Config/ProfileMan.razor.cs b/Lux.UI/Components/Compo/Config/ProfileMan.razor.cs index 34178d61..4a7ab7eb 100644 --- a/Lux.UI/Components/Compo/Config/ProfileMan.razor.cs +++ b/Lux.UI/Components/Compo/Config/ProfileMan.razor.cs @@ -1,4 +1,4 @@ -using EgwCoreLib.Lux.Core.RestPayload; +using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.DbModel.Config; using EgwCoreLib.Lux.Data.Services; using Microsoft.AspNetCore.Components; @@ -148,6 +148,10 @@ namespace Lux.UI.Components.Compo.Config // preparo args string reqUid = "Default"; DictExec.Add("UID", reqUid); + + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}"); CalcRequestDTO req = new CalcRequestDTO() { @@ -162,6 +166,17 @@ namespace Lux.UI.Components.Compo.Config await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req); } + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + private void FullUpdate() { ReloadData(); diff --git a/Lux.UI/Components/Compo/OfferCommonPar.razor.cs b/Lux.UI/Components/Compo/OfferCommonPar.razor.cs index a66acbbd..b45d4a5b 100644 --- a/Lux.UI/Components/Compo/OfferCommonPar.razor.cs +++ b/Lux.UI/Components/Compo/OfferCommonPar.razor.cs @@ -1,4 +1,4 @@ -using EgwCoreLib.Lux.Core; +using EgwCoreLib.Lux.Core; using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.DbModel.Sales; using EgwCoreLib.Lux.Data.Services; @@ -146,6 +146,10 @@ namespace Lux.UI.Components.Compo // preparo args string reqUid = "Default"; DictExec.Add("UID", reqUid); + + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}"); CalcRequestDTO req = new CalcRequestDTO() { @@ -156,6 +160,18 @@ namespace Lux.UI.Components.Compo await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req); } + + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + private void ConfInit() { apiUrl = Config.GetValue("ServerConf:Prog.ApiUrl") ?? ""; diff --git a/Lux.UI/Components/Compo/OfferRowMan.razor.cs b/Lux.UI/Components/Compo/OfferRowMan.razor.cs index 63be44d8..c415eed7 100644 --- a/Lux.UI/Components/Compo/OfferRowMan.razor.cs +++ b/Lux.UI/Components/Compo/OfferRowMan.razor.cs @@ -763,6 +763,10 @@ namespace Lux.UI.Components.Compo // preparo args string reqUid = "Default"; DictExec.Add("UID", reqUid); + + // FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!! + DictExec.Add("RUID", GenerateId()); + DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}"); CalcRequestDTO req = new CalcRequestDTO() { @@ -773,6 +777,18 @@ namespace Lux.UI.Components.Compo await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req); } + + private readonly Random _rnd = new Random(); + // --------------------------------------------------------- + // ✅ ID incrementale: timestamp ms + random 4-6 chars + // --------------------------------------------------------- + private string GenerateId() + { + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper(); + return $"{ts}-{rand}"; + } + /// /// Chiude edit andando eventualmente a salvare /// diff --git a/Lux.UI/Components/Compo/OrderRowMan.razor b/Lux.UI/Components/Compo/OrderRowMan.razor index 603f9853..59fe6236 100644 --- a/Lux.UI/Components/Compo/OrderRowMan.razor +++ b/Lux.UI/Components/Compo/OrderRowMan.razor @@ -486,7 +486,7 @@ else if (WorkLoadRecord != null) } else if (CurrEditMode == EditMode.WorkLoadDetailTag) { - + @*