using AnalyticsDAL.CustomCode.AnalysisQuery; using AnalyticsDAL.DTO.AnalysisQuery; using GB5Shared.DALCache; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; using static GB5Shared.GB5Constant.Constant; namespace AnalyticsBLL.AnalysisQuery { public class AnalysisQueryBLL : IAnalysisQueryBLL { private readonly IAnalysisQueryDAL _dal; private readonly ILogger _logger; private readonly AutoNumber _AutoNumber; private readonly IDALCache _dalCache; private readonly CacheKeyGeneration _keyGen = new(); public AnalysisQueryBLL(IAnalysisQueryDAL dal, ILogger logger, AutoNumber autoNumber, IDALCache dalCache) { _dal = dal; _logger = logger; _AutoNumber = autoNumber; _dalCache = dalCache; } public async Task GetAnalysisQuery(int analysisQueryId, LoginDTO login, CancellationToken ct) { try { return await _dal.GetAnalysisQuery(analysisQueryId, login, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("get-analysis-query-failed", ex); _logger.LogError(ex, "GetAnalysisQuery failed for AnalysisQueryId {Id}", analysisQueryId); throw; } } public async Task GetSelectListAnalysisQuery(int analysisId, LoginDTO login, CancellationToken ct) { try { return await _dal.GetSelectListAnalysisQuery(analysisId, login, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("get-selectlist-analysis-query-failed", ex); _logger.LogError(ex, "GetSelectListAnalysisQuery failed for AnalysisId {Id}", analysisId); throw; } } public async Task GetAnalysisFieldsForDesigner(int analysisId, int analysisQueryId, LoginDTO login, CancellationToken ct) { try { return await _dal.GetAnalysisFieldsForDesigner(analysisId, analysisQueryId, login, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("get-analysis-fields-designer-failed", ex); _logger.LogError(ex, "GetAnalysisFieldsForDesigner failed for AnalysisId {Id}", analysisId); throw; } } public async Task SaveAnalysisQuery(AnalysisQueryDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-analysis-query", new { dto.AnalysisQueryId, dto.AnalysisId }); if (string.IsNullOrWhiteSpace(dto.AnalysisQueryQueryName)) throw new ArgumentException("Query Name is required."); if (dto.AnalysisQueryQueryName.Length > 100) throw new ArgumentException("Query Name must not exceed 100 characters."); if (dto.AnalysisId == 0) throw new ArgumentException("Analysis is required."); bool isNew = dto.AnalysisQueryId == 0; if (isNew) { var parentAutoNumber = await _AutoNumber .GetNumberAsync(1, AUTONUMBERCONSTANT.ANALYSISQUERY, login) .ConfigureAwait(false); dto.AnalysisQueryId = parentAutoNumber.StartNumber; } // Child Auto Numbers if (dto.AnalysisQueryFieldDTOs != null) { var newFields = dto.AnalysisQueryFieldDTOs .Where(x => x.AnalysisQueryFieldsId == 0) .ToList(); if (newFields.Any()) { var childAutoNumber = await _AutoNumber .GetNumberAsync(newFields.Count, AUTONUMBERCONSTANT.ANALYSISQUERY, login) .ConfigureAwait(false); int nextId = childAutoNumber.StartNumber; foreach (var field in newFields) { field.AnalysisQueryFieldsId = nextId++; } } foreach (var field in dto.AnalysisQueryFieldDTOs) { field.AnalysisQueryId = dto.AnalysisQueryId; } } GB5Trace.Step("save-analysis-query", new { dto.AnalysisQueryId, isNew }); var savedId = await _dal.SaveAnalysisQuery(dto, login, isNew, ct).ConfigureAwait(false); // Invalidate DAL cache for this query's header and filter fields. // OBJECTANALYSISQUERYFIELDS entries (keyed by analysisQueryId+reportViewId) cannot // be precisely invalidated here since reportViewId is not part of the save payload — // those entries expire naturally after their 300 s TTL. var aqKey = _keyGen.KeyGeneration(dto.AnalysisQueryId, EntityConstant.OBJECTANALYSISQUERY, CacheKeyLevel.CLIENT_LEVEL, login); var afKey = _keyGen.KeyGeneration(dto.AnalysisQueryId, EntityConstant.OBJECTANALYSISFILTER, CacheKeyLevel.CLIENT_LEVEL, login); await Task.WhenAll( _dalCache.InvalidateAsync(aqKey, ct), _dalCache.InvalidateAsync(afKey, ct)).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-analysis-query-failed", ex); _logger.LogError(ex, "SaveAnalysisQuery failed for AnalysisId {Id}", dto.AnalysisId); throw; } } public async Task DeleteAnalysisQuery(int analysisQueryId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-analysis-query", new { analysisQueryId }); await _dal.DeleteAnalysisQuery(analysisQueryId, login, ct).ConfigureAwait(false); var aqKey = _keyGen.KeyGeneration(analysisQueryId, EntityConstant.OBJECTANALYSISQUERY, CacheKeyLevel.CLIENT_LEVEL, login); var afKey = _keyGen.KeyGeneration(analysisQueryId, EntityConstant.OBJECTANALYSISFILTER, CacheKeyLevel.CLIENT_LEVEL, login); await Task.WhenAll( _dalCache.InvalidateAsync(aqKey, ct), _dalCache.InvalidateAsync(afKey, ct)).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-analysis-query-failed", ex); _logger.LogError(ex, "DeleteAnalysisQuery failed for AnalysisQueryId {Id}", analysisQueryId); throw; } } } }