using AnalyticsBLL.ExecutionRouting; using AnalyticsDAL.CustomCode.Analysis; using AnalyticsDAL.DTO.Analysis; using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.EntityHandler; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using GB5Shared.Validation; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using static GB5Shared.GB5Constant.Constant; namespace AnalyticsBLL.Analysis { public class AnalysisBLL : IAnalysisBLL { private readonly IAnalysisDAL _dal; private readonly ILogger _logger; private readonly AutoNumber _AutoNumber; private readonly IQueryExecutor _QueryExecutor; private readonly BaseEntityAppService _baseEntityAppService; private readonly IValidation _validation; private readonly IAnalysisExecutionRouteResolver _routeResolver; public AnalysisBLL(IAnalysisDAL dal, ILogger logger, AutoNumber autoNumber, IQueryExecutor IQueryExecutor, BaseEntityAppService baseEntityAppService, IValidation validation, IAnalysisExecutionRouteResolver routeResolver) { _dal = dal; _logger = logger; _AutoNumber = autoNumber; _QueryExecutor = IQueryExecutor; _baseEntityAppService = baseEntityAppService; _validation = validation; _routeResolver = routeResolver; } public async Task GetAnalysis(int analysisId,LoginDTO login,CancellationToken ct) { var header = await _dal.GetAnalysisHeader(analysisId, login, ct).ConfigureAwait(false); if (header == null)return JsonConvert.SerializeObject(null); header.AnalysisObjectsArray = (await _dal.GetAnalysisObjects(analysisId, login, ct).ConfigureAwait(false)).ToList(); header.AnalysisFieldsArray =(await _dal.GetAnalysisFields(analysisId, login, ct).ConfigureAwait(false)).ToList(); return JsonConvert.SerializeObject(header); } public async Task GetSelectListAnalysis(int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO login, CancellationToken ct) { try { return await _dal.GetSelectListAnalysis(firstNumber, maxResult, criteriaDTO, login, ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "GetSelectListAnalysis failed"); throw; } } public async Task>> DynamicOutput(int analysisQueryId, int reportViewId, int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO login, CancellationToken ct) { try { // Resolve the execution route (local vs. an external SqlWorkbench-managed client // DB) here in BLL, not inside the DAL — the resolver reaches into SqlWorkbench's // BLL layer, which the DAL must stay free of per this repo's layering rule. The // overwhelmingly common case (no AnalysisId found, or no workspace override bound) // resolves to IsExternal=false, i.e. today's unchanged local-execution path. var analysisId = await _dal.GetAnalysisIdForQuery(analysisQueryId, login, ct).ConfigureAwait(false); var route = analysisId is int id ? await _routeResolver.ResolveAsync(id, login, ct).ConfigureAwait(false) : null; return await _dal.DynamicOutput(analysisQueryId, reportViewId, firstNumber, maxResult, criteriaDTO, login, ct, route).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "DynamicOutput failed for AnalysisQueryId {Id}", analysisQueryId); throw; } } public async Task DeleteAnalysis(int analysisId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-analysis", new { analysisId }); await _dal.DeleteAnalysis(analysisId, login, ct).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-analysis-failed", ex); _logger.LogError(ex, "DeleteAnalysis failed for AnalysisId {Id}", analysisId); throw; } } public async Task SaveAnalysis(AnalysisDTO analysisDTO, LoginDTO LoginDTO) { try { if (analysisDTO == null) throw new ArgumentNullException(nameof(analysisDTO)); bool isNew = analysisDTO.AnalysisId == 0; // Pre-Transaction : Auto Number var analysisAutoNumber = await _AutoNumber.GetAutoNumber( 1, AUTONUMBERCONSTANT.MANALYSIS, LoginDTO); var trans = await _QueryExecutor.BeginTransactionAsync(LoginDTO); try { await _baseEntityAppService.ExecuteSaveAsync( EntityConstant.ANALYSIS, EventTypeConstant.SAVEANALYSISEVENTTYPEID, analysisDTO, LoginDTO, async tx => { if (isNew) { analysisDTO.AnalysisId = analysisAutoNumber.StartNumber; #region Audit Fields analysisDTO.AnalysisCreatedById = LoginDTO.UserId; analysisDTO.AnalysisCreatedOn = DateTime.UtcNow; #endregion } #region Audit Fields analysisDTO.AnalysisModifiedById = LoginDTO.UserId; analysisDTO.AnalysisModifiedOn = DateTime.UtcNow; #endregion int result; if (isNew) { result = await _dal.SaveAnalysis(analysisDTO, LoginDTO, tx); } else { result = await _dal.UpdateAnalysis(analysisDTO, LoginDTO, tx); } return result; }, null, -1, -1, trans, isNew); await _QueryExecutor.CommitAsync(trans); if (isNew) { return SuccessResponse.SaveSuccess; } else { return SuccessResponse.UpdateSuccess; } } catch (Exception) { await _QueryExecutor.RollbackAsync(trans); throw; } } catch (Exception) { throw; } } public async Task ValidateAnalysisVisibility(int analysisId, LoginDTO login, CancellationToken ct) { var tenantId = await _dal.GetAnalysisTenantId(analysisId, login, ct).ConfigureAwait(false); if (tenantId is null) throw new ArgumentException($"Analysis {analysisId} does not exist.", nameof(analysisId)); if (tenantId != -1 && tenantId != login.ClientId) throw new ArgumentException( $"Analysis {analysisId} is private to another tenant and is not assignable here.", nameof(analysisId)); } } }