using AnalyticsDAL.CustomCode.KPILIST; using AnalyticsDAL.DTO.KPILIST; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; using static GB5Shared.GB5Constant.Constant; namespace AnalyticsBLL.KPILIST { public class KPILISTBLL : IKPILISTBLL { private readonly IKPILISTDAL _dal; private readonly KeyInvalidate _keyInvalidate; private readonly ILogger _logger; public KPILISTBLL( IKPILISTDAL dal, KeyInvalidate keyInvalidate, ILogger logger) { _dal = dal; _keyInvalidate = keyInvalidate; _logger = logger; } public async Task GetKPIList(int kpiListId, LoginDTO login, CancellationToken ct) => await _dal.GetKPIList(kpiListId, login, ct).ConfigureAwait(false); public async Task GetSelectListKPIList(LoginDTO login, CancellationToken ct) => await _dal.GetSelectListKPIList(login, ct).ConfigureAwait(false); public async Task SaveKPIList(KPILISTDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-kpilist", new { dto.KPILISTId }); if (string.IsNullOrWhiteSpace(dto.KPILISTName)) throw new ArgumentException("KPILISTName is required."); if (dto.KPILISTName.Length > 200) throw new ArgumentException("KPILISTName must not exceed 200 characters."); if (dto.KPILISTDETAILArray != null) { foreach (var row in dto.KPILISTDETAILArray) { // Real MKPI ids are negative (confirmed live, e.g. -1799999776 "Working // Capital") — "<= 0" rejects every real KPI selection; "== 0" is the // correct "not selected" check. Same fix as RoleKPIBLL's identical bug. if (row.KPIId == 0) throw new ArgumentException($"Detail row {row.KPILISTDETAILSLNO}: KPI must be selected."); } } if (await _dal.IsDuplicateName(dto.KPILISTName, dto.KPILISTId, login, ct).ConfigureAwait(false)) throw new InvalidOperationException($"KPI List name '{dto.KPILISTName}' already exists."); var isNew = dto.KPILISTId == 0; GB5Trace.Step("save-kpilist", new { dto.KPILISTId, isNew }); int savedId; if (isNew) savedId = await _dal.SaveKPIList(dto, login, ct).ConfigureAwait(false); else { savedId = dto.KPILISTId; await _dal.UpdateKPIList(dto, login, ct).ConfigureAwait(false); } var cacheKey = new CacheKeyGeneration().KeyGeneration( savedId, EntityConstant.OBJECTKPILIST, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-kpilist-failed", ex); _logger.LogError(ex, "SaveKPIList failed for KPILISTId {KPILISTId}", dto.KPILISTId); throw; } } public async Task DeleteKPIList(int kpiListId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-kpilist", new { kpiListId }); await _dal.DeleteKPIList(kpiListId, login, ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration( kpiListId, EntityConstant.OBJECTKPILIST, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-kpilist-failed", ex); _logger.LogError(ex, "DeleteKPIList failed for KPILISTId {KPILISTId}", kpiListId); throw; } } } }